- Add withRateLimit(rateLimit, fn) in rate-limit/with-rate-limit.ts, attaching the RateLimited brand at DI bind time - Extend wireUseCase to accept optional rateLimit?: IRateLimit and compose withRateLimit innermost (before analytics/audit); propagate __rateLimited through analytics + audit inline wrappers - Extend withSpan and withCapture PROPAGATED_BRANDS to include __rateLimited so the outermost binding carries the brand - Extend assertFeatureConformance to require __rateLimited brand when manifest.useCases[name].rateLimit.length > 0; refactored into helper functions to stay within complexity thresholds - Add rateLimit?: IRateLimit to BindContext; default to NoopRateLimit in web-next bindAllProduction and bindAllDevSeed aggregators - Unit tests for withRateLimit brand attachment, factory passthrough, and composition; synthetic fixture tests for conformance errors Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
46 lines
1.8 KiB
TypeScript
46 lines
1.8 KiB
TypeScript
import type { ITracer, SpanOpts } from "./tracer.interface";
|
|
import type { Instrumented } from "../conformance/brands";
|
|
import { attachBrand } from "../conformance/brand-runtime";
|
|
|
|
const PROPAGATED_BRANDS = [
|
|
"__captured",
|
|
"__audited",
|
|
"__analyzed",
|
|
"__consentChecked",
|
|
"__rateLimited",
|
|
] as const;
|
|
|
|
export function withSpan<Args extends unknown[], R, Extra extends object>(
|
|
tracer: ITracer,
|
|
opts: SpanOpts | ((args: Args) => SpanOpts),
|
|
fn: ((...args: Args) => Promise<R>) & Extra,
|
|
): Instrumented<((...args: Args) => Promise<R>) & Extra>;
|
|
export function withSpan<Args extends unknown[], R>(
|
|
tracer: ITracer,
|
|
opts: SpanOpts | ((args: Args) => SpanOpts),
|
|
fn: (...args: Args) => Promise<R>,
|
|
): Instrumented<(...args: Args) => Promise<R>>;
|
|
export function withSpan<Args extends unknown[], R>(
|
|
tracer: ITracer,
|
|
opts: SpanOpts | ((args: Args) => SpanOpts),
|
|
fn: (...args: Args) => Promise<R>,
|
|
): Instrumented<(...args: Args) => Promise<R>> {
|
|
const wrapped: (...args: Args) => Promise<R> = (...args) => {
|
|
const resolved = typeof opts === "function" ? opts(args) : opts;
|
|
return tracer.startSpan(resolved, () => fn(...args));
|
|
};
|
|
attachBrand(wrapped, "__instrumented");
|
|
// Propagate brands from the inner function (e.g. __captured from withCapture,
|
|
// __audited from withAudit) so the outermost binding carries all brands.
|
|
// withSpan is always outermost — the assertFeatureConformance check reads the
|
|
// container-resolved value (the withSpan result), so brands must be visible here.
|
|
for (const brand of PROPAGATED_BRANDS) {
|
|
if ((fn as unknown as Record<string, unknown>)[brand] === true) {
|
|
attachBrand(wrapped, brand);
|
|
}
|
|
}
|
|
// Cast is the type-level concession — the brand is now also a non-enumerable
|
|
// runtime property attached above by `attachBrand`.
|
|
return wrapped as Instrumented<(...args: Args) => Promise<R>>;
|
|
}
|