spaps-sdk 1.13.1 → 1.13.2
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/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/dist/index.d.mts +82 -23
- package/dist/index.d.ts +82 -23
- package/dist/index.js +158 -38
- package/dist/index.mjs +158 -38
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,10 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
- No changes yet.
|
|
10
|
+
|
|
11
|
+
## [1.13.1] - 2026-06-20
|
|
12
|
+
|
|
9
13
|
### Added
|
|
10
14
|
|
|
11
15
|
- Added `whitelist`, `users`, and `supportTelemetry` namespaces so the TypeScript SDK matches consumer-facing Python client domain coverage.
|
package/README.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -186,6 +186,28 @@ interface WebSocketAuthHelperConfig {
|
|
|
186
186
|
* Should perform the refresh and return the new access token.
|
|
187
187
|
*/
|
|
188
188
|
refreshAccessToken: () => Promise<string>;
|
|
189
|
+
/**
|
|
190
|
+
* Optional: mint a short-lived, single-use WebSocket connect ticket.
|
|
191
|
+
*
|
|
192
|
+
* When provided, the helper opens the socket with `?ticket=<ticket>` instead
|
|
193
|
+
* of placing any long-lived credential in the URL. This is the preferred,
|
|
194
|
+
* server-aligned path: it matches SPAPS `POST /realtime/ws-ticket`, whose
|
|
195
|
+
* tickets are single-use and expire in seconds, so a leak via logs / proxies
|
|
196
|
+
* / referrers is inert. A fresh ticket is minted for every connection attempt
|
|
197
|
+
* (including reconnects), because each ticket is consumed on use.
|
|
198
|
+
*
|
|
199
|
+
* When omitted, the helper falls back to carrying the bearer token in the
|
|
200
|
+
* WebSocket subprotocol (the `Sec-WebSocket-Protocol` handshake header) rather
|
|
201
|
+
* than the URL query string — so the long-lived JWT is never logged. The
|
|
202
|
+
* server must read the subprotocol for the fallback to authenticate.
|
|
203
|
+
*/
|
|
204
|
+
getConnectTicket?: () => Promise<string> | string;
|
|
205
|
+
/**
|
|
206
|
+
* Milliseconds to wait for the socket to open before rejecting `connect()`.
|
|
207
|
+
* Prevents `connect()` from hanging forever when the socket never opens.
|
|
208
|
+
* 0 disables the timeout (default 10000).
|
|
209
|
+
*/
|
|
210
|
+
connectTimeoutMs?: number;
|
|
189
211
|
/** Seconds before token expiry to proactively refresh (default 30). */
|
|
190
212
|
refreshBufferSeconds?: number;
|
|
191
213
|
/** Maximum number of reconnection attempts on auth failures (default 3). */
|
|
@@ -218,7 +240,11 @@ declare class WebSocketAuthHelper {
|
|
|
218
240
|
constructor(config: WebSocketAuthHelperConfig);
|
|
219
241
|
/**
|
|
220
242
|
* Open an authenticated WebSocket connection.
|
|
221
|
-
*
|
|
243
|
+
*
|
|
244
|
+
* The long-lived access token is never placed in the URL. Authentication uses
|
|
245
|
+
* a short-lived single-use connect ticket (`?ticket=...`) when
|
|
246
|
+
* `getConnectTicket` is configured, otherwise the bearer token is carried in
|
|
247
|
+
* the WebSocket subprotocol (handshake header).
|
|
222
248
|
*/
|
|
223
249
|
connect(): Promise<void>;
|
|
224
250
|
/**
|
|
@@ -230,8 +256,23 @@ declare class WebSocketAuthHelper {
|
|
|
230
256
|
* Throws if the connection is not open.
|
|
231
257
|
*/
|
|
232
258
|
send(data: string): void;
|
|
233
|
-
/**
|
|
234
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Build the WebSocket URL.
|
|
261
|
+
*
|
|
262
|
+
* Never embeds the long-lived access token. When a short-lived connect
|
|
263
|
+
* `ticket` is supplied it is appended as `?ticket=<ticket>` (single-use,
|
|
264
|
+
* seconds-long TTL — inert if leaked). Otherwise the bare base URL is
|
|
265
|
+
* returned and the credential travels in the subprotocol instead.
|
|
266
|
+
*/
|
|
267
|
+
buildAuthUrl(ticket?: string): string;
|
|
268
|
+
/**
|
|
269
|
+
* Build the WebSocket subprotocols carrying the bearer credential.
|
|
270
|
+
*
|
|
271
|
+
* Used only by the no-ticket fallback so the long-lived JWT travels in the
|
|
272
|
+
* `Sec-WebSocket-Protocol` handshake header rather than the URL query string.
|
|
273
|
+
* Throws when no access token is available.
|
|
274
|
+
*/
|
|
275
|
+
buildAuthProtocols(): string[];
|
|
235
276
|
private openConnection;
|
|
236
277
|
private handleAuthClose;
|
|
237
278
|
private handleNetworkClose;
|
|
@@ -301,6 +342,7 @@ interface EmailSendOptions {
|
|
|
301
342
|
to: string;
|
|
302
343
|
context: Record<string, string | number | boolean>;
|
|
303
344
|
userId?: string;
|
|
345
|
+
idempotencyKey?: string;
|
|
304
346
|
}
|
|
305
347
|
interface EmailSendResult {
|
|
306
348
|
success: boolean;
|
|
@@ -678,17 +720,22 @@ interface ListMembershipsParams {
|
|
|
678
720
|
cursor?: string;
|
|
679
721
|
}
|
|
680
722
|
interface SupportActor {
|
|
681
|
-
type:
|
|
723
|
+
type: SupportActorType;
|
|
682
724
|
id?: string | null;
|
|
683
725
|
}
|
|
726
|
+
type SupportActorType = 'user' | 'practitioner' | 'anonymous' | 'service' | 'support_operator';
|
|
727
|
+
type SupportCaseStatus = 'open' | 'in_progress' | 'resolved' | 'ignored';
|
|
728
|
+
type SupportCasePriority = 'low' | 'normal' | 'high' | 'urgent';
|
|
729
|
+
type SupportEventSeverity = 'info' | 'warning' | 'error' | 'critical';
|
|
730
|
+
type SupportSourceChannel = 'issue_log' | 'support_thread' | 'email' | 'frontend' | 'api' | 'webhook';
|
|
684
731
|
interface SupportCaseSummary {
|
|
685
732
|
id: string;
|
|
686
733
|
application_id: string;
|
|
687
734
|
external_case_ref?: string | null;
|
|
688
735
|
title?: string | null;
|
|
689
|
-
status:
|
|
690
|
-
priority:
|
|
691
|
-
highest_severity:
|
|
736
|
+
status: SupportCaseStatus;
|
|
737
|
+
priority: SupportCasePriority;
|
|
738
|
+
highest_severity: SupportEventSeverity;
|
|
692
739
|
assigned_to_user_id?: string | null;
|
|
693
740
|
first_event_at: string;
|
|
694
741
|
last_event_at: string;
|
|
@@ -700,9 +747,9 @@ interface SupportCaseEvent {
|
|
|
700
747
|
id: string;
|
|
701
748
|
case_id: string;
|
|
702
749
|
event_key: string;
|
|
703
|
-
source_channel:
|
|
750
|
+
source_channel: SupportSourceChannel;
|
|
704
751
|
event_type: string;
|
|
705
|
-
severity:
|
|
752
|
+
severity: SupportEventSeverity;
|
|
706
753
|
actor: SupportActor;
|
|
707
754
|
correlation_id?: string | null;
|
|
708
755
|
occurred_at: string;
|
|
@@ -712,9 +759,9 @@ interface SupportCaseEvent {
|
|
|
712
759
|
interface SupportTelemetryIngestRequest {
|
|
713
760
|
event_key: string;
|
|
714
761
|
occurred_at: string;
|
|
715
|
-
source_channel:
|
|
762
|
+
source_channel: SupportSourceChannel;
|
|
716
763
|
event_type: string;
|
|
717
|
-
severity:
|
|
764
|
+
severity: SupportEventSeverity;
|
|
718
765
|
actor: SupportActor;
|
|
719
766
|
payload: Record<string, unknown>;
|
|
720
767
|
application_id?: string | null;
|
|
@@ -727,13 +774,13 @@ interface SupportTelemetryIngestResponse {
|
|
|
727
774
|
case_id: string;
|
|
728
775
|
created_case: boolean;
|
|
729
776
|
deduplicated: boolean;
|
|
730
|
-
case_status:
|
|
777
|
+
case_status: SupportCaseStatus;
|
|
731
778
|
}
|
|
732
779
|
interface SupportTelemetryListCasesParams {
|
|
733
780
|
application_id?: string;
|
|
734
|
-
status?:
|
|
735
|
-
priority?:
|
|
736
|
-
severity_gte?:
|
|
781
|
+
status?: SupportCaseStatus;
|
|
782
|
+
priority?: SupportCasePriority;
|
|
783
|
+
severity_gte?: SupportEventSeverity;
|
|
737
784
|
assigned_to_user_id?: string;
|
|
738
785
|
limit?: number;
|
|
739
786
|
offset?: number;
|
|
@@ -748,16 +795,16 @@ interface SupportTelemetryCaseDetailResponse {
|
|
|
748
795
|
event_total: number;
|
|
749
796
|
}
|
|
750
797
|
interface SupportTelemetryPatchCaseRequest {
|
|
751
|
-
status?:
|
|
752
|
-
priority?:
|
|
798
|
+
status?: SupportCaseStatus;
|
|
799
|
+
priority?: SupportCasePriority;
|
|
753
800
|
assigned_to_user_id?: string | null;
|
|
754
801
|
resolution_note?: string | null;
|
|
755
802
|
}
|
|
756
803
|
interface SupportTelemetryPatchCaseResponse {
|
|
757
804
|
case: {
|
|
758
805
|
id: string;
|
|
759
|
-
status:
|
|
760
|
-
priority:
|
|
806
|
+
status: SupportCaseStatus;
|
|
807
|
+
priority: SupportCasePriority;
|
|
761
808
|
assigned_to_user_id?: string | null;
|
|
762
809
|
resolved_at?: string | null;
|
|
763
810
|
updated_at: string;
|
|
@@ -768,8 +815,8 @@ interface SupportTelemetryCaseByExternalResponse {
|
|
|
768
815
|
id: string;
|
|
769
816
|
application_id: string;
|
|
770
817
|
external_case_ref?: string | null;
|
|
771
|
-
status:
|
|
772
|
-
priority:
|
|
818
|
+
status: SupportCaseStatus;
|
|
819
|
+
priority: SupportCasePriority;
|
|
773
820
|
assigned_to_user_id?: string | null;
|
|
774
821
|
resolved_at?: string | null;
|
|
775
822
|
updated_at: string;
|
|
@@ -1390,9 +1437,21 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
1390
1437
|
private normalizeAuthPayload;
|
|
1391
1438
|
private storeAuthTokens;
|
|
1392
1439
|
private unwrapAuthMethodResponse;
|
|
1440
|
+
/**
|
|
1441
|
+
* Unwrap the SPAPS `{ success, data }` response envelope for the legacy
|
|
1442
|
+
* token-issuing auth methods (login/register/walletSignIn/refresh).
|
|
1443
|
+
*
|
|
1444
|
+
* The server's ResponseEnvelopeMiddleware wraps these responses, so the real
|
|
1445
|
+
* tokens live at `response.data.data`. Reading `response.data.access_token`
|
|
1446
|
+
* (one level too shallow) silently dropped the tokens. Reuse the shared
|
|
1447
|
+
* `unwrapApiResponse` primitive so envelope handling and error fidelity stay
|
|
1448
|
+
* consistent with the rest of the client.
|
|
1449
|
+
*/
|
|
1450
|
+
private extractAuthResponse;
|
|
1393
1451
|
private static isSdkManagedHeader;
|
|
1394
1452
|
private static hasHeader;
|
|
1395
1453
|
private static setHeader;
|
|
1454
|
+
private static deleteHeader;
|
|
1396
1455
|
private static assertSafeHeaderValue;
|
|
1397
1456
|
private applyCustomHeaders;
|
|
1398
1457
|
admin: {
|
|
@@ -1745,7 +1804,7 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
1745
1804
|
register: (payload: {
|
|
1746
1805
|
email: string;
|
|
1747
1806
|
password: string;
|
|
1748
|
-
}) => Promise<
|
|
1807
|
+
}) => Promise<AuthMethodResponse>;
|
|
1749
1808
|
/**
|
|
1750
1809
|
* Verify a magic link token and authenticate the user.
|
|
1751
1810
|
* Returns full auth response with tokens and auto-stores them.
|
|
@@ -2282,4 +2341,4 @@ declare function createServerClient(secretKey: string, options?: Omit<SPAPSConfi
|
|
|
2282
2341
|
*/
|
|
2283
2342
|
declare function detectKeyType(key: string): ApiKeyType | null;
|
|
2284
2343
|
|
|
2285
|
-
export { type AccessDecisionRequest, type AccessDecisionResponse, type AccountDeletionResponse, type ActionPreparationRequest, type ActionPreparationResponse, type AdminConfig, type ApiKeyType, type AuthDiscoveryMethod, type AuthMethodDescriptor, type AuthMethodResponse, type AuthMethodsResponse, type BatchEmailsResponse, type BatchUsersResponse, type CapabilityActionEnvironment, type CapabilityControlHints, type CapabilityDecisionActor, type CapabilityDecisionFact, type CapabilityDecisionNextAction, type CapabilityDecisionOutcome, type CapabilityDecisionReason, type CapabilityDecisionResource, type CapabilityDecisionSource, type CapabilityGraphContractResponse, type CapabilityGraphImpactQuery, type CapabilityGraphImpactResponse, type CapabilityGraphNode, type CapabilityGraphNodesQuery, type CapabilityGraphNodesResponse, type CapabilityGraphPath, type CapabilityGraphPathsQuery, type CapabilityGraphPathsResponse, type CapabilityGraphProjectionStatus, type CapabilityGraphRefreshDiagnostics, type CapabilityGraphRefreshPhase2Gate, type CapabilityGraphRefreshResponse, type CapabilityGraphRefreshSourceTiming, type CapabilityGraphRowStatus, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, type CurrentUserProjectAccessParams, type CurrentUserProjectGrantListParams, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type GraphExplainResponse, type HeaderProvider, ISSUE_REPORT_ATTACHMENT_ALLOWED_MIME_TYPES, ISSUE_REPORT_ATTACHMENT_MAX_BYTES, ISSUE_REPORT_ATTACHMENT_MAX_RETAINED, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportAttachmentMimeType, type IssueReportAttachmentUploadOptions, type IssueReportListParams, type IssueReportStatusParams, type ListMembershipsParams, type ListMembershipsResponse, type MarketingEventIngestRequest, type MarketingEventIngestResponse, type MarketingEventType, type MarketingExperimentDecision, type MarketingExperimentEffectDecision, type MarketingExperimentMinSampleDecision, type MarketingExperimentRecommendation, type MarketingExperimentResultsResponse, type MarketingExperimentSrmDecision, type MarketingExperimentSrmStatus, type MarketingExperimentVariantResult, type Membership, type MembershipInvitation, type MembershipMutationRequest, type MembershipMutationResponse, type MembershipRemoveResponse, type MembershipStatus, type MfaRequiredAuthResponse, type MfaTotpActivateRequest, type MfaTotpActivateResponse, type MfaTotpDisableRequest, type MfaTotpDisableResponse, type MfaTotpEnrollResponse, type MfaVerifyRequest, type MfaVerifyResponse, type OidcNonceResponse, type OidcSignInRequest, type PermissionCheckResult, PermissionChecker, type PersistedDecisionTrace, type PersistedDecisionTraceStep, type PreparedCapabilityActionStatus, type PreparedCapabilityExecution, type PreparedCapabilityNextAction, type PreparedCapabilitySideEffect, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SPAPSErrorDiagnostic, type SPAPSErrorRemediation, SPAPSSDKError, type SPAPSSDKErrorOptions, type SessionContext, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SmsOtpRequest, type SmsOtpRequestResponse, type SmsOtpVerifyRequest, type SubmitSkillEvalReviewRequest, type SupportActor, type SupportCaseEvent, type SupportCaseSummary, type SupportTelemetryCaseByExternalResponse, type SupportTelemetryCaseDetailResponse, type SupportTelemetryIngestRequest, type SupportTelemetryIngestResponse, type SupportTelemetryListCasesParams, type SupportTelemetryListCasesResponse, type SupportTelemetryPatchCaseRequest, type SupportTelemetryPatchCaseResponse, type TemplateVariable, TokenManager, type UserBatchInfo, WalletUtils, type WebAuthnAssertionOptionsRequest, type WebAuthnAssertionVerifyRequest, type WebAuthnOptionsResponse, type WebAuthnRegisterVerifyRequest, type WebAuthnRegisterVerifyResponse, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type WhitelistAddRequest, type WhitelistBulkAddRequest, type WhitelistBulkAddResponse, type WhitelistBulkEntry, type WhitelistBulkFailedEntry, type WhitelistCheckResponse, type WhitelistEntry, type WhitelistListParams, type WhitelistListResponse, type WhitelistMutationResponse, type WhitelistStatsResponse, type WhitelistUpdateRequest, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
|
|
2344
|
+
export { type AccessDecisionRequest, type AccessDecisionResponse, type AccountDeletionResponse, type ActionPreparationRequest, type ActionPreparationResponse, type AdminConfig, type ApiKeyType, type AuthDiscoveryMethod, type AuthMethodDescriptor, type AuthMethodResponse, type AuthMethodsResponse, type BatchEmailsResponse, type BatchUsersResponse, type CapabilityActionEnvironment, type CapabilityControlHints, type CapabilityDecisionActor, type CapabilityDecisionFact, type CapabilityDecisionNextAction, type CapabilityDecisionOutcome, type CapabilityDecisionReason, type CapabilityDecisionResource, type CapabilityDecisionSource, type CapabilityGraphContractResponse, type CapabilityGraphImpactQuery, type CapabilityGraphImpactResponse, type CapabilityGraphNode, type CapabilityGraphNodesQuery, type CapabilityGraphNodesResponse, type CapabilityGraphPath, type CapabilityGraphPathsQuery, type CapabilityGraphPathsResponse, type CapabilityGraphProjectionStatus, type CapabilityGraphRefreshDiagnostics, type CapabilityGraphRefreshPhase2Gate, type CapabilityGraphRefreshResponse, type CapabilityGraphRefreshSourceTiming, type CapabilityGraphRowStatus, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, type CurrentUserProjectAccessParams, type CurrentUserProjectGrantListParams, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type GraphExplainResponse, type HeaderProvider, ISSUE_REPORT_ATTACHMENT_ALLOWED_MIME_TYPES, ISSUE_REPORT_ATTACHMENT_MAX_BYTES, ISSUE_REPORT_ATTACHMENT_MAX_RETAINED, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportAttachmentMimeType, type IssueReportAttachmentUploadOptions, type IssueReportListParams, type IssueReportStatusParams, type ListMembershipsParams, type ListMembershipsResponse, type MarketingEventIngestRequest, type MarketingEventIngestResponse, type MarketingEventType, type MarketingExperimentDecision, type MarketingExperimentEffectDecision, type MarketingExperimentMinSampleDecision, type MarketingExperimentRecommendation, type MarketingExperimentResultsResponse, type MarketingExperimentSrmDecision, type MarketingExperimentSrmStatus, type MarketingExperimentVariantResult, type Membership, type MembershipInvitation, type MembershipMutationRequest, type MembershipMutationResponse, type MembershipRemoveResponse, type MembershipStatus, type MfaRequiredAuthResponse, type MfaTotpActivateRequest, type MfaTotpActivateResponse, type MfaTotpDisableRequest, type MfaTotpDisableResponse, type MfaTotpEnrollResponse, type MfaVerifyRequest, type MfaVerifyResponse, type OidcNonceResponse, type OidcSignInRequest, type PermissionCheckResult, PermissionChecker, type PersistedDecisionTrace, type PersistedDecisionTraceStep, type PreparedCapabilityActionStatus, type PreparedCapabilityExecution, type PreparedCapabilityNextAction, type PreparedCapabilitySideEffect, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SPAPSErrorDiagnostic, type SPAPSErrorRemediation, SPAPSSDKError, type SPAPSSDKErrorOptions, type SessionContext, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SmsOtpRequest, type SmsOtpRequestResponse, type SmsOtpVerifyRequest, type SubmitSkillEvalReviewRequest, type SupportActor, type SupportActorType, type SupportCaseEvent, type SupportCasePriority, type SupportCaseStatus, type SupportCaseSummary, type SupportEventSeverity, type SupportSourceChannel, type SupportTelemetryCaseByExternalResponse, type SupportTelemetryCaseDetailResponse, type SupportTelemetryIngestRequest, type SupportTelemetryIngestResponse, type SupportTelemetryListCasesParams, type SupportTelemetryListCasesResponse, type SupportTelemetryPatchCaseRequest, type SupportTelemetryPatchCaseResponse, type TemplateVariable, TokenManager, type UserBatchInfo, WalletUtils, type WebAuthnAssertionOptionsRequest, type WebAuthnAssertionVerifyRequest, type WebAuthnOptionsResponse, type WebAuthnRegisterVerifyRequest, type WebAuthnRegisterVerifyResponse, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type WhitelistAddRequest, type WhitelistBulkAddRequest, type WhitelistBulkAddResponse, type WhitelistBulkEntry, type WhitelistBulkFailedEntry, type WhitelistCheckResponse, type WhitelistEntry, type WhitelistListParams, type WhitelistListResponse, type WhitelistMutationResponse, type WhitelistStatsResponse, type WhitelistUpdateRequest, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
|
package/dist/index.d.ts
CHANGED
|
@@ -186,6 +186,28 @@ interface WebSocketAuthHelperConfig {
|
|
|
186
186
|
* Should perform the refresh and return the new access token.
|
|
187
187
|
*/
|
|
188
188
|
refreshAccessToken: () => Promise<string>;
|
|
189
|
+
/**
|
|
190
|
+
* Optional: mint a short-lived, single-use WebSocket connect ticket.
|
|
191
|
+
*
|
|
192
|
+
* When provided, the helper opens the socket with `?ticket=<ticket>` instead
|
|
193
|
+
* of placing any long-lived credential in the URL. This is the preferred,
|
|
194
|
+
* server-aligned path: it matches SPAPS `POST /realtime/ws-ticket`, whose
|
|
195
|
+
* tickets are single-use and expire in seconds, so a leak via logs / proxies
|
|
196
|
+
* / referrers is inert. A fresh ticket is minted for every connection attempt
|
|
197
|
+
* (including reconnects), because each ticket is consumed on use.
|
|
198
|
+
*
|
|
199
|
+
* When omitted, the helper falls back to carrying the bearer token in the
|
|
200
|
+
* WebSocket subprotocol (the `Sec-WebSocket-Protocol` handshake header) rather
|
|
201
|
+
* than the URL query string — so the long-lived JWT is never logged. The
|
|
202
|
+
* server must read the subprotocol for the fallback to authenticate.
|
|
203
|
+
*/
|
|
204
|
+
getConnectTicket?: () => Promise<string> | string;
|
|
205
|
+
/**
|
|
206
|
+
* Milliseconds to wait for the socket to open before rejecting `connect()`.
|
|
207
|
+
* Prevents `connect()` from hanging forever when the socket never opens.
|
|
208
|
+
* 0 disables the timeout (default 10000).
|
|
209
|
+
*/
|
|
210
|
+
connectTimeoutMs?: number;
|
|
189
211
|
/** Seconds before token expiry to proactively refresh (default 30). */
|
|
190
212
|
refreshBufferSeconds?: number;
|
|
191
213
|
/** Maximum number of reconnection attempts on auth failures (default 3). */
|
|
@@ -218,7 +240,11 @@ declare class WebSocketAuthHelper {
|
|
|
218
240
|
constructor(config: WebSocketAuthHelperConfig);
|
|
219
241
|
/**
|
|
220
242
|
* Open an authenticated WebSocket connection.
|
|
221
|
-
*
|
|
243
|
+
*
|
|
244
|
+
* The long-lived access token is never placed in the URL. Authentication uses
|
|
245
|
+
* a short-lived single-use connect ticket (`?ticket=...`) when
|
|
246
|
+
* `getConnectTicket` is configured, otherwise the bearer token is carried in
|
|
247
|
+
* the WebSocket subprotocol (handshake header).
|
|
222
248
|
*/
|
|
223
249
|
connect(): Promise<void>;
|
|
224
250
|
/**
|
|
@@ -230,8 +256,23 @@ declare class WebSocketAuthHelper {
|
|
|
230
256
|
* Throws if the connection is not open.
|
|
231
257
|
*/
|
|
232
258
|
send(data: string): void;
|
|
233
|
-
/**
|
|
234
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Build the WebSocket URL.
|
|
261
|
+
*
|
|
262
|
+
* Never embeds the long-lived access token. When a short-lived connect
|
|
263
|
+
* `ticket` is supplied it is appended as `?ticket=<ticket>` (single-use,
|
|
264
|
+
* seconds-long TTL — inert if leaked). Otherwise the bare base URL is
|
|
265
|
+
* returned and the credential travels in the subprotocol instead.
|
|
266
|
+
*/
|
|
267
|
+
buildAuthUrl(ticket?: string): string;
|
|
268
|
+
/**
|
|
269
|
+
* Build the WebSocket subprotocols carrying the bearer credential.
|
|
270
|
+
*
|
|
271
|
+
* Used only by the no-ticket fallback so the long-lived JWT travels in the
|
|
272
|
+
* `Sec-WebSocket-Protocol` handshake header rather than the URL query string.
|
|
273
|
+
* Throws when no access token is available.
|
|
274
|
+
*/
|
|
275
|
+
buildAuthProtocols(): string[];
|
|
235
276
|
private openConnection;
|
|
236
277
|
private handleAuthClose;
|
|
237
278
|
private handleNetworkClose;
|
|
@@ -301,6 +342,7 @@ interface EmailSendOptions {
|
|
|
301
342
|
to: string;
|
|
302
343
|
context: Record<string, string | number | boolean>;
|
|
303
344
|
userId?: string;
|
|
345
|
+
idempotencyKey?: string;
|
|
304
346
|
}
|
|
305
347
|
interface EmailSendResult {
|
|
306
348
|
success: boolean;
|
|
@@ -678,17 +720,22 @@ interface ListMembershipsParams {
|
|
|
678
720
|
cursor?: string;
|
|
679
721
|
}
|
|
680
722
|
interface SupportActor {
|
|
681
|
-
type:
|
|
723
|
+
type: SupportActorType;
|
|
682
724
|
id?: string | null;
|
|
683
725
|
}
|
|
726
|
+
type SupportActorType = 'user' | 'practitioner' | 'anonymous' | 'service' | 'support_operator';
|
|
727
|
+
type SupportCaseStatus = 'open' | 'in_progress' | 'resolved' | 'ignored';
|
|
728
|
+
type SupportCasePriority = 'low' | 'normal' | 'high' | 'urgent';
|
|
729
|
+
type SupportEventSeverity = 'info' | 'warning' | 'error' | 'critical';
|
|
730
|
+
type SupportSourceChannel = 'issue_log' | 'support_thread' | 'email' | 'frontend' | 'api' | 'webhook';
|
|
684
731
|
interface SupportCaseSummary {
|
|
685
732
|
id: string;
|
|
686
733
|
application_id: string;
|
|
687
734
|
external_case_ref?: string | null;
|
|
688
735
|
title?: string | null;
|
|
689
|
-
status:
|
|
690
|
-
priority:
|
|
691
|
-
highest_severity:
|
|
736
|
+
status: SupportCaseStatus;
|
|
737
|
+
priority: SupportCasePriority;
|
|
738
|
+
highest_severity: SupportEventSeverity;
|
|
692
739
|
assigned_to_user_id?: string | null;
|
|
693
740
|
first_event_at: string;
|
|
694
741
|
last_event_at: string;
|
|
@@ -700,9 +747,9 @@ interface SupportCaseEvent {
|
|
|
700
747
|
id: string;
|
|
701
748
|
case_id: string;
|
|
702
749
|
event_key: string;
|
|
703
|
-
source_channel:
|
|
750
|
+
source_channel: SupportSourceChannel;
|
|
704
751
|
event_type: string;
|
|
705
|
-
severity:
|
|
752
|
+
severity: SupportEventSeverity;
|
|
706
753
|
actor: SupportActor;
|
|
707
754
|
correlation_id?: string | null;
|
|
708
755
|
occurred_at: string;
|
|
@@ -712,9 +759,9 @@ interface SupportCaseEvent {
|
|
|
712
759
|
interface SupportTelemetryIngestRequest {
|
|
713
760
|
event_key: string;
|
|
714
761
|
occurred_at: string;
|
|
715
|
-
source_channel:
|
|
762
|
+
source_channel: SupportSourceChannel;
|
|
716
763
|
event_type: string;
|
|
717
|
-
severity:
|
|
764
|
+
severity: SupportEventSeverity;
|
|
718
765
|
actor: SupportActor;
|
|
719
766
|
payload: Record<string, unknown>;
|
|
720
767
|
application_id?: string | null;
|
|
@@ -727,13 +774,13 @@ interface SupportTelemetryIngestResponse {
|
|
|
727
774
|
case_id: string;
|
|
728
775
|
created_case: boolean;
|
|
729
776
|
deduplicated: boolean;
|
|
730
|
-
case_status:
|
|
777
|
+
case_status: SupportCaseStatus;
|
|
731
778
|
}
|
|
732
779
|
interface SupportTelemetryListCasesParams {
|
|
733
780
|
application_id?: string;
|
|
734
|
-
status?:
|
|
735
|
-
priority?:
|
|
736
|
-
severity_gte?:
|
|
781
|
+
status?: SupportCaseStatus;
|
|
782
|
+
priority?: SupportCasePriority;
|
|
783
|
+
severity_gte?: SupportEventSeverity;
|
|
737
784
|
assigned_to_user_id?: string;
|
|
738
785
|
limit?: number;
|
|
739
786
|
offset?: number;
|
|
@@ -748,16 +795,16 @@ interface SupportTelemetryCaseDetailResponse {
|
|
|
748
795
|
event_total: number;
|
|
749
796
|
}
|
|
750
797
|
interface SupportTelemetryPatchCaseRequest {
|
|
751
|
-
status?:
|
|
752
|
-
priority?:
|
|
798
|
+
status?: SupportCaseStatus;
|
|
799
|
+
priority?: SupportCasePriority;
|
|
753
800
|
assigned_to_user_id?: string | null;
|
|
754
801
|
resolution_note?: string | null;
|
|
755
802
|
}
|
|
756
803
|
interface SupportTelemetryPatchCaseResponse {
|
|
757
804
|
case: {
|
|
758
805
|
id: string;
|
|
759
|
-
status:
|
|
760
|
-
priority:
|
|
806
|
+
status: SupportCaseStatus;
|
|
807
|
+
priority: SupportCasePriority;
|
|
761
808
|
assigned_to_user_id?: string | null;
|
|
762
809
|
resolved_at?: string | null;
|
|
763
810
|
updated_at: string;
|
|
@@ -768,8 +815,8 @@ interface SupportTelemetryCaseByExternalResponse {
|
|
|
768
815
|
id: string;
|
|
769
816
|
application_id: string;
|
|
770
817
|
external_case_ref?: string | null;
|
|
771
|
-
status:
|
|
772
|
-
priority:
|
|
818
|
+
status: SupportCaseStatus;
|
|
819
|
+
priority: SupportCasePriority;
|
|
773
820
|
assigned_to_user_id?: string | null;
|
|
774
821
|
resolved_at?: string | null;
|
|
775
822
|
updated_at: string;
|
|
@@ -1390,9 +1437,21 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
1390
1437
|
private normalizeAuthPayload;
|
|
1391
1438
|
private storeAuthTokens;
|
|
1392
1439
|
private unwrapAuthMethodResponse;
|
|
1440
|
+
/**
|
|
1441
|
+
* Unwrap the SPAPS `{ success, data }` response envelope for the legacy
|
|
1442
|
+
* token-issuing auth methods (login/register/walletSignIn/refresh).
|
|
1443
|
+
*
|
|
1444
|
+
* The server's ResponseEnvelopeMiddleware wraps these responses, so the real
|
|
1445
|
+
* tokens live at `response.data.data`. Reading `response.data.access_token`
|
|
1446
|
+
* (one level too shallow) silently dropped the tokens. Reuse the shared
|
|
1447
|
+
* `unwrapApiResponse` primitive so envelope handling and error fidelity stay
|
|
1448
|
+
* consistent with the rest of the client.
|
|
1449
|
+
*/
|
|
1450
|
+
private extractAuthResponse;
|
|
1393
1451
|
private static isSdkManagedHeader;
|
|
1394
1452
|
private static hasHeader;
|
|
1395
1453
|
private static setHeader;
|
|
1454
|
+
private static deleteHeader;
|
|
1396
1455
|
private static assertSafeHeaderValue;
|
|
1397
1456
|
private applyCustomHeaders;
|
|
1398
1457
|
admin: {
|
|
@@ -1745,7 +1804,7 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
1745
1804
|
register: (payload: {
|
|
1746
1805
|
email: string;
|
|
1747
1806
|
password: string;
|
|
1748
|
-
}) => Promise<
|
|
1807
|
+
}) => Promise<AuthMethodResponse>;
|
|
1749
1808
|
/**
|
|
1750
1809
|
* Verify a magic link token and authenticate the user.
|
|
1751
1810
|
* Returns full auth response with tokens and auto-stores them.
|
|
@@ -2282,4 +2341,4 @@ declare function createServerClient(secretKey: string, options?: Omit<SPAPSConfi
|
|
|
2282
2341
|
*/
|
|
2283
2342
|
declare function detectKeyType(key: string): ApiKeyType | null;
|
|
2284
2343
|
|
|
2285
|
-
export { type AccessDecisionRequest, type AccessDecisionResponse, type AccountDeletionResponse, type ActionPreparationRequest, type ActionPreparationResponse, type AdminConfig, type ApiKeyType, type AuthDiscoveryMethod, type AuthMethodDescriptor, type AuthMethodResponse, type AuthMethodsResponse, type BatchEmailsResponse, type BatchUsersResponse, type CapabilityActionEnvironment, type CapabilityControlHints, type CapabilityDecisionActor, type CapabilityDecisionFact, type CapabilityDecisionNextAction, type CapabilityDecisionOutcome, type CapabilityDecisionReason, type CapabilityDecisionResource, type CapabilityDecisionSource, type CapabilityGraphContractResponse, type CapabilityGraphImpactQuery, type CapabilityGraphImpactResponse, type CapabilityGraphNode, type CapabilityGraphNodesQuery, type CapabilityGraphNodesResponse, type CapabilityGraphPath, type CapabilityGraphPathsQuery, type CapabilityGraphPathsResponse, type CapabilityGraphProjectionStatus, type CapabilityGraphRefreshDiagnostics, type CapabilityGraphRefreshPhase2Gate, type CapabilityGraphRefreshResponse, type CapabilityGraphRefreshSourceTiming, type CapabilityGraphRowStatus, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, type CurrentUserProjectAccessParams, type CurrentUserProjectGrantListParams, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type GraphExplainResponse, type HeaderProvider, ISSUE_REPORT_ATTACHMENT_ALLOWED_MIME_TYPES, ISSUE_REPORT_ATTACHMENT_MAX_BYTES, ISSUE_REPORT_ATTACHMENT_MAX_RETAINED, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportAttachmentMimeType, type IssueReportAttachmentUploadOptions, type IssueReportListParams, type IssueReportStatusParams, type ListMembershipsParams, type ListMembershipsResponse, type MarketingEventIngestRequest, type MarketingEventIngestResponse, type MarketingEventType, type MarketingExperimentDecision, type MarketingExperimentEffectDecision, type MarketingExperimentMinSampleDecision, type MarketingExperimentRecommendation, type MarketingExperimentResultsResponse, type MarketingExperimentSrmDecision, type MarketingExperimentSrmStatus, type MarketingExperimentVariantResult, type Membership, type MembershipInvitation, type MembershipMutationRequest, type MembershipMutationResponse, type MembershipRemoveResponse, type MembershipStatus, type MfaRequiredAuthResponse, type MfaTotpActivateRequest, type MfaTotpActivateResponse, type MfaTotpDisableRequest, type MfaTotpDisableResponse, type MfaTotpEnrollResponse, type MfaVerifyRequest, type MfaVerifyResponse, type OidcNonceResponse, type OidcSignInRequest, type PermissionCheckResult, PermissionChecker, type PersistedDecisionTrace, type PersistedDecisionTraceStep, type PreparedCapabilityActionStatus, type PreparedCapabilityExecution, type PreparedCapabilityNextAction, type PreparedCapabilitySideEffect, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SPAPSErrorDiagnostic, type SPAPSErrorRemediation, SPAPSSDKError, type SPAPSSDKErrorOptions, type SessionContext, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SmsOtpRequest, type SmsOtpRequestResponse, type SmsOtpVerifyRequest, type SubmitSkillEvalReviewRequest, type SupportActor, type SupportCaseEvent, type SupportCaseSummary, type SupportTelemetryCaseByExternalResponse, type SupportTelemetryCaseDetailResponse, type SupportTelemetryIngestRequest, type SupportTelemetryIngestResponse, type SupportTelemetryListCasesParams, type SupportTelemetryListCasesResponse, type SupportTelemetryPatchCaseRequest, type SupportTelemetryPatchCaseResponse, type TemplateVariable, TokenManager, type UserBatchInfo, WalletUtils, type WebAuthnAssertionOptionsRequest, type WebAuthnAssertionVerifyRequest, type WebAuthnOptionsResponse, type WebAuthnRegisterVerifyRequest, type WebAuthnRegisterVerifyResponse, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type WhitelistAddRequest, type WhitelistBulkAddRequest, type WhitelistBulkAddResponse, type WhitelistBulkEntry, type WhitelistBulkFailedEntry, type WhitelistCheckResponse, type WhitelistEntry, type WhitelistListParams, type WhitelistListResponse, type WhitelistMutationResponse, type WhitelistStatsResponse, type WhitelistUpdateRequest, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
|
|
2344
|
+
export { type AccessDecisionRequest, type AccessDecisionResponse, type AccountDeletionResponse, type ActionPreparationRequest, type ActionPreparationResponse, type AdminConfig, type ApiKeyType, type AuthDiscoveryMethod, type AuthMethodDescriptor, type AuthMethodResponse, type AuthMethodsResponse, type BatchEmailsResponse, type BatchUsersResponse, type CapabilityActionEnvironment, type CapabilityControlHints, type CapabilityDecisionActor, type CapabilityDecisionFact, type CapabilityDecisionNextAction, type CapabilityDecisionOutcome, type CapabilityDecisionReason, type CapabilityDecisionResource, type CapabilityDecisionSource, type CapabilityGraphContractResponse, type CapabilityGraphImpactQuery, type CapabilityGraphImpactResponse, type CapabilityGraphNode, type CapabilityGraphNodesQuery, type CapabilityGraphNodesResponse, type CapabilityGraphPath, type CapabilityGraphPathsQuery, type CapabilityGraphPathsResponse, type CapabilityGraphProjectionStatus, type CapabilityGraphRefreshDiagnostics, type CapabilityGraphRefreshPhase2Gate, type CapabilityGraphRefreshResponse, type CapabilityGraphRefreshSourceTiming, type CapabilityGraphRowStatus, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, type CurrentUserProjectAccessParams, type CurrentUserProjectGrantListParams, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type GraphExplainResponse, type HeaderProvider, ISSUE_REPORT_ATTACHMENT_ALLOWED_MIME_TYPES, ISSUE_REPORT_ATTACHMENT_MAX_BYTES, ISSUE_REPORT_ATTACHMENT_MAX_RETAINED, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportAttachmentMimeType, type IssueReportAttachmentUploadOptions, type IssueReportListParams, type IssueReportStatusParams, type ListMembershipsParams, type ListMembershipsResponse, type MarketingEventIngestRequest, type MarketingEventIngestResponse, type MarketingEventType, type MarketingExperimentDecision, type MarketingExperimentEffectDecision, type MarketingExperimentMinSampleDecision, type MarketingExperimentRecommendation, type MarketingExperimentResultsResponse, type MarketingExperimentSrmDecision, type MarketingExperimentSrmStatus, type MarketingExperimentVariantResult, type Membership, type MembershipInvitation, type MembershipMutationRequest, type MembershipMutationResponse, type MembershipRemoveResponse, type MembershipStatus, type MfaRequiredAuthResponse, type MfaTotpActivateRequest, type MfaTotpActivateResponse, type MfaTotpDisableRequest, type MfaTotpDisableResponse, type MfaTotpEnrollResponse, type MfaVerifyRequest, type MfaVerifyResponse, type OidcNonceResponse, type OidcSignInRequest, type PermissionCheckResult, PermissionChecker, type PersistedDecisionTrace, type PersistedDecisionTraceStep, type PreparedCapabilityActionStatus, type PreparedCapabilityExecution, type PreparedCapabilityNextAction, type PreparedCapabilitySideEffect, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SPAPSErrorDiagnostic, type SPAPSErrorRemediation, SPAPSSDKError, type SPAPSSDKErrorOptions, type SessionContext, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SmsOtpRequest, type SmsOtpRequestResponse, type SmsOtpVerifyRequest, type SubmitSkillEvalReviewRequest, type SupportActor, type SupportActorType, type SupportCaseEvent, type SupportCasePriority, type SupportCaseStatus, type SupportCaseSummary, type SupportEventSeverity, type SupportSourceChannel, type SupportTelemetryCaseByExternalResponse, type SupportTelemetryCaseDetailResponse, type SupportTelemetryIngestRequest, type SupportTelemetryIngestResponse, type SupportTelemetryListCasesParams, type SupportTelemetryListCasesResponse, type SupportTelemetryPatchCaseRequest, type SupportTelemetryPatchCaseResponse, type TemplateVariable, TokenManager, type UserBatchInfo, WalletUtils, type WebAuthnAssertionOptionsRequest, type WebAuthnAssertionVerifyRequest, type WebAuthnOptionsResponse, type WebAuthnRegisterVerifyRequest, type WebAuthnRegisterVerifyResponse, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type WhitelistAddRequest, type WhitelistBulkAddRequest, type WhitelistBulkAddResponse, type WhitelistBulkEntry, type WhitelistBulkFailedEntry, type WhitelistCheckResponse, type WhitelistEntry, type WhitelistListParams, type WhitelistListResponse, type WhitelistMutationResponse, type WhitelistStatsResponse, type WhitelistUpdateRequest, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
|
package/dist/index.js
CHANGED
|
@@ -335,6 +335,7 @@ var FeatureEvaluator = class {
|
|
|
335
335
|
|
|
336
336
|
// src/websocket-auth-helper.ts
|
|
337
337
|
var AUTH_CLOSE_CODES = /* @__PURE__ */ new Set([4001, 4003]);
|
|
338
|
+
var WS_BEARER_SUBPROTOCOL = "spaps.bearer.v1";
|
|
338
339
|
var WebSocketAuthHelper = class {
|
|
339
340
|
config;
|
|
340
341
|
ws = null;
|
|
@@ -348,6 +349,8 @@ var WebSocketAuthHelper = class {
|
|
|
348
349
|
url: config.url,
|
|
349
350
|
getAccessToken: config.getAccessToken,
|
|
350
351
|
refreshAccessToken: config.refreshAccessToken,
|
|
352
|
+
getConnectTicket: config.getConnectTicket,
|
|
353
|
+
connectTimeoutMs: config.connectTimeoutMs ?? 1e4,
|
|
351
354
|
refreshBufferSeconds: config.refreshBufferSeconds ?? 30,
|
|
352
355
|
maxAuthRetries: config.maxAuthRetries ?? 3,
|
|
353
356
|
maxNetworkRetries: config.maxNetworkRetries ?? 5,
|
|
@@ -363,7 +366,11 @@ var WebSocketAuthHelper = class {
|
|
|
363
366
|
// ── Public API ──────────────────────────────────────────────────────────
|
|
364
367
|
/**
|
|
365
368
|
* Open an authenticated WebSocket connection.
|
|
366
|
-
*
|
|
369
|
+
*
|
|
370
|
+
* The long-lived access token is never placed in the URL. Authentication uses
|
|
371
|
+
* a short-lived single-use connect ticket (`?ticket=...`) when
|
|
372
|
+
* `getConnectTicket` is configured, otherwise the bearer token is carried in
|
|
373
|
+
* the WebSocket subprotocol (handshake header).
|
|
367
374
|
*/
|
|
368
375
|
async connect() {
|
|
369
376
|
this.intentionalClose = false;
|
|
@@ -393,30 +400,84 @@ var WebSocketAuthHelper = class {
|
|
|
393
400
|
this.ws.send(data);
|
|
394
401
|
}
|
|
395
402
|
// ── Internal helpers ────────────────────────────────────────────────────
|
|
396
|
-
/**
|
|
397
|
-
|
|
403
|
+
/**
|
|
404
|
+
* Build the WebSocket URL.
|
|
405
|
+
*
|
|
406
|
+
* Never embeds the long-lived access token. When a short-lived connect
|
|
407
|
+
* `ticket` is supplied it is appended as `?ticket=<ticket>` (single-use,
|
|
408
|
+
* seconds-long TTL — inert if leaked). Otherwise the bare base URL is
|
|
409
|
+
* returned and the credential travels in the subprotocol instead.
|
|
410
|
+
*/
|
|
411
|
+
buildAuthUrl(ticket) {
|
|
412
|
+
if (ticket) {
|
|
413
|
+
const separator = this.config.url.includes("?") ? "&" : "?";
|
|
414
|
+
return `${this.config.url}${separator}ticket=${encodeURIComponent(ticket)}`;
|
|
415
|
+
}
|
|
416
|
+
return this.config.url;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Build the WebSocket subprotocols carrying the bearer credential.
|
|
420
|
+
*
|
|
421
|
+
* Used only by the no-ticket fallback so the long-lived JWT travels in the
|
|
422
|
+
* `Sec-WebSocket-Protocol` handshake header rather than the URL query string.
|
|
423
|
+
* Throws when no access token is available.
|
|
424
|
+
*/
|
|
425
|
+
buildAuthProtocols() {
|
|
398
426
|
const token = this.config.getAccessToken();
|
|
399
427
|
if (!token) {
|
|
400
428
|
throw new Error("No access token available for WebSocket authentication");
|
|
401
429
|
}
|
|
402
|
-
|
|
403
|
-
return `${this.config.url}${separator}token=${encodeURIComponent(token)}`;
|
|
430
|
+
return [WS_BEARER_SUBPROTOCOL, token];
|
|
404
431
|
}
|
|
405
432
|
async openConnection() {
|
|
406
|
-
|
|
433
|
+
let url;
|
|
434
|
+
let protocols;
|
|
435
|
+
if (this.config.getConnectTicket) {
|
|
436
|
+
const ticket = await this.config.getConnectTicket();
|
|
437
|
+
if (!ticket) {
|
|
438
|
+
throw new Error("Failed to obtain WebSocket connect ticket");
|
|
439
|
+
}
|
|
440
|
+
url = this.buildAuthUrl(ticket);
|
|
441
|
+
protocols = void 0;
|
|
442
|
+
} else {
|
|
443
|
+
url = this.buildAuthUrl();
|
|
444
|
+
protocols = this.buildAuthProtocols();
|
|
445
|
+
}
|
|
407
446
|
return new Promise((resolve, reject) => {
|
|
447
|
+
let settled = false;
|
|
448
|
+
let connectTimer = null;
|
|
449
|
+
const settle = (action) => {
|
|
450
|
+
if (settled) return;
|
|
451
|
+
settled = true;
|
|
452
|
+
if (connectTimer) {
|
|
453
|
+
clearTimeout(connectTimer);
|
|
454
|
+
connectTimer = null;
|
|
455
|
+
}
|
|
456
|
+
action();
|
|
457
|
+
};
|
|
408
458
|
try {
|
|
409
|
-
this.ws = new WebSocket(url);
|
|
459
|
+
this.ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
|
|
410
460
|
} catch (err) {
|
|
411
|
-
reject(err);
|
|
461
|
+
settle(() => reject(err));
|
|
412
462
|
return;
|
|
413
463
|
}
|
|
464
|
+
if (this.config.connectTimeoutMs > 0) {
|
|
465
|
+
connectTimer = setTimeout(() => {
|
|
466
|
+
settle(() => {
|
|
467
|
+
try {
|
|
468
|
+
this.ws?.close();
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
reject(new Error("WebSocket connection timed out before opening"));
|
|
472
|
+
});
|
|
473
|
+
}, this.config.connectTimeoutMs);
|
|
474
|
+
}
|
|
414
475
|
this.ws.onopen = () => {
|
|
415
476
|
this.networkRetries = 0;
|
|
416
477
|
this.startPing();
|
|
417
478
|
this.scheduleTokenRefresh();
|
|
418
479
|
this.config.onOpen?.();
|
|
419
|
-
resolve
|
|
480
|
+
settle(resolve);
|
|
420
481
|
};
|
|
421
482
|
this.ws.onmessage = (event) => {
|
|
422
483
|
this.config.onMessage?.(String(event.data));
|
|
@@ -427,6 +488,13 @@ var WebSocketAuthHelper = class {
|
|
|
427
488
|
this.ws.onclose = (event) => {
|
|
428
489
|
this.cleanup();
|
|
429
490
|
this.config.onClose?.(event.code, event.reason);
|
|
491
|
+
settle(
|
|
492
|
+
() => reject(
|
|
493
|
+
new Error(
|
|
494
|
+
`WebSocket closed before opening (code ${event.code}${event.reason ? `: ${event.reason}` : ""})`
|
|
495
|
+
)
|
|
496
|
+
)
|
|
497
|
+
);
|
|
430
498
|
if (this.intentionalClose) {
|
|
431
499
|
return;
|
|
432
500
|
}
|
|
@@ -817,6 +885,22 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
817
885
|
this.storeAuthTokens(result);
|
|
818
886
|
return result;
|
|
819
887
|
}
|
|
888
|
+
/**
|
|
889
|
+
* Unwrap the SPAPS `{ success, data }` response envelope for the legacy
|
|
890
|
+
* token-issuing auth methods (login/register/walletSignIn/refresh).
|
|
891
|
+
*
|
|
892
|
+
* The server's ResponseEnvelopeMiddleware wraps these responses, so the real
|
|
893
|
+
* tokens live at `response.data.data`. Reading `response.data.access_token`
|
|
894
|
+
* (one level too shallow) silently dropped the tokens. Reuse the shared
|
|
895
|
+
* `unwrapApiResponse` primitive so envelope handling and error fidelity stay
|
|
896
|
+
* consistent with the rest of the client.
|
|
897
|
+
*/
|
|
898
|
+
extractAuthResponse(response, fallback) {
|
|
899
|
+
return this.unwrapApiResponse(
|
|
900
|
+
response,
|
|
901
|
+
fallback
|
|
902
|
+
);
|
|
903
|
+
}
|
|
820
904
|
static isSdkManagedHeader(name) {
|
|
821
905
|
const normalized = name.toLowerCase();
|
|
822
906
|
return normalized === "authorization" || normalized === "x-api-key";
|
|
@@ -836,6 +920,19 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
836
920
|
}
|
|
837
921
|
headers[name] = value;
|
|
838
922
|
}
|
|
923
|
+
static deleteHeader(headers, name) {
|
|
924
|
+
if (!headers) return;
|
|
925
|
+
if (typeof headers.delete === "function") {
|
|
926
|
+
headers.delete(name);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
const normalized = name.toLowerCase();
|
|
930
|
+
for (const key of Object.keys(headers)) {
|
|
931
|
+
if (key.toLowerCase() === normalized) {
|
|
932
|
+
delete headers[key];
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
839
936
|
static assertSafeHeaderValue(name, value) {
|
|
840
937
|
if (typeof value === "string" && /[\r\n]/.test(value)) {
|
|
841
938
|
throw new Error(`Invalid header value for ${name}: control characters not allowed`);
|
|
@@ -1049,10 +1146,14 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1049
1146
|
} else {
|
|
1050
1147
|
formData.append("file", file);
|
|
1051
1148
|
}
|
|
1149
|
+
const uploadHeaders = { "Content-Type": null };
|
|
1150
|
+
if (this.accessToken) {
|
|
1151
|
+
uploadHeaders.Authorization = `Bearer ${this.accessToken}`;
|
|
1152
|
+
}
|
|
1052
1153
|
const res = await this.client.post(
|
|
1053
1154
|
"/api/v1/issue-reports/attachments",
|
|
1054
1155
|
formData,
|
|
1055
|
-
|
|
1156
|
+
{ headers: uploadHeaders }
|
|
1056
1157
|
);
|
|
1057
1158
|
return this.unwrapApiResponse(res, "Failed to upload issue report attachment");
|
|
1058
1159
|
},
|
|
@@ -1535,6 +1636,9 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1535
1636
|
});
|
|
1536
1637
|
this.client.interceptors.request.use((config2) => {
|
|
1537
1638
|
config2.headers = config2.headers || {};
|
|
1639
|
+
if (typeof FormData !== "undefined" && config2.data instanceof FormData) {
|
|
1640
|
+
_SPAPSClient.deleteHeader(config2.headers, "Content-Type");
|
|
1641
|
+
}
|
|
1538
1642
|
this.applyCustomHeaders(config2.headers);
|
|
1539
1643
|
if (this.apiKey && !_SPAPSClient.hasHeader(config2.headers, "X-API-Key")) {
|
|
1540
1644
|
_SPAPSClient.setHeader(config2.headers, "X-API-Key", this.apiKey);
|
|
@@ -1547,15 +1651,18 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1547
1651
|
this.client.interceptors.response.use(
|
|
1548
1652
|
(response) => response,
|
|
1549
1653
|
async (error) => {
|
|
1550
|
-
|
|
1654
|
+
const cfg = error.config || {};
|
|
1655
|
+
const requestUrl = cfg.url || "";
|
|
1656
|
+
const shouldAttemptRefresh = error.response?.status === 401 && !!this.refreshToken && !cfg.__isRetry && !requestUrl.includes("/api/auth/refresh") && !!error.config;
|
|
1657
|
+
if (shouldAttemptRefresh) {
|
|
1551
1658
|
try {
|
|
1552
1659
|
const { data } = await this.refresh(this.refreshToken);
|
|
1553
1660
|
this.accessToken = data.access_token;
|
|
1554
1661
|
this.refreshToken = data.refresh_token;
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1662
|
+
cfg.__isRetry = true;
|
|
1663
|
+
cfg.headers = cfg.headers || {};
|
|
1664
|
+
_SPAPSClient.setHeader(cfg.headers, "Authorization", `Bearer ${this.accessToken}`);
|
|
1665
|
+
return this.client.request(cfg);
|
|
1559
1666
|
} catch (refreshError) {
|
|
1560
1667
|
this.accessToken = void 0;
|
|
1561
1668
|
this.refreshToken = void 0;
|
|
@@ -1598,18 +1705,21 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1598
1705
|
email,
|
|
1599
1706
|
password
|
|
1600
1707
|
});
|
|
1601
|
-
this.
|
|
1602
|
-
this.
|
|
1603
|
-
|
|
1708
|
+
const data = this.extractAuthResponse(response, "Login failed");
|
|
1709
|
+
this.accessToken = data.access_token;
|
|
1710
|
+
this.refreshToken = data.refresh_token;
|
|
1711
|
+
return { data };
|
|
1604
1712
|
}
|
|
1605
1713
|
async register(email, password) {
|
|
1606
1714
|
const response = await this.client.post("/api/auth/register", {
|
|
1607
1715
|
email,
|
|
1608
1716
|
password
|
|
1609
1717
|
});
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1718
|
+
const data = this.normalizeAuthPayload(
|
|
1719
|
+
this.extractAuthResponse(response, "Registration failed")
|
|
1720
|
+
);
|
|
1721
|
+
this.storeAuthTokens(data);
|
|
1722
|
+
return { data };
|
|
1613
1723
|
}
|
|
1614
1724
|
async walletSignIn(walletAddress, signature, message, chainType = "solana") {
|
|
1615
1725
|
const response = await this.client.post("/api/auth/wallet-sign-in", {
|
|
@@ -1618,17 +1728,19 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1618
1728
|
message,
|
|
1619
1729
|
chain_type: chainType
|
|
1620
1730
|
});
|
|
1621
|
-
this.
|
|
1622
|
-
this.
|
|
1623
|
-
|
|
1731
|
+
const data = this.extractAuthResponse(response, "Wallet sign-in failed");
|
|
1732
|
+
this.accessToken = data.access_token;
|
|
1733
|
+
this.refreshToken = data.refresh_token;
|
|
1734
|
+
return { data };
|
|
1624
1735
|
}
|
|
1625
1736
|
async refresh(refreshToken) {
|
|
1626
1737
|
const response = await this.client.post("/api/auth/refresh", {
|
|
1627
1738
|
refresh_token: refreshToken || this.refreshToken
|
|
1628
1739
|
});
|
|
1629
|
-
this.
|
|
1630
|
-
this.
|
|
1631
|
-
|
|
1740
|
+
const data = this.extractAuthResponse(response, "Token refresh failed");
|
|
1741
|
+
this.accessToken = data.access_token;
|
|
1742
|
+
this.refreshToken = data.refresh_token;
|
|
1743
|
+
return { data };
|
|
1632
1744
|
}
|
|
1633
1745
|
async logout() {
|
|
1634
1746
|
await this.client.post("/api/auth/logout");
|
|
@@ -1636,7 +1748,9 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1636
1748
|
this.refreshToken = void 0;
|
|
1637
1749
|
}
|
|
1638
1750
|
async getUser() {
|
|
1639
|
-
|
|
1751
|
+
const res = await this.client.get("/api/auth/user");
|
|
1752
|
+
const payload = this.unwrapApiResponse(res, "Failed to get user");
|
|
1753
|
+
return { data: payload.user };
|
|
1640
1754
|
}
|
|
1641
1755
|
async getSessionContext() {
|
|
1642
1756
|
const res = await this.client.get("/api/auth/session-context", this.authConfig());
|
|
@@ -1715,12 +1829,7 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1715
1829
|
},
|
|
1716
1830
|
register: async (payload) => {
|
|
1717
1831
|
const res = await this.client.post("/api/auth/register", payload);
|
|
1718
|
-
|
|
1719
|
-
if (body?.success === false) throw new Error(body?.error?.message || "Register failed");
|
|
1720
|
-
const data = body?.data ?? body;
|
|
1721
|
-
this.accessToken = data.access_token;
|
|
1722
|
-
this.refreshToken = data.refresh_token;
|
|
1723
|
-
return data;
|
|
1832
|
+
return this.unwrapAuthMethodResponse(res, "Register failed");
|
|
1724
1833
|
},
|
|
1725
1834
|
/**
|
|
1726
1835
|
* Verify a magic link token and authenticate the user.
|
|
@@ -2522,15 +2631,25 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
2522
2631
|
}
|
|
2523
2632
|
async getSubscription(subscriptionId) {
|
|
2524
2633
|
if (subscriptionId) {
|
|
2525
|
-
|
|
2634
|
+
const res = await this.client.get(
|
|
2635
|
+
`/api/stripe/subscription/${subscriptionId}`
|
|
2636
|
+
);
|
|
2637
|
+
return { data: this.unwrapApiResponse(res, "Failed to get subscription") };
|
|
2526
2638
|
}
|
|
2527
|
-
|
|
2639
|
+
const response = await this.client.get(
|
|
2640
|
+
"/api/stripe/subscriptions"
|
|
2641
|
+
);
|
|
2642
|
+
return { data: response.data?.subscriptions ?? [] };
|
|
2528
2643
|
}
|
|
2529
2644
|
async cancelSubscription(subscriptionId, options = {}) {
|
|
2530
2645
|
if (!subscriptionId) {
|
|
2531
2646
|
throw new Error("subscriptionId is required to cancel a subscription.");
|
|
2532
2647
|
}
|
|
2533
|
-
|
|
2648
|
+
const res = await this.client.post(
|
|
2649
|
+
`/api/stripe/subscription/${subscriptionId}/cancel`,
|
|
2650
|
+
options
|
|
2651
|
+
);
|
|
2652
|
+
return { data: this.unwrapApiResponse(res, "Failed to cancel subscription") };
|
|
2534
2653
|
}
|
|
2535
2654
|
// Usage Methods
|
|
2536
2655
|
async authorizeUsage(payload) {
|
|
@@ -2573,7 +2692,8 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
2573
2692
|
template_key: options.templateKey,
|
|
2574
2693
|
to: options.to,
|
|
2575
2694
|
context: options.context,
|
|
2576
|
-
...options.userId && { user_id: options.userId }
|
|
2695
|
+
...options.userId && { user_id: options.userId },
|
|
2696
|
+
...options.idempotencyKey && { idempotency_key: options.idempotencyKey }
|
|
2577
2697
|
});
|
|
2578
2698
|
return this.unwrapApiResponse(response, "Failed to send email");
|
|
2579
2699
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -296,6 +296,7 @@ var FeatureEvaluator = class {
|
|
|
296
296
|
|
|
297
297
|
// src/websocket-auth-helper.ts
|
|
298
298
|
var AUTH_CLOSE_CODES = /* @__PURE__ */ new Set([4001, 4003]);
|
|
299
|
+
var WS_BEARER_SUBPROTOCOL = "spaps.bearer.v1";
|
|
299
300
|
var WebSocketAuthHelper = class {
|
|
300
301
|
config;
|
|
301
302
|
ws = null;
|
|
@@ -309,6 +310,8 @@ var WebSocketAuthHelper = class {
|
|
|
309
310
|
url: config.url,
|
|
310
311
|
getAccessToken: config.getAccessToken,
|
|
311
312
|
refreshAccessToken: config.refreshAccessToken,
|
|
313
|
+
getConnectTicket: config.getConnectTicket,
|
|
314
|
+
connectTimeoutMs: config.connectTimeoutMs ?? 1e4,
|
|
312
315
|
refreshBufferSeconds: config.refreshBufferSeconds ?? 30,
|
|
313
316
|
maxAuthRetries: config.maxAuthRetries ?? 3,
|
|
314
317
|
maxNetworkRetries: config.maxNetworkRetries ?? 5,
|
|
@@ -324,7 +327,11 @@ var WebSocketAuthHelper = class {
|
|
|
324
327
|
// ── Public API ──────────────────────────────────────────────────────────
|
|
325
328
|
/**
|
|
326
329
|
* Open an authenticated WebSocket connection.
|
|
327
|
-
*
|
|
330
|
+
*
|
|
331
|
+
* The long-lived access token is never placed in the URL. Authentication uses
|
|
332
|
+
* a short-lived single-use connect ticket (`?ticket=...`) when
|
|
333
|
+
* `getConnectTicket` is configured, otherwise the bearer token is carried in
|
|
334
|
+
* the WebSocket subprotocol (handshake header).
|
|
328
335
|
*/
|
|
329
336
|
async connect() {
|
|
330
337
|
this.intentionalClose = false;
|
|
@@ -354,30 +361,84 @@ var WebSocketAuthHelper = class {
|
|
|
354
361
|
this.ws.send(data);
|
|
355
362
|
}
|
|
356
363
|
// ── Internal helpers ────────────────────────────────────────────────────
|
|
357
|
-
/**
|
|
358
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Build the WebSocket URL.
|
|
366
|
+
*
|
|
367
|
+
* Never embeds the long-lived access token. When a short-lived connect
|
|
368
|
+
* `ticket` is supplied it is appended as `?ticket=<ticket>` (single-use,
|
|
369
|
+
* seconds-long TTL — inert if leaked). Otherwise the bare base URL is
|
|
370
|
+
* returned and the credential travels in the subprotocol instead.
|
|
371
|
+
*/
|
|
372
|
+
buildAuthUrl(ticket) {
|
|
373
|
+
if (ticket) {
|
|
374
|
+
const separator = this.config.url.includes("?") ? "&" : "?";
|
|
375
|
+
return `${this.config.url}${separator}ticket=${encodeURIComponent(ticket)}`;
|
|
376
|
+
}
|
|
377
|
+
return this.config.url;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Build the WebSocket subprotocols carrying the bearer credential.
|
|
381
|
+
*
|
|
382
|
+
* Used only by the no-ticket fallback so the long-lived JWT travels in the
|
|
383
|
+
* `Sec-WebSocket-Protocol` handshake header rather than the URL query string.
|
|
384
|
+
* Throws when no access token is available.
|
|
385
|
+
*/
|
|
386
|
+
buildAuthProtocols() {
|
|
359
387
|
const token = this.config.getAccessToken();
|
|
360
388
|
if (!token) {
|
|
361
389
|
throw new Error("No access token available for WebSocket authentication");
|
|
362
390
|
}
|
|
363
|
-
|
|
364
|
-
return `${this.config.url}${separator}token=${encodeURIComponent(token)}`;
|
|
391
|
+
return [WS_BEARER_SUBPROTOCOL, token];
|
|
365
392
|
}
|
|
366
393
|
async openConnection() {
|
|
367
|
-
|
|
394
|
+
let url;
|
|
395
|
+
let protocols;
|
|
396
|
+
if (this.config.getConnectTicket) {
|
|
397
|
+
const ticket = await this.config.getConnectTicket();
|
|
398
|
+
if (!ticket) {
|
|
399
|
+
throw new Error("Failed to obtain WebSocket connect ticket");
|
|
400
|
+
}
|
|
401
|
+
url = this.buildAuthUrl(ticket);
|
|
402
|
+
protocols = void 0;
|
|
403
|
+
} else {
|
|
404
|
+
url = this.buildAuthUrl();
|
|
405
|
+
protocols = this.buildAuthProtocols();
|
|
406
|
+
}
|
|
368
407
|
return new Promise((resolve, reject) => {
|
|
408
|
+
let settled = false;
|
|
409
|
+
let connectTimer = null;
|
|
410
|
+
const settle = (action) => {
|
|
411
|
+
if (settled) return;
|
|
412
|
+
settled = true;
|
|
413
|
+
if (connectTimer) {
|
|
414
|
+
clearTimeout(connectTimer);
|
|
415
|
+
connectTimer = null;
|
|
416
|
+
}
|
|
417
|
+
action();
|
|
418
|
+
};
|
|
369
419
|
try {
|
|
370
|
-
this.ws = new WebSocket(url);
|
|
420
|
+
this.ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
|
|
371
421
|
} catch (err) {
|
|
372
|
-
reject(err);
|
|
422
|
+
settle(() => reject(err));
|
|
373
423
|
return;
|
|
374
424
|
}
|
|
425
|
+
if (this.config.connectTimeoutMs > 0) {
|
|
426
|
+
connectTimer = setTimeout(() => {
|
|
427
|
+
settle(() => {
|
|
428
|
+
try {
|
|
429
|
+
this.ws?.close();
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
reject(new Error("WebSocket connection timed out before opening"));
|
|
433
|
+
});
|
|
434
|
+
}, this.config.connectTimeoutMs);
|
|
435
|
+
}
|
|
375
436
|
this.ws.onopen = () => {
|
|
376
437
|
this.networkRetries = 0;
|
|
377
438
|
this.startPing();
|
|
378
439
|
this.scheduleTokenRefresh();
|
|
379
440
|
this.config.onOpen?.();
|
|
380
|
-
resolve
|
|
441
|
+
settle(resolve);
|
|
381
442
|
};
|
|
382
443
|
this.ws.onmessage = (event) => {
|
|
383
444
|
this.config.onMessage?.(String(event.data));
|
|
@@ -388,6 +449,13 @@ var WebSocketAuthHelper = class {
|
|
|
388
449
|
this.ws.onclose = (event) => {
|
|
389
450
|
this.cleanup();
|
|
390
451
|
this.config.onClose?.(event.code, event.reason);
|
|
452
|
+
settle(
|
|
453
|
+
() => reject(
|
|
454
|
+
new Error(
|
|
455
|
+
`WebSocket closed before opening (code ${event.code}${event.reason ? `: ${event.reason}` : ""})`
|
|
456
|
+
)
|
|
457
|
+
)
|
|
458
|
+
);
|
|
391
459
|
if (this.intentionalClose) {
|
|
392
460
|
return;
|
|
393
461
|
}
|
|
@@ -778,6 +846,22 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
778
846
|
this.storeAuthTokens(result);
|
|
779
847
|
return result;
|
|
780
848
|
}
|
|
849
|
+
/**
|
|
850
|
+
* Unwrap the SPAPS `{ success, data }` response envelope for the legacy
|
|
851
|
+
* token-issuing auth methods (login/register/walletSignIn/refresh).
|
|
852
|
+
*
|
|
853
|
+
* The server's ResponseEnvelopeMiddleware wraps these responses, so the real
|
|
854
|
+
* tokens live at `response.data.data`. Reading `response.data.access_token`
|
|
855
|
+
* (one level too shallow) silently dropped the tokens. Reuse the shared
|
|
856
|
+
* `unwrapApiResponse` primitive so envelope handling and error fidelity stay
|
|
857
|
+
* consistent with the rest of the client.
|
|
858
|
+
*/
|
|
859
|
+
extractAuthResponse(response, fallback) {
|
|
860
|
+
return this.unwrapApiResponse(
|
|
861
|
+
response,
|
|
862
|
+
fallback
|
|
863
|
+
);
|
|
864
|
+
}
|
|
781
865
|
static isSdkManagedHeader(name) {
|
|
782
866
|
const normalized = name.toLowerCase();
|
|
783
867
|
return normalized === "authorization" || normalized === "x-api-key";
|
|
@@ -797,6 +881,19 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
797
881
|
}
|
|
798
882
|
headers[name] = value;
|
|
799
883
|
}
|
|
884
|
+
static deleteHeader(headers, name) {
|
|
885
|
+
if (!headers) return;
|
|
886
|
+
if (typeof headers.delete === "function") {
|
|
887
|
+
headers.delete(name);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const normalized = name.toLowerCase();
|
|
891
|
+
for (const key of Object.keys(headers)) {
|
|
892
|
+
if (key.toLowerCase() === normalized) {
|
|
893
|
+
delete headers[key];
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
800
897
|
static assertSafeHeaderValue(name, value) {
|
|
801
898
|
if (typeof value === "string" && /[\r\n]/.test(value)) {
|
|
802
899
|
throw new Error(`Invalid header value for ${name}: control characters not allowed`);
|
|
@@ -1010,10 +1107,14 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1010
1107
|
} else {
|
|
1011
1108
|
formData.append("file", file);
|
|
1012
1109
|
}
|
|
1110
|
+
const uploadHeaders = { "Content-Type": null };
|
|
1111
|
+
if (this.accessToken) {
|
|
1112
|
+
uploadHeaders.Authorization = `Bearer ${this.accessToken}`;
|
|
1113
|
+
}
|
|
1013
1114
|
const res = await this.client.post(
|
|
1014
1115
|
"/api/v1/issue-reports/attachments",
|
|
1015
1116
|
formData,
|
|
1016
|
-
|
|
1117
|
+
{ headers: uploadHeaders }
|
|
1017
1118
|
);
|
|
1018
1119
|
return this.unwrapApiResponse(res, "Failed to upload issue report attachment");
|
|
1019
1120
|
},
|
|
@@ -1496,6 +1597,9 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1496
1597
|
});
|
|
1497
1598
|
this.client.interceptors.request.use((config2) => {
|
|
1498
1599
|
config2.headers = config2.headers || {};
|
|
1600
|
+
if (typeof FormData !== "undefined" && config2.data instanceof FormData) {
|
|
1601
|
+
_SPAPSClient.deleteHeader(config2.headers, "Content-Type");
|
|
1602
|
+
}
|
|
1499
1603
|
this.applyCustomHeaders(config2.headers);
|
|
1500
1604
|
if (this.apiKey && !_SPAPSClient.hasHeader(config2.headers, "X-API-Key")) {
|
|
1501
1605
|
_SPAPSClient.setHeader(config2.headers, "X-API-Key", this.apiKey);
|
|
@@ -1508,15 +1612,18 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1508
1612
|
this.client.interceptors.response.use(
|
|
1509
1613
|
(response) => response,
|
|
1510
1614
|
async (error) => {
|
|
1511
|
-
|
|
1615
|
+
const cfg = error.config || {};
|
|
1616
|
+
const requestUrl = cfg.url || "";
|
|
1617
|
+
const shouldAttemptRefresh = error.response?.status === 401 && !!this.refreshToken && !cfg.__isRetry && !requestUrl.includes("/api/auth/refresh") && !!error.config;
|
|
1618
|
+
if (shouldAttemptRefresh) {
|
|
1512
1619
|
try {
|
|
1513
1620
|
const { data } = await this.refresh(this.refreshToken);
|
|
1514
1621
|
this.accessToken = data.access_token;
|
|
1515
1622
|
this.refreshToken = data.refresh_token;
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1623
|
+
cfg.__isRetry = true;
|
|
1624
|
+
cfg.headers = cfg.headers || {};
|
|
1625
|
+
_SPAPSClient.setHeader(cfg.headers, "Authorization", `Bearer ${this.accessToken}`);
|
|
1626
|
+
return this.client.request(cfg);
|
|
1520
1627
|
} catch (refreshError) {
|
|
1521
1628
|
this.accessToken = void 0;
|
|
1522
1629
|
this.refreshToken = void 0;
|
|
@@ -1559,18 +1666,21 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1559
1666
|
email,
|
|
1560
1667
|
password
|
|
1561
1668
|
});
|
|
1562
|
-
this.
|
|
1563
|
-
this.
|
|
1564
|
-
|
|
1669
|
+
const data = this.extractAuthResponse(response, "Login failed");
|
|
1670
|
+
this.accessToken = data.access_token;
|
|
1671
|
+
this.refreshToken = data.refresh_token;
|
|
1672
|
+
return { data };
|
|
1565
1673
|
}
|
|
1566
1674
|
async register(email, password) {
|
|
1567
1675
|
const response = await this.client.post("/api/auth/register", {
|
|
1568
1676
|
email,
|
|
1569
1677
|
password
|
|
1570
1678
|
});
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1679
|
+
const data = this.normalizeAuthPayload(
|
|
1680
|
+
this.extractAuthResponse(response, "Registration failed")
|
|
1681
|
+
);
|
|
1682
|
+
this.storeAuthTokens(data);
|
|
1683
|
+
return { data };
|
|
1574
1684
|
}
|
|
1575
1685
|
async walletSignIn(walletAddress, signature, message, chainType = "solana") {
|
|
1576
1686
|
const response = await this.client.post("/api/auth/wallet-sign-in", {
|
|
@@ -1579,17 +1689,19 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1579
1689
|
message,
|
|
1580
1690
|
chain_type: chainType
|
|
1581
1691
|
});
|
|
1582
|
-
this.
|
|
1583
|
-
this.
|
|
1584
|
-
|
|
1692
|
+
const data = this.extractAuthResponse(response, "Wallet sign-in failed");
|
|
1693
|
+
this.accessToken = data.access_token;
|
|
1694
|
+
this.refreshToken = data.refresh_token;
|
|
1695
|
+
return { data };
|
|
1585
1696
|
}
|
|
1586
1697
|
async refresh(refreshToken) {
|
|
1587
1698
|
const response = await this.client.post("/api/auth/refresh", {
|
|
1588
1699
|
refresh_token: refreshToken || this.refreshToken
|
|
1589
1700
|
});
|
|
1590
|
-
this.
|
|
1591
|
-
this.
|
|
1592
|
-
|
|
1701
|
+
const data = this.extractAuthResponse(response, "Token refresh failed");
|
|
1702
|
+
this.accessToken = data.access_token;
|
|
1703
|
+
this.refreshToken = data.refresh_token;
|
|
1704
|
+
return { data };
|
|
1593
1705
|
}
|
|
1594
1706
|
async logout() {
|
|
1595
1707
|
await this.client.post("/api/auth/logout");
|
|
@@ -1597,7 +1709,9 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1597
1709
|
this.refreshToken = void 0;
|
|
1598
1710
|
}
|
|
1599
1711
|
async getUser() {
|
|
1600
|
-
|
|
1712
|
+
const res = await this.client.get("/api/auth/user");
|
|
1713
|
+
const payload = this.unwrapApiResponse(res, "Failed to get user");
|
|
1714
|
+
return { data: payload.user };
|
|
1601
1715
|
}
|
|
1602
1716
|
async getSessionContext() {
|
|
1603
1717
|
const res = await this.client.get("/api/auth/session-context", this.authConfig());
|
|
@@ -1676,12 +1790,7 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
1676
1790
|
},
|
|
1677
1791
|
register: async (payload) => {
|
|
1678
1792
|
const res = await this.client.post("/api/auth/register", payload);
|
|
1679
|
-
|
|
1680
|
-
if (body?.success === false) throw new Error(body?.error?.message || "Register failed");
|
|
1681
|
-
const data = body?.data ?? body;
|
|
1682
|
-
this.accessToken = data.access_token;
|
|
1683
|
-
this.refreshToken = data.refresh_token;
|
|
1684
|
-
return data;
|
|
1793
|
+
return this.unwrapAuthMethodResponse(res, "Register failed");
|
|
1685
1794
|
},
|
|
1686
1795
|
/**
|
|
1687
1796
|
* Verify a magic link token and authenticate the user.
|
|
@@ -2483,15 +2592,25 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
2483
2592
|
}
|
|
2484
2593
|
async getSubscription(subscriptionId) {
|
|
2485
2594
|
if (subscriptionId) {
|
|
2486
|
-
|
|
2595
|
+
const res = await this.client.get(
|
|
2596
|
+
`/api/stripe/subscription/${subscriptionId}`
|
|
2597
|
+
);
|
|
2598
|
+
return { data: this.unwrapApiResponse(res, "Failed to get subscription") };
|
|
2487
2599
|
}
|
|
2488
|
-
|
|
2600
|
+
const response = await this.client.get(
|
|
2601
|
+
"/api/stripe/subscriptions"
|
|
2602
|
+
);
|
|
2603
|
+
return { data: response.data?.subscriptions ?? [] };
|
|
2489
2604
|
}
|
|
2490
2605
|
async cancelSubscription(subscriptionId, options = {}) {
|
|
2491
2606
|
if (!subscriptionId) {
|
|
2492
2607
|
throw new Error("subscriptionId is required to cancel a subscription.");
|
|
2493
2608
|
}
|
|
2494
|
-
|
|
2609
|
+
const res = await this.client.post(
|
|
2610
|
+
`/api/stripe/subscription/${subscriptionId}/cancel`,
|
|
2611
|
+
options
|
|
2612
|
+
);
|
|
2613
|
+
return { data: this.unwrapApiResponse(res, "Failed to cancel subscription") };
|
|
2495
2614
|
}
|
|
2496
2615
|
// Usage Methods
|
|
2497
2616
|
async authorizeUsage(payload) {
|
|
@@ -2534,7 +2653,8 @@ var SPAPSClient = class _SPAPSClient {
|
|
|
2534
2653
|
template_key: options.templateKey,
|
|
2535
2654
|
to: options.to,
|
|
2536
2655
|
context: options.context,
|
|
2537
|
-
...options.userId && { user_id: options.userId }
|
|
2656
|
+
...options.userId && { user_id: options.userId },
|
|
2657
|
+
...options.idempotencyKey && { idempotency_key: options.idempotencyKey }
|
|
2538
2658
|
});
|
|
2539
2659
|
return this.unwrapApiResponse(response, "Failed to send email");
|
|
2540
2660
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spaps-sdk",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.2",
|
|
4
4
|
"description": "Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking, role-based access control, and dayrate scheduling",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"axios": "^1.15.1",
|
|
53
53
|
"cross-fetch": "^4.0.0",
|
|
54
|
-
"spaps-types": "^1.5.
|
|
54
|
+
"spaps-types": "^1.5.2"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^20.10.0",
|