chore: import upstream snapshot with attribution
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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
/**
* 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
}