dimah-formv0.6.1
Plugins

Scoring

Compute Likert totals, keying, bands, and missing-item policies from snapshots.

@dimah-form/scoring computes named scores from a response definition snapshot and its answers. It never writes scores into answers and adds no tables.

npm i @dimah-form/scoring
lib/form-plugins.ts
import { scoringPlugin } from "@dimah-form/scoring";
import { createDefineForm } from "@dimah-form/server";

export const plugins = [scoringPlugin()] as const;
export const defineScoredForm = createDefineForm({ plugins });
lib/form.ts
import { dimahForm } from "@dimah-form/server";
import { plugins } from "@/lib/form-plugins";
import { forms } from "@/lib/forms";

export const form = dimahForm({
  database,
  forms,
  plugins,
});

For browser-side previews and typed plugin endpoints, add the matching scoringClientPlugin() from @dimah-form/scoring/client. Server plugins are not inferred by createFormClient. Authorize getResponseScores as you would any other admin operation.

lib/form-client.ts
import { scoringClientPlugin } from "@dimah-form/scoring/client";
import { createFormClient } from "@dimah-form/react";
import type { Form } from "@/lib/form";

const plugins = [scoringClientPlugin()] as const;
export const formClient = createFormClient<Form, typeof plugins>({ plugins });

Use the root entry on the server, /client in browser bundles, and @dimah-form/scoring/document in isomorphic packages that need document readers without loading the server plugin.

Author a score

There is no likert field type. Use select, multiSelect, number, or boolean, then configure scoring in meta.scoring.

lib/forms/gad7.ts
import { defineScoredForm } from "@/lib/form-plugins";

const likert = [
  { value: "0", label: "Not at all", meta: { scoring: { points: 0 } } },
  { value: "1", label: "Several days", meta: { scoring: { points: 1 } } },
  { value: "2", label: "More than half", meta: { scoring: { points: 2 } } },
  { value: "3", label: "Nearly every day", meta: { scoring: { points: 3 } } },
];

export const gad7 = defineScoredForm({
  title: "GAD-7",
  meta: {
    scoring: {
      variables: [{ id: "gad7", label: "GAD-7", min: 0, max: 21 }],
      bands: [
        { variable: "gad7", from: 0, to: 4, label: "Minimal" },
        { variable: "gad7", from: 5, to: 9, label: "Mild" },
        { variable: "gad7", from: 10, to: 14, label: "Moderate" },
        { variable: "gad7", from: 15, to: 21, label: "Severe" },
      ],
    },
  },
  fields: [
    {
      id: "q1",
      type: "select",
      options: likert,
      meta: { scoring: { variable: "gad7" } },
    },
    // Add q2 through q7 with the same mapping.
  ],
});

option.points contributes to a field's variable. Use option.add when one choice contributes to one or more variables; never mix it with points. reverse works for select, number, and boolean, not multiSelect.

import type {
  ScoringAdd,
  ScoringBand,
  ScoringFieldMeta,
  ScoringFormMeta,
  ScoringFormula,
  ScoringPluginOptions,
  ScoringVariable,
} from "@dimah-form/scoring";

ScoringPluginOptions

Prop

Type

ScoringFormMeta

Prop

Type

ScoringVariable

Prop

Type

ScoringBand

Prop

Type

ScoringFormula

Prop

Type

ScoringFieldMeta

Prop

Type

ScoringAdd

Likert options use { points } on option.meta.scoring instead. Never mix points and add.

Prop

Type

Read results

import { scoreResponse } from "@dimah-form/scoring/client";

const preview = scoreResponse(session.snapshot, session.answers);
const stored = await form.api.getResponseScores({ query: { responseId } });

Compute with the session snapshot or stored response.definition, never a later live form.

import type { ScoreResult } from "@dimah-form/scoring";

Prop

Type

Missing items default to "incomplete". Set a variable's missing policy to "zero" or "omit" when the instrument requires it. Bands are labels; formulas support sums of distinct variables only.

onScore runs after submit persistence. It may project scores to your own database, but should not mutate response.answers.

Next

On this page