@cortexkit/antigravity-auth-core 3.0.8 → 3.0.10
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/common-auth-embedded/auth-menu/login.d.ts +6 -10
- package/dist/common-auth-embedded/auth-menu/login.js +14 -11
- package/dist/common-auth-embedded/claustrum/consumer.d.ts +8 -2
- package/dist/common-auth-embedded/claustrum/consumer.js +64 -21
- package/dist/common-auth-embedded/claustrum/index.d.ts +2 -1
- package/dist/common-auth-embedded/claustrum/index.js +1 -0
- package/dist/common-auth-embedded/claustrum/insula.d.ts +23 -0
- package/dist/common-auth-embedded/claustrum/insula.js +64 -2
- package/dist/common-auth-embedded/claustrum/quota-writer.d.ts +18 -0
- package/dist/common-auth-embedded/claustrum/quota-writer.js +253 -0
- package/dist/common-auth-embedded/claustrum/roster.d.ts +18 -2
- package/dist/common-auth-embedded/claustrum/roster.js +100 -43
- package/dist/common-auth-embedded/fs/with-lock.d.ts +1 -0
- package/dist/common-auth-embedded/fs/with-lock.js +2 -0
- package/dist/common-auth-embedded/quota/format.d.ts +19 -0
- package/dist/common-auth-embedded/quota/format.js +50 -5
- package/dist/common-auth-embedded/quota/index.d.ts +3 -3
- package/dist/common-auth-embedded/quota/index.js +2 -2
- package/dist/common-auth-embedded/quota/map.d.ts +24 -1
- package/dist/common-auth-embedded/quota/map.js +31 -6
- package/dist/common-auth-embedded/quota/merge.d.ts +6 -1
- package/dist/common-auth-embedded/quota/merge.js +17 -3
- package/dist/common-auth-embedded/quota/projection.d.ts +3 -1
- package/dist/common-auth-embedded/quota/projection.js +3 -1
- package/dist/common-auth-embedded/source-output.json +89 -73
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -1
- package/dist/pre-response-deadline.d.ts.map +1 -1
- package/dist/pre-response-deadline.js +5 -1
- package/dist/pre-response-deadline.js.map +1 -1
- package/dist/vault-account-source.d.ts +14 -2
- package/dist/vault-account-source.d.ts.map +1 -1
- package/dist/vault-account-source.js +38 -0
- package/dist/vault-account-source.js.map +1 -1
- package/package.json +1 -1
|
@@ -106,6 +106,25 @@ export function quotaWindowParts(quota, options) {
|
|
|
106
106
|
const parts = windows.map(({ part }) => part);
|
|
107
107
|
return age === undefined ? { windows: parts } : { windows: parts, age };
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The narrow label of each scoped window part, as `narrowTextParts` names
|
|
111
|
+
* them: model short names are worked out over every model-scoped window in
|
|
112
|
+
* the reading, so two models never share a short name.
|
|
113
|
+
*/
|
|
114
|
+
function withShortNames(windows) {
|
|
115
|
+
const abbreviations = scopeAbbreviations(windows
|
|
116
|
+
.filter(({ part, scope }) => part.kind === 'window' && scope !== ALL_SCOPE)
|
|
117
|
+
.map(({ scope }) => scope));
|
|
118
|
+
for (const window of windows) {
|
|
119
|
+
window.part.short =
|
|
120
|
+
window.part.kind === 'budget'
|
|
121
|
+
? 'cr'
|
|
122
|
+
: window.scope === ALL_SCOPE
|
|
123
|
+
? window.part.name
|
|
124
|
+
: (abbreviations.get(window.scope) ?? window.scope);
|
|
125
|
+
}
|
|
126
|
+
return windows;
|
|
127
|
+
}
|
|
109
128
|
/**
|
|
110
129
|
* `quotaWindowParts` with each window's scope kept beside it. The narrow form
|
|
111
130
|
* needs the scope to name and pick model-scoped windows; the public parts do
|
|
@@ -128,6 +147,7 @@ function scopedWindowParts(quota, options) {
|
|
|
128
147
|
part: {
|
|
129
148
|
kind: 'window',
|
|
130
149
|
name: quotaWindowName(limit),
|
|
150
|
+
short: '',
|
|
131
151
|
figure: percentLeft(limit.remainingPercent ?? 0),
|
|
132
152
|
remainingPercent,
|
|
133
153
|
checkedAt: limit.checkedAt,
|
|
@@ -149,6 +169,7 @@ function scopedWindowParts(quota, options) {
|
|
|
149
169
|
part: {
|
|
150
170
|
kind: 'budget',
|
|
151
171
|
name: 'credits',
|
|
172
|
+
short: '',
|
|
152
173
|
figure,
|
|
153
174
|
...(remainingPercent === undefined
|
|
154
175
|
? {}
|
|
@@ -158,6 +179,7 @@ function scopedWindowParts(quota, options) {
|
|
|
158
179
|
},
|
|
159
180
|
});
|
|
160
181
|
}
|
|
182
|
+
withShortNames(windows);
|
|
161
183
|
if (windows.length > 0) {
|
|
162
184
|
const age = now - Math.min(...windows.map(({ part }) => part.checkedAt));
|
|
163
185
|
if (age > (options.staleAfterMs ?? QUOTA_STALE_AFTER_MS)) {
|
|
@@ -298,20 +320,21 @@ function narrowTextParts(quota, options) {
|
|
|
298
320
|
return grouped;
|
|
299
321
|
const general = windows.filter(({ part, scope }) => part.kind === 'window' && scope === ALL_SCOPE);
|
|
300
322
|
const scoped = windows.filter(({ part, scope }) => part.kind === 'window' && scope !== ALL_SCOPE);
|
|
301
|
-
const abbreviations = scopeAbbreviations(scoped.map(({ scope }) => scope));
|
|
302
323
|
// The items competing for the one slot after the account-wide windows, in
|
|
303
324
|
// tie-break order: scoped windows in display order, then the budget. A
|
|
304
325
|
// spent budget counts as 0% left; one without a percentage cannot compete.
|
|
326
|
+
// Their labels are the parts' short names, so the narrow line and a layout
|
|
327
|
+
// reading `short` always agree.
|
|
305
328
|
const budget = windows.find(({ part }) => part.kind === 'budget');
|
|
306
329
|
const budgetLeft = budget?.spent ? 0 : budget?.part.remainingPercent;
|
|
307
330
|
const extras = [
|
|
308
|
-
...scoped.map(({
|
|
309
|
-
label:
|
|
331
|
+
...scoped.map(({ part }) => ({
|
|
332
|
+
label: part.short,
|
|
310
333
|
remaining: part.remainingPercent ?? 0,
|
|
311
334
|
})),
|
|
312
|
-
...(budgetLeft === undefined
|
|
335
|
+
...(budgetLeft === undefined || budget === undefined
|
|
313
336
|
? []
|
|
314
|
-
: [{ label:
|
|
337
|
+
: [{ label: budget.part.short, remaining: budgetLeft }]),
|
|
315
338
|
];
|
|
316
339
|
// Strictly lower wins, so on a tie the earlier item keeps the slot.
|
|
317
340
|
const tightest = extras.reduce((best, item) => best === undefined || item.remaining < best.remaining ? item : best, undefined);
|
|
@@ -357,3 +380,25 @@ export function formatQuota(quota, options) {
|
|
|
357
380
|
? NO_LIMITS_REPORTED
|
|
358
381
|
: NO_QUOTA_READING;
|
|
359
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* Prepaid credits as one short value, worded as the official Codex client's
|
|
385
|
+
* status view words them: `Unlimited` for unlimited credits, `<n> credits`
|
|
386
|
+
* for a finite positive balance (rounded to a whole number), `Available` when
|
|
387
|
+
* the account has credits but no usable balance was reported, and undefined
|
|
388
|
+
* when the account has no credits (or `credits` is undefined), so a caller
|
|
389
|
+
* shows no credits row at all. Unlimited wins even when `hasCredits` is false.
|
|
390
|
+
* No other formatter adds this text; a caller that wants a credits row asks
|
|
391
|
+
* for it.
|
|
392
|
+
*/
|
|
393
|
+
export function quotaCreditsText(credits) {
|
|
394
|
+
if (credits === undefined)
|
|
395
|
+
return undefined;
|
|
396
|
+
if (credits.unlimited)
|
|
397
|
+
return 'Unlimited';
|
|
398
|
+
if (!credits.hasCredits)
|
|
399
|
+
return undefined;
|
|
400
|
+
const { balance } = credits;
|
|
401
|
+
return typeof balance === 'number' && Number.isFinite(balance) && balance > 0
|
|
402
|
+
? `${Math.round(balance)} credits`
|
|
403
|
+
: 'Available';
|
|
404
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export type { QuotaCodec } from './codec.js';
|
|
2
2
|
export { quotaCodec } from './codec.js';
|
|
3
3
|
export type { QuotaParts, QuotaTextForm, QuotaTextOptions, QuotaWindowPart, } from './format.js';
|
|
4
|
-
export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
|
|
5
|
-
export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
|
|
6
|
-
export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from './map.js';
|
|
4
|
+
export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaCreditsText, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
|
|
5
|
+
export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaCredits, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
|
|
6
|
+
export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaCredits, isQuotaMap, } from './map.js';
|
|
7
7
|
export type { ObservedBudget, ObservedPair, ObservedReading, QuotaObservation, } from './merge.js';
|
|
8
8
|
export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from './merge.js';
|
|
9
9
|
export type { ExhaustionReset, ProjectedBudget, ProjectedLimit, ProjectedQuota, QuotaGroup, } from './projection.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { quotaCodec } from './codec.js';
|
|
2
|
-
export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
|
|
3
|
-
export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from './map.js';
|
|
2
|
+
export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaCreditsText, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
|
|
3
|
+
export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaCredits, isQuotaMap, } from './map.js';
|
|
4
4
|
export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from './merge.js';
|
|
5
5
|
export { budgetExhaustedResetAt, futureResetAt, projectQuota, readsExhausted, } from './projection.js';
|
|
@@ -48,12 +48,30 @@ export interface QuotaCounter {
|
|
|
48
48
|
value: number;
|
|
49
49
|
checkedAt: number;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Prepaid credits a provider account holds beside its rate-limit windows (for
|
|
53
|
+
* example a ChatGPT workspace's credits, which keep requests working after a
|
|
54
|
+
* window is used up). This is a state, not a count or the spend budget: the
|
|
55
|
+
* account has credits or not, they may be unlimited, and the provider may
|
|
56
|
+
* report a balance. It carries its own observation time.
|
|
57
|
+
*/
|
|
58
|
+
export interface QuotaCredits {
|
|
59
|
+
hasCredits: boolean;
|
|
60
|
+
unlimited: boolean;
|
|
61
|
+
/** The balance the provider reported, when it reported one. */
|
|
62
|
+
balance?: number;
|
|
63
|
+
/** The provider's verdict that the overage allowance is used up. */
|
|
64
|
+
overageLimitReached?: boolean;
|
|
65
|
+
checkedAt: number;
|
|
66
|
+
}
|
|
51
67
|
export interface QuotaMap {
|
|
52
68
|
limits: QuotaEntry[];
|
|
53
69
|
/** Absent means no observation has reported on the budget yet. */
|
|
54
70
|
budget?: CreditBudgetEntry;
|
|
55
71
|
/** Provider-named counts, each with its own observation time. */
|
|
56
72
|
counters?: Record<string, QuotaCounter>;
|
|
73
|
+
/** Absent means no observation has reported on prepaid credits yet. */
|
|
74
|
+
credits?: QuotaCredits;
|
|
57
75
|
}
|
|
58
76
|
export declare function emptyQuotaMap(): QuotaMap;
|
|
59
77
|
/** The time an entry speaks for: its reading, retirement or absence time. */
|
|
@@ -61,9 +79,14 @@ export declare function entryTime(entry: QuotaEntry): number;
|
|
|
61
79
|
export declare function limitKey(scope: string, label: string): string;
|
|
62
80
|
export declare function isCreditBudgetEntry(value: unknown): value is CreditBudgetEntry;
|
|
63
81
|
export declare function isQuotaCounters(value: unknown): value is Record<string, QuotaCounter>;
|
|
82
|
+
/** True when `value` is a well-formed prepaid-credits record. */
|
|
83
|
+
export declare function isQuotaCredits(value: unknown): value is QuotaCredits;
|
|
64
84
|
/** Keep usable counters without letting one malformed count hide a quota map. */
|
|
65
85
|
export declare function readQuotaCounters(value: unknown): Record<string, QuotaCounter> | undefined;
|
|
66
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Decode persisted quota, dropping malformed counters or a malformed credits
|
|
88
|
+
* record but not limit or budget evidence.
|
|
89
|
+
*/
|
|
67
90
|
export declare function decodeQuotaMap(value: unknown): QuotaMap | undefined;
|
|
68
91
|
/**
|
|
69
92
|
* True when `value` is a well-formed quota map. Unrecognised top-level keys
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
// reset clock. It is a tri-state: absent (never reported), a reading, or
|
|
15
15
|
// cleared (the provider reported that no budget exists).
|
|
16
16
|
//
|
|
17
|
+
// Provider-named counters and the prepaid credits record sit beside them too,
|
|
18
|
+
// each judged by its own observation time rather than the limits'.
|
|
19
|
+
//
|
|
17
20
|
// The map is plain JSON so the store can persist it without interpreting it.
|
|
18
21
|
/** The scope of a limit that binds every request regardless of model. */
|
|
19
22
|
export const ALL_SCOPE = 'all';
|
|
@@ -93,6 +96,18 @@ function isQuotaCounter(value) {
|
|
|
93
96
|
export function isQuotaCounters(value) {
|
|
94
97
|
return isRecord(value) && Object.values(value).every(isQuotaCounter);
|
|
95
98
|
}
|
|
99
|
+
function isBoolean(value) {
|
|
100
|
+
return typeof value === 'boolean';
|
|
101
|
+
}
|
|
102
|
+
/** True when `value` is a well-formed prepaid-credits record. */
|
|
103
|
+
export function isQuotaCredits(value) {
|
|
104
|
+
return (isRecord(value) &&
|
|
105
|
+
typeof value.hasCredits === 'boolean' &&
|
|
106
|
+
typeof value.unlimited === 'boolean' &&
|
|
107
|
+
optional(value, 'balance', isFiniteNumber) &&
|
|
108
|
+
optional(value, 'overageLimitReached', isBoolean) &&
|
|
109
|
+
isFiniteNumber(value.checkedAt));
|
|
110
|
+
}
|
|
96
111
|
/** Keep usable counters without letting one malformed count hide a quota map. */
|
|
97
112
|
export function readQuotaCounters(value) {
|
|
98
113
|
if (!isRecord(value))
|
|
@@ -102,20 +117,29 @@ export function readQuotaCounters(value) {
|
|
|
102
117
|
? undefined
|
|
103
118
|
: Object.fromEntries(entries);
|
|
104
119
|
}
|
|
105
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Decode persisted quota, dropping malformed counters or a malformed credits
|
|
122
|
+
* record but not limit or budget evidence.
|
|
123
|
+
*/
|
|
106
124
|
export function decodeQuotaMap(value) {
|
|
107
125
|
// A valid map is returned as the same object, so stored quota keeps the
|
|
108
|
-
// identity it had before counters existed
|
|
109
|
-
// counter is copied
|
|
126
|
+
// identity it had before counters and credits existed. A map with a
|
|
127
|
+
// malformed counter or credits record is copied with those malformed
|
|
128
|
+
// fields left out.
|
|
110
129
|
if (isQuotaMap(value))
|
|
111
130
|
return value;
|
|
112
131
|
if (!isRecord(value))
|
|
113
132
|
return undefined;
|
|
114
|
-
const { counters: rawCounters, ...rest } = value;
|
|
133
|
+
const { counters: rawCounters, credits: rawCredits, ...rest } = value;
|
|
115
134
|
if (!isQuotaMap(rest))
|
|
116
135
|
return undefined;
|
|
117
136
|
const counters = readQuotaCounters(rawCounters);
|
|
118
|
-
|
|
137
|
+
const credits = isQuotaCredits(rawCredits) ? rawCredits : undefined;
|
|
138
|
+
return {
|
|
139
|
+
...rest,
|
|
140
|
+
...(counters === undefined ? {} : { counters }),
|
|
141
|
+
...(credits === undefined ? {} : { credits }),
|
|
142
|
+
};
|
|
119
143
|
}
|
|
120
144
|
/**
|
|
121
145
|
* True when `value` is a well-formed quota map. Unrecognised top-level keys
|
|
@@ -136,5 +160,6 @@ export function isQuotaMap(value) {
|
|
|
136
160
|
seen.add(key);
|
|
137
161
|
}
|
|
138
162
|
return (optional(value, 'budget', isCreditBudgetEntry) &&
|
|
139
|
-
optional(value, 'counters', isQuotaCounters)
|
|
163
|
+
optional(value, 'counters', isQuotaCounters) &&
|
|
164
|
+
optional(value, 'credits', isQuotaCredits));
|
|
140
165
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type QuotaCounter, type QuotaMap } from './map.js';
|
|
1
|
+
import { type QuotaCounter, type QuotaCredits, type QuotaMap } from './map.js';
|
|
2
2
|
export interface ObservedReading {
|
|
3
3
|
/** Defaults to `all`. */
|
|
4
4
|
scope?: string;
|
|
@@ -34,6 +34,11 @@ export interface QuotaObservation {
|
|
|
34
34
|
budget?: ObservedBudget;
|
|
35
35
|
/** Each named count uses its own time, independently of limits and budget. */
|
|
36
36
|
counters?: Record<string, QuotaCounter>;
|
|
37
|
+
/**
|
|
38
|
+
* Prepaid credits, judged by their own time independently of limits, budget
|
|
39
|
+
* and counters. Absent means the observation says nothing about credits.
|
|
40
|
+
*/
|
|
41
|
+
credits?: QuotaCredits;
|
|
37
42
|
}
|
|
38
43
|
/** Thrown by `mergeQuotaObservation` when either argument is malformed. */
|
|
39
44
|
export declare class QuotaCodecError extends TypeError {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// no entry gets an absence record. A pair outside the coverage is never
|
|
12
12
|
// touched, so a header-shaped partial observation (which covers only what it
|
|
13
13
|
// carries) cannot erase a family limit or the budget.
|
|
14
|
-
import { ALL_SCOPE, decodeQuotaMap, entryTime, isQuotaCounters, limitKey, } from './map.js';
|
|
14
|
+
import { ALL_SCOPE, decodeQuotaMap, entryTime, isQuotaCounters, isQuotaCredits, limitKey, } from './map.js';
|
|
15
15
|
/** Thrown by `mergeQuotaObservation` when either argument is malformed. */
|
|
16
16
|
export class QuotaCodecError extends TypeError {
|
|
17
17
|
constructor(message) {
|
|
@@ -84,7 +84,8 @@ export function isQuotaObservation(value) {
|
|
|
84
84
|
if (!coverage.every(isObservedPair))
|
|
85
85
|
return false;
|
|
86
86
|
return ((value.budget === undefined || isObservedBudget(value.budget)) &&
|
|
87
|
-
(value.counters === undefined || isQuotaCounters(value.counters))
|
|
87
|
+
(value.counters === undefined || isQuotaCounters(value.counters)) &&
|
|
88
|
+
(value.credits === undefined || isQuotaCredits(value.credits)));
|
|
88
89
|
}
|
|
89
90
|
function compareEntries(left, right) {
|
|
90
91
|
if (left.scope !== right.scope)
|
|
@@ -110,6 +111,17 @@ function mergeCounters(stored, observed) {
|
|
|
110
111
|
}
|
|
111
112
|
return counters.size === 0 ? undefined : Object.fromEntries(counters);
|
|
112
113
|
}
|
|
114
|
+
// Credits merge like a single counter: the newer record wins, an equal time
|
|
115
|
+
// applies (the observation applied last wins), and an observation without
|
|
116
|
+
// credits leaves the stored record alone.
|
|
117
|
+
function mergeCredits(stored, observed) {
|
|
118
|
+
if (observed === undefined)
|
|
119
|
+
return stored;
|
|
120
|
+
if (stored !== undefined && observed.checkedAt < stored.checkedAt) {
|
|
121
|
+
return stored;
|
|
122
|
+
}
|
|
123
|
+
return { ...observed };
|
|
124
|
+
}
|
|
113
125
|
/**
|
|
114
126
|
* Applies `observation` to `stored` (undefined for a row with no map yet) and
|
|
115
127
|
* returns the new map. Pure: neither argument is modified. Throws
|
|
@@ -166,13 +178,15 @@ export function mergeQuotaObservation(stored, observation) {
|
|
|
166
178
|
: { scope, label: pair.label, kind: 'retired', retiredAt: at });
|
|
167
179
|
}
|
|
168
180
|
// Keys this module does not recognise are carried over untouched.
|
|
169
|
-
const { limits: _limits, budget: _budget, counters: _counters, ...unknownKeys } = base;
|
|
181
|
+
const { limits: _limits, budget: _budget, counters: _counters, credits: _credits, ...unknownKeys } = base;
|
|
170
182
|
const budget = mergeBudget(base.budget, observation.budget, at);
|
|
171
183
|
const counters = mergeCounters(base.counters, observation.counters);
|
|
184
|
+
const credits = mergeCredits(base.credits, observation.credits);
|
|
172
185
|
return {
|
|
173
186
|
...unknownKeys,
|
|
174
187
|
limits: [...entries.values()].sort(compareEntries),
|
|
175
188
|
...(budget === undefined ? {} : { budget }),
|
|
176
189
|
...(counters === undefined ? {} : { counters }),
|
|
190
|
+
...(credits === undefined ? {} : { credits }),
|
|
177
191
|
};
|
|
178
192
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type QuotaCounter, type QuotaMap } from './map.js';
|
|
1
|
+
import { type QuotaCounter, type QuotaCredits, type QuotaMap } from './map.js';
|
|
2
2
|
export interface ProjectedLimit {
|
|
3
3
|
scope: string;
|
|
4
4
|
label: string;
|
|
@@ -40,6 +40,8 @@ export interface ProjectedQuota {
|
|
|
40
40
|
/** Present only when the stored budget is a reading, not cleared. */
|
|
41
41
|
budget?: ProjectedBudget;
|
|
42
42
|
counters?: Record<string, QuotaCounter>;
|
|
43
|
+
/** The stored prepaid credits, unchanged in every scope. */
|
|
44
|
+
credits?: QuotaCredits;
|
|
43
45
|
/**
|
|
44
46
|
* The named groups the windows belong to, in the order the plugin lists
|
|
45
47
|
* them. Set only by a plugin whose windows all belong to such groups; the
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// scope, orders the result the way the primitives expect, and carries each
|
|
7
7
|
// entry's (scope, label) so a decision names the limit it judged rather than
|
|
8
8
|
// the position it occupied.
|
|
9
|
-
import { ALL_SCOPE, readQuotaCounters, } from './map.js';
|
|
9
|
+
import { ALL_SCOPE, isQuotaCredits, readQuotaCounters, } from './map.js';
|
|
10
10
|
function project(entry) {
|
|
11
11
|
if (entry.kind === 'retired') {
|
|
12
12
|
return {
|
|
@@ -95,12 +95,14 @@ export function projectQuota(map, scope = ALL_SCOPE) {
|
|
|
95
95
|
: undefined;
|
|
96
96
|
const budget = map?.budget?.kind === 'reading' ? projectBudget(map.budget) : undefined;
|
|
97
97
|
const counters = readQuotaCounters(map?.counters);
|
|
98
|
+
const credits = isQuotaCredits(map?.credits) ? map.credits : undefined;
|
|
98
99
|
return {
|
|
99
100
|
scope,
|
|
100
101
|
limits,
|
|
101
102
|
...(checkedAt === undefined ? {} : { checkedAt }),
|
|
102
103
|
...(budget === undefined ? {} : { budget }),
|
|
103
104
|
...(counters === undefined ? {} : { counters }),
|
|
105
|
+
...(credits === undefined ? {} : { credits }),
|
|
104
106
|
};
|
|
105
107
|
}
|
|
106
108
|
/**
|