@comfyorg/account-core 1.0.0-alpha.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/LICENSE +21 -0
- package/dist/core/billing/balanceWatch.d.ts +35 -0
- package/dist/core/billing/balanceWatch.js +83 -0
- package/dist/core/billing/billingContracts.d.ts +123 -0
- package/dist/core/billing/billingContracts.js +11 -0
- package/dist/core/billing/billingErrorBody.d.ts +8 -0
- package/dist/core/billing/billingErrorBody.js +11 -0
- package/dist/core/billing/billingScope.d.ts +39 -0
- package/dist/core/billing/billingScope.js +45 -0
- package/dist/core/billing/capabilities.d.ts +70 -0
- package/dist/core/billing/capabilities.js +137 -0
- package/dist/core/billing/capabilityDenials.d.ts +26 -0
- package/dist/core/billing/capabilityDenials.js +43 -0
- package/dist/core/billing/challengeDriver.d.ts +22 -0
- package/dist/core/billing/challengeDriver.js +38 -0
- package/dist/core/billing/credentialedTransport.d.ts +35 -0
- package/dist/core/billing/credentialedTransport.js +53 -0
- package/dist/core/billing/credits.d.ts +43 -0
- package/dist/core/billing/credits.js +29 -0
- package/dist/core/billing/httpStatus.d.ts +7 -0
- package/dist/core/billing/httpStatus.js +25 -0
- package/dist/core/billing/index.d.ts +52 -0
- package/dist/core/billing/index.js +23 -0
- package/dist/core/billing/operationLifecycle.d.ts +77 -0
- package/dist/core/billing/operationLifecycle.js +547 -0
- package/dist/core/billing/operationPointer.d.ts +48 -0
- package/dist/core/billing/operationPointer.js +89 -0
- package/dist/core/billing/operationPolicy.d.ts +26 -0
- package/dist/core/billing/operationPolicy.js +51 -0
- package/dist/core/billing/operationState.d.ts +128 -0
- package/dist/core/billing/operationState.js +187 -0
- package/dist/core/billing/paymentCopy.d.ts +18 -0
- package/dist/core/billing/paymentCopy.js +47 -0
- package/dist/core/billing/paymentMethods.d.ts +40 -0
- package/dist/core/billing/paymentMethods.js +37 -0
- package/dist/core/billing/paymentProjection.d.ts +34 -0
- package/dist/core/billing/paymentProjection.js +56 -0
- package/dist/core/billing/plans.d.ts +44 -0
- package/dist/core/billing/plans.js +29 -0
- package/dist/core/billing/presentation.d.ts +20 -0
- package/dist/core/billing/presentation.js +10 -0
- package/dist/core/billing/scopedReader.d.ts +61 -0
- package/dist/core/billing/scopedReader.js +108 -0
- package/dist/core/billing/sharedRead.d.ts +33 -0
- package/dist/core/billing/sharedRead.js +59 -0
- package/dist/core/billing/status.d.ts +27 -0
- package/dist/core/billing/status.js +21 -0
- package/dist/core/billing/subscriptionCommands.d.ts +367 -0
- package/dist/core/billing/subscriptionCommands.js +263 -0
- package/dist/core/billing/topup.d.ts +113 -0
- package/dist/core/billing/topup.js +209 -0
- package/dist/core/billing/transport.d.ts +14 -0
- package/dist/core/billing/transport.js +106 -0
- package/dist/core/billing/transportExchange.d.ts +24 -0
- package/dist/core/billing/transportExchange.js +79 -0
- package/dist/core/boundedOperation.d.ts +16 -0
- package/dist/core/boundedOperation.js +12 -0
- package/dist/core/credentialCache.d.ts +41 -0
- package/dist/core/credentialCache.js +97 -0
- package/dist/core/customerRecovery.d.ts +39 -0
- package/dist/core/customerRecovery.js +79 -0
- package/dist/core/exchange.d.ts +50 -0
- package/dist/core/exchange.js +137 -0
- package/dist/core/identity.d.ts +21 -0
- package/dist/core/identity.js +16 -0
- package/dist/core/mintCoordinator.d.ts +18 -0
- package/dist/core/mintCoordinator.js +31 -0
- package/dist/core/refreshScheduler.d.ts +46 -0
- package/dist/core/refreshScheduler.js +262 -0
- package/dist/core/session.d.ts +146 -0
- package/dist/core/session.js +312 -0
- package/dist/core/sessionContracts.d.ts +135 -0
- package/dist/core/sessionContracts.js +9 -0
- package/dist/core/sessionState.d.ts +118 -0
- package/dist/core/sessionState.js +235 -0
- package/dist/firebase/index.d.ts +53 -0
- package/dist/firebase/index.js +74 -0
- package/dist/firebaseAuthError.d.ts +52 -0
- package/dist/firebaseAuthError.js +88 -0
- package/dist/loadExternalScript.d.ts +7 -0
- package/dist/loadExternalScript.js +88 -0
- package/dist/provisioning.d.ts +53 -0
- package/dist/provisioning.js +76 -0
- package/dist/redirect.d.ts +13 -0
- package/dist/redirect.js +38 -0
- package/dist/signInSchemas.d.ts +88 -0
- package/dist/signInSchemas.js +71 -0
- package/dist/telemetry.d.ts +39 -0
- package/dist/telemetry.js +26 -0
- package/dist/testing.d.ts +7 -0
- package/dist/testing.js +3 -0
- package/dist/turnstile.d.ts +14 -0
- package/dist/turnstile.js +17 -0
- package/dist/turnstileScript.d.ts +18 -0
- package/dist/turnstileScript.js +6 -0
- package/dist/web/crossTabRefresh.d.ts +18 -0
- package/dist/web/crossTabRefresh.js +121 -0
- package/dist/webviewDetection.d.ts +5 -0
- package/dist/webviewDetection.js +62 -0
- package/package.json +114 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drives one embedded challenge through the host's payment-provider port and
|
|
3
|
+
* reports the verdict back to the lifecycle. The provider object stays on the
|
|
4
|
+
* host side: the core sees only `handleNextAction`, so no provider SDK is
|
|
5
|
+
* imported here and a test can hand in a plain function.
|
|
6
|
+
*/
|
|
7
|
+
import type { BillingOperationLifecycle } from './operationLifecycle.js';
|
|
8
|
+
export interface EmbeddedChallengePort {
|
|
9
|
+
/** Stripe's `handleNextAction` shape: resolve the challenge for one client secret. */
|
|
10
|
+
handleNextAction: (clientSecret: string) => Promise<{
|
|
11
|
+
readonly error?: unknown;
|
|
12
|
+
readonly paymentIntent?: {
|
|
13
|
+
readonly status: string;
|
|
14
|
+
};
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export type EmbeddedChallengeOutcome = 'completed' | 'failed'
|
|
18
|
+
/** No required challenge on an embedded, pending operation with that id. */
|
|
19
|
+
| 'not_required';
|
|
20
|
+
type ChallengeLifecycle = Pick<BillingOperationLifecycle, 'get' | 'reportChallengeStarted' | 'reportChallengeSettled'>;
|
|
21
|
+
export declare function driveEmbeddedChallenge(lifecycle: ChallengeLifecycle, operationId: string, port: EmbeddedChallengePort): Promise<EmbeddedChallengeOutcome>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statuses that mean the resumed challenge left the intent exactly where it
|
|
3
|
+
* started, so the attempt needs a fresh start rather than a poll. A denylist
|
|
4
|
+
* fails toward the server: an unlisted or future status reports optimistically
|
|
5
|
+
* as completed, and the next poll's own `authentication_state` corrects it.
|
|
6
|
+
*/
|
|
7
|
+
const UNMOVED_INTENT_STATUSES = new Set([
|
|
8
|
+
'requires_payment_method',
|
|
9
|
+
'requires_action',
|
|
10
|
+
'canceled'
|
|
11
|
+
]);
|
|
12
|
+
export async function driveEmbeddedChallenge(lifecycle, operationId, port) {
|
|
13
|
+
const state = lifecycle.get(operationId);
|
|
14
|
+
if (state?.phase !== 'pending' ||
|
|
15
|
+
state.presentation !== 'embedded' ||
|
|
16
|
+
state.challenge?.status !== 'required') {
|
|
17
|
+
return 'not_required';
|
|
18
|
+
}
|
|
19
|
+
lifecycle.reportChallengeStarted(operationId);
|
|
20
|
+
const outcome = await settle(port, state.challenge.clientSecret);
|
|
21
|
+
lifecycle.reportChallengeSettled(operationId, outcome);
|
|
22
|
+
return outcome;
|
|
23
|
+
}
|
|
24
|
+
async function settle(port, clientSecret) {
|
|
25
|
+
try {
|
|
26
|
+
const result = await port.handleNextAction(clientSecret);
|
|
27
|
+
if (result.error !== undefined)
|
|
28
|
+
return 'failed';
|
|
29
|
+
if (result.paymentIntent !== undefined &&
|
|
30
|
+
UNMOVED_INTENT_STATUSES.has(result.paymentIntent.status)) {
|
|
31
|
+
return 'failed';
|
|
32
|
+
}
|
|
33
|
+
return 'completed';
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return 'failed';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The billing transport for a host whose credentials it never holds: a BFF
|
|
3
|
+
* that proxies the cloud API with server-held secrets on its own origin, or a
|
|
4
|
+
* hosted app the backend has given a session cookie. The request carries
|
|
5
|
+
* `credentials` and no `Authorization` header, and the scope comes from the
|
|
6
|
+
* host's scope source rather than from a minted credential.
|
|
7
|
+
*
|
|
8
|
+
* There is no 401 retry here, because there is nothing this client can
|
|
9
|
+
* re-mint. A 401 is returned as an HTTP answer like any other, marked
|
|
10
|
+
* `authenticationNotRenewable` so that `codeForHttpStatus` reads it as the
|
|
11
|
+
* host's session ending rather than a refusal: nothing was ever minted or
|
|
12
|
+
* proved, so it says the credential expired, not that the user lacks access.
|
|
13
|
+
* `authenticationRetrySkipped` stays unset; it means a replayable retry was
|
|
14
|
+
* possible and skipped, which is never the case on this transport.
|
|
15
|
+
*
|
|
16
|
+
* No CSRF header is sent. What stands in for one is the JSON content type
|
|
17
|
+
* `exchangeBillingRequest` always sends: it makes every request non-simple,
|
|
18
|
+
* so a cross-site caller has to clear a preflight it cannot satisfy. That
|
|
19
|
+
* rests on the cookie's `SameSite` attribute and the backend's CORS
|
|
20
|
+
* allowlist, neither of which is enforced here — sending a form-encoded or
|
|
21
|
+
* otherwise CORS-simple request from this transport removes the protection.
|
|
22
|
+
* A CSRF header is added here when the backend names one.
|
|
23
|
+
*/
|
|
24
|
+
import type { BillingTransport } from './billingContracts.js';
|
|
25
|
+
import type { BillingScopeSource } from './billingScope.js';
|
|
26
|
+
export interface CredentialedBillingTransportOptions {
|
|
27
|
+
/** Resolves a route to an absolute URL, as the session transport's does. */
|
|
28
|
+
readonly resolveUrl: (route: string) => string;
|
|
29
|
+
readonly scopeSource: BillingScopeSource;
|
|
30
|
+
/** Default `'include'`, so a cookie survives a cross-origin billing host. */
|
|
31
|
+
readonly credentials?: RequestCredentials;
|
|
32
|
+
readonly fetchImpl?: typeof fetch;
|
|
33
|
+
readonly defaultTimeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function createCredentialedBillingTransport(options: CredentialedBillingTransportOptions): BillingTransport;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { sameBillingScope } from './billingScope.js';
|
|
2
|
+
import { DEFAULT_BILLING_TIMEOUT_MS, exchangeBillingRequest, startRequestBudget } from './transportExchange.js';
|
|
3
|
+
/**
|
|
4
|
+
* Any scope other than the captured one supersedes the response, undefined
|
|
5
|
+
* included. A source has no "still minting" state: undefined means the scope
|
|
6
|
+
* is gone — a sign-out or a workspace teardown — which is how
|
|
7
|
+
* `createBillingScopeTracker` reads it, and how the session transport reads a
|
|
8
|
+
* delivered sign-out. Attributing the response anyway would land it in a
|
|
9
|
+
* reader that has already dropped that scope.
|
|
10
|
+
*/
|
|
11
|
+
function movedOutOfScope(source, captured) {
|
|
12
|
+
const current = source.getScope();
|
|
13
|
+
return current === undefined || !sameBillingScope(captured, current);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A 401 reaching here means the host's credential is gone, not that access
|
|
17
|
+
* was refused: this transport never held one to re-prove with.
|
|
18
|
+
*/
|
|
19
|
+
function markSessionEnded(response) {
|
|
20
|
+
if (response.httpStatus !== 401)
|
|
21
|
+
return response;
|
|
22
|
+
return { ...response, authenticationNotRenewable: true };
|
|
23
|
+
}
|
|
24
|
+
export function createCredentialedBillingTransport(options) {
|
|
25
|
+
const { resolveUrl, scopeSource, credentials = 'include', fetchImpl = fetch, defaultTimeoutMs = DEFAULT_BILLING_TIMEOUT_MS } = options;
|
|
26
|
+
return async function transport(request) {
|
|
27
|
+
const captured = scopeSource.getScope();
|
|
28
|
+
if (captured === undefined) {
|
|
29
|
+
return { status: 'error', code: 'NOT_AUTHENTICATED' };
|
|
30
|
+
}
|
|
31
|
+
const budget = startRequestBudget(request.signal, request.timeoutMs ?? defaultTimeoutMs);
|
|
32
|
+
try {
|
|
33
|
+
const response = await exchangeBillingRequest(request, {
|
|
34
|
+
fetchImpl,
|
|
35
|
+
url: resolveUrl(request.route),
|
|
36
|
+
signal: budget.signal,
|
|
37
|
+
credentials
|
|
38
|
+
});
|
|
39
|
+
if (response.status === 'error')
|
|
40
|
+
return response;
|
|
41
|
+
if (movedOutOfScope(scopeSource, captured)) {
|
|
42
|
+
return { status: 'error', code: 'SUPERSEDED' };
|
|
43
|
+
}
|
|
44
|
+
return { status: 'ok', value: markSessionEnded(response.value) };
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return { status: 'error', code: 'REQUEST_FAILED' };
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
budget.close();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The credits read: a workspace's balance, cached per scope.
|
|
3
|
+
*
|
|
4
|
+
* Balances are reported in micros and kept that way. Converting to a display
|
|
5
|
+
* unit here would bake one currency's minor-unit assumption into the core and
|
|
6
|
+
* lose precision before the host ever sees the number; formatting belongs to
|
|
7
|
+
* the view layer, which has the locale.
|
|
8
|
+
*/
|
|
9
|
+
import { zBillingBalanceResponse } from '@comfyorg/ingest-types/zod';
|
|
10
|
+
import type { z } from 'zod';
|
|
11
|
+
import type { BillingResult, BillingTransport } from './billingContracts.js';
|
|
12
|
+
import type { BillingScope, BillingScopeSource } from './billingScope.js';
|
|
13
|
+
export declare const CREDITS_ROUTE = "/billing/balance";
|
|
14
|
+
export type BillingBalance = z.infer<typeof zBillingBalanceResponse>;
|
|
15
|
+
export type CreditsScope = BillingScope;
|
|
16
|
+
export interface CreditsSnapshot {
|
|
17
|
+
readonly balance: BillingBalance;
|
|
18
|
+
readonly scope: CreditsScope;
|
|
19
|
+
/** ms since epoch at which this read settled. */
|
|
20
|
+
readonly readAt: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CreditsReadOptions {
|
|
23
|
+
readonly signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
export interface CreditsReader {
|
|
26
|
+
/**
|
|
27
|
+
* Always requests. A balance has no server-declared lifetime and moves on
|
|
28
|
+
* every run, so there is no interval over which serving a cached one is
|
|
29
|
+
* correct; the cache exists to survive a failure, not to skip a read.
|
|
30
|
+
* Concurrent callers for one scope still share a single request.
|
|
31
|
+
*/
|
|
32
|
+
read: (options?: CreditsReadOptions) => Promise<BillingResult<CreditsSnapshot>>;
|
|
33
|
+
/** The last balance published for the current scope. */
|
|
34
|
+
getSnapshot: () => CreditsSnapshot | undefined;
|
|
35
|
+
dispose: () => void;
|
|
36
|
+
}
|
|
37
|
+
export interface CreditsReaderOptions {
|
|
38
|
+
readonly transport: BillingTransport;
|
|
39
|
+
/** Where the core learns which user, workspace, and role it runs as. */
|
|
40
|
+
readonly scopeSource: BillingScopeSource;
|
|
41
|
+
readonly now?: () => number;
|
|
42
|
+
}
|
|
43
|
+
export declare function createCreditsReader(options: CreditsReaderOptions): CreditsReader;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The credits read: a workspace's balance, cached per scope.
|
|
3
|
+
*
|
|
4
|
+
* Balances are reported in micros and kept that way. Converting to a display
|
|
5
|
+
* unit here would bake one currency's minor-unit assumption into the core and
|
|
6
|
+
* lose precision before the host ever sees the number; formatting belongs to
|
|
7
|
+
* the view layer, which has the locale.
|
|
8
|
+
*/
|
|
9
|
+
import { zBillingBalanceResponse } from '@comfyorg/ingest-types/zod';
|
|
10
|
+
import { createScopedReader } from './scopedReader.js';
|
|
11
|
+
export const CREDITS_ROUTE = '/billing/balance';
|
|
12
|
+
export function createCreditsReader(options) {
|
|
13
|
+
const { transport, scopeSource, now = Date.now } = options;
|
|
14
|
+
const reader = createScopedReader({
|
|
15
|
+
transport,
|
|
16
|
+
scopeSource,
|
|
17
|
+
route: CREDITS_ROUTE,
|
|
18
|
+
parse: (body) => zBillingBalanceResponse.safeParse(body),
|
|
19
|
+
project: ({ data }, scope) => ({
|
|
20
|
+
status: 'ok',
|
|
21
|
+
value: { balance: data, scope, readAt: now() }
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
read: reader.read,
|
|
26
|
+
getSnapshot: reader.getSnapshot,
|
|
27
|
+
dispose: reader.dispose
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place a billing HTTP status becomes a code. The transport
|
|
3
|
+
* deliberately returns an answer of any status as `ok`, so that every typed
|
|
4
|
+
* operation maps status the same way instead of each inventing its own.
|
|
5
|
+
*/
|
|
6
|
+
import type { BillingErrorCode, BillingHttpResponse } from './billingContracts.js';
|
|
7
|
+
export declare function codeForHttpStatus(response: Pick<BillingHttpResponse, 'httpStatus' | 'authenticationRetrySkipped' | 'authenticationNotRenewable'>): BillingErrorCode;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function codeForHttpStatus(response) {
|
|
2
|
+
const status = response.httpStatus;
|
|
3
|
+
// A 401 the transport re-minted and retried is a real refusal. One it had
|
|
4
|
+
// to leave alone, because the write was not replayable, says only that the
|
|
5
|
+
// token had gone stale; the caller's next attempt mints a fresh one, so it
|
|
6
|
+
// joins the transient bucket rather than reading as a permanent denial.
|
|
7
|
+
if (status === 401 && response.authenticationRetrySkipped) {
|
|
8
|
+
return 'REQUEST_FAILED';
|
|
9
|
+
}
|
|
10
|
+
// A 401 on a transport that never minted anything proves nothing about
|
|
11
|
+
// permission: the host's credential expired, and signing in again is the
|
|
12
|
+
// only recovery. ACCESS_DENIED is the one code that tells a user not to try.
|
|
13
|
+
if (status === 401 && response.authenticationNotRenewable) {
|
|
14
|
+
return 'NOT_AUTHENTICATED';
|
|
15
|
+
}
|
|
16
|
+
if (status === 401 || status === 403)
|
|
17
|
+
return 'ACCESS_DENIED';
|
|
18
|
+
if (status === 404)
|
|
19
|
+
return 'NOT_FOUND';
|
|
20
|
+
if (status === 409)
|
|
21
|
+
return 'CONFLICT';
|
|
22
|
+
// 5xx joins the transient bucket with the network failures the transport
|
|
23
|
+
// already reports there: one retryable failure for a caller, not several.
|
|
24
|
+
return 'REQUEST_FAILED';
|
|
25
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The billing half of the account layer: framework-free by construction, so
|
|
3
|
+
* the composables and components that present this state live in their own
|
|
4
|
+
* view package rather than here.
|
|
5
|
+
*
|
|
6
|
+
* This entry carries the host ports, the session-backed transport, and the
|
|
7
|
+
* read path over it. The payment commands and their state machine land on
|
|
8
|
+
* top of these same contracts.
|
|
9
|
+
*/
|
|
10
|
+
export type { BillingErrorCode, BillingFailure, BillingHttpResponse, BillingRequest, BillingResult, BillingServerCode, BillingSession, BillingTransport } from './billingContracts.js';
|
|
11
|
+
export { matchesServerCode, unwrapServerCode } from './billingContracts.js';
|
|
12
|
+
export type { SessionBillingTransportOptions } from './transport.js';
|
|
13
|
+
export { createSessionBillingTransport } from './transport.js';
|
|
14
|
+
export type { CredentialedBillingTransportOptions } from './credentialedTransport.js';
|
|
15
|
+
export { createCredentialedBillingTransport } from './credentialedTransport.js';
|
|
16
|
+
export type { BillingScope, BillingScopeSource } from './billingScope.js';
|
|
17
|
+
export { sessionBillingScopeSource } from './billingScope.js';
|
|
18
|
+
export type { BillingCapabilities, CapabilitiesReadOptions, CapabilitiesReader, CapabilitiesReaderOptions, CapabilitiesSnapshot, CapabilityRolloutDefaults, CapabilityScope } from './capabilities.js';
|
|
19
|
+
export { CAPABILITIES_ROUTE, CAPABILITY_REVISION_HEADER, createCapabilitiesReader, readCapabilityRevision } from './capabilities.js';
|
|
20
|
+
export type { CapabilityDenialReason, CapabilityDenials } from './capabilityDenials.js';
|
|
21
|
+
export { decodeCapabilityDenials } from './capabilityDenials.js';
|
|
22
|
+
export type { BillingBalance, CreditsReadOptions, CreditsReader, CreditsReaderOptions, CreditsScope, CreditsSnapshot } from './credits.js';
|
|
23
|
+
export { CREDITS_ROUTE, createCreditsReader } from './credits.js';
|
|
24
|
+
export type { BillingPlansData, PlansReadOptions, PlansReader, PlansReaderOptions, PlansScope, PlansSnapshot } from './plans.js';
|
|
25
|
+
export { PLANS_ROUTE, createPlansReader } from './plans.js';
|
|
26
|
+
export type { PaymentMethodsReadOptions, PaymentMethodsReader, PaymentMethodsReaderOptions, PaymentMethodsScope, PaymentMethodsSnapshot, SavedPaymentMethod } from './paymentMethods.js';
|
|
27
|
+
export { PAYMENT_METHODS_ROUTE, createPaymentMethodsReader } from './paymentMethods.js';
|
|
28
|
+
export type { BillingStatusData, BillingStatusReader, BillingStatusReaderOptions, BillingStatusReadOptions, BillingStatusScope, BillingStatusSnapshot } from './status.js';
|
|
29
|
+
export { BILLING_STATUS_ROUTE, createBillingStatusReader } from './status.js';
|
|
30
|
+
export type { BillingAuthenticationState, BillingDeclineReason, BillingOpStatus, BillingOperationEvent, BillingOperationIdentity, BillingOperationKind, BillingOperationPhase, BillingOperationServerPhase, BillingOperationState, BillingPresentation, BillingPresentationState, BillingRecoveryAction, EmbeddedChallenge, FailedBillingOperation, HostedBillingDestination, PendingBillingOperation } from './operationState.js';
|
|
31
|
+
export { isBlockedOnCustomerPhase, isTerminal, reduceBillingOperation, validateActionUrl } from './operationState.js';
|
|
32
|
+
export { OPERATION_POLL_BUDGET, OPERATION_POLL_TIMING, hasExhaustedPollBudget, isParkedOnCustomer, nextPollDelayMs, pollBudgetMs } from './operationPolicy.js';
|
|
33
|
+
export type { BillingOperationPointer, BillingOperationPointerStorage, OperationPointerStore } from './operationPointer.js';
|
|
34
|
+
export { OPERATION_POINTER_MAX_AGE_MS, createOperationPointerStore, operationPointerKey } from './operationPointer.js';
|
|
35
|
+
export type { PresentationRoutingInput } from './presentation.js';
|
|
36
|
+
export { selectBillingPresentation } from './presentation.js';
|
|
37
|
+
export type { BillingOperationFailureCategory, BillingOperationLifecycle, BillingOperationLifecycleOptions, BillingOperationTelemetryEvent, IssuedBillingOperation, PresentationSwitchOutcome } from './operationLifecycle.js';
|
|
38
|
+
export { createBillingOperationLifecycle, operationRoute } from './operationLifecycle.js';
|
|
39
|
+
export { BILLING_OPERATION_TELEMETRY_EVENT } from '../../telemetry.js';
|
|
40
|
+
export type { EmbeddedChallengeOutcome, EmbeddedChallengePort } from './challengeDriver.js';
|
|
41
|
+
export { driveEmbeddedChallenge } from './challengeDriver.js';
|
|
42
|
+
export { readBillingErrorCode } from './billingErrorBody.js';
|
|
43
|
+
export type { BillingCommands, BillingCommandsOptions, PaymentPortalResult, PreviewSubscribeInput, PreviewSubscribeOptions, PreviewSubscribeResult, SubscribeInput, SubscriptionCommandCode, SubscriptionCommandFailure, SubscriptionCommandOutcome, SubscriptionCommandResult, SubscriptionPreview, TerminalBillingOperation } from './subscriptionCommands.js';
|
|
44
|
+
export { CANCEL_SUBSCRIPTION_ROUTE, PAYMENT_PORTAL_ROUTE, PREVIEW_SUBSCRIBE_ROUTE, RESUBSCRIBE_ROUTE, SUBSCRIBE_ROUTE, createBillingCommands } from './subscriptionCommands.js';
|
|
45
|
+
export type { HostPaymentStep, PaymentProjection, PaymentReasonKey, PaymentStep } from './paymentProjection.js';
|
|
46
|
+
export { projectPaymentStep } from './paymentProjection.js';
|
|
47
|
+
export type { PaymentCopyKey, PaymentCopyKeys } from './paymentCopy.js';
|
|
48
|
+
export { DEFAULT_PAYMENT_COPY, createPaymentCopy, paymentCopyKeys } from './paymentCopy.js';
|
|
49
|
+
export type { CreateHostedTopupCheckoutInput, CreateTopupCheckoutInput, HostedTopupCheckout, HostedTopupCheckoutFailure, HostedTopupCheckoutResult, TopupCommand, TopupCommandOptions, TopupDeclined, TopupDenied, TopupFailure, TopupInvalidAmount, TopupInvalidReturnUrl, TopupNoPaymentMethod, TopupNotAvailable, TopupResult, TopupSucceeded, TopupUnsettled } from './topup.js';
|
|
50
|
+
export { TOPUP_CHECKOUT_ROUTE, TOPUP_ROUTE, createTopupCommand } from './topup.js';
|
|
51
|
+
export type { BalanceWatch, BalanceWatchOptions, BalanceWatchOutcome } from './balanceWatch.js';
|
|
52
|
+
export { BALANCE_WATCH_LIFETIME_MS, BALANCE_WATCH_MAX_SCHEDULED_RUNS, BALANCE_WATCH_RETRY_GAPS_MS, createBalanceWatch } from './balanceWatch.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { matchesServerCode, unwrapServerCode } from './billingContracts.js';
|
|
2
|
+
export { createSessionBillingTransport } from './transport.js';
|
|
3
|
+
export { createCredentialedBillingTransport } from './credentialedTransport.js';
|
|
4
|
+
export { sessionBillingScopeSource } from './billingScope.js';
|
|
5
|
+
export { CAPABILITIES_ROUTE, CAPABILITY_REVISION_HEADER, createCapabilitiesReader, readCapabilityRevision } from './capabilities.js';
|
|
6
|
+
export { decodeCapabilityDenials } from './capabilityDenials.js';
|
|
7
|
+
export { CREDITS_ROUTE, createCreditsReader } from './credits.js';
|
|
8
|
+
export { PLANS_ROUTE, createPlansReader } from './plans.js';
|
|
9
|
+
export { PAYMENT_METHODS_ROUTE, createPaymentMethodsReader } from './paymentMethods.js';
|
|
10
|
+
export { BILLING_STATUS_ROUTE, createBillingStatusReader } from './status.js';
|
|
11
|
+
export { isBlockedOnCustomerPhase, isTerminal, reduceBillingOperation, validateActionUrl } from './operationState.js';
|
|
12
|
+
export { OPERATION_POLL_BUDGET, OPERATION_POLL_TIMING, hasExhaustedPollBudget, isParkedOnCustomer, nextPollDelayMs, pollBudgetMs } from './operationPolicy.js';
|
|
13
|
+
export { OPERATION_POINTER_MAX_AGE_MS, createOperationPointerStore, operationPointerKey } from './operationPointer.js';
|
|
14
|
+
export { selectBillingPresentation } from './presentation.js';
|
|
15
|
+
export { createBillingOperationLifecycle, operationRoute } from './operationLifecycle.js';
|
|
16
|
+
export { BILLING_OPERATION_TELEMETRY_EVENT } from '../../telemetry.js';
|
|
17
|
+
export { driveEmbeddedChallenge } from './challengeDriver.js';
|
|
18
|
+
export { readBillingErrorCode } from './billingErrorBody.js';
|
|
19
|
+
export { CANCEL_SUBSCRIPTION_ROUTE, PAYMENT_PORTAL_ROUTE, PREVIEW_SUBSCRIBE_ROUTE, RESUBSCRIBE_ROUTE, SUBSCRIBE_ROUTE, createBillingCommands } from './subscriptionCommands.js';
|
|
20
|
+
export { projectPaymentStep } from './paymentProjection.js';
|
|
21
|
+
export { DEFAULT_PAYMENT_COPY, createPaymentCopy, paymentCopyKeys } from './paymentCopy.js';
|
|
22
|
+
export { TOPUP_CHECKOUT_ROUTE, TOPUP_ROUTE, createTopupCommand } from './topup.js';
|
|
23
|
+
export { BALANCE_WATCH_LIFETIME_MS, BALANCE_WATCH_MAX_SCHEDULED_RUNS, BALANCE_WATCH_RETRY_GAPS_MS, createBalanceWatch } from './balanceWatch.js';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BILLING_OPERATION_TELEMETRY_EVENT } from '../../telemetry.js';
|
|
2
|
+
import type { BillingResult, BillingTransport } from './billingContracts.js';
|
|
3
|
+
import type { BillingScope, BillingScopeSource } from './billingScope.js';
|
|
4
|
+
import type { BillingOperationPointerStorage } from './operationPointer.js';
|
|
5
|
+
import type { BillingDeclineReason, BillingOperationKind, BillingOperationState, BillingPresentation, HostedBillingDestination } from './operationState.js';
|
|
6
|
+
import type { BillingStatusReader } from './status.js';
|
|
7
|
+
export declare function operationRoute(operationId: string): string;
|
|
8
|
+
/** What a command learned from the backend when it issued the operation. */
|
|
9
|
+
export interface IssuedBillingOperation {
|
|
10
|
+
readonly operationId: string;
|
|
11
|
+
/** A hosted continuation the response already carried (`payment_method_url`, `action_url`). */
|
|
12
|
+
readonly actionUrl?: string;
|
|
13
|
+
/** An in-page challenge the response already carried. */
|
|
14
|
+
readonly clientSecret?: string;
|
|
15
|
+
}
|
|
16
|
+
export type BillingOperationFailureCategory = 'provider_decline' | 'api_rejected' | 'poll_timeout' | 'reconciliation_needed' | 'stale_operation';
|
|
17
|
+
export interface BillingOperationTelemetryEvent {
|
|
18
|
+
readonly name: (typeof BILLING_OPERATION_TELEMETRY_EVENT)[keyof typeof BILLING_OPERATION_TELEMETRY_EVENT];
|
|
19
|
+
readonly billing_op_id: string;
|
|
20
|
+
readonly operation_type: BillingOperationKind;
|
|
21
|
+
readonly presentation: BillingPresentation;
|
|
22
|
+
/** True when this tab reattached to an operation it did not issue. */
|
|
23
|
+
readonly resumed: boolean;
|
|
24
|
+
readonly failure_category?: BillingOperationFailureCategory;
|
|
25
|
+
readonly decline_reason?: BillingDeclineReason;
|
|
26
|
+
/** From the attempt's start, so a resumed operation reports its whole life. */
|
|
27
|
+
readonly duration_ms?: number;
|
|
28
|
+
}
|
|
29
|
+
export type PresentationSwitchOutcome = 'switched' | 'unchanged'
|
|
30
|
+
/** The server has offered no hosted continuation yet. */
|
|
31
|
+
| 'no_hosted_url'
|
|
32
|
+
/** The host cannot drive a challenge, or none has been issued for this operation. */
|
|
33
|
+
| 'embedded_unavailable' | 'not_pending' | 'unknown_operation';
|
|
34
|
+
export interface BillingOperationLifecycleOptions {
|
|
35
|
+
readonly transport: BillingTransport;
|
|
36
|
+
/** Where the core learns which user, workspace, and role it runs as. */
|
|
37
|
+
readonly scopeSource: BillingScopeSource;
|
|
38
|
+
/** The Phase 2 status reader; consulted before every start and recovery. */
|
|
39
|
+
readonly statusReader: BillingStatusReader;
|
|
40
|
+
/** Tab-local storage for the operation pointer; absent means nothing survives a reload. */
|
|
41
|
+
readonly pointerStorage?: BillingOperationPointerStorage;
|
|
42
|
+
/** Whether the host can drive an in-page challenge right now. Absent routes everything hosted. */
|
|
43
|
+
readonly embeddedCheckoutAvailable?: () => boolean;
|
|
44
|
+
/** Which origin serves a hosted page right now. Absent keeps every hosted operation on the provider page. */
|
|
45
|
+
readonly hostedDestination?: () => HostedBillingDestination;
|
|
46
|
+
readonly onTelemetry?: (event: BillingOperationTelemetryEvent) => void;
|
|
47
|
+
readonly now?: () => number;
|
|
48
|
+
}
|
|
49
|
+
export interface BillingOperationLifecycle {
|
|
50
|
+
/**
|
|
51
|
+
* Runs a command on the lifecycle: single-flight per kind, the backend's
|
|
52
|
+
* pending operation adopted instead of a second one issued, and the
|
|
53
|
+
* issued id observed from the moment it exists. Resolves once the
|
|
54
|
+
* operation is adopted, not once it settles — see `settled`.
|
|
55
|
+
*/
|
|
56
|
+
begin: (kind: BillingOperationKind, issue: (scope: BillingScope) => Promise<BillingResult<IssuedBillingOperation>>) => Promise<BillingResult<BillingOperationState>>;
|
|
57
|
+
/**
|
|
58
|
+
* Reattaches to whatever this scope is already waiting on: the backend's
|
|
59
|
+
* pending operation first, then the tab-local pointer. Resolves undefined
|
|
60
|
+
* when there is nothing to recover.
|
|
61
|
+
*/
|
|
62
|
+
recover: () => Promise<BillingResult<BillingOperationState | undefined>>;
|
|
63
|
+
/** The host became visible or focused: poll every pending operation now. */
|
|
64
|
+
wake: () => void;
|
|
65
|
+
/** Moves the operation between presentations under the same id. */
|
|
66
|
+
switchPresentation: (operationId: string, presentation: BillingPresentation) => PresentationSwitchOutcome;
|
|
67
|
+
/** The host began driving the operation's challenge; polling pauses until it settles. */
|
|
68
|
+
reportChallengeStarted: (operationId: string) => void;
|
|
69
|
+
reportChallengeSettled: (operationId: string, outcome: 'completed' | 'failed') => void;
|
|
70
|
+
get: (operationId: string) => BillingOperationState | undefined;
|
|
71
|
+
getSnapshot: () => readonly BillingOperationState[];
|
|
72
|
+
subscribe: (listener: (state: BillingOperationState) => void) => () => void;
|
|
73
|
+
/** Resolves with the terminal state; undefined for an id this lifecycle never adopted. */
|
|
74
|
+
settled: (operationId: string) => Promise<BillingOperationState> | undefined;
|
|
75
|
+
dispose: () => void;
|
|
76
|
+
}
|
|
77
|
+
export declare function createBillingOperationLifecycle(options: BillingOperationLifecycleOptions): BillingOperationLifecycle;
|