Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/strict-metering-usage-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@croco/metering-core": minor
"@croco/problems-core": patch
---

Reject usage values outside the positive 32-bit integer range before idempotency or storage, and fail closed when Redis contains an invalid or unsafe accumulated value.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
32 changes: 31 additions & 1 deletion docs/problem-code-registry.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": "croco.problem-code-registry.v1",
"problemCount": 541,
"problemCount": 542,
"problems": [
{
"code": "ACCESS_DENIED",
Expand Down Expand Up @@ -8612,6 +8612,36 @@
}
]
},
{
"code": "metering/invalid-usage-value",
"category": "ValidationError",
"status": 422,
"title": "Validation Error",
"cookbookPath": "/reference/problem-recovery-cookbook/#metering-invalid-usage-value",
"recovery": {
"cause": "The request or generated contract failed schema or semantic validation.",
"userAction": "Fix the invalid fields and retry with schema-conformant input.",
"operatorAction": "Inspect schema diagnostics, generated contracts, and validation metadata.",
"retryability": "not-retryable",
"redactionPolicy": "public",
"telemetry": {
"eventName": "croco.problem.info",
"severity": "info",
"attributes": ["problem.code", "problem.category", "problem.status"]
}
},
"lifecycle": {
"status": "active"
},
"sources": [
{
"file": "packages/metering-core/src/libs/problems/InvalidUsageValueProblem.ts",
"line": 7,
"column": 5,
"kind": "problem-constructor"
}
]
},
{
"code": "metering/quota-exceeded",
"category": "TooManyRequests",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
---
editUrl: false
next: false
prev: false
title: "InvalidUsageValueProblem"
---

A usage record contains a value that cannot be represented by every supported storage adapter.

## Extends

- [`Problem`](/api/problems-core/src/classes/problem/)

## Constructors

### Constructor

> **new InvalidUsageValueProblem**(`value`, `reason?`): `InvalidUsageValueProblem`

#### Parameters

##### value

`number`

##### reason?

`string` = `...`

#### Returns

`InvalidUsageValueProblem`

#### Overrides

`Problem.constructor`

## Properties

### category

> `readonly` **category**: [`ProblemCategory`](/api/problems-core/src/enumerations/problemcategory/)

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`category`](/api/problems-core/src/classes/problem/#category)

***

### cause?

> `readonly` `optional` **cause?**: `Error`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`cause`](/api/problems-core/src/classes/problem/#cause)

***

### code

> `readonly` **code**: `string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`code`](/api/problems-core/src/classes/problem/#code)

***

### detail?

> `readonly` `optional` **detail?**: `string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`detail`](/api/problems-core/src/classes/problem/#detail)

***

### extensions?

> `readonly` `optional` **extensions?**: [`ProblemExtensions`](/api/problems-core/src/type-aliases/problemextensions/)

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`extensions`](/api/problems-core/src/classes/problem/#extensions)

***

### instance?

> `readonly` `optional` **instance?**: `string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`instance`](/api/problems-core/src/classes/problem/#instance)

***

### message

> **message**: `string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`message`](/api/problems-core/src/classes/problem/#message)

***

### name

> **name**: `string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`name`](/api/problems-core/src/classes/problem/#name)

***

### stack?

> `optional` **stack?**: `string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`stack`](/api/problems-core/src/classes/problem/#stack)

***

### type

> `readonly` **type**: `string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`type`](/api/problems-core/src/classes/problem/#type)

***

### stackTraceLimit

> `static` **stackTraceLimit**: `number`

The `Error.stackTraceLimit` property specifies the number of stack frames
collected by a stack trace (whether generated by `new Error().stack` or
`Error.captureStackTrace(obj)`).

The default value is `10` but may be set to any valid JavaScript number. Changes
will affect any stack trace captured _after_ the value has been changed.

If set to a non-number value, or set to a negative number, stack traces will
not capture any frames.

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`stackTraceLimit`](/api/problems-core/src/classes/problem/#stacktracelimit)

## Accessors

### status

#### Get Signature

> **get** **status**(): `number`

##### Returns

`number`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`status`](/api/problems-core/src/classes/problem/#status)

***

### title

#### Get Signature

> **get** **title**(): `string`

##### Returns

`string`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`title`](/api/problems-core/src/classes/problem/#title)

## Methods

### toJSON()

> **toJSON**(): [`ProblemDetails`](/api/problems-core/src/type-aliases/problemdetails/)

#### Returns

[`ProblemDetails`](/api/problems-core/src/type-aliases/problemdetails/)

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`toJSON`](/api/problems-core/src/classes/problem/#tojson)

***

### captureStackTrace()

> `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void`

Creates a `.stack` property on `targetObject`, which when accessed returns
a string representing the location in the code at which
`Error.captureStackTrace()` was called.

```js
const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`
```

The first line of the trace will be prefixed with
`${myObject.name}: ${myObject.message}`.

The optional `constructorOpt` argument accepts a function. If given, all frames
above `constructorOpt`, including `constructorOpt`, will be omitted from the
generated stack trace.

The `constructorOpt` argument is useful for hiding implementation
details of error generation from the user. For instance:

```js
function a() {
b();
}

function b() {
c();
}

function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;

// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}

a();
```

#### Parameters

##### targetObject

`object`

##### constructorOpt?

`Function`

#### Returns

`void`

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`captureStackTrace`](/api/problems-core/src/classes/problem/#capturestacktrace)

***

### prepareStackTrace()

> `static` **prepareStackTrace**(`err`, `stackTraces`): `any`

#### Parameters

##### err

`Error`

##### stackTraces

`CallSite`[]

#### Returns

`any`

#### See

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

#### Inherited from

[`Problem`](/api/problems-core/src/classes/problem/).[`prepareStackTrace`](/api/problems-core/src/classes/problem/#preparestacktrace)
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ InvalidMeterProblem meter 없을 시

InvalidUsageEnvelopeProblem typed usage envelope이 meter 계약과 일치하지 않을 시

##### Throws

InvalidUsageValueProblem value가 1부터 2,147,483,647까지의 정수가 아닐 시

#### Call Signature

> **record**(`options`): `Promise`\<[`UsageRecord`](/api/metering-core/src/type-aliases/usagerecord/)\>
Expand Down Expand Up @@ -120,3 +124,7 @@ DuplicateRecordProblem 중복 idempotencyKey 시
##### Throws

InvalidMeterProblem meter 없을 시

##### Throws

InvalidUsageValueProblem value가 1부터 2,147,483,647까지의 정수가 아닐 시
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ record() 메서드의 호환성 옵션
### value?

> `optional` **value?**: `number`

Optional usage amount from 1 through 2_147_483_647. Defaults to 1.
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,5 @@ title: "UsageRecord"
### value

> **value**: `number`

Usage amount from 1 through 2_147_483_647, supported by every storage adapter.
Loading
Loading