effect 4.0.0-beta.24 → 4.0.0-beta.25
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/Effect.d.ts +1 -1
- package/dist/Effect.d.ts.map +1 -1
- package/dist/internal/effect.js.map +1 -1
- package/dist/testing/TestClock.d.ts +2 -2
- package/dist/unstable/http/HttpClient.d.ts.map +1 -1
- package/dist/unstable/http/HttpClient.js +18 -10
- package/dist/unstable/http/HttpClient.js.map +1 -1
- package/dist/unstable/rpc/RpcServer.d.ts +2 -2
- package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
- package/dist/unstable/sql/SqlResolver.d.ts.map +1 -1
- package/dist/unstable/sql/SqlResolver.js +15 -6
- package/dist/unstable/sql/SqlResolver.js.map +1 -1
- package/package.json +1 -1
- package/src/Effect.ts +1 -1
- package/src/internal/effect.ts +2 -1
- package/src/unstable/http/HttpClient.ts +28 -13
- package/src/unstable/sql/SqlResolver.ts +15 -5
package/src/internal/effect.ts
CHANGED
|
@@ -4950,7 +4950,8 @@ export const forkScoped: {
|
|
|
4950
4950
|
readonly startImmediately?: boolean | undefined
|
|
4951
4951
|
readonly uninterruptible?: boolean | "inherit" | undefined
|
|
4952
4952
|
} | undefined
|
|
4953
|
-
): [Arg] extends [Effect.Effect<infer _A, infer _E, infer _R>] ?
|
|
4953
|
+
): [Arg] extends [Effect.Effect<infer _A, infer _E, infer _R>] ?
|
|
4954
|
+
Effect.Effect<Fiber.Fiber<_A, _E>, never, _R | Scope.Scope>
|
|
4954
4955
|
: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<Fiber.Fiber<A, E>, never, R | Scope.Scope>
|
|
4955
4956
|
} = dual((args) => isEffect(args[0]), <A, E, R>(
|
|
4956
4957
|
self: Effect.Effect<A, E, R>,
|
|
@@ -8,7 +8,7 @@ import * as Duration from "../../Duration.ts"
|
|
|
8
8
|
import * as Effect from "../../Effect.ts"
|
|
9
9
|
import * as Exit from "../../Exit.ts"
|
|
10
10
|
import * as Fiber from "../../Fiber.ts"
|
|
11
|
-
import { constFalse, constTrue, dual, flow, identity } from "../../Function.ts"
|
|
11
|
+
import { constant, constFalse, constTrue, dual, flow, identity } from "../../Function.ts"
|
|
12
12
|
import * as Inspectable from "../../Inspectable.ts"
|
|
13
13
|
import * as Layer from "../../Layer.ts"
|
|
14
14
|
import { type Pipeable, pipeArguments } from "../../Pipeable.ts"
|
|
@@ -1158,6 +1158,7 @@ export const withRateLimiter: {
|
|
|
1158
1158
|
options: WithRateLimiter.Options
|
|
1159
1159
|
): HttpClient.With<E | RateLimiter.RateLimiterError, R> => {
|
|
1160
1160
|
const initialState: RateLimiterState = {
|
|
1161
|
+
initial: true,
|
|
1161
1162
|
limit: options.limit,
|
|
1162
1163
|
window: Duration.max(Duration.fromInputUnsafe(options.window), Duration.millis(1))
|
|
1163
1164
|
}
|
|
@@ -1166,10 +1167,11 @@ export const withRateLimiter: {
|
|
|
1166
1167
|
const keyOption = options.key
|
|
1167
1168
|
const resolveKey: (request: HttpClientRequest.HttpClientRequest) => string = typeof keyOption === "function"
|
|
1168
1169
|
? keyOption
|
|
1169
|
-
: ()
|
|
1170
|
+
: constant(keyOption)
|
|
1170
1171
|
const tokensOption = options.tokens
|
|
1171
|
-
const resolveTokens: (request: HttpClientRequest.HttpClientRequest) => number
|
|
1172
|
-
|
|
1172
|
+
const resolveTokens: (request: HttpClientRequest.HttpClientRequest) => number = typeof tokensOption === "function"
|
|
1173
|
+
? tokensOption
|
|
1174
|
+
: constant(tokensOption ?? 1)
|
|
1173
1175
|
|
|
1174
1176
|
const getState = (key: string): RateLimiterState => {
|
|
1175
1177
|
const current = states.get(key)
|
|
@@ -1182,7 +1184,7 @@ export const withRateLimiter: {
|
|
|
1182
1184
|
|
|
1183
1185
|
const onResponse = options.disableResponseInspection
|
|
1184
1186
|
? undefined
|
|
1185
|
-
: (clock: Clock, key: string, headers: Headers.Headers, tokens: number
|
|
1187
|
+
: (clock: Clock, key: string, headers: Headers.Headers, tokens: number) => {
|
|
1186
1188
|
const current = getState(key)
|
|
1187
1189
|
const next = parseRateLimiterState(current, clock, headers, tokens)
|
|
1188
1190
|
if (next.limit !== current.limit || !Duration.equals(next.window, current.window)) {
|
|
@@ -1198,8 +1200,15 @@ export const withRateLimiter: {
|
|
|
1198
1200
|
const fiber = Fiber.getCurrent()!
|
|
1199
1201
|
const clock = fiber.getRef(Clock)
|
|
1200
1202
|
const key = resolveKey(request)
|
|
1201
|
-
const tokens = resolveTokens(request)
|
|
1203
|
+
const tokens = Math.max(resolveTokens(request), 1)
|
|
1202
1204
|
const current = getState(key)
|
|
1205
|
+
function retry(response: HttpClientResponse.HttpClientResponse) {
|
|
1206
|
+
if (options.disableResponseInspection) return loop(effect, request)
|
|
1207
|
+
const retryAfter = parseRetryAfter(clock, getHeader(response.headers, "retry-after"))
|
|
1208
|
+
return retryAfter
|
|
1209
|
+
? Effect.flatMap(Effect.sleep(retryAfter), () => loop(effect, request))
|
|
1210
|
+
: loop(effect, request)
|
|
1211
|
+
}
|
|
1203
1212
|
return Effect.flatMap(
|
|
1204
1213
|
options.limiter.consume({
|
|
1205
1214
|
algorithm: options.algorithm,
|
|
@@ -1213,12 +1222,13 @@ export const withRateLimiter: {
|
|
|
1213
1222
|
const run = Effect.matchEffect(effect, {
|
|
1214
1223
|
onSuccess(response) {
|
|
1215
1224
|
onResponse?.(clock, key, response.headers, tokens)
|
|
1216
|
-
|
|
1225
|
+
if (response.status !== 429) return Effect.succeed(response)
|
|
1226
|
+
return retry(response)
|
|
1217
1227
|
},
|
|
1218
1228
|
onFailure(error) {
|
|
1219
1229
|
if (isTooManyRequestsHttpClientError(error)) {
|
|
1220
1230
|
onResponse?.(clock, key, error.reason.response.headers, tokens)
|
|
1221
|
-
return
|
|
1231
|
+
return retry(error.reason.response)
|
|
1222
1232
|
}
|
|
1223
1233
|
return Effect.fail(error)
|
|
1224
1234
|
}
|
|
@@ -1232,23 +1242,28 @@ export const withRateLimiter: {
|
|
|
1232
1242
|
interface RateLimiterState {
|
|
1233
1243
|
readonly limit: number
|
|
1234
1244
|
readonly window: Duration.Duration
|
|
1245
|
+
readonly initial: boolean
|
|
1235
1246
|
}
|
|
1236
1247
|
|
|
1237
1248
|
const parseRateLimiterState = (
|
|
1238
1249
|
state: RateLimiterState,
|
|
1239
1250
|
clock: Clock,
|
|
1240
1251
|
headers: Headers.Headers,
|
|
1241
|
-
tokens: number
|
|
1252
|
+
tokens: number
|
|
1242
1253
|
): RateLimiterState => {
|
|
1243
|
-
const limit = parseRateLimitLimit(headers, tokens) ?? state.limit
|
|
1254
|
+
const limit = parseRateLimitLimit(state, headers, tokens) ?? state.limit
|
|
1244
1255
|
const window = parseRateLimitWindow(clock, headers) ?? state.window
|
|
1245
1256
|
if (limit === state.limit && Duration.equals(window, state.window)) {
|
|
1246
1257
|
return state
|
|
1247
1258
|
}
|
|
1248
|
-
return { limit, window }
|
|
1259
|
+
return { limit, window, initial: false }
|
|
1249
1260
|
}
|
|
1250
1261
|
|
|
1251
|
-
const parseRateLimitLimit = (
|
|
1262
|
+
const parseRateLimitLimit = (
|
|
1263
|
+
state: RateLimiterState,
|
|
1264
|
+
headers: Headers.Headers,
|
|
1265
|
+
tokens: number
|
|
1266
|
+
): number | undefined => {
|
|
1252
1267
|
const raw = getHeader(headers, "ratelimit-limit", "x-ratelimit-limit")
|
|
1253
1268
|
const value = parseNumberHeader(raw)
|
|
1254
1269
|
if (value !== undefined && value > 0) {
|
|
@@ -1258,7 +1273,7 @@ const parseRateLimitLimit = (headers: Headers.Headers, tokens: number | undefine
|
|
|
1258
1273
|
if (remaining === undefined) {
|
|
1259
1274
|
return undefined
|
|
1260
1275
|
}
|
|
1261
|
-
return
|
|
1276
|
+
return state.initial ? remaining + tokens : Math.max(remaining + tokens, state.limit)
|
|
1262
1277
|
}
|
|
1263
1278
|
|
|
1264
1279
|
const parseRateLimitRemaining = (headers: Headers.Headers): number | undefined => {
|
|
@@ -104,14 +104,14 @@ export const ordered = <Req extends Schema.Top, Res extends Schema.Top, _, E, R>
|
|
|
104
104
|
>,
|
|
105
105
|
SqlClient.TransactionConnection["Service"] | undefined
|
|
106
106
|
>({
|
|
107
|
-
key:
|
|
107
|
+
key: transactionKey,
|
|
108
108
|
resolver: Effect.fnUntraced(function*(entries) {
|
|
109
109
|
const inputs = yield* partitionRequests(entries, options.Request)
|
|
110
110
|
const results = yield* options.execute(inputs as any).pipe(
|
|
111
111
|
Effect.provideServices(entries[0].services)
|
|
112
112
|
)
|
|
113
113
|
if (results.length !== inputs.length) {
|
|
114
|
-
return yield*
|
|
114
|
+
return yield* new ResultLengthMismatch({ expected: inputs.length, actual: results.length })
|
|
115
115
|
}
|
|
116
116
|
const decodedResults = yield* decodeArray(results).pipe(
|
|
117
117
|
Effect.provideServices(entries[0].services)
|
|
@@ -160,7 +160,7 @@ export const grouped = <Req extends Schema.Top, Res extends Schema.Top, K, Row,
|
|
|
160
160
|
>,
|
|
161
161
|
SqlClient.TransactionConnection["Service"] | undefined
|
|
162
162
|
>({
|
|
163
|
-
key:
|
|
163
|
+
key: transactionKey,
|
|
164
164
|
resolver: Effect.fnUntraced(function*(entries) {
|
|
165
165
|
const inputs = yield* partitionRequests(entries, options.Request)
|
|
166
166
|
const resultMap = MutableHashMap.empty<K, Arr.NonEmptyArray<Res["Type"]>>()
|
|
@@ -226,7 +226,11 @@ export const findById = <Id extends Schema.Top, Res extends Schema.Top, Row, E,
|
|
|
226
226
|
>,
|
|
227
227
|
SqlClient.TransactionConnection["Service"] | undefined
|
|
228
228
|
>({
|
|
229
|
-
key
|
|
229
|
+
key(entry) {
|
|
230
|
+
const conn = entry.services.mapUnsafe.get(SqlClient.TransactionConnection.key)
|
|
231
|
+
if (!conn) return undefined
|
|
232
|
+
return Equal.byReferenceUnsafe(conn)
|
|
233
|
+
},
|
|
230
234
|
resolver: Effect.fnUntraced(function*(entries) {
|
|
231
235
|
const [inputs, idMap] = yield* partitionRequestsById(entries, options.Id)
|
|
232
236
|
const results = yield* options.execute(inputs as any).pipe(
|
|
@@ -279,7 +283,7 @@ const void_ = <Req extends Schema.Top, _, E, R>(
|
|
|
279
283
|
>,
|
|
280
284
|
SqlClient.TransactionConnection["Service"] | undefined
|
|
281
285
|
>({
|
|
282
|
-
key:
|
|
286
|
+
key: transactionKey,
|
|
283
287
|
resolver: Effect.fnUntraced(function*(entries) {
|
|
284
288
|
const inputs = yield* partitionRequests(entries, options.Request)
|
|
285
289
|
yield* options.execute(inputs as any).pipe(
|
|
@@ -354,3 +358,9 @@ const partitionRequestsById = function*<In, A, E, R, InE>(
|
|
|
354
358
|
|
|
355
359
|
return [inputs, byIdMap] as const
|
|
356
360
|
}
|
|
361
|
+
|
|
362
|
+
function transactionKey<A>(entry: Request.Entry<A>): SqlClient.TransactionConnection["Service"] | undefined {
|
|
363
|
+
const conn = entry.services.mapUnsafe.get(SqlClient.TransactionConnection.key)
|
|
364
|
+
if (!conn) return undefined
|
|
365
|
+
return Equal.byReferenceUnsafe(conn)
|
|
366
|
+
}
|