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
| Parameter | Type |
|---|---|
deps | FormFeatureDeps |
Returns
FormSchemaService
Inherited from
Properties
deps
protected readonly deps: FormFeatureDeps;Defined in: server/forms/base-form-service.ts:5
Inherited from
Accessors
actorId
Get Signature
get protected actorId(): string | null;Defined in: server/forms/base-form-service.ts:7
Returns
string | null
Inherited from
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
| Parameter | Type |
|---|---|
versionId | string |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
ruleId | string |
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
| Parameter | Type |
|---|---|
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.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"; }[] |
input.type | string |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
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
| Parameter | Type |
|---|---|
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.name | string |
input.type | string |
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
| Parameter | Type |
|---|---|
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"; }[]; }; } |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
ruleId | string |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
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
FormNotFoundErrorif the version is missing or already soft-deleted (every read filtersdeletedAt: null, so a re-discard surfaces a clean error, never a partial write). - Throws
FormValidationErrorif the version is published, or is the schema'scurrentVersionId— 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
| Parameter | Type |
|---|---|
versionId | string |
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
| Parameter | Type |
|---|---|
id | string |
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
| Parameter | Type |
|---|---|
id | string |
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
| Parameter | Type |
|---|---|
id | string |
Returns
Promise<FormSchemaWithVersion>
getByType()
getByType(type: string): Promise<FormSchemaWithVersion>;Defined in: server/forms/form-schema-service.ts:121
Parameters
| Parameter | Type |
|---|---|
type | string |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
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
| Parameter | Type |
|---|---|
versionId | string |
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
| Parameter | Type |
|---|---|
formSchemaId | string |
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
| Parameter | Type |
|---|---|
versionId | string |
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
| Parameter | Type |
|---|---|
id | string |
isActive | boolean |
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
| Parameter | Type |
|---|---|
id | string |
allowed | boolean |
Returns
Promise<FormSchema>
softDelete()
softDelete(id: string): Promise<FormSchema>;Defined in: server/forms/form-schema-service.ts:362
Parameters
| Parameter | Type |
|---|---|
id | string |
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
| Parameter | Type |
|---|---|
fn | (deps: FormFeatureDeps) => Promise<T> |
Returns
Promise<T>
Inherited from
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.schemapopulated,entryStepKey = null. Callers must usecreateVersionuntil a Shape B → Shape A backfill ships. - Multi-step — more than one
FormStepsrow or anyFormStepTransitionsrow on the current version. Use the step / transition services directly.
Parameters
| Parameter | Type |
|---|---|
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; } |
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>