@returningai/widget-sdk 1.8.12 → 1.8.14

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.
@@ -15,6 +15,14 @@ export declare abstract class BaseWidget extends HTMLElement {
15
15
  private themeObserver?;
16
16
  private identityAttributeObserver?;
17
17
  private identitySignature;
18
+ /** The `embed-token` attribute as last read; `config.embedToken` may hold a renewed pass instead. */
19
+ private lastEmbedTokenAttribute;
20
+ /** The init generation whose widget launched; part of the silent-swap health check. */
21
+ private launchedInitId;
22
+ /** Set when the expired-pass error was shown with no `embedTokenRequest` registered. */
23
+ private lateEmbedTokenRegistrationReload;
24
+ /** An error screen is showing for the current init (any cause); clears on the next launch. */
25
+ private errorScreenShown;
18
26
  private hasAttemptedInit;
19
27
  private initGeneration;
20
28
  private lottieInstances;
@@ -35,6 +43,15 @@ export declare abstract class BaseWidget extends HTMLElement {
35
43
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
36
44
  private setupIdentityAttributeObserver;
37
45
  private handleIdentityConfigChange;
46
+ private readEmbedTokenProvider;
47
+ /**
48
+ * Renewal options for auth calls started by init `initId`; the provider is
49
+ * read at call time. A disconnect or a newer init stops the retry. Writes
50
+ * from this call land only in the state object init `initId` owns.
51
+ */
52
+ private embedTokenRenewal;
53
+ /** Authenticated, no error state or error screen, and launched for the current init. */
54
+ private isHealthyForEmbedTokenSwap;
38
55
  private emit;
39
56
  /**
40
57
  * Reads host callback registrations at call time so framework integrations
@@ -59,6 +76,7 @@ export declare abstract class BaseWidget extends HTMLElement {
59
76
  private applyPalette;
60
77
  private applyLoadingSettings;
61
78
  private renderShell;
79
+ private showLoader;
62
80
  /**
63
81
  * Loads the community loading animation + theme palette from cache or API
64
82
  * and renders the configured animation above the loading text.
@@ -105,6 +123,15 @@ export declare abstract class BaseWidget extends HTMLElement {
105
123
  * sub-type + community. Best-effort: never throws into the host page.
106
124
  */
107
125
  private mountBannerForWidget;
126
+ /**
127
+ * Starts a new init: it owns a fresh state object and its own auth-session
128
+ * subscription. The previous init's in-flight work keeps the retired state,
129
+ * so it can finish but never writes this init's state or storage. It still
130
+ * publishes to the session it started on; after a same-member reload that is
131
+ * the same shared session this init joined, which is intended (siblings and
132
+ * this init get the result).
133
+ */
134
+ private beginInit;
108
135
  private init;
109
136
  private isCurrentInit;
110
137
  private teardownMountedWidget;
@@ -1,9 +1,24 @@
1
1
  import type { WidgetConfig, WidgetState } from '../types';
2
+ import { type EmbedTokenRenewalOptions } from './embed-token-renewal';
3
+ export type { EmbedTokenRenewalOptions } from './embed-token-renewal';
2
4
  export declare function calculateRefreshDelay(accessTokenExpiry: number, now?: number): number;
3
- export declare function authenticateServerless(config: WidgetConfig, state: WidgetState, onRefreshScheduled?: () => void): Promise<boolean>;
5
+ /**
6
+ * Signs in with the embed token or Public Embed identifiers.
7
+ *
8
+ * `renewal` is required so every call site declares whether the host page can
9
+ * supply a fresh embed token (`embedTokenRequest`). With a provider:
10
+ * - a pass past `exp` (or within 30 s of it) is renewed before signing in;
11
+ * - after the backend's expired-pass 401, the pass is renewed once and a new
12
+ * sign-in attempt (new nonce and timestamp) is made, if the widget is still
13
+ * current.
14
+ * The provider is consulted at most once per call. With a null provider this
15
+ * behaves as before, except that `lastAuthError` always describes the latest
16
+ * attempt.
17
+ */
18
+ export declare function authenticateServerless(config: WidgetConfig, state: WidgetState, renewal: EmbedTokenRenewalOptions, onRefreshScheduled?: () => void): Promise<boolean>;
4
19
  export declare function authenticateViaProxy(config: WidgetConfig, state: WidgetState, onRefreshScheduled?: () => void): Promise<boolean>;
5
- export declare function refreshAccessToken(config: WidgetConfig, state: WidgetState, onRefreshScheduled?: () => void, sendToken?: () => void): Promise<boolean>;
6
- export declare function getValidToken(config: WidgetConfig, state: WidgetState, onRefreshScheduled?: () => void): Promise<string>;
20
+ export declare function refreshAccessToken(config: WidgetConfig, state: WidgetState, renewal: EmbedTokenRenewalOptions, onRefreshScheduled?: () => void, sendToken?: () => void): Promise<boolean>;
21
+ export declare function getValidToken(config: WidgetConfig, state: WidgetState, renewal: EmbedTokenRenewalOptions, onRefreshScheduled?: () => void): Promise<string>;
7
22
  export declare function logout(config: WidgetConfig, state: WidgetState): Promise<void>;
8
23
  export declare function applyErrorSettingsToState(state: WidgetState, data: Record<string, unknown>): void;
9
24
  /**
@@ -0,0 +1,116 @@
1
+ import type { ReturningAIEmbedTokenRequestCallback, ReturningAIEmbedTokenRequestReason, ReturningAIWidgetCallbacks, WidgetConfig } from '../types';
2
+ /**
3
+ * Host-page embed-token renewal (`embedTokenRequest`).
4
+ *
5
+ * Pure logic with no DOM: decoding pass claims, the acceptance rule for a
6
+ * renewed pass, the page-wide de-duplicated renewal registry, and the
7
+ * identity-change decision used when a host sets a new `embed-token`.
8
+ * The SDK only decodes passes; their signatures are verified by the server.
9
+ */
10
+ export declare const EMBED_TOKEN_REJECTED_ERROR = "Invalid or expired embed token";
11
+ /** A pass within this many ms of `exp` is treated as expired before sign-in. */
12
+ export declare const EMBED_TOKEN_EXPIRY_WINDOW_MS = 30000;
13
+ /** The host gets this long to answer before the widget keeps today's error. */
14
+ export declare const EMBED_TOKEN_REQUEST_TIMEOUT_MS = 10000;
15
+ /** After a failed or rejected answer, the host is not asked again for this long. */
16
+ export declare const EMBED_TOKEN_REQUEST_COOLDOWN_MS = 30000;
17
+ /** An accepted pass is reused until this long before its `exp`. */
18
+ export declare const EMBED_TOKEN_CACHE_MARGIN_MS = 60000;
19
+ export type EmbedTokenProvider = ReturningAIEmbedTokenRequestCallback;
20
+ /**
21
+ * Required by every sign-in and renewal entry point in `auth.ts`, so a call
22
+ * site that forgets to declare its provider fails `tsc`. Pass
23
+ * `{ getEmbedTokenProvider: null }` where the host cannot supply one.
24
+ */
25
+ export interface EmbedTokenRenewalOptions {
26
+ getEmbedTokenProvider: (() => EmbedTokenProvider | null) | null;
27
+ /** False once the widget that started the sign-in was disconnected or re-initialised. Stops the retry. */
28
+ isCurrent?: () => boolean;
29
+ /** Set by the refresh-failure path; defaults to `expired`. */
30
+ reason?: ReturningAIEmbedTokenRequestReason;
31
+ }
32
+ export interface EmbedTokenClaims {
33
+ accessId?: string;
34
+ communityId?: string;
35
+ userIdentifiers?: Record<string, string>;
36
+ exp?: number;
37
+ }
38
+ export interface EmbedTokenRenewalClock {
39
+ now: () => number;
40
+ setTimeout: (callback: () => void, ms: number) => unknown;
41
+ clearTimeout: (handle: unknown) => void;
42
+ }
43
+ /** A pass's claims, decoded without verification (the server verifies). Null when undecodable. */
44
+ export declare function decodeEmbedTokenClaims(embedToken?: string | null): EmbedTokenClaims | null;
45
+ /**
46
+ * The member a pass is for: `accessId`, `communityId` and sorted
47
+ * `userIdentifiers`. Falls back to the raw string for an undecodable pass or a
48
+ * legacy pass without identifiers; an absent pass is `""`.
49
+ */
50
+ export declare function embedIdentityKey(embedToken?: string | null): string;
51
+ /**
52
+ * The swap signature: the pass's identity key plus the `data-*` identifiers.
53
+ * A same-member pass keeps the signature; a different member, access key or
54
+ * `data-*` identifier changes it.
55
+ */
56
+ export declare function identitySignatureForConfig(config: Pick<WidgetConfig, 'embedToken' | 'userIdentifiers'>): string;
57
+ /** True only for the backend's expired-pass refusal, the one refusal a new pass can fix. */
58
+ export declare function isEmbedTokenRejected(error: {
59
+ status?: number;
60
+ error?: string;
61
+ } | null | undefined): boolean;
62
+ /** True when the pass's `exp` is past or within the pre-check window. */
63
+ export declare function isEmbedTokenExpiring(embedToken: string | null | undefined, now?: number): boolean;
64
+ /** Reads the host's registered provider, ignoring a non-function registration. */
65
+ export declare function readEmbedTokenProvider(callbacks: ReturningAIWidgetCallbacks | null | undefined): EmbedTokenProvider | null;
66
+ /**
67
+ * Accepts the host's answer only when it is a fresh pass for the same member:
68
+ * a string other than the stale pass, `exp` more than 30 s ahead, the same
69
+ * `communityId`, and the same `userIdentifiers` (or none, when the stale pass
70
+ * has none). `accessId` may differ, so an access-key rotation can recover.
71
+ */
72
+ export declare function acceptRenewedEmbedToken(staleToken: string, result: unknown, now?: number): string | null;
73
+ /**
74
+ * The server still refused a renewed pass the SDK had accepted (for example a
75
+ * host minting from the wrong environment). Forget it and do not ask the host
76
+ * again for that member for 30 s, as for a rejected answer.
77
+ */
78
+ export declare function markRenewedEmbedTokenRefused(staleToken: string, refusedToken: string, clock?: EmbedTokenRenewalClock): void;
79
+ /** Test-only: forget every in-flight request, cached pass and cooldown on the page. */
80
+ export declare function resetEmbedTokenRenewalForTests(): void;
81
+ export interface RenewEmbedTokenRequest {
82
+ staleToken: string;
83
+ reason: ReturningAIEmbedTokenRequestReason;
84
+ provider: EmbedTokenProvider;
85
+ clock?: EmbedTokenRenewalClock;
86
+ }
87
+ /**
88
+ * Asks the host page for a fresh pass for the stale pass's member.
89
+ *
90
+ * Every widget on the page shares one in-flight request per member, so seven
91
+ * widgets with the same stale pass make one host call. Resolves to the
92
+ * accepted pass, or null when the host throws, answers with anything the
93
+ * acceptance rule refuses, or does not answer within 10 s; a null result
94
+ * leaves the caller's existing error in place.
95
+ */
96
+ export declare function renewEmbedToken(request: RenewEmbedTokenRequest): Promise<string | null>;
97
+ export type EmbedTokenChangeDecision = 'update' | 'swap' | 'reload';
98
+ export interface EmbedTokenChangeInput {
99
+ /** The `embed-token` attribute differs from the last attribute value read (not from `config.embedToken`). */
100
+ tokenChanged: boolean;
101
+ /** The swap signature is unchanged. */
102
+ sameSignature: boolean;
103
+ hasAttemptedInit: boolean;
104
+ /** Authenticated, no `lastAuthError`, no error screen shown, and launched for the current init. */
105
+ healthy: boolean;
106
+ /** `embedTokenRequest` is registered; the silent swap is opt-in. */
107
+ hasProvider: boolean;
108
+ }
109
+ /**
110
+ * What an identity attribute change does to a widget:
111
+ * - `update`: not started yet, or nothing identity-relevant changed. Keep going.
112
+ * - `swap`: a same-member pass on a healthy widget whose host registered
113
+ * `embedTokenRequest`. Replace the pass in place, no reload.
114
+ * - `reload`: everything else once started, as today.
115
+ */
116
+ export declare function decideEmbedTokenChange(input: EmbedTokenChangeInput): EmbedTokenChangeDecision;
@@ -35,11 +35,15 @@ export interface DispatchHostCallbackInput {
35
35
  transport?: HostCallbackTransport;
36
36
  observer?: HostCallbackObserver;
37
37
  }
38
+ /** Decode an embed token's claims without verifying it; the server verifies. */
39
+ export declare function readEmbedTokenClaims(embedToken?: string): Record<string, unknown> | null;
38
40
  /** Mirrors auth identity-source precedence; token authenticity is enforced by widget authentication. */
39
41
  export declare function resolveHostUserIdentifiers(config: Pick<WidgetConfig, 'embedToken' | 'userIdentifiers'>): Record<string, string>;
40
42
  /** Adds host-owned identity values without making widget code depend on auth internals. */
41
43
  export declare function enrichHostCallbackPayload(payload: unknown, userIdentifiers?: Record<string, string>): unknown;
42
44
  export declare function getHostCallbackDefinition(callbackName: string, payload: unknown): HostCallbackDefinition | undefined;
45
+ /** Read registered capabilities without invoking customer code. */
46
+ export declare function getSupportedHostCallbacks(callbacks: ReturningAIWidgetCallbacks, hostWidgetType: ReturningAIWidgetType): string[];
43
47
  export declare function isHostCallbackMessageSupported(definition: HostCallbackDefinition, action: string, sourceWidgetType: string, hostWidgetType: ReturningAIWidgetType): boolean;
44
48
  /**
45
49
  * Runs every supported host callback through one policy-driven boundary.
@@ -1,7 +1,9 @@
1
1
  import type { WidgetConfig, WidgetState } from '../types';
2
+ /** A state a newer init replaced: its in-flight work may finish, but never reaches storage. */
3
+ export declare function retireWidgetState(state: WidgetState): void;
2
4
  export declare function saveToStorage(config: WidgetConfig, state: WidgetState): void;
3
5
  export declare function loadFromStorage(config: WidgetConfig, state: WidgetState): boolean;
4
- export declare function clearStorage(config: WidgetConfig): void;
6
+ export declare function clearStorage(config: WidgetConfig, state?: WidgetState): void;
5
7
  export declare function loadErrorSettingsFromStorage(config: WidgetConfig): Record<string, unknown> | null;
6
8
  export declare function saveErrorSettingsToStorage(config: WidgetConfig, settings: Record<string, unknown>): void;
7
9
  declare function isTokenExpired(expiry: number | null): boolean;
@@ -18,6 +18,6 @@ declare global {
18
18
  }
19
19
  }
20
20
  export { StoreWidget, ChannelWidget, MilestoneWidget, SocialWidget, CurrencyWidget, ReferralWidget, CustomWidget };
21
- export type { MilestoneCtaClickPayload, ReturningAIGenericHostCallbackActionMessage, ReturningAIMilestoneCtaClickCallback, ReturningAIStoreCallbackFieldOptionsCallback, ReturningAIStoreHostCallbackActionMessage, ReturningAIStorePurchaseSuccessCallback, ReturningAIWidgetCallback, ReturningAIWidgetCallbackMap, ReturningAIWidgetCallbackRegistration, ReturningAIWidgetActionMessage, ReturningAIWidgetActionResultMessage, ReturningAIWidgetCallbacks, ReturningAIWidgetHostCallbackActionMessage, ReturningAIWidgetType, StoreCallbackFieldOptionsContext, StoreCallbackFieldOptionsField, StoreCallbackFieldOptionsFieldType, StoreCallbackFieldOptionsPayload, StoreCallbackFieldOptionsResult, StoreHostCallbackPayload, StorePurchaseSuccessPayload, WidgetHostCallbackPayload, } from './types';
21
+ export type { EmbedTokenRequestPayload, EmbedTokenRequestResult, MilestoneCtaClickPayload, ReturningAIEmbedTokenRequestCallback, ReturningAIEmbedTokenRequestReason, ReturningAIGenericHostCallbackActionMessage, ReturningAIMilestoneCtaClickCallback, ReturningAIStoreCallbackFieldOptionsCallback, ReturningAIStoreHostCallbackActionMessage, ReturningAIStorePurchaseSuccessCallback, ReturningAIWidgetCallback, ReturningAIWidgetCallbackMap, ReturningAIWidgetCallbackRegistration, ReturningAIWidgetActionMessage, ReturningAIWidgetActionResultMessage, ReturningAIWidgetCallbacks, ReturningAIWidgetHostCallbackActionMessage, ReturningAIWidgetType, StoreCallbackFieldOptionsContext, StoreCallbackFieldOptionsField, StoreCallbackFieldOptionsFieldType, StoreCallbackFieldOptionsPayload, StoreCallbackFieldOptionsResult, StoreHostCallbackPayload, StorePurchaseSuccessPayload, WidgetHostCallbackPayload, } from './types';
22
22
  export { MILESTONE_CTA_CLICK_CALLBACK_NAME, MILESTONE_CTA_CLICK_EVENT, MILESTONE_CTA_CLICK_SCOPE, STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME, STORE_CALLBACK_FIELD_OPTIONS_SCOPE, STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS, STORE_PURCHASE_SUCCESS_CALLBACK_NAME, STORE_PURCHASE_SUCCESS_SCOPE, } from './types';
23
23
  export type {} from './jsx';
@@ -26,6 +26,13 @@ export interface StoreCallbackFieldOptionsPayload {
26
26
  export interface StoreCallbackFieldOptionsResult {
27
27
  fieldOptions: Record<string, string[]>;
28
28
  signature?: string;
29
+ /** Display-only disabled accounts by field ID; excluded from the signed result. */
30
+ ineligibleOptions?: Record<string, string[]>;
31
+ /** Plain-text hints keyed by field ID and canonical account value.
32
+ * Display-only: the frontend ignores invalid/blank entries and limits text to
33
+ * 200 Unicode code points, appending an ellipsis when truncated.
34
+ */
35
+ optionDescriptions?: Record<string, Record<string, string>>;
29
36
  }
30
37
  export interface StorePurchaseSuccessPayload {
31
38
  scope: typeof STORE_PURCHASE_SUCCESS_SCOPE;
@@ -88,10 +95,33 @@ export type ReturningAIWidgetCallback<TPayload = any, TResult = any> = (payload:
88
95
  export type ReturningAIStoreCallbackFieldOptionsCallback = (payload: StoreCallbackFieldOptionsPayload) => Promise<StoreCallbackFieldOptionsResult> | StoreCallbackFieldOptionsResult;
89
96
  export type ReturningAIStorePurchaseSuccessCallback = (payload: StorePurchaseSuccessPayload) => Promise<unknown> | unknown;
90
97
  export type ReturningAIMilestoneCtaClickCallback = (payload: MilestoneCtaClickPayload) => Promise<unknown> | unknown;
98
+ /**
99
+ * Why the SDK asks the host page for a new embed token:
100
+ * - `session-expired`: renewing a running session failed and signing in again
101
+ * needs a fresh pass.
102
+ * - `expired`: the pass is past its expiry, or the server refused it as
103
+ * expired, when signing in outside session renewal (for example at mount).
104
+ */
105
+ export type ReturningAIEmbedTokenRequestReason = 'expired' | 'session-expired';
106
+ export interface EmbedTokenRequestPayload {
107
+ reason: ReturningAIEmbedTokenRequestReason;
108
+ }
109
+ export interface EmbedTokenRequestResult {
110
+ /** A freshly minted embed token for the same member and community. */
111
+ embedToken: string;
112
+ }
113
+ /**
114
+ * SDK-invoked host callback: return a fresh embed token for the widget's
115
+ * member. Widgets for the same member share one call. Answers that are not a
116
+ * fresh same-member pass, throws, or no answer within 10 seconds leave the
117
+ * widget's existing error in place. Never advertised to widget content.
118
+ */
119
+ export type ReturningAIEmbedTokenRequestCallback = (payload: EmbedTokenRequestPayload) => Promise<EmbedTokenRequestResult> | EmbedTokenRequestResult;
91
120
  export interface ReturningAIWidgetCallbackMap {
92
121
  callbackFieldOptions: ReturningAIStoreCallbackFieldOptionsCallback;
93
122
  storePurchaseSuccess: ReturningAIStorePurchaseSuccessCallback;
94
123
  milestoneCtaClick: ReturningAIMilestoneCtaClickCallback;
124
+ embedTokenRequest: ReturningAIEmbedTokenRequestCallback;
95
125
  }
96
126
  export type ReturningAIWidgetCallbacks = Partial<ReturningAIWidgetCallbackMap> & {
97
127
  [callbackName: string]: ReturningAIWidgetCallback | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.8.12",
3
+ "version": "1.8.14",
4
4
  "description": "Shadow DOM isolated widget SDK for ReturningAI",
5
5
  "main": "dist/rai-widget.iife.js",
6
6
  "module": "dist/rai-widget.js",
@@ -42,7 +42,7 @@
42
42
  "build:all": "npm run build && npm run build:banner",
43
43
  "build:all:min": "npm run build:min && npm run build:banner:min",
44
44
  "types": "tsc --emitDeclarationOnly --outDir dist/types && cp src/jsx.d.ts src/react18.d.ts src/react19.d.ts dist/types/",
45
- "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:bundle-runtime-cache && npm run test:bundle-token-sync && npm run test:auth-bootstrap && npm run test:auth-error-reporting && npm run test:auth-refresh-state && npm run test:auth-renewal-recovery && npm run test:auth-session-coordinator && npm run test:startup-performance && npm run test:widget-error-presentation && npm run test:fonts && npm run test:jsx-types",
45
+ "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:bundle-runtime-cache && npm run test:bundle-token-sync && npm run test:auth-bootstrap && npm run test:auth-error-reporting && npm run test:auth-refresh-state && npm run test:auth-renewal-recovery && npm run test:auth-session-coordinator && npm run test:startup-performance && npm run test:widget-error-presentation && npm run test:embed-token-renewal && npm run test:auth-embed-token-request && npm run test:fonts && npm run test:jsx-types && npm run test:loader-lifecycle && npm run test:auth-renewal-budget && npm run test:state-ownership",
46
46
  "test:host-callback": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/postmessage-host-callback.test.js",
47
47
  "test:bundle-readiness": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-readiness.test.js",
48
48
  "test:bundle-runtime-cache": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-runtime-cache.test.js",
@@ -54,12 +54,17 @@
54
54
  "test:auth-session-coordinator": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/auth-session-coordinator.test.js",
55
55
  "test:startup-performance": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/startup-performance.test.js",
56
56
  "test:widget-error-presentation": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/widget-error-presentation.test.js",
57
+ "test:auth-embed-token-request": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/auth-embed-token-request.test.js",
58
+ "test:embed-token-renewal": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/embed-token-renewal.test.js",
57
59
  "test:fonts": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/fonts.test.js",
58
60
  "test:jsx-types": "npm run types && node --test test/jsx/declaration-shape.test.mjs test/jsx/packed-consumer.test.mjs && tsc -p test/jsx/tsconfig.react.json && tsc -p test/jsx/tsconfig.vue.json",
59
61
  "coverage:repowise": "rm -rf coverage && npm run coverage:host-callback && npm run coverage:bundle-readiness && npm run test:jsx-types",
60
62
  "coverage:host-callback": "rm -rf test/.tmp && tsc -p test/tsconfig.json && c8 --reporter=lcov --reports-dir=coverage/host-callback --exclude-after-remap --exclude='**/*.test.ts' node test/.tmp/test/postmessage-host-callback.test.js && node scripts/name-lcov-test.mjs coverage/host-callback/lcov.info test/postmessage-host-callback.test.ts",
61
63
  "coverage:bundle-readiness": "rm -rf test/.tmp && tsc -p test/tsconfig.json && c8 --reporter=lcov --reports-dir=coverage/bundle-readiness --exclude-after-remap --exclude='**/*.test.ts' node test/.tmp/test/bundle-readiness.test.js && node scripts/name-lcov-test.mjs coverage/bundle-readiness/lcov.info test/bundle-readiness.test.ts",
62
- "prepublishOnly": "npm run build:all:min && npm run types"
64
+ "prepublishOnly": "npm run build:all:min && npm run types",
65
+ "test:loader-lifecycle": "node --test test/loader-lifecycle.test.mjs",
66
+ "test:state-ownership": "node --test test/state-ownership.test.mjs",
67
+ "test:auth-renewal-budget": "node --test test/auth-renewal-budget.test.mjs"
63
68
  },
64
69
  "devDependencies": {
65
70
  "@types/react": "^19.1.10",