Skip to content

feat: define typed billable meters - #1554

Closed
kang-heewon wants to merge 1 commit into
trunkfrom
feat/1515-typed-billable-meters
Closed

feat: define typed billable meters#1554
kang-heewon wants to merge 1 commit into
trunkfrom
feat/1515-typed-billable-meters

Conversation

@kang-heewon

Copy link
Copy Markdown
Member

Outcome

@croco/metering-core now provides definition-first defineMeter() contracts with branded MeterRef values, deterministic serializable descriptors, inferred enum dimensions, explicit aggregation and unit, and local or billing-required intent.

Typed MeteringService.record(meter, input) calls require exact declared dimensions, finite SUM values, COUNT default values, and stable event IDs for billing-required meters at both compile time and runtime. Application metadata remains separate from billing dimensions. Bare @Metered({ meter }) is limited to dimensionless local COUNT meters, while the existing string-based record and decorator APIs remain source-compatible.

Stable validation Problems, negative compile fixtures, runtime regression coverage, generated API docs, public snapshots, README guidance, and changesets for metering-core and the shared Problem registry are included.

Fixes #1515

Verification

  • @croco/metering-core — 191/191 tests; lint, typecheck, and ESM/CJS/declaration build passed
  • Negative TypeScript fixtures — missing, extra, invalid, and union-unsafe dimension/input forms rejected
  • pnpm public-api:check — 111 package snapshots match
  • pnpm problem-registry:check — 456 codes from 456 discoveries
  • pnpm docs:api:check — 112/112 build tasks; generated API docs match
  • pnpm check — repository profile passed; 21/22 applicable gates passed and one was not applicable before commit
  • Changeset-required gate — all affected publishable packages covered
  • Pre-push workspace tests and typecheck — passed

Review gates

  • Correctness and regression: PASS — literal inference, exact dimension keys and domains, SUM/COUNT envelopes, billing event identity, deterministic descriptors, local generated IDs, forged references, malformed JavaScript inputs, validation ordering, and legacy compatibility are covered.
  • API, security, compatibility, and release: PASS — typed and legacy overloads coexist, billing dimensions cannot leak through metadata, stable Problems are registered, generated docs and snapshots agree, and changesets cover both affected packages.
  • Maintainability and minimality: PASS — one meter definition artifact owns compile-time and runtime contracts; provider delivery, pricing, invoice calculation, and decorator extraction callbacks remain outside this change.
  • Independent adversarial review: APPROVE after resolving union correlation, variable-borne extra keys, decorator union safety, annotated COUNT inputs, forged refs, malformed inputs, and enum tuple/runtime preservation.

Residual risk

Provider delivery and durable financial storage remain separate integration concerns. Billing-required callers must supply an event ID that is stable across retries; this API validates presence and shape but cannot prove business-level identity stability. The legacy string path intentionally retains its existing generated-id and open-metadata behavior.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@kang-heewon, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 25 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 604bdedb-8171-4a86-9fc5-ab3549e90c0a

📥 Commits

Reviewing files that changed from the base of the PR and between 2b4ad01 and 8467952.

⛔ Files ignored due to path filters (1)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
📒 Files selected for processing (43)
  • .changeset/typed-billable-meters.md
  • docs/problem-code-registry.json
  • packages/docs/src/content/docs/api/metering-core/src/classes/InvalidMeterDefinitionProblem.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/InvalidUsageEnvelopeProblem.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/MeteringService.md
  • packages/docs/src/content/docs/api/metering-core/src/functions/Metered.md
  • packages/docs/src/content/docs/api/metering-core/src/functions/defineMeter.md
  • packages/docs/src/content/docs/api/metering-core/src/functions/isMeterRef.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/EnumDimension.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/LegacyMeteredOptions.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterAggregation.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterAggregationOf.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterBillingIntent.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterBillingOf.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDefinitionInput.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDescriptor.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDimension.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDimensionSchema.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDimensionValue.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDimensionValues.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDimensionsOf.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterRecordInput.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterRef.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeteredMetadata.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeteredOptions.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/TypedMeteredOptions.md
  • packages/docs/src/content/docs/api/metering-core/src/variables/dimension.md
  • packages/docs/src/content/docs/api/problems-core/src/classes/Problem.md
  • packages/docs/src/content/docs/api/problems-core/src/variables/CROCO_PROBLEM_CODE_REGISTRY.md
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/metering-core/README.md
  • packages/metering-core/src/index.ts
  • packages/metering-core/src/libs/MeterDefinition.ts
  • packages/metering-core/src/libs/MeteringService.ts
  • packages/metering-core/src/libs/decorators/Metered.ts
  • packages/metering-core/src/libs/problems/InvalidMeterDefinitionProblem.ts
  • packages/metering-core/src/libs/problems/InvalidUsageEnvelopeProblem.ts
  • packages/metering-core/src/tests/MeterDefinition.spec.ts
  • packages/metering-core/src/tests/MeteringService.spec.ts
  • packages/metering-core/src/tests/decorators/Metered.spec.ts
  • packages/metering-core/src/tests/problems/Problems.spec.ts
  • packages/metering-core/src/type-tests/MeterDefinitionTypes.ts
  • public-api-surface.snapshot.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1515-typed-billable-meters

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

📊 Benchmark Results

✅ All benchmarks passed

Benchmark p75 Threshold Baseline vs Baseline Status Notes
CrocoApp constructor 7.5μs 30.0ms 8.2μs -8.2% -
CrocoApp lambdaHandler (10 controllers) 182.0μs 50.0ms 258.4μs -29.6% -
Lambda cold-start simulation 323.0μs 80.0ms 418.1μs -22.8% -
Lambda cold-start with headers 297.6μs 80.0ms 369.7μs -19.5% -
Lambda cold-start with binary body 255.8μs 80.0ms 339.1μs -24.6% -
Lambda cold-start with query params 224.8μs 80.0ms 301.3μs -25.4% -
Lambda cold-start with authorizer context 230.0μs 80.0ms 299.8μs -23.3% -
Lambda cold-start realistic scenario 226.7μs 80.0ms 299.2μs -24.2% -
EventBusConfig.start (10 handlers) 1.5μs 10.0ms 1.4μs +3.4% -
EventPublisher.publishNow single event 1.6μs 2.0ms 1.7μs -4.8% -
DefaultHandlerResolver.resolve × 10 0.1μs 5.0ms 0.1μs -25.0% -
Container.get singleton (cold) 47.1μs 5.0ms 70.3μs -33.0% -
Container.register × 50 components 2.6ms 10.0ms 3.2ms -20.2% -
Container.validate (50 components) 3.0ms 20.0ms 3.4ms -12.2% -
Container.get singleton (warm) 1.2μs 500.0μs 1.6μs -24.0% -
TelemetryRuntime.init (lambda preset) 1.9μs 200.0ms 1.1ms -99.8% -
lambdaPreset config creation 1.1μs 2.0ms 1.4μs -21.8% -

Updated: 2026-07-25T21:25:49.812Z · Commit: a5e90f9

@kang-heewon

Copy link
Copy Markdown
Member Author

Superseded by #1555, which carries typed usage envelopes through Redis and Drizzle and includes the required migrations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[metering-core] Define typed billable meters and usage envelopes

1 participant