@billfn/core 0.3.0 → 0.4.0
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/dist/grants.d.ts +77 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +459 -0
- package/dist/grants.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +24 -2
- package/dist/schema.js.map +1 -1
- package/dist/service.d.ts +3 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +70 -23
- package/dist/service.js.map +1 -1
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/grants.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Adapter } from '@superfunctions/db';
|
|
2
|
+
import type { MetricsEmitter } from '@superfunctions/metrics';
|
|
3
|
+
import type { BillFnBillingEvent, BillFnCatalog, BillFnCreateGrantRequest, BillFnEntitlementGrant, BillFnEntitlementGrantSummary, BillFnEntitlementGrantKind, BillFnEntitlementSnapshot, BillFnOperator, BillFnRevokeGrantRequest } from './types.js';
|
|
4
|
+
/** The service dependencies grants use; the billing event and duplicate-key helpers are the service's own. */
|
|
5
|
+
export interface GrantDeps {
|
|
6
|
+
db: Adapter;
|
|
7
|
+
namespace: string;
|
|
8
|
+
catalog: BillFnCatalog;
|
|
9
|
+
now: () => Date;
|
|
10
|
+
metrics: MetricsEmitter;
|
|
11
|
+
/** Records the event through `db` when given (a transaction), otherwise through the service's adapter. */
|
|
12
|
+
recordBillingEvent(event: BillFnBillingEvent, db?: Adapter): Promise<void>;
|
|
13
|
+
isDuplicateRecordError(error: unknown): boolean;
|
|
14
|
+
}
|
|
15
|
+
export type GrantCreateInput = {
|
|
16
|
+
planKey: string;
|
|
17
|
+
kind: BillFnEntitlementGrantKind;
|
|
18
|
+
expiresAt: string;
|
|
19
|
+
operator: BillFnOperator | null;
|
|
20
|
+
reason: string | null;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Validates a grant request before any write, so a refused request creates no billing account. The expiry's
|
|
24
|
+
* position against now is checked separately (`requireFutureExpiry`), after `repairGrantEvents`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readCreateGrantInput(deps: GrantDeps, input: BillFnCreateGrantRequest): GrantCreateInput;
|
|
27
|
+
/** Refuses a grant whose expiry is not after now. */
|
|
28
|
+
export declare function requireFutureExpiry(deps: GrantDeps, input: GrantCreateInput): void;
|
|
29
|
+
/**
|
|
30
|
+
* Records the account's grant events that are missing. Without transactions a grant change commits before its
|
|
31
|
+
* billing event, and a change whose event write fails stands (see `commitWithEvent`). Every grant create and
|
|
32
|
+
* revoke runs this first, before it checks the expiry or reads the grant it acts on, and every entitlements read
|
|
33
|
+
* runs it through `listRepairedGrants`: whatever that request then does, the account's earlier creations and
|
|
34
|
+
* revocations, also of grants that have since expired or been revoked, have their events. The events' ids and
|
|
35
|
+
* payloads derive from the stored grant, so a recorded event is kept and none is written twice. With
|
|
36
|
+
* transactions each change commits with its event and there is nothing to repair. Best effort: finding the
|
|
37
|
+
* missing events and writing each one fail on their own, a failed write skips no other event, and a repair that
|
|
38
|
+
* cannot write leaves the events to a later one and never fails the request. Pass the account's grants when
|
|
39
|
+
* already read.
|
|
40
|
+
*/
|
|
41
|
+
export declare function repairGrantEvents(deps: GrantDeps, billingAccountId: string, grants?: BillFnEntitlementGrant[]): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* The account's grants for an entitlements read: their missing events are repaired first (see
|
|
44
|
+
* `repairGrantEvents`), and their status is derived after that, so a grant that ends during the repair reads
|
|
45
|
+
* as ended.
|
|
46
|
+
*/
|
|
47
|
+
export declare function listRepairedGrants(deps: GrantDeps, billingAccountId: string): Promise<BillFnEntitlementGrant[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Stores a grant. A trial's id is derived from its billing account, so the
|
|
50
|
+
* unique id refuses a second trial even when two requests race. Comps take
|
|
51
|
+
* numbered ids (`grant_comp_<account>_<n>`): a comp is refused while the
|
|
52
|
+
* latest one is active, and a new one claims the next number, so of two racing
|
|
53
|
+
* comps the unique id stores one and refuses the other. The grant and its
|
|
54
|
+
* creation event commit together (see `commitWithEvent`); without transactions
|
|
55
|
+
* the caller runs `repairGrantEvents` first, and a request refused by a racing
|
|
56
|
+
* grant records that grant's creation event.
|
|
57
|
+
*/
|
|
58
|
+
export declare function insertGrant(deps: GrantDeps, billingAccountId: string, input: GrantCreateInput): Promise<BillFnEntitlementGrant>;
|
|
59
|
+
/**
|
|
60
|
+
* Revokes a comp. A revoked comp is returned unchanged; a racing revoke is applied once. Without transactions the
|
|
61
|
+
* account's unrecorded grant events are recorded first, also when the grant is then not found.
|
|
62
|
+
*/
|
|
63
|
+
export declare function revokeGrant(deps: GrantDeps, input: BillFnRevokeGrantRequest): Promise<BillFnEntitlementGrant>;
|
|
64
|
+
/** The account's grants, newest first, with their derived status. */
|
|
65
|
+
export declare function listGrants(deps: GrantDeps, billingAccountId: string): Promise<BillFnEntitlementGrant[]>;
|
|
66
|
+
/** A grant as its billing subject sees it: without the operator, reasons or revoking operator. */
|
|
67
|
+
export declare function toGrantSummary(grant: BillFnEntitlementGrant): BillFnEntitlementGrantSummary;
|
|
68
|
+
/**
|
|
69
|
+
* The account's entitlements: the paid snapshot while it is not `inactive`
|
|
70
|
+
* (paid supersedes a grant), otherwise the active grant's plan (`trialing` for
|
|
71
|
+
* a trial, `active` for a comp) until the grant's expiry. Without an active
|
|
72
|
+
* grant the paid snapshot stands; an account with ended grants and no paid
|
|
73
|
+
* snapshot is `inactive` with no features or limits. Grants are read only when
|
|
74
|
+
* the paid snapshot does not decide, unless the caller passes them.
|
|
75
|
+
*/
|
|
76
|
+
export declare function applyGrants(deps: GrantDeps, billingAccountId: string, paid: BillFnEntitlementSnapshot | null, grants?: BillFnEntitlementGrant[]): Promise<BillFnEntitlementSnapshot | null>;
|
|
77
|
+
//# sourceMappingURL=grants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAG9D,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,sBAAsB,EACtB,6BAA6B,EAC7B,0BAA0B,EAC1B,yBAAyB,EACzB,cAAc,EACd,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAQpB,8GAA8G;AAC9G,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,OAAO,EAAE,cAAc,CAAC;IACxB,0GAA0G;IAC1G,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CACjD;AAKD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,0BAA0B,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,wBAAwB,GAAG,gBAAgB,CAqBvG;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,QAI3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,sBAAsB,EAAE,iBA8BnH;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,qCAKjF;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,mCA6CnG;AAwBD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,wBAAwB,mCA0DjF;AAgDD,qEAAqE;AACrE,wBAAsB,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAW7G;AAED,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,KAAK,EAAE,sBAAsB,GAAG,6BAA6B,CAU3F;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,SAAS,EACf,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,yBAAyB,GAAG,IAAI,EACtC,MAAM,CAAC,EAAE,sBAAsB,EAAE,GAChC,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAyC3C"}
|
package/dist/grants.js
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { createBillFnError } from './errors.js';
|
|
2
|
+
import { resolvePlan, toIsoString } from './helpers.js';
|
|
3
|
+
const GRANTS_TABLE = 'entitlementGrants';
|
|
4
|
+
const BILLING_EVENTS_TABLE = 'billingEvents';
|
|
5
|
+
const GRANT_KINDS = new Set(['trial', 'comp']);
|
|
6
|
+
const REVOKE_ATTEMPTS = 3;
|
|
7
|
+
const CREATE_ATTEMPTS = 3;
|
|
8
|
+
/**
|
|
9
|
+
* Validates a grant request before any write, so a refused request creates no billing account. The expiry's
|
|
10
|
+
* position against now is checked separately (`requireFutureExpiry`), after `repairGrantEvents`.
|
|
11
|
+
*/
|
|
12
|
+
export function readCreateGrantInput(deps, input) {
|
|
13
|
+
if (typeof input.billingAccountId !== 'string' || input.billingAccountId.length === 0) {
|
|
14
|
+
throw validation('billingAccountId must be a non-empty string');
|
|
15
|
+
}
|
|
16
|
+
if (!GRANT_KINDS.has(input.kind)) {
|
|
17
|
+
throw validation("kind must be 'trial' or 'comp'");
|
|
18
|
+
}
|
|
19
|
+
if (typeof input.planKey !== 'string' || !resolvePlan(deps.catalog, input.planKey)) {
|
|
20
|
+
throw createBillFnError({ code: 'BILLFN_NOT_FOUND', message: `Plan not found: ${String(input.planKey)}` });
|
|
21
|
+
}
|
|
22
|
+
const expiresAt = typeof input.expiresAt === 'string' ? Date.parse(input.expiresAt) : Number.NaN;
|
|
23
|
+
if (!Number.isFinite(expiresAt)) {
|
|
24
|
+
throw validation('expiresAt must be an ISO 8601 time');
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
planKey: input.planKey,
|
|
28
|
+
kind: input.kind,
|
|
29
|
+
expiresAt: toIsoString(new Date(expiresAt)),
|
|
30
|
+
operator: readOperator(input.operator),
|
|
31
|
+
reason: readReason(input.reason)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Refuses a grant whose expiry is not after now. */
|
|
35
|
+
export function requireFutureExpiry(deps, input) {
|
|
36
|
+
if (Date.parse(input.expiresAt) <= deps.now().getTime()) {
|
|
37
|
+
throw validation('expiresAt must be in the future');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Records the account's grant events that are missing. Without transactions a grant change commits before its
|
|
42
|
+
* billing event, and a change whose event write fails stands (see `commitWithEvent`). Every grant create and
|
|
43
|
+
* revoke runs this first, before it checks the expiry or reads the grant it acts on, and every entitlements read
|
|
44
|
+
* runs it through `listRepairedGrants`: whatever that request then does, the account's earlier creations and
|
|
45
|
+
* revocations, also of grants that have since expired or been revoked, have their events. The events' ids and
|
|
46
|
+
* payloads derive from the stored grant, so a recorded event is kept and none is written twice. With
|
|
47
|
+
* transactions each change commits with its event and there is nothing to repair. Best effort: finding the
|
|
48
|
+
* missing events and writing each one fail on their own, a failed write skips no other event, and a repair that
|
|
49
|
+
* cannot write leaves the events to a later one and never fails the request. Pass the account's grants when
|
|
50
|
+
* already read.
|
|
51
|
+
*/
|
|
52
|
+
export async function repairGrantEvents(deps, billingAccountId, grants) {
|
|
53
|
+
if (deps.db.capabilities.transactions.supported) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let missing;
|
|
57
|
+
try {
|
|
58
|
+
const events = (grants ?? (await listGrants(deps, billingAccountId))).flatMap((grant) => grant.revokedAt ? [grantCreatedEvent(grant), grantRevokedEvent(grant)] : [grantCreatedEvent(grant)]);
|
|
59
|
+
if (events.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const recorded = await deps.db.findMany({
|
|
63
|
+
model: BILLING_EVENTS_TABLE,
|
|
64
|
+
where: [{ field: 'id', operator: 'in', value: events.map((event) => event.id) }],
|
|
65
|
+
namespace: deps.namespace
|
|
66
|
+
});
|
|
67
|
+
const recordedIds = new Set(recorded.map((event) => event.id));
|
|
68
|
+
missing = events.filter((candidate) => !recordedIds.has(candidate.id));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
deps.metrics.track('grant.event_deferred', { type: 'repair' });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
for (const event of missing) {
|
|
75
|
+
try {
|
|
76
|
+
await deps.recordBillingEvent(event);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
deps.metrics.track('grant.event_deferred', { type: event.type });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The account's grants for an entitlements read: their missing events are repaired first (see
|
|
85
|
+
* `repairGrantEvents`), and their status is derived after that, so a grant that ends during the repair reads
|
|
86
|
+
* as ended.
|
|
87
|
+
*/
|
|
88
|
+
export async function listRepairedGrants(deps, billingAccountId) {
|
|
89
|
+
const grants = await listGrants(deps, billingAccountId);
|
|
90
|
+
await repairGrantEvents(deps, billingAccountId, grants);
|
|
91
|
+
const now = deps.now();
|
|
92
|
+
return grants.map((grant) => withStatus(grant, now));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Stores a grant. A trial's id is derived from its billing account, so the
|
|
96
|
+
* unique id refuses a second trial even when two requests race. Comps take
|
|
97
|
+
* numbered ids (`grant_comp_<account>_<n>`): a comp is refused while the
|
|
98
|
+
* latest one is active, and a new one claims the next number, so of two racing
|
|
99
|
+
* comps the unique id stores one and refuses the other. The grant and its
|
|
100
|
+
* creation event commit together (see `commitWithEvent`); without transactions
|
|
101
|
+
* the caller runs `repairGrantEvents` first, and a request refused by a racing
|
|
102
|
+
* grant records that grant's creation event.
|
|
103
|
+
*/
|
|
104
|
+
export async function insertGrant(deps, billingAccountId, input) {
|
|
105
|
+
for (let attempt = 0; attempt < CREATE_ATTEMPTS; attempt += 1) {
|
|
106
|
+
const id = input.kind === 'trial' ? trialGrantId(billingAccountId) : await nextCompId(deps, billingAccountId);
|
|
107
|
+
const timestamp = toIsoString(deps.now());
|
|
108
|
+
const record = {
|
|
109
|
+
id,
|
|
110
|
+
billingAccountId,
|
|
111
|
+
planKey: input.planKey,
|
|
112
|
+
kind: input.kind,
|
|
113
|
+
startsAt: timestamp,
|
|
114
|
+
expiresAt: input.expiresAt,
|
|
115
|
+
revokedAt: null,
|
|
116
|
+
operator: input.operator,
|
|
117
|
+
reason: input.reason,
|
|
118
|
+
revokedBy: null,
|
|
119
|
+
revokeReason: null,
|
|
120
|
+
createdAt: timestamp,
|
|
121
|
+
updatedAt: timestamp
|
|
122
|
+
};
|
|
123
|
+
try {
|
|
124
|
+
await commitWithEvent(deps, grantCreatedEvent(record), async (db) => {
|
|
125
|
+
await db.create({ model: GRANTS_TABLE, data: record, namespace: deps.namespace });
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (!deps.isDuplicateRecordError(error)) {
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
const existing = await findGrant(deps, billingAccountId, record.id);
|
|
134
|
+
if (existing) {
|
|
135
|
+
await recordCommittedEvent(deps, grantCreatedEvent(existing));
|
|
136
|
+
}
|
|
137
|
+
if (input.kind === 'trial') {
|
|
138
|
+
throw conflict('A trial was already granted to this billing account', 'trial_already_granted', record.id);
|
|
139
|
+
}
|
|
140
|
+
// The comp that took this number is active, or (read stale) it ended and the next number is free.
|
|
141
|
+
if (!existing || withStatus(existing, deps.now()).status === 'active') {
|
|
142
|
+
throw activeCompExists(record.id);
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
deps.metrics.track('grant.created', { kind: record.kind });
|
|
147
|
+
return withStatus(record, deps.now());
|
|
148
|
+
}
|
|
149
|
+
throw createBillFnError({ code: 'BILLFN_CONFLICT', message: 'Unable to create the grant after multiple retries' });
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The id of the account's next comp. Comp n is created only while comp n-1 is
|
|
153
|
+
* not active, so the latest comp is the only one that can be active; while it
|
|
154
|
+
* is, the account's active comp is refused.
|
|
155
|
+
*/
|
|
156
|
+
async function nextCompId(deps, billingAccountId) {
|
|
157
|
+
const prefix = compGrantIdPrefix(billingAccountId);
|
|
158
|
+
let latest;
|
|
159
|
+
let latestNumber = 0;
|
|
160
|
+
for (const grant of await listGrants(deps, billingAccountId)) {
|
|
161
|
+
const number = grant.kind === 'comp' && grant.id.startsWith(prefix) ? Number(grant.id.slice(prefix.length)) : 0;
|
|
162
|
+
if (Number.isSafeInteger(number) && number > latestNumber) {
|
|
163
|
+
latest = grant;
|
|
164
|
+
latestNumber = number;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (latest?.status === 'active') {
|
|
168
|
+
throw activeCompExists(latest.id);
|
|
169
|
+
}
|
|
170
|
+
return `${prefix}${latestNumber + 1}`;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Revokes a comp. A revoked comp is returned unchanged; a racing revoke is applied once. Without transactions the
|
|
174
|
+
* account's unrecorded grant events are recorded first, also when the grant is then not found.
|
|
175
|
+
*/
|
|
176
|
+
export async function revokeGrant(deps, input) {
|
|
177
|
+
if (typeof input.billingAccountId !== 'string' || input.billingAccountId.length === 0) {
|
|
178
|
+
throw validation('billingAccountId must be a non-empty string');
|
|
179
|
+
}
|
|
180
|
+
if (typeof input.grantId !== 'string' || input.grantId.length === 0) {
|
|
181
|
+
throw validation('grantId must be a non-empty string');
|
|
182
|
+
}
|
|
183
|
+
const operator = readOperator(input.operator);
|
|
184
|
+
const reason = readReason(input.reason);
|
|
185
|
+
await repairGrantEvents(deps, input.billingAccountId);
|
|
186
|
+
for (let attempt = 0; attempt < REVOKE_ATTEMPTS; attempt += 1) {
|
|
187
|
+
const existing = await findGrant(deps, input.billingAccountId, input.grantId);
|
|
188
|
+
if (!existing) {
|
|
189
|
+
throw createBillFnError({ code: 'BILLFN_NOT_FOUND', message: 'Grant not found' });
|
|
190
|
+
}
|
|
191
|
+
if (existing.kind !== 'comp') {
|
|
192
|
+
throw createBillFnError({
|
|
193
|
+
code: 'BILLFN_VALIDATION_ERROR',
|
|
194
|
+
message: 'Only a comp grant can be revoked',
|
|
195
|
+
details: { reason: 'grant_not_revocable' }
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (existing.revokedAt) {
|
|
199
|
+
await recordCommittedEvent(deps, grantRevokedEvent(existing));
|
|
200
|
+
return withStatus(existing, deps.now());
|
|
201
|
+
}
|
|
202
|
+
const now = deps.now();
|
|
203
|
+
const revoked = {
|
|
204
|
+
...existing,
|
|
205
|
+
revokedAt: toIsoString(now),
|
|
206
|
+
revokedBy: operator,
|
|
207
|
+
revokeReason: reason,
|
|
208
|
+
// Strictly later than the stored time, so a racing revoke's compare-and-set on it fails.
|
|
209
|
+
updatedAt: laterThan(now, existing.updatedAt)
|
|
210
|
+
};
|
|
211
|
+
const applied = await commitWithEvent(deps, grantRevokedEvent(revoked), async (db) => {
|
|
212
|
+
const updated = await db.updateMany({
|
|
213
|
+
model: GRANTS_TABLE,
|
|
214
|
+
where: [
|
|
215
|
+
{ field: 'id', operator: 'eq', value: existing.id },
|
|
216
|
+
{ field: 'updatedAt', operator: 'eq', value: existing.updatedAt }
|
|
217
|
+
],
|
|
218
|
+
data: {
|
|
219
|
+
revokedAt: revoked.revokedAt,
|
|
220
|
+
revokedBy: revoked.revokedBy,
|
|
221
|
+
revokeReason: revoked.revokeReason,
|
|
222
|
+
updatedAt: revoked.updatedAt
|
|
223
|
+
},
|
|
224
|
+
namespace: deps.namespace
|
|
225
|
+
});
|
|
226
|
+
return updated === 1;
|
|
227
|
+
});
|
|
228
|
+
if (applied) {
|
|
229
|
+
deps.metrics.track('grant.revoked', { kind: revoked.kind });
|
|
230
|
+
return withStatus(revoked, now);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
throw createBillFnError({ code: 'BILLFN_CONFLICT', message: 'Unable to revoke the grant after multiple retries' });
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Applies a grant change and records its billing event. Where the adapter
|
|
237
|
+
* supports transactions (memory, Prisma, Kysely, Drizzle Postgres) both commit
|
|
238
|
+
* in one transaction, so a failed event write leaves no grant change and the
|
|
239
|
+
* request fails. Without transactions (Drizzle SQLite) nothing can undo the
|
|
240
|
+
* change safely, so the committed change is the outcome: the grant stores its
|
|
241
|
+
* operator and reasons, a failed event write is retried once, and the request
|
|
242
|
+
* succeeds. An event still unrecorded derives from the grant and is recorded
|
|
243
|
+
* once by the account's next grant create or revoke or entitlements read
|
|
244
|
+
* (`repairGrantEvents`). Returns whether `write` applied the change; an
|
|
245
|
+
* unapplied change records no event.
|
|
246
|
+
*/
|
|
247
|
+
async function commitWithEvent(deps, event, write) {
|
|
248
|
+
if (deps.db.capabilities.transactions.supported) {
|
|
249
|
+
return deps.db.transaction(async (transaction) => {
|
|
250
|
+
const db = transaction;
|
|
251
|
+
if (!(await write(db))) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
await deps.recordBillingEvent(event, db);
|
|
255
|
+
return true;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
if (!(await write(deps.db))) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
await recordCommittedEvent(deps, event);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Records the event of a committed grant change: retried once, then left to `repairGrantEvents`. Never fails
|
|
266
|
+
* the request, whose outcome is the committed change.
|
|
267
|
+
*/
|
|
268
|
+
async function recordCommittedEvent(deps, event) {
|
|
269
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
270
|
+
try {
|
|
271
|
+
await deps.recordBillingEvent(event);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Retried once, then deferred.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
deps.metrics.track('grant.event_deferred', { type: event.type });
|
|
279
|
+
}
|
|
280
|
+
/** The account's grants, newest first, with their derived status. */
|
|
281
|
+
export async function listGrants(deps, billingAccountId) {
|
|
282
|
+
const records = await deps.db.findMany({
|
|
283
|
+
model: GRANTS_TABLE,
|
|
284
|
+
where: [{ field: 'billingAccountId', operator: 'eq', value: billingAccountId }],
|
|
285
|
+
orderBy: [{ field: 'createdAt', direction: 'desc' }],
|
|
286
|
+
namespace: deps.namespace
|
|
287
|
+
});
|
|
288
|
+
const now = deps.now();
|
|
289
|
+
return records
|
|
290
|
+
.map((record) => withStatus(record, now))
|
|
291
|
+
.sort((left, right) => compareTimes(right.createdAt, left.createdAt) || right.id.localeCompare(left.id));
|
|
292
|
+
}
|
|
293
|
+
/** A grant as its billing subject sees it: without the operator, reasons or revoking operator. */
|
|
294
|
+
export function toGrantSummary(grant) {
|
|
295
|
+
return {
|
|
296
|
+
id: grant.id,
|
|
297
|
+
kind: grant.kind,
|
|
298
|
+
planKey: grant.planKey,
|
|
299
|
+
status: grant.status,
|
|
300
|
+
startsAt: grant.startsAt,
|
|
301
|
+
expiresAt: grant.expiresAt,
|
|
302
|
+
revokedAt: grant.revokedAt ?? null
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* The account's entitlements: the paid snapshot while it is not `inactive`
|
|
307
|
+
* (paid supersedes a grant), otherwise the active grant's plan (`trialing` for
|
|
308
|
+
* a trial, `active` for a comp) until the grant's expiry. Without an active
|
|
309
|
+
* grant the paid snapshot stands; an account with ended grants and no paid
|
|
310
|
+
* snapshot is `inactive` with no features or limits. Grants are read only when
|
|
311
|
+
* the paid snapshot does not decide, unless the caller passes them.
|
|
312
|
+
*/
|
|
313
|
+
export async function applyGrants(deps, billingAccountId, paid, grants) {
|
|
314
|
+
if (paid && paid.status !== 'inactive') {
|
|
315
|
+
return paid;
|
|
316
|
+
}
|
|
317
|
+
const candidates = grants ?? (await listGrants(deps, billingAccountId));
|
|
318
|
+
const active = selectActiveGrant(candidates);
|
|
319
|
+
if (active) {
|
|
320
|
+
const plan = resolvePlan(deps.catalog, active.planKey);
|
|
321
|
+
return {
|
|
322
|
+
id: paid?.id ?? `ent_${billingAccountId}`,
|
|
323
|
+
billingAccountId,
|
|
324
|
+
planKey: active.planKey,
|
|
325
|
+
status: active.kind === 'trial' ? 'trialing' : 'active',
|
|
326
|
+
// A plan removed from the catalog after the grant entitles nothing.
|
|
327
|
+
features: { ...(plan?.features ?? {}) },
|
|
328
|
+
limits: { ...(plan?.limits ?? {}) },
|
|
329
|
+
effectiveAt: active.startsAt,
|
|
330
|
+
expiresAt: active.expiresAt,
|
|
331
|
+
sourceEventId: `grant:${active.id}`,
|
|
332
|
+
grant: { id: active.id, kind: active.kind, expiresAt: active.expiresAt },
|
|
333
|
+
createdAt: paid?.createdAt ?? active.createdAt,
|
|
334
|
+
updatedAt: active.updatedAt
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (paid || candidates.length === 0) {
|
|
338
|
+
return paid;
|
|
339
|
+
}
|
|
340
|
+
const latest = candidates[0];
|
|
341
|
+
return {
|
|
342
|
+
id: `ent_${billingAccountId}`,
|
|
343
|
+
billingAccountId,
|
|
344
|
+
planKey: latest.planKey,
|
|
345
|
+
status: 'inactive',
|
|
346
|
+
features: {},
|
|
347
|
+
limits: {},
|
|
348
|
+
effectiveAt: latest.revokedAt ?? latest.expiresAt,
|
|
349
|
+
expiresAt: latest.revokedAt ?? latest.expiresAt,
|
|
350
|
+
sourceEventId: `grant:${latest.id}`,
|
|
351
|
+
createdAt: latest.createdAt,
|
|
352
|
+
updatedAt: latest.updatedAt
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/** A comp before a trial, then the later expiry; racing comps resolve the same way on every read. */
|
|
356
|
+
function selectActiveGrant(grants) {
|
|
357
|
+
return grants
|
|
358
|
+
.filter((grant) => grant.status === 'active')
|
|
359
|
+
.sort((left, right) => (left.kind === right.kind ? 0 : left.kind === 'comp' ? -1 : 1) ||
|
|
360
|
+
compareTimes(right.expiresAt, left.expiresAt) ||
|
|
361
|
+
left.id.localeCompare(right.id))[0];
|
|
362
|
+
}
|
|
363
|
+
function withStatus(record, now) {
|
|
364
|
+
const expiresAt = Date.parse(record.expiresAt);
|
|
365
|
+
const status = record.revokedAt
|
|
366
|
+
? 'revoked'
|
|
367
|
+
: Number.isFinite(expiresAt) && expiresAt > now.getTime()
|
|
368
|
+
? 'active'
|
|
369
|
+
: 'expired';
|
|
370
|
+
return { ...record, status };
|
|
371
|
+
}
|
|
372
|
+
async function findGrant(deps, billingAccountId, grantId) {
|
|
373
|
+
return deps.db.findOne({
|
|
374
|
+
model: GRANTS_TABLE,
|
|
375
|
+
where: [
|
|
376
|
+
{ field: 'id', operator: 'eq', value: grantId },
|
|
377
|
+
{ field: 'billingAccountId', operator: 'eq', value: billingAccountId }
|
|
378
|
+
],
|
|
379
|
+
namespace: deps.namespace
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
function trialGrantId(billingAccountId) {
|
|
383
|
+
return `grant_trial_${billingAccountId}`;
|
|
384
|
+
}
|
|
385
|
+
function compGrantIdPrefix(billingAccountId) {
|
|
386
|
+
return `grant_comp_${billingAccountId}_`;
|
|
387
|
+
}
|
|
388
|
+
function activeCompExists(grantId) {
|
|
389
|
+
return conflict('An active comp already exists for this billing account', 'active_comp_exists', grantId);
|
|
390
|
+
}
|
|
391
|
+
function grantCreatedEvent(grant) {
|
|
392
|
+
return {
|
|
393
|
+
id: `evt_grant_created_${grant.id}`,
|
|
394
|
+
billingAccountId: grant.billingAccountId,
|
|
395
|
+
type: 'billfn.grant.created',
|
|
396
|
+
payload: {
|
|
397
|
+
grantId: grant.id,
|
|
398
|
+
kind: grant.kind,
|
|
399
|
+
planKey: grant.planKey,
|
|
400
|
+
startsAt: grant.startsAt,
|
|
401
|
+
expiresAt: grant.expiresAt,
|
|
402
|
+
operator: grant.operator ?? null,
|
|
403
|
+
reason: grant.reason ?? null
|
|
404
|
+
},
|
|
405
|
+
createdAt: grant.createdAt
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function grantRevokedEvent(grant) {
|
|
409
|
+
return {
|
|
410
|
+
id: `evt_grant_revoked_${grant.id}`,
|
|
411
|
+
billingAccountId: grant.billingAccountId,
|
|
412
|
+
type: 'billfn.grant.revoked',
|
|
413
|
+
payload: {
|
|
414
|
+
grantId: grant.id,
|
|
415
|
+
kind: grant.kind,
|
|
416
|
+
planKey: grant.planKey,
|
|
417
|
+
operator: grant.revokedBy ?? null,
|
|
418
|
+
reason: grant.revokeReason ?? null
|
|
419
|
+
},
|
|
420
|
+
createdAt: grant.revokedAt ?? grant.updatedAt
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function readOperator(operator) {
|
|
424
|
+
if (operator === undefined) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
if (typeof operator !== 'object' || operator === null || typeof operator.id !== 'string' || operator.id.length === 0) {
|
|
428
|
+
throw validation('operator.id must be a non-empty string');
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
id: operator.id,
|
|
432
|
+
...(typeof operator.type === 'string' ? { type: operator.type } : {}),
|
|
433
|
+
...(typeof operator.requestId === 'string' ? { requestId: operator.requestId } : {})
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function readReason(reason) {
|
|
437
|
+
if (reason === undefined) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
if (typeof reason !== 'string') {
|
|
441
|
+
throw validation('reason must be a string');
|
|
442
|
+
}
|
|
443
|
+
return reason;
|
|
444
|
+
}
|
|
445
|
+
function laterThan(now, previous) {
|
|
446
|
+
const previousTime = Date.parse(previous);
|
|
447
|
+
const time = Number.isFinite(previousTime) && now.getTime() <= previousTime ? previousTime + 1 : now.getTime();
|
|
448
|
+
return toIsoString(new Date(time));
|
|
449
|
+
}
|
|
450
|
+
function compareTimes(left, right) {
|
|
451
|
+
return (Date.parse(left) || 0) - (Date.parse(right) || 0);
|
|
452
|
+
}
|
|
453
|
+
function validation(message) {
|
|
454
|
+
return createBillFnError({ code: 'BILLFN_VALIDATION_ERROR', message });
|
|
455
|
+
}
|
|
456
|
+
function conflict(message, reason, grantId) {
|
|
457
|
+
return createBillFnError({ code: 'BILLFN_CONFLICT', message, details: { reason, grantId } });
|
|
458
|
+
}
|
|
459
|
+
//# sourceMappingURL=grants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/D,mBAAmB,YAAY,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,EAClC,sCAAsC,EACtC,gCAAgC,EACjC,MAAM,cAAc,CAAC;AAEtB,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/D,mBAAmB,YAAY,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,EAClC,sCAAsC,EACtC,gCAAgC,EACjC,MAAM,cAAc,CAAC;AAEtB,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAkCjE"}
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,10 @@ export function createBillFn(config) {
|
|
|
28
28
|
services: {
|
|
29
29
|
subscriptions: {
|
|
30
30
|
import: service.importSubscription
|
|
31
|
+
},
|
|
32
|
+
grants: {
|
|
33
|
+
create: service.createGrant,
|
|
34
|
+
revoke: service.revokeGrant
|
|
31
35
|
}
|
|
32
36
|
},
|
|
33
37
|
subscriptionProvider: service.subscriptionProvider,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAInD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,EAClC,sCAAsC,EACtC,gCAAgC,EACjC,MAAM,cAAc,CAAC;AAEtB,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE1C,OAAO;QACL,MAAM;QACN,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE;QAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,wBAAwB,EAAE,OAAO,CAAC,wBAAwB;QAC1D,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;QAClD,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;QAClD,QAAQ,EAAE;YACR,aAAa,EAAE;gBACb,MAAM,EAAE,OAAO,CAAC,kBAAkB;aACnC;SACF;QACD,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;QAClD,aAAa,EAAE,OAAO,CAAC,aAAa;KACrC,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAInD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,EAClC,sCAAsC,EACtC,gCAAgC,EACjC,MAAM,cAAc,CAAC;AAEtB,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE1C,OAAO;QACL,MAAM;QACN,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE;QAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,wBAAwB,EAAE,OAAO,CAAC,wBAAwB;QAC1D,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;QAClD,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;QAClD,QAAQ,EAAE;YACR,aAAa,EAAE;gBACb,MAAM,EAAE,OAAO,CAAC,kBAAkB;aACnC;YACD,MAAM,EAAE;gBACN,MAAM,EAAE,OAAO,CAAC,WAAW;gBAC3B,MAAM,EAAE,OAAO,CAAC,WAAW;aAC5B;SACF;QACD,oBAAoB,EAAE,OAAO,CAAC,oBAAoB;QAClD,aAAa,EAAE,OAAO,CAAC,aAAa;KACrC,CAAC;AACJ,CAAC"}
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEtE,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,QAAQ,GAAE,mBAAwB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEtE,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,QAAQ,GAAE,mBAAwB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CA2UzG;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,cAAc,CAG9E"}
|
package/dist/schema.js
CHANGED
|
@@ -176,6 +176,27 @@ export function getSchema(_options = {}) {
|
|
|
176
176
|
{ name: 'replaced_provider_references_provider_reference_idx', fields: ['provider', 'providerReference'], unique: true }
|
|
177
177
|
]
|
|
178
178
|
};
|
|
179
|
+
const entitlementGrants = {
|
|
180
|
+
modelName: 'entitlementGrants',
|
|
181
|
+
fields: {
|
|
182
|
+
id: { type: 'string', required: true, unique: true },
|
|
183
|
+
billingAccountId: { type: 'string', required: true },
|
|
184
|
+
planKey: { type: 'string', required: true },
|
|
185
|
+
kind: { type: 'string', required: true },
|
|
186
|
+
startsAt: { type: 'string', required: true },
|
|
187
|
+
expiresAt: { type: 'string', required: true },
|
|
188
|
+
revokedAt: { type: 'string', required: false },
|
|
189
|
+
operator: { type: 'json', required: false },
|
|
190
|
+
reason: { type: 'string', required: false },
|
|
191
|
+
revokedBy: { type: 'json', required: false },
|
|
192
|
+
revokeReason: { type: 'string', required: false },
|
|
193
|
+
createdAt: { type: 'string', required: true },
|
|
194
|
+
updatedAt: { type: 'string', required: true }
|
|
195
|
+
},
|
|
196
|
+
indexes: [
|
|
197
|
+
{ name: 'entitlement_grants_billing_account_idx', fields: ['billingAccountId', 'createdAt'] }
|
|
198
|
+
]
|
|
199
|
+
};
|
|
179
200
|
const billingEvents = {
|
|
180
201
|
modelName: 'billingEvents',
|
|
181
202
|
fields: {
|
|
@@ -285,10 +306,11 @@ export function getSchema(_options = {}) {
|
|
|
285
306
|
reconciliationJobs,
|
|
286
307
|
reconciliationCursors,
|
|
287
308
|
unlinkedWebhookEvents,
|
|
288
|
-
replacedProviderReferences
|
|
309
|
+
replacedProviderReferences,
|
|
310
|
+
entitlementGrants
|
|
289
311
|
];
|
|
290
312
|
return {
|
|
291
|
-
version:
|
|
313
|
+
version: 5,
|
|
292
314
|
schemas
|
|
293
315
|
};
|
|
294
316
|
}
|