From 552d625a21bf271a41cb83c25a1e605e987de311 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 20 Dec 2024 10:18:11 +1300 Subject: [PATCH] capture redactable context at creation --- packages/effect/src/Inspectable.ts | 64 +++++++++++---- .../src/internal/httpClient.ts | 4 +- .../platform-bun/src/internal/httpServer.ts | 6 +- .../src/internal/httpClientUndici.ts | 6 +- .../src/internal/httpIncomingMessage.ts | 9 +-- .../platform-node/src/internal/httpServer.ts | 24 +++--- packages/platform/src/Headers.ts | 77 ++++++++++++------- .../src/internal/httpClientRequest.ts | 2 +- .../src/internal/httpClientResponse.ts | 6 +- .../platform/src/internal/httpPlatform.ts | 4 +- .../src/internal/httpServerRequest.ts | 6 +- .../src/internal/httpServerResponse.ts | 20 ++--- packages/platform/test/Headers.test.ts | 49 ++++-------- packages/rpc/src/Rpc.ts | 15 ++-- packages/rpc/src/RpcResolver.ts | 10 +-- 15 files changed, 168 insertions(+), 134 deletions(-) diff --git a/packages/effect/src/Inspectable.ts b/packages/effect/src/Inspectable.ts index 7d99db7b189..4467c6ebaf3 100644 --- a/packages/effect/src/Inspectable.ts +++ b/packages/effect/src/Inspectable.ts @@ -1,9 +1,9 @@ /** * @since 2.0.0 */ - +import * as Context from "./Context.js" import type { RuntimeFiber } from "./Fiber.js" -import type * as FiberRefs from "./FiberRefs.js" +import { globalValue } from "./GlobalValue.js" import { hasProperty, isFunction } from "./Predicate.js" /** @@ -126,15 +126,15 @@ export const stringifyCircular = (obj: unknown, whitespace?: number | string | u * @since 3.10.0 * @category redactable */ -export interface Redactable { - readonly [symbolRedactable]: (fiberRefs: FiberRefs.FiberRefs) => unknown -} +export const symbolRedactable: unique symbol = Symbol.for("effect/Inspectable/Redactable") /** * @since 3.10.0 * @category redactable */ -export const symbolRedactable: unique symbol = Symbol.for("effect/Inspectable/Redactable") +export interface Redactable { + [symbolRedactable](): unknown +} /** * @since 3.10.0 @@ -143,18 +143,56 @@ export const symbolRedactable: unique symbol = Symbol.for("effect/Inspectable/Re export const isRedactable = (u: unknown): u is Redactable => typeof u === "object" && u !== null && symbolRedactable in u -const currentFiberURI = "effect/FiberCurrent" - /** * @since 3.10.0 * @category redactable */ export const redact = (u: unknown): unknown => { - if (isRedactable(u)) { - const fiber = (globalThis as any)[currentFiberURI] as RuntimeFiber | undefined - if (fiber !== undefined) { - return u[symbolRedactable](fiber.getFiberRefs()) + return isRedactable(u) ? u[symbolRedactable]() : u +} + +const redactableContext = globalValue("effect/Inspectable/redactableContext", () => new WeakMap()) + +/** + * @since 3.12.0 + * @category redactable + */ +export const makeRedactableContext = (make: (context: Context.Context) => A): { + readonly register: (self: unknown, context?: Context.Context, input?: unknown) => void + readonly get: (u: unknown) => A +} => ({ + register(self, context, input) { + if (input && redactableContext.has(input)) { + redactableContext.set(self, redactableContext.get(input)) + return } + redactableContext.set(self, make(context ?? currentContext() ?? Context.empty())) + }, + get(u) { + return redactableContext.has(u) ? redactableContext.get(u) : make(currentContext() ?? Context.empty()) } - return u +}) + +/** + * @since 3.12.0 + * @category redactable + */ +export const RedactableClass = () => +(options: { + readonly context: (fiberRefs: Context.Context) => A + readonly redact: (self: Self, context: A) => unknown +}): new(context?: Context.Context, input?: unknown) => Redactable => { + const redactable = makeRedactableContext(options.context) + function Redactable(this: any, context?: Context.Context, input?: unknown) { + redactable.register(this, context, input) + } + Redactable.prototype[symbolRedactable] = function() { + return options.redact(this, redactable.get(this)) + } + return Redactable as any +} + +const currentContext = (): Context.Context | undefined => { + const fiber = (globalThis as any)["effect/FiberCurrent"] as RuntimeFiber | undefined + return fiber ? fiber.currentContext : undefined } diff --git a/packages/platform-browser/src/internal/httpClient.ts b/packages/platform-browser/src/internal/httpClient.ts index 74139107690..0689e4e0e9d 100644 --- a/packages/platform-browser/src/internal/httpClient.ts +++ b/packages/platform-browser/src/internal/httpClient.ts @@ -142,12 +142,12 @@ export abstract class IncomingMessageImpl extends Inspectable.Class return this._headers } if (this._rawHeaderString === "") { - return this._headers = Headers.empty + return this._headers = Headers.empty() } const parser = HeaderParser.make() const result = parser(encoder.encode(this._rawHeaderString + "\r\n"), 0) this._rawHeaders = result._tag === "Headers" ? result.headers : undefined - const parsed = result._tag === "Headers" ? Headers.fromInput(result.headers) : Headers.empty + const parsed = result._tag === "Headers" ? Headers.fromInput(result.headers) : Headers.empty() return this._headers = parsed } diff --git a/packages/platform-bun/src/internal/httpServer.ts b/packages/platform-bun/src/internal/httpServer.ts index c85cc148805..81f24e93bdc 100644 --- a/packages/platform-bun/src/internal/httpServer.ts +++ b/packages/platform-bun/src/internal/httpServer.ts @@ -208,6 +208,7 @@ function wsDefaultRun(this: WebSocketContext, _: Uint8Array | string) { class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpServerRequest { readonly [ServerRequest.TypeId]: ServerRequest.TypeId readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId + readonly headers: Headers.Headers constructor( readonly source: Request, public resolve: (response: Response) => void, @@ -219,6 +220,7 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS super() this[ServerRequest.TypeId] = ServerRequest.TypeId this[IncomingMessage.TypeId] = IncomingMessage.TypeId + this.headers = headersOverride ?? Headers.fromInput(source.headers) } toJSON(): unknown { return IncomingMessage.inspect(this, { @@ -254,10 +256,6 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS ? Option.some(this.remoteAddressOverride) : Option.fromNullable(this.bunServer.requestIP(this.source)?.address) } - get headers(): Headers.Headers { - this.headersOverride ??= Headers.fromInput(this.source.headers) - return this.headersOverride - } private cachedCookies: ReadonlyRecord | undefined get cookies() { diff --git a/packages/platform-node/src/internal/httpClientUndici.ts b/packages/platform-node/src/internal/httpClientUndici.ts index fb67f4380b9..9c25499edb4 100644 --- a/packages/platform-node/src/internal/httpClientUndici.ts +++ b/packages/platform-node/src/internal/httpClientUndici.ts @@ -97,6 +97,7 @@ function noopErrorHandler(_: any) {} class ClientResponseImpl extends Inspectable.Class implements ClientResponse.HttpClientResponse { readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId readonly [ClientResponse.TypeId]: ClientResponse.TypeId + readonly headers: Headers.Headers constructor( readonly request: ClientRequest.HttpClientRequest, @@ -105,6 +106,7 @@ class ClientResponseImpl extends Inspectable.Class implements ClientResponse.Htt super() this[IncomingMessage.TypeId] = IncomingMessage.TypeId this[ClientResponse.TypeId] = ClientResponse.TypeId + this.headers = Headers.fromInput(this.source.headers) source.body.on("error", noopErrorHandler) } @@ -128,10 +130,6 @@ class ClientResponseImpl extends Inspectable.Class implements ClientResponse.Htt return this.cachedCookies = Cookies.empty } - get headers(): Headers.Headers { - return Headers.fromInput(this.source.headers) - } - get remoteAddress(): Option.Option { return Option.none() } diff --git a/packages/platform-node/src/internal/httpIncomingMessage.ts b/packages/platform-node/src/internal/httpIncomingMessage.ts index e89a78032aa..770e64fa153 100644 --- a/packages/platform-node/src/internal/httpIncomingMessage.ts +++ b/packages/platform-node/src/internal/httpIncomingMessage.ts @@ -14,18 +14,17 @@ export abstract class HttpIncomingMessageImpl extends Inspectable.Class implements IncomingMessage.HttpIncomingMessage { readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId + readonly headers: Headers.Headers constructor( readonly source: Http.IncomingMessage, readonly onError: (error: unknown) => E, - readonly remoteAddressOverride?: string + readonly remoteAddressOverride?: string, + readonly headersOverride?: Headers.Headers ) { super() this[IncomingMessage.TypeId] = IncomingMessage.TypeId - } - - get headers() { - return Headers.fromInput(this.source.headers as any) + this.headers = headersOverride ?? Headers.fromInput(this.source.headers as any) } get remoteAddress() { diff --git a/packages/platform-node/src/internal/httpServer.ts b/packages/platform-node/src/internal/httpServer.ts index d9d63e30d86..a8249152919 100644 --- a/packages/platform-node/src/internal/httpServer.ts +++ b/packages/platform-node/src/internal/httpServer.ts @@ -213,15 +213,20 @@ class ServerRequestImpl extends HttpIncomingMessageImpl impl readonly response: Http.ServerResponse | LazyArg, private upgradeEffect?: Effect.Effect, readonly url = source.url!, - private headersOverride?: Headers.Headers, + headersOverride?: Headers.Headers, remoteAddressOverride?: string ) { - super(source, (cause) => - new Error.RequestError({ - request: this, - reason: "Decode", - cause - }), remoteAddressOverride) + super( + source, + (cause) => + new Error.RequestError({ + request: this, + reason: "Decode", + cause + }), + remoteAddressOverride, + headersOverride + ) this[ServerRequest.TypeId] = ServerRequest.TypeId } @@ -262,11 +267,6 @@ class ServerRequestImpl extends HttpIncomingMessageImpl impl return this.source.method!.toUpperCase() as HttpMethod } - get headers(): Headers.Headers { - this.headersOverride ??= this.source.headers as Headers.Headers - return this.headersOverride - } - private multipartEffect: | Effect.Effect< Multipart.Persisted, diff --git a/packages/platform/src/Headers.ts b/packages/platform/src/Headers.ts index 83c27d96efd..852b66e4f33 100644 --- a/packages/platform/src/Headers.ts +++ b/packages/platform/src/Headers.ts @@ -1,10 +1,8 @@ /** * @since 1.0.0 */ -import { FiberRefs } from "effect" -import * as FiberRef from "effect/FiberRef" +import * as Context from "effect/Context" import { dual, identity } from "effect/Function" -import { globalValue } from "effect/GlobalValue" import * as Inspectable from "effect/Inspectable" import type * as Option from "effect/Option" import * as Predicate from "effect/Predicate" @@ -13,7 +11,6 @@ import * as Redacted from "effect/Redacted" import * as Schema from "effect/Schema" import * as String from "effect/String" import type { Mutable } from "effect/Types" - /** * @since 1.0.0 * @category type ids @@ -44,18 +41,24 @@ export interface Headers extends Inspectable.Redactable { const Proto = Object.assign(Object.create(null), { [HeadersTypeId]: HeadersTypeId, [Inspectable.symbolRedactable]( - this: Headers, - fiberRefs: FiberRefs.FiberRefs + this: Headers ): Record> { - return redact(this, FiberRefs.getOrDefault(fiberRefs, currentRedactedNames)) + return redact(this, redactableContext.get(this)) }, [Inspectable.NodeInspectSymbol]() { return Inspectable.redact(this) } }) -const make = (input: Record.ReadonlyRecord): Mutable => - Object.assign(Object.create(Proto), input) as Headers +const redactableContext = Inspectable.makeRedactableContext((context) => Context.get(context, RedactedNames)) + +const make = (input: Record.ReadonlyRecord, options?: { + readonly redactableContext?: Context.Context | undefined +}): Mutable => { + const self = Object.assign(Object.create(Proto), input) as Headers + redactableContext.register(self, options?.redactableContext, input) + return self +} /** * @since 1.0.0 @@ -89,23 +92,29 @@ export type Input = * @since 1.0.0 * @category constructors */ -export const empty: Headers = Object.create(Proto) +export const empty = (options?: { + readonly redactableContext?: Context.Context | undefined +}): Headers => make({}, options) /** * @since 1.0.0 * @category constructors */ -export const fromInput: (input?: Input) => Headers = (input) => { +export const fromInput = (input?: Input, options?: { + readonly redactableContext?: Context.Context | undefined +}): Headers => { if (input === undefined) { - return empty + return empty(options) + } else if (isHeaders(input)) { + return input } else if (Symbol.iterator in input) { - const out: Record = Object.create(Proto) + const out: Record = make({}, options) for (const [k, v] of input) { out[k.toLowerCase()] = v } return out as Headers } - const out: Record = Object.create(Proto) + const out: Record = make({}, options) for (const [k, v] of Object.entries(input)) { if (Array.isArray(v)) { out[k.toLowerCase()] = v.join(", ") @@ -120,8 +129,13 @@ export const fromInput: (input?: Input) => Headers = (input) => { * @since 1.0.0 * @category constructors */ -export const unsafeFromRecord = (input: Record.ReadonlyRecord): Headers => - Object.setPrototypeOf(input, Proto) as Headers +export const unsafeFromRecord = (input: Record.ReadonlyRecord, options?: { + readonly redactableContext?: Context.Context | undefined +}): Headers => { + const self = Object.setPrototypeOf(input, Proto) + redactableContext.register(self, options?.redactableContext, input) + return self as Headers +} /** * @since 1.0.0 @@ -173,11 +187,14 @@ export const setAll: { } = dual< (headers: Input) => (self: Headers) => Headers, (self: Headers, headers: Input) => Headers ->(2, (self, headers) => - make({ +>(2, (self, headers) => { + const out = make({ ...self, ...fromInput(headers) - })) + }) + redactableContext.register(out, undefined, self) + return out +}) /** * @since 1.0.0 @@ -192,6 +209,7 @@ export const merge: { >(2, (self, headers) => { const out = make(self) Object.assign(out, headers) + redactableContext.register(out, undefined, self) return out }) @@ -257,15 +275,16 @@ export const redact: { /** * @since 1.0.0 - * @category fiber refs + * @category references */ -export const currentRedactedNames: FiberRef.FiberRef> = globalValue( +export class RedactedNames extends Context.Reference()< "@effect/platform/Headers/currentRedactedNames", - () => - FiberRef.unsafeMake>([ - "authorization", - "cookie", - "set-cookie", - "x-api-key" - ]) -) + ReadonlyArray +>("@effect/platform/Headers/currentRedactedNames", { + defaultValue: () => [ + "authorization", + "cookie", + "set-cookie", + "x-api-key" + ] +}) {} diff --git a/packages/platform/src/internal/httpClientRequest.ts b/packages/platform/src/internal/httpClientRequest.ts index 1cf89476c73..a7643480c83 100644 --- a/packages/platform/src/internal/httpClientRequest.ts +++ b/packages/platform/src/internal/httpClientRequest.ts @@ -66,7 +66,7 @@ export const empty: ClientRequest.HttpClientRequest = makeInternal( "", UrlParams.empty, Option.none(), - Headers.empty, + Headers.empty(), internalBody.empty ) diff --git a/packages/platform/src/internal/httpClientResponse.ts b/packages/platform/src/internal/httpClientResponse.ts index d5a902a778b..5ba35c57e51 100644 --- a/packages/platform/src/internal/httpClientResponse.ts +++ b/packages/platform/src/internal/httpClientResponse.ts @@ -27,6 +27,7 @@ export const fromWeb = ( class ClientResponseImpl extends Inspectable.Class implements ClientResponse.HttpClientResponse { readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId readonly [TypeId]: ClientResponse.TypeId + readonly headers: Headers.Headers constructor( readonly request: ClientRequest.HttpClientRequest, @@ -35,6 +36,7 @@ class ClientResponseImpl extends Inspectable.Class implements ClientResponse.Htt super() this[IncomingMessage.TypeId] = IncomingMessage.TypeId this[TypeId] = TypeId + this.headers = Headers.fromInput(this.source.headers) } toJSON(): unknown { @@ -49,10 +51,6 @@ class ClientResponseImpl extends Inspectable.Class implements ClientResponse.Htt return this.source.status } - get headers(): Headers.Headers { - return Headers.fromInput(this.source.headers) - } - cachedCookies?: Cookies.Cookies get cookies(): Cookies.Cookies { if (this.cachedCookies) { diff --git a/packages/platform/src/internal/httpPlatform.ts b/packages/platform/src/internal/httpPlatform.ts index 3194524be0e..2022efd998b 100644 --- a/packages/platform/src/internal/httpPlatform.ts +++ b/packages/platform/src/internal/httpPlatform.ts @@ -50,7 +50,7 @@ export const make = (impl: { const start = Number(options?.offset ?? 0) const end = options?.bytesToRead !== undefined ? start + Number(options.bytesToRead) : undefined const headers = Headers.set( - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), "etag", Etag.toString(etag) ) @@ -73,7 +73,7 @@ export const make = (impl: { fileWebResponse(file, options) { return Effect.map(etagGen.fromFileWeb(file), (etag) => { const headers = Headers.merge( - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), Headers.unsafeFromRecord({ etag: Etag.toString(etag), "last-modified": new Date(file.lastModified).toUTCString() diff --git a/packages/platform/src/internal/httpServerRequest.ts b/packages/platform/src/internal/httpServerRequest.ts index dc9d8eade58..00e184c360b 100644 --- a/packages/platform/src/internal/httpServerRequest.ts +++ b/packages/platform/src/internal/httpServerRequest.ts @@ -188,6 +188,7 @@ const removeHost = (url: string) => { class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpServerRequest { readonly [TypeId]: ServerRequest.TypeId readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId + readonly headers: Headers.Headers constructor( readonly source: Request, readonly url: string, @@ -197,6 +198,7 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS super() this[TypeId] = TypeId this[IncomingMessage.TypeId] = IncomingMessage.TypeId + this.headers = headersOverride ?? Headers.fromInput(source.headers) } toJSON(): unknown { return IncomingMessage.inspect(this, { @@ -228,10 +230,6 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS get remoteAddress(): Option.Option { return this.remoteAddressOverride ? Option.some(this.remoteAddressOverride) : Option.none() } - get headers(): Headers.Headers { - this.headersOverride ??= Headers.fromInput(this.source.headers) - return this.headersOverride - } private cachedCookies: ReadonlyRecord | undefined get cookies() { diff --git a/packages/platform/src/internal/httpServerResponse.ts b/packages/platform/src/internal/httpServerResponse.ts index a3415857009..69661d06e7c 100644 --- a/packages/platform/src/internal/httpServerResponse.ts +++ b/packages/platform/src/internal/httpServerResponse.ts @@ -91,7 +91,7 @@ export const empty = (options?: ServerResponse.Options.WithContent | undefined): new ServerResponseImpl( options?.status ?? 204, options?.statusText, - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), options?.cookies ?? Cookies.empty, internalBody.empty ) @@ -121,7 +121,7 @@ export const uint8Array = ( body: Uint8Array, options?: ServerResponse.Options.WithContentType ): ServerResponse.HttpServerResponse => { - const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty + const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty() return new ServerResponseImpl( options?.status ?? 200, options?.statusText, @@ -136,7 +136,7 @@ export const text = ( body: string, options?: ServerResponse.Options.WithContentType ): ServerResponse.HttpServerResponse => { - const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty + const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty() return new ServerResponseImpl( options?.status ?? 200, options?.statusText, @@ -201,7 +201,7 @@ export const json = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), options?.cookies ?? Cookies.empty, body )) @@ -214,7 +214,7 @@ export const unsafeJson = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), options?.cookies ?? Cookies.empty, internalBody.unsafeJson(body) ) @@ -233,7 +233,7 @@ export const schemaJson = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), options?.cookies ?? Cookies.empty, body )) @@ -269,7 +269,7 @@ export const urlParams = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), options?.cookies ?? Cookies.empty, internalBody.text(UrlParams.toString(UrlParams.fromInput(body)), "application/x-www-form-urlencoded") ) @@ -279,7 +279,7 @@ export const raw = (body: unknown, options?: ServerResponse.Options | undefined) new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), options?.cookies ?? Cookies.empty, internalBody.raw(body) ) @@ -292,7 +292,7 @@ export const formData = ( new ServerResponseImpl( options?.status ?? 200, options?.statusText, - options?.headers ? Headers.fromInput(options.headers) : Headers.empty, + options?.headers ? Headers.fromInput(options.headers) : Headers.empty(), options?.cookies ?? Cookies.empty, internalBody.formData(body) ) @@ -302,7 +302,7 @@ export const stream = ( body: Stream.Stream, options?: ServerResponse.Options | undefined ): ServerResponse.HttpServerResponse => { - const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty + const headers = options?.headers ? Headers.fromInput(options.headers) : Headers.empty() return new ServerResponseImpl( options?.status ?? 200, options?.statusText, diff --git a/packages/platform/test/Headers.test.ts b/packages/platform/test/Headers.test.ts index aea0a4745e3..b0ae2b72b1d 100644 --- a/packages/platform/test/Headers.test.ts +++ b/packages/platform/test/Headers.test.ts @@ -1,29 +1,26 @@ import * as Headers from "@effect/platform/Headers" import { assert, describe, it } from "@effect/vitest" -import { Console, Effect, FiberId, FiberRef, FiberRefs, HashSet, Inspectable, Logger } from "effect" +import { Effect, FiberRef, HashSet, Inspectable, Logger } from "effect" import * as Redacted from "effect/Redacted" describe("Headers", () => { describe("Redactable", () => { it("one key", async () => { - const headers = Headers.fromInput({ + const rawHeaders = { "Content-Type": "application/json", "Authorization": "Bearer some-token", "X-Api-Key": "some-key" + } + assert.deepEqual(JSON.parse(Inspectable.toStringUnknown(Headers.fromInput(rawHeaders))), { + "content-type": "application/json", + "authorization": "", + "x-api-key": "" }) - const fiberRefs = FiberRefs.unsafeMake( - new Map([ - [ - Headers.currentRedactedNames, - [[FiberId.none, ["Authorization"]] as const] - ] as const - ]) + const r = Effect.sync(() => Inspectable.toStringUnknown(Headers.fromInput(rawHeaders))).pipe( + Effect.provideService(Headers.RedactedNames, ["Authorization"]), + Effect.runSync ) - const r = Effect.gen(function*() { - yield* Effect.setFiberRefs(fiberRefs) - return Inspectable.toStringUnknown(headers) - }).pipe(Effect.runSync) const redacted = JSON.parse(r) as any assert.deepEqual(redacted, { @@ -39,25 +36,12 @@ describe("Headers", () => { "Authorization": "Bearer some-token", "X-Api-Key": "some-key" }) - - const fiberRefs = FiberRefs.unsafeMake( - new Map([ - [ - Headers.currentRedactedNames, - [[FiberId.none, ["Authorization"]] as const] - ] as const - ]) - ) - const r = Effect.gen(function*() { - yield* Effect.setFiberRefs(fiberRefs) - return Inspectable.toStringUnknown({ headers }) - }).pipe(Effect.runSync) - const redacted = JSON.parse(r) as { headers: unknown } - - assert.deepEqual(redacted.headers, { - "content-type": "application/json", - "authorization": "", - "x-api-key": "some-key" + assert.deepEqual(JSON.parse(Inspectable.toStringUnknown({ headers })), { + headers: { + "content-type": "application/json", + "authorization": "", + "x-api-key": "" + } }) }) @@ -78,7 +62,6 @@ describe("Headers", () => { yield* Effect.log(headers).pipe( Effect.annotateLogs({ headers }) ) - yield* Console.log(headers) assert.include(messages[0], "application/json") assert.notInclude(messages[0], "some-token") assert.notInclude(messages[0], "some-key") diff --git a/packages/rpc/src/Rpc.ts b/packages/rpc/src/Rpc.ts index 8e096e793b5..545b5dfa698 100644 --- a/packages/rpc/src/Rpc.ts +++ b/packages/rpc/src/Rpc.ts @@ -303,7 +303,7 @@ export const RequestSchema = ( */ export const currentHeaders: FiberRef.FiberRef = globalValue( "@effect/rpc/Rpc/currentHeaders", - () => FiberRef.unsafeMake(Headers.empty) + () => FiberRef.unsafeMake(Headers.empty()) ) /** @@ -313,10 +313,15 @@ export const currentHeaders: FiberRef.FiberRef = globalValue( export const annotateHeaders: { (headers: Headers.Input): (self: Effect.Effect) => Effect.Effect (self: Effect.Effect, headers: Headers.Input): Effect.Effect -} = dual(2, (self, headers) => { - const resolved = Headers.fromInput(headers) - return Effect.locallyWith(self, currentHeaders, (prev) => ({ ...prev, ...resolved })) -}) +} = dual( + 2, + (self, headers) => + Effect.locallyWith( + self, + currentHeaders, + (prev) => ({ ...prev, ...Headers.fromInput(headers) }) + ) +) /** * @since 1.0.0 diff --git a/packages/rpc/src/RpcResolver.ts b/packages/rpc/src/RpcResolver.ts index fabb0cf829c..111840c4cac 100644 --- a/packages/rpc/src/RpcResolver.ts +++ b/packages/rpc/src/RpcResolver.ts @@ -121,17 +121,15 @@ export const annotateHeaders: { } = dual(2, ( self: RequestResolver.RequestResolver, R>, headers: Headers.Input -): RequestResolver.RequestResolver, R> => { - const resolved = Headers.fromInput(headers) - return RequestResolver.makeWithEntry((requests) => { +): RequestResolver.RequestResolver, R> => + RequestResolver.makeWithEntry((requests) => { requests.forEach((entries) => entries.forEach((entry) => { - ;(entry.request as any).headers = Headers.merge(entry.request.headers, resolved) + ;(entry.request as any).headers = Headers.merge(entry.request.headers, Headers.fromInput(headers)) }) ) return self.runAll(requests) - }) -}) + })) /** * @since 1.0.0