@sealcode/sealgen 0.8.48 → 0.8.49

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.
Files changed (136) hide show
  1. package/.log/ti-107027.log +576 -0
  2. package/.log/ti-129414.log +1355 -0
  3. package/.log/ti-135707.log +1049 -0
  4. package/.log/ti-136226.log +1049 -0
  5. package/.log/ti-147652.log +533 -0
  6. package/.log/ti-157088.log +2938 -0
  7. package/.log/ti-250667.log +548 -0
  8. package/.log/ti-29438.log +165 -0
  9. package/.log/ti-29894.log +149 -0
  10. package/.log/ti-33785.log +149 -0
  11. package/.log/ti-34262.log +149 -0
  12. package/.log/ti-35719.log +149 -0
  13. package/.log/ti-41629.log +518 -0
  14. package/.log/ti-441248.log +517 -0
  15. package/.log/ti-5352.log +517 -0
  16. package/.log/ti-76335.log +639 -0
  17. package/.log/ti-811639.log +517 -0
  18. package/.log/ti-81805.log +564 -0
  19. package/.log/ti-86459.log +517 -0
  20. package/.log/tsserver.log +6881 -0
  21. package/.log/tsserver.sync-conflict-20221010-222251-JUX7UR2.log +9412 -0
  22. package/package.json +1 -1
  23. package/src/add-collection.ts +31 -0
  24. package/src/add-route.test.ts +58 -0
  25. package/src/add-route.ts +89 -0
  26. package/src/build.ts +94 -0
  27. package/src/cli.ts +36 -0
  28. package/src/components/components.ts +2 -0
  29. package/src/components/tabs.ts +95 -0
  30. package/src/components/toggle-modal.ts +52 -0
  31. package/src/example.test.ts +3 -0
  32. package/src/find-scss-includes.ts +32 -0
  33. package/src/forms/controls/array.ts +226 -0
  34. package/src/forms/controls/autocomplete.ts +33 -0
  35. package/src/forms/controls/big-radio-group.ts +48 -0
  36. package/src/forms/controls/checkbox-group.ts +71 -0
  37. package/src/forms/controls/checkbox.ts +34 -0
  38. package/src/forms/controls/checkboxed-list-input.ts +50 -0
  39. package/src/forms/controls/collapsible-checkbox-group.ts +28 -0
  40. package/src/forms/controls/collapsible-field-group.ts +94 -0
  41. package/src/forms/controls/controls.ts +30 -0
  42. package/src/forms/controls/derived-frame.ts +91 -0
  43. package/src/forms/controls/derived.ts +108 -0
  44. package/src/forms/controls/dropdown.ts +87 -0
  45. package/src/forms/controls/editable-collection-subset.ts +97 -0
  46. package/src/forms/controls/field-group.ts +69 -0
  47. package/src/forms/controls/file.ts +47 -0
  48. package/src/forms/controls/form-control.ts +24 -0
  49. package/src/forms/controls/form-field-control.ts +22 -0
  50. package/src/forms/controls/form-header.ts +19 -0
  51. package/src/forms/controls/form-paragraph.ts +11 -0
  52. package/src/forms/controls/frame.ts +26 -0
  53. package/src/forms/controls/hidden-input.ts +22 -0
  54. package/src/forms/controls/html.ts +38 -0
  55. package/src/forms/controls/multiple-files.ts +256 -0
  56. package/src/forms/controls/multiple-photos.ts +44 -0
  57. package/src/forms/controls/number.ts +38 -0
  58. package/src/forms/controls/photo.ts +74 -0
  59. package/src/forms/controls/radio.ts +43 -0
  60. package/src/forms/controls/reverse-single-reference.ts +50 -0
  61. package/src/forms/controls/simple-input.ts +148 -0
  62. package/src/forms/controls/textarea.ts +60 -0
  63. package/src/forms/controls/tickable.ts +127 -0
  64. package/src/forms/fields/boolean.test.ts +36 -0
  65. package/src/forms/fields/boolean.ts +63 -0
  66. package/src/forms/fields/checkboxed-list.ts +66 -0
  67. package/src/forms/fields/collection-field.ts +62 -0
  68. package/src/forms/fields/email.test.ts +24 -0
  69. package/src/forms/fields/email.ts +20 -0
  70. package/src/forms/fields/enum-multiple.test.ts +31 -0
  71. package/src/forms/fields/enum-multiple.ts +62 -0
  72. package/src/forms/fields/enum.test.ts +25 -0
  73. package/src/forms/fields/enum.ts +56 -0
  74. package/src/forms/fields/existing-sealious-value.ts +22 -0
  75. package/src/forms/fields/field.ts +172 -0
  76. package/src/forms/fields/file.ts +89 -0
  77. package/src/forms/fields/multiple-files.ts +20 -0
  78. package/src/forms/fields/number.test.ts +34 -0
  79. package/src/forms/fields/number.ts +70 -0
  80. package/src/forms/fields/pick-from-list.test.ts +19 -0
  81. package/src/forms/fields/pick-from-list.ts +52 -0
  82. package/src/forms/fields/proxy-field.ts +24 -0
  83. package/src/forms/fields/regexp.test.ts +25 -0
  84. package/src/forms/fields/regexp.ts +53 -0
  85. package/src/forms/fields/reverse-single-reference.ts +20 -0
  86. package/src/forms/fields/simple-form-field.ts +30 -0
  87. package/src/forms/fields/structured-array.ts +27 -0
  88. package/src/forms/form.test.ts +351 -0
  89. package/src/forms/form.ts +294 -0
  90. package/src/forms/forms.scss +85 -0
  91. package/src/forms/multiform.test.ts +540 -0
  92. package/src/forms/multiform.ts +236 -0
  93. package/src/generate-collections.ts +77 -0
  94. package/src/generate-routes.ts +121 -0
  95. package/src/index.ts +25 -0
  96. package/src/make-env.ts +15 -0
  97. package/src/make-hidden-inputs.ts +24 -0
  98. package/src/mount.ts +85 -0
  99. package/src/page/access-control.ts +18 -0
  100. package/src/page/list.ts +240 -0
  101. package/src/page/mountable-with-fields.ts +158 -0
  102. package/src/page/mountable.ts +13 -0
  103. package/src/page/page.ts +46 -0
  104. package/src/page/props-parser.ts +38 -0
  105. package/src/page/stateful-page.ts +202 -0
  106. package/src/run.ts +0 -0
  107. package/src/sanitize.ts +9 -0
  108. package/src/templates/collection.ts +11 -0
  109. package/src/templates/form.ts +60 -0
  110. package/src/templates/list.ts +75 -0
  111. package/src/templates/multiform.ts +58 -0
  112. package/src/templates/page.ts +25 -0
  113. package/src/templates/post.ts +21 -0
  114. package/src/templates/redirect.ts +22 -0
  115. package/src/templates/route-test.ts +27 -0
  116. package/src/templates/stateful-page.ts +65 -0
  117. package/src/test_utils/locator_is_visible.ts +17 -0
  118. package/src/utils/assert-throws.ts +18 -0
  119. package/src/utils/base64.ts +7 -0
  120. package/src/utils/browser-creator.js +10 -0
  121. package/src/utils/escape-url-params.ts +14 -0
  122. package/src/utils/exec.ts +30 -0
  123. package/src/utils/format-external-url.ts +13 -0
  124. package/src/utils/import-path.ts +25 -0
  125. package/src/utils/input-wrapper.ts +21 -0
  126. package/src/utils/list-collections.ts +16 -0
  127. package/src/utils/make-slug.ts +5 -0
  128. package/src/utils/natural-numbers.ts +5 -0
  129. package/src/utils/prettier.ts +13 -0
  130. package/src/utils/question.ts +23 -0
  131. package/src/utils/render-attributes.ts +17 -0
  132. package/src/utils/sleep.ts +3 -0
  133. package/src/utils/translate.ts +13 -0
  134. package/src/utils/url-with-new-params.ts +9 -0
  135. package/src/utils/utils.ts +23 -0
  136. package/src/utils/walk.ts +20 -0
@@ -0,0 +1,172 @@
1
+ import { Predicate, predicates } from "@sealcode/ts-predicates";
2
+ import { Context } from "koa";
3
+ import { t } from "../../utils/translate";
4
+ import { FormDataValue } from "../form";
5
+
6
+ export type FormFieldValidationResponse = { valid: boolean; message: string };
7
+
8
+ export type FormFieldValidationFn<ValueType> = (
9
+ ctx: Context,
10
+ value: ValueType,
11
+ field: FormField<boolean, ValueType>
12
+ ) => Promise<FormFieldValidationResponse>;
13
+
14
+ export type FieldParsedValue<T> = T extends FormField<infer R> ? R : never;
15
+
16
+ export type GetPredicate<F extends FormField> = F extends FormField<true>
17
+ ? F["predicate"]
18
+ : F["predicate"] | typeof predicates.undefined;
19
+
20
+ // for quickly testing GetPredicate conditonally applying the undefined predicate:
21
+ // const f = new SimpleFormField(true);
22
+ // type p = GetPredicate<typeof f>;
23
+ // const f2 = new SimpleFormField(false);
24
+ // type p2 = GetPredicate<typeof f2>;
25
+
26
+ export type FieldsToShape<T extends Record<string, FormField>> = {
27
+ [property in keyof T]: GetPredicate<T[property]>;
28
+ };
29
+
30
+ export function fieldsToShape<Fields extends Record<string, FormField>>(
31
+ obj: Fields
32
+ ): FieldsToShape<Fields> {
33
+ return Object.fromEntries(
34
+ Object.entries(obj).map(([key, value]) => [key, value.predicate])
35
+ ) as FieldsToShape<Fields>;
36
+ }
37
+
38
+ export type FieldParseResult<T> =
39
+ | {
40
+ parsable: true;
41
+ error: null;
42
+ parsed_value: T;
43
+ }
44
+ | {
45
+ parsable: false;
46
+ error: string;
47
+ parsed_value: null;
48
+ };
49
+
50
+ export abstract class FormField<
51
+ Required extends boolean = boolean,
52
+ ParsedValue extends unknown = unknown
53
+ > {
54
+ name: string;
55
+ constructor(public readonly required: Required) {}
56
+ predicate: Predicate = predicates.unknown;
57
+ mapToFilter: (parsed: ParsedValue) => unknown = (value) => value;
58
+
59
+ async init(fieldname: string): Promise<void> {
60
+ this.name = fieldname;
61
+ }
62
+
63
+ public async _validate(
64
+ ctx: Context,
65
+ value: ParsedValue
66
+ ): Promise<FormFieldValidationResponse> {
67
+ if (
68
+ this.required &&
69
+ (value === "" || value === null || value === undefined)
70
+ ) {
71
+ return {
72
+ valid: false,
73
+
74
+ message: t(
75
+ ctx,
76
+ "field_is_required",
77
+ [],
78
+ "This field is required"
79
+ ),
80
+ };
81
+ }
82
+ return this.isValueValid(ctx, value);
83
+ }
84
+
85
+ public abstract getEmptyValue(): ParsedValue;
86
+
87
+ public async isValueValid(
88
+ ctx: Context,
89
+ value: ParsedValue
90
+ ): Promise<FormFieldValidationResponse> {
91
+ return { valid: true, message: "" };
92
+ }
93
+
94
+ abstract parse(
95
+ ctx: Context,
96
+ raw_value: FormDataValue
97
+ ): Promise<FieldParseResult<ParsedValue>>;
98
+
99
+ async getValue(
100
+ ctx: Context,
101
+ all_form_values: Record<string, FormDataValue>
102
+ ): Promise<{
103
+ valid: boolean;
104
+ message: string;
105
+ parsed: ParsedValue;
106
+ raw: FormDataValue;
107
+ }> {
108
+ if (Object.keys(all_form_values || {}).length === 0) {
109
+ // don't validate on virgin form
110
+ return {
111
+ parsed: this.getEmptyValue(),
112
+ valid: true,
113
+ message: "",
114
+ raw: "",
115
+ };
116
+ }
117
+ const raw = all_form_values[this.name];
118
+ const {
119
+ parsable,
120
+ parsed_value,
121
+ error: parse_error,
122
+ } = await this.parse(ctx, raw);
123
+ if (!parsable) {
124
+ return {
125
+ valid: false,
126
+ parsed: this.getEmptyValue(),
127
+ raw,
128
+ message: parse_error || "Error",
129
+ };
130
+ }
131
+ const { valid, message } = await this._validate(
132
+ ctx,
133
+ parsed_value as ParsedValue
134
+ );
135
+ return {
136
+ parsed: parsed_value as ParsedValue,
137
+ valid,
138
+ message,
139
+ raw: all_form_values[this.name],
140
+ };
141
+ }
142
+
143
+ setMapToFilter(fn: (parsed: ParsedValue) => unknown) {
144
+ this.mapToFilter = fn;
145
+ return this;
146
+ }
147
+
148
+ //return undefined here to delete a value
149
+ async getDatabaseValue(
150
+ ctx: Context,
151
+ data: Record<string, FormDataValue>
152
+ ): Promise<unknown> {
153
+ const { parsed } = await this.getValue(ctx, data);
154
+ return parsed;
155
+ }
156
+ }
157
+
158
+ export * from "./simple-form-field";
159
+ export * from "./checkboxed-list";
160
+ export * from "./collection-field";
161
+ export * from "./number";
162
+ export * from "./pick-from-list";
163
+ export * from "./regexp";
164
+ export * from "./email";
165
+ export * from "./enum";
166
+ export * from "./boolean";
167
+ export * from "./enum-multiple";
168
+ export * from "./existing-sealious-value";
169
+ export * from "./file";
170
+ export * from "./multiple-files";
171
+ export * from "./reverse-single-reference";
172
+ export * from "./structured-array";
@@ -0,0 +1,89 @@
1
+ import {
2
+ hasField,
3
+ hasFieldOfType,
4
+ hasShape,
5
+ is,
6
+ predicates,
7
+ } from "@sealcode/ts-predicates";
8
+ import { Context } from "koa";
9
+ import { File as SealiousFile } from "sealious";
10
+ import { FormDataValue } from "../form";
11
+ import { FieldParseResult, FormField } from "./field";
12
+
13
+ export type FileContainer = {
14
+ old: SealiousFile | null;
15
+ new: SealiousFile | null;
16
+ };
17
+
18
+ export class File<Required extends boolean> extends FormField<
19
+ Required,
20
+ FileContainer
21
+ > {
22
+ async parse(
23
+ ctx: Context,
24
+ raw_value: FormDataValue
25
+ ): Promise<FieldParseResult<FileContainer>> {
26
+ let result;
27
+ if (
28
+ raw_value instanceof SealiousFile ||
29
+ (is(raw_value, predicates.object) &&
30
+ hasShape(
31
+ {
32
+ filename: predicates.string,
33
+ id: predicates.string,
34
+ },
35
+ raw_value
36
+ ) &&
37
+ raw_value.id)
38
+ ) {
39
+ const file = await SealiousFile.fromID(
40
+ ctx.$app,
41
+ raw_value.id as string
42
+ );
43
+ result = {
44
+ parsed_value: { old: file, new: null },
45
+ error: null,
46
+ parsable: true as true,
47
+ };
48
+ } else if (!is(raw_value, predicates.object)) {
49
+ result = {
50
+ parsed_value: null,
51
+ error: "Expected a field with old.id:string, old.filename:string and new?:File",
52
+ parsable: false as false,
53
+ };
54
+ } else {
55
+ const old_file =
56
+ hasFieldOfType(raw_value, "old", predicates.object) &&
57
+ hasShape(
58
+ { id: predicates.string, filename: predicates.string },
59
+ raw_value.old
60
+ ) &&
61
+ raw_value.old.id
62
+ ? await SealiousFile.fromID(ctx.$app, raw_value.old.id)
63
+ : null;
64
+ const new_file =
65
+ hasFieldOfType(
66
+ raw_value,
67
+ "new",
68
+ predicates.array(predicates.object)
69
+ ) && raw_value.new[0] instanceof SealiousFile
70
+ ? raw_value.new[0]
71
+ : null;
72
+ result = {
73
+ parsable: true as true,
74
+ error: null,
75
+ parsed_value: { old: old_file, new: new_file },
76
+ };
77
+ }
78
+ return result;
79
+ }
80
+
81
+ async getDatabaseValue(ctx: Context, data: Record<string, FormDataValue>) {
82
+ const { parsed } = await this.getValue(ctx, data);
83
+ return parsed.new || undefined;
84
+ }
85
+
86
+ getEmptyValue() {
87
+ return { old: null, new: null };
88
+ }
89
+ }
@@ -0,0 +1,20 @@
1
+ import { Context } from "koa";
2
+ import { FieldTypes as SealiousFieldTypes } from "sealious";
3
+ import { FormField } from "./field";
4
+
5
+ export class MultipleFiles extends FormField<false> {
6
+ constructor(
7
+ public collection_field: SealiousFieldTypes.ReverseSingleReference,
8
+ public getItemId: (ctx: Context) => Promise<string> | string
9
+ ) {
10
+ super(false);
11
+ }
12
+
13
+ getEmptyValue() {
14
+ return undefined;
15
+ }
16
+
17
+ async parse() {
18
+ return <const>{ parsed_value: undefined, parsable: true, error: null }; // the control is supposed to do the heavy lifting here
19
+ }
20
+ }
@@ -0,0 +1,34 @@
1
+ import assert from "assert";
2
+ import { Fields } from "../../index";
3
+
4
+ describe("number field", () => {
5
+ const ctx = {} as any;
6
+
7
+ it("validation logic", async () => {
8
+ const required = new Fields.NumberField(true, 0);
9
+ const not_required = new Fields.NumberField(false, 0);
10
+
11
+ assert.strictEqual((await required.isValueValid(ctx, 42)).valid, true);
12
+ assert.strictEqual((await required.isValueValid(ctx, "42")).valid, true);
13
+ assert.strictEqual((await required.isValueValid(ctx, "notvalid")).valid, false);
14
+ assert.strictEqual((await not_required.isValueValid(ctx, "")).valid, true);
15
+ });
16
+
17
+ it("parse function", async () => {
18
+ const required = new Fields.NumberField(true, 0);
19
+ const decimal_digits = new Fields.NumberField(true, 0, { decimalDigits: 1 });
20
+ const number_as_string_response = await required.parse(ctx, "42");
21
+ const number_response = await required.parse(ctx, 42);
22
+ const not_a_number_response = await required.parse(ctx, "notanumber");
23
+ const decimal_number_response = await decimal_digits.parse(ctx, 42.42);
24
+
25
+ assert.strictEqual(number_as_string_response.parsable, true);
26
+ assert.strictEqual(number_as_string_response.parsed_value, 42);
27
+ assert.strictEqual(number_response.parsable, true);
28
+ assert.strictEqual(number_response.parsed_value, 42);
29
+ assert.strictEqual(not_a_number_response.parsable, false);
30
+ assert.strictEqual(not_a_number_response.error, "Please enter a number");
31
+ assert.strictEqual(decimal_number_response.parsable, true);
32
+ assert.strictEqual(decimal_number_response.parsed_value, 42.4);
33
+ });
34
+ });
@@ -0,0 +1,70 @@
1
+ import { is, predicates } from "@sealcode/ts-predicates";
2
+ import { Context } from "koa";
3
+ import { FormDataValue } from "../form";
4
+ import { FieldParseResult, FormField } from "./field";
5
+
6
+ export class NumberField<
7
+ DefaultValue extends number | null = number,
8
+ Required extends boolean = boolean
9
+ > extends FormField<Required, number | DefaultValue> {
10
+ constructor(
11
+ required: Required,
12
+ public default_value: DefaultValue,
13
+ public options: Partial<{
14
+ max: number;
15
+ min: number;
16
+ decimalDigits: number;
17
+ }> = {}
18
+ ) {
19
+ super(required);
20
+ }
21
+
22
+ predicate = predicates.number;
23
+
24
+ public async isValueValid(_: Context, value: unknown) {
25
+ if (
26
+ (is(value, predicates.string) &&
27
+ !isNaN(parseFloat(value)) &&
28
+ parseFloat(value).toString() == value.trim()) ||
29
+ is(value, predicates.number) ||
30
+ ((is(value, predicates.undefined) || value == "") && !this.required)
31
+ ) {
32
+ return { valid: true, message: "" };
33
+ }
34
+ return { valid: false, message: "Proszę wprowadzić liczbę" };
35
+ }
36
+
37
+ async parse(
38
+ _: Context,
39
+ value: FormDataValue
40
+ ): Promise<FieldParseResult<number>> {
41
+ if (
42
+ (is(value, predicates.string) &&
43
+ !isNaN(parseFloat(value)) &&
44
+ parseFloat(value).toString() == value.trim()) ||
45
+ is(value, predicates.number)
46
+ ) {
47
+ return {
48
+ parsable: true,
49
+ parsed_value: this.options.decimalDigits
50
+ ? parseFloat(
51
+ parseFloat(value.toString()).toFixed(
52
+ this.options.decimalDigits
53
+ )
54
+ )
55
+ : parseFloat(value.toString()),
56
+ error: null,
57
+ };
58
+ } else {
59
+ return {
60
+ parsable: false,
61
+ parsed_value: null,
62
+ error: "Please enter a number",
63
+ };
64
+ }
65
+ }
66
+
67
+ public getEmptyValue(): DefaultValue {
68
+ return this.default_value;
69
+ }
70
+ }
@@ -0,0 +1,19 @@
1
+ import assert from "assert";
2
+ import { Fields } from "../../index";
3
+
4
+ describe("pick-from-list field", () => {
5
+ const ctx = {} as any;
6
+
7
+ it("validation logic", async () => {
8
+ const field = new Fields.PickFromListField(true, async () => {
9
+ return [
10
+ { value: "samplev", label: "samplel" },
11
+ { value: "samplevv", label: "samplell" },
12
+ ];
13
+ });
14
+
15
+ assert.strictEqual((await field.isValueValid(ctx, 42 as any)).valid, false);
16
+ assert.strictEqual((await field.isValueValid(ctx, "samplevv")).valid, true);
17
+ assert.strictEqual((await field.isValueValid(ctx, "notvalid")).valid, false);
18
+ });
19
+ });
@@ -0,0 +1,52 @@
1
+ import { is, predicates } from "@sealcode/ts-predicates";
2
+ import { Context } from "koa";
3
+ import { DropdownOption } from "../controls/dropdown";
4
+ import { FormFieldValidationResponse } from "./field";
5
+ import { SimpleFormField } from "./simple-form-field";
6
+
7
+ export class PickFromListField<
8
+ Required extends boolean
9
+ > extends SimpleFormField<Required> {
10
+ constructor(
11
+ public required: Required,
12
+ public generateOptions: (ctx: Context) => Promise<DropdownOption[]>,
13
+ public customValidation: (
14
+ ctx: Context,
15
+ value: unknown,
16
+ instance: PickFromListField<Required>
17
+ ) => Promise<FormFieldValidationResponse> = (ctx, value, instance) =>
18
+ instance.valueInList(ctx, value)
19
+ ) {
20
+ super(required);
21
+ }
22
+
23
+ predicate = predicates.string;
24
+
25
+ public isValueValid(
26
+ ctx: Context,
27
+ value: string
28
+ ): Promise<FormFieldValidationResponse> {
29
+ return this.customValidation(ctx, value, this);
30
+ }
31
+
32
+ async valueInList(
33
+ ctx: Context,
34
+ value: unknown
35
+ ): Promise<FormFieldValidationResponse> {
36
+ const options = await this.generateOptions(ctx);
37
+ if (!is(value, predicates.string)) {
38
+ return { valid: false, message: "not a string" };
39
+ }
40
+ if (this.required && !options.map((o) => o.value).includes(value)) {
41
+ return {
42
+ valid: false,
43
+ message: `"${value}" is not one of the options`,
44
+ };
45
+ }
46
+ return { valid: true, message: "" };
47
+ }
48
+
49
+ public getEmptyValue(): string {
50
+ return "";
51
+ }
52
+ }
@@ -0,0 +1,24 @@
1
+ import { FormField } from "./field";
2
+
3
+ export class ProxyFormField<T> extends FormField<boolean, T> {
4
+ constructor(
5
+ required: boolean,
6
+ public name: string,
7
+ public empty_value: T,
8
+ public parsed_value: T
9
+ ) {
10
+ super(required);
11
+ }
12
+
13
+ getEmptyValue() {
14
+ return this.empty_value;
15
+ }
16
+
17
+ async parse() {
18
+ return <const>{
19
+ parsable: true,
20
+ error: null,
21
+ parsed_value: this.parsed_value,
22
+ };
23
+ }
24
+ }
@@ -0,0 +1,25 @@
1
+ import assert from "assert";
2
+ import { Fields } from "../../index";
3
+
4
+ describe("regexp field", () => {
5
+ const ctx = {} as any;
6
+
7
+ it("validation logic", async () => {
8
+ const field = new Fields.RegExpField(true, /^4/, "error");
9
+
10
+ assert.strictEqual((await field.isValueValid(ctx, "42")).valid, true);
11
+ assert.strictEqual((await field.isValueValid(ctx, "notvalid")).valid, false);
12
+ assert.strictEqual((await field.isValueValid(ctx, 42)).valid, false);
13
+ });
14
+
15
+ it("parse function", async () => {
16
+ const field = new Fields.RegExpField(true, /^4/, "error");
17
+ const string_response = await field.parse(ctx, "42");
18
+ const number_response = await field.parse(ctx, 42);
19
+
20
+ assert.strictEqual(string_response.parsable, true);
21
+ assert.strictEqual(string_response.parsed_value, "42");
22
+ assert.strictEqual(number_response.parsable, false);
23
+ assert.strictEqual(number_response.error, "Please enter a string");
24
+ });
25
+ });
@@ -0,0 +1,53 @@
1
+ import { is, predicates } from "@sealcode/ts-predicates";
2
+ import { Context } from "koa";
3
+ import { FormDataValue } from "../form";
4
+ import { FieldParseResult, FormField } from "./field";
5
+
6
+ export class RegExpField<Required extends boolean> extends FormField<Required> {
7
+ constructor(
8
+ required: Required,
9
+ public regexp: RegExp,
10
+ public error_message: string,
11
+ public default_value: string = ""
12
+ ) {
13
+ super(required);
14
+ }
15
+
16
+ predicate = predicates.string;
17
+
18
+ public async isValueValid(
19
+ _: Context,
20
+ value: unknown
21
+ ): Promise<{ valid: boolean; message: string }> {
22
+ if (!is(value, predicates.string)) {
23
+ return { valid: false, message: "expected a string" };
24
+ }
25
+ if (this.regexp.exec(value)) {
26
+ return { valid: true, message: "" };
27
+ }
28
+ return { valid: false, message: this.error_message };
29
+ }
30
+
31
+ async parse(
32
+ _: Context,
33
+ value: FormDataValue
34
+ ): Promise<FieldParseResult<string>> {
35
+ if (is(value, predicates.string)) {
36
+ return {
37
+ parsable: true,
38
+ parsed_value: value,
39
+ error: null,
40
+ };
41
+ } else {
42
+ return {
43
+ parsable: false,
44
+ parsed_value: null,
45
+ error: "Please enter a string",
46
+ };
47
+ }
48
+ }
49
+
50
+ public getEmptyValue(): string {
51
+ return this.default_value || "";
52
+ }
53
+ }
@@ -0,0 +1,20 @@
1
+ import { FormField } from "./field";
2
+ import { FieldTypes as SealiousFields } from "sealious";
3
+ import { Context } from "koa";
4
+
5
+ export class ReverseSingleReference extends FormField {
6
+ constructor(
7
+ required: boolean,
8
+ public sealious_field: SealiousFields.ReverseSingleReference
9
+ ) {
10
+ super(required);
11
+ }
12
+
13
+ async parse(ctx: Context, raw_value: unknown) {
14
+ return { parsable: <const>true, error: null, parsed_value: raw_value };
15
+ }
16
+
17
+ getEmptyValue() {
18
+ return [];
19
+ }
20
+ }
@@ -0,0 +1,30 @@
1
+ import { FieldParseResult, FormField } from "./field";
2
+ import { FormDataValue } from "../form";
3
+ import { Context } from "koa";
4
+ import { predicates } from "@sealcode/ts-predicates";
5
+
6
+ export class SimpleFormField<Required extends boolean> extends FormField<
7
+ Required,
8
+ string | null
9
+ > {
10
+ async parse(
11
+ _: Context,
12
+ raw_value: FormDataValue
13
+ ): Promise<FieldParseResult<string>> {
14
+ return {
15
+ parsed_value: (raw_value || "").toString(),
16
+ parsable: true,
17
+ error: null,
18
+ };
19
+ }
20
+
21
+ predicate = predicates.string;
22
+
23
+ public getEmptyValue(): string {
24
+ return "";
25
+ }
26
+
27
+ constructor(required: Required) {
28
+ super(required);
29
+ }
30
+ }
@@ -0,0 +1,27 @@
1
+ import { Context } from "koa";
2
+ import { Field, FieldsetInput, FieldsetOutput, FieldTypes } from "sealious";
3
+ import { FormDataValue } from "../form";
4
+ import { FormField } from "./field";
5
+
6
+ export class StructuredArrayField<
7
+ Fields extends Record<string, Field>
8
+ > extends FormField<
9
+ true,
10
+ FieldTypes.ArrayStorageInput<FieldsetOutput<Fields>>
11
+ > {
12
+ constructor(public sealious_field: FieldTypes.StructuredArray<Fields>) {
13
+ super(true);
14
+ }
15
+
16
+ async parse(context: Context, value: FormDataValue) {
17
+ return {
18
+ parsable: <const>true,
19
+ error: null,
20
+ parsed_value: (value as FieldsetOutput<Fields>) || [],
21
+ };
22
+ }
23
+
24
+ getEmptyValue() {
25
+ return [] as FieldTypes.ArrayStorageInput<FieldsetOutput<Fields>>;
26
+ }
27
+ }