@frigear-nu/forms 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/dist/module.d.mts +148 -8
- package/dist/module.json +1 -1
- package/dist/module.mjs +65 -35
- package/dist/runtime/app/components/FormBuilder.d.vue.ts +26 -0
- package/dist/runtime/app/components/FormBuilder.vue +220 -0
- package/dist/runtime/app/components/FormBuilder.vue.d.ts +26 -0
- package/dist/runtime/app/components/FormBuilderAccess.d.vue.ts +16 -0
- package/dist/runtime/app/components/FormBuilderAccess.vue +404 -0
- package/dist/runtime/app/components/FormBuilderAccess.vue.d.ts +16 -0
- package/dist/runtime/app/components/FormBuilderCaptchaSettings.d.vue.ts +15 -0
- package/dist/runtime/app/components/FormBuilderCaptchaSettings.vue +126 -0
- package/dist/runtime/app/components/FormBuilderCaptchaSettings.vue.d.ts +15 -0
- package/dist/runtime/app/components/FormBuilderCondition.d.vue.ts +16 -0
- package/dist/runtime/app/components/FormBuilderCondition.vue +62 -0
- package/dist/runtime/app/components/FormBuilderCondition.vue.d.ts +16 -0
- package/dist/runtime/app/components/FormBuilderFieldRow.d.vue.ts +27 -0
- package/dist/runtime/app/components/FormBuilderFieldRow.vue +87 -0
- package/dist/runtime/app/components/FormBuilderFieldRow.vue.d.ts +27 -0
- package/dist/runtime/app/components/FormBuilderFieldSettings.d.vue.ts +25 -0
- package/dist/runtime/app/components/FormBuilderFieldSettings.vue +170 -0
- package/dist/runtime/app/components/FormBuilderFieldSettings.vue.d.ts +25 -0
- package/dist/runtime/app/components/FormBuilderInsertRow.d.vue.ts +20 -0
- package/dist/runtime/app/components/FormBuilderInsertRow.vue +51 -0
- package/dist/runtime/app/components/FormBuilderInsertRow.vue.d.ts +20 -0
- package/dist/runtime/app/components/FormBuilderOptions.d.vue.ts +11 -0
- package/dist/runtime/app/components/FormBuilderOptions.vue +81 -0
- package/dist/runtime/app/components/FormBuilderOptions.vue.d.ts +11 -0
- package/dist/runtime/app/components/FormBuilderPhoneSettings.d.vue.ts +11 -0
- package/dist/runtime/app/components/FormBuilderPhoneSettings.vue +63 -0
- package/dist/runtime/app/components/FormBuilderPhoneSettings.vue.d.ts +11 -0
- package/dist/runtime/app/components/FormBuilderSettings.d.vue.ts +11 -0
- package/dist/runtime/app/components/FormBuilderSettings.vue +233 -0
- package/dist/runtime/app/components/FormBuilderSettings.vue.d.ts +11 -0
- package/dist/runtime/app/components/FormBuilderStepHeader.d.vue.ts +27 -0
- package/dist/runtime/app/components/FormBuilderStepHeader.vue +71 -0
- package/dist/runtime/app/components/FormBuilderStepHeader.vue.d.ts +27 -0
- package/dist/runtime/app/components/FormBuilderStepSettings.d.vue.ts +13 -0
- package/dist/runtime/app/components/FormBuilderStepSettings.vue +33 -0
- package/dist/runtime/app/components/FormBuilderStepSettings.vue.d.ts +13 -0
- package/dist/runtime/app/components/FormBuilderText.d.vue.ts +19 -0
- package/dist/runtime/app/components/FormBuilderText.vue +127 -0
- package/dist/runtime/app/components/FormBuilderText.vue.d.ts +19 -0
- package/dist/runtime/app/components/FormCaptcha.d.vue.ts +19 -0
- package/dist/runtime/app/components/FormCaptcha.vue +95 -0
- package/dist/runtime/app/components/FormCaptcha.vue.d.ts +19 -0
- package/dist/runtime/app/components/FormEmbed.d.vue.ts +17 -0
- package/dist/runtime/app/components/FormEmbed.vue +113 -0
- package/dist/runtime/app/components/FormEmbed.vue.d.ts +17 -0
- package/dist/runtime/app/components/FormField.d.vue.ts +4 -0
- package/dist/runtime/app/components/FormField.vue +35 -3
- package/dist/runtime/app/components/FormField.vue.d.ts +4 -0
- package/dist/runtime/app/components/FormPhoneInput.d.vue.ts +20 -0
- package/dist/runtime/app/components/FormPhoneInput.vue +56 -0
- package/dist/runtime/app/components/FormPhoneInput.vue.d.ts +20 -0
- package/dist/runtime/app/components/FormPreview.d.vue.ts +12 -0
- package/dist/runtime/app/components/FormPreview.vue +244 -0
- package/dist/runtime/app/components/FormPreview.vue.d.ts +12 -0
- package/dist/runtime/app/components/FormRenderer.d.vue.ts +6 -0
- package/dist/runtime/app/components/FormRenderer.vue +16 -7
- package/dist/runtime/app/components/FormRenderer.vue.d.ts +6 -0
- package/dist/runtime/app/components/FormSubmissionView.vue +2 -2
- package/dist/runtime/app/composables/index.d.ts +4 -1
- package/dist/runtime/app/composables/index.js +4 -1
- package/dist/runtime/app/composables/useCaptcha.d.ts +22 -0
- package/dist/runtime/app/composables/useCaptcha.js +39 -0
- package/dist/runtime/app/composables/useCaptchaSecrets.d.ts +7 -0
- package/dist/runtime/app/composables/useCaptchaSecrets.js +69 -0
- package/dist/runtime/app/composables/useFormBuilderEditor.d.ts +49 -0
- package/dist/runtime/app/composables/useFormBuilderEditor.js +210 -0
- package/dist/runtime/app/composables/useFormBuilderLabels.d.ts +8 -0
- package/dist/runtime/app/composables/useFormBuilderLabels.js +41 -0
- package/dist/runtime/app/composables/useForms.d.ts +18 -3
- package/dist/runtime/app/composables/useForms.js +38 -5
- package/dist/runtime/app/pages/admin-form.vue +112 -69
- package/dist/runtime/app/pages/admin-submission.vue +7 -16
- package/dist/runtime/app/pages/admin-submissions.vue +41 -46
- package/dist/runtime/app/pages/admin.vue +128 -44
- package/dist/runtime/app/pages/form.vue +71 -5
- package/dist/runtime/i18n/lang/da.json +231 -3
- package/dist/runtime/i18n/lang/en.json +231 -3
- package/dist/runtime/menus.js +10 -0
- package/dist/runtime/server/db/schema.sqlite.d.ts +283 -0
- package/dist/runtime/server/db/schema.sqlite.js +33 -1
- package/dist/runtime/server/mcp/schemas.d.ts +9 -0
- package/dist/runtime/server/mcp/schemas.js +8 -0
- package/dist/runtime/server/mcp/tools/admin/create-form.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/admin/create-form.js +21 -0
- package/dist/runtime/server/mcp/tools/admin/get-form-definition.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/admin/get-form-definition.js +14 -0
- package/dist/runtime/server/mcp/tools/admin/get-submission.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/admin/get-submission.js +26 -0
- package/dist/runtime/server/mcp/tools/admin/list-all-forms.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/admin/list-all-forms.js +21 -0
- package/dist/runtime/server/mcp/tools/admin/list-submissions.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/admin/list-submissions.js +22 -0
- package/dist/runtime/server/mcp/tools/admin/update-form.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/admin/update-form.js +24 -0
- package/dist/runtime/server/mcp/tools/get-form.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/get-form.js +12 -0
- package/dist/runtime/server/mcp/tools/list-forms.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/list-forms.js +11 -0
- package/dist/runtime/server/mcp/tools/submit-form.d.ts +2 -0
- package/dist/runtime/server/mcp/tools/submit-form.js +17 -0
- package/dist/runtime/server/plugins/email-delivery.js +16 -11
- package/dist/runtime/server/plugins/encryption.d.ts +2 -0
- package/dist/runtime/server/plugins/encryption.js +4 -0
- package/dist/runtime/server/plugins/staff-scopes.d.ts +2 -0
- package/dist/runtime/server/plugins/staff-scopes.js +1 -0
- package/dist/runtime/server/routes/admin/form-clone.post.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-clone.post.js +17 -0
- package/dist/runtime/server/routes/admin/form-password.delete.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-password.delete.js +8 -0
- package/dist/runtime/server/routes/admin/form-password.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-password.get.js +8 -0
- package/dist/runtime/server/routes/admin/form-password.put.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-password.put.js +14 -0
- package/dist/runtime/server/routes/admin/form-secret.delete.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-secret.delete.js +8 -0
- package/dist/runtime/server/routes/admin/form-secrets.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-secrets.get.js +5 -0
- package/dist/runtime/server/routes/admin/form-secrets.put.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-secrets.put.js +12 -0
- package/dist/runtime/server/routes/admin/form-token.delete.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-token.delete.js +12 -0
- package/dist/runtime/server/routes/admin/form-tokens.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-tokens.get.js +8 -0
- package/dist/runtime/server/routes/admin/form-tokens.post.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form-tokens.post.js +12 -0
- package/dist/runtime/server/routes/admin/form.get.js +5 -1
- package/dist/runtime/server/routes/admin/form.patch.js +3 -12
- package/dist/runtime/server/routes/admin/forms.post.js +1 -6
- package/dist/runtime/server/routes/public/access.get.d.ts +2 -0
- package/dist/runtime/server/routes/public/access.get.js +3 -0
- package/dist/runtime/server/routes/public/form.get.js +3 -1
- package/dist/runtime/server/routes/public/submit.post.js +2 -2
- package/dist/runtime/server/utils/access.d.ts +27 -0
- package/dist/runtime/server/utils/access.js +63 -0
- package/dist/runtime/server/utils/admin-schemas.d.ts +219 -15
- package/dist/runtime/server/utils/auth.d.ts +7 -7
- package/dist/runtime/server/utils/auth.js +4 -4
- package/dist/runtime/server/utils/captcha.d.ts +10 -0
- package/dist/runtime/server/utils/captcha.js +25 -0
- package/dist/runtime/server/utils/crypto.d.ts +18 -0
- package/dist/runtime/server/utils/crypto.js +48 -0
- package/dist/runtime/server/utils/forms.d.ts +6 -1
- package/dist/runtime/server/utils/forms.js +28 -0
- package/dist/runtime/server/utils/mcp-access.d.ts +9 -0
- package/dist/runtime/server/utils/mcp-access.js +8 -0
- package/dist/runtime/server/utils/rate-limit.d.ts +3 -0
- package/dist/runtime/server/utils/rate-limit.js +19 -0
- package/dist/runtime/server/utils/render.d.ts +2 -11
- package/dist/runtime/server/utils/render.js +1 -26
- package/dist/runtime/server/utils/secrets.d.ts +28 -0
- package/dist/runtime/server/utils/secrets.js +60 -0
- package/dist/runtime/server/utils/submit.d.ts +12 -1
- package/dist/runtime/server/utils/submit.js +18 -4
- package/dist/runtime/utils/builder.d.ts +73 -0
- package/dist/runtime/utils/builder.js +220 -0
- package/dist/runtime/utils/captcha.d.ts +9 -0
- package/dist/runtime/utils/captcha.js +7 -0
- package/dist/runtime/utils/conditions.d.ts +3 -1
- package/dist/runtime/utils/conditions.js +4 -0
- package/dist/runtime/utils/definition.d.ts +116 -6
- package/dist/runtime/utils/definition.js +53 -4
- package/dist/runtime/utils/embed.d.ts +8 -0
- package/dist/runtime/utils/embed.js +7 -0
- package/dist/runtime/utils/index.d.ts +16 -3
- package/dist/runtime/utils/index.js +66 -3
- package/dist/runtime/utils/markdown.d.ts +1 -5
- package/dist/runtime/utils/markdown.js +1 -5
- package/dist/runtime/utils/phone.d.ts +29 -0
- package/dist/runtime/utils/phone.js +294 -0
- package/dist/runtime/utils/preview.d.ts +12 -0
- package/dist/runtime/utils/preview.js +17 -0
- package/dist/runtime/utils/schedule.d.ts +1 -0
- package/dist/runtime/utils/schedule.js +1 -0
- package/dist/runtime/utils/schema.d.ts +2 -0
- package/dist/runtime/utils/schema.js +12 -4
- package/dist/runtime/utils/template.d.ts +57 -0
- package/dist/runtime/utils/template.js +147 -0
- package/dist/runtime/utils/text.d.ts +0 -3
- package/dist/runtime/utils/text.js +0 -5
- package/dist/types.d.mts +5 -1
- package/package.json +13 -5
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type { FormDefinition } from '../../types.js';
|
|
2
|
+
import type { FormAccess, FormDefinition } from '../../types.js';
|
|
3
|
+
export declare const accessSchema: z.ZodObject<{
|
|
4
|
+
availability: z.ZodDefault<z.ZodEnum<{
|
|
5
|
+
web: "web";
|
|
6
|
+
api: "api";
|
|
7
|
+
}>>;
|
|
8
|
+
auth: z.ZodDefault<z.ZodEnum<{
|
|
9
|
+
none: "none";
|
|
10
|
+
user: "user";
|
|
11
|
+
token: "token";
|
|
12
|
+
password: "password";
|
|
13
|
+
}>>;
|
|
14
|
+
schedule: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
15
|
+
type: z.ZodLiteral<"always">;
|
|
16
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
17
|
+
type: z.ZodLiteral<"range">;
|
|
18
|
+
from: z.ZodOptional<z.ZodString>;
|
|
19
|
+
to: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
21
|
+
type: z.ZodLiteral<"yearly">;
|
|
22
|
+
from: z.ZodObject<{
|
|
23
|
+
month: z.ZodNumber;
|
|
24
|
+
day: z.ZodNumber;
|
|
25
|
+
time: z.ZodString;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
to: z.ZodObject<{
|
|
28
|
+
month: z.ZodNumber;
|
|
29
|
+
day: z.ZodNumber;
|
|
30
|
+
time: z.ZodString;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
timezone: z.ZodString;
|
|
33
|
+
}, z.core.$strip>], "type">>;
|
|
34
|
+
}, z.core.$strip>;
|
|
3
35
|
export declare const formDefinitionSchema: z.ZodObject<{
|
|
4
36
|
mode: z.ZodEnum<{
|
|
5
37
|
single: "single";
|
|
@@ -16,9 +48,11 @@ export declare const formDefinitionSchema: z.ZodObject<{
|
|
|
16
48
|
showIf: z.ZodOptional<z.ZodObject<{
|
|
17
49
|
field: z.ZodString;
|
|
18
50
|
op: z.ZodEnum<{
|
|
51
|
+
in: "in";
|
|
19
52
|
eq: "eq";
|
|
20
53
|
neq: "neq";
|
|
21
|
-
|
|
54
|
+
empty: "empty";
|
|
55
|
+
notEmpty: "notEmpty";
|
|
22
56
|
truthy: "truthy";
|
|
23
57
|
}>;
|
|
24
58
|
value: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -27,16 +61,17 @@ export declare const formDefinitionSchema: z.ZodObject<{
|
|
|
27
61
|
key: z.ZodString;
|
|
28
62
|
type: z.ZodEnum<{
|
|
29
63
|
number: "number";
|
|
30
|
-
|
|
64
|
+
date: "date";
|
|
65
|
+
file: "file";
|
|
31
66
|
email: "email";
|
|
67
|
+
text: "text";
|
|
32
68
|
tel: "tel";
|
|
33
69
|
textarea: "textarea";
|
|
34
70
|
select: "select";
|
|
35
71
|
radio: "radio";
|
|
36
72
|
checkbox: "checkbox";
|
|
37
|
-
date: "date";
|
|
38
|
-
file: "file";
|
|
39
73
|
markdown: "markdown";
|
|
74
|
+
captcha: "captcha";
|
|
40
75
|
}>;
|
|
41
76
|
content: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
42
77
|
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
@@ -60,12 +95,39 @@ export declare const formDefinitionSchema: z.ZodObject<{
|
|
|
60
95
|
accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
61
96
|
message: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
62
97
|
}, z.core.$strip>>;
|
|
98
|
+
phone: z.ZodOptional<z.ZodObject<{
|
|
99
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
100
|
+
simple: "simple";
|
|
101
|
+
country: "country";
|
|
102
|
+
}>>;
|
|
103
|
+
countries: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
104
|
+
defaultCountry: z.ZodOptional<z.ZodString>;
|
|
105
|
+
}, z.core.$strip>>;
|
|
106
|
+
captcha: z.ZodOptional<z.ZodObject<{
|
|
107
|
+
provider: z.ZodOptional<z.ZodEnum<{
|
|
108
|
+
turnstile: "turnstile";
|
|
109
|
+
recaptcha: "recaptcha";
|
|
110
|
+
hcaptcha: "hcaptcha";
|
|
111
|
+
}>>;
|
|
112
|
+
siteKey: z.ZodOptional<z.ZodString>;
|
|
113
|
+
theme: z.ZodOptional<z.ZodEnum<{
|
|
114
|
+
auto: "auto";
|
|
115
|
+
light: "light";
|
|
116
|
+
dark: "dark";
|
|
117
|
+
}>>;
|
|
118
|
+
size: z.ZodOptional<z.ZodEnum<{
|
|
119
|
+
normal: "normal";
|
|
120
|
+
compact: "compact";
|
|
121
|
+
}>>;
|
|
122
|
+
}, z.core.$strip>>;
|
|
63
123
|
showIf: z.ZodOptional<z.ZodObject<{
|
|
64
124
|
field: z.ZodString;
|
|
65
125
|
op: z.ZodEnum<{
|
|
126
|
+
in: "in";
|
|
66
127
|
eq: "eq";
|
|
67
128
|
neq: "neq";
|
|
68
|
-
|
|
129
|
+
empty: "empty";
|
|
130
|
+
notEmpty: "notEmpty";
|
|
69
131
|
truthy: "truthy";
|
|
70
132
|
}>;
|
|
71
133
|
value: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -80,12 +142,60 @@ export declare const formDefinitionSchema: z.ZodObject<{
|
|
|
80
142
|
keep: z.ZodArray<z.ZodString>;
|
|
81
143
|
}, z.core.$strip>>;
|
|
82
144
|
}, z.core.$strip>>;
|
|
145
|
+
access: z.ZodOptional<z.ZodObject<{
|
|
146
|
+
availability: z.ZodDefault<z.ZodEnum<{
|
|
147
|
+
web: "web";
|
|
148
|
+
api: "api";
|
|
149
|
+
}>>;
|
|
150
|
+
auth: z.ZodDefault<z.ZodEnum<{
|
|
151
|
+
none: "none";
|
|
152
|
+
user: "user";
|
|
153
|
+
token: "token";
|
|
154
|
+
password: "password";
|
|
155
|
+
}>>;
|
|
156
|
+
schedule: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
157
|
+
type: z.ZodLiteral<"always">;
|
|
158
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
159
|
+
type: z.ZodLiteral<"range">;
|
|
160
|
+
from: z.ZodOptional<z.ZodString>;
|
|
161
|
+
to: z.ZodOptional<z.ZodString>;
|
|
162
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
163
|
+
type: z.ZodLiteral<"yearly">;
|
|
164
|
+
from: z.ZodObject<{
|
|
165
|
+
month: z.ZodNumber;
|
|
166
|
+
day: z.ZodNumber;
|
|
167
|
+
time: z.ZodString;
|
|
168
|
+
}, z.core.$strip>;
|
|
169
|
+
to: z.ZodObject<{
|
|
170
|
+
month: z.ZodNumber;
|
|
171
|
+
day: z.ZodNumber;
|
|
172
|
+
time: z.ZodString;
|
|
173
|
+
}, z.core.$strip>;
|
|
174
|
+
timezone: z.ZodString;
|
|
175
|
+
}, z.core.$strip>], "type">>;
|
|
176
|
+
}, z.core.$strip>>;
|
|
83
177
|
delivery: z.ZodOptional<z.ZodObject<{
|
|
84
178
|
email: z.ZodOptional<z.ZodObject<{
|
|
85
179
|
to: z.ZodArray<z.ZodString>;
|
|
86
180
|
subject: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
181
|
+
body: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
182
|
+
routes: z.ZodOptional<z.ZodObject<{
|
|
183
|
+
field: z.ZodString;
|
|
184
|
+
to: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
|
|
185
|
+
}, z.core.$strip>>;
|
|
186
|
+
replyToField: z.ZodOptional<z.ZodString>;
|
|
87
187
|
}, z.core.$strip>>;
|
|
88
188
|
}, z.core.$strip>>;
|
|
89
189
|
}, z.core.$strip>;
|
|
90
190
|
export type FormDefinitionInput = z.input<typeof formDefinitionSchema>;
|
|
91
191
|
export declare function parseFormDefinition(input: unknown): FormDefinition;
|
|
192
|
+
/** Recipients and reply-to of an email delivery for the given answers (a matching route wins over `to`). */
|
|
193
|
+
export declare function resolveEmailDelivery(email: NonNullable<NonNullable<FormDefinition['delivery']>['email']>, answers: Record<string, unknown>): {
|
|
194
|
+
to: string[];
|
|
195
|
+
replyTo: string | undefined;
|
|
196
|
+
};
|
|
197
|
+
export declare const defaultAccess: FormAccess;
|
|
198
|
+
/** The effective access of a definition (older definitions have none and stay open). */
|
|
199
|
+
export declare function resolveAccess(definition: {
|
|
200
|
+
access?: Partial<FormAccess>;
|
|
201
|
+
}): FormAccess;
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { scheduleSchema } from "@frigear-nu/module-kit/schedule";
|
|
3
|
+
import { isPhoneCountry } from "./phone.js";
|
|
2
4
|
const translated = z.union([z.string(), z.record(z.string(), z.string())]);
|
|
5
|
+
const MAX_BODY = 1e4;
|
|
6
|
+
const bodyText = z.union([
|
|
7
|
+
z.string().max(MAX_BODY),
|
|
8
|
+
z.record(z.string(), z.string().max(MAX_BODY))
|
|
9
|
+
]);
|
|
3
10
|
const KEY = /^[a-z]\w*$/i;
|
|
4
11
|
const ID = /^[a-z0-9][\w-]*$/i;
|
|
5
12
|
const condition = z.object({
|
|
6
13
|
field: z.string().regex(KEY),
|
|
7
|
-
op: z.enum(["eq", "neq", "in", "truthy"]),
|
|
14
|
+
op: z.enum(["eq", "neq", "in", "empty", "notEmpty", "truthy"]),
|
|
8
15
|
value: z.unknown().optional()
|
|
9
16
|
});
|
|
10
17
|
const field = z.object({
|
|
11
18
|
key: z.string().regex(KEY, "Field keys must start with a letter and contain only letters, digits and underscores."),
|
|
12
|
-
type: z.enum(["text", "email", "tel", "number", "textarea", "select", "radio", "checkbox", "date", "file", "markdown"]),
|
|
19
|
+
type: z.enum(["text", "email", "tel", "number", "textarea", "select", "radio", "checkbox", "date", "file", "markdown", "captcha"]),
|
|
13
20
|
content: translated.optional(),
|
|
14
21
|
label: translated.optional(),
|
|
15
22
|
description: translated.optional(),
|
|
@@ -29,6 +36,17 @@ const field = z.object({
|
|
|
29
36
|
accept: z.array(z.string()).optional(),
|
|
30
37
|
message: translated.optional()
|
|
31
38
|
}).optional(),
|
|
39
|
+
phone: z.object({
|
|
40
|
+
mode: z.enum(["simple", "country"]).optional(),
|
|
41
|
+
countries: z.array(z.string().toUpperCase().refine(isPhoneCountry, "Unknown country code.")).max(300).optional(),
|
|
42
|
+
defaultCountry: z.string().toUpperCase().refine(isPhoneCountry, "Unknown country code.").optional()
|
|
43
|
+
}).optional(),
|
|
44
|
+
captcha: z.object({
|
|
45
|
+
provider: z.enum(["turnstile", "recaptcha", "hcaptcha"]).optional(),
|
|
46
|
+
siteKey: z.string().max(200).regex(/^[\w-]*$/, "Site keys contain only letters, digits, dashes and underscores.").optional(),
|
|
47
|
+
theme: z.enum(["auto", "light", "dark"]).optional(),
|
|
48
|
+
size: z.enum(["normal", "compact"]).optional()
|
|
49
|
+
}).optional(),
|
|
32
50
|
showIf: condition.optional()
|
|
33
51
|
});
|
|
34
52
|
const step = z.object({
|
|
@@ -40,6 +58,11 @@ const step = z.object({
|
|
|
40
58
|
});
|
|
41
59
|
const sameOriginPath = (value) => value.startsWith("/") && !value.startsWith("//") && !value.startsWith("/\\");
|
|
42
60
|
const emailAddress = z.string().regex(/^[^\s@${}]+@[^\s$@{}][^\s$.@{}]*\.[^\s$@{}]+$/, "Recipients must be plain email addresses.");
|
|
61
|
+
export const accessSchema = z.object({
|
|
62
|
+
availability: z.enum(["web", "api"]).default("web"),
|
|
63
|
+
auth: z.enum(["none", "user", "token", "password"]).default("none"),
|
|
64
|
+
schedule: scheduleSchema.optional()
|
|
65
|
+
});
|
|
43
66
|
export const formDefinitionSchema = z.object({
|
|
44
67
|
mode: z.enum(["single", "stepped"]),
|
|
45
68
|
storage: z.enum(["db", "none"]).default("db"),
|
|
@@ -50,14 +73,24 @@ export const formDefinitionSchema = z.object({
|
|
|
50
73
|
redirect: z.string().refine(sameOriginPath, "Redirect must be a path on this site.").optional(),
|
|
51
74
|
allowResubmit: z.object({ keep: z.array(z.string()) }).optional()
|
|
52
75
|
}).optional(),
|
|
76
|
+
access: accessSchema.optional(),
|
|
53
77
|
delivery: z.object({
|
|
54
|
-
email: z.object({
|
|
78
|
+
email: z.object({
|
|
79
|
+
to: z.array(emailAddress).min(1),
|
|
80
|
+
subject: translated.optional(),
|
|
81
|
+
body: bodyText.optional(),
|
|
82
|
+
routes: z.object({ field: z.string(), to: z.record(z.string(), z.array(emailAddress).min(1)) }).optional(),
|
|
83
|
+
replyToField: z.string().optional()
|
|
84
|
+
}).optional()
|
|
55
85
|
}).optional()
|
|
56
86
|
}).superRefine((definition, ctx) => {
|
|
57
87
|
const fail = (message, path) => ctx.addIssue({ code: "custom", message, path });
|
|
58
88
|
if (definition.mode === "single" && definition.steps.length !== 1) {
|
|
59
89
|
fail("A single page form has exactly one step.", ["steps"]);
|
|
60
90
|
}
|
|
91
|
+
if (definition.access?.auth === "token" && definition.access.availability !== "api") {
|
|
92
|
+
fail("Token access needs the form to be API only.", ["access", "availability"]);
|
|
93
|
+
}
|
|
61
94
|
const stepIds = /* @__PURE__ */ new Set();
|
|
62
95
|
const keys = /* @__PURE__ */ new Map();
|
|
63
96
|
definition.steps.forEach((s, stepIndex) => {
|
|
@@ -91,9 +124,16 @@ export const formDefinitionSchema = z.object({
|
|
|
91
124
|
check(s.showIf, ["steps", stepIndex, "showIf", "field"]);
|
|
92
125
|
s.fields.forEach((f, fieldIndex) => {
|
|
93
126
|
check(f.showIf, ["steps", stepIndex, "fields", fieldIndex, "showIf", "field"]);
|
|
94
|
-
if (f.type !== "markdown") seen.add(f.key);
|
|
127
|
+
if (f.type !== "markdown" && f.type !== "captcha") seen.add(f.key);
|
|
95
128
|
});
|
|
96
129
|
});
|
|
130
|
+
const emailDelivery = definition.delivery?.email;
|
|
131
|
+
if (emailDelivery?.routes && !seen.has(emailDelivery.routes.field)) {
|
|
132
|
+
fail(`Unknown field "${emailDelivery.routes.field}".`, ["delivery", "email", "routes", "field"]);
|
|
133
|
+
}
|
|
134
|
+
if (emailDelivery?.replyToField && keys.get(emailDelivery.replyToField) !== "email") {
|
|
135
|
+
fail("Reply-to must be an email field.", ["delivery", "email", "replyToField"]);
|
|
136
|
+
}
|
|
97
137
|
definition.submit?.allowResubmit?.keep.forEach((key, index) => {
|
|
98
138
|
if (!keys.has(key)) fail(`Unknown field "${key}".`, ["submit", "allowResubmit", "keep", index]);
|
|
99
139
|
});
|
|
@@ -101,3 +141,12 @@ export const formDefinitionSchema = z.object({
|
|
|
101
141
|
export function parseFormDefinition(input) {
|
|
102
142
|
return formDefinitionSchema.parse(input);
|
|
103
143
|
}
|
|
144
|
+
export function resolveEmailDelivery(email, answers) {
|
|
145
|
+
const routed = email.routes?.to[String(answers[email.routes.field] ?? "")];
|
|
146
|
+
const replyTo = email.replyToField ? String(answers[email.replyToField] ?? "").trim() : "";
|
|
147
|
+
return { to: routed?.length ? routed : email.to, replyTo: replyTo || void 0 };
|
|
148
|
+
}
|
|
149
|
+
export const defaultAccess = { availability: "web", auth: "none" };
|
|
150
|
+
export function resolveAccess(definition) {
|
|
151
|
+
return { ...defaultAccess, ...definition.access };
|
|
152
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FormAccessInfoDto } from '../../types.js';
|
|
2
|
+
/** What `<FormEmbed>` renders for a form. */
|
|
3
|
+
export type EmbedMode = 'form' | 'password' | 'sign-in' | 'hidden' | 'not-found';
|
|
4
|
+
/**
|
|
5
|
+
* Picks what an embedded form shows from its access info. API-only forms and token forms have no
|
|
6
|
+
* public UI (a bearer token cannot be typed into a page), so they render nothing; a missing form is `not-found`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function embedMode(access: FormAccessInfoDto | null | undefined): EmbedMode;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function embedMode(access) {
|
|
2
|
+
if (!access) return "not-found";
|
|
3
|
+
if (access.access.availability === "api" || access.access.auth === "token") return "hidden";
|
|
4
|
+
if (access.access.auth === "user" && !access.signedIn) return "sign-in";
|
|
5
|
+
if (access.access.auth === "password") return "password";
|
|
6
|
+
return "form";
|
|
7
|
+
}
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import type { FormDefinition } from '../../types.js';
|
|
2
2
|
export type * from '../../types';
|
|
3
|
-
export {
|
|
3
|
+
export { interpolate, defaultMessages, newId } from './text.js';
|
|
4
|
+
export { translate, readTranslated, writeTranslated, expandTranslated, collapseTranslated } from '@frigear-nu/ui/translated';
|
|
4
5
|
export type { MessageTranslator, TranslateParams } from './text.js';
|
|
5
|
-
export { evaluateCondition, resolveVisibility, isFieldVisible, isInputField } from './conditions.js';
|
|
6
|
+
export { evaluateCondition, resolveVisibility, isFieldVisible, isInputField, isAnswerField } from './conditions.js';
|
|
6
7
|
export type { Answers, VisibleStep } from './conditions.js';
|
|
7
8
|
export { buildFormSchema, buildFieldSchema, validatedFields } from './schema.js';
|
|
8
9
|
export type { SchemaOptions } from './schema.js';
|
|
9
|
-
export {
|
|
10
|
+
export { fieldTypes, arrayMove, moveField, moveStep, dropIndex, insertField, insertStep, removeField, updateField, updateStep, duplicateField, createField, createStep, removeStep, changeMode, pruneReferences, renameFieldKey, allFieldKeys, uniqueFieldKey, uniqueStepId, setEmailRecipients, patchEmailDelivery, setRouteField, setRoute, setStorage, setAccess, storageWarnings, emailFieldKeys, routableFields, } from './builder.js';
|
|
11
|
+
export type { FieldPosition } from './builder.js';
|
|
12
|
+
export { formDefinitionSchema, accessSchema, defaultAccess, resolveAccess, parseFormDefinition, resolveEmailDelivery } from './definition.js';
|
|
10
13
|
export type { FormDefinitionInput } from './definition.js';
|
|
11
14
|
/** Typed identity helper for authoring definitions (seed files, tests). */
|
|
12
15
|
export declare function defineForm<const T extends FormDefinition>(definition: T): T;
|
|
13
16
|
export { renderMarkdown } from './markdown.js';
|
|
17
|
+
export { captchaProviders, normalizeCaptchaProvider, resolveCaptcha } from './captcha.js';
|
|
18
|
+
export { previewPayload, previewScale, PREVIEW_WIDTHS } from './preview.js';
|
|
19
|
+
export type { PreviewDevice } from './preview.js';
|
|
20
|
+
export { renderAnswers, formatValue, escapeHtml, answersToText, answersToHtml, composeEmail, renderTemplateText, renderTemplateHtml, renderTemplateLine, templateVariables, availableVariables, unknownBodyVariables, sampleAnswers, staticVariables, } from './template.js';
|
|
21
|
+
export type { RenderedAnswer, EmailContext } from './template.js';
|
|
22
|
+
export { allPhoneCountries, phoneCountries, defaultPhoneCountry, countryName, composePhone, parsePhone, phoneIssue, isPhoneCountry, isCountryPhone, } from './phone.js';
|
|
23
|
+
export type { PhoneCountry } from './phone.js';
|
|
24
|
+
export { embedMode } from './embed.js';
|
|
25
|
+
export type { EmbedMode } from './embed.js';
|
|
26
|
+
export { isFormOpen, nextFormOpening } from './schedule.js';
|
|
@@ -1,8 +1,71 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { interpolate, defaultMessages, newId } from "./text.js";
|
|
2
|
+
export { translate, readTranslated, writeTranslated, expandTranslated, collapseTranslated } from "@frigear-nu/ui/translated";
|
|
3
|
+
export { evaluateCondition, resolveVisibility, isFieldVisible, isInputField, isAnswerField } from "./conditions.js";
|
|
3
4
|
export { buildFormSchema, buildFieldSchema, validatedFields } from "./schema.js";
|
|
4
|
-
export {
|
|
5
|
+
export {
|
|
6
|
+
fieldTypes,
|
|
7
|
+
arrayMove,
|
|
8
|
+
moveField,
|
|
9
|
+
moveStep,
|
|
10
|
+
dropIndex,
|
|
11
|
+
insertField,
|
|
12
|
+
insertStep,
|
|
13
|
+
removeField,
|
|
14
|
+
updateField,
|
|
15
|
+
updateStep,
|
|
16
|
+
duplicateField,
|
|
17
|
+
createField,
|
|
18
|
+
createStep,
|
|
19
|
+
removeStep,
|
|
20
|
+
changeMode,
|
|
21
|
+
pruneReferences,
|
|
22
|
+
renameFieldKey,
|
|
23
|
+
allFieldKeys,
|
|
24
|
+
uniqueFieldKey,
|
|
25
|
+
uniqueStepId,
|
|
26
|
+
setEmailRecipients,
|
|
27
|
+
patchEmailDelivery,
|
|
28
|
+
setRouteField,
|
|
29
|
+
setRoute,
|
|
30
|
+
setStorage,
|
|
31
|
+
setAccess,
|
|
32
|
+
storageWarnings,
|
|
33
|
+
emailFieldKeys,
|
|
34
|
+
routableFields
|
|
35
|
+
} from "./builder.js";
|
|
36
|
+
export { formDefinitionSchema, accessSchema, defaultAccess, resolveAccess, parseFormDefinition, resolveEmailDelivery } from "./definition.js";
|
|
5
37
|
export function defineForm(definition) {
|
|
6
38
|
return definition;
|
|
7
39
|
}
|
|
8
40
|
export { renderMarkdown } from "./markdown.js";
|
|
41
|
+
export { captchaProviders, normalizeCaptchaProvider, resolveCaptcha } from "./captcha.js";
|
|
42
|
+
export { previewPayload, previewScale, PREVIEW_WIDTHS } from "./preview.js";
|
|
43
|
+
export {
|
|
44
|
+
renderAnswers,
|
|
45
|
+
formatValue,
|
|
46
|
+
escapeHtml,
|
|
47
|
+
answersToText,
|
|
48
|
+
answersToHtml,
|
|
49
|
+
composeEmail,
|
|
50
|
+
renderTemplateText,
|
|
51
|
+
renderTemplateHtml,
|
|
52
|
+
renderTemplateLine,
|
|
53
|
+
templateVariables,
|
|
54
|
+
availableVariables,
|
|
55
|
+
unknownBodyVariables,
|
|
56
|
+
sampleAnswers,
|
|
57
|
+
staticVariables
|
|
58
|
+
} from "./template.js";
|
|
59
|
+
export {
|
|
60
|
+
allPhoneCountries,
|
|
61
|
+
phoneCountries,
|
|
62
|
+
defaultPhoneCountry,
|
|
63
|
+
countryName,
|
|
64
|
+
composePhone,
|
|
65
|
+
parsePhone,
|
|
66
|
+
phoneIssue,
|
|
67
|
+
isPhoneCountry,
|
|
68
|
+
isCountryPhone
|
|
69
|
+
} from "./phone.js";
|
|
70
|
+
export { embedMode } from "./embed.js";
|
|
71
|
+
export { isFormOpen, nextFormOpening } from "./schedule.js";
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Tiny, safe renderer for `markdown` fields: escapes everything, then supports `**bold**`,
|
|
3
|
-
* `_italic_`, `[text](https://…)` links and blank-line separated paragraphs.
|
|
4
|
-
*/
|
|
5
|
-
export declare function renderMarkdown(source: string): string;
|
|
1
|
+
export { renderMarkdown } from '@frigear-nu/ui/markdown';
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const safeHref = (href) => /^(?:https?:\/\/|\/(?![/\\])|mailto:)/.test(href);
|
|
3
|
-
export function renderMarkdown(source) {
|
|
4
|
-
return source.split(/\n{2,}/).map((paragraph) => escape(paragraph.trim()).replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>").replace(/\b_(.+?)_\b/g, "<em>$1</em>").replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (match, label, href) => safeHref(href.replace(/&/g, "&")) ? `<a href="${href}" target="_blank" rel="noopener" class="underline">${label}</a>` : match).replace(/\n/g, "<br>")).filter(Boolean).map((paragraph) => `<p>${paragraph}</p>`).join("");
|
|
5
|
-
}
|
|
1
|
+
export { renderMarkdown } from "@frigear-nu/ui/markdown";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FormField, PhoneConfig } from '../../types.js';
|
|
2
|
+
export declare const DEFAULT_PHONE_COUNTRY = "DK";
|
|
3
|
+
export type PhoneCountry = {
|
|
4
|
+
code: string;
|
|
5
|
+
dial: string;
|
|
6
|
+
flag: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const allPhoneCountries: PhoneCountry[];
|
|
9
|
+
export declare const isPhoneCountry: (code: string) => boolean;
|
|
10
|
+
/** Whether the field collects its phone number with a separate country selector. */
|
|
11
|
+
export declare const isCountryPhone: (field: Pick<FormField, "type" | "phone">) => boolean;
|
|
12
|
+
/** The countries a phone field offers, in the order of its `countries` allow-list (unknown codes dropped), or every country. */
|
|
13
|
+
export declare function phoneCountries(config?: PhoneConfig): PhoneCountry[];
|
|
14
|
+
/** The preselected country: `defaultCountry` when offered, else Denmark when offered, else the first one offered. */
|
|
15
|
+
export declare function defaultPhoneCountry(config?: PhoneConfig): string;
|
|
16
|
+
/** Localised country name, falling back to the ISO code. */
|
|
17
|
+
export declare function countryName(code: string, locale: string): string;
|
|
18
|
+
/** Merges a country and the national number into the single stored value, e.g. `+4512345678`. Empty without digits. */
|
|
19
|
+
export declare function composePhone(country: string, national: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Splits a stored value back into country and national digits. Among the offered countries the longest
|
|
22
|
+
* matching dialing code wins; `preferred` wins between countries sharing that code (`+1`, `+44`, ...).
|
|
23
|
+
*/
|
|
24
|
+
export declare function parsePhone(value: string, config?: PhoneConfig, preferred?: string): {
|
|
25
|
+
country: string;
|
|
26
|
+
national: string;
|
|
27
|
+
};
|
|
28
|
+
/** Why a value is not an acceptable phone number for the field, or `undefined` when it is. */
|
|
29
|
+
export declare function phoneIssue(field: Pick<FormField, 'phone'>, value: string): 'format' | 'country' | undefined;
|