diff --git a/.changeset/gorgeous-doors-train.md b/.changeset/gorgeous-doors-train.md new file mode 100644 index 000000000..2a10c0840 --- /dev/null +++ b/.changeset/gorgeous-doors-train.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/graph-engine": minor +--- + +Add a parseNumber node that lets you parse a string into a number. diff --git a/packages/graph-engine/src/nodes/typing/index.ts b/packages/graph-engine/src/nodes/typing/index.ts index ea6a5b192..e644d183e 100644 --- a/packages/graph-engine/src/nodes/typing/index.ts +++ b/packages/graph-engine/src/nodes/typing/index.ts @@ -1,6 +1,13 @@ import assertDefined from './assertDefined.js'; import hasValue from './hasValue.js'; +import parseNumber from './parseNumber.js'; import parseUnit from './parseUnit.js'; import passUnit from './passUnit.js'; -export const nodes = [assertDefined, hasValue, passUnit, parseUnit]; +export const nodes = [ + assertDefined, + hasValue, + parseNumber, + parseUnit, + passUnit +]; diff --git a/packages/graph-engine/src/nodes/typing/parseNumber.ts b/packages/graph-engine/src/nodes/typing/parseNumber.ts new file mode 100644 index 000000000..2bdff7439 --- /dev/null +++ b/packages/graph-engine/src/nodes/typing/parseNumber.ts @@ -0,0 +1,37 @@ +import { INodeDefinition, ToInput, ToOutput } from '../../index.js'; +import { Node } from '../../programmatic/node.js'; +import { NumberSchema, StringSchema } from '../../schemas/index.js'; + +export default class NodeDefinition extends Node { + static title = 'Parse Number'; + static type = 'studio.tokens.typing.parseNumber'; + static description = 'Converts a string to a number'; + + declare inputs: ToInput<{ + value: string; + }>; + declare outputs: ToOutput<{ + value: number; + }>; + + constructor(props: INodeDefinition) { + super(props); + this.addInput('value', { + type: StringSchema + }); + this.addOutput('value', { + type: NumberSchema + }); + } + + execute(): void | Promise { + const { value } = this.getAllInputs(); + const parsed = Number(value); + + if (isNaN(parsed)) { + throw new Error('Could not parse string to number'); + } + + this.outputs.value.set(parsed); + } +} diff --git a/packages/graph-engine/tests/suites/nodes/typing/parseNumber.test.ts b/packages/graph-engine/tests/suites/nodes/typing/parseNumber.test.ts new file mode 100644 index 000000000..4967cf185 --- /dev/null +++ b/packages/graph-engine/tests/suites/nodes/typing/parseNumber.test.ts @@ -0,0 +1,44 @@ +import { Graph } from '../../../../src/graph/graph.js'; +import { describe, expect, test } from 'vitest'; +import Node from '../../../../src/nodes/typing/parseNumber.js'; + +describe('typing/parseNumber', () => { + test('converts integer string to number', () => { + const graph = new Graph(); + const node = new Node({ graph }); + + node.inputs.value.setValue('42'); + node.execute(); + + expect(node.outputs.value.value).toBe(42); + }); + + test('converts decimal string to number', () => { + const graph = new Graph(); + const node = new Node({ graph }); + + node.inputs.value.setValue('3.14'); + node.execute(); + + expect(node.outputs.value.value).toBe(3.14); + }); + + test('converts negative string to number', () => { + const graph = new Graph(); + const node = new Node({ graph }); + + node.inputs.value.setValue('-123'); + node.execute(); + + expect(node.outputs.value.value).toBe(-123); + }); + + test('throws error for invalid number string', () => { + const graph = new Graph(); + const node = new Node({ graph }); + + node.inputs.value.setValue('not a number'); + + expect(() => node.execute()).toThrow('Could not parse string to number'); + }); +});