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
88 lines
3.7 KiB
TypeScript
88 lines
3.7 KiB
TypeScript
/**
|
|
* Pure `select`-option helpers, with no database dependency.
|
|
*
|
|
* These live in their own leaf module rather than in `validation.ts` on
|
|
* purpose. `validation.ts` imports `@sim/db`, `drizzle-orm`, and `next/server`,
|
|
* so anything importing it — `select-values.ts`, and transitively
|
|
* `cell-format.ts` and `export-format.ts` — becomes server-only. That taint is
|
|
* the sole reason the tables grid used to hand-roll its own copy of the
|
|
* id-resolution logic client-side, and why a select cell's stored ids could
|
|
* drift from the names the client resolved them to.
|
|
*
|
|
* Keeping the option primitives here lets both sides share one implementation.
|
|
*/
|
|
|
|
import type { ColumnDefinition, JsonValue, SelectOption } from '@/lib/table/types'
|
|
|
|
/** Set of valid option ids for a `select`/`multiselect` column. */
|
|
export function optionIds(column: ColumnDefinition): Set<string> {
|
|
return new Set((column.options ?? []).map((o) => o.id))
|
|
}
|
|
|
|
/**
|
|
* Resolves a raw cell value to a declared option id, accepting either the
|
|
* stable id or (tolerant for tool/import writes) the option's display name.
|
|
* Returns null when no option matches.
|
|
*
|
|
* Id wins over name, and an exact name wins over a case-folded one, so an
|
|
* option whose *name* equals another option's *id* can never repoint a cell.
|
|
*/
|
|
export function resolveSelectOptionId(value: JsonValue, options: SelectOption[]): string | null {
|
|
// The block builder serializes without schema access, so an option NAME that
|
|
// looks numeric or boolean ("123", "true") arrives scalar-coerced. Stringify
|
|
// scalars so the name still resolves; arrays/objects stay unresolvable.
|
|
const text =
|
|
typeof value === 'string'
|
|
? value
|
|
: typeof value === 'number' || typeof value === 'boolean'
|
|
? String(value)
|
|
: null
|
|
if (text === null) return null
|
|
const byId = options.find((o) => o.id === text)
|
|
if (byId) return byId.id
|
|
const byName =
|
|
options.find((o) => o.name === text) ??
|
|
options.find((o) => o.name.toLowerCase() === text.toLowerCase())
|
|
return byName ? byName.id : null
|
|
}
|
|
|
|
/**
|
|
* Splits a raw value into the parts a multi-select cell should resolve. A cell
|
|
* may arrive as an array (canonical) or as a single comma-delimited string —
|
|
* the shape a multi cell exports, copies, and converts to text as — so both the
|
|
* write-path coercion and the column-conversion compatibility check read it
|
|
* through here rather than each deciding for itself. Option names that
|
|
* themselves contain commas are an accepted ambiguity.
|
|
*/
|
|
export function splitMultiSelectInput(value: JsonValue): JsonValue[] {
|
|
if (Array.isArray(value)) return value
|
|
if (typeof value !== 'string') return [value]
|
|
return value
|
|
.split(',')
|
|
.map((part) => part.trim())
|
|
.filter((part) => part !== '')
|
|
}
|
|
|
|
/**
|
|
* Resolves a raw value to the canonical stored shape for a select column: an
|
|
* array of option ids when `multiple`, otherwise a single id (or `null`).
|
|
*
|
|
* This is the one place the multi/single split is decided. Unresolvable parts
|
|
* are dropped and duplicates collapse to their first occurrence.
|
|
*/
|
|
export function resolveSelectCellValue(value: JsonValue, column: ColumnDefinition): JsonValue {
|
|
const options = column.options ?? []
|
|
if (column.multiple) {
|
|
const ids: string[] = []
|
|
for (const entry of splitMultiSelectInput(value)) {
|
|
const id = resolveSelectOptionId(entry, options)
|
|
if (id !== null && !ids.includes(id)) ids.push(id)
|
|
}
|
|
return ids
|
|
}
|
|
// Tolerate an array left behind by a multiple→single toggle by resolving its
|
|
// first element, rather than dropping the cell wholesale.
|
|
const single = Array.isArray(value) ? value[0] : value
|
|
return single === undefined ? null : resolveSelectOptionId(single, options)
|
|
}
|