@shiprev/server-sdk-node 0.1.0-beta.0 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -15
- package/dist/auth/token-verifier.d.ts +16 -2
- package/dist/auth/token-verifier.js +34 -7
- package/dist/client/connection-supervisor.d.ts +32 -1
- package/dist/client/connection-supervisor.js +109 -6
- package/dist/client/shiprev-client.d.ts +38 -3
- package/dist/client/shiprev-client.js +146 -6
- package/dist/mirror/degradation.d.ts +22 -0
- package/dist/mirror/degradation.js +67 -0
- package/dist/mirror/entitlement-mirror.d.ts +15 -1
- package/dist/mirror/entitlement-mirror.js +36 -2
- package/dist/ruling/adjudicator.js +5 -12
- package/dist/transport/http.transport.d.ts +6 -1
- package/dist/transport/http.transport.js +43 -2
- package/dist/types.d.ts +106 -2
- package/dist/types.js +48 -1
- package/package.json +10 -11
package/dist/types.d.ts
CHANGED
|
@@ -4,7 +4,13 @@ export interface Ruling {
|
|
|
4
4
|
remaining?: number;
|
|
5
5
|
limit?: number;
|
|
6
6
|
source: RulingSource;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Why the call was refused. `quota_exhausted`: the allowance is used up and the credits do not cover the overage
|
|
9
|
+
* (BLOCK, ALLOW_AND_BILL). `plan_upgrade_required`: the same under the PROMPT_UPGRADE policy, where the way on
|
|
10
|
+
* is a larger plan. Other values: `capability_not_found`, `capability_not_granted`, `disconnected_write`,
|
|
11
|
+
* `invalid_request`.
|
|
12
|
+
*/
|
|
13
|
+
reason?: 'quota_exhausted' | 'plan_upgrade_required' | 'disconnected_write' | 'invalid_request' | string;
|
|
8
14
|
adjudicated_at: number;
|
|
9
15
|
}
|
|
10
16
|
export interface AdjudicateOptions {
|
|
@@ -50,7 +56,59 @@ export interface SnapshotItem {
|
|
|
50
56
|
export interface OrganizationSnapshot {
|
|
51
57
|
revision: number;
|
|
52
58
|
wallet_balance: string;
|
|
59
|
+
/**
|
|
60
|
+
* Credits the wallet may go below zero by for ALLOW_AND_BILL capabilities. Informational: the mirror never widens
|
|
61
|
+
* a local ruling with it; beyond the allowance the service decides. Absent on instances that predate it.
|
|
62
|
+
*/
|
|
63
|
+
overdraft_limit?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Credits that could still be spent when the entry was read: `wallet_balance` plus the overdraft limit, minus
|
|
66
|
+
* the credits held by open reservations. Informational, like `overdraft_limit`. Absent on instances that predate it.
|
|
67
|
+
*/
|
|
68
|
+
available_credits?: string;
|
|
53
69
|
items: Record<string, SnapshotItem>;
|
|
70
|
+
/**
|
|
71
|
+
* Degradation level the instance computed for the organization. Instances that predate it leave it out; a
|
|
72
|
+
* value outside `DegradationLevel` is read as `RESTRICT_USAGE`.
|
|
73
|
+
*/
|
|
74
|
+
degradation_level?: string;
|
|
75
|
+
/** ISO 8601 time until which this entry may be used without a newer answer. */
|
|
76
|
+
valid_until?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Degradation level of a buyer organization, strictly increasing: `RESTRICT_NEW` blocks new commercial objects,
|
|
80
|
+
* `RESTRICT_USAGE` every write, `RESTRICT_READ` end-user reads too, and `SUSPENDED` is an explicit "no
|
|
81
|
+
* entitlement". Computed by the instance; a client only enforces it.
|
|
82
|
+
*/
|
|
83
|
+
export type DegradationLevel = 'NONE' | 'RESTRICT_NEW' | 'RESTRICT_USAGE' | 'RESTRICT_READ' | 'SUSPENDED';
|
|
84
|
+
/**
|
|
85
|
+
* The level to act on for one organization, combined from the mirror entry and what the client observes:
|
|
86
|
+
* `source: 'answer'` while the instance answers (online, or a pull of the organization just succeeded);
|
|
87
|
+
* `'degraded'` when acting on the mirror without an answer, with `expired` once the entry's `valid_until` has
|
|
88
|
+
* passed (the level is then at least `RESTRICT_USAGE`).
|
|
89
|
+
*/
|
|
90
|
+
export interface EffectiveLevel {
|
|
91
|
+
level: DegradationLevel;
|
|
92
|
+
source: 'answer' | 'degraded';
|
|
93
|
+
expired: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** One usage event for `sendUsage()`. The quantity may be a decimal string, sent as it is. */
|
|
96
|
+
export interface UsageSendEvent {
|
|
97
|
+
usage_event_id: string;
|
|
98
|
+
organization_id: string;
|
|
99
|
+
capability_key: string;
|
|
100
|
+
quantity: number | string;
|
|
101
|
+
occurred_at: string;
|
|
102
|
+
properties?: Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
/** What the instance did with a batch sent by `sendUsage()`. */
|
|
105
|
+
export interface UsageSendResult {
|
|
106
|
+
accepted_count: number;
|
|
107
|
+
duplicates_dropped: number;
|
|
108
|
+
rejected: Array<{
|
|
109
|
+
usage_event_id: string;
|
|
110
|
+
reason: string;
|
|
111
|
+
}>;
|
|
54
112
|
}
|
|
55
113
|
/**
|
|
56
114
|
* Application-level entitlement snapshot envelope served by `GET /sdk/v1/entitlements/snapshot`:
|
|
@@ -117,7 +175,7 @@ export interface ReserveOptions {
|
|
|
117
175
|
}
|
|
118
176
|
export interface ReservationResult {
|
|
119
177
|
reservation_id: string;
|
|
120
|
-
status: '
|
|
178
|
+
status: 'RESERVED' | 'CONFIRMED' | 'RELEASED';
|
|
121
179
|
expires_at: string;
|
|
122
180
|
}
|
|
123
181
|
export interface ConfirmReservationResult {
|
|
@@ -150,11 +208,57 @@ export interface ShipRevClientOptions {
|
|
|
150
208
|
backlog?: BacklogConfig;
|
|
151
209
|
on_flush_failure?: (events: UsageEventItem[], error: Error) => void;
|
|
152
210
|
on_rejected?: (rejected: RejectedEvent[]) => void;
|
|
211
|
+
/**
|
|
212
|
+
* Called when the effective level of an organization changes: a new entry, the connection going up or down,
|
|
213
|
+
* or an entry's `valid_until` passing while disconnected. Never called for an organization the mirror does
|
|
214
|
+
* not hold.
|
|
215
|
+
*/
|
|
216
|
+
on_level_change?: (organization_id: string, level: EffectiveLevel) => void;
|
|
153
217
|
}
|
|
154
218
|
export declare class DisconnectedWriteError extends Error {
|
|
155
219
|
readonly reason = "disconnected_write";
|
|
156
220
|
constructor(message?: string);
|
|
157
221
|
}
|
|
222
|
+
/** The `error.code` values of a refusal for funds, as the instance sends them and the two error types carry them. */
|
|
223
|
+
export type FundsRefusalCode = 'QUOTA_EXHAUSTED' | 'PLAN_UPGRADE_REQUIRED';
|
|
224
|
+
/** Where a funds refusal came from: the instance's HTTP answer (`status`, `request_id`) or a local ruling. */
|
|
225
|
+
export interface FundsRefusalDetails {
|
|
226
|
+
/** HTTP status of the instance's answer (400); absent when the refusal is a local ruling. */
|
|
227
|
+
status?: number;
|
|
228
|
+
/** The instance's request id, for support; absent when the refusal is a local ruling. */
|
|
229
|
+
request_id?: string;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The allowance is used up and the credits do not cover the overage: the buyer can top up (BLOCK, ALLOW_AND_BILL
|
|
233
|
+
* past the overdraft limit, credits held by other reservations, or an overage that is not for sale). Thrown by
|
|
234
|
+
* `reserve()` when the instance answers `code: 'QUOTA_EXHAUSTED'`, and by `guardWrite()` when the local ruling is
|
|
235
|
+
* `quota_exhausted`. The type follows the code, never the message.
|
|
236
|
+
*/
|
|
237
|
+
export declare class QuotaExhaustedError extends Error {
|
|
238
|
+
readonly code: "QUOTA_EXHAUSTED";
|
|
239
|
+
readonly status?: number;
|
|
240
|
+
readonly request_id?: string;
|
|
241
|
+
constructor(message?: string, details?: FundsRefusalDetails);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The capability's policy is PROMPT_UPGRADE and the allowance is used up: the buyer has to move to a larger plan.
|
|
245
|
+
* Thrown by `reserve()` when the instance answers `code: 'PLAN_UPGRADE_REQUIRED'`, and by `guardWrite()` when the
|
|
246
|
+
* local ruling is `plan_upgrade_required`. The type follows the code, never the message.
|
|
247
|
+
*/
|
|
248
|
+
export declare class PlanUpgradeRequiredError extends Error {
|
|
249
|
+
readonly code: "PLAN_UPGRADE_REQUIRED";
|
|
250
|
+
readonly status?: number;
|
|
251
|
+
readonly request_id?: string;
|
|
252
|
+
constructor(message?: string, details?: FundsRefusalDetails);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Thrown by `sendUsage()` when the batch did not reach the instance or the instance failed to process it (a
|
|
256
|
+
* network error, a timeout or a 5xx answer). Nothing in the batch can be assumed delivered; sending it again is
|
|
257
|
+
* safe, as every event carries its idempotency key.
|
|
258
|
+
*/
|
|
259
|
+
export declare class UsageDeliveryError extends Error {
|
|
260
|
+
constructor(message?: string);
|
|
261
|
+
}
|
|
158
262
|
/** Why `verifyToken()` refused a correctly signed, unexpired token. */
|
|
159
263
|
export type TokenRejectionReason =
|
|
160
264
|
/** The client has not obtained a machine token yet, so the expected issuer and application are unknown. */
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TokenRejectedError = exports.DisconnectedWriteError = void 0;
|
|
3
|
+
exports.TokenRejectedError = exports.UsageDeliveryError = exports.PlanUpgradeRequiredError = exports.QuotaExhaustedError = exports.DisconnectedWriteError = void 0;
|
|
4
4
|
class DisconnectedWriteError extends Error {
|
|
5
5
|
reason = 'disconnected_write';
|
|
6
6
|
constructor(message = 'Write operation denied while client is disconnected') {
|
|
@@ -9,6 +9,53 @@ class DisconnectedWriteError extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
exports.DisconnectedWriteError = DisconnectedWriteError;
|
|
12
|
+
/**
|
|
13
|
+
* The allowance is used up and the credits do not cover the overage: the buyer can top up (BLOCK, ALLOW_AND_BILL
|
|
14
|
+
* past the overdraft limit, credits held by other reservations, or an overage that is not for sale). Thrown by
|
|
15
|
+
* `reserve()` when the instance answers `code: 'QUOTA_EXHAUSTED'`, and by `guardWrite()` when the local ruling is
|
|
16
|
+
* `quota_exhausted`. The type follows the code, never the message.
|
|
17
|
+
*/
|
|
18
|
+
class QuotaExhaustedError extends Error {
|
|
19
|
+
code = 'QUOTA_EXHAUSTED';
|
|
20
|
+
status;
|
|
21
|
+
request_id;
|
|
22
|
+
constructor(message = 'The allowance is used up and the credits do not cover the overage', details = {}) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'QuotaExhaustedError';
|
|
25
|
+
this.status = details.status;
|
|
26
|
+
this.request_id = details.request_id;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.QuotaExhaustedError = QuotaExhaustedError;
|
|
30
|
+
/**
|
|
31
|
+
* The capability's policy is PROMPT_UPGRADE and the allowance is used up: the buyer has to move to a larger plan.
|
|
32
|
+
* Thrown by `reserve()` when the instance answers `code: 'PLAN_UPGRADE_REQUIRED'`, and by `guardWrite()` when the
|
|
33
|
+
* local ruling is `plan_upgrade_required`. The type follows the code, never the message.
|
|
34
|
+
*/
|
|
35
|
+
class PlanUpgradeRequiredError extends Error {
|
|
36
|
+
code = 'PLAN_UPGRADE_REQUIRED';
|
|
37
|
+
status;
|
|
38
|
+
request_id;
|
|
39
|
+
constructor(message = 'The allowance is used up; the plan has to be upgraded', details = {}) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = 'PlanUpgradeRequiredError';
|
|
42
|
+
this.status = details.status;
|
|
43
|
+
this.request_id = details.request_id;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.PlanUpgradeRequiredError = PlanUpgradeRequiredError;
|
|
47
|
+
/**
|
|
48
|
+
* Thrown by `sendUsage()` when the batch did not reach the instance or the instance failed to process it (a
|
|
49
|
+
* network error, a timeout or a 5xx answer). Nothing in the batch can be assumed delivered; sending it again is
|
|
50
|
+
* safe, as every event carries its idempotency key.
|
|
51
|
+
*/
|
|
52
|
+
class UsageDeliveryError extends Error {
|
|
53
|
+
constructor(message = 'Usage events could not be delivered') {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = 'UsageDeliveryError';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.UsageDeliveryError = UsageDeliveryError;
|
|
12
59
|
/**
|
|
13
60
|
* Thrown by `verifyToken()` when a token is correctly signed but is not a session token this client
|
|
14
61
|
* accepts. The message names the reason and never contains the token.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shiprev/server-sdk-node",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.2",
|
|
4
4
|
"description": "Server SDK for Node.js and NestJS services: token verification, local entitlement rulings, usage reporting, and two-phase reservations against a ShipRev instance.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shiprev",
|
|
@@ -42,15 +42,6 @@
|
|
|
42
42
|
"access": "public",
|
|
43
43
|
"tag": "beta"
|
|
44
44
|
},
|
|
45
|
-
"scripts": {
|
|
46
|
-
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
47
|
-
"build": "tsc -p tsconfig.build.json",
|
|
48
|
-
"prepack": "pnpm run clean && pnpm run build",
|
|
49
|
-
"test": "jest",
|
|
50
|
-
"test:nest9": "node scripts/test-nest9.mjs",
|
|
51
|
-
"lint": "eslint \"src/**/*.ts\"",
|
|
52
|
-
"format": "prettier --write \"src/**/*.ts\""
|
|
53
|
-
},
|
|
54
45
|
"peerDependencies": {
|
|
55
46
|
"@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0",
|
|
56
47
|
"@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0",
|
|
@@ -96,5 +87,13 @@
|
|
|
96
87
|
"^.+\\.ts$": "ts-jest"
|
|
97
88
|
},
|
|
98
89
|
"testEnvironment": "node"
|
|
90
|
+
},
|
|
91
|
+
"scripts": {
|
|
92
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
93
|
+
"build": "tsc -p tsconfig.build.json",
|
|
94
|
+
"test": "jest",
|
|
95
|
+
"test:nest9": "node scripts/test-nest9.mjs",
|
|
96
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
97
|
+
"format": "prettier --write \"src/**/*.ts\""
|
|
99
98
|
}
|
|
100
|
-
}
|
|
99
|
+
}
|