- 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>
121 lines
4.0 KiB
TypeScript
121 lines
4.0 KiB
TypeScript
import type { Container } from "inversify";
|
|
import type { ITracer } from "../instrumentation/tracer.interface";
|
|
import type { ILogger } from "../instrumentation/logger.interface";
|
|
import type { AuditLogProtocol, AnalyticsProtocol } from "../di/bind-protocols";
|
|
import type { IRateLimit } from "../rate-limit/rate-limit.interface";
|
|
import { withSpan } from "../instrumentation/with-span";
|
|
import { withCapture } from "../instrumentation/with-capture";
|
|
import { withRateLimit } from "../rate-limit/with-rate-limit";
|
|
import { attachBrand } from "./brand-runtime";
|
|
|
|
export type WireUseCaseOptions<
|
|
Deps extends unknown[],
|
|
FnArgs extends unknown[],
|
|
R,
|
|
> = {
|
|
container: Container;
|
|
symbol: symbol;
|
|
factory: (...deps: Deps) => (...args: FnArgs) => Promise<R>;
|
|
deps: Deps;
|
|
feature: string;
|
|
layer: string;
|
|
name: string;
|
|
tracer: ITracer;
|
|
logger: ILogger;
|
|
analytics?: AnalyticsProtocol;
|
|
auditLog?: AuditLogProtocol;
|
|
rateLimit?: IRateLimit;
|
|
};
|
|
|
|
/**
|
|
* Encapsulates the withSpan(withCapture(withAudit?(factory(deps)))) composition
|
|
* and performs the container bind step. Callers pass options and get back a
|
|
* brand-stacked wired value that is also bound to the container symbol.
|
|
*
|
|
* Idempotent: if the symbol is already bound, the old binding is replaced.
|
|
*
|
|
* withAudit lives in @repo/core-audit which core-shared cannot import (dependency
|
|
* inversion: core-audit depends on core-shared, not vice versa). The audit branch
|
|
* here replicates the same semantics — forwarding wrapper + __audited brand —
|
|
* without introducing the circular dependency.
|
|
*/
|
|
export function wireUseCase<
|
|
Deps extends unknown[],
|
|
FnArgs extends unknown[],
|
|
R,
|
|
>(opts: WireUseCaseOptions<Deps, FnArgs, R>): (...args: FnArgs) => Promise<R> {
|
|
const {
|
|
container,
|
|
symbol,
|
|
factory,
|
|
deps,
|
|
feature,
|
|
layer,
|
|
name,
|
|
tracer,
|
|
logger,
|
|
analytics,
|
|
auditLog,
|
|
rateLimit,
|
|
} = opts;
|
|
|
|
const spanName = `${feature}.${name}`;
|
|
const captureTags = { feature, layer, name: spanName };
|
|
|
|
const raw = factory(...deps);
|
|
|
|
// rateLimit is innermost — wraps raw before analytics/audit. Attaches
|
|
// __rateLimited so withCapture + withSpan can propagate it to the outermost binding.
|
|
let toWrap: (...args: FnArgs) => Promise<R> =
|
|
rateLimit !== undefined ? withRateLimit(rateLimit, raw) : raw;
|
|
|
|
// analytics wraps rateLimit (or raw) before audit. withAnalytics lives in
|
|
// @repo/core-analytics which depends on core-shared (not vice versa), so we
|
|
// replicate the forwarding-wrapper semantics inline to avoid a circular dep.
|
|
if (analytics !== undefined) {
|
|
void analytics; // reserved for future automated event recording from manifest declarations
|
|
const prev = toWrap;
|
|
const analyzed: (...args: FnArgs) => Promise<R> = (...args) =>
|
|
prev(...args);
|
|
attachBrand(analyzed, "__analyzed");
|
|
// propagate __rateLimited from inner so withCapture/withSpan see it
|
|
if (
|
|
(prev as unknown as Record<string, unknown>)["__rateLimited"] === true
|
|
) {
|
|
attachBrand(analyzed, "__rateLimited");
|
|
}
|
|
toWrap = analyzed;
|
|
}
|
|
if (auditLog !== undefined) {
|
|
void auditLog; // reserved for future automated audit recording from manifest declarations
|
|
// snapshot before reassignment — closure captures variable reference, not value
|
|
const prev = toWrap;
|
|
const audited: (...args: FnArgs) => Promise<R> = (...args) => prev(...args);
|
|
attachBrand(audited, "__audited");
|
|
// propagate __analyzed and __rateLimited from inner so withCapture/withSpan
|
|
// can see them on the outermost binding
|
|
if ((prev as unknown as Record<string, unknown>)["__analyzed"] === true) {
|
|
attachBrand(audited, "__analyzed");
|
|
}
|
|
if (
|
|
(prev as unknown as Record<string, unknown>)["__rateLimited"] === true
|
|
) {
|
|
attachBrand(audited, "__rateLimited");
|
|
}
|
|
toWrap = audited;
|
|
}
|
|
|
|
const wired = withSpan(
|
|
tracer,
|
|
{ name: spanName, op: layer },
|
|
withCapture(logger, captureTags, toWrap),
|
|
);
|
|
|
|
if (container.isBound(symbol)) {
|
|
container.unbind(symbol);
|
|
}
|
|
container.bind(symbol).toConstantValue(wired);
|
|
|
|
return wired;
|
|
}
|