From bffb83a5280a4d74e424c4e0f4fbd46f790227a3 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Wed, 5 Nov 2025 15:43:30 -0800 Subject: [PATCH] more robust isValue() check --- src/value.ts | 3 ++- tests/value.test.ts | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/value.ts b/src/value.ts index 0f74f5f..e5133fc 100644 --- a/src/value.ts +++ b/src/value.ts @@ -6,6 +6,7 @@ import { VM } from "./vm" export type NativeFunction = (...args: Value[]) => Promise | Value export type TypeScriptFunction = (this: VM, ...args: any[]) => any const REEF_FUNCTION = Symbol('__reefFunction') +const VALUE_TYPES = new Set(['null', 'boolean', 'number', 'string', 'array', 'dict', 'regex', 'native', 'function']) export type Value = | { type: 'null', value: null } @@ -39,7 +40,7 @@ export type FunctionDef = { } export function isValue(v: any): boolean { - return !!(v && typeof v === 'object' && v.type && 'value' in v) + return !!(v && typeof v === 'object' && VALUE_TYPES.has(v.type) && 'value' in v) } export function toValue(v: any, vm?: VM): Value /* throws */ { diff --git a/tests/value.test.ts b/tests/value.test.ts index 4acb35c..570c8ce 100644 --- a/tests/value.test.ts +++ b/tests/value.test.ts @@ -71,7 +71,6 @@ test("isValue - used by toValue to detect already-converted values", () => { }) test("isValue - edge cases with type and value properties", () => { - // extra properties expect(isValue({ type: 'number', value: 42, extra: 'data' })).toBe(true) expect(isValue({ type: null, value: 42 })).toBe(false) @@ -81,6 +80,29 @@ test("isValue - edge cases with type and value properties", () => { expect(isValue({ typ: 'number', value: 42 })).toBe(false) }) +test("isValue - rejects objects with invalid type values", () => { + expect(isValue({ type: 'text', value: 'Bob' })).toBe(false) + expect(isValue({ type: 'email', value: 'test@example.com' })).toBe(false) + expect(isValue({ type: 'password', value: 'secret' })).toBe(false) + expect(isValue({ type: 'checkbox', value: true })).toBe(false) + expect(isValue({ type: 'custom', value: 123 })).toBe(false) + expect(isValue({ type: 'unknown', value: null })).toBe(false) +}) + +test("toValue - correctly handles HTML input props", async () => { + const { VM, toBytecode, toValue } = await import("#reef") + + const bytecode = toBytecode([["HALT"]]) + const vm = new VM(bytecode) + + const inputProps = { type: 'text', value: 'Bob' } + const converted = toValue(inputProps, vm) + + expect(converted.type).toBe('dict') + expect(converted.value.get('type')).toEqual({ type: 'string', value: 'text' }) + expect(converted.value.get('value')).toEqual({ type: 'string', value: 'Bob' }) +}) + test("toValue - converts wrapped Reef functions back to original Value", async () => { const { VM, toBytecode, fnFromValue } = await import("#reef")