diff --git a/packages/client/src/promise/generated/types.ts b/packages/client/src/promise/generated/types.ts index a08a3889b4..a0b9d6fadd 100644 --- a/packages/client/src/promise/generated/types.ts +++ b/packages/client/src/promise/generated/types.ts @@ -2453,7 +2453,11 @@ export type FormListRequestsOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -2473,7 +2477,11 @@ export type FormListRequestsOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2484,7 +2492,11 @@ export type FormListRequestsOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2495,7 +2507,11 @@ export type FormListRequestsOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -2504,7 +2520,11 @@ export type FormListRequestsOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -2545,7 +2565,11 @@ export type FormListOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -2565,7 +2589,11 @@ export type FormListOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2576,7 +2604,11 @@ export type FormListOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2587,7 +2619,11 @@ export type FormListOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -2596,7 +2632,11 @@ export type FormListOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -2634,7 +2674,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -2654,7 +2698,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2665,7 +2713,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2676,7 +2728,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -2685,7 +2741,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -2711,7 +2771,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -2731,7 +2795,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2742,7 +2810,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2753,7 +2825,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -2762,7 +2838,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -2788,7 +2868,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -2808,7 +2892,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2819,7 +2907,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2830,7 +2922,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -2839,7 +2935,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -2865,7 +2965,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -2885,7 +2989,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2896,7 +3004,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2907,7 +3019,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -2916,7 +3032,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -2942,7 +3062,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -2962,7 +3086,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2973,7 +3101,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -2984,7 +3116,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -2993,7 +3129,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -3019,7 +3159,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -3039,7 +3183,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -3050,7 +3198,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -3061,7 +3213,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -3070,7 +3226,11 @@ export type FormCreateInput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -3101,7 +3261,11 @@ export type FormCreateOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -3121,7 +3285,11 @@ export type FormCreateOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -3132,7 +3300,11 @@ export type FormCreateOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -3143,7 +3315,11 @@ export type FormCreateOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -3152,7 +3328,11 @@ export type FormCreateOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -3195,7 +3375,11 @@ export type FormGetOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -3215,7 +3399,11 @@ export type FormGetOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "number" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -3226,7 +3414,11 @@ export type FormGetOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "integer" readonly minimum?: number | "Infinity" | "-Infinity" | "NaN" readonly maximum?: number | "Infinity" | "-Infinity" | "NaN" @@ -3237,7 +3429,11 @@ export type FormGetOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -3246,7 +3442,11 @@ export type FormGetOutput = { readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | "Infinity" | "-Infinity" | "NaN" | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string @@ -4933,7 +5133,11 @@ export type EventSubscribeOutput = readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | boolean + }> readonly type: "string" readonly format?: "email" | "uri" | "date" | "date-time" readonly minLength?: number @@ -4953,7 +5157,11 @@ export type EventSubscribeOutput = readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | boolean + }> readonly type: "number" readonly minimum?: number readonly maximum?: number @@ -4964,7 +5172,11 @@ export type EventSubscribeOutput = readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | boolean + }> readonly type: "integer" readonly minimum?: number readonly maximum?: number @@ -4975,7 +5187,11 @@ export type EventSubscribeOutput = readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | boolean + }> readonly type: "boolean" readonly default?: boolean } @@ -4984,7 +5200,11 @@ export type EventSubscribeOutput = readonly title?: string readonly description?: string readonly required?: boolean - readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string } + readonly when?: ReadonlyArray<{ + readonly key: string + readonly op: "eq" | "neq" + readonly value: string | number | boolean + }> readonly type: "multiselect" readonly options: ReadonlyArray<{ readonly value: string diff --git a/packages/core/src/form.ts b/packages/core/src/form.ts index ef28dd2bd2..47a72ed23a 100644 --- a/packages/core/src/form.ts +++ b/packages/core/src/form.ts @@ -16,6 +16,9 @@ export type Info = typeof Info.Type export const Field = Form.Field export type Field = Form.Field +export const When = Form.When +export type When = Form.When + export const State = Form.State export type State = typeof State.Type @@ -56,6 +59,10 @@ export class InvalidAnswerError extends Schema.TaggedErrorClass()("Form.InvalidFormError", { + message: Schema.String, +}) {} + export type CreateInput = | (Omit & { readonly id?: ID }) | (Omit & { readonly id?: ID }) @@ -70,8 +77,8 @@ export interface ListInput { } export interface Interface { - readonly create: (input: CreateInput) => Effect.Effect - readonly ask: (input: CreateInput) => Effect.Effect + readonly create: (input: CreateInput) => Effect.Effect + readonly ask: (input: CreateInput) => Effect.Effect readonly get: (id: ID) => Effect.Effect readonly list: (input?: ListInput) => Effect.Effect> readonly state: (id: ID) => Effect.Effect @@ -113,6 +120,10 @@ export const layer = Layer.effect( const id = input.id ?? ID.create() const existing = yield* Cache.getSuccess(forms, id) if (Option.isSome(existing)) return yield* new AlreadyExistsError({ id }) + if (input.mode === "form") { + const invalid = validateFields(input.fields) + if (invalid) return yield* new InvalidFormError({ message: invalid }) + } const base = { id, sessionID: input.sessionID, @@ -218,10 +229,12 @@ function validateAnswer(form: Info, answer: Answer) { } for (const field of form.fields) { const value = answer[field.key] + const active = isActive(field, answer) if (value === undefined) { - if (field.required && isActive(field, answer)) return `Missing required form field: ${field.key}` + if (field.required && active) return `Missing required form field: ${field.key}` continue } + if (!active) return `Form field is not active: ${field.key}` const invalid = validateField(field, value) if (invalid) return invalid } @@ -229,9 +242,50 @@ function validateAnswer(form: Info, answer: Answer) { function isActive(field: Form.Field, answer: Answer) { if (!field.when) return true - const value = answer[field.when.key] - if (field.when.op === "eq") return value === field.when.value - return value !== field.when.value + return field.when.every((when) => matches(when, answer[when.key])) +} + +// An unanswered referenced field makes the condition false for both ops. Combined with inactive +// fields being unanswerable, this cascades: hiding a field falsifies every condition referencing it. +function matches(when: Form.When, value: Form.Value | undefined) { + if (value === undefined) return false + const hit = Array.isArray(value) ? value.some((item) => item === when.value) : value === when.value + return when.op === "eq" ? hit : !hit +} + +// Create-time validation of `when` references: each condition must point at an earlier field, +// carry a value matching that field's type, and use a declared option when the field's options +// are closed. Rejecting these at creation surfaces authoring mistakes to the caller instead of +// silently never matching. +function validateFields(fields: ReadonlyArray) { + const earlier = new Map() + for (const field of fields) { + if (earlier.has(field.key)) return `Duplicate form field key: ${field.key}` + for (const when of field.when ?? []) { + const target = earlier.get(when.key) + if (!target) return `Form field condition must reference an earlier field: ${field.key} -> ${when.key}` + const invalid = validateWhen(when, target) + if (invalid) return `${invalid}: ${field.key} -> ${when.key}` + } + earlier.set(field.key, field) + } +} + +function validateWhen(when: Form.When, target: Form.Field) { + if (target.type === "boolean") { + if (typeof when.value !== "boolean") return "Form field condition value must be a boolean" + return + } + if (target.type === "number" || target.type === "integer") { + if (typeof when.value !== "number") return "Form field condition value must be a number" + return + } + // string and multiselect targets both compare against string values + if (typeof when.value !== "string") return "Form field condition value must be a string" + const closed = target.type === "multiselect" ? !target.custom : target.options !== undefined && !target.custom + if (closed && !target.options?.some((option) => option.value === when.value)) { + return "Form field condition value must be one of the field's options" + } } function validateField(field: Form.Field, value: Form.Value): string | undefined { diff --git a/packages/core/src/mcp/client.ts b/packages/core/src/mcp/client.ts index 8041ee2338..fcd0814fb8 100644 --- a/packages/core/src/mcp/client.ts +++ b/packages/core/src/mcp/client.ts @@ -9,7 +9,13 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/ import { UnauthorizedError, type OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js" import { CallToolResultSchema, + ElicitationCompleteNotificationSchema, + ElicitRequestSchema, GetPromptResultSchema, + type ElicitRequestFormParams, + type ElicitRequestParams, + type ElicitRequestURLParams, + type ElicitResult, ListPromptsResultSchema, ListRootsRequestSchema, ListToolsResultSchema, @@ -82,6 +88,22 @@ export interface CallToolResult { readonly content: ReadonlyArray } +export type ElicitationFormParams = ElicitRequestFormParams +export type ElicitationParams = ElicitRequestParams +export type ElicitationResult = ElicitResult + +export interface ElicitationHandler { + readonly create: (input: { + readonly server: string + readonly params: ElicitationParams + readonly signal: AbortSignal + }) => Effect.Effect + readonly complete: (input: { + readonly server: string + readonly elicitationID: ElicitRequestURLParams["elicitationId"] + }) => Effect.Effect +} + export interface LogMessage { readonly level: LoggingMessageNotification["params"]["level"] readonly logger?: LoggingMessageNotification["params"]["logger"] @@ -123,6 +145,7 @@ export const connect = Effect.fnUntraced(function* ( // Only consumed by the remote transport; stdio servers have no auth concept. A provider with no // stored token (and a no-op redirect) surfaces an UnauthorizedError, which we map to needs_auth. authProvider?: OAuthClientProvider, + elicitation?: ElicitationHandler, ) { const transport: Transport = yield* Effect.gen(function* () { if (config.type === "local") { @@ -149,6 +172,7 @@ export const connect = Effect.fnUntraced(function* ( { name: "opencode", version: InstallationVersion }, { capabilities: { + ...(elicitation ? { elicitation: { form: { applyDefaults: true }, url: {} } } : {}), // https://github.com/anomalyco/opencode/issues/2308 roots: {}, }, @@ -157,6 +181,14 @@ export const connect = Effect.fnUntraced(function* ( client.setRequestHandler(ListRootsRequestSchema, () => Promise.resolve({ roots: [{ uri: pathToFileURL(directory).href }] }), ) + if (elicitation) { + client.setRequestHandler(ElicitRequestSchema, (request, extra) => + Effect.runPromise(elicitation.create({ server, params: request.params, signal: extra.signal })), + ) + client.setNotificationHandler(ElicitationCompleteNotificationSchema, (notification) => + Effect.runPromise(elicitation.complete({ server, elicitationID: notification.params.elicitationId })), + ) + } const exit = yield* Effect.tryPromise({ try: (signal) => client.connect(transport, { timeout: config.timeout?.startup ?? DEFAULT_STARTUP_TIMEOUT, signal }), diff --git a/packages/core/src/mcp/index.ts b/packages/core/src/mcp/index.ts index 6992942674..2075c8dd19 100644 --- a/packages/core/src/mcp/index.ts +++ b/packages/core/src/mcp/index.ts @@ -10,9 +10,11 @@ import { Config } from "../config" import { ConfigMCP } from "../config/mcp" import { Credential } from "../credential" import { EventV2 } from "../event" +import { Form } from "../form" import { Integration } from "../integration" import { IntegrationConnection } from "../integration/connection" import { Location } from "../location" +import { waitForAbort } from "../process" import { MCPClient } from "./client" import { MCPOAuth } from "./oauth" @@ -145,6 +147,10 @@ type ServerEntry = { integrationID?: Integration.ID } +// MCP elicitations are Location-scoped, not Session-scoped: the server cannot attribute them to a +// persisted session row, so their forms are owned by this opaque sentinel session identifier. +const GLOBAL_ELICITATION_SESSION_ID = "global" + export interface Interface { readonly servers: () => Effect.Effect readonly tools: () => Effect.Effect @@ -175,6 +181,7 @@ export const layer = Layer.effect( const config = yield* Config.Service const location = yield* Location.Service const events = yield* EventV2.Service + const forms = yield* Form.Service const integration = yield* Integration.Service const credentials = yield* Credential.Service const root = yield* Scope.make() @@ -189,6 +196,7 @@ export const layer = Layer.effect( ) // Later config files win for duplicate server names; per-server timeout overrides globals. const runtime = new Map() + const urlElicitations = new Map() for (const entry of documents) { for (const [name, server] of Object.entries(entry.info.mcp?.servers ?? {})) { runtime.set(ServerName.make(name), { @@ -308,6 +316,76 @@ export const layer = Layer.effect( }) }) + const elicitation = { + create: (input: { + readonly server: string + readonly params: MCPClient.ElicitationParams + readonly signal: AbortSignal + }) => + Effect.gen(function* () { + if (input.params.mode === "url") { + const formID = Form.ID.create() + const key = input.server + "\u0000" + input.params.elicitationId + urlElicitations.set(key, formID) + return yield* forms + .ask({ + id: formID, + sessionID: GLOBAL_ELICITATION_SESSION_ID, + title: `${input.server} is requesting input`, + metadata: { + kind: "mcp-elicitation", + server: input.server, + elicitationID: input.params.elicitationId, + message: input.params.message, + }, + mode: "url", + url: input.params.url, + }) + .pipe( + Effect.raceFirst(waitForAbort(input.signal)), + Effect.ensuring(Effect.sync(() => urlElicitations.delete(key))), + Effect.map( + (state): MCPClient.ElicitationResult => ({ + action: state.status === "answered" ? "accept" : "cancel", + }), + ), + ) + } + const params = input.params + return yield* forms + .ask({ + sessionID: GLOBAL_ELICITATION_SESSION_ID, + title: `${input.server} is requesting input`, + metadata: { kind: "mcp-elicitation", server: input.server, message: params.message }, + mode: "form", + fields: Object.entries(params.requestedSchema.properties).map(([key, property]) => + toElicitationField(key, property, params.requestedSchema.required?.includes(key) === true), + ), + }) + .pipe( + Effect.raceFirst(waitForAbort(input.signal)), + Effect.map((state): MCPClient.ElicitationResult => { + if (state.status !== "answered") return { action: "cancel" } + return { + action: "accept", + content: Object.fromEntries( + Object.entries(state.answer).map( + ([key, value]): [string, NonNullable[string]] => + typeof value === "object" ? [key, Array.from(value)] : [key, value], + ), + ), + } + }), + ) + }), + complete: (input: { readonly server: string; readonly elicitationID: string }) => + Effect.gen(function* () { + const formID = urlElicitations.get(input.server + "\u0000" + input.elicitationID) + if (!formID) return + yield* forms.reply({ id: formID, answer: {} }).pipe(Effect.ignore) + }), + } satisfies MCPClient.ElicitationHandler + const toTool = (server: ServerName, def: MCPClient.ToolDefinition) => new Tool({ server, name: def.name, description: def.description, inputSchema: def.inputSchema }) @@ -396,7 +474,7 @@ export const layer = Layer.effect( const authProvider = yield* connectProvider(entry) // List tools as part of connect so a failure here marks the server failed rather than // leaving it connected with a silently empty tool list and no path to recover. - const result = yield* MCPClient.connect(name, entry.config, location.directory, authProvider).pipe( + const result = yield* MCPClient.connect(name, entry.config, location.directory, authProvider, elicitation).pipe( Effect.flatMap((connection) => connection.tools().pipe(Effect.map((tools) => ({ connection, tools })))), Scope.provide(scope), Effect.exit, @@ -561,5 +639,69 @@ export const layer = Layer.effect( export const node = makeLocationNode({ service: Service, layer, - deps: [Config.node, Location.node, EventV2.node, Integration.node, Credential.node], + deps: [Config.node, Location.node, EventV2.node, Form.node, Integration.node, Credential.node], }) + +// Schema `optional` strips undefined-valued properties on encode, so fields can assign +// optional properties directly instead of conditionally spreading them. +function toElicitationField(key: string, property: ElicitationProperty, required: boolean): Form.Field { + // Some servers emit machine titles like "string with format email"; prefer description/key over those. + const machineTitle = /^(boolean|string|number|integer|array|object)(\s+with\b.*|\s+in\b.*)?$/i + const title = + property.title && !machineTitle.test(property.title.trim()) ? property.title : (property.description ?? key) + const base = { + key, + title, + description: property.description === title ? undefined : property.description, + required: required || undefined, + } + switch (property.type) { + case "boolean": + return { ...base, type: "boolean", default: property.default } + case "number": + case "integer": + return { + ...base, + type: property.type, + minimum: property.minimum, + maximum: property.maximum, + default: property.default, + } + case "array": + return { + ...base, + type: "multiselect", + options: + "anyOf" in property.items + ? property.items.anyOf.map((option) => ({ value: option.const, label: option.title })) + : property.items.enum.map((value) => ({ value, label: value })), + custom: false, + minItems: property.minItems, + maxItems: property.maxItems, + default: property.default, + } + case "string": { + const options = + "oneOf" in property + ? property.oneOf.map((option) => ({ value: option.const, label: option.title })) + : "enum" in property + ? property.enum.map((value, index) => ({ + value, + label: ("enumNames" in property ? property.enumNames?.[index] : undefined) ?? value, + })) + : undefined + return { + ...base, + type: "string", + format: "format" in property ? property.format : undefined, + minLength: "minLength" in property ? property.minLength : undefined, + maxLength: "maxLength" in property ? property.maxLength : undefined, + default: property.default, + options, + custom: options ? false : undefined, + } + } + } +} + +type ElicitationProperty = MCPClient.ElicitationFormParams["requestedSchema"]["properties"][string] diff --git a/packages/core/test/form.test.ts b/packages/core/test/form.test.ts index 19f6022bf8..7ea351df3a 100644 --- a/packages/core/test/form.test.ts +++ b/packages/core/test/form.test.ts @@ -38,6 +38,124 @@ describe("Form", () => { }), ) + it.effect("gates required fields and rejects inactive answers via when", () => + Effect.gen(function* () { + const service = yield* Form.Service + const created = yield* service.create({ + sessionID: "global", + mode: "form", + fields: [ + { key: "confirm", type: "boolean", required: true }, + { key: "reason", type: "string", required: true, when: [{ key: "confirm", op: "eq", value: false }] }, + ], + }) + + const inactive = yield* service.reply({ id: created.id, answer: { confirm: true, reason: "x" } }).pipe(Effect.flip) + expect(inactive).toEqual(new Form.InvalidAnswerError({ id: created.id, message: "Form field is not active: reason" })) + + const missing = yield* service.reply({ id: created.id, answer: { confirm: false } }).pipe(Effect.flip) + expect(missing).toEqual( + new Form.InvalidAnswerError({ id: created.id, message: "Missing required form field: reason" }), + ) + + yield* service.reply({ id: created.id, answer: { confirm: false, reason: "not ready" } }) + expect(yield* service.state(created.id)).toEqual({ + status: "answered", + answer: { confirm: false, reason: "not ready" }, + }) + }), + ) + + it.effect("evaluates when against multiselect answers as inclusion", () => + Effect.gen(function* () { + const service = yield* Form.Service + const options = [ + { value: "go", label: "Go" }, + { value: "ts", label: "TypeScript" }, + ] + const created = yield* service.create({ + sessionID: "global", + mode: "form", + fields: [ + { key: "langs", type: "multiselect", options }, + { key: "goVersion", type: "string", required: true, when: [{ key: "langs", op: "eq", value: "go" }] }, + ], + }) + + const missing = yield* service.reply({ id: created.id, answer: { langs: ["go", "ts"] } }).pipe(Effect.flip) + expect(missing).toEqual( + new Form.InvalidAnswerError({ id: created.id, message: "Missing required form field: goVersion" }), + ) + + yield* service.reply({ id: created.id, answer: { langs: ["ts"] } }) + expect(yield* service.state(created.id)).toEqual({ status: "answered", answer: { langs: ["ts"] } }) + }), + ) + + it.effect("treats unanswered when references as false and cascades inactivity", () => + Effect.gen(function* () { + const service = yield* Form.Service + const created = yield* service.create({ + sessionID: "global", + mode: "form", + fields: [ + { key: "a", type: "boolean" }, + { key: "b", type: "string", when: [{ key: "a", op: "eq", value: true }] }, + // neq also fails against an unanswered reference, and hiding b cascades here through + // the reject-inactive-answers rule: b can never be answered while a is false. + { key: "c", type: "string", required: true, when: [{ key: "b", op: "neq", value: "x" }] }, + ], + }) + + const inactive = yield* service.reply({ id: created.id, answer: { a: false, b: "yes" } }).pipe(Effect.flip) + expect(inactive).toEqual(new Form.InvalidAnswerError({ id: created.id, message: "Form field is not active: b" })) + + yield* service.reply({ id: created.id, answer: { a: false } }) + expect(yield* service.state(created.id)).toEqual({ status: "answered", answer: { a: false } }) + }), + ) + + it.effect("rejects invalid when definitions at creation", () => + Effect.gen(function* () { + const service = yield* Form.Service + const flipCreate = (fields: ReadonlyArray) => + service.create({ sessionID: "global", mode: "form", fields }).pipe(Effect.flip) + + expect( + yield* flipCreate([ + { key: "b", type: "string", when: [{ key: "missing", op: "eq", value: "x" }] }, + ]), + ).toEqual(new Form.InvalidFormError({ message: "Form field condition must reference an earlier field: b -> missing" })) + + expect( + yield* flipCreate([ + { key: "a", type: "string" }, + { key: "a", type: "string" }, + ]), + ).toEqual(new Form.InvalidFormError({ message: "Duplicate form field key: a" })) + + expect( + yield* flipCreate([ + { key: "a", type: "boolean" }, + { key: "b", type: "string", when: [{ key: "a", op: "eq", value: "yes" }] }, + ]), + ).toEqual( + new Form.InvalidFormError({ message: "Form field condition value must be a boolean: b -> a" }), + ) + + expect( + yield* flipCreate([ + { key: "a", type: "string", options: [{ value: "x", label: "X" }] }, + { key: "b", type: "string", when: [{ key: "a", op: "eq", value: "y" }] }, + ]), + ).toEqual( + new Form.InvalidFormError({ + message: "Form field condition value must be one of the field's options: b -> a", + }), + ) + }), + ) + it.effect("cleans up created forms when event publication fails", () => Effect.gen(function* () { const service = yield* Form.Service diff --git a/packages/schema/src/form.ts b/packages/schema/src/form.ts index 23f8a015b6..820192d06f 100644 --- a/packages/schema/src/form.ts +++ b/packages/schema/src/form.ts @@ -22,10 +22,17 @@ export const Option = Schema.Struct({ }).annotate({ identifier: "Form.Option" }) export interface Option extends Schema.Schema.Type {} +// One visibility condition on a field. A field's `when` is a list of conditions that must all +// hold (AND) against the current answers for the field to be active. Semantics: +// - `value` must match the referenced field's type; against a multiselect answer, `eq` means +// "selection includes value" and `neq` means "selection does not include value". +// - An unanswered referenced field makes the condition false for both ops. +// - `key` must reference a field defined earlier in the form's field list. +// Inactive fields are neither required nor answerable. export const When = Schema.Struct({ key: Schema.String, op: Schema.Literals(["eq", "neq"]), - value: Schema.String, + value: Schema.Union([Schema.String, Schema.Number, Schema.Boolean]), }).annotate({ identifier: "Form.When" }) export interface When extends Schema.Schema.Type {} @@ -34,7 +41,7 @@ const FieldBase = { title: Schema.String.pipe(optional), description: Schema.String.pipe(optional), required: Schema.Boolean.pipe(optional), - when: When.pipe(optional), + when: Schema.Array(When).pipe(optional), } export const StringField = Schema.Struct({ diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index dd4c35a2e0..0aa1dbeb22 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -3425,7 +3425,7 @@ export type FormMetadata = { export type FormWhen = { key: string op: "eq" | "neq" - value: string + value: string | number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" | boolean } export type FormOption = { @@ -3439,7 +3439,7 @@ export type FormStringField = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "string" format?: "email" | "uri" | "date" | "date-time" minLength?: number @@ -3456,7 +3456,7 @@ export type FormNumberField = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "number" minimum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" maximum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" @@ -3468,7 +3468,7 @@ export type FormIntegerField = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "integer" minimum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" maximum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" @@ -3480,7 +3480,7 @@ export type FormBooleanField = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "boolean" default?: boolean } @@ -3490,7 +3490,7 @@ export type FormMultiselectField = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "multiselect" options: Array minItems?: number @@ -6382,12 +6382,18 @@ export type QuestionV2Rejected = { } } +export type FormWhen1 = { + key: string + op: "eq" | "neq" + value: string | number | "NaN" | "Infinity" | "-Infinity" | boolean +} + export type FormNumberField1 = { key: string title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "number" minimum?: number | "NaN" | "Infinity" | "-Infinity" maximum?: number | "NaN" | "Infinity" | "-Infinity" @@ -6399,7 +6405,7 @@ export type FormIntegerField1 = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "integer" minimum?: number | "NaN" | "Infinity" | "-Infinity" maximum?: number | "NaN" | "Infinity" | "-Infinity" @@ -7778,7 +7784,7 @@ export type FormNumberField2 = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "number" minimum?: number | "NaN" | "Infinity" | "-Infinity" maximum?: number | "NaN" | "Infinity" | "-Infinity" @@ -7790,7 +7796,7 @@ export type FormIntegerField2 = { title?: string description?: string required?: boolean - when?: FormWhen + when?: Array type: "integer" minimum?: number | "NaN" | "Infinity" | "-Infinity" maximum?: number | "NaN" | "Infinity" | "-Infinity" @@ -9659,7 +9665,7 @@ export type FormMetadata2 = { export type FormWhen2 = { key: string op: "eq" | "neq" - value: string + value: string | number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" | boolean } export type FormOption2 = { @@ -9673,7 +9679,7 @@ export type FormStringField2 = { title?: string description?: string required?: boolean - when?: FormWhen2 + when?: Array type: "string" format?: "email" | "uri" | "date" | "date-time" minLength?: number @@ -9690,7 +9696,7 @@ export type FormNumberField3 = { title?: string description?: string required?: boolean - when?: FormWhen2 + when?: Array type: "number" minimum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" maximum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" @@ -9702,7 +9708,7 @@ export type FormIntegerField3 = { title?: string description?: string required?: boolean - when?: FormWhen2 + when?: Array type: "integer" minimum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" maximum?: number | "NaN" | "Infinity" | "-Infinity" | "Infinity" | "-Infinity" | "NaN" @@ -9714,7 +9720,7 @@ export type FormBooleanField2 = { title?: string description?: string required?: boolean - when?: FormWhen2 + when?: Array type: "boolean" default?: boolean } @@ -9724,7 +9730,7 @@ export type FormMultiselectField2 = { title?: string description?: string required?: boolean - when?: FormWhen2 + when?: Array type: "multiselect" options: Array minItems?: number @@ -11102,12 +11108,35 @@ export type FormMetadata1 = { [key: string]: unknown } +export type FormWhen12 = { + key: string + op: "eq" | "neq" + value: string | number | "NaN" | "Infinity" | "-Infinity" | boolean +} + +export type FormStringField1 = { + key: string + title?: string + description?: string + required?: boolean + when?: Array + type: "string" + format?: "email" | "uri" | "date" | "date-time" + minLength?: number + maxLength?: number + pattern?: string + placeholder?: string + default?: string + options?: Array + custom?: boolean +} + export type FormNumberField12 = { key: string title?: string description?: string required?: boolean - when?: FormWhen2 + when?: Array type: "number" minimum?: number | "NaN" | "Infinity" | "-Infinity" maximum?: number | "NaN" | "Infinity" | "-Infinity" @@ -11119,20 +11148,44 @@ export type FormIntegerField12 = { title?: string description?: string required?: boolean - when?: FormWhen2 + when?: Array type: "integer" minimum?: number | "NaN" | "Infinity" | "-Infinity" maximum?: number | "NaN" | "Infinity" | "-Infinity" default?: number | "NaN" | "Infinity" | "-Infinity" } +export type FormBooleanField1 = { + key: string + title?: string + description?: string + required?: boolean + when?: Array + type: "boolean" + default?: boolean +} + +export type FormMultiselectField1 = { + key: string + title?: string + description?: string + required?: boolean + when?: Array + type: "multiselect" + options: Array + minItems?: number + maxItems?: number + custom?: boolean + default?: Array +} + export type FormFormInfo1 = { id: string sessionID: string title?: string metadata?: FormMetadata1 mode: "form" - fields: Array + fields: Array } export type FormUrlInfo1 = { diff --git a/packages/server/src/handlers/form.ts b/packages/server/src/handlers/form.ts index 9ffd1b93e1..db910464af 100644 --- a/packages/server/src/handlers/form.ts +++ b/packages/server/src/handlers/form.ts @@ -61,14 +61,12 @@ export const FormHandler = HttpApiBuilder.group(Api, "server.form", (handlers) = return Effect.succeed({ ...common, mode: "url" as const, url: ctx.payload.url }) })() - const created = yield* form - .create(input) - .pipe( - Effect.catchTag( - "Form.AlreadyExistsError", - (error) => new ConflictError({ resource: error.id, message: error.message }), - ), - ) + const created = yield* form.create(input).pipe( + Effect.catchTags({ + "Form.AlreadyExistsError": (error) => new ConflictError({ resource: error.id, message: error.message }), + "Form.InvalidFormError": (error) => new InvalidRequestError({ message: error.message, field: "fields" }), + }), + ) return { data: created } }), )