effect 4.0.0-beta.87 → 4.0.0-beta.89
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 +5 -2
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +4 -1
- package/dist/Effect.js.map +1 -1
- package/dist/Latch.d.ts +19 -0
- package/dist/Latch.d.ts.map +1 -1
- package/dist/Latch.js +11 -0
- package/dist/Latch.js.map +1 -1
- package/dist/Schema.d.ts +14 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +14 -1
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaAST.d.ts +17 -5
- package/dist/SchemaAST.d.ts.map +1 -1
- package/dist/SchemaAST.js +22 -7
- package/dist/SchemaAST.js.map +1 -1
- package/dist/internal/effect.js +17 -15
- package/dist/internal/effect.js.map +1 -1
- package/dist/unstable/http/HttpClient.d.ts +4 -0
- package/dist/unstable/http/HttpClient.d.ts.map +1 -1
- package/dist/unstable/http/HttpClient.js +56 -20
- package/dist/unstable/http/HttpClient.js.map +1 -1
- package/dist/unstable/observability/OtlpTracer.d.ts.map +1 -1
- package/dist/unstable/observability/OtlpTracer.js +2 -1
- package/dist/unstable/observability/OtlpTracer.js.map +1 -1
- package/dist/unstable/persistence/RateLimiter.d.ts +106 -3
- package/dist/unstable/persistence/RateLimiter.d.ts.map +1 -1
- package/dist/unstable/persistence/RateLimiter.js +397 -5
- package/dist/unstable/persistence/RateLimiter.js.map +1 -1
- package/dist/unstable/rpc/RpcServer.js +4 -4
- package/dist/unstable/rpc/RpcServer.js.map +1 -1
- package/package.json +1 -1
- package/src/Effect.ts +10 -4
- package/src/Latch.ts +21 -0
- package/src/Schema.ts +14 -1
- package/src/SchemaAST.ts +23 -7
- package/src/internal/effect.ts +30 -18
- package/src/unstable/http/HttpClient.ts +115 -24
- package/src/unstable/observability/OtlpTracer.ts +2 -1
- package/src/unstable/persistence/RateLimiter.ts +599 -5
- package/src/unstable/rpc/RpcServer.ts +4 -4
|
@@ -1299,6 +1299,10 @@ export declare namespace WithRateLimiter {
|
|
|
1299
1299
|
* Disable automatic limits updates from response headers.
|
|
1300
1300
|
*/
|
|
1301
1301
|
readonly disableResponseInspection?: boolean | undefined
|
|
1302
|
+
/**
|
|
1303
|
+
* Disable adaptive learning from `Retry-After` responses.
|
|
1304
|
+
*/
|
|
1305
|
+
readonly disableAdaptiveLearning?: boolean | undefined
|
|
1302
1306
|
}
|
|
1303
1307
|
}
|
|
1304
1308
|
|
|
@@ -1362,6 +1366,7 @@ export const withRateLimiter: {
|
|
|
1362
1366
|
const resolveTokens: (request: HttpClientRequest.HttpClientRequest) => number = typeof tokensOption === "function"
|
|
1363
1367
|
? tokensOption
|
|
1364
1368
|
: constant(tokensOption ?? 1)
|
|
1369
|
+
const adaptiveLearningEnabled = !options.disableAdaptiveLearning
|
|
1365
1370
|
|
|
1366
1371
|
const getState = (key: string): RateLimiterState => {
|
|
1367
1372
|
const current = states.get(key)
|
|
@@ -1392,13 +1397,75 @@ export const withRateLimiter: {
|
|
|
1392
1397
|
const key = resolveKey(request)
|
|
1393
1398
|
const tokens = Math.max(resolveTokens(request), 1)
|
|
1394
1399
|
const current = getState(key)
|
|
1395
|
-
function retry(
|
|
1400
|
+
function retry(retryAfter: Duration.Duration | undefined) {
|
|
1396
1401
|
if (options.disableResponseInspection) return loop(effect, request)
|
|
1397
|
-
const retryAfter = parseRetryAfter(clock, getHeader(response.headers, "retry-after"))
|
|
1398
1402
|
return retryAfter
|
|
1399
1403
|
? Effect.flatMap(Effect.sleep(retryAfter), () => loop(effect, request))
|
|
1400
1404
|
: loop(effect, request)
|
|
1401
1405
|
}
|
|
1406
|
+
const inspectResponse = (
|
|
1407
|
+
response: HttpClientResponse.HttpClientResponse,
|
|
1408
|
+
adaptive: RateLimiter.AdaptiveConsumeResult | undefined
|
|
1409
|
+
) => {
|
|
1410
|
+
onResponse?.(clock, key, response.headers, tokens)
|
|
1411
|
+
if (options.disableResponseInspection || response.status !== 429) {
|
|
1412
|
+
return Effect.succeed</**
|
|
1413
|
+
* Applies request rate limiting using the `RateLimiter` service.
|
|
1414
|
+
*
|
|
1415
|
+
* **Details**
|
|
1416
|
+
*
|
|
1417
|
+
* It can update limits by inspecting common rate limit response headers and
|
|
1418
|
+
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1419
|
+
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1420
|
+
*
|
|
1421
|
+
* @category rate limiting
|
|
1422
|
+
* @since 4.0.0
|
|
1423
|
+
*/
|
|
1424
|
+
Duration.Duration | undefined>(undefined);
|
|
1425
|
+
}
|
|
1426
|
+
const retryAfter = parseRetryAfter(clock, getHeader(response.headers, "retry-after"))
|
|
1427
|
+
if (retryAfter === undefined) {
|
|
1428
|
+
return Effect.succeed</**
|
|
1429
|
+
* Applies request rate limiting using the `RateLimiter` service.
|
|
1430
|
+
*
|
|
1431
|
+
* **Details**
|
|
1432
|
+
*
|
|
1433
|
+
* It can update limits by inspecting common rate limit response headers and
|
|
1434
|
+
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1435
|
+
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1436
|
+
*
|
|
1437
|
+
* @category rate limiting
|
|
1438
|
+
* @since 4.0.0
|
|
1439
|
+
*/
|
|
1440
|
+
Duration.Duration | undefined>(undefined);
|
|
1441
|
+
}
|
|
1442
|
+
const delay = parseRateLimitWindow(clock, response.headers) ?? retryAfter
|
|
1443
|
+
if (adaptive === undefined) {
|
|
1444
|
+
return Effect.succeed</**
|
|
1445
|
+
* Applies request rate limiting using the `RateLimiter` service.
|
|
1446
|
+
*
|
|
1447
|
+
* **Details**
|
|
1448
|
+
*
|
|
1449
|
+
* It can update limits by inspecting common rate limit response headers and
|
|
1450
|
+
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1451
|
+
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1452
|
+
*
|
|
1453
|
+
* @category rate limiting
|
|
1454
|
+
* @since 4.0.0
|
|
1455
|
+
*/
|
|
1456
|
+
Duration.Duration | undefined>(delay);
|
|
1457
|
+
}
|
|
1458
|
+
return Effect.as(
|
|
1459
|
+
options.limiter.adaptiveFeedback({
|
|
1460
|
+
key,
|
|
1461
|
+
epoch: adaptive.epoch,
|
|
1462
|
+
tokens,
|
|
1463
|
+
status: response.status,
|
|
1464
|
+
retryAfter: delay
|
|
1465
|
+
}),
|
|
1466
|
+
delay
|
|
1467
|
+
)
|
|
1468
|
+
}
|
|
1402
1469
|
return Effect.flatMap(
|
|
1403
1470
|
options.limiter.consume({
|
|
1404
1471
|
algorithm: options.algorithm,
|
|
@@ -1409,24 +1476,55 @@ export const withRateLimiter: {
|
|
|
1409
1476
|
tokens
|
|
1410
1477
|
}),
|
|
1411
1478
|
({ delay }) => {
|
|
1412
|
-
const
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1479
|
+
const runAdaptive = (): Effect.Effect<
|
|
1480
|
+
HttpClientResponse.HttpClientResponse,
|
|
1481
|
+
E | RateLimiter.RateLimiterError,
|
|
1482
|
+
R
|
|
1483
|
+
> => {
|
|
1484
|
+
const runRequest = (adaptive: RateLimiter.AdaptiveConsumeResult | undefined) => {
|
|
1485
|
+
const request = Effect.matchEffect(effect, {
|
|
1486
|
+
onSuccess(response) {
|
|
1487
|
+
return Effect.flatMap(inspectResponse(response, adaptive), (retryAfter) => {
|
|
1488
|
+
if (response.status !== 429) return Effect.succeed(response)
|
|
1489
|
+
return retry(retryAfter)
|
|
1490
|
+
})
|
|
1491
|
+
},
|
|
1492
|
+
onFailure(error) {
|
|
1493
|
+
if (isTooManyRequestsHttpClientError(error)) {
|
|
1494
|
+
return Effect.flatMap(
|
|
1495
|
+
inspectResponse(error.reason.response, adaptive),
|
|
1496
|
+
(retryAfter) => retry(retryAfter)
|
|
1497
|
+
)
|
|
1498
|
+
}
|
|
1499
|
+
return Effect.fail(error)
|
|
1500
|
+
}
|
|
1501
|
+
})
|
|
1502
|
+
return adaptive === undefined || Duration.isZero(adaptive.delay)
|
|
1503
|
+
? request
|
|
1504
|
+
: Effect.delay(request, adaptive.delay)
|
|
1424
1505
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1506
|
+
if (!adaptiveLearningEnabled) {
|
|
1507
|
+
return runRequest(undefined)
|
|
1508
|
+
}
|
|
1509
|
+
return Effect.flatMap(
|
|
1510
|
+
options.limiter.adaptiveConsume({
|
|
1511
|
+
key,
|
|
1512
|
+
tokens,
|
|
1513
|
+
fallbackLimit: current.limit,
|
|
1514
|
+
fallbackWindow: current.window
|
|
1515
|
+
}),
|
|
1516
|
+
(adaptive) => {
|
|
1517
|
+
if (!Duration.isZero(adaptive.delay) && adaptive.phase === "cooldown") {
|
|
1518
|
+
return Effect.flatMap(Effect.sleep(adaptive.delay), runAdaptive)
|
|
1519
|
+
}
|
|
1520
|
+
return runRequest(adaptive)
|
|
1521
|
+
}
|
|
1522
|
+
)
|
|
1523
|
+
}
|
|
1524
|
+
return Duration.isZero(delay) ? runAdaptive() : Effect.flatMap(Effect.sleep(delay), runAdaptive)
|
|
1427
1525
|
}
|
|
1428
1526
|
)
|
|
1429
|
-
})
|
|
1527
|
+
});
|
|
1430
1528
|
})
|
|
1431
1529
|
|
|
1432
1530
|
interface RateLimiterState {
|
|
@@ -1476,13 +1574,6 @@ const parseRateLimitWindow = (
|
|
|
1476
1574
|
clock: Clock,
|
|
1477
1575
|
headers: Headers.Headers
|
|
1478
1576
|
): Duration.Duration | undefined => {
|
|
1479
|
-
const retryAfter = parseRetryAfter(
|
|
1480
|
-
clock,
|
|
1481
|
-
getHeader(headers, "retry-after")
|
|
1482
|
-
)
|
|
1483
|
-
if (retryAfter !== undefined) {
|
|
1484
|
-
return retryAfter
|
|
1485
|
-
}
|
|
1486
1577
|
const resetAfter = parseResetAfter(getHeader(headers, "ratelimit-reset-after", "x-ratelimit-reset-after"))
|
|
1487
1578
|
if (resetAfter !== undefined) {
|
|
1488
1579
|
return resetAfter
|
|
@@ -17,6 +17,7 @@ import * as Duration from "../../Duration.ts"
|
|
|
17
17
|
import * as Effect from "../../Effect.ts"
|
|
18
18
|
import type * as Exit from "../../Exit.ts"
|
|
19
19
|
import { flow } from "../../Function.ts"
|
|
20
|
+
import { renderPrettyError } from "../../internal/effect.ts"
|
|
20
21
|
import * as Layer from "../../Layer.ts"
|
|
21
22
|
import * as Option from "../../Option.ts"
|
|
22
23
|
import type * as Scope from "../../Scope.ts"
|
|
@@ -311,7 +312,7 @@ const makeOtlpSpan = (self: SpanImpl): OtlpSpan => {
|
|
|
311
312
|
{
|
|
312
313
|
"key": "exception.stacktrace",
|
|
313
314
|
"value": {
|
|
314
|
-
"stringValue": error
|
|
315
|
+
"stringValue": renderPrettyError(error) ?? "No stack trace available"
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
318
|
]
|