feat(core-shared): add withRateLimit wrapper and conformance enforcement

- 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>
This commit is contained in:
2026-05-20 09:00:31 +00:00
parent 24b2490d86
commit cb61f51ee1
12 changed files with 477 additions and 61 deletions

View File

@@ -7,6 +7,7 @@ import {
isCaptured,
isAudited,
isAnalyzed,
isRateLimited,
} from "@/conformance/brand-runtime";
import type {
ITracer,
@@ -15,6 +16,7 @@ import type {
} from "@/instrumentation/tracer.interface";
import type { ILogger } from "@/instrumentation/logger.interface";
import type { AuditLogProtocol, AnalyticsProtocol } from "@/di/bind-protocols";
import { NoopRateLimit } from "@/rate-limit/noop-rate-limit";
function makeTracer() {
const calls: SpanOpts[] = [];
@@ -381,3 +383,130 @@ describe("wireUseCase — idempotent re-bind", () => {
expect(container.get(sym)).toBe(second);
});
});
describe("wireUseCase — rate limit path", () => {
it("attaches RateLimited brand when rateLimit is provided", () => {
const { tracer } = makeTracer();
const logger = makeLogger();
const container = new Container();
const sym = Symbol("test.double");
const rateLimit = new NoopRateLimit();
const wired = wireUseCase({
container,
symbol: sym,
factory: doubleFactory,
deps: [],
feature: "test",
layer: "use-case",
name: "double",
tracer,
logger,
rateLimit,
});
expect(isRateLimited(wired)).toBe(true);
expect(isInstrumented(wired)).toBe(true);
expect(isCaptured(wired)).toBe(true);
});
it("does not attach RateLimited brand when rateLimit is absent", () => {
const { tracer } = makeTracer();
const logger = makeLogger();
const container = new Container();
const sym = Symbol("test.double");
const wired = wireUseCase({
container,
symbol: sym,
factory: doubleFactory,
deps: [],
feature: "test",
layer: "use-case",
name: "double",
tracer,
logger,
});
expect(isRateLimited(wired)).toBe(false);
});
it("executes factory correctly when rateLimit is provided", async () => {
const { tracer } = makeTracer();
const logger = makeLogger();
const container = new Container();
const sym = Symbol("test.double");
const rateLimit = new NoopRateLimit();
const wired = wireUseCase({
container,
symbol: sym,
factory: doubleFactory,
deps: [],
feature: "test",
layer: "use-case",
name: "double",
tracer,
logger,
rateLimit,
});
await expect(wired(5)).resolves.toBe(10);
});
it("attaches RateLimited + Analyzed brands when both rateLimit and analytics are provided", () => {
const { tracer } = makeTracer();
const logger = makeLogger();
const container = new Container();
const sym = Symbol("test.double");
const rateLimit = new NoopRateLimit();
const analytics = makeAnalytics();
const wired = wireUseCase({
container,
symbol: sym,
factory: doubleFactory,
deps: [],
feature: "test",
layer: "use-case",
name: "double",
tracer,
logger,
rateLimit,
analytics,
});
expect(isRateLimited(wired)).toBe(true);
expect(isAnalyzed(wired)).toBe(true);
expect(isInstrumented(wired)).toBe(true);
expect(isCaptured(wired)).toBe(true);
});
it("attaches RateLimited + Audited brands when both rateLimit and auditLog are provided", () => {
const { tracer } = makeTracer();
const logger = makeLogger();
const container = new Container();
const sym = Symbol("test.double");
const rateLimit = new NoopRateLimit();
const auditLog = makeAuditLog();
const wired = wireUseCase({
container,
symbol: sym,
factory: doubleFactory,
deps: [],
feature: "test",
layer: "use-case",
name: "double",
tracer,
logger,
rateLimit,
auditLog,
});
expect(isRateLimited(wired)).toBe(true);
expect(isAudited(wired)).toBe(true);
expect(isInstrumented(wired)).toBe(true);
expect(isCaptured(wired)).toBe(true);
});
});