d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
/**
|
|
* Default Thrive API host (Production, all regions except MEA).
|
|
* The Thrive REST API is reached at `https://{host}/rest/{version}`.
|
|
*/
|
|
export const THRIVE_DEFAULT_HOST = 'public.api.learn.link'
|
|
|
|
/**
|
|
* Builds the base REST URL for a Thrive API request.
|
|
*
|
|
* @param host - The region-specific API host (e.g. `public.api.learn.link`).
|
|
* @param version - The API version: `v1` for most resources, `v2` for user lifecycle.
|
|
*/
|
|
export function getThriveBaseUrl(host: string | undefined, version: 'v1' | 'v2'): string {
|
|
const resolvedHost = host?.trim() || THRIVE_DEFAULT_HOST
|
|
return `https://${resolvedHost}/rest/${version}`
|
|
}
|
|
|
|
/**
|
|
* Builds the HTTP Basic authentication headers for a Thrive API request.
|
|
* The Tenant ID is used as the username and the API key as the password.
|
|
*/
|
|
export function getThriveHeaders(tenantId: string, apiKey: string): Record<string, string> {
|
|
return {
|
|
Authorization: `Basic ${btoa(`${tenantId}:${apiKey}`)}`,
|
|
'Content-Type': 'application/json',
|
|
Accept: 'application/json',
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Appends a query parameter to a URL when the value is defined and non-empty.
|
|
* Numbers are coerced to strings; empty strings and `undefined`/`null` are skipped.
|
|
*/
|
|
export function appendThriveQuery(
|
|
url: URL,
|
|
key: string,
|
|
value: string | number | boolean | undefined | null
|
|
): void {
|
|
if (value === undefined || value === null) return
|
|
const stringValue = typeof value === 'string' ? value.trim() : String(value)
|
|
if (stringValue === '') return
|
|
url.searchParams.set(key, stringValue)
|
|
}
|
|
|
|
/**
|
|
* Parses a value that may be a JSON array string (from LLM/user input) or an
|
|
* already-parsed array into a typed array. An empty/whitespace string yields an
|
|
* empty array; malformed JSON throws so the caller sees a clear error rather
|
|
* than silently sending an empty collection.
|
|
*/
|
|
export function parseThriveArray<T = unknown>(value: unknown, fieldName = 'array'): T[] {
|
|
if (Array.isArray(value)) return value as T[]
|
|
if (typeof value === 'string') {
|
|
const trimmed = value.trim()
|
|
if (trimmed === '') return []
|
|
let parsed: unknown
|
|
try {
|
|
parsed = JSON.parse(trimmed)
|
|
} catch {
|
|
throw new Error(`Invalid JSON provided for ${fieldName}: expected a JSON array`)
|
|
}
|
|
return Array.isArray(parsed) ? (parsed as T[]) : [parsed as T]
|
|
}
|
|
return []
|
|
}
|
|
|
|
/**
|
|
* Parses a JSON object string (from LLM/user input), throwing a descriptive
|
|
* error on malformed JSON so it is never silently dropped.
|
|
*/
|
|
export function parseThriveJsonObject(value: string, fieldName: string): Record<string, unknown> {
|
|
try {
|
|
return JSON.parse(value)
|
|
} catch {
|
|
throw new Error(`Invalid JSON provided for ${fieldName}: expected a JSON object`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads a Thrive API response, throwing a descriptive error on non-2xx status.
|
|
* Tolerates empty bodies (returned by some delete endpoints) and non-JSON bodies.
|
|
*/
|
|
export async function parseThriveResponse<T = any>(
|
|
response: Response,
|
|
fallbackError: string
|
|
): Promise<T> {
|
|
const text = await response.text()
|
|
let data: any = {}
|
|
if (text) {
|
|
try {
|
|
data = JSON.parse(text)
|
|
} catch {
|
|
data = { raw: text }
|
|
}
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const message =
|
|
(data && typeof data.message === 'string' && data.message) ||
|
|
(data && typeof data.error === 'string' && data.error) ||
|
|
fallbackError
|
|
throw new Error(message)
|
|
}
|
|
|
|
return data as T
|
|
}
|