Kaizen
Browse modulesFormsforms/serverFunctions

Function: validatePublishable()

function validatePublishable(input: {
  attachedRuleIds?: Set<string>;
  entryStepKey: string | null;
  rules: {
     createdAt: Date;
     createdBy: string | null;
     currentVersionId: string | null;
     deletedAt: Date | null;
     deletedBy: string | null;
     description: string | null;
     domain: string;
     id: string;
     identifier: string;
     module: string | null;
     name: string;
     organizationId: string;
     rateTierId: string | null;
     scheduleConditionId: string | null;
     status: RuleStatus;
     statusUpdatedAt: Date;
     statusUpdatedBy: string | null;
     updatedAt: Date;
     updatedBy: string | null;
  }[];
  steps: {
     description: string | null;
     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?: ... | ...;
              title?: ... | ...;
           };
           adoptionStatement?: string;
           allowedFonts?: string[];
           asset?: {
              alt?: ... | ...;
              fileId?: ... | ...;
              filename?: ... | ...;
              mimeType?: ... | ...;
              url?: ... | ...;
           };
           dataSource?: string;
           defaultValue?:   | string
              | {
              from: string;
              to: ... | ...;
            };
           description?: string;
           format?: string;
           label: string;
           maxDate?: string;
           maxFiles?: number;
           maxLength?: number;
           minDate?: string;
           name: string;
           options?: (... | ...)[];
           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";
     }[];
     formSchemaVersionId: string;
     id: string;
     key: string;
     label: string | null;
  }[];
  transitions: {
     fromStepId: string;
     id: string;
     label: string | null;
     priority: number;
     ruleId: string | null;
     ruleVersionId: string | null;
     toStepId: string;
  }[];
}): PublishValidationResult;

Defined in: server/forms/publish-validators.ts:43

Determine if a form is publishable, where "publishable" means:

  • The entry step exists (for starting)
  • A final step exists (for submitting)
  • There are no cycles
  • There are no unreachable/orphaned steps
  • Every transition's ruleId resolves to a known rule
  • Every transition's ruleId is attached to the schema's rule pool
  • Outgoing transitions from a step have unique priorities
  • Field names have stable types across the version's steps

Pure validator that enforces the publish-time invariants from the spec. Operates entirely on in-memory data — no DB access, no async — so it's trivially unit-testable.

Returned referencedRuleIds is always populated (even on failure) so the publish flow can do a two-pass walk: first to discover referenced ids, then to validate after the rules are loaded.

attachedRuleIds is the set of rule ids attached to the schema via FormSchemaRules. Pass undefined to skip the attachment check (callers in tests or older codepaths). When provided, every referenced ruleId must be in the set.

Parameters

ParameterType
input{ attachedRuleIds?: Set<string>; entryStepKey: string | null; rules: { createdAt: Date; createdBy: string | null; currentVersionId: string | null; deletedAt: Date | null; deletedBy: string | null; description: string | null; domain: string; id: string; identifier: string; module: string | null; name: string; organizationId: string; rateTierId: string | null; scheduleConditionId: string | null; status: RuleStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; updatedAt: Date; updatedBy: string | null; }[]; steps: { description: string | null; 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?: ... | ...; title?: ... | ...; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: ... | ...; fileId?: ... | ...; filename?: ... | ...; mimeType?: ... | ...; url?: ... | ...; }; dataSource?: string; defaultValue?: | string | { from: string; to: ... | ...; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: (... | ...)[]; 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"; }[]; formSchemaVersionId: string; id: string; key: string; label: string | null; }[]; transitions: { fromStepId: string; id: string; label: string | null; priority: number; ruleId: string | null; ruleVersionId: string | null; toStepId: string; }[]; }
input.attachedRuleIds?Set<string>
input.entryStepKeystring | null
input.rules{ createdAt: Date; createdBy: string | null; currentVersionId: string | null; deletedAt: Date | null; deletedBy: string | null; description: string | null; domain: string; id: string; identifier: string; module: string | null; name: string; organizationId: string; rateTierId: string | null; scheduleConditionId: string | null; status: RuleStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; updatedAt: Date; updatedBy: string | null; }[]
input.steps{ description: string | null; 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?: ... | ...; title?: ... | ...; }; adoptionStatement?: string; allowedFonts?: string[]; asset?: { alt?: ... | ...; fileId?: ... | ...; filename?: ... | ...; mimeType?: ... | ...; url?: ... | ...; }; dataSource?: string; defaultValue?: | string | { from: string; to: ... | ...; }; description?: string; format?: string; label: string; maxDate?: string; maxFiles?: number; maxLength?: number; minDate?: string; name: string; options?: (... | ...)[]; 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"; }[]; formSchemaVersionId: string; id: string; key: string; label: string | null; }[]
input.transitions{ fromStepId: string; id: string; label: string | null; priority: number; ruleId: string | null; ruleVersionId: string | null; toStepId: string; }[]

Returns

PublishValidationResult

On this page