@gravity-rail/sdk 0.50.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-interaction-CmGKKlnL.d.ts → chat-interaction-CklXa8iQ.d.cts} +390 -215
- package/dist/{chat-interaction-THDGF4ko.d.cts → chat-interaction-Dei_j8Sd.d.ts} +390 -215
- package/dist/{chunk-QRZVXPR4.js → chunk-6F4YJJKQ.js} +21 -6
- package/dist/chunk-6F4YJJKQ.js.map +1 -0
- package/dist/{chunk-TGL72KPS.js → chunk-GCN4OMYU.js} +3 -3
- package/dist/chunk-GCN4OMYU.js.map +1 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +4 -4
- package/dist/helpers.d.ts +4 -4
- package/dist/helpers.js +1 -1
- package/dist/index.cjs +21 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +2 -2
- package/dist/internal.cjs +21 -6
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +12 -6
- package/dist/internal.d.ts +12 -6
- package/dist/internal.js +2 -2
- package/dist/{member-import-9zHkUjcn.d.cts → member-import-BtTWfQ46.d.cts} +1 -1
- package/dist/{member-import-ocJ_K474.d.ts → member-import-Do6D2g14.d.ts} +1 -1
- package/dist/{path-DZbDwvpS.d.cts → path-D1ZCbboc.d.cts} +5 -5
- package/dist/{path-BQgkft9c.d.ts → path-DKPLfZj4.d.ts} +5 -5
- package/dist/schemas.cjs +34 -1
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +1677 -1677
- package/dist/schemas.d.ts +1677 -1677
- package/dist/schemas.js +33 -1
- package/dist/schemas.js.map +1 -1
- package/dist/{tasks-DsNsbco5.d.cts → tasks-JweebIce.d.cts} +171 -58
- package/dist/{tasks-DsNsbco5.d.ts → tasks-JweebIce.d.ts} +171 -58
- package/package.json +1 -1
- package/dist/chunk-QRZVXPR4.js.map +0 -1
- package/dist/chunk-TGL72KPS.js.map +0 -1
|
@@ -202,7 +202,7 @@ var BaseApiClient = class {
|
|
|
202
202
|
this.enhancedAuthInProgress = this.enhancedAuthHandler();
|
|
203
203
|
try {
|
|
204
204
|
const newToken = await this.enhancedAuthInProgress;
|
|
205
|
-
this.apiKey = newToken;
|
|
205
|
+
if (this.apiKey) this.apiKey = newToken;
|
|
206
206
|
} finally {
|
|
207
207
|
this.enhancedAuthInProgress = null;
|
|
208
208
|
}
|
|
@@ -313,7 +313,7 @@ ${formattedErrors}`, response.status);
|
|
|
313
313
|
this.enhancedAuthInProgress = this.enhancedAuthHandler();
|
|
314
314
|
try {
|
|
315
315
|
const newToken = await this.enhancedAuthInProgress;
|
|
316
|
-
this.apiKey = newToken;
|
|
316
|
+
if (this.apiKey) this.apiKey = newToken;
|
|
317
317
|
} finally {
|
|
318
318
|
this.enhancedAuthInProgress = null;
|
|
319
319
|
}
|
|
@@ -446,4 +446,4 @@ export {
|
|
|
446
446
|
BaseApiClient,
|
|
447
447
|
ChatInteractionController
|
|
448
448
|
};
|
|
449
|
-
//# sourceMappingURL=chunk-
|
|
449
|
+
//# sourceMappingURL=chunk-GCN4OMYU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/base.ts","../src/helpers/chat-interaction.ts"],"sourcesContent":["// Utility function to read a specific cookie value\nexport function getCookieValue(name: string): string | undefined {\n if (typeof document === 'undefined') return undefined;\n\n const cookies = document.cookie.split(';');\n for (const cookie of cookies) {\n const trimmedCookie = cookie.trim();\n const eqIndex = trimmedCookie.indexOf('=');\n if (eqIndex === -1) continue;\n\n const cookieName = trimmedCookie.substring(0, eqIndex);\n const cookieValue = trimmedCookie.substring(eqIndex + 1);\n\n if (cookieName === name) {\n return cookieValue;\n }\n }\n\n return undefined;\n}\n\n/**\n * Structured validation error returned by the backend's workflow-expression\n * validator (CEL / Jinja schema checks). See WorkflowValidationErrorResponse\n * in workflowValidationTypes.ts for the public-facing shape.\n */\nexport interface ApiValidationError {\n field: string;\n kind: 'cel_syntax' | 'cel_schema' | 'jinja_syntax' | 'jinja_schema';\n message: string;\n offendingPath: string | null;\n location: {\n start: number;\n end: number;\n line: number;\n column: number;\n length: number;\n } | null;\n suggestion: string | null;\n}\n\nfunction isApiValidationError(value: unknown): value is ApiValidationError {\n if (!value || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.field === 'string' && typeof v.kind === 'string' && typeof v.message === 'string'\n );\n}\n\n/** Browser-console sink for API failures. A fixed event name only — never the\n * error object, its ``name``, or response-derived ``message``/``errorData``. */\nfunction logApiClientError(_error: unknown): void {\n console.warn('API request failed');\n}\n\nexport class ApiError extends Error {\n statusCode: number;\n /** Legacy string-array errors — kept for non-validation callers. */\n errors?: string[];\n /**\n * Structured workflow-expression validation errors (CEL/Jinja).\n * Populated when the backend returns a WorkflowValidationErrorResponse.\n */\n validationErrors?: ApiValidationError[];\n /**\n * The raw structured `detail` object when the backend returned one (a body\n * with `error: true` or a `code`). Declared here rather than bolted on with\n * `as any` at the throw site so consumers can narrow it with a type guard —\n * see `isMemberContactConflict` in `types/member-types`.\n */\n errorData?: unknown;\n\n constructor(\n message: string,\n statusCode: number,\n errors?: string[],\n validationErrors?: ApiValidationError[]\n ) {\n super(message);\n this.name = 'ApiError';\n this.statusCode = statusCode;\n this.errors = errors;\n this.validationErrors = validationErrors;\n }\n}\n\n/**\n * Handler for enhanced authentication requirement (RFC 9470 Step-Up Authentication).\n * Called when a 401 response includes WWW-Authenticate header with\n * error=\"insufficient_user_authentication\".\n * Should return a promise that resolves with the new auth token after re-auth.\n */\nexport type EnhancedAuthHandler = () => Promise<string>;\n\n/**\n * Info parsed from WWW-Authenticate header for org auth requirement.\n */\nexport interface OrgAuthInfo {\n orgUuid: string;\n authUrl: string;\n}\n\n/**\n * Handler for organization authentication requirement.\n * Called when a 401 response includes WWW-Authenticate header with realm=\"org\".\n * The handler should redirect the user to the auth URL for consent/SSO.\n * Returns a promise that resolves when the user should retry (after page reload with cookie).\n */\nexport type OrgAuthHandler = (info: OrgAuthInfo) => Promise<void>;\n\n/**\n * Check if a WWW-Authenticate header indicates step-up authentication is required.\n * Per RFC 9470, this is indicated by error=\"insufficient_user_authentication\".\n */\nfunction requiresStepUpAuth(wwwAuthenticate: string | null): boolean {\n if (!wwwAuthenticate) return false;\n return wwwAuthenticate.includes('error=\"insufficient_user_authentication\"');\n}\n\n/**\n * Parse org auth info from WWW-Authenticate header.\n * Returns null if this is not an org auth challenge.\n * Expected format: Bearer realm=\"org\", org_uuid=\"...\", auth_url=\"...\"\n * (the challenge names no cookie: a browser's org session is an httpOnly\n * cookie on its own host, and a bearer client sends `X-Org-Session`).\n *\n * Exported so non-SDK fetch paths (e.g. the app's same-origin file-content\n * proxy fetches) can detect the same challenge and trigger the shared org\n * step-up flow.\n */\nexport function parseOrgAuthChallenge(wwwAuthenticate: string | null): OrgAuthInfo | null {\n if (!wwwAuthenticate) return null;\n if (!wwwAuthenticate.includes('realm=\"org\"')) return null;\n\n // Parse the key-value pairs from the header\n const orgUuidMatch = wwwAuthenticate.match(/org_uuid=\"([^\"]+)\"/);\n const authUrlMatch = wwwAuthenticate.match(/auth_url=\"([^\"]+)\"/);\n\n if (!orgUuidMatch?.[1] || !authUrlMatch?.[1]) {\n // Don't log the raw header value — it embeds org_uuid + auth_url.\n console.warn('Invalid org auth challenge header: missing required fields');\n return null;\n }\n\n return {\n orgUuid: orgUuidMatch[1],\n authUrl: authUrlMatch[1],\n };\n}\n\nfunction orgAuthHeaderName(token: string): 'X-Org-Token' | 'X-Org-Session' {\n return token.startsWith('gr_sess_') ? 'X-Org-Session' : 'X-Org-Token';\n}\n\nfunction setOrgAuthHeader(headers: Headers, token: string | undefined): void {\n if (!token) return;\n headers.set(orgAuthHeaderName(token), token);\n}\n\n/**\n * Transport and credential for every request the client makes.\n *\n * - **Bearer** (`apiKey` set): `Authorization: Bearer <apiKey>` on every\n * request, no cookies. This is how non-browser consumers (the CLI, servers,\n * API keys) and bearer-holding browser runtimes (widget iframes) call the API.\n * - **Same-origin session** (no `apiKey`): no `Authorization` header and\n * `credentials: 'same-origin'`. The browser presents the host's httpOnly\n * session cookie, and only to the origin the page is on; the client never\n * reads a cookie to build a credential. A browser app points `baseUrl` at its\n * own origin, which serves `/api/v2` (Gravity Rail's edge on full-account\n * hosts, the frontend's proxy on Site hosts).\n *\n * The org-auth challenge (`WWW-Authenticate: Bearer realm=\"org\"`) is answered by\n * `setOrgAuthHandler`. A bearer client's handler hands back the org session with\n * `setOrgSession`, which is sent as `X-Org-Session`; in same-origin session\n * mode the org session is an httpOnly cookie on the page's origin, so the\n * request is retried once after the handler and the browser presents it.\n */\nexport class BaseApiClient {\n protected baseUrl: string;\n protected apiKey: string | undefined;\n protected isCrossDomain: boolean;\n protected fetchFunction: typeof fetch;\n private enhancedAuthHandler?: EnhancedAuthHandler;\n private enhancedAuthInProgress: Promise<string> | null = null;\n private orgAuthHandler?: OrgAuthHandler;\n private orgAuthInProgress: Promise<void> | null = null;\n private orgSession?: string;\n /**\n * The org_uuid that issued the current org auth credential. Tracked so we can detect\n * a 401 challenge from a *different* org and re-fire the handler — the\n * credential is opaque to the client, so without this the original guard\n * `!this.orgSession` would prevent re-auth across orgs and surface as a silent\n * 401 in multi-org CLI workflows.\n */\n private orgSessionOrgUuid?: string;\n\n constructor(apiKey: string | undefined, baseUrl: string, customFetch?: typeof fetch) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n // console.log('[BaseApiClient] constructor - baseUrl:', baseUrl);\n // Bind fetch to its proper context (window in browser, global in Node)\n this.fetchFunction =\n customFetch || (typeof window !== 'undefined' ? fetch.bind(window) : fetch);\n\n // Determine if we're making cross-domain requests by comparing current origin to API origin\n const apiUrlParsed = new URL(baseUrl);\n const currentOrigin = typeof window !== 'undefined' ? window.location.origin : '';\n this.isCrossDomain = currentOrigin !== '' && currentOrigin !== apiUrlParsed.origin;\n // console.log('[BaseApiClient] isCrossDomain:', this.isCrossDomain, 'currentOrigin:', currentOrigin, 'apiOrigin:', apiUrlParsed.origin);\n }\n\n /**\n * Set the handler for enhanced authentication requirements (RFC 9470 Step-Up Auth).\n * This handler is called when a 401 response includes WWW-Authenticate header\n * with error=\"insufficient_user_authentication\".\n */\n setEnhancedAuthHandler(handler: EnhancedAuthHandler | undefined) {\n this.enhancedAuthHandler = handler;\n }\n\n /**\n * Set the handler for organization authentication requirements.\n * This handler is called when a 401 response includes WWW-Authenticate header\n * with realm=\"org\". The handler should redirect the user to complete org auth.\n */\n setOrgAuthHandler(handler: OrgAuthHandler | undefined) {\n this.orgAuthHandler = handler;\n }\n\n /**\n * Update the API key (used after successful re-authentication).\n */\n updateApiKey(newApiKey: string) {\n this.apiKey = newApiKey;\n }\n\n /**\n * Set the opaque org session credential a bearer client sends as `X-Org-Session`.\n * Opaque gr_sess_ values use X-Org-Session. Legacy orgToken JWE values still\n * use X-Org-Token through setOrgToken() during the migration window.\n *\n * `orgUuid` is the org that issued the credential; passing it lets the 401\n * handler detect a stale session when a subsequent request targets a\n * different org and re-fire the org-auth handler instead of silently\n * 401-ing. Callers without that context (legacy / unknown) may omit it.\n */\n setOrgSession(session: string | undefined, orgUuid?: string) {\n this.orgSession = session;\n this.orgSessionOrgUuid = session ? orgUuid : undefined;\n }\n\n /**\n * @deprecated Legacy compatibility for stateless orgToken JWE credentials.\n * Prefer setOrgSession() for opaque org browser sessions.\n */\n setOrgToken(token: string | undefined, orgUuid?: string) {\n this.setOrgSession(token, orgUuid);\n }\n\n /**\n * The `credentials` a request carries: none for a bearer client (the fetch\n * default), the page's own-origin cookies for a same-origin session.\n */\n private credentialsInit(): Pick<RequestInit, 'credentials'> {\n return this.apiKey ? {} : { credentials: 'same-origin' };\n }\n\n /**\n * Run the org-auth handler once for concurrent challenges. A bearer client's\n * handler sets the org session with `setOrgSession`; a same-origin session's\n * handler leaves an httpOnly org-session cookie on the page's origin instead,\n * which the retry presents. A bearer client whose handler issued nothing has\n * nothing to retry with.\n */\n private async answerOrgAuthChallenge(orgAuthInfo: OrgAuthInfo): Promise<void> {\n const handler = this.orgAuthHandler;\n if (!handler) return;\n if (!this.orgAuthInProgress) {\n this.orgAuthInProgress = (async () => {\n try {\n await handler(orgAuthInfo);\n } finally {\n this.orgAuthInProgress = null;\n }\n })();\n }\n await this.orgAuthInProgress;\n if (this.orgSession) {\n this.orgSessionOrgUuid = orgAuthInfo.orgUuid;\n return;\n }\n if (this.apiKey) {\n throw new ApiError('Organization authentication did not issue a session', 401);\n }\n }\n\n protected async fetch<T>(\n endpoint: string,\n options?: RequestInit,\n orgAuthRetry = false\n ): Promise<T> {\n try {\n const headers = new Headers({\n // Don't set Content-Type for FormData — the browser must set it\n // with the multipart boundary automatically.\n ...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n // console.warn(\"Calling with apiKey:\", this.apiKey)\n const fullUrl = `${this.baseUrl}/api/v2/${endpoint}`;\n // console.log('[BaseApiClient] fetch - URL:', fullUrl, 'baseUrl:', this.baseUrl, 'endpoint:', endpoint);\n\n const response = await this.fetchFunction(fullUrl, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n // Check for step-up auth requirement per RFC 9470 (superuser 2FA re-auth needed)\n if (\n response.status === 401 &&\n requiresStepUpAuth(response.headers.get('WWW-Authenticate')) &&\n this.enhancedAuthHandler\n ) {\n // If re-auth is already in progress, wait for it\n if (this.enhancedAuthInProgress) {\n await this.enhancedAuthInProgress;\n } else {\n // Trigger re-auth and wait for completion\n this.enhancedAuthInProgress = this.enhancedAuthHandler();\n try {\n const newToken = await this.enhancedAuthInProgress;\n if (this.apiKey) this.apiKey = newToken;\n } finally {\n this.enhancedAuthInProgress = null;\n }\n }\n // Retry the request with the new token\n try {\n return await this.fetch<T>(endpoint, options);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n\n if (response.status === 401) {\n const orgAuthInfo = parseOrgAuthChallenge(response.headers.get('WWW-Authenticate'));\n // If the challenge targets a different org than our current\n // token, the token is stale (e.g. switching from Org A to Org\n // B mid-session). Clear it so the handler re-fires below.\n if (\n orgAuthInfo &&\n this.orgSession &&\n this.orgSessionOrgUuid &&\n this.orgSessionOrgUuid !== orgAuthInfo.orgUuid\n ) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n }\n if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {\n await this.answerOrgAuthChallenge(orgAuthInfo);\n try {\n return await this.fetch<T>(endpoint, options, true);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n }\n\n const errorData = await response.json().catch(() => ({}));\n\n // Handle unauthorized errors for account endpoint\n if (response.status === 401 && endpoint === 'account') {\n throw new Error('unauthorized');\n }\n\n // Handle structured validation errors (Pydantic array format)\n if (Array.isArray(errorData.detail)) {\n const formattedErrors = errorData.detail\n .map((error: any) => {\n const location = error.loc.join(' > ');\n return `${location}: ${error.msg}`;\n })\n .join('\\n');\n throw new ApiError(`Validation Error:\\n${formattedErrors}`, response.status);\n }\n\n // Handle validation errors with errors array (new format)\n if (errorData.errors && Array.isArray(errorData.errors)) {\n // Structured workflow-expression validation errors — objects with\n // { field, kind, message, offendingPath, location }. Build a\n // readable message and preserve the structured payload.\n if (errorData.errors.length > 0 && isApiValidationError(errorData.errors[0])) {\n const validationErrors: ApiValidationError[] =\n errorData.errors.filter(isApiValidationError);\n const [first] = validationErrors;\n if (first) {\n const extra =\n validationErrors.length > 1\n ? ` (+${validationErrors.length - 1} more)`\n : '';\n const readable = `${first.field}: ${first.message}${extra}`;\n throw new ApiError(\n readable,\n response.status,\n validationErrors.map((e) => `${e.field}: ${e.message}`),\n validationErrors\n );\n }\n }\n\n // Legacy string-array errors.\n const stringErrors: string[] = errorData.errors.filter(\n (e: unknown): e is string => typeof e === 'string'\n );\n const details = stringErrors.join('; ');\n const message = errorData.detail\n ? `${errorData.detail}: ${details}`\n : `Validation failed: ${details}`;\n throw new ApiError(message, response.status, stringErrors);\n }\n\n // Handle structured errors (with error: true or with code property).\n //\n // TWO body shapes reach here and both carry structure:\n //\n // * `detail` IS the structured object — the member/account contact\n // conflicts build their whole payload as `HTTPException.detail`.\n // * `detail` is PROSE and the machine-readable fields sit beside it\n // at the top level. That is what the domain exception handlers\n // emit: `conflict_handler` writes `{detail, code, ...payload}`, so\n // a Journey v2 refusal arrives as\n // `{detail: \"...\", code: \"journey_v2_invalid_topology\", issues: [...]}`.\n //\n // `detail || errorData` picked the prose string for the second shape,\n // which is not an object, so the branch fell through and `code` and\n // `issues` were dropped on the floor — leaving a client unable to\n // tell a definition conflict from a structural refusal, or to point\n // at the resources a validator rejected. Prefer an OBJECT detail and\n // otherwise read the body itself; the message is unchanged in both\n // cases (prose still wins when there is prose).\n const detail = errorData.detail;\n const structuredError = detail && typeof detail === 'object' ? detail : errorData;\n if (\n structuredError &&\n typeof structuredError === 'object' &&\n (structuredError.error === true || structuredError.code)\n ) {\n const message =\n (typeof detail === 'string' && detail) ||\n structuredError.message ||\n structuredError.code ||\n 'Unknown error';\n const error = new ApiError(message, response.status);\n // Attach the full error data for frontend handling\n error.errorData = structuredError;\n throw error;\n }\n\n // Handle simple error messages\n const errorMessage =\n typeof errorData.detail === 'string'\n ? errorData.detail\n : errorData.message || response.statusText;\n const simpleError = new ApiError(errorMessage, response.status);\n // A prose `detail` STRING alongside a top-level `code` is the shape\n // the backend's domain-validation handler produces: it merges an\n // exception's `payload` into the body and keeps `detail`\n // authoritative. That misses the structured branch above, because\n // `errorData.detail || errorData` resolves to the string. Carry the\n // body through so a caller can still branch on the token — the\n // alternative is parsing `detail`, which is deliberately\n // human-facing prose and not a stable contract.\n if (typeof errorData.code === 'string') {\n simpleError.errorData = errorData;\n }\n throw simpleError;\n }\n\n // Handle 204 No Content - no JSON body to parse\n if (response.status === 204) {\n return undefined as any;\n }\n\n return response.json();\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n\n protected async fetchRaw(\n endpoint: string,\n options?: RequestInit,\n allowedErrorStatuses: number[] = [],\n orgAuthRetry = false\n ): Promise<Response> {\n try {\n const headers = new Headers({\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n const response = await this.fetchFunction(`${this.baseUrl}/api/v2/${endpoint}`, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n // Check for step-up auth requirement per RFC 9470 (superuser 2FA re-auth needed)\n if (\n response.status === 401 &&\n requiresStepUpAuth(response.headers.get('WWW-Authenticate')) &&\n this.enhancedAuthHandler\n ) {\n // If re-auth is already in progress, wait for it\n if (this.enhancedAuthInProgress) {\n await this.enhancedAuthInProgress;\n } else {\n // Trigger re-auth and wait for completion\n this.enhancedAuthInProgress = this.enhancedAuthHandler();\n try {\n const newToken = await this.enhancedAuthInProgress;\n if (this.apiKey) this.apiKey = newToken;\n } finally {\n this.enhancedAuthInProgress = null;\n }\n }\n // Retry the request with the new token\n try {\n return await this.fetchRaw(endpoint, options, allowedErrorStatuses);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n\n // Mirror of the org-auth branch in fetch() above; see comments there.\n if (response.status === 401) {\n const orgAuthInfo = parseOrgAuthChallenge(response.headers.get('WWW-Authenticate'));\n if (\n orgAuthInfo &&\n this.orgSession &&\n this.orgSessionOrgUuid &&\n this.orgSessionOrgUuid !== orgAuthInfo.orgUuid\n ) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n }\n if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {\n await this.answerOrgAuthChallenge(orgAuthInfo);\n try {\n return await this.fetchRaw(endpoint, options, allowedErrorStatuses, true);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n }\n\n if (allowedErrorStatuses.includes(response.status)) {\n return response;\n }\n\n const errorText = await response.text().catch(() => '');\n throw new ApiError(errorText || response.statusText, response.status);\n }\n\n return response;\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n\n /**\n * Fetch from root path (not prefixed with /api/v2/)\n * Used for OAuth and other non-API endpoints\n */\n protected async fetchRoot<T>(path: string, options?: RequestInit): Promise<T> {\n try {\n const headers = new Headers({\n ...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n const fullUrl = `${this.baseUrl}/${path}`;\n\n const response = await this.fetchFunction(fullUrl, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null as T;\n }\n const errorData = await response.json().catch(() => ({}));\n const errorMessage = errorData.detail || errorData.message || response.statusText;\n throw new ApiError(errorMessage, response.status);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json();\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n}\n","import type { ChatsApi } from '../api/chats';\nimport type { InteractionMode, InteractionSessionResponse } from '../types/chat-types';\n\ntype InteractionApi = Pick<\n ChatsApi,\n 'createChatInteractionSession' | 'transitionChatInteractionSession'\n>;\n\n/** One client's mode history in one Chat. Create a separate controller per device/session. */\nexport class ChatInteractionController {\n private current: InteractionSessionResponse | undefined;\n private pending: { mode: InteractionMode; requestId: string } | undefined;\n private tail: Promise<unknown> = Promise.resolve();\n private readonly clientSessionId = crypto.randomUUID();\n\n constructor(\n private readonly api: InteractionApi,\n private readonly workspaceId: string,\n private readonly chatId: number\n ) {}\n\n /** Serialize switches and reuse request IDs when a response is lost. */\n ensure(mode: InteractionMode): Promise<InteractionSessionResponse> {\n const operation = this.tail.then(async () => {\n // Resolve an uncertain earlier write before selecting the next mode.\n if (this.pending) await this.applyPending();\n if (this.current?.mode !== mode) {\n this.pending = { mode, requestId: crypto.randomUUID() };\n await this.applyPending();\n }\n if (!this.current) throw new Error('Interaction session unavailable');\n return this.current;\n });\n this.tail = operation.catch(() => undefined);\n return operation;\n }\n\n private async applyPending(): Promise<void> {\n const pending = this.pending;\n if (!pending) return;\n this.current = this.current\n ? await this.api.transitionChatInteractionSession(\n this.workspaceId,\n this.chatId,\n this.current.id,\n pending\n )\n : await this.api.createChatInteractionSession(this.workspaceId, this.chatId, {\n ...pending,\n clientSessionId: this.clientSessionId,\n });\n this.pending = undefined;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/helpers.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/helpers/index.ts","../src/helpers/outbound.ts","../src/api/base.ts","../src/helpers/enrollment.ts","../src/helpers/webhooks.ts","../src/helpers/workspace-export-archive.ts","../src/helpers/chat-interaction.ts","../src/helpers/member-import.ts"],"sourcesContent":["export {\n findMemberByPhone,\n listOutboundPhoneNumbers,\n normalizePhoneLast10,\n resolveOutboundPhoneNumberId,\n resolveOutboundWorkflowId,\n type OutboundPhoneNumberConfigDefaults,\n type OutboundPhoneNumberOption,\n type WorkspacePhoneNumbersPayload,\n} from './outbound';\n\nexport {\n resolveMemberId,\n findOrCreateMemberLabels,\n upsertDataRecordForMember,\n enrollMemberInJourney,\n type MemberRef,\n type EnrollMemberInJourneyOptions,\n type EnrollMemberInJourneyResult,\n} from './enrollment';\n\nexport {\n WEBHOOK_SIGNATURE_HEADER,\n signWebhookPayload,\n verifyWebhookSignature,\n type VerifyWebhookOptions,\n type VerifyWebhookResult,\n} from './webhooks';\n\nexport {\n MAX_EXPORT_JSON_BYTES,\n WORKSPACE_EXPORT_JSON_MEMBER,\n extractWorkspaceExportJson,\n extractZipMember,\n isZipBuffer,\n} from './workspace-export-archive';\n\nexport { ChatInteractionController } from './chat-interaction';\n\nexport { toSafeMemberImportPreview, toSafeMemberImportSummary } from './member-import';\n","import type { GravityRailClient } from '../client/client';\nimport type { Member } from '../types/member-types';\n\nexport interface OutboundPhoneNumberOption {\n id: number;\n number: string;\n name: string | null;\n}\n\nexport interface WorkspacePhoneNumbersPayload {\n phones: OutboundPhoneNumberOption[];\n defaultPhoneNumberId: number | null;\n}\n\n/** App/env fallbacks when resolving outbound phone numbers. */\nexport interface OutboundPhoneNumberConfigDefaults {\n /** e.g. GRAVITY_RAIL_PHONE_NUMBER_ID */\n phoneNumberId?: number;\n /** Explicit selection overriding workspace default */\n requestedPhoneNumberId?: number;\n}\n\n/** Strip to digits and return the last 10 (US-friendly match). */\nexport function normalizePhoneLast10(phone: string): string {\n const digits = phone.replace(/\\D/g, '');\n return digits.length >= 10 ? digits.slice(-10) : digits;\n}\n\nfunction phonesMatchLast10(a: string, b: string): boolean {\n const da = normalizePhoneLast10(a);\n const db = normalizePhoneLast10(b);\n if (!da || !db) return false;\n if (da.length >= 10 && db.length >= 10) {\n return da === db;\n }\n return da === db;\n}\n\nexport async function listOutboundPhoneNumbers(\n client: GravityRailClient,\n workspaceUuid: string\n): Promise<WorkspacePhoneNumbersPayload> {\n const [phones, defaults] = await Promise.all([\n client.getPhoneNumbers(workspaceUuid, { isArchived: false }),\n client.getWorkspaceDefaults(workspaceUuid),\n ]);\n\n const voicePhones = phones\n .filter((phone) => phone.enableVoice && !phone.isArchived)\n .map((phone) => ({\n id: phone.id,\n number: phone.number,\n name: phone.name,\n }));\n\n let defaultPhoneNumberId = defaults.defaultPhoneNumberId ?? null;\n if (\n defaultPhoneNumberId !== null &&\n !voicePhones.some((phone) => phone.id === defaultPhoneNumberId)\n ) {\n defaultPhoneNumberId = voicePhones[0]?.id ?? null;\n }\n if (defaultPhoneNumberId === null) {\n defaultPhoneNumberId = voicePhones[0]?.id ?? null;\n }\n\n return { phones: voicePhones, defaultPhoneNumberId };\n}\n\nexport async function resolveOutboundPhoneNumberId(\n client: GravityRailClient,\n workspaceUuid: string,\n configDefaults: OutboundPhoneNumberConfigDefaults\n): Promise<number> {\n const { phones, defaultPhoneNumberId } = await listOutboundPhoneNumbers(client, workspaceUuid);\n\n if (phones.length === 0) {\n throw new Error(\n 'No voice-enabled phone numbers in this workspace. Add a line in Gravity Rail first.'\n );\n }\n\n const requestedId = configDefaults.requestedPhoneNumberId;\n const candidate =\n requestedId ?? defaultPhoneNumberId ?? configDefaults.phoneNumberId ?? phones[0]?.id;\n\n if (candidate === undefined || !phones.some((phone) => phone.id === candidate)) {\n throw new Error('Selected outbound phone number is not available.');\n }\n\n return candidate;\n}\n\nexport async function resolveOutboundWorkflowId(\n client: GravityRailClient,\n workspaceUuid: string,\n defaultWorkflowId: number,\n override?: number\n): Promise<number> {\n const workflowsPage = await client.getWorkflows(workspaceUuid, { archived: false });\n const options = workflowsPage.items\n .filter((item) => !item.workflow.archived)\n .map((item) => ({\n id: item.workflow.id,\n name: item.workflow.name,\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n if (options.length === 0) {\n throw new Error('No workflows in this workspace. Create a workflow in Gravity Rail first.');\n }\n\n let resolvedDefault = defaultWorkflowId;\n if (!options.some((workflow) => workflow.id === resolvedDefault)) {\n resolvedDefault = options[0]?.id ?? defaultWorkflowId;\n }\n\n const candidate = override ?? resolvedDefault;\n\n if (!options.some((workflow) => workflow.id === candidate)) {\n throw new Error('Selected workflow is not available.');\n }\n\n return candidate;\n}\n\nexport async function findMemberByPhone(\n client: GravityRailClient,\n workspaceUuid: string,\n phone: string\n): Promise<Member | null> {\n const searchDigits = phone.replace(/\\D/g, '');\n\n for (let page = 1; page <= 5; page += 1) {\n const response = await client.getWorkspaceMembers(workspaceUuid, page, 50, searchDigits);\n const match = response.items.find(\n (member) => member.phone && phonesMatchLast10(member.phone, phone)\n );\n if (match) return match;\n\n if (page >= response.totalPages) break;\n }\n\n return null;\n}\n","// Utility function to read a specific cookie value\nexport function getCookieValue(name: string): string | undefined {\n if (typeof document === 'undefined') return undefined;\n\n const cookies = document.cookie.split(';');\n for (const cookie of cookies) {\n const trimmedCookie = cookie.trim();\n const eqIndex = trimmedCookie.indexOf('=');\n if (eqIndex === -1) continue;\n\n const cookieName = trimmedCookie.substring(0, eqIndex);\n const cookieValue = trimmedCookie.substring(eqIndex + 1);\n\n if (cookieName === name) {\n return cookieValue;\n }\n }\n\n return undefined;\n}\n\n/**\n * Structured validation error returned by the backend's workflow-expression\n * validator (CEL / Jinja schema checks). See WorkflowValidationErrorResponse\n * in workflowValidationTypes.ts for the public-facing shape.\n */\nexport interface ApiValidationError {\n field: string;\n kind: 'cel_syntax' | 'cel_schema' | 'jinja_syntax' | 'jinja_schema';\n message: string;\n offendingPath: string | null;\n location: {\n start: number;\n end: number;\n line: number;\n column: number;\n length: number;\n } | null;\n suggestion: string | null;\n}\n\nfunction isApiValidationError(value: unknown): value is ApiValidationError {\n if (!value || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.field === 'string' && typeof v.kind === 'string' && typeof v.message === 'string'\n );\n}\n\n/** Browser-console sink for API failures. A fixed event name only — never the\n * error object, its ``name``, or response-derived ``message``/``errorData``. */\nfunction logApiClientError(_error: unknown): void {\n console.warn('API request failed');\n}\n\nexport class ApiError extends Error {\n statusCode: number;\n /** Legacy string-array errors — kept for non-validation callers. */\n errors?: string[];\n /**\n * Structured workflow-expression validation errors (CEL/Jinja).\n * Populated when the backend returns a WorkflowValidationErrorResponse.\n */\n validationErrors?: ApiValidationError[];\n /**\n * The raw structured `detail` object when the backend returned one (a body\n * with `error: true` or a `code`). Declared here rather than bolted on with\n * `as any` at the throw site so consumers can narrow it with a type guard —\n * see `isMemberContactConflict` in `types/member-types`.\n */\n errorData?: unknown;\n\n constructor(\n message: string,\n statusCode: number,\n errors?: string[],\n validationErrors?: ApiValidationError[]\n ) {\n super(message);\n this.name = 'ApiError';\n this.statusCode = statusCode;\n this.errors = errors;\n this.validationErrors = validationErrors;\n }\n}\n\n/**\n * Handler for enhanced authentication requirement (RFC 9470 Step-Up Authentication).\n * Called when a 401 response includes WWW-Authenticate header with\n * error=\"insufficient_user_authentication\".\n * Should return a promise that resolves with the new auth token after re-auth.\n */\nexport type EnhancedAuthHandler = () => Promise<string>;\n\n/**\n * Info parsed from WWW-Authenticate header for org auth requirement.\n */\nexport interface OrgAuthInfo {\n orgUuid: string;\n authUrl: string;\n}\n\n/**\n * Handler for organization authentication requirement.\n * Called when a 401 response includes WWW-Authenticate header with realm=\"org\".\n * The handler should redirect the user to the auth URL for consent/SSO.\n * Returns a promise that resolves when the user should retry (after page reload with cookie).\n */\nexport type OrgAuthHandler = (info: OrgAuthInfo) => Promise<void>;\n\n/**\n * Check if a WWW-Authenticate header indicates step-up authentication is required.\n * Per RFC 9470, this is indicated by error=\"insufficient_user_authentication\".\n */\nfunction requiresStepUpAuth(wwwAuthenticate: string | null): boolean {\n if (!wwwAuthenticate) return false;\n return wwwAuthenticate.includes('error=\"insufficient_user_authentication\"');\n}\n\n/**\n * Parse org auth info from WWW-Authenticate header.\n * Returns null if this is not an org auth challenge.\n * Expected format: Bearer realm=\"org\", org_uuid=\"...\", auth_url=\"...\"\n * (the challenge names no cookie: a browser's org session is an httpOnly\n * cookie on its own host, and a bearer client sends `X-Org-Session`).\n *\n * Exported so non-SDK fetch paths (e.g. the app's same-origin file-content\n * proxy fetches) can detect the same challenge and trigger the shared org\n * step-up flow.\n */\nexport function parseOrgAuthChallenge(wwwAuthenticate: string | null): OrgAuthInfo | null {\n if (!wwwAuthenticate) return null;\n if (!wwwAuthenticate.includes('realm=\"org\"')) return null;\n\n // Parse the key-value pairs from the header\n const orgUuidMatch = wwwAuthenticate.match(/org_uuid=\"([^\"]+)\"/);\n const authUrlMatch = wwwAuthenticate.match(/auth_url=\"([^\"]+)\"/);\n\n if (!orgUuidMatch?.[1] || !authUrlMatch?.[1]) {\n // Don't log the raw header value — it embeds org_uuid + auth_url.\n console.warn('Invalid org auth challenge header: missing required fields');\n return null;\n }\n\n return {\n orgUuid: orgUuidMatch[1],\n authUrl: authUrlMatch[1],\n };\n}\n\nfunction orgAuthHeaderName(token: string): 'X-Org-Token' | 'X-Org-Session' {\n return token.startsWith('gr_sess_') ? 'X-Org-Session' : 'X-Org-Token';\n}\n\nfunction setOrgAuthHeader(headers: Headers, token: string | undefined): void {\n if (!token) return;\n headers.set(orgAuthHeaderName(token), token);\n}\n\n/**\n * Transport and credential for every request the client makes.\n *\n * - **Bearer** (`apiKey` set): `Authorization: Bearer <apiKey>` on every\n * request, no cookies. This is how non-browser consumers (the CLI, servers,\n * API keys) and bearer-holding browser runtimes (widget iframes) call the API.\n * - **Same-origin session** (no `apiKey`): no `Authorization` header and\n * `credentials: 'same-origin'`. The browser presents the host's httpOnly\n * session cookie, and only to the origin the page is on; the client never\n * reads a cookie to build a credential. A browser app points `baseUrl` at its\n * own origin, which serves `/api/v2` (Gravity Rail's edge on full-account\n * hosts, the frontend's proxy on Site hosts).\n *\n * The org-auth challenge (`WWW-Authenticate: Bearer realm=\"org\"`) is answered by\n * `setOrgAuthHandler`. A bearer client's handler hands back the org session with\n * `setOrgSession`, which is sent as `X-Org-Session`; in same-origin session\n * mode the org session is an httpOnly cookie on the page's origin, so the\n * request is retried once after the handler and the browser presents it.\n */\nexport class BaseApiClient {\n protected baseUrl: string;\n protected apiKey: string | undefined;\n protected isCrossDomain: boolean;\n protected fetchFunction: typeof fetch;\n private enhancedAuthHandler?: EnhancedAuthHandler;\n private enhancedAuthInProgress: Promise<string> | null = null;\n private orgAuthHandler?: OrgAuthHandler;\n private orgAuthInProgress: Promise<void> | null = null;\n private orgSession?: string;\n /**\n * The org_uuid that issued the current org auth credential. Tracked so we can detect\n * a 401 challenge from a *different* org and re-fire the handler — the\n * credential is opaque to the client, so without this the original guard\n * `!this.orgSession` would prevent re-auth across orgs and surface as a silent\n * 401 in multi-org CLI workflows.\n */\n private orgSessionOrgUuid?: string;\n\n constructor(apiKey: string | undefined, baseUrl: string, customFetch?: typeof fetch) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n // console.log('[BaseApiClient] constructor - baseUrl:', baseUrl);\n // Bind fetch to its proper context (window in browser, global in Node)\n this.fetchFunction =\n customFetch || (typeof window !== 'undefined' ? fetch.bind(window) : fetch);\n\n // Determine if we're making cross-domain requests by comparing current origin to API origin\n const apiUrlParsed = new URL(baseUrl);\n const currentOrigin = typeof window !== 'undefined' ? window.location.origin : '';\n this.isCrossDomain = currentOrigin !== '' && currentOrigin !== apiUrlParsed.origin;\n // console.log('[BaseApiClient] isCrossDomain:', this.isCrossDomain, 'currentOrigin:', currentOrigin, 'apiOrigin:', apiUrlParsed.origin);\n }\n\n /**\n * Set the handler for enhanced authentication requirements (RFC 9470 Step-Up Auth).\n * This handler is called when a 401 response includes WWW-Authenticate header\n * with error=\"insufficient_user_authentication\".\n */\n setEnhancedAuthHandler(handler: EnhancedAuthHandler | undefined) {\n this.enhancedAuthHandler = handler;\n }\n\n /**\n * Set the handler for organization authentication requirements.\n * This handler is called when a 401 response includes WWW-Authenticate header\n * with realm=\"org\". The handler should redirect the user to complete org auth.\n */\n setOrgAuthHandler(handler: OrgAuthHandler | undefined) {\n this.orgAuthHandler = handler;\n }\n\n /**\n * Update the API key (used after successful re-authentication).\n */\n updateApiKey(newApiKey: string) {\n this.apiKey = newApiKey;\n }\n\n /**\n * Set the opaque org session credential a bearer client sends as `X-Org-Session`.\n * Opaque gr_sess_ values use X-Org-Session. Legacy orgToken JWE values still\n * use X-Org-Token through setOrgToken() during the migration window.\n *\n * `orgUuid` is the org that issued the credential; passing it lets the 401\n * handler detect a stale session when a subsequent request targets a\n * different org and re-fire the org-auth handler instead of silently\n * 401-ing. Callers without that context (legacy / unknown) may omit it.\n */\n setOrgSession(session: string | undefined, orgUuid?: string) {\n this.orgSession = session;\n this.orgSessionOrgUuid = session ? orgUuid : undefined;\n }\n\n /**\n * @deprecated Legacy compatibility for stateless orgToken JWE credentials.\n * Prefer setOrgSession() for opaque org browser sessions.\n */\n setOrgToken(token: string | undefined, orgUuid?: string) {\n this.setOrgSession(token, orgUuid);\n }\n\n /**\n * The `credentials` a request carries: none for a bearer client (the fetch\n * default), the page's own-origin cookies for a same-origin session.\n */\n private credentialsInit(): Pick<RequestInit, 'credentials'> {\n return this.apiKey ? {} : { credentials: 'same-origin' };\n }\n\n /**\n * Run the org-auth handler once for concurrent challenges. A bearer client's\n * handler sets the org session with `setOrgSession`; a same-origin session's\n * handler leaves an httpOnly org-session cookie on the page's origin instead,\n * which the retry presents. A bearer client whose handler issued nothing has\n * nothing to retry with.\n */\n private async answerOrgAuthChallenge(orgAuthInfo: OrgAuthInfo): Promise<void> {\n const handler = this.orgAuthHandler;\n if (!handler) return;\n if (!this.orgAuthInProgress) {\n this.orgAuthInProgress = (async () => {\n try {\n await handler(orgAuthInfo);\n } finally {\n this.orgAuthInProgress = null;\n }\n })();\n }\n await this.orgAuthInProgress;\n if (this.orgSession) {\n this.orgSessionOrgUuid = orgAuthInfo.orgUuid;\n return;\n }\n if (this.apiKey) {\n throw new ApiError('Organization authentication did not issue a session', 401);\n }\n }\n\n protected async fetch<T>(\n endpoint: string,\n options?: RequestInit,\n orgAuthRetry = false\n ): Promise<T> {\n try {\n const headers = new Headers({\n // Don't set Content-Type for FormData — the browser must set it\n // with the multipart boundary automatically.\n ...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n // console.warn(\"Calling with apiKey:\", this.apiKey)\n const fullUrl = `${this.baseUrl}/api/v2/${endpoint}`;\n // console.log('[BaseApiClient] fetch - URL:', fullUrl, 'baseUrl:', this.baseUrl, 'endpoint:', endpoint);\n\n const response = await this.fetchFunction(fullUrl, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n // Check for step-up auth requirement per RFC 9470 (superuser 2FA re-auth needed)\n if (\n response.status === 401 &&\n requiresStepUpAuth(response.headers.get('WWW-Authenticate')) &&\n this.enhancedAuthHandler\n ) {\n // If re-auth is already in progress, wait for it\n if (this.enhancedAuthInProgress) {\n await this.enhancedAuthInProgress;\n } else {\n // Trigger re-auth and wait for completion\n this.enhancedAuthInProgress = this.enhancedAuthHandler();\n try {\n const newToken = await this.enhancedAuthInProgress;\n this.apiKey = newToken;\n } finally {\n this.enhancedAuthInProgress = null;\n }\n }\n // Retry the request with the new token\n try {\n return await this.fetch<T>(endpoint, options);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n\n if (response.status === 401) {\n const orgAuthInfo = parseOrgAuthChallenge(response.headers.get('WWW-Authenticate'));\n // If the challenge targets a different org than our current\n // token, the token is stale (e.g. switching from Org A to Org\n // B mid-session). Clear it so the handler re-fires below.\n if (\n orgAuthInfo &&\n this.orgSession &&\n this.orgSessionOrgUuid &&\n this.orgSessionOrgUuid !== orgAuthInfo.orgUuid\n ) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n }\n if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {\n await this.answerOrgAuthChallenge(orgAuthInfo);\n try {\n return await this.fetch<T>(endpoint, options, true);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n }\n\n const errorData = await response.json().catch(() => ({}));\n\n // Handle unauthorized errors for account endpoint\n if (response.status === 401 && endpoint === 'account') {\n throw new Error('unauthorized');\n }\n\n // Handle structured validation errors (Pydantic array format)\n if (Array.isArray(errorData.detail)) {\n const formattedErrors = errorData.detail\n .map((error: any) => {\n const location = error.loc.join(' > ');\n return `${location}: ${error.msg}`;\n })\n .join('\\n');\n throw new ApiError(`Validation Error:\\n${formattedErrors}`, response.status);\n }\n\n // Handle validation errors with errors array (new format)\n if (errorData.errors && Array.isArray(errorData.errors)) {\n // Structured workflow-expression validation errors — objects with\n // { field, kind, message, offendingPath, location }. Build a\n // readable message and preserve the structured payload.\n if (errorData.errors.length > 0 && isApiValidationError(errorData.errors[0])) {\n const validationErrors: ApiValidationError[] =\n errorData.errors.filter(isApiValidationError);\n const [first] = validationErrors;\n if (first) {\n const extra =\n validationErrors.length > 1\n ? ` (+${validationErrors.length - 1} more)`\n : '';\n const readable = `${first.field}: ${first.message}${extra}`;\n throw new ApiError(\n readable,\n response.status,\n validationErrors.map((e) => `${e.field}: ${e.message}`),\n validationErrors\n );\n }\n }\n\n // Legacy string-array errors.\n const stringErrors: string[] = errorData.errors.filter(\n (e: unknown): e is string => typeof e === 'string'\n );\n const details = stringErrors.join('; ');\n const message = errorData.detail\n ? `${errorData.detail}: ${details}`\n : `Validation failed: ${details}`;\n throw new ApiError(message, response.status, stringErrors);\n }\n\n // Handle structured errors (with error: true or with code property).\n //\n // TWO body shapes reach here and both carry structure:\n //\n // * `detail` IS the structured object — the member/account contact\n // conflicts build their whole payload as `HTTPException.detail`.\n // * `detail` is PROSE and the machine-readable fields sit beside it\n // at the top level. That is what the domain exception handlers\n // emit: `conflict_handler` writes `{detail, code, ...payload}`, so\n // a Journey v2 refusal arrives as\n // `{detail: \"...\", code: \"journey_v2_invalid_topology\", issues: [...]}`.\n //\n // `detail || errorData` picked the prose string for the second shape,\n // which is not an object, so the branch fell through and `code` and\n // `issues` were dropped on the floor — leaving a client unable to\n // tell a definition conflict from a structural refusal, or to point\n // at the resources a validator rejected. Prefer an OBJECT detail and\n // otherwise read the body itself; the message is unchanged in both\n // cases (prose still wins when there is prose).\n const detail = errorData.detail;\n const structuredError = detail && typeof detail === 'object' ? detail : errorData;\n if (\n structuredError &&\n typeof structuredError === 'object' &&\n (structuredError.error === true || structuredError.code)\n ) {\n const message =\n (typeof detail === 'string' && detail) ||\n structuredError.message ||\n structuredError.code ||\n 'Unknown error';\n const error = new ApiError(message, response.status);\n // Attach the full error data for frontend handling\n error.errorData = structuredError;\n throw error;\n }\n\n // Handle simple error messages\n const errorMessage =\n typeof errorData.detail === 'string'\n ? errorData.detail\n : errorData.message || response.statusText;\n const simpleError = new ApiError(errorMessage, response.status);\n // A prose `detail` STRING alongside a top-level `code` is the shape\n // the backend's domain-validation handler produces: it merges an\n // exception's `payload` into the body and keeps `detail`\n // authoritative. That misses the structured branch above, because\n // `errorData.detail || errorData` resolves to the string. Carry the\n // body through so a caller can still branch on the token — the\n // alternative is parsing `detail`, which is deliberately\n // human-facing prose and not a stable contract.\n if (typeof errorData.code === 'string') {\n simpleError.errorData = errorData;\n }\n throw simpleError;\n }\n\n // Handle 204 No Content - no JSON body to parse\n if (response.status === 204) {\n return undefined as any;\n }\n\n return response.json();\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n\n protected async fetchRaw(\n endpoint: string,\n options?: RequestInit,\n allowedErrorStatuses: number[] = [],\n orgAuthRetry = false\n ): Promise<Response> {\n try {\n const headers = new Headers({\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n const response = await this.fetchFunction(`${this.baseUrl}/api/v2/${endpoint}`, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n // Check for step-up auth requirement per RFC 9470 (superuser 2FA re-auth needed)\n if (\n response.status === 401 &&\n requiresStepUpAuth(response.headers.get('WWW-Authenticate')) &&\n this.enhancedAuthHandler\n ) {\n // If re-auth is already in progress, wait for it\n if (this.enhancedAuthInProgress) {\n await this.enhancedAuthInProgress;\n } else {\n // Trigger re-auth and wait for completion\n this.enhancedAuthInProgress = this.enhancedAuthHandler();\n try {\n const newToken = await this.enhancedAuthInProgress;\n this.apiKey = newToken;\n } finally {\n this.enhancedAuthInProgress = null;\n }\n }\n // Retry the request with the new token\n try {\n return await this.fetchRaw(endpoint, options, allowedErrorStatuses);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n\n // Mirror of the org-auth branch in fetch() above; see comments there.\n if (response.status === 401) {\n const orgAuthInfo = parseOrgAuthChallenge(response.headers.get('WWW-Authenticate'));\n if (\n orgAuthInfo &&\n this.orgSession &&\n this.orgSessionOrgUuid &&\n this.orgSessionOrgUuid !== orgAuthInfo.orgUuid\n ) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n }\n if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {\n await this.answerOrgAuthChallenge(orgAuthInfo);\n try {\n return await this.fetchRaw(endpoint, options, allowedErrorStatuses, true);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n }\n\n if (allowedErrorStatuses.includes(response.status)) {\n return response;\n }\n\n const errorText = await response.text().catch(() => '');\n throw new ApiError(errorText || response.statusText, response.status);\n }\n\n return response;\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n\n /**\n * Fetch from root path (not prefixed with /api/v2/)\n * Used for OAuth and other non-API endpoints\n */\n protected async fetchRoot<T>(path: string, options?: RequestInit): Promise<T> {\n try {\n const headers = new Headers({\n ...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n const fullUrl = `${this.baseUrl}/${path}`;\n\n const response = await this.fetchFunction(fullUrl, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null as T;\n }\n const errorData = await response.json().catch(() => ({}));\n const errorMessage = errorData.detail || errorData.message || response.statusText;\n throw new ApiError(errorMessage, response.status);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json();\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n}\n","/**\n * Enrollment-flow helpers: the small compound operations every integration\n * re-implements when wiring \"register member → populate a form → enroll into a\n * journey\". They build on the plain client methods and are safe to call from a\n * server-side BFF.\n */\nimport { ApiError } from '../api/base';\nimport type { GravityRailClient } from '../client/client';\nimport type { DataRecord } from '../types/data-types';\nimport { findMemberByPhone } from './outbound';\n\n/** Identify a member by any one of these; `memberId` wins when present. */\nexport interface MemberRef {\n memberId?: number;\n phone?: string;\n externalId?: string;\n}\n\n/** Resolve a `MemberRef` to a concrete member id (throws if none matches). */\nexport async function resolveMemberId(\n client: GravityRailClient,\n workspaceUuid: string,\n ref: MemberRef\n): Promise<number> {\n if (typeof ref.memberId === 'number') {\n return ref.memberId;\n }\n if (ref.phone) {\n const member = await findMemberByPhone(client, workspaceUuid, ref.phone);\n if (member?.id) return member.id;\n // Do not put the phone (PHI) in the message — it propagates to callers.\n throw new Error('No member found for the provided phone number');\n }\n if (ref.externalId) {\n const page = await client.getWorkspaceMembers(workspaceUuid, 1, 25, ref.externalId);\n const match = page.items.find((m) => m.externalId === ref.externalId);\n if (match?.id) return match.id;\n // Do not put the externalId (PHI) in the message — it propagates to callers.\n throw new Error('No member found for the provided externalId');\n }\n throw new Error('Provide memberId, phone, or externalId');\n}\n\n/**\n * Find member labels by name, creating any that don't exist, and return their\n * ids. Case-insensitive match on name. (The API derives the slug from the name.)\n */\nexport async function findOrCreateMemberLabels(\n client: GravityRailClient,\n workspaceUuid: string,\n names: string[],\n options: { color?: string } = {}\n): Promise<number[]> {\n const clean = names.map((n) => n.trim()).filter(Boolean);\n if (clean.length === 0) return [];\n\n const existing = await client.getMemberLabels(workspaceUuid);\n const byName = new Map(existing.map((l) => [l.name.toLowerCase(), l]));\n const ids: number[] = [];\n for (const name of clean) {\n const found = byName.get(name.toLowerCase());\n if (found) {\n ids.push(found.id);\n continue;\n }\n const created = await client.createMemberLabel(workspaceUuid, {\n name,\n color: options.color ?? '#6366f1',\n });\n byName.set(name.toLowerCase(), created);\n ids.push(created.id);\n }\n return ids;\n}\n\n/**\n * Idempotently write a member-scoped data record: update the member's existing\n * record for the DataType if one exists, otherwise create it.\n */\nexport async function upsertDataRecordForMember(\n client: GravityRailClient,\n workspaceUuid: string,\n dataTypeId: number,\n memberId: number,\n fieldValues: Record<string, unknown>\n): Promise<DataRecord> {\n const existing = await client.getDataRecords(workspaceUuid, dataTypeId, {\n memberId,\n pageSize: 1,\n });\n const record = existing.items?.[0];\n const body = { dataTypeId, memberId, fieldValues };\n if (record?.id) {\n return client.updateDataRecord(workspaceUuid, dataTypeId, record.id, body);\n }\n return client.createDataRecord(workspaceUuid, dataTypeId, body);\n}\n\nexport interface EnrollMemberInJourneyOptions extends MemberRef {\n /** Journey to enroll into. Defaults to the workspace's first journey. */\n journeyId?: number;\n /** Labels to apply to the member before enrolling (journey/cohort tags). */\n labels?: string[];\n labelColor?: string;\n}\n\nexport interface EnrollMemberInJourneyResult {\n journeyId: number;\n journeyName?: string;\n memberId: number;\n appliedLabels: string[];\n}\n\n/**\n * Resolve a member, optionally tag them, and enroll them into a journey. Treats\n * an \"already enrolled\" (409) response as success.\n */\nexport async function enrollMemberInJourney(\n client: GravityRailClient,\n workspaceUuid: string,\n options: EnrollMemberInJourneyOptions\n): Promise<EnrollMemberInJourneyResult> {\n const memberId = await resolveMemberId(client, workspaceUuid, options);\n\n const page = await client.getJourneys(workspaceUuid);\n const journeys = page.items ?? [];\n let journey: { id: number; name?: string } | undefined;\n if (options.journeyId != null) {\n journey = journeys.find((j) => j.id === options.journeyId) ?? { id: options.journeyId };\n } else {\n journey = journeys[0];\n }\n if (!journey) {\n throw new Error('No journey to enroll into: pass journeyId or create a journey');\n }\n\n const labelNames = (options.labels ?? []).map((l) => l.trim()).filter(Boolean);\n if (labelNames.length > 0) {\n const labelIds = await findOrCreateMemberLabels(client, workspaceUuid, labelNames, {\n color: options.labelColor,\n });\n // updateMember replaces the label set, so merge with the member's existing\n // labels rather than wiping them.\n const member = await client.getWorkspaceMember(workspaceUuid, memberId);\n const existingIds = (member.labels ?? [])\n .map((l) => l.id)\n .filter((id): id is number => typeof id === 'number');\n const mergedIds = Array.from(new Set([...existingIds, ...labelIds]));\n await client.updateMember(workspaceUuid, memberId, { labelIds: mergedIds });\n }\n\n try {\n await client.enrollMembers(workspaceUuid, journey.id, { memberId });\n } catch (error) {\n if (!(error instanceof ApiError && error.statusCode === 409)) {\n throw error;\n }\n }\n\n return { journeyId: journey.id, journeyName: journey.name, memberId, appliedLabels: labelNames };\n}\n","/**\n * Webhook signature helpers.\n *\n * Gravity Rail signs every outbound webhook with HMAC-SHA256 and sends the\n * result in the `X-Webhook-Signature` header as `t={unixSeconds},v1={hexDigest}`.\n * The signed message is the timestamp, a literal dot, then the raw request body:\n *\n * HMAC_SHA256(secret, `${t}.` + rawBody)\n *\n * The timestamp defeats replay — reject signatures older than the tolerance\n * window. Verify the signature against the RAW request body bytes, before any\n * JSON parsing/re-serialization.\n *\n * These use the Web Crypto API (`crypto.subtle`), so they run unchanged in Node\n * 18+ and the browser/edge runtimes.\n */\n\nexport const WEBHOOK_SIGNATURE_HEADER = 'X-Webhook-Signature';\n\nconst encoder = new TextEncoder();\n\nasync function hmacSha256Hex(secret: string, message: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(message));\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/** Constant-time comparison of two equal-length hex strings. */\nfunction timingSafeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let mismatch = 0;\n for (let i = 0; i < a.length; i += 1) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return mismatch === 0;\n}\n\n/**\n * Produce the `X-Webhook-Signature` header value for a payload. Primarily useful\n * for tests and for re-signing when relaying webhooks.\n */\nexport async function signWebhookPayload(\n rawBody: string,\n secret: string,\n timestampSeconds: number\n): Promise<string> {\n const digest = await hmacSha256Hex(secret, `${timestampSeconds}.${rawBody}`);\n return `t=${timestampSeconds},v1=${digest}`;\n}\n\nexport interface VerifyWebhookOptions {\n /** Max age of the signature in seconds (default 300). */\n toleranceSeconds?: number;\n /** Override \"now\" (unix seconds) — for deterministic tests. */\n nowSeconds?: number;\n}\n\nexport type VerifyWebhookResult = { ok: true } | { ok: false; reason: string };\n\n/**\n * Verify a webhook signature against the raw body. Returns a discriminated\n * result so callers can log the reason; it never throws on a bad signature.\n */\nexport async function verifyWebhookSignature(\n rawBody: string,\n signatureHeader: string | null | undefined,\n secret: string,\n options: VerifyWebhookOptions = {}\n): Promise<VerifyWebhookResult> {\n if (!signatureHeader) {\n return { ok: false, reason: 'missing signature header' };\n }\n\n const parts: Record<string, string> = {};\n for (const segment of signatureHeader.split(',')) {\n const idx = segment.indexOf('=');\n if (idx > 0) {\n parts[segment.slice(0, idx).trim()] = segment.slice(idx + 1).trim();\n }\n }\n\n const timestamp = Number(parts.t);\n const provided = parts.v1;\n if (!Number.isFinite(timestamp) || !provided) {\n return { ok: false, reason: 'malformed signature header' };\n }\n\n const tolerance = options.toleranceSeconds ?? 300;\n const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - timestamp) > tolerance) {\n return { ok: false, reason: 'signature timestamp outside tolerance' };\n }\n\n const expected = await hmacSha256Hex(secret, `${timestamp}.${rawBody}`);\n if (!timingSafeEqualHex(expected, provided)) {\n return { ok: false, reason: 'signature mismatch' };\n }\n\n return { ok: true };\n}\n","/**\n * Helpers for the portable workspace export ZIP.\n *\n * Layout: ``export.json`` plus ``blobs/ab/cd/<sha256>``.\n */\n\nexport const WORKSPACE_EXPORT_JSON_MEMBER = 'export.json';\nexport const MAX_EXPORT_JSON_BYTES = 8 * 1024 * 1024;\n\nconst EOCD_SIGNATURE = 0x06054b50;\nconst CDFH_SIGNATURE = 0x02014b50;\nconst LFH_SIGNATURE = 0x04034b50;\nconst ZIP_STORED = 0;\nconst ZIP_DEFLATED = 8;\n\nfunction requireRange(bytes: Uint8Array, start: number, length: number): void {\n if (start < 0 || length < 0 || start + length > bytes.length) {\n throw new Error('ZIP offsets are out of range.');\n }\n}\n\nfunction u16(bytes: Uint8Array, offset: number): number {\n requireRange(bytes, offset, 2);\n return bytes[offset]! | (bytes[offset + 1]! << 8);\n}\n\nfunction u32(bytes: Uint8Array, offset: number): number {\n requireRange(bytes, offset, 4);\n return (\n (bytes[offset]! |\n (bytes[offset + 1]! << 8) |\n (bytes[offset + 2]! << 16) |\n (bytes[offset + 3]! << 24)) >>>\n 0\n );\n}\n\nexport function isZipBuffer(bytes: Uint8Array): boolean {\n return bytes.length >= 4 && bytes[0] === 0x50 && bytes[1] === 0x4b;\n}\n\nasync function inflateRaw(data: Uint8Array, maxBytes: number): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'function') {\n const stream = new Blob([data as BlobPart])\n .stream()\n .pipeThrough(new DecompressionStream('deflate-raw'));\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel();\n throw new Error('ZIP member exceeds the export.json size limit.');\n }\n chunks.push(value);\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n }\n // Keep the Node-only fallback out of browser bundles. Native decompression\n // above handles browsers; unsupported browsers get a clear capability error.\n if (typeof window !== 'undefined') {\n throw new Error('This browser does not support ZIP decompression.');\n }\n const zlib = await import(/* webpackIgnore: true */ 'node:zlib');\n return Uint8Array.from(zlib.inflateRawSync(data, { maxOutputLength: maxBytes }));\n}\n\nfunction findEocdOffset(bytes: Uint8Array): number {\n const min = 22;\n const maxComment = 65535;\n const start = Math.max(0, bytes.length - (min + maxComment));\n for (let i = bytes.length - min; i >= start; i--) {\n if (u32(bytes, i) !== EOCD_SIGNATURE) continue;\n const commentLength = u16(bytes, i + 20);\n if (i + min + commentLength === bytes.length) {\n return i;\n }\n }\n throw new Error('Not a ZIP archive (missing end-of-central-directory).');\n}\n\n/**\n * Extract a single ZIP member. Supports STORE and DEFLATE (method 8).\n */\nexport async function extractZipMember(bytes: Uint8Array, memberName: string): Promise<Uint8Array> {\n const eocd = findEocdOffset(bytes);\n const entries = u16(bytes, eocd + 10);\n let offset = u32(bytes, eocd + 16);\n for (let i = 0; i < entries; i++) {\n if (u32(bytes, offset) !== CDFH_SIGNATURE) {\n throw new Error('ZIP central directory is corrupt.');\n }\n requireRange(bytes, offset, 46);\n const method = u16(bytes, offset + 10);\n const compressedSize = u32(bytes, offset + 20);\n const uncompressedSize = u32(bytes, offset + 24);\n const nameLen = u16(bytes, offset + 28);\n const extraLen = u16(bytes, offset + 30);\n const commentLen = u16(bytes, offset + 32);\n const localOffset = u32(bytes, offset + 42);\n requireRange(bytes, offset + 46, nameLen);\n const name = new TextDecoder().decode(bytes.subarray(offset + 46, offset + 46 + nameLen));\n offset += 46 + nameLen + extraLen + commentLen;\n requireRange(bytes, offset, 0);\n if (name !== memberName) continue;\n if (uncompressedSize === 0xffffffff || compressedSize === 0xffffffff) {\n throw new Error('ZIP64 members are not supported for export.json.');\n }\n if (uncompressedSize > MAX_EXPORT_JSON_BYTES || compressedSize > MAX_EXPORT_JSON_BYTES) {\n throw new Error('ZIP member exceeds the export.json size limit.');\n }\n\n if (u32(bytes, localOffset) !== LFH_SIGNATURE) {\n throw new Error(`ZIP local header for ${memberName} is corrupt.`);\n }\n const localNameLen = u16(bytes, localOffset + 26);\n const localExtraLen = u16(bytes, localOffset + 28);\n const dataStart = localOffset + 30 + localNameLen + localExtraLen;\n requireRange(bytes, dataStart, compressedSize);\n const compressed = bytes.subarray(dataStart, dataStart + compressedSize);\n if (method === ZIP_STORED) {\n if (compressed.length !== uncompressedSize) {\n throw new Error('ZIP stored member size does not match the directory.');\n }\n return compressed.slice();\n }\n if (method === ZIP_DEFLATED) {\n const inflated = await inflateRaw(compressed, MAX_EXPORT_JSON_BYTES);\n if (inflated.length !== uncompressedSize) {\n throw new Error('ZIP deflated member size does not match the directory.');\n }\n return inflated;\n }\n throw new Error(`Unsupported ZIP compression method ${method} for ${memberName}.`);\n }\n throw new Error(`ZIP is missing ${memberName}.`);\n}\n\nexport async function extractWorkspaceExportJson(bytes: Uint8Array): Promise<string> {\n const raw = await extractZipMember(bytes, WORKSPACE_EXPORT_JSON_MEMBER);\n return new TextDecoder().decode(raw);\n}\n","import type { ChatsApi } from '../api/chats';\nimport type { InteractionMode, InteractionSessionResponse } from '../types/chat-types';\n\ntype InteractionApi = Pick<\n ChatsApi,\n 'createChatInteractionSession' | 'transitionChatInteractionSession'\n>;\n\n/** One client's mode history in one Chat. Create a separate controller per device/session. */\nexport class ChatInteractionController {\n private current: InteractionSessionResponse | undefined;\n private pending: { mode: InteractionMode; requestId: string } | undefined;\n private tail: Promise<unknown> = Promise.resolve();\n private readonly clientSessionId = crypto.randomUUID();\n\n constructor(\n private readonly api: InteractionApi,\n private readonly workspaceId: string,\n private readonly chatId: number\n ) {}\n\n /** Serialize switches and reuse request IDs when a response is lost. */\n ensure(mode: InteractionMode): Promise<InteractionSessionResponse> {\n const operation = this.tail.then(async () => {\n // Resolve an uncertain earlier write before selecting the next mode.\n if (this.pending) await this.applyPending();\n if (this.current?.mode !== mode) {\n this.pending = { mode, requestId: crypto.randomUUID() };\n await this.applyPending();\n }\n if (!this.current) throw new Error('Interaction session unavailable');\n return this.current;\n });\n this.tail = operation.catch(() => undefined);\n return operation;\n }\n\n private async applyPending(): Promise<void> {\n const pending = this.pending;\n if (!pending) return;\n this.current = this.current\n ? await this.api.transitionChatInteractionSession(\n this.workspaceId,\n this.chatId,\n this.current.id,\n pending\n )\n : await this.api.createChatInteractionSession(this.workspaceId, this.chatId, {\n ...pending,\n clientSessionId: this.clientSessionId,\n });\n this.pending = undefined;\n }\n}\n","import type { MemberImportPreviewResponse, MemberImportStreamEvent } from '../types/member-types';\n\n/** Counts and row references for logs/CLI/chat. Drops identity, contacts and free text. */\nexport function toSafeMemberImportPreview(preview: MemberImportPreviewResponse) {\n return {\n previewPlanToken: preview.previewPlanToken,\n totalRows: preview.totalRows,\n summary: preview.summary,\n rows: preview.rows.map((row) => ({\n row: row.row,\n action: row.action,\n memberId: row.match?.memberId,\n matchType: row.match?.matchType,\n conflict: row.conflict\n ? { code: row.conflict.code, fields: row.conflict.fields }\n : undefined,\n proposedChanges: row.proposedChanges,\n sharedContacts: row.sharedContacts,\n })),\n };\n}\n\n/** Deliberate allowlist: stream conflicts can contain global Account contact values. */\nexport function toSafeMemberImportSummary(event: MemberImportStreamEvent) {\n return {\n total: event.total,\n created: event.created,\n updated: event.updated,\n unchanged: event.unchanged,\n failed: event.failed,\n conflicted: event.conflicted,\n skipped: event.skipped,\n failedRows: event.failedRows,\n conflictRows: event.conflictRows,\n skippedRows: event.skippedRows,\n conflicts: event.conflicts?.map(({ row, code, fields, member_id }) => ({\n row,\n code,\n fields,\n memberId: member_id,\n })),\n updatedMembers: event.updatedMembers?.map(({ row, memberId, changes }) => ({\n row,\n memberId,\n changes,\n })),\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/helpers/index.ts","../src/helpers/outbound.ts","../src/api/base.ts","../src/helpers/enrollment.ts","../src/helpers/webhooks.ts","../src/helpers/workspace-export-archive.ts","../src/helpers/chat-interaction.ts","../src/helpers/member-import.ts"],"sourcesContent":["export {\n findMemberByPhone,\n listOutboundPhoneNumbers,\n normalizePhoneLast10,\n resolveOutboundPhoneNumberId,\n resolveOutboundWorkflowId,\n type OutboundPhoneNumberConfigDefaults,\n type OutboundPhoneNumberOption,\n type WorkspacePhoneNumbersPayload,\n} from './outbound';\n\nexport {\n resolveMemberId,\n findOrCreateMemberLabels,\n upsertDataRecordForMember,\n enrollMemberInJourney,\n type MemberRef,\n type EnrollMemberInJourneyOptions,\n type EnrollMemberInJourneyResult,\n} from './enrollment';\n\nexport {\n WEBHOOK_SIGNATURE_HEADER,\n signWebhookPayload,\n verifyWebhookSignature,\n type VerifyWebhookOptions,\n type VerifyWebhookResult,\n} from './webhooks';\n\nexport {\n MAX_EXPORT_JSON_BYTES,\n WORKSPACE_EXPORT_JSON_MEMBER,\n extractWorkspaceExportJson,\n extractZipMember,\n isZipBuffer,\n} from './workspace-export-archive';\n\nexport { ChatInteractionController } from './chat-interaction';\n\nexport { toSafeMemberImportPreview, toSafeMemberImportSummary } from './member-import';\n","import type { GravityRailClient } from '../client/client';\nimport type { Member } from '../types/member-types';\n\nexport interface OutboundPhoneNumberOption {\n id: number;\n number: string;\n name: string | null;\n}\n\nexport interface WorkspacePhoneNumbersPayload {\n phones: OutboundPhoneNumberOption[];\n defaultPhoneNumberId: number | null;\n}\n\n/** App/env fallbacks when resolving outbound phone numbers. */\nexport interface OutboundPhoneNumberConfigDefaults {\n /** e.g. GRAVITY_RAIL_PHONE_NUMBER_ID */\n phoneNumberId?: number;\n /** Explicit selection overriding workspace default */\n requestedPhoneNumberId?: number;\n}\n\n/** Strip to digits and return the last 10 (US-friendly match). */\nexport function normalizePhoneLast10(phone: string): string {\n const digits = phone.replace(/\\D/g, '');\n return digits.length >= 10 ? digits.slice(-10) : digits;\n}\n\nfunction phonesMatchLast10(a: string, b: string): boolean {\n const da = normalizePhoneLast10(a);\n const db = normalizePhoneLast10(b);\n if (!da || !db) return false;\n if (da.length >= 10 && db.length >= 10) {\n return da === db;\n }\n return da === db;\n}\n\nexport async function listOutboundPhoneNumbers(\n client: GravityRailClient,\n workspaceUuid: string\n): Promise<WorkspacePhoneNumbersPayload> {\n const [phones, defaults] = await Promise.all([\n client.getPhoneNumbers(workspaceUuid, { isArchived: false }),\n client.getWorkspaceDefaults(workspaceUuid),\n ]);\n\n const voicePhones = phones\n .filter((phone) => phone.enableVoice && !phone.isArchived)\n .map((phone) => ({\n id: phone.id,\n number: phone.number,\n name: phone.name,\n }));\n\n let defaultPhoneNumberId = defaults.defaultPhoneNumberId ?? null;\n if (\n defaultPhoneNumberId !== null &&\n !voicePhones.some((phone) => phone.id === defaultPhoneNumberId)\n ) {\n defaultPhoneNumberId = voicePhones[0]?.id ?? null;\n }\n if (defaultPhoneNumberId === null) {\n defaultPhoneNumberId = voicePhones[0]?.id ?? null;\n }\n\n return { phones: voicePhones, defaultPhoneNumberId };\n}\n\nexport async function resolveOutboundPhoneNumberId(\n client: GravityRailClient,\n workspaceUuid: string,\n configDefaults: OutboundPhoneNumberConfigDefaults\n): Promise<number> {\n const { phones, defaultPhoneNumberId } = await listOutboundPhoneNumbers(client, workspaceUuid);\n\n if (phones.length === 0) {\n throw new Error(\n 'No voice-enabled phone numbers in this workspace. Add a line in Gravity Rail first.'\n );\n }\n\n const requestedId = configDefaults.requestedPhoneNumberId;\n const candidate =\n requestedId ?? defaultPhoneNumberId ?? configDefaults.phoneNumberId ?? phones[0]?.id;\n\n if (candidate === undefined || !phones.some((phone) => phone.id === candidate)) {\n throw new Error('Selected outbound phone number is not available.');\n }\n\n return candidate;\n}\n\nexport async function resolveOutboundWorkflowId(\n client: GravityRailClient,\n workspaceUuid: string,\n defaultWorkflowId: number,\n override?: number\n): Promise<number> {\n const workflowsPage = await client.getWorkflows(workspaceUuid, { archived: false });\n const options = workflowsPage.items\n .filter((item) => !item.workflow.archived)\n .map((item) => ({\n id: item.workflow.id,\n name: item.workflow.name,\n }))\n .sort((a, b) => a.name.localeCompare(b.name));\n\n if (options.length === 0) {\n throw new Error('No workflows in this workspace. Create a workflow in Gravity Rail first.');\n }\n\n let resolvedDefault = defaultWorkflowId;\n if (!options.some((workflow) => workflow.id === resolvedDefault)) {\n resolvedDefault = options[0]?.id ?? defaultWorkflowId;\n }\n\n const candidate = override ?? resolvedDefault;\n\n if (!options.some((workflow) => workflow.id === candidate)) {\n throw new Error('Selected workflow is not available.');\n }\n\n return candidate;\n}\n\nexport async function findMemberByPhone(\n client: GravityRailClient,\n workspaceUuid: string,\n phone: string\n): Promise<Member | null> {\n const searchDigits = phone.replace(/\\D/g, '');\n\n for (let page = 1; page <= 5; page += 1) {\n const response = await client.getWorkspaceMembers(workspaceUuid, page, 50, searchDigits);\n const match = response.items.find(\n (member) => member.phone && phonesMatchLast10(member.phone, phone)\n );\n if (match) return match;\n\n if (page >= response.totalPages) break;\n }\n\n return null;\n}\n","// Utility function to read a specific cookie value\nexport function getCookieValue(name: string): string | undefined {\n if (typeof document === 'undefined') return undefined;\n\n const cookies = document.cookie.split(';');\n for (const cookie of cookies) {\n const trimmedCookie = cookie.trim();\n const eqIndex = trimmedCookie.indexOf('=');\n if (eqIndex === -1) continue;\n\n const cookieName = trimmedCookie.substring(0, eqIndex);\n const cookieValue = trimmedCookie.substring(eqIndex + 1);\n\n if (cookieName === name) {\n return cookieValue;\n }\n }\n\n return undefined;\n}\n\n/**\n * Structured validation error returned by the backend's workflow-expression\n * validator (CEL / Jinja schema checks). See WorkflowValidationErrorResponse\n * in workflowValidationTypes.ts for the public-facing shape.\n */\nexport interface ApiValidationError {\n field: string;\n kind: 'cel_syntax' | 'cel_schema' | 'jinja_syntax' | 'jinja_schema';\n message: string;\n offendingPath: string | null;\n location: {\n start: number;\n end: number;\n line: number;\n column: number;\n length: number;\n } | null;\n suggestion: string | null;\n}\n\nfunction isApiValidationError(value: unknown): value is ApiValidationError {\n if (!value || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.field === 'string' && typeof v.kind === 'string' && typeof v.message === 'string'\n );\n}\n\n/** Browser-console sink for API failures. A fixed event name only — never the\n * error object, its ``name``, or response-derived ``message``/``errorData``. */\nfunction logApiClientError(_error: unknown): void {\n console.warn('API request failed');\n}\n\nexport class ApiError extends Error {\n statusCode: number;\n /** Legacy string-array errors — kept for non-validation callers. */\n errors?: string[];\n /**\n * Structured workflow-expression validation errors (CEL/Jinja).\n * Populated when the backend returns a WorkflowValidationErrorResponse.\n */\n validationErrors?: ApiValidationError[];\n /**\n * The raw structured `detail` object when the backend returned one (a body\n * with `error: true` or a `code`). Declared here rather than bolted on with\n * `as any` at the throw site so consumers can narrow it with a type guard —\n * see `isMemberContactConflict` in `types/member-types`.\n */\n errorData?: unknown;\n\n constructor(\n message: string,\n statusCode: number,\n errors?: string[],\n validationErrors?: ApiValidationError[]\n ) {\n super(message);\n this.name = 'ApiError';\n this.statusCode = statusCode;\n this.errors = errors;\n this.validationErrors = validationErrors;\n }\n}\n\n/**\n * Handler for enhanced authentication requirement (RFC 9470 Step-Up Authentication).\n * Called when a 401 response includes WWW-Authenticate header with\n * error=\"insufficient_user_authentication\".\n * Should return a promise that resolves with the new auth token after re-auth.\n */\nexport type EnhancedAuthHandler = () => Promise<string>;\n\n/**\n * Info parsed from WWW-Authenticate header for org auth requirement.\n */\nexport interface OrgAuthInfo {\n orgUuid: string;\n authUrl: string;\n}\n\n/**\n * Handler for organization authentication requirement.\n * Called when a 401 response includes WWW-Authenticate header with realm=\"org\".\n * The handler should redirect the user to the auth URL for consent/SSO.\n * Returns a promise that resolves when the user should retry (after page reload with cookie).\n */\nexport type OrgAuthHandler = (info: OrgAuthInfo) => Promise<void>;\n\n/**\n * Check if a WWW-Authenticate header indicates step-up authentication is required.\n * Per RFC 9470, this is indicated by error=\"insufficient_user_authentication\".\n */\nfunction requiresStepUpAuth(wwwAuthenticate: string | null): boolean {\n if (!wwwAuthenticate) return false;\n return wwwAuthenticate.includes('error=\"insufficient_user_authentication\"');\n}\n\n/**\n * Parse org auth info from WWW-Authenticate header.\n * Returns null if this is not an org auth challenge.\n * Expected format: Bearer realm=\"org\", org_uuid=\"...\", auth_url=\"...\"\n * (the challenge names no cookie: a browser's org session is an httpOnly\n * cookie on its own host, and a bearer client sends `X-Org-Session`).\n *\n * Exported so non-SDK fetch paths (e.g. the app's same-origin file-content\n * proxy fetches) can detect the same challenge and trigger the shared org\n * step-up flow.\n */\nexport function parseOrgAuthChallenge(wwwAuthenticate: string | null): OrgAuthInfo | null {\n if (!wwwAuthenticate) return null;\n if (!wwwAuthenticate.includes('realm=\"org\"')) return null;\n\n // Parse the key-value pairs from the header\n const orgUuidMatch = wwwAuthenticate.match(/org_uuid=\"([^\"]+)\"/);\n const authUrlMatch = wwwAuthenticate.match(/auth_url=\"([^\"]+)\"/);\n\n if (!orgUuidMatch?.[1] || !authUrlMatch?.[1]) {\n // Don't log the raw header value — it embeds org_uuid + auth_url.\n console.warn('Invalid org auth challenge header: missing required fields');\n return null;\n }\n\n return {\n orgUuid: orgUuidMatch[1],\n authUrl: authUrlMatch[1],\n };\n}\n\nfunction orgAuthHeaderName(token: string): 'X-Org-Token' | 'X-Org-Session' {\n return token.startsWith('gr_sess_') ? 'X-Org-Session' : 'X-Org-Token';\n}\n\nfunction setOrgAuthHeader(headers: Headers, token: string | undefined): void {\n if (!token) return;\n headers.set(orgAuthHeaderName(token), token);\n}\n\n/**\n * Transport and credential for every request the client makes.\n *\n * - **Bearer** (`apiKey` set): `Authorization: Bearer <apiKey>` on every\n * request, no cookies. This is how non-browser consumers (the CLI, servers,\n * API keys) and bearer-holding browser runtimes (widget iframes) call the API.\n * - **Same-origin session** (no `apiKey`): no `Authorization` header and\n * `credentials: 'same-origin'`. The browser presents the host's httpOnly\n * session cookie, and only to the origin the page is on; the client never\n * reads a cookie to build a credential. A browser app points `baseUrl` at its\n * own origin, which serves `/api/v2` (Gravity Rail's edge on full-account\n * hosts, the frontend's proxy on Site hosts).\n *\n * The org-auth challenge (`WWW-Authenticate: Bearer realm=\"org\"`) is answered by\n * `setOrgAuthHandler`. A bearer client's handler hands back the org session with\n * `setOrgSession`, which is sent as `X-Org-Session`; in same-origin session\n * mode the org session is an httpOnly cookie on the page's origin, so the\n * request is retried once after the handler and the browser presents it.\n */\nexport class BaseApiClient {\n protected baseUrl: string;\n protected apiKey: string | undefined;\n protected isCrossDomain: boolean;\n protected fetchFunction: typeof fetch;\n private enhancedAuthHandler?: EnhancedAuthHandler;\n private enhancedAuthInProgress: Promise<string> | null = null;\n private orgAuthHandler?: OrgAuthHandler;\n private orgAuthInProgress: Promise<void> | null = null;\n private orgSession?: string;\n /**\n * The org_uuid that issued the current org auth credential. Tracked so we can detect\n * a 401 challenge from a *different* org and re-fire the handler — the\n * credential is opaque to the client, so without this the original guard\n * `!this.orgSession` would prevent re-auth across orgs and surface as a silent\n * 401 in multi-org CLI workflows.\n */\n private orgSessionOrgUuid?: string;\n\n constructor(apiKey: string | undefined, baseUrl: string, customFetch?: typeof fetch) {\n this.baseUrl = baseUrl;\n this.apiKey = apiKey;\n // console.log('[BaseApiClient] constructor - baseUrl:', baseUrl);\n // Bind fetch to its proper context (window in browser, global in Node)\n this.fetchFunction =\n customFetch || (typeof window !== 'undefined' ? fetch.bind(window) : fetch);\n\n // Determine if we're making cross-domain requests by comparing current origin to API origin\n const apiUrlParsed = new URL(baseUrl);\n const currentOrigin = typeof window !== 'undefined' ? window.location.origin : '';\n this.isCrossDomain = currentOrigin !== '' && currentOrigin !== apiUrlParsed.origin;\n // console.log('[BaseApiClient] isCrossDomain:', this.isCrossDomain, 'currentOrigin:', currentOrigin, 'apiOrigin:', apiUrlParsed.origin);\n }\n\n /**\n * Set the handler for enhanced authentication requirements (RFC 9470 Step-Up Auth).\n * This handler is called when a 401 response includes WWW-Authenticate header\n * with error=\"insufficient_user_authentication\".\n */\n setEnhancedAuthHandler(handler: EnhancedAuthHandler | undefined) {\n this.enhancedAuthHandler = handler;\n }\n\n /**\n * Set the handler for organization authentication requirements.\n * This handler is called when a 401 response includes WWW-Authenticate header\n * with realm=\"org\". The handler should redirect the user to complete org auth.\n */\n setOrgAuthHandler(handler: OrgAuthHandler | undefined) {\n this.orgAuthHandler = handler;\n }\n\n /**\n * Update the API key (used after successful re-authentication).\n */\n updateApiKey(newApiKey: string) {\n this.apiKey = newApiKey;\n }\n\n /**\n * Set the opaque org session credential a bearer client sends as `X-Org-Session`.\n * Opaque gr_sess_ values use X-Org-Session. Legacy orgToken JWE values still\n * use X-Org-Token through setOrgToken() during the migration window.\n *\n * `orgUuid` is the org that issued the credential; passing it lets the 401\n * handler detect a stale session when a subsequent request targets a\n * different org and re-fire the org-auth handler instead of silently\n * 401-ing. Callers without that context (legacy / unknown) may omit it.\n */\n setOrgSession(session: string | undefined, orgUuid?: string) {\n this.orgSession = session;\n this.orgSessionOrgUuid = session ? orgUuid : undefined;\n }\n\n /**\n * @deprecated Legacy compatibility for stateless orgToken JWE credentials.\n * Prefer setOrgSession() for opaque org browser sessions.\n */\n setOrgToken(token: string | undefined, orgUuid?: string) {\n this.setOrgSession(token, orgUuid);\n }\n\n /**\n * The `credentials` a request carries: none for a bearer client (the fetch\n * default), the page's own-origin cookies for a same-origin session.\n */\n private credentialsInit(): Pick<RequestInit, 'credentials'> {\n return this.apiKey ? {} : { credentials: 'same-origin' };\n }\n\n /**\n * Run the org-auth handler once for concurrent challenges. A bearer client's\n * handler sets the org session with `setOrgSession`; a same-origin session's\n * handler leaves an httpOnly org-session cookie on the page's origin instead,\n * which the retry presents. A bearer client whose handler issued nothing has\n * nothing to retry with.\n */\n private async answerOrgAuthChallenge(orgAuthInfo: OrgAuthInfo): Promise<void> {\n const handler = this.orgAuthHandler;\n if (!handler) return;\n if (!this.orgAuthInProgress) {\n this.orgAuthInProgress = (async () => {\n try {\n await handler(orgAuthInfo);\n } finally {\n this.orgAuthInProgress = null;\n }\n })();\n }\n await this.orgAuthInProgress;\n if (this.orgSession) {\n this.orgSessionOrgUuid = orgAuthInfo.orgUuid;\n return;\n }\n if (this.apiKey) {\n throw new ApiError('Organization authentication did not issue a session', 401);\n }\n }\n\n protected async fetch<T>(\n endpoint: string,\n options?: RequestInit,\n orgAuthRetry = false\n ): Promise<T> {\n try {\n const headers = new Headers({\n // Don't set Content-Type for FormData — the browser must set it\n // with the multipart boundary automatically.\n ...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n // console.warn(\"Calling with apiKey:\", this.apiKey)\n const fullUrl = `${this.baseUrl}/api/v2/${endpoint}`;\n // console.log('[BaseApiClient] fetch - URL:', fullUrl, 'baseUrl:', this.baseUrl, 'endpoint:', endpoint);\n\n const response = await this.fetchFunction(fullUrl, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n // Check for step-up auth requirement per RFC 9470 (superuser 2FA re-auth needed)\n if (\n response.status === 401 &&\n requiresStepUpAuth(response.headers.get('WWW-Authenticate')) &&\n this.enhancedAuthHandler\n ) {\n // If re-auth is already in progress, wait for it\n if (this.enhancedAuthInProgress) {\n await this.enhancedAuthInProgress;\n } else {\n // Trigger re-auth and wait for completion\n this.enhancedAuthInProgress = this.enhancedAuthHandler();\n try {\n const newToken = await this.enhancedAuthInProgress;\n if (this.apiKey) this.apiKey = newToken;\n } finally {\n this.enhancedAuthInProgress = null;\n }\n }\n // Retry the request with the new token\n try {\n return await this.fetch<T>(endpoint, options);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n\n if (response.status === 401) {\n const orgAuthInfo = parseOrgAuthChallenge(response.headers.get('WWW-Authenticate'));\n // If the challenge targets a different org than our current\n // token, the token is stale (e.g. switching from Org A to Org\n // B mid-session). Clear it so the handler re-fires below.\n if (\n orgAuthInfo &&\n this.orgSession &&\n this.orgSessionOrgUuid &&\n this.orgSessionOrgUuid !== orgAuthInfo.orgUuid\n ) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n }\n if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {\n await this.answerOrgAuthChallenge(orgAuthInfo);\n try {\n return await this.fetch<T>(endpoint, options, true);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n }\n\n const errorData = await response.json().catch(() => ({}));\n\n // Handle unauthorized errors for account endpoint\n if (response.status === 401 && endpoint === 'account') {\n throw new Error('unauthorized');\n }\n\n // Handle structured validation errors (Pydantic array format)\n if (Array.isArray(errorData.detail)) {\n const formattedErrors = errorData.detail\n .map((error: any) => {\n const location = error.loc.join(' > ');\n return `${location}: ${error.msg}`;\n })\n .join('\\n');\n throw new ApiError(`Validation Error:\\n${formattedErrors}`, response.status);\n }\n\n // Handle validation errors with errors array (new format)\n if (errorData.errors && Array.isArray(errorData.errors)) {\n // Structured workflow-expression validation errors — objects with\n // { field, kind, message, offendingPath, location }. Build a\n // readable message and preserve the structured payload.\n if (errorData.errors.length > 0 && isApiValidationError(errorData.errors[0])) {\n const validationErrors: ApiValidationError[] =\n errorData.errors.filter(isApiValidationError);\n const [first] = validationErrors;\n if (first) {\n const extra =\n validationErrors.length > 1\n ? ` (+${validationErrors.length - 1} more)`\n : '';\n const readable = `${first.field}: ${first.message}${extra}`;\n throw new ApiError(\n readable,\n response.status,\n validationErrors.map((e) => `${e.field}: ${e.message}`),\n validationErrors\n );\n }\n }\n\n // Legacy string-array errors.\n const stringErrors: string[] = errorData.errors.filter(\n (e: unknown): e is string => typeof e === 'string'\n );\n const details = stringErrors.join('; ');\n const message = errorData.detail\n ? `${errorData.detail}: ${details}`\n : `Validation failed: ${details}`;\n throw new ApiError(message, response.status, stringErrors);\n }\n\n // Handle structured errors (with error: true or with code property).\n //\n // TWO body shapes reach here and both carry structure:\n //\n // * `detail` IS the structured object — the member/account contact\n // conflicts build their whole payload as `HTTPException.detail`.\n // * `detail` is PROSE and the machine-readable fields sit beside it\n // at the top level. That is what the domain exception handlers\n // emit: `conflict_handler` writes `{detail, code, ...payload}`, so\n // a Journey v2 refusal arrives as\n // `{detail: \"...\", code: \"journey_v2_invalid_topology\", issues: [...]}`.\n //\n // `detail || errorData` picked the prose string for the second shape,\n // which is not an object, so the branch fell through and `code` and\n // `issues` were dropped on the floor — leaving a client unable to\n // tell a definition conflict from a structural refusal, or to point\n // at the resources a validator rejected. Prefer an OBJECT detail and\n // otherwise read the body itself; the message is unchanged in both\n // cases (prose still wins when there is prose).\n const detail = errorData.detail;\n const structuredError = detail && typeof detail === 'object' ? detail : errorData;\n if (\n structuredError &&\n typeof structuredError === 'object' &&\n (structuredError.error === true || structuredError.code)\n ) {\n const message =\n (typeof detail === 'string' && detail) ||\n structuredError.message ||\n structuredError.code ||\n 'Unknown error';\n const error = new ApiError(message, response.status);\n // Attach the full error data for frontend handling\n error.errorData = structuredError;\n throw error;\n }\n\n // Handle simple error messages\n const errorMessage =\n typeof errorData.detail === 'string'\n ? errorData.detail\n : errorData.message || response.statusText;\n const simpleError = new ApiError(errorMessage, response.status);\n // A prose `detail` STRING alongside a top-level `code` is the shape\n // the backend's domain-validation handler produces: it merges an\n // exception's `payload` into the body and keeps `detail`\n // authoritative. That misses the structured branch above, because\n // `errorData.detail || errorData` resolves to the string. Carry the\n // body through so a caller can still branch on the token — the\n // alternative is parsing `detail`, which is deliberately\n // human-facing prose and not a stable contract.\n if (typeof errorData.code === 'string') {\n simpleError.errorData = errorData;\n }\n throw simpleError;\n }\n\n // Handle 204 No Content - no JSON body to parse\n if (response.status === 204) {\n return undefined as any;\n }\n\n return response.json();\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n\n protected async fetchRaw(\n endpoint: string,\n options?: RequestInit,\n allowedErrorStatuses: number[] = [],\n orgAuthRetry = false\n ): Promise<Response> {\n try {\n const headers = new Headers({\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n const response = await this.fetchFunction(`${this.baseUrl}/api/v2/${endpoint}`, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n // Check for step-up auth requirement per RFC 9470 (superuser 2FA re-auth needed)\n if (\n response.status === 401 &&\n requiresStepUpAuth(response.headers.get('WWW-Authenticate')) &&\n this.enhancedAuthHandler\n ) {\n // If re-auth is already in progress, wait for it\n if (this.enhancedAuthInProgress) {\n await this.enhancedAuthInProgress;\n } else {\n // Trigger re-auth and wait for completion\n this.enhancedAuthInProgress = this.enhancedAuthHandler();\n try {\n const newToken = await this.enhancedAuthInProgress;\n if (this.apiKey) this.apiKey = newToken;\n } finally {\n this.enhancedAuthInProgress = null;\n }\n }\n // Retry the request with the new token\n try {\n return await this.fetchRaw(endpoint, options, allowedErrorStatuses);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n\n // Mirror of the org-auth branch in fetch() above; see comments there.\n if (response.status === 401) {\n const orgAuthInfo = parseOrgAuthChallenge(response.headers.get('WWW-Authenticate'));\n if (\n orgAuthInfo &&\n this.orgSession &&\n this.orgSessionOrgUuid &&\n this.orgSessionOrgUuid !== orgAuthInfo.orgUuid\n ) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n }\n if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {\n await this.answerOrgAuthChallenge(orgAuthInfo);\n try {\n return await this.fetchRaw(endpoint, options, allowedErrorStatuses, true);\n } catch (retryError) {\n this.orgSession = undefined;\n this.orgSessionOrgUuid = undefined;\n throw retryError;\n }\n }\n }\n\n if (allowedErrorStatuses.includes(response.status)) {\n return response;\n }\n\n const errorText = await response.text().catch(() => '');\n throw new ApiError(errorText || response.statusText, response.status);\n }\n\n return response;\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n\n /**\n * Fetch from root path (not prefixed with /api/v2/)\n * Used for OAuth and other non-API endpoints\n */\n protected async fetchRoot<T>(path: string, options?: RequestInit): Promise<T> {\n try {\n const headers = new Headers({\n ...(options?.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),\n ...((options?.headers as Record<string, string>) || {}),\n });\n\n if (this.apiKey) {\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n }\n\n setOrgAuthHeader(headers, this.orgSession);\n\n const fullUrl = `${this.baseUrl}/${path}`;\n\n const response = await this.fetchFunction(fullUrl, {\n ...options,\n ...this.credentialsInit(),\n ...(this.isCrossDomain ? { mode: 'cors' } : {}),\n headers,\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null as T;\n }\n const errorData = await response.json().catch(() => ({}));\n const errorMessage = errorData.detail || errorData.message || response.statusText;\n throw new ApiError(errorMessage, response.status);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json();\n } catch (error) {\n logApiClientError(error);\n throw error;\n }\n }\n}\n","/**\n * Enrollment-flow helpers: the small compound operations every integration\n * re-implements when wiring \"register member → populate a form → enroll into a\n * journey\". They build on the plain client methods and are safe to call from a\n * server-side BFF.\n */\nimport { ApiError } from '../api/base';\nimport type { GravityRailClient } from '../client/client';\nimport type { DataRecord } from '../types/data-types';\nimport { findMemberByPhone } from './outbound';\n\n/** Identify a member by any one of these; `memberId` wins when present. */\nexport interface MemberRef {\n memberId?: number;\n phone?: string;\n externalId?: string;\n}\n\n/** Resolve a `MemberRef` to a concrete member id (throws if none matches). */\nexport async function resolveMemberId(\n client: GravityRailClient,\n workspaceUuid: string,\n ref: MemberRef\n): Promise<number> {\n if (typeof ref.memberId === 'number') {\n return ref.memberId;\n }\n if (ref.phone) {\n const member = await findMemberByPhone(client, workspaceUuid, ref.phone);\n if (member?.id) return member.id;\n // Do not put the phone (PHI) in the message — it propagates to callers.\n throw new Error('No member found for the provided phone number');\n }\n if (ref.externalId) {\n const page = await client.getWorkspaceMembers(workspaceUuid, 1, 25, ref.externalId);\n const match = page.items.find((m) => m.externalId === ref.externalId);\n if (match?.id) return match.id;\n // Do not put the externalId (PHI) in the message — it propagates to callers.\n throw new Error('No member found for the provided externalId');\n }\n throw new Error('Provide memberId, phone, or externalId');\n}\n\n/**\n * Find member labels by name, creating any that don't exist, and return their\n * ids. Case-insensitive match on name. (The API derives the slug from the name.)\n */\nexport async function findOrCreateMemberLabels(\n client: GravityRailClient,\n workspaceUuid: string,\n names: string[],\n options: { color?: string } = {}\n): Promise<number[]> {\n const clean = names.map((n) => n.trim()).filter(Boolean);\n if (clean.length === 0) return [];\n\n const existing = await client.getMemberLabels(workspaceUuid);\n const byName = new Map(existing.map((l) => [l.name.toLowerCase(), l]));\n const ids: number[] = [];\n for (const name of clean) {\n const found = byName.get(name.toLowerCase());\n if (found) {\n ids.push(found.id);\n continue;\n }\n const created = await client.createMemberLabel(workspaceUuid, {\n name,\n color: options.color ?? '#6366f1',\n });\n byName.set(name.toLowerCase(), created);\n ids.push(created.id);\n }\n return ids;\n}\n\n/**\n * Idempotently write a member-scoped data record: update the member's existing\n * record for the DataType if one exists, otherwise create it.\n */\nexport async function upsertDataRecordForMember(\n client: GravityRailClient,\n workspaceUuid: string,\n dataTypeId: number,\n memberId: number,\n fieldValues: Record<string, unknown>\n): Promise<DataRecord> {\n const existing = await client.getDataRecords(workspaceUuid, dataTypeId, {\n memberId,\n pageSize: 1,\n });\n const record = existing.items?.[0];\n const body = { dataTypeId, memberId, fieldValues };\n if (record?.id) {\n return client.updateDataRecord(workspaceUuid, dataTypeId, record.id, body);\n }\n return client.createDataRecord(workspaceUuid, dataTypeId, body);\n}\n\nexport interface EnrollMemberInJourneyOptions extends MemberRef {\n /** Journey to enroll into. Defaults to the workspace's first journey. */\n journeyId?: number;\n /** Labels to apply to the member before enrolling (journey/cohort tags). */\n labels?: string[];\n labelColor?: string;\n}\n\nexport interface EnrollMemberInJourneyResult {\n journeyId: number;\n journeyName?: string;\n memberId: number;\n appliedLabels: string[];\n}\n\n/**\n * Resolve a member, optionally tag them, and enroll them into a journey. Treats\n * an \"already enrolled\" (409) response as success.\n */\nexport async function enrollMemberInJourney(\n client: GravityRailClient,\n workspaceUuid: string,\n options: EnrollMemberInJourneyOptions\n): Promise<EnrollMemberInJourneyResult> {\n const memberId = await resolveMemberId(client, workspaceUuid, options);\n\n const page = await client.getJourneys(workspaceUuid);\n const journeys = page.items ?? [];\n let journey: { id: number; name?: string } | undefined;\n if (options.journeyId != null) {\n journey = journeys.find((j) => j.id === options.journeyId) ?? { id: options.journeyId };\n } else {\n journey = journeys[0];\n }\n if (!journey) {\n throw new Error('No journey to enroll into: pass journeyId or create a journey');\n }\n\n const labelNames = (options.labels ?? []).map((l) => l.trim()).filter(Boolean);\n if (labelNames.length > 0) {\n const labelIds = await findOrCreateMemberLabels(client, workspaceUuid, labelNames, {\n color: options.labelColor,\n });\n // updateMember replaces the label set, so merge with the member's existing\n // labels rather than wiping them.\n const member = await client.getWorkspaceMember(workspaceUuid, memberId);\n const existingIds = (member.labels ?? [])\n .map((l) => l.id)\n .filter((id): id is number => typeof id === 'number');\n const mergedIds = Array.from(new Set([...existingIds, ...labelIds]));\n await client.updateMember(workspaceUuid, memberId, { labelIds: mergedIds });\n }\n\n try {\n await client.enrollMembers(workspaceUuid, journey.id, { memberId });\n } catch (error) {\n if (!(error instanceof ApiError && error.statusCode === 409)) {\n throw error;\n }\n }\n\n return { journeyId: journey.id, journeyName: journey.name, memberId, appliedLabels: labelNames };\n}\n","/**\n * Webhook signature helpers.\n *\n * Gravity Rail signs every outbound webhook with HMAC-SHA256 and sends the\n * result in the `X-Webhook-Signature` header as `t={unixSeconds},v1={hexDigest}`.\n * The signed message is the timestamp, a literal dot, then the raw request body:\n *\n * HMAC_SHA256(secret, `${t}.` + rawBody)\n *\n * The timestamp defeats replay — reject signatures older than the tolerance\n * window. Verify the signature against the RAW request body bytes, before any\n * JSON parsing/re-serialization.\n *\n * These use the Web Crypto API (`crypto.subtle`), so they run unchanged in Node\n * 18+ and the browser/edge runtimes.\n */\n\nexport const WEBHOOK_SIGNATURE_HEADER = 'X-Webhook-Signature';\n\nconst encoder = new TextEncoder();\n\nasync function hmacSha256Hex(secret: string, message: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(message));\n return Array.from(new Uint8Array(signature))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/** Constant-time comparison of two equal-length hex strings. */\nfunction timingSafeEqualHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let mismatch = 0;\n for (let i = 0; i < a.length; i += 1) {\n mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return mismatch === 0;\n}\n\n/**\n * Produce the `X-Webhook-Signature` header value for a payload. Primarily useful\n * for tests and for re-signing when relaying webhooks.\n */\nexport async function signWebhookPayload(\n rawBody: string,\n secret: string,\n timestampSeconds: number\n): Promise<string> {\n const digest = await hmacSha256Hex(secret, `${timestampSeconds}.${rawBody}`);\n return `t=${timestampSeconds},v1=${digest}`;\n}\n\nexport interface VerifyWebhookOptions {\n /** Max age of the signature in seconds (default 300). */\n toleranceSeconds?: number;\n /** Override \"now\" (unix seconds) — for deterministic tests. */\n nowSeconds?: number;\n}\n\nexport type VerifyWebhookResult = { ok: true } | { ok: false; reason: string };\n\n/**\n * Verify a webhook signature against the raw body. Returns a discriminated\n * result so callers can log the reason; it never throws on a bad signature.\n */\nexport async function verifyWebhookSignature(\n rawBody: string,\n signatureHeader: string | null | undefined,\n secret: string,\n options: VerifyWebhookOptions = {}\n): Promise<VerifyWebhookResult> {\n if (!signatureHeader) {\n return { ok: false, reason: 'missing signature header' };\n }\n\n const parts: Record<string, string> = {};\n for (const segment of signatureHeader.split(',')) {\n const idx = segment.indexOf('=');\n if (idx > 0) {\n parts[segment.slice(0, idx).trim()] = segment.slice(idx + 1).trim();\n }\n }\n\n const timestamp = Number(parts.t);\n const provided = parts.v1;\n if (!Number.isFinite(timestamp) || !provided) {\n return { ok: false, reason: 'malformed signature header' };\n }\n\n const tolerance = options.toleranceSeconds ?? 300;\n const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - timestamp) > tolerance) {\n return { ok: false, reason: 'signature timestamp outside tolerance' };\n }\n\n const expected = await hmacSha256Hex(secret, `${timestamp}.${rawBody}`);\n if (!timingSafeEqualHex(expected, provided)) {\n return { ok: false, reason: 'signature mismatch' };\n }\n\n return { ok: true };\n}\n","/**\n * Helpers for the portable workspace export ZIP.\n *\n * Layout: ``export.json`` plus ``blobs/ab/cd/<sha256>``.\n */\n\nexport const WORKSPACE_EXPORT_JSON_MEMBER = 'export.json';\nexport const MAX_EXPORT_JSON_BYTES = 8 * 1024 * 1024;\n\nconst EOCD_SIGNATURE = 0x06054b50;\nconst CDFH_SIGNATURE = 0x02014b50;\nconst LFH_SIGNATURE = 0x04034b50;\nconst ZIP_STORED = 0;\nconst ZIP_DEFLATED = 8;\n\nfunction requireRange(bytes: Uint8Array, start: number, length: number): void {\n if (start < 0 || length < 0 || start + length > bytes.length) {\n throw new Error('ZIP offsets are out of range.');\n }\n}\n\nfunction u16(bytes: Uint8Array, offset: number): number {\n requireRange(bytes, offset, 2);\n return bytes[offset]! | (bytes[offset + 1]! << 8);\n}\n\nfunction u32(bytes: Uint8Array, offset: number): number {\n requireRange(bytes, offset, 4);\n return (\n (bytes[offset]! |\n (bytes[offset + 1]! << 8) |\n (bytes[offset + 2]! << 16) |\n (bytes[offset + 3]! << 24)) >>>\n 0\n );\n}\n\nexport function isZipBuffer(bytes: Uint8Array): boolean {\n return bytes.length >= 4 && bytes[0] === 0x50 && bytes[1] === 0x4b;\n}\n\nasync function inflateRaw(data: Uint8Array, maxBytes: number): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'function') {\n const stream = new Blob([data as BlobPart])\n .stream()\n .pipeThrough(new DecompressionStream('deflate-raw'));\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel();\n throw new Error('ZIP member exceeds the export.json size limit.');\n }\n chunks.push(value);\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n }\n // Keep the Node-only fallback out of browser bundles. Native decompression\n // above handles browsers; unsupported browsers get a clear capability error.\n if (typeof window !== 'undefined') {\n throw new Error('This browser does not support ZIP decompression.');\n }\n const zlib = await import(/* webpackIgnore: true */ 'node:zlib');\n return Uint8Array.from(zlib.inflateRawSync(data, { maxOutputLength: maxBytes }));\n}\n\nfunction findEocdOffset(bytes: Uint8Array): number {\n const min = 22;\n const maxComment = 65535;\n const start = Math.max(0, bytes.length - (min + maxComment));\n for (let i = bytes.length - min; i >= start; i--) {\n if (u32(bytes, i) !== EOCD_SIGNATURE) continue;\n const commentLength = u16(bytes, i + 20);\n if (i + min + commentLength === bytes.length) {\n return i;\n }\n }\n throw new Error('Not a ZIP archive (missing end-of-central-directory).');\n}\n\n/**\n * Extract a single ZIP member. Supports STORE and DEFLATE (method 8).\n */\nexport async function extractZipMember(bytes: Uint8Array, memberName: string): Promise<Uint8Array> {\n const eocd = findEocdOffset(bytes);\n const entries = u16(bytes, eocd + 10);\n let offset = u32(bytes, eocd + 16);\n for (let i = 0; i < entries; i++) {\n if (u32(bytes, offset) !== CDFH_SIGNATURE) {\n throw new Error('ZIP central directory is corrupt.');\n }\n requireRange(bytes, offset, 46);\n const method = u16(bytes, offset + 10);\n const compressedSize = u32(bytes, offset + 20);\n const uncompressedSize = u32(bytes, offset + 24);\n const nameLen = u16(bytes, offset + 28);\n const extraLen = u16(bytes, offset + 30);\n const commentLen = u16(bytes, offset + 32);\n const localOffset = u32(bytes, offset + 42);\n requireRange(bytes, offset + 46, nameLen);\n const name = new TextDecoder().decode(bytes.subarray(offset + 46, offset + 46 + nameLen));\n offset += 46 + nameLen + extraLen + commentLen;\n requireRange(bytes, offset, 0);\n if (name !== memberName) continue;\n if (uncompressedSize === 0xffffffff || compressedSize === 0xffffffff) {\n throw new Error('ZIP64 members are not supported for export.json.');\n }\n if (uncompressedSize > MAX_EXPORT_JSON_BYTES || compressedSize > MAX_EXPORT_JSON_BYTES) {\n throw new Error('ZIP member exceeds the export.json size limit.');\n }\n\n if (u32(bytes, localOffset) !== LFH_SIGNATURE) {\n throw new Error(`ZIP local header for ${memberName} is corrupt.`);\n }\n const localNameLen = u16(bytes, localOffset + 26);\n const localExtraLen = u16(bytes, localOffset + 28);\n const dataStart = localOffset + 30 + localNameLen + localExtraLen;\n requireRange(bytes, dataStart, compressedSize);\n const compressed = bytes.subarray(dataStart, dataStart + compressedSize);\n if (method === ZIP_STORED) {\n if (compressed.length !== uncompressedSize) {\n throw new Error('ZIP stored member size does not match the directory.');\n }\n return compressed.slice();\n }\n if (method === ZIP_DEFLATED) {\n const inflated = await inflateRaw(compressed, MAX_EXPORT_JSON_BYTES);\n if (inflated.length !== uncompressedSize) {\n throw new Error('ZIP deflated member size does not match the directory.');\n }\n return inflated;\n }\n throw new Error(`Unsupported ZIP compression method ${method} for ${memberName}.`);\n }\n throw new Error(`ZIP is missing ${memberName}.`);\n}\n\nexport async function extractWorkspaceExportJson(bytes: Uint8Array): Promise<string> {\n const raw = await extractZipMember(bytes, WORKSPACE_EXPORT_JSON_MEMBER);\n return new TextDecoder().decode(raw);\n}\n","import type { ChatsApi } from '../api/chats';\nimport type { InteractionMode, InteractionSessionResponse } from '../types/chat-types';\n\ntype InteractionApi = Pick<\n ChatsApi,\n 'createChatInteractionSession' | 'transitionChatInteractionSession'\n>;\n\n/** One client's mode history in one Chat. Create a separate controller per device/session. */\nexport class ChatInteractionController {\n private current: InteractionSessionResponse | undefined;\n private pending: { mode: InteractionMode; requestId: string } | undefined;\n private tail: Promise<unknown> = Promise.resolve();\n private readonly clientSessionId = crypto.randomUUID();\n\n constructor(\n private readonly api: InteractionApi,\n private readonly workspaceId: string,\n private readonly chatId: number\n ) {}\n\n /** Serialize switches and reuse request IDs when a response is lost. */\n ensure(mode: InteractionMode): Promise<InteractionSessionResponse> {\n const operation = this.tail.then(async () => {\n // Resolve an uncertain earlier write before selecting the next mode.\n if (this.pending) await this.applyPending();\n if (this.current?.mode !== mode) {\n this.pending = { mode, requestId: crypto.randomUUID() };\n await this.applyPending();\n }\n if (!this.current) throw new Error('Interaction session unavailable');\n return this.current;\n });\n this.tail = operation.catch(() => undefined);\n return operation;\n }\n\n private async applyPending(): Promise<void> {\n const pending = this.pending;\n if (!pending) return;\n this.current = this.current\n ? await this.api.transitionChatInteractionSession(\n this.workspaceId,\n this.chatId,\n this.current.id,\n pending\n )\n : await this.api.createChatInteractionSession(this.workspaceId, this.chatId, {\n ...pending,\n clientSessionId: this.clientSessionId,\n });\n this.pending = undefined;\n }\n}\n","import type { MemberImportPreviewResponse, MemberImportStreamEvent } from '../types/member-types';\n\n/** Counts and row references for logs/CLI/chat. Drops identity, contacts and free text. */\nexport function toSafeMemberImportPreview(preview: MemberImportPreviewResponse) {\n return {\n previewPlanToken: preview.previewPlanToken,\n totalRows: preview.totalRows,\n summary: preview.summary,\n rows: preview.rows.map((row) => ({\n row: row.row,\n action: row.action,\n memberId: row.match?.memberId,\n matchType: row.match?.matchType,\n conflict: row.conflict\n ? { code: row.conflict.code, fields: row.conflict.fields }\n : undefined,\n proposedChanges: row.proposedChanges,\n sharedContacts: row.sharedContacts,\n })),\n };\n}\n\n/** Deliberate allowlist: stream conflicts can contain global Account contact values. */\nexport function toSafeMemberImportSummary(event: MemberImportStreamEvent) {\n return {\n total: event.total,\n created: event.created,\n updated: event.updated,\n unchanged: event.unchanged,\n failed: event.failed,\n conflicted: event.conflicted,\n skipped: event.skipped,\n failedRows: event.failedRows,\n conflictRows: event.conflictRows,\n skippedRows: event.skippedRows,\n conflicts: event.conflicts?.map(({ row, code, fields, member_id }) => ({\n row,\n code,\n fields,\n memberId: member_id,\n })),\n updatedMembers: event.updatedMembers?.map(({ row, memberId, changes }) => ({\n row,\n memberId,\n changes,\n })),\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/helpers.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ce as ChatInteractionController } from './chat-interaction-
|
|
3
|
-
import { c7 as WorkspaceMemberResponse, aT as DataRecordResponse } from './tasks-
|
|
4
|
-
export { t as toSafeMemberImportPreview, a as toSafeMemberImportSummary } from './member-import-
|
|
1
|
+
import { gc as GravityRailClient } from './chat-interaction-CklXa8iQ.cjs';
|
|
2
|
+
export { ce as ChatInteractionController } from './chat-interaction-CklXa8iQ.cjs';
|
|
3
|
+
import { c7 as WorkspaceMemberResponse, aT as DataRecordResponse } from './tasks-JweebIce.cjs';
|
|
4
|
+
export { t as toSafeMemberImportPreview, a as toSafeMemberImportSummary } from './member-import-BtTWfQ46.cjs';
|
|
5
5
|
import 'zod';
|
|
6
6
|
|
|
7
7
|
interface OutboundPhoneNumberOption {
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ce as ChatInteractionController } from './chat-interaction-
|
|
3
|
-
import { c7 as WorkspaceMemberResponse, aT as DataRecordResponse } from './tasks-
|
|
4
|
-
export { t as toSafeMemberImportPreview, a as toSafeMemberImportSummary } from './member-import-
|
|
1
|
+
import { gc as GravityRailClient } from './chat-interaction-Dei_j8Sd.js';
|
|
2
|
+
export { ce as ChatInteractionController } from './chat-interaction-Dei_j8Sd.js';
|
|
3
|
+
import { c7 as WorkspaceMemberResponse, aT as DataRecordResponse } from './tasks-JweebIce.js';
|
|
4
|
+
export { t as toSafeMemberImportPreview, a as toSafeMemberImportSummary } from './member-import-Do6D2g14.js';
|
|
5
5
|
import 'zod';
|
|
6
6
|
|
|
7
7
|
interface OutboundPhoneNumberOption {
|
package/dist/helpers.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -474,7 +474,7 @@ var BaseApiClient = class {
|
|
|
474
474
|
this.enhancedAuthInProgress = this.enhancedAuthHandler();
|
|
475
475
|
try {
|
|
476
476
|
const newToken = await this.enhancedAuthInProgress;
|
|
477
|
-
this.apiKey = newToken;
|
|
477
|
+
if (this.apiKey) this.apiKey = newToken;
|
|
478
478
|
} finally {
|
|
479
479
|
this.enhancedAuthInProgress = null;
|
|
480
480
|
}
|
|
@@ -585,7 +585,7 @@ ${formattedErrors}`, response.status);
|
|
|
585
585
|
this.enhancedAuthInProgress = this.enhancedAuthHandler();
|
|
586
586
|
try {
|
|
587
587
|
const newToken = await this.enhancedAuthInProgress;
|
|
588
|
-
this.apiKey = newToken;
|
|
588
|
+
if (this.apiKey) this.apiKey = newToken;
|
|
589
589
|
} finally {
|
|
590
590
|
this.enhancedAuthInProgress = null;
|
|
591
591
|
}
|
|
@@ -1631,7 +1631,7 @@ var AuthApi = class extends BaseApiClient {
|
|
|
1631
1631
|
isSignUp
|
|
1632
1632
|
})
|
|
1633
1633
|
});
|
|
1634
|
-
if (!verifyResponse.requiresTwoFactor && verifyResponse.token) {
|
|
1634
|
+
if (!verifyResponse.requiresTwoFactor && verifyResponse.token && verifyResponse.sessionKind !== "widget") {
|
|
1635
1635
|
const completeResponse = await fetch(`/api/auth/complete`, {
|
|
1636
1636
|
method: "POST",
|
|
1637
1637
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1720,6 +1720,18 @@ var AuthApi = class extends BaseApiClient {
|
|
|
1720
1720
|
body: JSON.stringify({})
|
|
1721
1721
|
});
|
|
1722
1722
|
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Mint a single-use realtime login token (`gr_wst_`) bound to one
|
|
1725
|
+
* workspace, with this client's credential. A credential narrower than the
|
|
1726
|
+
* full browser session (a Site session, a device, a widget session) mints
|
|
1727
|
+
* this kind; it opens only that workspace's sockets. Call once per connect
|
|
1728
|
+
* attempt.
|
|
1729
|
+
*/
|
|
1730
|
+
async createWorkspaceRealtimeLoginToken(workspaceUuid) {
|
|
1731
|
+
return this.fetch(`w/${workspaceUuid}/realtime/ws-token`, {
|
|
1732
|
+
method: "POST"
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1723
1735
|
/** Get the organization and workspace mapping for a custom domain. */
|
|
1724
1736
|
async getDomainMapping(customDomain) {
|
|
1725
1737
|
return this.fetch(`s/mapping/${customDomain}`);
|
|
@@ -1760,7 +1772,7 @@ var AuthApi = class extends BaseApiClient {
|
|
|
1760
1772
|
method: "POST",
|
|
1761
1773
|
body: JSON.stringify({ email, code, organizationUuid })
|
|
1762
1774
|
});
|
|
1763
|
-
if (!verifyResponse.requiresTwoFactor && verifyResponse.token) {
|
|
1775
|
+
if (!verifyResponse.requiresTwoFactor && verifyResponse.token && verifyResponse.sessionKind !== "widget") {
|
|
1764
1776
|
const completeResponse = await fetch(`/api/auth/complete`, {
|
|
1765
1777
|
method: "POST",
|
|
1766
1778
|
headers: { "Content-Type": "application/json" },
|
|
@@ -9028,7 +9040,7 @@ var SitesApi = class extends BaseApiClient {
|
|
|
9028
9040
|
method: "POST",
|
|
9029
9041
|
body: JSON.stringify({ code, phoneNumber, email })
|
|
9030
9042
|
});
|
|
9031
|
-
if (!verifyResponse.requiresTwoFactor && verifyResponse.token) {
|
|
9043
|
+
if (!verifyResponse.requiresTwoFactor && verifyResponse.token && verifyResponse.sessionKind !== "widget") {
|
|
9032
9044
|
const completeResponse = await fetch(`/api/auth/complete`, {
|
|
9033
9045
|
method: "POST",
|
|
9034
9046
|
headers: { "Content-Type": "application/json" },
|
|
@@ -10364,7 +10376,7 @@ var WorkflowsApi = class extends BaseApiClient {
|
|
|
10364
10376
|
* Classify the prompts of a Workflow's draft revision.
|
|
10365
10377
|
*
|
|
10366
10378
|
* A decision model reads each live Task with Workflow or Task instructions
|
|
10367
|
-
* and reports which
|
|
10379
|
+
* or an active outgoing Choice prompt and reports which
|
|
10368
10380
|
* prompt classifications probably apply. Each call asks the model again.
|
|
10369
10381
|
*/
|
|
10370
10382
|
async getWorkflowPromptClassifications(wid, workflowId) {
|
|
@@ -12739,6 +12751,7 @@ var WORKSPACE_METHOD_NAMES = [
|
|
|
12739
12751
|
"createWorkflowNote",
|
|
12740
12752
|
"createWorkflowOutboundVoiceChat",
|
|
12741
12753
|
"createWorkspaceMember",
|
|
12754
|
+
"createWorkspaceRealtimeLoginToken",
|
|
12742
12755
|
"createWorkspaceSupportRequest",
|
|
12743
12756
|
"deleteAbility",
|
|
12744
12757
|
"deleteAgent",
|
|
@@ -13950,6 +13963,7 @@ function bindAuthMethods(target, auth, assignments) {
|
|
|
13950
13963
|
target.requestCrossDomainToken = auth.requestCrossDomainToken.bind(auth);
|
|
13951
13964
|
target.exchangeToken = auth.exchangeToken.bind(auth);
|
|
13952
13965
|
target.createRealtimeLoginToken = auth.createRealtimeLoginToken.bind(auth);
|
|
13966
|
+
target.createWorkspaceRealtimeLoginToken = auth.createWorkspaceRealtimeLoginToken.bind(auth);
|
|
13953
13967
|
target.getDomainMapping = auth.getDomainMapping.bind(auth);
|
|
13954
13968
|
target.createSignup = auth.createSignup.bind(auth);
|
|
13955
13969
|
target.createAccount = auth.emailPasswordSignup.bind(auth);
|
|
@@ -15394,6 +15408,7 @@ var GravityRailClient = class {
|
|
|
15394
15408
|
requestCrossDomainToken;
|
|
15395
15409
|
exchangeToken;
|
|
15396
15410
|
createRealtimeLoginToken;
|
|
15411
|
+
createWorkspaceRealtimeLoginToken;
|
|
15397
15412
|
getDomainMapping;
|
|
15398
15413
|
createSignup;
|
|
15399
15414
|
createAccount;
|