effect 4.1.1 → 4.1.2
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/ai-docs/package.json +2 -2
- package/dist/ByteSize.d.ts.map +1 -1
- package/dist/ByteSize.js +27 -14
- package/dist/ByteSize.js.map +1 -1
- package/dist/Iterable.d.ts.map +1 -1
- package/dist/Iterable.js +27 -24
- package/dist/Iterable.js.map +1 -1
- package/dist/JsonSchema.d.ts.map +1 -1
- package/dist/JsonSchema.js +27 -6
- package/dist/JsonSchema.js.map +1 -1
- package/dist/MutableRef.d.ts.map +1 -1
- package/dist/MutableRef.js +0 -7
- package/dist/MutableRef.js.map +1 -1
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +0 -8
- package/dist/Ref.js.map +1 -1
- package/dist/Schedule.d.ts.map +1 -1
- package/dist/Schedule.js +8 -3
- package/dist/Schedule.js.map +1 -1
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +6 -5
- package/dist/Stream.js.map +1 -1
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +61 -33
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +4 -3
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/structured-output.js +11 -2
- package/dist/ai/internal/structured-output.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +18 -9
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/Entity.d.ts +26 -0
- package/dist/cluster/Entity.d.ts.map +1 -1
- package/dist/cluster/Entity.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts +6 -6
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +1 -3
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Runners.d.ts.map +1 -1
- package/dist/cluster/Runners.js +13 -2
- package/dist/cluster/Runners.js.map +1 -1
- package/dist/cluster/Sharding.d.ts +13 -0
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/internal/clusterAbandon.d.ts.map +1 -1
- package/dist/cluster/internal/clusterAbandon.js +6 -4
- package/dist/cluster/internal/clusterAbandon.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +48 -4
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/cluster/internal/transactionNotifications.d.ts +5 -0
- package/dist/cluster/internal/transactionNotifications.d.ts.map +1 -0
- package/dist/cluster/internal/transactionNotifications.js +5 -0
- package/dist/cluster/internal/transactionNotifications.js.map +1 -0
- package/dist/encoding/Json5.d.ts +44 -0
- package/dist/encoding/Json5.d.ts.map +1 -0
- package/dist/encoding/Json5.js +56 -0
- package/dist/encoding/Json5.js.map +1 -0
- package/dist/encoding/Jsonc.d.ts +47 -0
- package/dist/encoding/Jsonc.d.ts.map +1 -0
- package/dist/encoding/Jsonc.js +120 -0
- package/dist/encoding/Jsonc.js.map +1 -0
- package/dist/encoding/SchemaBinary.d.ts +94 -0
- package/dist/encoding/SchemaBinary.d.ts.map +1 -1
- package/dist/encoding/SchemaBinary.js +219 -6
- package/dist/encoding/SchemaBinary.js.map +1 -1
- package/dist/encoding/index.d.ts +10 -0
- package/dist/encoding/index.d.ts.map +1 -1
- package/dist/encoding/index.js +10 -0
- package/dist/encoding/index.js.map +1 -1
- package/dist/eventlog/EventLogServer.d.ts.map +1 -1
- package/dist/eventlog/EventLogServer.js +28 -12
- package/dist/eventlog/EventLogServer.js.map +1 -1
- package/dist/http/HttpClient.d.ts +15 -9
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +18 -11
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/internal/dateTime.js +17 -3
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/jsonParser.d.ts +5 -0
- package/dist/internal/jsonParser.d.ts.map +1 -0
- package/dist/internal/jsonParser.js +280 -0
- package/dist/internal/jsonParser.js.map +1 -0
- package/dist/internal/persistence.js +8 -1
- package/dist/internal/persistence.js.map +1 -1
- package/dist/internal/sql.js +16 -0
- package/dist/internal/sql.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/observability/OtlpExporter.d.ts +4 -0
- package/dist/observability/OtlpExporter.d.ts.map +1 -1
- package/dist/observability/OtlpExporter.js +40 -12
- package/dist/observability/OtlpExporter.js.map +1 -1
- package/dist/persistence/RateLimiter.d.ts +7 -0
- package/dist/persistence/RateLimiter.d.ts.map +1 -1
- package/dist/persistence/RateLimiter.js +13 -4
- package/dist/persistence/RateLimiter.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +10 -1
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +8 -0
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +36 -12
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/sql/Statement.d.ts +5 -0
- package/dist/sql/Statement.d.ts.map +1 -1
- package/dist/sql/Statement.js +8 -6
- package/dist/sql/Statement.js.map +1 -1
- package/package.json +4 -4
- package/src/ByteSize.ts +29 -14
- package/src/Iterable.ts +20 -14
- package/src/JsonSchema.ts +45 -7
- package/src/MutableRef.ts +1 -8
- package/src/Ref.ts +0 -8
- package/src/Schedule.ts +8 -6
- package/src/Stream.ts +6 -5
- package/src/ai/McpServer.ts +66 -43
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +4 -5
- package/src/ai/internal/structured-output.ts +10 -4
- package/src/cluster/ClusterWorkflowEngine.ts +29 -28
- package/src/cluster/Entity.ts +52 -0
- package/src/cluster/MessageStorage.ts +3 -6
- package/src/cluster/Runners.ts +16 -2
- package/src/cluster/Sharding.ts +13 -0
- package/src/cluster/internal/clusterAbandon.ts +7 -7
- package/src/cluster/internal/entityManager.ts +65 -5
- package/src/cluster/internal/transactionNotifications.ts +7 -0
- package/src/encoding/Json5.ts +59 -0
- package/src/encoding/Jsonc.ts +126 -0
- package/src/encoding/SchemaBinary.ts +228 -7
- package/src/encoding/index.ts +12 -0
- package/src/eventlog/EventLogServer.ts +36 -12
- package/src/http/HttpClient.ts +34 -22
- package/src/internal/dateTime.ts +26 -3
- package/src/internal/jsonParser.ts +307 -0
- package/src/internal/persistence.ts +9 -1
- package/src/internal/sql.ts +25 -0
- package/src/internal/version.ts +1 -1
- package/src/observability/OtlpExporter.ts +42 -18
- package/src/persistence/RateLimiter.ts +13 -4
- package/src/rpc/RpcServer.ts +12 -1
- package/src/sql/SqlClient.ts +48 -14
- package/src/sql/Statement.ts +8 -13
|
@@ -972,6 +972,139 @@ export function fieldId(id: number) {
|
|
|
972
972
|
|
|
973
973
|
const FIELD_ID_ANNOTATION_KEY = "~effect/encoding/SchemaBinary/fieldId"
|
|
974
974
|
|
|
975
|
+
// Marks the check that opts a number or bigint into a fixed-width encoding.
|
|
976
|
+
// It lives on a check, not the AST, so later annotations and checks keep it.
|
|
977
|
+
const FIXED_WIDTH_ANNOTATION_KEY = "~effect/encoding/SchemaBinary/fixedWidth"
|
|
978
|
+
|
|
979
|
+
type FixedWidthKind = "i32" | "u32" | "i64" | "u64"
|
|
980
|
+
|
|
981
|
+
const BIGINT_I64_MIN = BigInt("-9223372036854775808")
|
|
982
|
+
const BIGINT_I64_MAX = BigInt("9223372036854775807")
|
|
983
|
+
const BIGINT_U64_MAX = BigInt("18446744073709551615")
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* A 32-bit signed integer written as four little-endian bytes.
|
|
987
|
+
*
|
|
988
|
+
* **When to use**
|
|
989
|
+
*
|
|
990
|
+
* Use when values regularly need more than four bytes as a varint, such as
|
|
991
|
+
* hashes or full-range ids. Small values are cheaper as `Schema.Int`.
|
|
992
|
+
*
|
|
993
|
+
* **Details**
|
|
994
|
+
*
|
|
995
|
+
* Rejects non-integers and values outside -2147483648 through 2147483647
|
|
996
|
+
* instead of truncating them. `-0` is written as `0`.
|
|
997
|
+
*
|
|
998
|
+
* **Gotchas**
|
|
999
|
+
*
|
|
1000
|
+
* Switching a field between this, {@link uint32}, `Schema.Number` or
|
|
1001
|
+
* `Schema.Int` changes its encoding, so it is not a compatible evolution. In
|
|
1002
|
+
* default mode the other reader can decode a different value without an error,
|
|
1003
|
+
* for example after a signedness swap or inside an array of structs.
|
|
1004
|
+
* Fingerprint mode rejects the layout mismatch.
|
|
1005
|
+
*
|
|
1006
|
+
* **Example** (Declaring fixed-width integer fields)
|
|
1007
|
+
*
|
|
1008
|
+
* ```ts
|
|
1009
|
+
* import { Schema } from "effect"
|
|
1010
|
+
* import { SchemaBinary } from "effect/encoding"
|
|
1011
|
+
*
|
|
1012
|
+
* const Packet = Schema.Struct({
|
|
1013
|
+
* version: SchemaBinary.uint32,
|
|
1014
|
+
* offset: SchemaBinary.int32,
|
|
1015
|
+
* sequence: SchemaBinary.uint64,
|
|
1016
|
+
* timestamp: SchemaBinary.int64
|
|
1017
|
+
* })
|
|
1018
|
+
* ```
|
|
1019
|
+
*
|
|
1020
|
+
* @stability unstable
|
|
1021
|
+
* @category schemas
|
|
1022
|
+
* @since 4.0.0
|
|
1023
|
+
*/
|
|
1024
|
+
export const int32: Schema.Number = Schema.Number.check(
|
|
1025
|
+
Schema.isInt32({ [FIXED_WIDTH_ANNOTATION_KEY]: "i32" })
|
|
1026
|
+
)
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* A 32-bit unsigned integer written as four little-endian bytes.
|
|
1030
|
+
*
|
|
1031
|
+
* **Details**
|
|
1032
|
+
*
|
|
1033
|
+
* Rejects non-integers and values outside 0 through 4294967295 instead of
|
|
1034
|
+
* wrapping them. See {@link int32} for when a fixed width pays off.
|
|
1035
|
+
*
|
|
1036
|
+
* @stability unstable
|
|
1037
|
+
* @category schemas
|
|
1038
|
+
* @since 4.0.0
|
|
1039
|
+
*/
|
|
1040
|
+
export const uint32: Schema.Number = Schema.Number.check(
|
|
1041
|
+
Schema.isUint32({ [FIXED_WIDTH_ANNOTATION_KEY]: "u32" })
|
|
1042
|
+
)
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* A 64-bit signed integer, as a `bigint`, written as eight little-endian bytes.
|
|
1046
|
+
*
|
|
1047
|
+
* **Details**
|
|
1048
|
+
*
|
|
1049
|
+
* Rejects values outside -2^63 through 2^63 - 1 instead of wrapping them. As a
|
|
1050
|
+
* struct field it costs one length byte more than the payload, because the
|
|
1051
|
+
* eight-byte field kind belongs to floating-point numbers.
|
|
1052
|
+
*
|
|
1053
|
+
* **Gotchas**
|
|
1054
|
+
*
|
|
1055
|
+
* Switching a field between this, {@link uint64} or `Schema.BigInt` is not a
|
|
1056
|
+
* compatible evolution. All three are length-prefixed struct fields, and eight
|
|
1057
|
+
* bytes can be valid for each, so a default-mode reader may decode the other
|
|
1058
|
+
* form as a different value without an error. Fingerprint mode rejects the
|
|
1059
|
+
* layout mismatch.
|
|
1060
|
+
*
|
|
1061
|
+
* @stability unstable
|
|
1062
|
+
* @category schemas
|
|
1063
|
+
* @since 4.0.0
|
|
1064
|
+
*/
|
|
1065
|
+
export const int64: Schema.BigInt = Schema.BigInt.check(
|
|
1066
|
+
Schema.isBetweenBigInt({ minimum: BIGINT_I64_MIN, maximum: BIGINT_I64_MAX }, {
|
|
1067
|
+
expected: "a 64-bit integer",
|
|
1068
|
+
[FIXED_WIDTH_ANNOTATION_KEY]: "i64"
|
|
1069
|
+
})
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* A 64-bit unsigned integer, as a `bigint`, written as eight little-endian
|
|
1074
|
+
* bytes.
|
|
1075
|
+
*
|
|
1076
|
+
* **Details**
|
|
1077
|
+
*
|
|
1078
|
+
* Rejects values outside 0 through 2^64 - 1 instead of wrapping them. See
|
|
1079
|
+
* {@link int64} for the struct field cost.
|
|
1080
|
+
*
|
|
1081
|
+
* **Gotchas**
|
|
1082
|
+
*
|
|
1083
|
+
* Switching a field between this, {@link int64} or `Schema.BigInt` can decode
|
|
1084
|
+
* as a different value without an error in default mode; see {@link int64}.
|
|
1085
|
+
*
|
|
1086
|
+
* @stability unstable
|
|
1087
|
+
* @category schemas
|
|
1088
|
+
* @since 4.0.0
|
|
1089
|
+
*/
|
|
1090
|
+
export const uint64: Schema.BigInt = Schema.BigInt.check(
|
|
1091
|
+
Schema.isBetweenBigInt({ minimum: BigInt(0), maximum: BIGINT_U64_MAX }, {
|
|
1092
|
+
expected: "a 64-bit unsigned integer",
|
|
1093
|
+
[FIXED_WIDTH_ANNOTATION_KEY]: "u64"
|
|
1094
|
+
})
|
|
1095
|
+
)
|
|
1096
|
+
|
|
1097
|
+
// The fixed width the schema opted into, from any of its checks.
|
|
1098
|
+
function fixedWidthKind(ast: SchemaAST.AST): FixedWidthKind | undefined {
|
|
1099
|
+
const checks = ast.checks
|
|
1100
|
+
if (checks === undefined) return undefined
|
|
1101
|
+
for (const check of checks) {
|
|
1102
|
+
const kind = check.annotations?.[FIXED_WIDTH_ANNOTATION_KEY]
|
|
1103
|
+
if (kind !== undefined) return kind as FixedWidthKind
|
|
1104
|
+
}
|
|
1105
|
+
return undefined
|
|
1106
|
+
}
|
|
1107
|
+
|
|
975
1108
|
// Bit 0 selects fingerprint mode; all other flag bits are reserved.
|
|
976
1109
|
const ENVELOPE = 0x20 // version nibble 2, flags 0
|
|
977
1110
|
const ENVELOPE_FINGERPRINT = 0x21 // version nibble 2, flag bit 0
|
|
@@ -1370,6 +1503,11 @@ class Writer {
|
|
|
1370
1503
|
this.view.setBigInt64(this.start + this.len, n, true)
|
|
1371
1504
|
this.len += 8
|
|
1372
1505
|
}
|
|
1506
|
+
u64(n: bigint) {
|
|
1507
|
+
this.ensure(8)
|
|
1508
|
+
this.view.setBigUint64(this.start + this.len, n, true)
|
|
1509
|
+
this.len += 8
|
|
1510
|
+
}
|
|
1373
1511
|
u32le(n: number) {
|
|
1374
1512
|
this.ensure(4)
|
|
1375
1513
|
this.view.setUint32(this.start + this.len, n >>> 0, true)
|
|
@@ -1801,6 +1939,12 @@ class Reader {
|
|
|
1801
1939
|
this.pos += 8
|
|
1802
1940
|
return value
|
|
1803
1941
|
}
|
|
1942
|
+
u64(): bigint {
|
|
1943
|
+
if (this.pos + 8 > this.end) invalid("complete value", undefined, this.options)
|
|
1944
|
+
const value = (this.view ?? this.dataView()).getBigUint64(this.pos, true)
|
|
1945
|
+
this.pos += 8
|
|
1946
|
+
return value
|
|
1947
|
+
}
|
|
1804
1948
|
u32le(): number {
|
|
1805
1949
|
if (this.pos + 4 > this.end) invalid("complete value", undefined, this.options)
|
|
1806
1950
|
const value = (this.view ?? this.dataView()).getUint32(this.pos, true)
|
|
@@ -1825,6 +1969,7 @@ type LeafKind =
|
|
|
1825
1969
|
| "symbol"
|
|
1826
1970
|
| "bytes"
|
|
1827
1971
|
| "bigint"
|
|
1972
|
+
| FixedWidthKind
|
|
1828
1973
|
| "json"
|
|
1829
1974
|
| "duration"
|
|
1830
1975
|
| "bigDecimal"
|
|
@@ -2007,7 +2152,12 @@ function packedSize(layout: Layout): number | undefined {
|
|
|
2007
2152
|
return packedSize(layout.leaf)
|
|
2008
2153
|
case "bool":
|
|
2009
2154
|
return 1
|
|
2155
|
+
case "i32":
|
|
2156
|
+
case "u32":
|
|
2157
|
+
return 4
|
|
2010
2158
|
case "int64":
|
|
2159
|
+
case "i64":
|
|
2160
|
+
case "u64":
|
|
2011
2161
|
return 8
|
|
2012
2162
|
default:
|
|
2013
2163
|
return undefined
|
|
@@ -2318,10 +2468,15 @@ function compileLayout(root: SchemaAST.AST): CompiledLayout {
|
|
|
2318
2468
|
case "Undefined":
|
|
2319
2469
|
case "Void":
|
|
2320
2470
|
return leaf("undefined")
|
|
2321
|
-
case "Number":
|
|
2471
|
+
case "Number": {
|
|
2472
|
+
const fixed = fixedWidthKind(ast)
|
|
2473
|
+
if (fixed === "i32" || fixed === "u32") return leaf(fixed)
|
|
2322
2474
|
return leaf(provesInteger(ast) ? "int" : "number")
|
|
2323
|
-
|
|
2324
|
-
|
|
2475
|
+
}
|
|
2476
|
+
case "BigInt": {
|
|
2477
|
+
const fixed = fixedWidthKind(ast)
|
|
2478
|
+
return leaf(fixed === "i64" || fixed === "u64" ? fixed : "bigint")
|
|
2479
|
+
}
|
|
2325
2480
|
case "Literal":
|
|
2326
2481
|
return literalLayout(leaf(literalKind(ast.literal)), [ast.literal])
|
|
2327
2482
|
case "Unknown":
|
|
@@ -2685,7 +2840,7 @@ function isExact(root: SchemaAST.AST): boolean {
|
|
|
2685
2840
|
const walked = new Map<SchemaAST.AST, boolean>()
|
|
2686
2841
|
const exact = (ast: SchemaAST.AST): boolean => {
|
|
2687
2842
|
if (
|
|
2688
|
-
ast.encoding !== undefined || ast.checks !== undefined ||
|
|
2843
|
+
ast.encoding !== undefined || (ast.checks !== undefined && !isFixedWidthOnly(ast)) ||
|
|
2689
2844
|
(ast as { readonly encodingChecks?: SchemaAST.Checks }).encodingChecks !== undefined
|
|
2690
2845
|
) {
|
|
2691
2846
|
return false
|
|
@@ -2746,6 +2901,13 @@ function isExact(root: SchemaAST.AST): boolean {
|
|
|
2746
2901
|
return exact(root)
|
|
2747
2902
|
}
|
|
2748
2903
|
|
|
2904
|
+
// The binary layer enforces a fixed-width check on its own, so it is exact
|
|
2905
|
+
// when no other check rides along.
|
|
2906
|
+
function isFixedWidthOnly(ast: SchemaAST.AST): boolean {
|
|
2907
|
+
return (ast._tag === "Number" || ast._tag === "BigInt") && ast.checks?.length === 1 &&
|
|
2908
|
+
fixedWidthKind(ast) !== undefined
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2749
2911
|
// A clean Exit whose success schema is exact: the binary layer fully produces
|
|
2750
2912
|
// and validates success exits, so only failure exits need the schema pass for
|
|
2751
2913
|
// their cause's error and defect encodings.
|
|
@@ -2784,7 +2946,11 @@ const F = {
|
|
|
2784
2946
|
exit: 22,
|
|
2785
2947
|
cause: 23,
|
|
2786
2948
|
causeReason: 24,
|
|
2787
|
-
literal: 25
|
|
2949
|
+
literal: 25,
|
|
2950
|
+
i32: 26,
|
|
2951
|
+
u32: 27,
|
|
2952
|
+
i64: 28,
|
|
2953
|
+
u64: 29
|
|
2788
2954
|
} as const
|
|
2789
2955
|
|
|
2790
2956
|
// Hash the compiled layout graph, including cycle back-edge distances.
|
|
@@ -2828,6 +2994,10 @@ function layoutFingerprint(root: Layout): bigint {
|
|
|
2828
2994
|
case "symbol":
|
|
2829
2995
|
case "bytes":
|
|
2830
2996
|
case "bigint":
|
|
2997
|
+
case "i32":
|
|
2998
|
+
case "u32":
|
|
2999
|
+
case "i64":
|
|
3000
|
+
case "u64":
|
|
2831
3001
|
case "json":
|
|
2832
3002
|
case "duration":
|
|
2833
3003
|
case "bigDecimal":
|
|
@@ -2986,12 +3156,16 @@ function matchesLayout(layout: Layout, value: unknown): boolean {
|
|
|
2986
3156
|
return value === undefined
|
|
2987
3157
|
case "number":
|
|
2988
3158
|
case "int":
|
|
3159
|
+
case "i32":
|
|
3160
|
+
case "u32":
|
|
2989
3161
|
return typeof value === "number"
|
|
2990
3162
|
case "string":
|
|
2991
3163
|
return typeof value === "string"
|
|
2992
3164
|
case "symbol":
|
|
2993
3165
|
return typeof value === "symbol"
|
|
2994
3166
|
case "bigint":
|
|
3167
|
+
case "i64":
|
|
3168
|
+
case "u64":
|
|
2995
3169
|
return typeof value === "bigint"
|
|
2996
3170
|
case "bytes":
|
|
2997
3171
|
return value instanceof Uint8Array
|
|
@@ -3287,6 +3461,10 @@ function valueWireCode(layout: Layout, value: unknown, options: SchemaAST.ParseO
|
|
|
3287
3461
|
case "int":
|
|
3288
3462
|
if (!Number.isSafeInteger(value)) encodeFail("an integer", value, options)
|
|
3289
3463
|
return FIELD_WIRE_VARINT
|
|
3464
|
+
// The payload write validates the value.
|
|
3465
|
+
case "i32":
|
|
3466
|
+
case "u32":
|
|
3467
|
+
return FIELD_WIRE_FIXED32
|
|
3290
3468
|
default:
|
|
3291
3469
|
return FIELD_WIRE_SIZED
|
|
3292
3470
|
}
|
|
@@ -3295,8 +3473,8 @@ function valueWireCode(layout: Layout, value: unknown, options: SchemaAST.ParseO
|
|
|
3295
3473
|
function encodeWirePayload(ctx: EncodeContext, layout: Layout, value: unknown, wire: number, w: Writer) {
|
|
3296
3474
|
const kind = wire % FIELD_WIRE_FACTOR
|
|
3297
3475
|
switch (kind) {
|
|
3298
|
-
// EMPTY
|
|
3299
|
-
//
|
|
3476
|
+
// EMPTY is decoder-side skip support only; `valueWireCode` never produces
|
|
3477
|
+
// it.
|
|
3300
3478
|
case FIELD_WIRE_FALSE:
|
|
3301
3479
|
case FIELD_WIRE_TRUE:
|
|
3302
3480
|
return
|
|
@@ -3306,6 +3484,9 @@ function encodeWirePayload(ctx: EncodeContext, layout: Layout, value: unknown, w
|
|
|
3306
3484
|
case FIELD_WIRE_FIXED64:
|
|
3307
3485
|
w.f64(value as number)
|
|
3308
3486
|
return
|
|
3487
|
+
case FIELD_WIRE_FIXED32:
|
|
3488
|
+
encodeValue(ctx, layout, value, w)
|
|
3489
|
+
return
|
|
3309
3490
|
case FIELD_WIRE_DECIMAL: {
|
|
3310
3491
|
const scale = Math.floor(wire / FIELD_WIRE_FACTOR)
|
|
3311
3492
|
w.numberVarint(Math.round((value as number) * POW10[scale]))
|
|
@@ -4010,6 +4191,29 @@ function encodeValue(ctx: EncodeContext, layout: Layout, value: unknown, w: Writ
|
|
|
4010
4191
|
if (typeof value !== "bigint") encodeFail("a bigint", value, ctx.options)
|
|
4011
4192
|
w.zigzag(value)
|
|
4012
4193
|
return
|
|
4194
|
+
// `disableChecks` can bypass the range check used to select these layouts.
|
|
4195
|
+
case "i32":
|
|
4196
|
+
if (typeof value !== "number" || (value | 0) !== value) encodeFail("a 32-bit integer", value, ctx.options)
|
|
4197
|
+
w.i32le(value)
|
|
4198
|
+
return
|
|
4199
|
+
case "u32":
|
|
4200
|
+
if (typeof value !== "number" || value >>> 0 !== value) {
|
|
4201
|
+
encodeFail("a 32-bit unsigned integer", value, ctx.options)
|
|
4202
|
+
}
|
|
4203
|
+
w.u32le(value)
|
|
4204
|
+
return
|
|
4205
|
+
case "i64":
|
|
4206
|
+
if (typeof value !== "bigint" || value < BIGINT_I64_MIN || value > BIGINT_I64_MAX) {
|
|
4207
|
+
encodeFail("a 64-bit integer", value, ctx.options)
|
|
4208
|
+
}
|
|
4209
|
+
w.i64(value)
|
|
4210
|
+
return
|
|
4211
|
+
case "u64":
|
|
4212
|
+
if (typeof value !== "bigint" || value < BIGINT_ZERO || value > BIGINT_U64_MAX) {
|
|
4213
|
+
encodeFail("a 64-bit unsigned integer", value, ctx.options)
|
|
4214
|
+
}
|
|
4215
|
+
w.u64(value)
|
|
4216
|
+
return
|
|
4013
4217
|
case "int64": {
|
|
4014
4218
|
if (!matchesLayout(layout, value)) {
|
|
4015
4219
|
encodeFail(layout.flavor === "date" ? "a Date" : "a DateTime.Utc", value, ctx.options)
|
|
@@ -4375,6 +4579,9 @@ function fieldWireMask(layout: Layout): number {
|
|
|
4375
4579
|
return (1 << FIELD_WIRE_VARINT) | (1 << FIELD_WIRE_FIXED64) | (1 << FIELD_WIRE_DECIMAL)
|
|
4376
4580
|
case "int":
|
|
4377
4581
|
return 1 << FIELD_WIRE_VARINT
|
|
4582
|
+
case "i32":
|
|
4583
|
+
case "u32":
|
|
4584
|
+
return 1 << FIELD_WIRE_FIXED32
|
|
4378
4585
|
default:
|
|
4379
4586
|
return 1 << FIELD_WIRE_SIZED
|
|
4380
4587
|
}
|
|
@@ -4406,6 +4613,8 @@ function decodeFieldPayload(layout: Layout, wireMask: number, kind: number, r: R
|
|
|
4406
4613
|
skipFieldPayload(kind, r)
|
|
4407
4614
|
return ABSENT
|
|
4408
4615
|
}
|
|
4616
|
+
// Only the two 32-bit layouts accept this kind, so the payload needs no window.
|
|
4617
|
+
if (kind === FIELD_WIRE_FIXED32) return layout._ === "u32" ? r.u32le() : r.i32le()
|
|
4409
4618
|
if (kind !== FIELD_WIRE_SIZED) {
|
|
4410
4619
|
const value = decodeScalarField(kind, r)
|
|
4411
4620
|
if (layout._ === "literal" && !hasLiteral(layout, value)) {
|
|
@@ -4993,6 +5202,18 @@ function decodeValueUnchecked(layout: Layout, r: Reader): unknown {
|
|
|
4993
5202
|
return r.takeCopy(r.end - r.pos)
|
|
4994
5203
|
case "bigint":
|
|
4995
5204
|
return r.zigzag()
|
|
5205
|
+
case "i32":
|
|
5206
|
+
if (r.remaining !== 4) invalid("a 32-bit integer", undefined, r.options)
|
|
5207
|
+
return r.i32le()
|
|
5208
|
+
case "u32":
|
|
5209
|
+
if (r.remaining !== 4) invalid("a 32-bit unsigned integer", undefined, r.options)
|
|
5210
|
+
return r.u32le()
|
|
5211
|
+
case "i64":
|
|
5212
|
+
if (r.remaining !== 8) invalid("a 64-bit integer", undefined, r.options)
|
|
5213
|
+
return r.i64()
|
|
5214
|
+
case "u64":
|
|
5215
|
+
if (r.remaining !== 8) invalid("a 64-bit unsigned integer", undefined, r.options)
|
|
5216
|
+
return r.u64()
|
|
4996
5217
|
case "int64": {
|
|
4997
5218
|
if (r.remaining !== 8) invalid("int64", undefined, r.options)
|
|
4998
5219
|
const millis = Number(r.i64())
|
package/src/encoding/index.ts
CHANGED
|
@@ -41,12 +41,24 @@ export * as Hex from "./Hex.ts"
|
|
|
41
41
|
*/
|
|
42
42
|
export * as Ini from "./Ini.ts"
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @stability unstable
|
|
46
|
+
* @since 4.1.2
|
|
47
|
+
*/
|
|
48
|
+
export * as Json5 from "./Json5.ts"
|
|
49
|
+
|
|
44
50
|
/**
|
|
45
51
|
* @stability unstable
|
|
46
52
|
* @since 4.0.6
|
|
47
53
|
*/
|
|
48
54
|
export * as JsonArray from "./JsonArray.ts"
|
|
49
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @stability unstable
|
|
58
|
+
* @since 4.1.2
|
|
59
|
+
*/
|
|
60
|
+
export * as Jsonc from "./Jsonc.ts"
|
|
61
|
+
|
|
50
62
|
/**
|
|
51
63
|
* @stability unstable
|
|
52
64
|
* @since 4.0.0
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import type { NonEmptyReadonlyArray } from "../Array.ts"
|
|
13
13
|
import * as Arr from "../Array.ts"
|
|
14
14
|
import * as Cache from "../Cache.ts"
|
|
15
|
+
import * as Clock from "../Clock.ts"
|
|
15
16
|
import * as Context from "../Context.ts"
|
|
16
17
|
import * as Mac from "../crypto/Mac.ts"
|
|
17
18
|
import * as Data from "../Data.ts"
|
|
@@ -22,7 +23,6 @@ import * as Exit from "../Exit.ts"
|
|
|
22
23
|
import * as Hash from "../Hash.ts"
|
|
23
24
|
import * as Uuid from "../internal/uuid.ts"
|
|
24
25
|
import * as Layer from "../Layer.ts"
|
|
25
|
-
import * as Option from "../Option.ts"
|
|
26
26
|
import * as Redacted from "../Redacted.ts"
|
|
27
27
|
import type * as Rpc from "../rpc/Rpc.ts"
|
|
28
28
|
import type * as RpcGroup from "../rpc/RpcGroup.ts"
|
|
@@ -119,11 +119,6 @@ export const layerRpcHandlers = (options: {
|
|
|
119
119
|
Rpc.ToHandler<RpcGroup.Rpcs<typeof EventLogRemoteRpcs>> | EventLogAuthentication
|
|
120
120
|
> =>
|
|
121
121
|
EventLogRemoteRpcs.toLayer(Effect.gen(function*() {
|
|
122
|
-
const clientChallenges = yield* Cache.make({
|
|
123
|
-
lookup: (_clientId: number) => Effect.orDie(EventLogSessionAuth.makeSessionAuthChallenge),
|
|
124
|
-
capacity: Number.MAX_SAFE_INTEGER,
|
|
125
|
-
timeToLive: EventLogSessionAuth.SessionAuthChallengeTimeToLiveMillis
|
|
126
|
-
})
|
|
127
122
|
const toFrames = makeFramer()
|
|
128
123
|
|
|
129
124
|
const persistedSigningPublicKeys = yield* Cache.makeWith(
|
|
@@ -148,17 +143,31 @@ export const layerRpcHandlers = (options: {
|
|
|
148
143
|
|
|
149
144
|
return EventLogRemoteRpcs.of({
|
|
150
145
|
"EventLog.Hello": Effect.fnUntraced(function*(_, { client }) {
|
|
151
|
-
|
|
146
|
+
// Kept on the connection, so it is released when the client disconnects
|
|
147
|
+
// and shared by overlapping Hellos until `Authenticate` consumes it.
|
|
148
|
+
let pending = getPendingChallenge(client, yield* Clock.currentTimeMillis)
|
|
149
|
+
if (pending === undefined) {
|
|
150
|
+
const challenge = yield* Effect.orDie(EventLogSessionAuth.makeSessionAuthChallenge)
|
|
151
|
+
// Read the clock again, as generating the challenge can suspend. A
|
|
152
|
+
// concurrent Hello may have stored a challenge in the meantime.
|
|
153
|
+
const now = yield* Clock.currentTimeMillis
|
|
154
|
+
pending = getPendingChallenge(client, now) ?? {
|
|
155
|
+
challenge,
|
|
156
|
+
expiresAt: now + EventLogSessionAuth.SessionAuthChallengeTimeToLiveMillis
|
|
157
|
+
}
|
|
158
|
+
client.annotate(PendingChallenge, pending)
|
|
159
|
+
}
|
|
152
160
|
return new HelloResponse({
|
|
153
161
|
remoteId: options.remoteId,
|
|
154
|
-
challenge,
|
|
162
|
+
challenge: pending.challenge,
|
|
155
163
|
firstSequence: options.firstSequence,
|
|
156
164
|
compaction: options.compaction === true ? true : undefined
|
|
157
165
|
})
|
|
158
166
|
}),
|
|
159
167
|
"EventLog.Authenticate": Effect.fnUntraced(function*(request, { client }) {
|
|
160
|
-
const
|
|
161
|
-
|
|
168
|
+
const pending = getPendingChallenge(client, yield* Clock.currentTimeMillis)
|
|
169
|
+
client.annotate(PendingChallenge, undefined)
|
|
170
|
+
if (pending === undefined) {
|
|
162
171
|
return yield* new EventLogProtocolError({
|
|
163
172
|
requestTag: "Authenticate",
|
|
164
173
|
publicKey: request.publicKey,
|
|
@@ -166,13 +175,12 @@ export const layerRpcHandlers = (options: {
|
|
|
166
175
|
message: "Session auth challenge has expired"
|
|
167
176
|
})
|
|
168
177
|
}
|
|
169
|
-
yield* Cache.invalidate(clientChallenges, client.id)
|
|
170
178
|
if (options.authorizeIdentity) {
|
|
171
179
|
yield* options.authorizeIdentity(request.publicKey, request.signingPublicKey)
|
|
172
180
|
}
|
|
173
181
|
const verified = yield* EventLogSessionAuth.verifySessionAuthenticateRequest({
|
|
174
182
|
remoteId: options.remoteId,
|
|
175
|
-
challenge,
|
|
183
|
+
challenge: pending.challenge,
|
|
176
184
|
publicKey: request.publicKey,
|
|
177
185
|
signingPublicKey: request.signingPublicKey,
|
|
178
186
|
signature: request.signature,
|
|
@@ -409,6 +417,22 @@ class ConnectionWritesState extends Context.Service<ConnectionWritesState, Conne
|
|
|
409
417
|
"effect/eventlog/EventLogServer/ConnectionWrites"
|
|
410
418
|
) {}
|
|
411
419
|
|
|
420
|
+
// The connection's unconsumed `Hello` challenge, cleared by `Authenticate`.
|
|
421
|
+
const PendingChallenge = Context.Reference<
|
|
422
|
+
| {
|
|
423
|
+
readonly challenge: Uint8Array<ArrayBuffer>
|
|
424
|
+
readonly expiresAt: number
|
|
425
|
+
}
|
|
426
|
+
| undefined
|
|
427
|
+
>("effect/eventlog/EventLogServer/PendingChallenge", {
|
|
428
|
+
defaultValue: () => undefined
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
const getPendingChallenge = (client: Rpc.ServerClient, now: number) => {
|
|
432
|
+
const pending = Context.get(client.annotations, PendingChallenge)
|
|
433
|
+
return pending !== undefined && pending.expiresAt > now ? pending : undefined
|
|
434
|
+
}
|
|
435
|
+
|
|
412
436
|
const getConnectionWrites = (client: Rpc.ServerClient): ConnectionWrites => {
|
|
413
437
|
const existing = Context.getOrUndefined(client.annotations, ConnectionWritesState)
|
|
414
438
|
if (existing !== undefined) return existing
|
package/src/http/HttpClient.ts
CHANGED
|
@@ -2666,9 +2666,11 @@ export const tapRequest: {
|
|
|
2666
2666
|
* **Details**
|
|
2667
2667
|
*
|
|
2668
2668
|
* Response cookies are merged into the ref with `Cookies.mergeIntoArray` for
|
|
2669
|
-
* the response URL.
|
|
2670
|
-
* the cookies for
|
|
2671
|
-
*
|
|
2669
|
+
* the response URL. When a request is sent, `Cookies.toRequestCookieHeader`
|
|
2670
|
+
* selects the cookies for its final URL, after later transformations such as
|
|
2671
|
+
* `HttpClientRequest.prependUrl`, and sets them as the `cookie` header. With
|
|
2672
|
+
* `followRedirects` applied after `withCookiesRef`, this runs for every hop.
|
|
2673
|
+
* Expiry uses the current `Clock`.
|
|
2672
2674
|
*
|
|
2673
2675
|
* @stability unstable
|
|
2674
2676
|
* @category cookies
|
|
@@ -2687,9 +2689,11 @@ export const withCookiesRef: {
|
|
|
2687
2689
|
* **Details**
|
|
2688
2690
|
*
|
|
2689
2691
|
* Response cookies are merged into the ref with `Cookies.mergeIntoArray` for
|
|
2690
|
-
* the response URL.
|
|
2691
|
-
* the cookies for
|
|
2692
|
-
*
|
|
2692
|
+
* the response URL. When a request is sent, `Cookies.toRequestCookieHeader`
|
|
2693
|
+
* selects the cookies for its final URL, after later transformations such as
|
|
2694
|
+
* `HttpClientRequest.prependUrl`, and sets them as the `cookie` header. With
|
|
2695
|
+
* `followRedirects` applied after `withCookiesRef`, this runs for every hop.
|
|
2696
|
+
* Expiry uses the current `Clock`.
|
|
2693
2697
|
*
|
|
2694
2698
|
* @stability unstable
|
|
2695
2699
|
* @category cookies
|
|
@@ -2708,9 +2712,11 @@ export const withCookiesRef: {
|
|
|
2708
2712
|
* **Details**
|
|
2709
2713
|
*
|
|
2710
2714
|
* Response cookies are merged into the ref with `Cookies.mergeIntoArray` for
|
|
2711
|
-
* the response URL.
|
|
2712
|
-
* the cookies for
|
|
2713
|
-
*
|
|
2715
|
+
* the response URL. When a request is sent, `Cookies.toRequestCookieHeader`
|
|
2716
|
+
* selects the cookies for its final URL, after later transformations such as
|
|
2717
|
+
* `HttpClientRequest.prependUrl`, and sets them as the `cookie` header. With
|
|
2718
|
+
* `followRedirects` applied after `withCookiesRef`, this runs for every hop.
|
|
2719
|
+
* Expiry uses the current `Clock`.
|
|
2714
2720
|
*
|
|
2715
2721
|
* @stability unstable
|
|
2716
2722
|
* @category cookies
|
|
@@ -2722,21 +2728,27 @@ export const withCookiesRef: {
|
|
|
2722
2728
|
<E, R>(
|
|
2723
2729
|
self: HttpClient.With<E, R>,
|
|
2724
2730
|
ref: Ref.Ref<ReadonlyArray<Cookies.Cookie>>
|
|
2725
|
-
): HttpClient.With<E, R> =>
|
|
2726
|
-
|
|
2731
|
+
): HttpClient.With<E, R> => {
|
|
2732
|
+
const now = () => Fiber.getCurrent()!.getRef(Clock).currentTimeMillisUnsafe()
|
|
2733
|
+
const addCookies = (request: HttpClientRequest.HttpClientRequest) =>
|
|
2734
|
+
Effect.map(Ref.get(ref), (cookies) => {
|
|
2735
|
+
const header = Cookies.toRequestCookieHeader(cookies, request.url, now())
|
|
2736
|
+
if (header === undefined) return request
|
|
2737
|
+
const withCookies = HttpClientRequest.setHeader(request, "cookie", header)
|
|
2738
|
+
// setHeader returns a new request, so carry over the withScope controller
|
|
2739
|
+
const controller = scopedRequests.get(request)
|
|
2740
|
+
if (controller !== undefined) scopedRequests.set(withCookies, controller)
|
|
2741
|
+
return withCookies
|
|
2742
|
+
})
|
|
2743
|
+
return makeWith(
|
|
2727
2744
|
(request: Effect.Effect<HttpClientRequest.HttpClientRequest, E, R>) =>
|
|
2728
|
-
Effect.tap(
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
Effect.flatMap(self.preprocess(request), (request) =>
|
|
2734
|
-
Effect.map(Ref.get(ref), (cookies) => {
|
|
2735
|
-
const now = Fiber.getCurrent()!.getRef(Clock).currentTimeMillisUnsafe()
|
|
2736
|
-
const header = Cookies.toRequestCookieHeader(cookies, request.url, now)
|
|
2737
|
-
return header === undefined ? request : HttpClientRequest.setHeader(request, "cookie", header)
|
|
2738
|
-
}))
|
|
2745
|
+
Effect.tap(
|
|
2746
|
+
self.postprocess(Effect.flatMap(request, addCookies)),
|
|
2747
|
+
(response) => Ref.update(ref, Cookies.mergeIntoArray(response.url, response.cookies, now()))
|
|
2748
|
+
),
|
|
2749
|
+
self.preprocess
|
|
2739
2750
|
)
|
|
2751
|
+
}
|
|
2740
2752
|
)
|
|
2741
2753
|
|
|
2742
2754
|
/**
|
package/src/internal/dateTime.ts
CHANGED
|
@@ -1054,6 +1054,29 @@ export const subtract: {
|
|
|
1054
1054
|
return add(self, newParts)
|
|
1055
1055
|
})
|
|
1056
1056
|
|
|
1057
|
+
const mutateRounding = (
|
|
1058
|
+
self: DateTime.DateTime,
|
|
1059
|
+
part: DateTime.DateTime.UnitSingular,
|
|
1060
|
+
f: (date: Date) => void,
|
|
1061
|
+
disambiguation: DateTime.Disambiguation = "compatible"
|
|
1062
|
+
): DateTime.DateTime => {
|
|
1063
|
+
if (
|
|
1064
|
+
self._tag === "Utc" || self.zone._tag === "Offset" ||
|
|
1065
|
+
part === "day" || part === "week" || part === "month" || part === "year"
|
|
1066
|
+
) {
|
|
1067
|
+
return mutate(self, f, { disambiguation })
|
|
1068
|
+
}
|
|
1069
|
+
// keep the input's offset so rounding stays in the same occurrence of a repeated hour
|
|
1070
|
+
const date = toDate(self)
|
|
1071
|
+
const offset = date.getTime() - Math.trunc(self.epochMilliseconds)
|
|
1072
|
+
f(date)
|
|
1073
|
+
const adjustedMillis = date.getTime()
|
|
1074
|
+
const result = makeZonedProto(adjustedMillis - offset, self.zone)
|
|
1075
|
+
return zonedOffset(result) === offset
|
|
1076
|
+
? result
|
|
1077
|
+
: makeZonedFromAdjusted(adjustedMillis, self.zone, disambiguation)
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1057
1080
|
const startOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: {
|
|
1058
1081
|
readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
|
1059
1082
|
}) => {
|
|
@@ -1105,7 +1128,7 @@ export const startOf: {
|
|
|
1105
1128
|
}): A
|
|
1106
1129
|
} = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: {
|
|
1107
1130
|
readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
|
1108
|
-
}): DateTime.DateTime =>
|
|
1131
|
+
}): DateTime.DateTime => mutateRounding(self, part, (date) => startOfDate(date, part, options)))
|
|
1109
1132
|
|
|
1110
1133
|
const endOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: {
|
|
1111
1134
|
readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
|
@@ -1158,7 +1181,7 @@ export const endOf: {
|
|
|
1158
1181
|
}): A
|
|
1159
1182
|
} = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: {
|
|
1160
1183
|
readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
|
1161
|
-
}): DateTime.DateTime =>
|
|
1184
|
+
}): DateTime.DateTime => mutateRounding(self, part, (date) => endOfDate(date, part, options), "later"))
|
|
1162
1185
|
|
|
1163
1186
|
/** @internal */
|
|
1164
1187
|
export const nearest: {
|
|
@@ -1171,7 +1194,7 @@ export const nearest: {
|
|
|
1171
1194
|
} = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: {
|
|
1172
1195
|
readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined
|
|
1173
1196
|
}): DateTime.DateTime =>
|
|
1174
|
-
|
|
1197
|
+
mutateRounding(self, part, (date) => {
|
|
1175
1198
|
if (part === "millisecond") return
|
|
1176
1199
|
const millis = date.getTime()
|
|
1177
1200
|
const start = new Date(millis)
|