npm package: @question-forms/core
Description: Core engine for Question Forms SDK — schema types, validation, conditions, formatters
Author: question-forms
Latest version: 0.5.4

# @question-forms/core

Core engine for the [Question Forms SDK](https://jolly-dune-009ebe90f.4.azurestaticapps.net/) — schema types, validation, conditional logic, formatters, and value helpers.

Zero dependencies. Works with any JavaScript runtime.

## Installation

```bash
npm install @question-forms/core
```

## Quick Start

```typescript
import {
    resolveVisibility,
    validateForm,
    setValue,
} from '@question-forms/core';
import type { FormSchema, FormValues } from '@question-forms/core';

const schema: FormSchema = {
    id: 'my-form',
    sections: [
        {
            id: 'section-1',
            questions: [
                {
                    id: 'q1',
                    key: 'name',
                    type: 'TEXT',
                    subtype: 'short',
                    label: 'Your Name',
                    required: true,
                },
            ],
        },
    ],
};

let values: FormValues = {};
values = setValue(values, 'name', 'Alice');

const visibility = resolveVisibility(schema, values);
const result = validateForm(schema, values, visibility);

console.log(result.valid); // true
```

## Features

- **Schema-driven** — define forms as plain JSON objects
- **10 question types** — text, date, time, select, rating, image, handwriting, radio grid, checkbox grid
- **Conditional visibility** — show/hide sections and questions based on answers
- **Enabled state** — enable/disable questions based on conditions
- **Validation** — required, min/max length, min/max value, regex pattern, custom rules
- **Formatters** — currency and percentage formatting
- **Immutable value helpers** — get, set, reset, dirty tracking
- **License system** — built-in plan-based licensing for commercial distribution
- **Zero dependencies** — pure TypeScript, works anywhere
- **Tree-shakeable** — ESM + CJS dual output

## Question Types

| Type | Subtypes / Variants | Key Features |
|------|---------------------|--------------|
| `TEXT` | `short`, `long`, `formatted` | Min/max length, currency/percentage formatting |
| `DATE` | `full-date`, `year`, `month` | Min/max date constraints |
| `TIME` | — | Min/max time constraints |
| `DATE_TIME` | — | Combined date + time with min/max |
| `SELECT` | `single-select`, `multi-select`, `dropdown` | Radio, dropdown, checkbox, autocomplete variants; static or async data sources |
| `HANDWRITING` | — | Output as base64, blob, or URI |
| `RATING` | `smile`, `star`, `heart` | Configurable max value |
| `IMAGE` | — | Multiple files, max count, accepted formats |
| `RADIO_GRID` | — | Row/column matrix, mutually exclusive per row |
| `CHECKBOX_GRID` | — | Row/column matrix, multiple selections per row |

## API Reference

### Schema Types

```typescript
import type {
    FormSchema,
    FormSection,
    QuestionSchema,
    TextQuestionSchema,
    DateQuestionSchema,
    TimeQuestionSchema,
    DateTimeQuestionSchema,
    SelectQuestionSchema,
    HandwritingQuestionSchema,
    RatingQuestionSchema,
    ImageQuestionSchema,
    RadioGridQuestionSchema,
    CheckboxGridQuestionSchema,
    SelectOption,
    SelectDataSource,
    TextFormat,
    TextSubtype,
    TextFormatKind,
    DateSubtype,
    SelectSubtype,
    SelectVariant,
    RatingVariant,
} from '@question-forms/core';
```

### Conditions & Visibility

```typescript
import {
    evaluateCondition,
    evaluateRuleGroup,
    resolveVisibility,
    resolveEnabled,
} from '@question-forms/core';
import type {
    Condition,
    Comparator,
    VisibilityRule,
    EnabledRule,
    FormValues,
    VisibilityResult,
    EnabledResult,
} from '@question-forms/core';
```

**Comparators:** `equals`, `notEquals`, `includes`, `notIncludes`, `isEmpty`, `isNotEmpty`, `greaterThan`, `lessThan`

**Rule groups** combine conditions with `AND` / `OR` operators and can be nested.

```typescript
const visibility: VisibilityRule = {
    operator: 'AND',
    conditions: [
        { questionKey: 'role', comparator: 'equals', value: 'manager' },
        { questionKey: 'experience', comparator: 'greaterThan', value: 5 },
    ],
};

const result = evaluateRuleGroup(visibility, values); // true/false
```

### Validation

```typescript
import { validateQuestion, validateForm } from '@question-forms/core';
import type { ValidationRule, ValidationError, FormValidationResult } from '@question-forms/core';
```

**Validation rule types:** `required`, `minLength`, `maxLength`, `min`, `max`, `pattern`, `custom`

```typescript
const visibility = resolveVisibility(schema, values);
const result = validateForm(schema, values, visibility);

if (!result.valid) {
    console.log(result.errors);
    // [{ questionId: 'q1', questionKey: 'name', rule: 'required', message: '...' }]
}
```

### Formatters

```typescript
import { formatTextValue, parseFormattedNumber } from '@question-forms/core';

formatTextValue(1234.5, { kind: 'currency' });    // "$1,234.50"
formatTextValue(0.85, { kind: 'percentage' });     // "85%"
parseFormattedNumber('$1,234.50');                  // 1234.5
```

### Value Helpers

All value operations return new objects (immutable).

```typescript
import {
    getValue,
    setValue,
    setValues,
    resetValues,
    getDirtyKeys,
    isDirty,
} from '@question-forms/core';

let values: FormValues = {};

values = setValue(values, 'name', 'Alice');
values = setValues(values, { name: 'Bob', age: 30 });

getValue(values, 'name'); // 'Bob'

const initial = { name: 'Alice' };
getDirtyKeys(values, initial); // ['name', 'age']
isDirty(values, initial);      // true

values = resetValues(values, initial, ['name']); // resets only 'name'
```

### License System

```typescript
import {
    validateLicense,
    hasAccess,
    generateLicenseKey,
} from '@question-forms/core';
import type { LicensePlan, LicenseInfo } from '@question-forms/core';
```

**Plans (ascending):** `FREE` → `BUILDER` → `PRO` → `ENTERPRISE`

```typescript
const key = generateLicenseKey('PRO', 365);
const info = validateLicense(key);
// { valid: true, plan: 'PRO', expired: false }

hasAccess(info, 'BUILDER'); // true — PRO includes BUILDER
hasAccess(info, 'ENTERPRISE'); // false
```

## Conditional Visibility Example

```typescript
const schema: FormSchema = {
    id: 'survey',
    sections: [
        {
            id: 'followup',
            visibility: {
                operator: 'AND',
                conditions: [
                    { questionKey: 'interested', comparator: 'equals', value: 'yes' },
                ],
            },
            questions: [
                {
                    id: 'q-details',
                    key: 'details',
                    type: 'TEXT',
                    subtype: 'long',
                    label: 'Tell us more',
                    required: true,
                },
            ],
        },
    ],
};

const visibility = resolveVisibility(schema, { interested: 'no' });
// visibility.sections['followup'] === false
// visibility.questions['q-details'] === false
```

## License

MIT — [Question Forms SDK](https://jolly-dune-009ebe90f.4.azurestaticapps.net/)