Files
WeHub Mirror 6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (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 / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00

106 lines
4.4 KiB
TypeScript

/**
* Outbound representation of stored table rows — the counterpart to the inbound
* `coerceValueToColumnType` switch in `validation.ts`.
*
* Row data is keyed by a column's stable **id**, and a `select` cell's stored
* value is an opaque option **id**. Turning a row into the form consumers see
* therefore needs two translations — keys and values — and they must happen
* together: every past bug in this area came from a boundary translating the
* keys and forgetting the values. {@link namedRowMapper} fuses them into a
* single pass so they cannot drift apart again.
*/
import { getColumnId } from '@/lib/table/column-keys'
import { columnTypeOf } from '@/lib/table/column-types'
import { selectValueToNames } from '@/lib/table/select-values'
import type { ColumnDefinition, JsonValue, RowData } from '@/lib/table/types'
/**
* One stored cell value → the value consumers see. `select` resolves option
* ids to names (an array when `multiple`, dropping ids whose option was
* deleted); every other column type passes through untouched.
*
* The passthrough is load-bearing, not laziness: `rawRow` is documented public
* trigger output, so a `date`, `json`, `number`, `boolean`, or `string` cell
* must reach consumers byte-identical to what is stored.
*/
export function formatCellValue(value: unknown, column: ColumnDefinition): JsonValue {
if (columnTypeOf(column).storesOpaqueIds) return selectValueToNames(column, value)
return value as JsonValue
}
/**
* Builds the id-keyed → name-keyed row translator for a set of columns,
* translating keys and values in one pass.
*
* Returns a closure because callers hoist it once per request or export stream:
* a per-row signature would rebuild the column index for every row inside the
* paginated export loop. Keys with no current column — e.g. a column deleted by
* a not-yet-finished background strip — are dropped, so orphaned keys never
* surface.
*
* Takes a column list rather than a schema so callers can pass a filtered set
* (workflow-column inputs exclude the group's own output columns).
*/
export function namedRowMapper(columns: ColumnDefinition[]): (data: RowData) => RowData {
const byId = new Map<string, ColumnDefinition>()
for (const column of columns) byId.set(getColumnId(column), column)
return (data: RowData): RowData => {
const out: RowData = {}
for (const [id, value] of Object.entries(data)) {
const column = byId.get(id)
if (column !== undefined) out[column.name] = formatCellValue(value, column)
}
return out
}
}
/**
* Widens an already name-keyed row to one entry per column, filling absent
* cells with `null`.
*
* For payloads whose field set must match a sibling `headers` array — the table
* trigger's `row` and the workflow-column `inputRow`. Pass the same column list
* used to build those headers so the two cannot drift.
*
* Absent cells become `null` rather than being run through
* {@link formatCellValue}: an empty multi-select would otherwise render as `[]`,
* which downstream emptiness checks do not treat as empty.
*/
export function fillMissingColumns(named: RowData, columns: ColumnDefinition[]): RowData {
const out: RowData = {}
for (const column of columns) out[column.name] = named[column.name] ?? null
return out
}
/**
* Resolves a workflow group's input mappings to their outward cell values.
*
* `WorkflowGroupInputMapping.columnName` holds a column **id**, not a name (the
* field name is a known misnomer; renaming it is a persisted-schema migration).
* Mappings referencing a column that no longer exists are skipped.
*
* An absent cell stays `undefined` rather than being formatted: an empty
* multi-select would otherwise become `[]`, which the callers' required-input
* emptiness checks do not treat as empty, silently running enrichments on rows
* they currently skip.
*/
export function mapInputValues(
data: RowData,
columns: ColumnDefinition[],
mappings: Array<{ inputName: string; columnName: string }>
): Record<string, unknown> {
const byId = new Map<string, ColumnDefinition>()
for (const column of columns) byId.set(getColumnId(column), column)
const out: Record<string, unknown> = {}
for (const mapping of mappings) {
const column = byId.get(mapping.columnName)
if (column === undefined) continue
const value = data[mapping.columnName]
out[mapping.inputName] = value === undefined ? undefined : formatCellValue(value, column)
}
return out
}