Kaizen
Browse modulesFormsforms/serverClasses

Class: FormSchemaService

Defined in: server/forms/form-schema-service.ts:61

Extends

Constructors

Constructor

new FormSchemaService(deps: FormFeatureDeps): FormSchemaService;

Defined in: server/forms/base-form-service.ts:5

Parameters

ParameterType
depsFormFeatureDeps

Returns

FormSchemaService

Inherited from

BaseFormService.constructor

Properties

deps

protected readonly deps: FormFeatureDeps;

Defined in: server/forms/base-form-service.ts:5

Inherited from

BaseFormService.deps

Accessors

actorId

Get Signature

get protected actorId(): string | null;

Defined in: server/forms/base-form-service.ts:7

Returns

string | null

Inherited from

BaseFormService.actorId

Methods

assertVersionUnpublished()

protected assertVersionUnpublished(versionId: string): Promise<void>;

Defined in: server/forms/base-form-service.ts:28

Guard that rejects mutations against a published FormSchemaVersion. Once a version is published the DAG (steps, transitions, entry-step key) is frozen — editors must spawn a fresh draft version with FormSchemaService.createDraftVersion to make further changes.

Centralised here so step / transition / future services share one invariant and one error message.

Parameters

ParameterType
versionIdstring

Returns

Promise<void>

Inherited from

BaseFormService.assertVersionUnpublished


attachRule()

attachRule(formSchemaId: string, ruleId: string): Promise<void>;

Defined in: server/forms/form-schema-service.ts:378

Attach a rule to this schema's rule pool. Idempotent — re-attaching an already-attached rule is a no-op. The publish validator enforces that every FormStepTransitions.ruleId referenced by this schema resolves to an attached rule, so callers should attach before authoring the transition.

Throws FormNotFoundError("FormSchema", id) if the schema is missing or soft-deleted. Doesn't check Rule existence directly — the DB FK rejects unknown rule ids.

Parameters

ParameterType
formSchemaIdstring
ruleIdstring

Returns

Promise<void>


create()

create(input: {
  name: string;
  schema: {
     fields: {
        additionalInfo?: {
           body?: string;
           title?: string;
        };
        adoptionStatement?: string;
        allowedFonts?: string[];
        asset?: {
           alt?: string;
           fileId?: string;
           filename?: string;
           mimeType?: string;
           url?: string;
        };
        dataSource?: string;
        defaultValue?:   | string
           | {
           from: string;
           to: string | null;
         };
        description?: string;
        fields?: {
           additionalInfo?: {
              body?: string;
              title?: string;
           };
           adoptionStatement?: string;
           allowedFonts?: string[];
           asset?: {
              alt?: string;
              fileId?: string;
              filename?: string;
              mimeType?: string;
              url?: string;
           };
           dataSource?: string;
           defaultValue?:   | string
              | {
              from: string;
              to: string | null;
            };
           description?: string;
           format?: string;
           label: string;
           maxDate?: string;
           maxFiles?: number;
           maxLength?: number;
           minDate?: string;
           name: string;
           options?: (
              | string
              | {
              description?: ...;
              value: ...;
           })[];
           placeholder?: string;
           prefillFrom?: string;
           required: boolean;
           showRange?: boolean;
           showTime?: boolean;
           signatureMode?: "typed" | "drawn";
           timezone?: string;
           type:   | "number"
              | "date"
              | "file"
              | "email"
              | "url"
              | "select"
              | "text"
              | "checkbox"
              | "textarea"
              | "phone"
              | "radio-group"
              | "multiselect"
              | "field-array"
              | "image"
              | "file-download"
              | "section-header"
              | "section-unset"
              | "signature"
              | "signature-adopted";
        }[];
        format?: string;
        label: string;
        maxDate?: string;
        maxFiles?: number;
        maxLength?: number;
        maxRows?: number;
        minDate?: string;
        minRows?: number;
        name: string;
        options?: (
           | string
           | {
           description?: string;
           value: string;
        })[];
        placeholder?: string;
        prefillFrom?: string;
        required: boolean;
        showRange?: boolean;
        showTime?: boolean;
        signatureMode?: "typed" | "drawn";
        timezone?: string;
        type:   | "number"
           | "date"
           | "file"
           | "email"
           | "url"
           | "select"
           | "text"
           | "checkbox"
           | "textarea"
           | "phone"
           | "radio-group"
           | "multiselect"
           | "field-array"
           | "image"
           | "file-download"
           | "section-header"
           | "section-unset"
           | "signature"
           | "signature-adopted";
     }[];
  };
  type: string;
}): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:154

Parameters

ParameterType
input{ name: string; schema: { fields: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ...; value: ...; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; }; type: string; }
input.namestring
input.schema{ fields: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ...; value: ...; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; }
input.schema.fields{ additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ...; value: ...; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]
input.typestring

Returns

Promise<FormSchemaWithVersion>


createDraftVersion()

createDraftVersion(formSchemaId: string, input?: {
  changeReason?: string;
  entryStepKey?: string | null;
  name?: string;
  renderMode?: "wizard" | "single-page";
}): Promise<FormSchemaVersion>;

Defined in: server/forms/form-schema-service.ts:425

Create a fresh draft version on an existing schema. Used by the multi-step builder: the caller picks up steps / transitions / conditions on the returned version, then calls publishVersion to flip publishedAt and advance currentVersionId.

Optional name / renderMode flow through to the parent schema. The legacy schema JSON column is left null — multi-step versions render off FormSteps rather than the column, and the column is nullable precisely so we don't carry dummy { fields: [] } blobs around.

Single open draft per schema: throws FormValidationError if the schema already has an open (unpublished, non-soft-deleted) draft. "The draft" is canonical — author it, then publishVersion or discardVersion it before starting another. Callers that want resume-or-create semantics should use getOrCreateDraft instead. The guard is enforced in the service layer (mirroring submission createDraft); see the forms docs for the residual-concurrency caveat.

Parameters

ParameterType
formSchemaIdstring
input{ changeReason?: string; entryStepKey?: string | null; name?: string; renderMode?: "wizard" | "single-page"; }
input.changeReason?string
input.entryStepKey?string | null
input.name?string
input.renderMode?"wizard" | "single-page"

Returns

Promise<FormSchemaVersion>


createSingleStepForm()

createSingleStepForm(input: {
  fields: {
     additionalInfo?: {
        body?: string;
        title?: string;
     };
     adoptionStatement?: string;
     allowedFonts?: string[];
     asset?: {
        alt?: string;
        fileId?: string;
        filename?: string;
        mimeType?: string;
        url?: string;
     };
     dataSource?: string;
     defaultValue?:   | string
        | {
        from: string;
        to: string | null;
      };
     description?: string;
     fields?: {
        additionalInfo?: {
           body?: string;
           title?: string;
        };
        adoptionStatement?: string;
        allowedFonts?: string[];
        asset?: {
           alt?: string;
           fileId?: string;
           filename?: string;
           mimeType?: string;
           url?: string;
        };
        dataSource?: string;
        defaultValue?:   | string
           | {
           from: string;
           to: string | null;
         };
        description?: string;
        format?: string;
        label: string;
        maxDate?: string;
        maxFiles?: number;
        maxLength?: number;
        minDate?: string;
        name: string;
        options?: (
           | string
           | {
           description?: ... | ...;
           value: string;
        })[];
        placeholder?: string;
        prefillFrom?: string;
        required: boolean;
        showRange?: boolean;
        showTime?: boolean;
        signatureMode?: "typed" | "drawn";
        timezone?: string;
        type:   | "number"
           | "date"
           | "file"
           | "email"
           | "url"
           | "select"
           | "text"
           | "checkbox"
           | "textarea"
           | "phone"
           | "radio-group"
           | "multiselect"
           | "field-array"
           | "image"
           | "file-download"
           | "section-header"
           | "section-unset"
           | "signature"
           | "signature-adopted";
     }[];
     format?: string;
     label: string;
     maxDate?: string;
     maxFiles?: number;
     maxLength?: number;
     maxRows?: number;
     minDate?: string;
     minRows?: number;
     name: string;
     options?: (
        | string
        | {
        description?: string;
        value: string;
     })[];
     placeholder?: string;
     prefillFrom?: string;
     required: boolean;
     showRange?: boolean;
     showTime?: boolean;
     signatureMode?: "typed" | "drawn";
     timezone?: string;
     type:   | "number"
        | "date"
        | "file"
        | "email"
        | "url"
        | "select"
        | "text"
        | "checkbox"
        | "textarea"
        | "phone"
        | "radio-group"
        | "multiselect"
        | "field-array"
        | "image"
        | "file-download"
        | "section-header"
        | "section-unset"
        | "signature"
        | "signature-adopted";
  }[];
  name: string;
  type: string;
}): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:809

Create a brand-new schema with a born-published Shape A version carrying one FormSteps row keyed SINGLE_STEP_KEY.

Single Prisma transaction — schema row, version row, step row, and currentVersionId advance commit together or roll back together. Version history starts at v1.

renderMode is set to "single-page" so submission flows that branch on it (e.g. finalizeSinglePage) work without further setup.

Parameters

ParameterType
input{ fields: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ... | ...; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; name: string; type: string; }
input.fields{ additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ... | ...; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]
input.namestring
input.typestring

Returns

Promise<FormSchemaWithVersion>


createVersion()

createVersion(id: string, input: {
  changeReason?: string;
  name?: string;
  schema: {
     fields: {
        additionalInfo?: {
           body?: string;
           title?: string;
        };
        adoptionStatement?: string;
        allowedFonts?: string[];
        asset?: {
           alt?: string;
           fileId?: string;
           filename?: string;
           mimeType?: string;
           url?: string;
        };
        dataSource?: string;
        defaultValue?:   | string
           | {
           from: string;
           to: string | null;
         };
        description?: string;
        fields?: {
           additionalInfo?: {
              body?: string;
              title?: string;
           };
           adoptionStatement?: string;
           allowedFonts?: string[];
           asset?: {
              alt?: string;
              fileId?: string;
              filename?: string;
              mimeType?: string;
              url?: string;
           };
           dataSource?: string;
           defaultValue?:   | string
              | {
              from: string;
              to: string | null;
            };
           description?: string;
           format?: string;
           label: string;
           maxDate?: string;
           maxFiles?: number;
           maxLength?: number;
           minDate?: string;
           name: string;
           options?: (
              | string
              | {
              description?: ...;
              value: ...;
           })[];
           placeholder?: string;
           prefillFrom?: string;
           required: boolean;
           showRange?: boolean;
           showTime?: boolean;
           signatureMode?: "typed" | "drawn";
           timezone?: string;
           type:   | "number"
              | "date"
              | "file"
              | "email"
              | "url"
              | "select"
              | "text"
              | "checkbox"
              | "textarea"
              | "phone"
              | "radio-group"
              | "multiselect"
              | "field-array"
              | "image"
              | "file-download"
              | "section-header"
              | "section-unset"
              | "signature"
              | "signature-adopted";
        }[];
        format?: string;
        label: string;
        maxDate?: string;
        maxFiles?: number;
        maxLength?: number;
        maxRows?: number;
        minDate?: string;
        minRows?: number;
        name: string;
        options?: (
           | string
           | {
           description?: string;
           value: string;
        })[];
        placeholder?: string;
        prefillFrom?: string;
        required: boolean;
        showRange?: boolean;
        showTime?: boolean;
        signatureMode?: "typed" | "drawn";
        timezone?: string;
        type:   | "number"
           | "date"
           | "file"
           | "email"
           | "url"
           | "select"
           | "text"
           | "checkbox"
           | "textarea"
           | "phone"
           | "radio-group"
           | "multiselect"
           | "field-array"
           | "image"
           | "file-download"
           | "section-header"
           | "section-unset"
           | "signature"
           | "signature-adopted";
     }[];
  };
}): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:196

Parameters

ParameterType
idstring
input{ changeReason?: string; name?: string; schema: { fields: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ...; value: ...; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; }; }
input.changeReason?string
input.name?string
input.schema{ fields: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ...; value: ...; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; }
input.schema.fields{ additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ...; value: ...; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]

Returns

Promise<FormSchemaWithVersion>


detachRule()

detachRule(formSchemaId: string, ruleId: string): Promise<void>;

Defined in: server/forms/form-schema-service.ts:389

Detach a rule from this schema's rule pool. Idempotent — detaching a rule that isn't attached is a no-op. Does not check whether the rule is still referenced by a transition; the publish validator surfaces that at publish time.

Parameters

ParameterType
formSchemaIdstring
ruleIdstring

Returns

Promise<void>


discardDraft()

discardDraft(formSchemaId: string): Promise<void>;

Defined in: server/forms/form-schema-service.ts:733

Discard the schema's open draft (resume-or-fail convenience over discardVersion). Resolves the open draft and discards it.

Throws FormNotFoundError("ActiveDraft", formSchemaId) if the schema has no open draft.

Parameters

ParameterType
formSchemaIdstring

Returns

Promise<void>


discardVersion()

discardVersion(versionId: string): Promise<void>;

Defined in: server/forms/form-schema-service.ts:594

Discard (soft-delete) an unpublished draft version, cascading to its child rows. This is the only path that stamps FormSchemaVersions.deletedAt; every repo read filters deletedAt: null, so a discarded version disappears from all reads. Step-count-agnostic: works identically for a 1-step or a 5-step branching draft.

Guards (drafts only):

  • Throws FormNotFoundError if the version is missing or already soft-deleted (every read filters deletedAt: null, so a re-discard surfaces a clean error, never a partial write).
  • Throws FormValidationError if the version is published, or is the schema's currentVersionId — discarding the live pointer would orphan it.

In one transaction it soft-deletes the version's FormStepTransitions and FormSteps (children first) before the version itself, so reads stay internally consistent.

Only-version edge case: allowed. Discarding a schema's sole (draft) version leaves it with no current version — a state reads already tolerate (getByIdWithCurrentVersion / getAllActive handle a null currentVersionId).

Parameters

ParameterType
versionIdstring

Returns

Promise<void>


getAllActive()

getAllActive(): Promise<FormSchemaWithVersion[]>;

Defined in: server/forms/form-schema-service.ts:135

Returns

Promise<FormSchemaWithVersion[]>


getById()

getById(id: string): Promise<FormSchema>;

Defined in: server/forms/form-schema-service.ts:65

Get just the form schema record without any versions

Parameters

ParameterType
idstring

Returns

Promise<FormSchema>


getByIdWithAllVersions()

getByIdWithAllVersions(id: string): Promise<FormSchemaWithVersions>;

Defined in: server/forms/form-schema-service.ts:90

Get the form schema record with all versions

Parameters

ParameterType
idstring

Returns

Promise<FormSchemaWithVersions>


getByIdWithCurrentVersion()

getByIdWithCurrentVersion(id: string): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:73

Get the form schema record with only the current version

Parameters

ParameterType
idstring

Returns

Promise<FormSchemaWithVersion>


getByType()

getByType(type: string): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:121

Parameters

ParameterType
typestring

Returns

Promise<FormSchemaWithVersion>


getEditableVersion()

getEditableVersion(formSchemaId: string): Promise<EditableVersionResult>;

Defined in: server/forms/form-schema-service.ts:660

The schema's effective working version: the open draft if one exists, else the published current version. Read-only — it returns the version row plus a hasDraft flag and leaves reading steps/fields off it to the caller (field aggregation differs by step count, so this method stays out of that business).

Throws FormNotFoundError("FormSchemaVersion", formSchemaId) if the schema has neither an open draft nor a current version.

Parameters

ParameterType
formSchemaIdstring

Returns

Promise<EditableVersionResult>


getOrCreateDraft()

getOrCreateDraft(formSchemaId: string, opts?: {
  entryStepKey?: string | null;
  renderMode?: "wizard" | "single-page";
}): Promise<FormSchemaVersion>;

Defined in: server/forms/form-schema-service.ts:695

Best-effort idempotent: returns the schema's open draft if one exists, otherwise forks a fresh draft via createDraftVersion. The managed entry point for "give me something editable" — mirrors submission createDraft's resume-or-create semantics.

Resumes on a lost create-race: if createDraftVersion's single-draft guard fires because a concurrent transaction opened the draft after our initial check (visible mid-transaction under READ COMMITTED), we re-resolve and return that draft instead of surfacing the guard error. This is best-effort, not race-tight — the DB-level single-draft guard (a partial unique index) remains deferred; see the forms docs.

Does not copy step content: a freshly-forked draft starts empty (no steps/transitions cloned from the current version). The caller authors steps, then calls publishVersion(draft.id) or discardVersion(draft.id).

Parameters

ParameterType
formSchemaIdstring
opts{ entryStepKey?: string | null; renderMode?: "wizard" | "single-page"; }
opts.entryStepKey?string | null
opts.renderMode?"wizard" | "single-page"

Returns

Promise<FormSchemaVersion>


getSingleStepFields()

getSingleStepFields(formSchemaId: string): Promise<{
  additionalInfo?: {
     body?: string;
     title?: string;
  };
  adoptionStatement?: string;
  allowedFonts?: string[];
  asset?: {
     alt?: string;
     fileId?: string;
     filename?: string;
     mimeType?: string;
     url?: string;
  };
  dataSource?: string;
  defaultValue?:   | string
     | {
     from: string;
     to: string | null;
   };
  description?: string;
  fields?: {
     additionalInfo?: {
        body?: string;
        title?: string;
     };
     adoptionStatement?: string;
     allowedFonts?: string[];
     asset?: {
        alt?: string;
        fileId?: string;
        filename?: string;
        mimeType?: string;
        url?: string;
     };
     dataSource?: string;
     defaultValue?:   | string
        | {
        from: string;
        to: string | null;
      };
     description?: string;
     format?: string;
     label: string;
     maxDate?: string;
     maxFiles?: number;
     maxLength?: number;
     minDate?: string;
     name: string;
     options?: (
        | string
        | {
        description?: ... | ...;
        value: string;
     })[];
     placeholder?: string;
     prefillFrom?: string;
     required: boolean;
     showRange?: boolean;
     showTime?: boolean;
     signatureMode?: "typed" | "drawn";
     timezone?: string;
     type:   | "number"
        | "date"
        | "file"
        | "email"
        | "url"
        | "select"
        | "text"
        | "checkbox"
        | "textarea"
        | "phone"
        | "radio-group"
        | "multiselect"
        | "field-array"
        | "image"
        | "file-download"
        | "section-header"
        | "section-unset"
        | "signature"
        | "signature-adopted";
  }[];
  format?: string;
  label: string;
  maxDate?: string;
  maxFiles?: number;
  maxLength?: number;
  maxRows?: number;
  minDate?: string;
  minRows?: number;
  name: string;
  options?: (
     | string
     | {
     description?: string;
     value: string;
  })[];
  placeholder?: string;
  prefillFrom?: string;
  required: boolean;
  showRange?: boolean;
  showTime?: boolean;
  signatureMode?: "typed" | "drawn";
  timezone?: string;
  type:   | "number"
     | "date"
     | "file"
     | "email"
     | "url"
     | "select"
     | "text"
     | "checkbox"
     | "textarea"
     | "phone"
     | "radio-group"
     | "multiselect"
     | "field-array"
     | "image"
     | "file-download"
     | "section-header"
     | "section-unset"
     | "signature"
     | "signature-adopted";
}[]>;

Defined in: server/forms/form-schema-service.ts:915

Return the flat FormField[] for the schema's current single-step row. Mirrors updateSingleStepSchema's shape guard so the read and write surfaces stay symmetric: any form getSingleStepFields returns can be passed back through updateSingleStepSchema.

Throws FormValidationError on Shape B or multi-step forms. Callers (e.g. a single-step builder page) can catch the error to render a locked / read-only state instead of loading uneditable fields.

Throws FormNotFoundError("FormSchemaVersion", formSchemaId) if the schema exists but has no current version.

Parameters

ParameterType
formSchemaIdstring

Returns

Promise<{ additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ... | ...; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]>


getVersion()

getVersion(versionId: string): Promise<FormSchemaVersion>;

Defined in: server/forms/form-schema-service.ts:148

Parameters

ParameterType
versionIdstring

Returns

Promise<FormSchemaVersion>


listAttachedRuleIds()

listAttachedRuleIds(formSchemaId: string): Promise<string[]>;

Defined in: server/forms/form-schema-service.ts:400

Return the rule ids attached to this schema's rule pool, in attachment order. The caller is responsible for joining against Rules rows if it needs full rule metadata — this is intentionally a thin id list so the forms service stays a leaf consumer of the rules engine's public surface.

Parameters

ParameterType
formSchemaIdstring

Returns

Promise<string[]>


publishVersion()

publishVersion(versionId: string): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:481

Publish a draft version atomically. Validates the DAG against the spec's publish-time invariants, pins each transition's ruleVersionId to the referenced rule's currentVersionId (so runtime evaluation always uses the snapshot), flips publishedAt, and advances the parent schema's currentVersionId — all in one Prisma transaction so a partial failure leaves no orphan state.

Parameters

ParameterType
versionIdstring

Returns

Promise<FormSchemaWithVersion>

Throws

FormPublishValidationError with structured errors when any invariant is violated, so the builder UI can surface each issue with the offending step / transition / rule context.


setActive()

setActive(id: string, isActive: boolean): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:320

Toggle whether new submissions can be created against this schema. Activating refuses to flip when the current version is still a draft — exposing a half-built form as "active" would let users start drafts against an unpublished version. Deactivating is always allowed (admins may need to pull a broken form offline regardless of publish state).

Parameters

ParameterType
idstring
isActiveboolean

Returns

Promise<FormSchemaWithVersion>


setAllowUserEditing()

setAllowUserEditing(id: string, allowed: boolean): Promise<FormSchema>;

Defined in: server/forms/form-schema-service.ts:355

Set the default for new submissions against this schema. Existing submissions keep their individual allowUserEditingOverride value.

Parameters

ParameterType
idstring
allowedboolean

Returns

Promise<FormSchema>


softDelete()

softDelete(id: string): Promise<FormSchema>;

Defined in: server/forms/form-schema-service.ts:362

Parameters

ParameterType
idstring

Returns

Promise<FormSchema>


transaction()

protected transaction<T>(fn: (deps: FormFeatureDeps) => Promise<T>): Promise<T>;

Defined in: server/forms/base-form-service.ts:11

Type Parameters

Type Parameter
T

Parameters

ParameterType
fn(deps: FormFeatureDeps) => Promise<T>

Returns

Promise<T>

Inherited from

BaseFormService.transaction


updateSingleStepSchema()

updateSingleStepSchema(formSchemaId: string, input: {
  changeReason?: string;
  fields: {
     additionalInfo?: {
        body?: string;
        title?: string;
     };
     adoptionStatement?: string;
     allowedFonts?: string[];
     asset?: {
        alt?: string;
        fileId?: string;
        filename?: string;
        mimeType?: string;
        url?: string;
     };
     dataSource?: string;
     defaultValue?:   | string
        | {
        from: string;
        to: string | null;
      };
     description?: string;
     fields?: {
        additionalInfo?: {
           body?: string;
           title?: string;
        };
        adoptionStatement?: string;
        allowedFonts?: string[];
        asset?: {
           alt?: string;
           fileId?: string;
           filename?: string;
           mimeType?: string;
           url?: string;
        };
        dataSource?: string;
        defaultValue?:   | string
           | {
           from: string;
           to: string | null;
         };
        description?: string;
        format?: string;
        label: string;
        maxDate?: string;
        maxFiles?: number;
        maxLength?: number;
        minDate?: string;
        name: string;
        options?: (
           | string
           | {
           description?: ... | ...;
           value: string;
        })[];
        placeholder?: string;
        prefillFrom?: string;
        required: boolean;
        showRange?: boolean;
        showTime?: boolean;
        signatureMode?: "typed" | "drawn";
        timezone?: string;
        type:   | "number"
           | "date"
           | "file"
           | "email"
           | "url"
           | "select"
           | "text"
           | "checkbox"
           | "textarea"
           | "phone"
           | "radio-group"
           | "multiselect"
           | "field-array"
           | "image"
           | "file-download"
           | "section-header"
           | "section-unset"
           | "signature"
           | "signature-adopted";
     }[];
     format?: string;
     label: string;
     maxDate?: string;
     maxFiles?: number;
     maxLength?: number;
     maxRows?: number;
     minDate?: string;
     minRows?: number;
     name: string;
     options?: (
        | string
        | {
        description?: string;
        value: string;
     })[];
     placeholder?: string;
     prefillFrom?: string;
     required: boolean;
     showRange?: boolean;
     showTime?: boolean;
     signatureMode?: "typed" | "drawn";
     timezone?: string;
     type:   | "number"
        | "date"
        | "file"
        | "email"
        | "url"
        | "select"
        | "text"
        | "checkbox"
        | "textarea"
        | "phone"
        | "radio-group"
        | "multiselect"
        | "field-array"
        | "image"
        | "file-download"
        | "section-header"
        | "section-unset"
        | "signature"
        | "signature-adopted";
  }[];
  name?: string;
}): Promise<FormSchemaWithVersion>;

Defined in: server/forms/form-schema-service.ts:857

Publish a new Shape A version of an existing single-step form. Single Prisma transaction: guard reads, cross-version field-type validation, version + step writes, and currentVersionId advance commit together. History (prior version + its FormSteps row) is preserved untouched.

Throws FormValidationError if the form's current version is:

  • Shape B — FormSchemaVersions.schema populated, entryStepKey = null. Callers must use createVersion until a Shape B → Shape A backfill ships.
  • Multi-step — more than one FormSteps row or any FormStepTransitions row on the current version. Use the step / transition services directly.

Parameters

ParameterType
formSchemaIdstring
input{ changeReason?: string; fields: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ... | ...; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; name?: string; }
input.changeReason?string
input.fields{ additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; fields?: { additionalInfo?: { body?: string; title?: string; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: string; fileId?: string; filename?: string; mimeType?: string; url?: string; }; dataSource?: string; defaultValue?: | string | { from: string; to: string | null; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: ( | string | { description?: ... | ...; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; maxRows?: number; minDate?: string; minRows?: number; name: string; options?: ( | string | { description?: string; value: string; })[]; placeholder?: string; prefillFrom?: string; required: boolean; showRange?: boolean; showTime?: boolean; signatureMode?: "typed" | "drawn"; timezone?: string; type: | "number" | "date" | "file" | "email" | "url" | "select" | "text" | "checkbox" | "textarea" | "phone" | "radio-group" | "multiselect" | "field-array" | "image" | "file-download" | "section-header" | "section-unset" | "signature" | "signature-adopted"; }[]
input.name?string

Returns

Promise<FormSchemaWithVersion>

On this page