@nebulr-group/bridge-auth-core 0.1.2 → 0.4.0-beta.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/billing/bridge-subscription.d.ts +71 -0
- package/dist/billing/bridge-subscription.d.ts.map +1 -0
- package/dist/billing/bridge-subscription.js +209 -0
- package/dist/billing/bridge-subscription.js.map +1 -0
- package/dist/billing/entitlements-store.d.ts +61 -0
- package/dist/billing/entitlements-store.d.ts.map +1 -0
- package/dist/billing/entitlements-store.js +127 -0
- package/dist/billing/entitlements-store.js.map +1 -0
- package/dist/billing/fetch-billing-state.d.ts +9 -0
- package/dist/billing/fetch-billing-state.d.ts.map +1 -0
- package/dist/billing/fetch-billing-state.js +22 -0
- package/dist/billing/fetch-billing-state.js.map +1 -0
- package/dist/billing/lock-signal.d.ts +6 -0
- package/dist/billing/lock-signal.d.ts.map +1 -0
- package/dist/billing/lock-signal.js +8 -0
- package/dist/billing/lock-signal.js.map +1 -0
- package/dist/billing/quota-store.d.ts +86 -0
- package/dist/billing/quota-store.d.ts.map +1 -0
- package/dist/billing/quota-store.js +180 -0
- package/dist/billing/quota-store.js.map +1 -0
- package/dist/billing/types.d.ts +64 -0
- package/dist/billing/types.d.ts.map +1 -0
- package/dist/billing/types.js +49 -0
- package/dist/billing/types.js.map +1 -0
- package/dist/billing/use-bridge.d.ts +131 -0
- package/dist/billing/use-bridge.d.ts.map +1 -0
- package/dist/billing/use-bridge.js +181 -0
- package/dist/billing/use-bridge.js.map +1 -0
- package/dist/bridge-auth.d.ts +36 -1
- package/dist/bridge-auth.d.ts.map +1 -1
- package/dist/bridge-auth.js +57 -3
- package/dist/bridge-auth.js.map +1 -1
- package/dist/direct-auth.d.ts +1 -0
- package/dist/direct-auth.d.ts.map +1 -1
- package/dist/direct-auth.js +7 -0
- package/dist/direct-auth.js.map +1 -1
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -1
- package/dist/flags/attribute-providers.d.ts +229 -0
- package/dist/flags/attribute-providers.d.ts.map +1 -0
- package/dist/flags/attribute-providers.js +375 -0
- package/dist/flags/attribute-providers.js.map +1 -0
- package/dist/flags/dev-attribute-provider.d.ts +67 -0
- package/dist/flags/dev-attribute-provider.d.ts.map +1 -0
- package/dist/flags/dev-attribute-provider.js +200 -0
- package/dist/flags/dev-attribute-provider.js.map +1 -0
- package/dist/flags/evaluator.d.ts +58 -0
- package/dist/flags/evaluator.d.ts.map +1 -0
- package/dist/flags/evaluator.js +160 -0
- package/dist/flags/evaluator.js.map +1 -0
- package/dist/flags/flag.d.ts +199 -0
- package/dist/flags/flag.d.ts.map +1 -0
- package/dist/flags/flag.js +337 -0
- package/dist/flags/flag.js.map +1 -0
- package/dist/flags/identity.d.ts +77 -0
- package/dist/flags/identity.d.ts.map +1 -0
- package/dist/flags/identity.js +134 -0
- package/dist/flags/identity.js.map +1 -0
- package/dist/flags/index.d.ts +20 -0
- package/dist/flags/index.d.ts.map +1 -0
- package/dist/flags/index.js +21 -0
- package/dist/flags/index.js.map +1 -0
- package/dist/flags/operators.d.ts +63 -0
- package/dist/flags/operators.d.ts.map +1 -0
- package/dist/flags/operators.js +299 -0
- package/dist/flags/operators.js.map +1 -0
- package/dist/flags/propagation.d.ts +16 -0
- package/dist/flags/propagation.d.ts.map +1 -0
- package/dist/flags/propagation.js +107 -0
- package/dist/flags/propagation.js.map +1 -0
- package/dist/flags/realtime.d.ts +328 -0
- package/dist/flags/realtime.d.ts.map +1 -0
- package/dist/flags/realtime.js +504 -0
- package/dist/flags/realtime.js.map +1 -0
- package/dist/flags/runtime-mode.d.ts +32 -0
- package/dist/flags/runtime-mode.d.ts.map +1 -0
- package/dist/flags/runtime-mode.js +84 -0
- package/dist/flags/runtime-mode.js.map +1 -0
- package/dist/flags/telemetry.d.ts +79 -0
- package/dist/flags/telemetry.d.ts.map +1 -0
- package/dist/flags/telemetry.js +286 -0
- package/dist/flags/telemetry.js.map +1 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +11 -1
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/management-types.d.ts +38 -0
- package/dist/management-types.d.ts.map +1 -1
- package/dist/route-guard.d.ts.map +1 -1
- package/dist/route-guard.js +15 -5
- package/dist/route-guard.js.map +1 -1
- package/dist/token-manager.d.ts +3 -1
- package/dist/token-manager.d.ts.map +1 -1
- package/dist/token-manager.js +10 -6
- package/dist/token-manager.js.map +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/usage/index.d.ts +3 -0
- package/dist/usage/index.d.ts.map +1 -0
- package/dist/usage/index.js +4 -0
- package/dist/usage/index.js.map +1 -0
- package/dist/usage/storage/durable-storage.d.ts +47 -0
- package/dist/usage/storage/durable-storage.d.ts.map +1 -0
- package/dist/usage/storage/durable-storage.js +27 -0
- package/dist/usage/storage/durable-storage.js.map +1 -0
- package/dist/usage/storage/index.d.ts +7 -0
- package/dist/usage/storage/index.d.ts.map +1 -0
- package/dist/usage/storage/index.js +28 -0
- package/dist/usage/storage/index.js.map +1 -0
- package/dist/usage/storage/indexeddb-storage.d.ts +18 -0
- package/dist/usage/storage/indexeddb-storage.d.ts.map +1 -0
- package/dist/usage/storage/indexeddb-storage.js +159 -0
- package/dist/usage/storage/indexeddb-storage.js.map +1 -0
- package/dist/usage/storage/node-fs-storage.d.ts +29 -0
- package/dist/usage/storage/node-fs-storage.d.ts.map +1 -0
- package/dist/usage/storage/node-fs-storage.js +180 -0
- package/dist/usage/storage/node-fs-storage.js.map +1 -0
- package/dist/usage/storage/noop-storage.d.ts +17 -0
- package/dist/usage/storage/noop-storage.d.ts.map +1 -0
- package/dist/usage/storage/noop-storage.js +67 -0
- package/dist/usage/storage/noop-storage.js.map +1 -0
- package/dist/usage/usage-reporter.d.ts +64 -0
- package/dist/usage/usage-reporter.d.ts.map +1 -0
- package/dist/usage/usage-reporter.js +234 -0
- package/dist/usage/usage-reporter.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Logger } from '../logger.js';
|
|
2
|
+
import type { RealtimeClient, SubscriptionPlanChangedMessage } from '../flags/realtime.js';
|
|
3
|
+
import type { BillingLockedPayload, BillingSubscriptionSnapshot, BillingSubscriptionState, MountOptions } from './types.js';
|
|
4
|
+
type Listener = (snapshot: BillingSubscriptionSnapshot) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Reactive container for the canonical Billing 2.0 subscription state.
|
|
7
|
+
* Framework-agnostic; consumers attach a listener via `subscribe()` and read
|
|
8
|
+
* the current value via `snapshot()`. Compatible with the Svelte store
|
|
9
|
+
* contract (`subscribe(listener) => unsubscribe`).
|
|
10
|
+
*
|
|
11
|
+
* Parallel to the FF 2.0 `BridgeFlags` reactive surface — do NOT unify yet.
|
|
12
|
+
* REF-1 (post-feature) will fold them together.
|
|
13
|
+
*/
|
|
14
|
+
export declare class BridgeSubscription {
|
|
15
|
+
private _state;
|
|
16
|
+
private _loading;
|
|
17
|
+
private _error;
|
|
18
|
+
private _listeners;
|
|
19
|
+
/** Current snapshot. */
|
|
20
|
+
snapshot(): BillingSubscriptionSnapshot;
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to snapshot changes. Invokes the listener immediately with the
|
|
23
|
+
* current snapshot (Svelte-store compatible). Returns an unsubscribe fn.
|
|
24
|
+
*/
|
|
25
|
+
subscribe(listener: Listener): () => void;
|
|
26
|
+
/** Replace the cached state and notify subscribers. */
|
|
27
|
+
hydrate(state: BillingSubscriptionState): void;
|
|
28
|
+
setLoading(loading: boolean): void;
|
|
29
|
+
setError(error: string | null): void;
|
|
30
|
+
/**
|
|
31
|
+
* Fetch `GET /billing/state` once and hydrate. Idempotent on success — the
|
|
32
|
+
* SDK caches the result in memory and any subsequent `mount()` call is
|
|
33
|
+
* effectively a manual refetch. US-3 adds the live push that updates the
|
|
34
|
+
* cache without a refetch.
|
|
35
|
+
*
|
|
36
|
+
* HTTP is intentionally inside auth-core here (per US-2 AC) so framework
|
|
37
|
+
* wrappers don't each reimplement the fetch.
|
|
38
|
+
*/
|
|
39
|
+
mount(opts: MountOptions, logger?: Logger): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Flip the store into the locked state from a caught `BillingLockedError`.
|
|
42
|
+
* Patches `gateEngaged`/`status`/`recoveryUrl` so the notice + gate react
|
|
43
|
+
* immediately even when no prior `mount()` baseline exists.
|
|
44
|
+
*/
|
|
45
|
+
markLocked(payload: BillingLockedPayload): void;
|
|
46
|
+
/**
|
|
47
|
+
* Billing 2.0 — attach to a Milestone K RealtimeClient so canonical
|
|
48
|
+
* lifecycle events on the workspace channel patch the reactive store.
|
|
49
|
+
* Handles US-3 plan-change + US-4+ lifecycle (payment.*, subscription.*,
|
|
50
|
+
* dunning.*, entitlements.*). Idempotent: re-attaching replaces hooks.
|
|
51
|
+
* Framework SDKs call this once after constructing both the RealtimeClient
|
|
52
|
+
* and the BridgeSubscription instance.
|
|
53
|
+
*/
|
|
54
|
+
attach(rt: RealtimeClient): void;
|
|
55
|
+
/**
|
|
56
|
+
* TBP-360 — Hydrate from a `subscription.plan_changed` push. Extracted into
|
|
57
|
+
* a public-but-internal method so `useBridge().attachToRealtimeClient` can
|
|
58
|
+
* install a composite hook (subscription hydrate + dev-handler fan-out)
|
|
59
|
+
* without re-implementing the patch logic.
|
|
60
|
+
*/
|
|
61
|
+
_applyPlanChanged(msg: SubscriptionPlanChangedMessage): void;
|
|
62
|
+
/**
|
|
63
|
+
* Patch the snapshot based on a lifecycle event. Conservative — only
|
|
64
|
+
* touches the fields the event semantically affects. Callers can still
|
|
65
|
+
* trigger a full refetch via `mount(opts)` to re-sync from the server.
|
|
66
|
+
*/
|
|
67
|
+
private _applyLifecycle;
|
|
68
|
+
private _notify;
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=bridge-subscription.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-subscription.d.ts","sourceRoot":"","sources":["../../src/billing/bridge-subscription.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAEV,cAAc,EACd,8BAA8B,EAC/B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACV,oBAAoB,EACpB,2BAA2B,EAC3B,wBAAwB,EAExB,YAAY,EAEb,MAAM,YAAY,CAAC;AAEpB,KAAK,QAAQ,GAAG,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,CAAC;AAQhE;;;;;;;;GAQG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAuB;IAEzC,wBAAwB;IACxB,QAAQ,IAAI,2BAA2B;IAQvC;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI;IAQzC,uDAAuD;IACvD,OAAO,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI;IAM9C,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAMlC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKpC;;;;;;;;OAQG;IACG,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,GAAE,MAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3E;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAW/C;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAKhC;;;;;OAKG;IACH,iBAAiB,CAAC,GAAG,EAAE,8BAA8B,GAAG,IAAI;IAO5D;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAwEvB,OAAO,CAAC,OAAO;CAMhB"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { fetchBillingState } from './fetch-billing-state.js';
|
|
2
|
+
const noopLogger = {
|
|
3
|
+
debug: () => { },
|
|
4
|
+
warn: () => { },
|
|
5
|
+
error: () => { },
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Reactive container for the canonical Billing 2.0 subscription state.
|
|
9
|
+
* Framework-agnostic; consumers attach a listener via `subscribe()` and read
|
|
10
|
+
* the current value via `snapshot()`. Compatible with the Svelte store
|
|
11
|
+
* contract (`subscribe(listener) => unsubscribe`).
|
|
12
|
+
*
|
|
13
|
+
* Parallel to the FF 2.0 `BridgeFlags` reactive surface — do NOT unify yet.
|
|
14
|
+
* REF-1 (post-feature) will fold them together.
|
|
15
|
+
*/
|
|
16
|
+
export class BridgeSubscription {
|
|
17
|
+
_state = null;
|
|
18
|
+
_loading = false;
|
|
19
|
+
_error = null;
|
|
20
|
+
_listeners = new Set();
|
|
21
|
+
/** Current snapshot. */
|
|
22
|
+
snapshot() {
|
|
23
|
+
return {
|
|
24
|
+
state: this._state,
|
|
25
|
+
loading: this._loading,
|
|
26
|
+
error: this._error,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to snapshot changes. Invokes the listener immediately with the
|
|
31
|
+
* current snapshot (Svelte-store compatible). Returns an unsubscribe fn.
|
|
32
|
+
*/
|
|
33
|
+
subscribe(listener) {
|
|
34
|
+
this._listeners.add(listener);
|
|
35
|
+
listener(this.snapshot());
|
|
36
|
+
return () => {
|
|
37
|
+
this._listeners.delete(listener);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Replace the cached state and notify subscribers. */
|
|
41
|
+
hydrate(state) {
|
|
42
|
+
this._state = state;
|
|
43
|
+
this._error = null;
|
|
44
|
+
this._notify();
|
|
45
|
+
}
|
|
46
|
+
setLoading(loading) {
|
|
47
|
+
if (this._loading === loading)
|
|
48
|
+
return;
|
|
49
|
+
this._loading = loading;
|
|
50
|
+
this._notify();
|
|
51
|
+
}
|
|
52
|
+
setError(error) {
|
|
53
|
+
this._error = error;
|
|
54
|
+
this._notify();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Fetch `GET /billing/state` once and hydrate. Idempotent on success — the
|
|
58
|
+
* SDK caches the result in memory and any subsequent `mount()` call is
|
|
59
|
+
* effectively a manual refetch. US-3 adds the live push that updates the
|
|
60
|
+
* cache without a refetch.
|
|
61
|
+
*
|
|
62
|
+
* HTTP is intentionally inside auth-core here (per US-2 AC) so framework
|
|
63
|
+
* wrappers don't each reimplement the fetch.
|
|
64
|
+
*/
|
|
65
|
+
async mount(opts, logger = noopLogger) {
|
|
66
|
+
this.setLoading(true);
|
|
67
|
+
this.setError(null);
|
|
68
|
+
try {
|
|
69
|
+
const state = await fetchBillingState(opts, logger);
|
|
70
|
+
this.hydrate(state);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const message = err instanceof Error ? err.message : 'Failed to load billing state';
|
|
74
|
+
this.setError(message);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this.setLoading(false);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Flip the store into the locked state from a caught `BillingLockedError`.
|
|
82
|
+
* Patches `gateEngaged`/`status`/`recoveryUrl` so the notice + gate react
|
|
83
|
+
* immediately even when no prior `mount()` baseline exists.
|
|
84
|
+
*/
|
|
85
|
+
markLocked(payload) {
|
|
86
|
+
const current = this._state;
|
|
87
|
+
const next = current
|
|
88
|
+
? { ...current }
|
|
89
|
+
: { plan: { slug: 'unknown', name: 'Unknown' }, status: payload.billing.status };
|
|
90
|
+
next.status = payload.billing.status;
|
|
91
|
+
next.gateEngaged = payload.billing.gateEngaged;
|
|
92
|
+
if (payload.billing.recoveryUrl)
|
|
93
|
+
next.recoveryUrl = payload.billing.recoveryUrl;
|
|
94
|
+
this.hydrate(next);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Billing 2.0 — attach to a Milestone K RealtimeClient so canonical
|
|
98
|
+
* lifecycle events on the workspace channel patch the reactive store.
|
|
99
|
+
* Handles US-3 plan-change + US-4+ lifecycle (payment.*, subscription.*,
|
|
100
|
+
* dunning.*, entitlements.*). Idempotent: re-attaching replaces hooks.
|
|
101
|
+
* Framework SDKs call this once after constructing both the RealtimeClient
|
|
102
|
+
* and the BridgeSubscription instance.
|
|
103
|
+
*/
|
|
104
|
+
attach(rt) {
|
|
105
|
+
rt.setOnSubscriptionPlanChanged((msg) => this._applyPlanChanged(msg));
|
|
106
|
+
rt.setOnBillingLifecycle((msg) => this._applyLifecycle(msg));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* TBP-360 — Hydrate from a `subscription.plan_changed` push. Extracted into
|
|
110
|
+
* a public-but-internal method so `useBridge().attachToRealtimeClient` can
|
|
111
|
+
* install a composite hook (subscription hydrate + dev-handler fan-out)
|
|
112
|
+
* without re-implementing the patch logic.
|
|
113
|
+
*/
|
|
114
|
+
_applyPlanChanged(msg) {
|
|
115
|
+
this.hydrate({
|
|
116
|
+
plan: msg.to,
|
|
117
|
+
status: msg.status,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Patch the snapshot based on a lifecycle event. Conservative — only
|
|
122
|
+
* touches the fields the event semantically affects. Callers can still
|
|
123
|
+
* trigger a full refetch via `mount(opts)` to re-sync from the server.
|
|
124
|
+
*/
|
|
125
|
+
_applyLifecycle(msg) {
|
|
126
|
+
const current = this._state;
|
|
127
|
+
if (!current) {
|
|
128
|
+
// No baseline yet; lifecycle events can't reconstruct a full state
|
|
129
|
+
// from scratch — they're patches. Skip; consumer should mount first.
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const next = { ...current };
|
|
133
|
+
switch (msg.kind) {
|
|
134
|
+
case 'payment.failed':
|
|
135
|
+
next.status = 'past_due';
|
|
136
|
+
next.pastDueReason = msg.pastDueReason ?? 'card_declined';
|
|
137
|
+
if (msg.cardLast4)
|
|
138
|
+
next.cardLast4 = msg.cardLast4;
|
|
139
|
+
break;
|
|
140
|
+
case 'payment.succeeded':
|
|
141
|
+
case 'dunning.recovered':
|
|
142
|
+
next.status = 'active';
|
|
143
|
+
next.pastDueReason = null;
|
|
144
|
+
next.nextRetryAt = undefined;
|
|
145
|
+
next.finalRetryAt = undefined;
|
|
146
|
+
next.gateEngaged = false;
|
|
147
|
+
break;
|
|
148
|
+
case 'subscription.canceled':
|
|
149
|
+
next.status = 'canceled';
|
|
150
|
+
if (msg.endsAt)
|
|
151
|
+
next.endsAt = msg.endsAt;
|
|
152
|
+
break;
|
|
153
|
+
case 'subscription.reactivated':
|
|
154
|
+
next.status = 'active';
|
|
155
|
+
next.endsAt = undefined;
|
|
156
|
+
break;
|
|
157
|
+
case 'subscription.trial_started':
|
|
158
|
+
next.status = 'trial';
|
|
159
|
+
if (msg.endsAt)
|
|
160
|
+
next.endsAt = msg.endsAt;
|
|
161
|
+
if (msg.daysLeft !== undefined)
|
|
162
|
+
next.daysLeft = msg.daysLeft;
|
|
163
|
+
if (msg.hasCardOnFile !== undefined)
|
|
164
|
+
next.hasCardOnFile = msg.hasCardOnFile;
|
|
165
|
+
break;
|
|
166
|
+
case 'subscription.trial_ending_soon':
|
|
167
|
+
if (msg.daysLeft !== undefined)
|
|
168
|
+
next.daysLeft = msg.daysLeft;
|
|
169
|
+
break;
|
|
170
|
+
case 'subscription.trial_converted':
|
|
171
|
+
next.status = 'active';
|
|
172
|
+
next.endsAt = undefined;
|
|
173
|
+
next.daysLeft = undefined;
|
|
174
|
+
break;
|
|
175
|
+
case 'subscription.trial_expired':
|
|
176
|
+
next.status = 'past_due';
|
|
177
|
+
next.pastDueReason = 'trial_expired';
|
|
178
|
+
break;
|
|
179
|
+
case 'dunning.entered':
|
|
180
|
+
case 'dunning.retry_scheduled':
|
|
181
|
+
next.status = 'past_due';
|
|
182
|
+
if (msg.nextRetryAt)
|
|
183
|
+
next.nextRetryAt = msg.nextRetryAt;
|
|
184
|
+
if (msg.finalRetryAt)
|
|
185
|
+
next.finalRetryAt = msg.finalRetryAt;
|
|
186
|
+
break;
|
|
187
|
+
case 'dunning.exhausted':
|
|
188
|
+
next.status = 'past_due';
|
|
189
|
+
next.gateEngaged = msg.gateEngaged ?? true;
|
|
190
|
+
break;
|
|
191
|
+
case 'entitlements.changed':
|
|
192
|
+
// Pure signal — consumer apps re-read entitlements. No status mutation here.
|
|
193
|
+
break;
|
|
194
|
+
case 'subscription.created':
|
|
195
|
+
case 'subscription.updated':
|
|
196
|
+
// No-op on the cached snapshot. Mount fetches the full state if the
|
|
197
|
+
// consumer needs the new values.
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
this.hydrate(next);
|
|
201
|
+
}
|
|
202
|
+
_notify() {
|
|
203
|
+
const snap = this.snapshot();
|
|
204
|
+
for (const listener of this._listeners) {
|
|
205
|
+
listener(snap);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=bridge-subscription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-subscription.js","sourceRoot":"","sources":["../../src/billing/bridge-subscription.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Logger } from '../logger.js';
|
|
2
|
+
/** Public entitlement snapshot shape — a flat boolean map. */
|
|
3
|
+
export type EntitlementSnapshot = Record<string, boolean>;
|
|
4
|
+
type Listener = (snapshot: EntitlementSnapshot) => void;
|
|
5
|
+
interface ConfigureOptions {
|
|
6
|
+
apiBaseUrl: string;
|
|
7
|
+
/**
|
|
8
|
+
* Returns the current Bearer access token, or null when unauthenticated
|
|
9
|
+
* (hydrate is skipped). Lazy so the wrapper can rotate tokens without
|
|
10
|
+
* reconfiguring the store.
|
|
11
|
+
*/
|
|
12
|
+
getAccessToken: () => string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare class EntitlementsStore {
|
|
15
|
+
private _cache;
|
|
16
|
+
private _hydrated;
|
|
17
|
+
private _hydrating;
|
|
18
|
+
private _subscribers;
|
|
19
|
+
private _opts;
|
|
20
|
+
private _logger;
|
|
21
|
+
/** Wire HTTP options + optional logger. Framework wrappers call this once. */
|
|
22
|
+
configure(opts: ConfigureOptions, logger?: Logger): void;
|
|
23
|
+
/** Has the initial REST fetch completed? Drives fail-closed reads. */
|
|
24
|
+
isHydrated(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Initial-hydration fetch. Idempotent — concurrent callers share one
|
|
27
|
+
* round-trip; subsequent calls after success are no-ops unless the caller
|
|
28
|
+
* explicitly resets. Failures leave the store unhydrated; `can()` keeps
|
|
29
|
+
* returning false until a live push or a retry succeeds.
|
|
30
|
+
*/
|
|
31
|
+
hydrate(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Wholesale-replace the cache from a server-pushed snapshot. The server
|
|
34
|
+
* publishes the FULL set on every change so the SDK never has to merge
|
|
35
|
+
* partial deltas — last-write-wins.
|
|
36
|
+
*/
|
|
37
|
+
applyEntitlementsChanged(snapshot: EntitlementSnapshot): void;
|
|
38
|
+
/**
|
|
39
|
+
* Fail-closed read.
|
|
40
|
+
* - Before hydration: returns `false`.
|
|
41
|
+
* - After hydration with a key NOT in the cache: returns `false`.
|
|
42
|
+
* - After hydration with a key in the cache: returns the boolean.
|
|
43
|
+
*
|
|
44
|
+
* Consumers gate UX with `{#if bridge.entitlements.can('ai_completions')}`
|
|
45
|
+
* — fail-closed protects against showing privileged UX on a cold start.
|
|
46
|
+
*/
|
|
47
|
+
can(name: string): boolean;
|
|
48
|
+
/** All cached entitlements (shallow copy). */
|
|
49
|
+
all(): EntitlementSnapshot;
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to snapshot changes. Returns an unsubscribe fn.
|
|
52
|
+
* Does NOT invoke immediately with the current snapshot — the consumer can
|
|
53
|
+
* read `all()` synchronously if it needs the initial value.
|
|
54
|
+
*/
|
|
55
|
+
subscribe(cb: Listener): () => void;
|
|
56
|
+
/** Test-only: clear cache + listeners. */
|
|
57
|
+
__resetForTests(): void;
|
|
58
|
+
private _notify;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=entitlements-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements-store.d.ts","sourceRoot":"","sources":["../../src/billing/entitlements-store.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,8DAA8D;AAC9D,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1D,KAAK,QAAQ,GAAG,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAExD,UAAU,gBAAgB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,cAAc,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACrC;AAQD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,OAAO,CAAsB;IAErC,8EAA8E;IAC9E,SAAS,CAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,GAAE,MAAmB,GAAG,IAAI;IAKpE,sEAAsE;IACtE,UAAU,IAAI,OAAO;IAIrB;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B9B;;;;OAIG;IACH,wBAAwB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAM7D;;;;;;;;OAQG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAK1B,8CAA8C;IAC9C,GAAG,IAAI,mBAAmB;IAI1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,IAAI;IAOnC,0CAA0C;IAC1C,eAAe,IAAI,IAAI;IAQvB,OAAO,CAAC,OAAO;CAUhB"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Billing 2.0 US-12 (TBP-264) — SDK entitlement cache + reactive surface.
|
|
2
|
+
//
|
|
3
|
+
// Backs `useBridge().entitlements.can(name)`. Holds the boolean map published
|
|
4
|
+
// by the bridge-api EntitlementService:
|
|
5
|
+
// - `hydrate()` — `GET /entitlements` once on attach.
|
|
6
|
+
// - `applyEntitlementsChanged(snapshot)` — replaces cache wholesale on every
|
|
7
|
+
// `entitlements.changed` push from the workspace channel. Last-write-wins.
|
|
8
|
+
// - `can(name)` — fail-closed: returns `false` until hydrated and only `true`
|
|
9
|
+
// when the key is explicitly present and `true`.
|
|
10
|
+
//
|
|
11
|
+
// Framework-agnostic. Subscribers receive a snapshot notification on every
|
|
12
|
+
// cache mutation; the bridge-svelte/react wrappers wire those into reactive
|
|
13
|
+
// containers.
|
|
14
|
+
import { httpFetch } from '../http.js';
|
|
15
|
+
const noopLogger = {
|
|
16
|
+
debug: () => { },
|
|
17
|
+
warn: () => { },
|
|
18
|
+
error: () => { },
|
|
19
|
+
};
|
|
20
|
+
export class EntitlementsStore {
|
|
21
|
+
_cache = {};
|
|
22
|
+
_hydrated = false;
|
|
23
|
+
_hydrating = false;
|
|
24
|
+
_subscribers = new Set();
|
|
25
|
+
_opts = null;
|
|
26
|
+
_logger = noopLogger;
|
|
27
|
+
/** Wire HTTP options + optional logger. Framework wrappers call this once. */
|
|
28
|
+
configure(opts, logger = noopLogger) {
|
|
29
|
+
this._opts = opts;
|
|
30
|
+
this._logger = logger;
|
|
31
|
+
}
|
|
32
|
+
/** Has the initial REST fetch completed? Drives fail-closed reads. */
|
|
33
|
+
isHydrated() {
|
|
34
|
+
return this._hydrated;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Initial-hydration fetch. Idempotent — concurrent callers share one
|
|
38
|
+
* round-trip; subsequent calls after success are no-ops unless the caller
|
|
39
|
+
* explicitly resets. Failures leave the store unhydrated; `can()` keeps
|
|
40
|
+
* returning false until a live push or a retry succeeds.
|
|
41
|
+
*/
|
|
42
|
+
async hydrate() {
|
|
43
|
+
if (this._hydrated || this._hydrating)
|
|
44
|
+
return;
|
|
45
|
+
if (!this._opts)
|
|
46
|
+
return;
|
|
47
|
+
const token = this._opts.getAccessToken();
|
|
48
|
+
if (!token)
|
|
49
|
+
return;
|
|
50
|
+
this._hydrating = true;
|
|
51
|
+
try {
|
|
52
|
+
const url = `${this._opts.apiBaseUrl.replace(/\/+$/, '')}/entitlements`;
|
|
53
|
+
const body = await httpFetch(url, {
|
|
54
|
+
method: 'GET',
|
|
55
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
56
|
+
}, this._logger);
|
|
57
|
+
this._cache = { ...(body?.entitlements ?? {}) };
|
|
58
|
+
this._hydrated = true;
|
|
59
|
+
this._notify();
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
this._logger.warn('[bridge.entitlements] hydrate failed', err instanceof Error ? err.message : err);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
this._hydrating = false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Wholesale-replace the cache from a server-pushed snapshot. The server
|
|
70
|
+
* publishes the FULL set on every change so the SDK never has to merge
|
|
71
|
+
* partial deltas — last-write-wins.
|
|
72
|
+
*/
|
|
73
|
+
applyEntitlementsChanged(snapshot) {
|
|
74
|
+
this._cache = { ...snapshot };
|
|
75
|
+
this._hydrated = true;
|
|
76
|
+
this._notify();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Fail-closed read.
|
|
80
|
+
* - Before hydration: returns `false`.
|
|
81
|
+
* - After hydration with a key NOT in the cache: returns `false`.
|
|
82
|
+
* - After hydration with a key in the cache: returns the boolean.
|
|
83
|
+
*
|
|
84
|
+
* Consumers gate UX with `{#if bridge.entitlements.can('ai_completions')}`
|
|
85
|
+
* — fail-closed protects against showing privileged UX on a cold start.
|
|
86
|
+
*/
|
|
87
|
+
can(name) {
|
|
88
|
+
if (!this._hydrated)
|
|
89
|
+
return false;
|
|
90
|
+
return this._cache[name] === true;
|
|
91
|
+
}
|
|
92
|
+
/** All cached entitlements (shallow copy). */
|
|
93
|
+
all() {
|
|
94
|
+
return { ...this._cache };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Subscribe to snapshot changes. Returns an unsubscribe fn.
|
|
98
|
+
* Does NOT invoke immediately with the current snapshot — the consumer can
|
|
99
|
+
* read `all()` synchronously if it needs the initial value.
|
|
100
|
+
*/
|
|
101
|
+
subscribe(cb) {
|
|
102
|
+
this._subscribers.add(cb);
|
|
103
|
+
return () => {
|
|
104
|
+
this._subscribers.delete(cb);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/** Test-only: clear cache + listeners. */
|
|
108
|
+
__resetForTests() {
|
|
109
|
+
this._cache = {};
|
|
110
|
+
this._hydrated = false;
|
|
111
|
+
this._hydrating = false;
|
|
112
|
+
this._subscribers.clear();
|
|
113
|
+
this._opts = null;
|
|
114
|
+
}
|
|
115
|
+
_notify() {
|
|
116
|
+
const snap = this.all();
|
|
117
|
+
for (const cb of this._subscribers) {
|
|
118
|
+
try {
|
|
119
|
+
cb(snap);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// ignore — listener errors must not break the cache
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=entitlements-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements-store.js","sourceRoot":"","sources":["../../src/billing/entitlements-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Logger } from '../logger.js';
|
|
2
|
+
import type { BillingSubscriptionState, MountOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pure, isomorphic read of `GET /billing/state`. No store, no realtime — server
|
|
5
|
+
* plugins (bridge-nextjs) call this directly; the client store's `mount()` wraps
|
|
6
|
+
* it. A billing-locked workspace throws `BillingLockedError` via the http chokepoint.
|
|
7
|
+
*/
|
|
8
|
+
export declare function fetchBillingState(opts: MountOptions, logger?: Logger): Promise<BillingSubscriptionState>;
|
|
9
|
+
//# sourceMappingURL=fetch-billing-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-billing-state.d.ts","sourceRoot":"","sources":["../../src/billing/fetch-billing-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQzE;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,YAAY,EAClB,MAAM,GAAE,MAAmB,GAC1B,OAAO,CAAC,wBAAwB,CAAC,CAanC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { httpFetch } from '../http.js';
|
|
2
|
+
const noopLogger = {
|
|
3
|
+
debug: () => { },
|
|
4
|
+
warn: () => { },
|
|
5
|
+
error: () => { },
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Pure, isomorphic read of `GET /billing/state`. No store, no realtime — server
|
|
9
|
+
* plugins (bridge-nextjs) call this directly; the client store's `mount()` wraps
|
|
10
|
+
* it. A billing-locked workspace throws `BillingLockedError` via the http chokepoint.
|
|
11
|
+
*/
|
|
12
|
+
export async function fetchBillingState(opts, logger = noopLogger) {
|
|
13
|
+
const url = `${opts.apiBaseUrl}/billing/state`;
|
|
14
|
+
return httpFetch(url, {
|
|
15
|
+
method: 'GET',
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${opts.accessToken}`,
|
|
18
|
+
'x-app-id': opts.appId,
|
|
19
|
+
},
|
|
20
|
+
}, logger);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=fetch-billing-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-billing-state.js","sourceRoot":"","sources":["../../src/billing/fetch-billing-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,MAAM,UAAU,GAAW;IACzB,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAkB,EAClB,SAAiB,UAAU;IAE3B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,UAAU,gBAAgB,CAAC;IAC/C,OAAO,SAAS,CACd,GAAG,EACH;QACE,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,IAAI,CAAC,WAAW,EAAE;YAC3C,UAAU,EAAE,IAAI,CAAC,KAAK;SACvB;KACF,EACD,MAAM,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BillingLockedPayload } from './types.js';
|
|
2
|
+
type LockHandler = (payload: BillingLockedPayload) => void;
|
|
3
|
+
export declare function setBillingLockHandler(fn: LockHandler | null): void;
|
|
4
|
+
export declare function emitBillingLock(payload: BillingLockedPayload): void;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=lock-signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock-signal.d.ts","sourceRoot":"","sources":["../../src/billing/lock-signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAMvD,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;AAI3D,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAElE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAEnE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock-signal.js","sourceRoot":"","sources":["../../src/billing/lock-signal.ts"],"names":[],"mappings":"AAQA,IAAI,OAAO,GAAuB,IAAI,CAAC;AAEvC,MAAM,UAAU,qBAAqB,CAAC,EAAsB;IAC1D,OAAO,GAAG,EAAE,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAA6B;IAC3D,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Logger } from '../logger.js';
|
|
2
|
+
import type { QuotaUpdatedMessage, RealtimeClient } from '../flags/realtime.js';
|
|
3
|
+
/** Public quota snapshot shape exposed to SDK consumers. */
|
|
4
|
+
export interface QuotaSnapshot {
|
|
5
|
+
metric: string;
|
|
6
|
+
used: number;
|
|
7
|
+
limit: number;
|
|
8
|
+
remaining: number;
|
|
9
|
+
/** Convenience: `used / limit` clamped to [0, +inf). */
|
|
10
|
+
percent_used: number;
|
|
11
|
+
/**
|
|
12
|
+
* US-12 — server-driven per-metric policy.
|
|
13
|
+
* - `metered` → Stripe bills the overage automatically; NO entitlement
|
|
14
|
+
* produced; UI shows "metered usage" copy.
|
|
15
|
+
* - `hard` → entitlement flips to false at the cap; UI gates on
|
|
16
|
+
* `useBridge().entitlements.can(<key>)` instead of on the counter.
|
|
17
|
+
*
|
|
18
|
+
* Default for backward compatibility (pre-US-12 server payload missing
|
|
19
|
+
* `policy`): `'metered'`.
|
|
20
|
+
*/
|
|
21
|
+
policy: 'hard' | 'metered';
|
|
22
|
+
warningLevel: null | 'approaching' | 'critical';
|
|
23
|
+
/** Display label. US-11 uses the raw metric key; framework wrappers can override. */
|
|
24
|
+
label: string;
|
|
25
|
+
}
|
|
26
|
+
type Listener = (metric: string, snap: QuotaSnapshot | undefined) => void;
|
|
27
|
+
interface MountOptions {
|
|
28
|
+
apiBaseUrl: string;
|
|
29
|
+
/** Bearer access token. Null when unauthenticated — hydrate is skipped. */
|
|
30
|
+
accessToken: string | null;
|
|
31
|
+
appId: string;
|
|
32
|
+
}
|
|
33
|
+
export declare class QuotaStore {
|
|
34
|
+
private _snapshots;
|
|
35
|
+
private _hydrating;
|
|
36
|
+
private _listeners;
|
|
37
|
+
/**
|
|
38
|
+
* Options used for hydration HTTP. Set via `configure()` before the first
|
|
39
|
+
* `get(metric)` call; otherwise hydration is skipped and only live pushes
|
|
40
|
+
* populate the cache.
|
|
41
|
+
*/
|
|
42
|
+
private _opts;
|
|
43
|
+
private _logger;
|
|
44
|
+
/** Wire HTTP options + optional logger. Framework wrappers call this once. */
|
|
45
|
+
configure(opts: MountOptions, logger?: Logger): void;
|
|
46
|
+
/** Current cached snapshot for a metric, or undefined while hydrating / unconfigured. */
|
|
47
|
+
get(metric: string): QuotaSnapshot | undefined;
|
|
48
|
+
/** All cached snapshots. */
|
|
49
|
+
getAll(): Map<string, QuotaSnapshot>;
|
|
50
|
+
/** Subscribe to per-metric snapshot changes. Returns an unsubscribe fn. */
|
|
51
|
+
subscribe(listener: Listener): () => void;
|
|
52
|
+
/**
|
|
53
|
+
* Lazy hydration entry point. On the first call for a metric, kicks off a
|
|
54
|
+
* `GET /usage/quota/:metric` round-trip and marks the metric as hydrating.
|
|
55
|
+
* Subsequent calls are no-ops until the response arrives or a live push
|
|
56
|
+
* lands.
|
|
57
|
+
*
|
|
58
|
+
* Non-blocking — returns the current cached snapshot (or undefined) and
|
|
59
|
+
* lets the fetch resolve asynchronously. Consumers re-render via the
|
|
60
|
+
* subscriber notification.
|
|
61
|
+
*/
|
|
62
|
+
ensureHydrated(metric: string): QuotaSnapshot | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Apply a live `quota.updated` payload to the cache. Last-write-wins;
|
|
65
|
+
* notifies subscribers. Hydration is implicitly cleared if it was pending.
|
|
66
|
+
*/
|
|
67
|
+
applyQuotaUpdated(msg: QuotaUpdatedMessage): void;
|
|
68
|
+
/**
|
|
69
|
+
* Apply the result of an initial-hydration REST fetch. Same semantics as a
|
|
70
|
+
* live push but driven by the server's `GET /usage/quota/:metric` reply.
|
|
71
|
+
*/
|
|
72
|
+
applyInitialSnapshot(metric: string, snapshot: (Omit<QuotaSnapshot, 'percent_used' | 'policy' | 'label'> & {
|
|
73
|
+
policy?: 'hard' | 'metered';
|
|
74
|
+
}) | null): void;
|
|
75
|
+
/**
|
|
76
|
+
* Wire the store to a RealtimeClient so `quota.updated` pushes flow into
|
|
77
|
+
* `applyQuotaUpdated`. Idempotent: re-attaching replaces the hook.
|
|
78
|
+
*/
|
|
79
|
+
attach(rt: RealtimeClient): void;
|
|
80
|
+
/** Test-only: clear cache + listeners. */
|
|
81
|
+
__resetForTests(): void;
|
|
82
|
+
private _notify;
|
|
83
|
+
private _fetchSnapshot;
|
|
84
|
+
}
|
|
85
|
+
export {};
|
|
86
|
+
//# sourceMappingURL=quota-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota-store.d.ts","sourceRoot":"","sources":["../../src/billing/quota-store.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;OASG;IACH,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,YAAY,EAAE,IAAI,GAAG,aAAa,GAAG,UAAU,CAAC;IAChD,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,KAAK,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,SAAS,KAAK,IAAI,CAAC;AAE1E,UAAU,YAAY;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAQD,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAAuB;IACzC;;;;OAIG;IACH,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,OAAO,CAAsB;IAErC,8EAA8E;IAC9E,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,GAAE,MAAmB,GAAG,IAAI;IAKhE,yFAAyF;IACzF,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9C,4BAA4B;IAC5B,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC;IAIpC,2EAA2E;IAC3E,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI;IAOzC;;;;;;;;;OASG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAUzD;;;OAGG;IACH,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI;IAmBjD;;;OAGG;IACH,oBAAoB,CAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EACJ,CAAC,IAAI,CAAC,aAAa,EAAE,cAAc,GAAG,QAAQ,GAAG,OAAO,CAAC,GAAG;QAC1D,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,CAAC,GACF,IAAI,GACP,IAAI;IAyBP;;;OAGG;IACH,MAAM,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAIhC,0CAA0C;IAC1C,eAAe,IAAI,IAAI;IAOvB,OAAO,CAAC,OAAO;YAUD,cAAc;CAkD7B"}
|