Imported from arrai-innovations/vueda (
client/AGENTS.md). Install upstream withnpx skills add arrai-innovations/vueda --skill client. Copyright stays with the author.
Agent Guidelines: @arrai-innovations/vueda
This directory contains @arrai-innovations/vueda, a Vue 3 component library built with Vite. Consuming projects use an alias @vueda that resolves directly to the lib/ source directory. This allows customization developers to import uncompiled components and composables. The library source (lib/) is plain JavaScript (no TypeScript). There are no .ts files. Type information is expressed through JSDoc annotations.
Local Development
To get started:
-
Install dependencies with:
pnpm install -
Run tests:
just test-client(accepts extra vitest args, paths relative toclient/)Testing scope: this is a component library with no standalone dev app. Vitest unit tests are the primary verification. For rendered or visual behavior (layout, borders, fonts, theme tokens), check the component in the docs site or in a consuming app, for example by screenshotting a running
just docs-serve. When reporting, say which results came from unit tests and which from a rendered check.just test-client just test-client tests/unit/lib/views/ViewActionRouter.spec.js -
View coverage:
just coverage-client(accepts extra vitest args)just coverage-client -
This project uses Vite as its development server and bundler.
Package Scripts
Scripts defined in package.json:
-
test -
npx --no-install vitest run -
coverage -
npm test -- run --coverage -
lint -
pnpm -C .. exec eslint --no-warn-ignored --cache client -
format -
pnpm -C .. exec prettier --check client -
eslint -
pnpm -C .. exec eslint --no-warn-ignored --cache --fix client -
prettier -
pnpm -C .. exec prettier --write client
Commit Message Style
We use a custom commitlint configuration based on Conventional Commits. It is customized to have the following valid types:
build, ci, chore, content, docs, feat, fix, perf, refactor, remove, revert, style, test, wip
Example:
fix(WidgetSearchableSelect): correct options grouping
The scope should reference the affected filename (sans extension), module, or concern.
Changelog
Public client changelog entries belong in docs/reference/changelog/client.md.
Before adding or editing entries, consult
../docs/reference/changelog/README.md for the shared authoring convention.
Add entries for changes that affect integrators: public components, composables, routes, stores, theme behavior, build integration, dependency expectations, documented behavior, and migration notes.
For each release tag, use the following format:
## vX.Y.Z (2025-MM-DD)
### Breaking Changes
### Features
### Fixes
- **File Name or Component Name**:
- individual notes
- _actions that consuming developers of the library should take_
When making changes, suggest changelog entries if they impact consuming applications or public components.
If there is no current unreleased section, start a new one using the next version number and set the date to unreleased. For example:
## v2.0.0-beta.1 (unreleased)
### Breaking Changes
- **ActionForm**:
- The `handleActionCompletion` prop has been removed. Redirection after action completion now uses the model config's `defaultView`, which prefers `update`, `read`, then `list` in that order.
_If your use case required a custom post-action redirect, update the model config accordingly._
### Features
### Fixes
API Documentation Annotations
The docs-tooling pipeline reads several annotation conventions from client/lib/ source files. The authoritative contract lives in the docs-tooling package next to the extractors that enforce it:
When editing source annotations that affect generated API docs, consult
../docs-tooling/briefings/client-annotations.md.
JSDoc Type Style
When writing TypeScript types in JSDoc contexts, prefer literal syntax over utility generics.
Arrays: Use bracket syntax instead of Array<T>. For complex element types (function signatures, unions), wrap the element type in parentheses:
/** @type {string[]} */
/** @type {(() => void)[]} */
/** @type {(string | number)[]} */
Objects: Use index signature syntax instead of Record<K, V>:
/** @type {{ [key: string]: Foo }} */
Composable and Utility JSDoc
The rules above cover Vue SFCs. The following additional conventions apply to all .js files under lib/use/ and lib/utils/.
Module header
Every file opens with a @module tag matching its import path, followed by a @description:
/**
* @module use/useField
* @description Provides reactive field context including value tracking, validation, and error management.
*/
Typedefs
Define a @typedef for every non-trivial object or options bag that crosses a function boundary. Use @property entries for each member. Mark optional properties with brackets:
/**
* @typedef {object} TextValidationOptions
* @property {number} [maxLength] - Maximum character count.
* @property {number} [minLength] - Minimum character count.
* @property {string} [patternRegex] - Regex the value must match after the field is touched.
*/
Group properties with plain-text section headers when the typedef has more than ~8 members:
/**
* @typedef {object} FieldContextRawState
*
* Identification and metadata.
* @property {import('vue').ComputedRef<string>} name - The field name.
* ...
*
* Validation state.
* @property {import('vue').ComputedRef<boolean>} required - Whether the field is required.
* ...
*/
Reactive return types
Composables that return reactive state should document the unwrapped shape using the three-tier pattern established by useField:
*RawStatetypedef withComputedRef<T>/Ref<T>property types (the shape beforereactive()wrapping).*Statetypedef asimport('vue').UnwrapNestedRefs<*RawState>(the shape consumers interact with).*Contexttypedef combiningstatewith any methods.
When a composable does not return state (only produces side effects like registering watches), document @returns {void} explicitly.
Function signatures
Every exported function has @param and @returns tags. Use inline import paths for Vue and internal types:
/**
* Registers reactive text validation watches on a field context.
*
* @param {import('@vueda/use/useField.js').FieldContext} fieldContext - The field context to validate against.
* @param {TextValidationOptions} options - Constraint configuration.
* @returns {void}
*/
export function useTextValidation(fieldContext, options) { ... }
Provide/inject annotations
Annotate provide() calls with /** @type {TypeName} */ on the context object. Annotate inject() calls with the expected type including null:
/** @type {import('@vueda/use/useField.js').FieldContext|null} */
const fieldContext = inject(FieldContextSymbol, null);
Private/unexported functions
Private helpers do not appear in generated API docs, but they benefit from @param/@returns type annotations for IDE inference (autocomplete, hover tooltips, inline errors). Prose descriptions are optional; bare types are enough:
/**
* @param {string|Date} raw
* @returns {import('luxon').DateTime|null}
*/
function parseToDate(raw) { ... }
Complete type annotations also position the codebase for future .d.ts generation from JSDoc.
Test Structure and Isolation
-
Use
scopedIt(...)from@tests/unit/utils.jsin place ofit(...)for all tests involving Vue components, reactivity, lifecycle hooks, or injections. This runs tests in a fresheffectScope()to prevent state leakage. -
Wrap all test files in a root
describe("<source file path>", ...)block (e.g.describe("lib/views/ActionForm.vue", ...)) to clearly associate tests with their source. This improves readability, traceability in CI, and allows behavioral grouping inside without losing context. -
If a spec intentionally covers a set of source files, use a source-like pseudo-glob in the root
describe(...)block (e.g.describe("lib/**/*.vue", ...)). Put the contract or behavior name in a nesteddescribe(...)block. -
Group related tests with
describe(...)blocks that reflect behavioral responsibilities, not implementation details (e.g."Confirm flow"or"Rendering with slots").
Test Execution
For fast feedback during development, run only the spec file you are currently working on:
pnpm -C client test run tests/unit/lib/views/MyComponent.spec.js
Multiple files or a glob may be passed if the feature spans more than one spec.
Run the full suite before marking a task complete or opening a PR:
pnpm -C client test run
Terse output
For a compact summary, use --reporter=dot (one character per test) or --reporter=basic (one line per file):
pnpm -C client test run --reporter=dot
pnpm -C client test run tests/unit/lib/views/MyComponent.spec.js --reporter=basic
To stop on the first failure, add --bail=1:
pnpm -C client test run --bail=1 --reporter=dot
Piping caution
Do not pipe test output through head or tail. Vitest spawns multiple worker processes; when head/tail exits early and sends SIGPIPE, the workers may not terminate cleanly and will continue consuming memory in the background. Running the suite again before those workers die compounds the problem and can OOM the system.
To capture output for later inspection, redirect to a file instead:
pnpm -C client test run > /tmp/test-out.txt 2>&1
grep "FAIL\|×" /tmp/test-out.txt
Coverage Limitations
Running less than the full suite with coverage is not recommended, due to how coverage is collected by istanbul and v8.
Spec files that dynamically import components (e.g. for mocking) may not produce reliable coverage output in isolation. Coverage for a single file is often misleading. Always run the full suite for coverage:
pnpm -C client run coverage