@turndown/library 0.1.64 → 0.1.65

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.
@@ -0,0 +1,174 @@
1
+ // src/types/validation/index.ts
2
+ var createValidationSchema = (fields) => {
3
+ const allowedFields = Object.keys(fields);
4
+ const requiredFields = allowedFields.filter((fieldName) => {
5
+ return fields[fieldName].required;
6
+ });
7
+ return {
8
+ fields,
9
+ allowedFields,
10
+ requiredFields,
11
+ validate: (value, options = {}) => {
12
+ if (!isRecord(value)) {
13
+ return {
14
+ success: false,
15
+ issues: [
16
+ {
17
+ fieldName: "body",
18
+ type: "INVALID_BODY"
19
+ }
20
+ ]
21
+ };
22
+ }
23
+ const issues = getValidationIssues(fields, value, options);
24
+ if (issues.length > 0) {
25
+ return {
26
+ success: false,
27
+ issues
28
+ };
29
+ }
30
+ return {
31
+ success: true,
32
+ data: value,
33
+ issues: []
34
+ };
35
+ }
36
+ };
37
+ };
38
+ var requiredStringField = (missingValuePolicy = "Nullish") => ({
39
+ required: true,
40
+ missingValuePolicy,
41
+ validate: isNonEmptyString
42
+ });
43
+ var optionalStringField = () => ({
44
+ required: false,
45
+ missingValuePolicy: "Nullish",
46
+ validate: isNonEmptyString
47
+ });
48
+ var optionalNullableStringField = () => ({
49
+ required: false,
50
+ missingValuePolicy: "Nullish",
51
+ validate: (value) => {
52
+ return value === null || isNonEmptyString(value);
53
+ }
54
+ });
55
+ var requiredEmailField = (missingValuePolicy = "BlankString") => ({
56
+ required: true,
57
+ missingValuePolicy,
58
+ validate: isEmail
59
+ });
60
+ var requiredUuidField = (missingValuePolicy = "Nullish") => ({
61
+ required: true,
62
+ missingValuePolicy,
63
+ validate: isUuid
64
+ });
65
+ var optionalNullableNonNegativeIntegerField = () => ({
66
+ required: false,
67
+ missingValuePolicy: "Nullish",
68
+ validate: (value) => {
69
+ return value === null || Number.isInteger(value) && Number(value) >= 0;
70
+ }
71
+ });
72
+ var optionalRecordField = () => ({
73
+ required: false,
74
+ missingValuePolicy: "Nullish",
75
+ validate: isRecord
76
+ });
77
+ var requiredEnumField = (allowedValues, missingValuePolicy = "Nullish") => ({
78
+ required: true,
79
+ missingValuePolicy,
80
+ validate: (value) => {
81
+ return isAllowedStringValue(value, allowedValues);
82
+ }
83
+ });
84
+ var optionalEnumField = (allowedValues) => ({
85
+ required: false,
86
+ missingValuePolicy: "Nullish",
87
+ validate: (value) => {
88
+ return isAllowedStringValue(value, allowedValues);
89
+ }
90
+ });
91
+ var optionalNullableEnumField = (allowedValues) => ({
92
+ required: false,
93
+ missingValuePolicy: "Nullish",
94
+ validate: (value) => {
95
+ return value === null || isAllowedStringValue(value, allowedValues);
96
+ }
97
+ });
98
+ var getValidationIssues = (fields, value, options) => {
99
+ const allowedFieldSet = new Set(Object.keys(fields));
100
+ const issues = [];
101
+ Object.entries(fields).forEach(([fieldName, field]) => {
102
+ const fieldValue = value[fieldName];
103
+ if (field.required && isMissingValue(fieldValue, field.missingValuePolicy)) {
104
+ issues.push({
105
+ fieldName,
106
+ type: "MISSING_FIELD"
107
+ });
108
+ return;
109
+ }
110
+ if (fieldValue === void 0) {
111
+ return;
112
+ }
113
+ if (!field.validate(fieldValue)) {
114
+ issues.push({
115
+ fieldName,
116
+ type: "INVALID_FIELD"
117
+ });
118
+ }
119
+ });
120
+ if (options.rejectUnknownFields === true) {
121
+ Object.keys(value).forEach((fieldName) => {
122
+ if (!allowedFieldSet.has(fieldName)) {
123
+ issues.push({
124
+ fieldName,
125
+ type: "UNSUPPORTED_FIELD"
126
+ });
127
+ }
128
+ });
129
+ }
130
+ return issues;
131
+ };
132
+ var isRecord = (value) => {
133
+ return typeof value === "object" && value !== null && !Array.isArray(value);
134
+ };
135
+ var isMissingValue = (value, missingValuePolicy) => {
136
+ if (value === void 0 || value === null) {
137
+ return true;
138
+ }
139
+ return missingValuePolicy === "BlankString" && typeof value === "string" && value.trim().length === 0;
140
+ };
141
+ var isNonEmptyString = (value) => {
142
+ return typeof value === "string" && value.trim().length > 0;
143
+ };
144
+ var isAllowedStringValue = (value, allowedValues) => {
145
+ return typeof value === "string" && allowedValues.includes(value);
146
+ };
147
+ var isEmail = (value) => {
148
+ if (!isNonEmptyString(value)) {
149
+ return false;
150
+ }
151
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
152
+ };
153
+ var isUuid = (value) => {
154
+ if (!isNonEmptyString(value)) {
155
+ return false;
156
+ }
157
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
158
+ value
159
+ );
160
+ };
161
+ export {
162
+ createValidationSchema,
163
+ optionalEnumField,
164
+ optionalNullableEnumField,
165
+ optionalNullableNonNegativeIntegerField,
166
+ optionalNullableStringField,
167
+ optionalRecordField,
168
+ optionalStringField,
169
+ requiredEmailField,
170
+ requiredEnumField,
171
+ requiredStringField,
172
+ requiredUuidField
173
+ };
174
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/types/validation/index.ts"],"sourcesContent":["export type TValidationIssueType =\n | \"INVALID_BODY\"\n | \"MISSING_FIELD\"\n | \"INVALID_FIELD\"\n | \"UNSUPPORTED_FIELD\";\n\nexport interface IValidationIssue<TFieldName extends string = string> {\n fieldName: TFieldName;\n type: TValidationIssueType;\n}\n\nexport interface IValidationResult<TValue extends object> {\n success: boolean;\n data?: TValue;\n issues: IValidationIssue[];\n}\n\nexport type TMissingValuePolicy = \"Nullish\" | \"BlankString\";\n\nexport interface IValidationField<TValue, TRequired extends boolean> {\n readonly valueType?: TValue;\n required: TRequired;\n missingValuePolicy: TMissingValuePolicy;\n validate: (value: unknown) => boolean;\n}\n\nexport type TValidationFieldMap = Record<\n string,\n IValidationField<unknown, boolean>\n>;\n\ntype TRequiredSchemaKeys<TFields extends TValidationFieldMap> = {\n [TKey in keyof TFields]: TFields[TKey] extends IValidationField<unknown, true>\n ? TKey\n : never;\n}[keyof TFields];\n\ntype TOptionalSchemaKeys<TFields extends TValidationFieldMap> = Exclude<\n keyof TFields,\n TRequiredSchemaKeys<TFields>\n>;\n\ntype TFieldValue<TField> =\n TField extends IValidationField<infer TValue, boolean> ? TValue : never;\n\nexport type TInferValidationFields<TFields extends TValidationFieldMap> = {\n [TKey in TRequiredSchemaKeys<TFields>]: TFieldValue<TFields[TKey]>;\n} & {\n [TKey in TOptionalSchemaKeys<TFields>]?: TFieldValue<TFields[TKey]>;\n};\n\nexport type TInferValidationSchemaInput<TSchema> =\n TSchema extends IRuntimeValidationSchema<infer TFields>\n ? TInferValidationFields<TFields>\n : never;\n\nexport interface IRuntimeValidationSchema<\n TFields extends TValidationFieldMap = TValidationFieldMap,\n> {\n fields: TFields;\n allowedFields: readonly Extract<keyof TFields, string>[];\n requiredFields: readonly Extract<TRequiredSchemaKeys<TFields>, string>[];\n validate: (\n value: unknown,\n options?: IRuntimeValidationOptions,\n ) => IValidationResult<TInferValidationFields<TFields>>;\n}\n\nexport interface IRuntimeValidationOptions {\n rejectUnknownFields?: boolean;\n}\n\nexport const createValidationSchema = <TFields extends TValidationFieldMap>(\n fields: TFields,\n): IRuntimeValidationSchema<TFields> => {\n const allowedFields = Object.keys(fields) as Extract<keyof TFields, string>[];\n const requiredFields = allowedFields.filter((fieldName) => {\n return fields[fieldName].required;\n }) as Extract<TRequiredSchemaKeys<TFields>, string>[];\n\n return {\n fields,\n allowedFields,\n requiredFields,\n validate: (value, options = {}) => {\n if (!isRecord(value)) {\n return {\n success: false,\n issues: [\n {\n fieldName: \"body\",\n type: \"INVALID_BODY\",\n },\n ],\n };\n }\n\n const issues = getValidationIssues(fields, value, options);\n\n if (issues.length > 0) {\n return {\n success: false,\n issues,\n };\n }\n\n return {\n success: true,\n data: value as TInferValidationFields<TFields>,\n issues: [],\n };\n },\n };\n};\n\nexport const requiredStringField = (\n missingValuePolicy: TMissingValuePolicy = \"Nullish\",\n): IValidationField<string, true> => ({\n required: true,\n missingValuePolicy,\n validate: isNonEmptyString,\n});\n\nexport const optionalStringField = (): IValidationField<string, false> => ({\n required: false,\n missingValuePolicy: \"Nullish\",\n validate: isNonEmptyString,\n});\n\nexport const optionalNullableStringField = (): IValidationField<\n string | null,\n false\n> => ({\n required: false,\n missingValuePolicy: \"Nullish\",\n validate: (value): value is string | null => {\n return value === null || isNonEmptyString(value);\n },\n});\n\nexport const requiredEmailField = (\n missingValuePolicy: TMissingValuePolicy = \"BlankString\",\n): IValidationField<string, true> => ({\n required: true,\n missingValuePolicy,\n validate: isEmail,\n});\n\nexport const requiredUuidField = (\n missingValuePolicy: TMissingValuePolicy = \"Nullish\",\n): IValidationField<string, true> => ({\n required: true,\n missingValuePolicy,\n validate: isUuid,\n});\n\nexport const optionalNullableNonNegativeIntegerField = (): IValidationField<\n number | null,\n false\n> => ({\n required: false,\n missingValuePolicy: \"Nullish\",\n validate: (value): value is number | null => {\n return value === null || (Number.isInteger(value) && Number(value) >= 0);\n },\n});\n\nexport const optionalRecordField = (): IValidationField<\n Record<string, unknown>,\n false\n> => ({\n required: false,\n missingValuePolicy: \"Nullish\",\n validate: isRecord,\n});\n\nexport const requiredEnumField = <TValue extends string>(\n allowedValues: readonly TValue[],\n missingValuePolicy: TMissingValuePolicy = \"Nullish\",\n): IValidationField<TValue, true> => ({\n required: true,\n missingValuePolicy,\n validate: (value): value is TValue => {\n return isAllowedStringValue(value, allowedValues);\n },\n});\n\nexport const optionalEnumField = <TValue extends string>(\n allowedValues: readonly TValue[],\n): IValidationField<TValue, false> => ({\n required: false,\n missingValuePolicy: \"Nullish\",\n validate: (value): value is TValue => {\n return isAllowedStringValue(value, allowedValues);\n },\n});\n\nexport const optionalNullableEnumField = <TValue extends string>(\n allowedValues: readonly TValue[],\n): IValidationField<TValue | null, false> => ({\n required: false,\n missingValuePolicy: \"Nullish\",\n validate: (value): value is TValue | null => {\n return value === null || isAllowedStringValue(value, allowedValues);\n },\n});\n\nconst getValidationIssues = <TFields extends TValidationFieldMap>(\n fields: TFields,\n value: Record<string, unknown>,\n options: IRuntimeValidationOptions,\n): IValidationIssue[] => {\n const allowedFieldSet = new Set(Object.keys(fields));\n const issues: IValidationIssue[] = [];\n\n Object.entries(fields).forEach(([fieldName, field]) => {\n const fieldValue = value[fieldName];\n\n if (\n field.required &&\n isMissingValue(fieldValue, field.missingValuePolicy)\n ) {\n issues.push({\n fieldName,\n type: \"MISSING_FIELD\",\n });\n return;\n }\n\n if (fieldValue === undefined) {\n return;\n }\n\n if (!field.validate(fieldValue)) {\n issues.push({\n fieldName,\n type: \"INVALID_FIELD\",\n });\n }\n });\n\n if (options.rejectUnknownFields === true) {\n Object.keys(value).forEach((fieldName) => {\n if (!allowedFieldSet.has(fieldName)) {\n issues.push({\n fieldName,\n type: \"UNSUPPORTED_FIELD\",\n });\n }\n });\n }\n\n return issues;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n};\n\nconst isMissingValue = (\n value: unknown,\n missingValuePolicy: TMissingValuePolicy,\n): boolean => {\n if (value === undefined || value === null) {\n return true;\n }\n\n return (\n missingValuePolicy === \"BlankString\" &&\n typeof value === \"string\" &&\n value.trim().length === 0\n );\n};\n\nconst isNonEmptyString = (value: unknown): value is string => {\n return typeof value === \"string\" && value.trim().length > 0;\n};\n\nconst isAllowedStringValue = <TValue extends string>(\n value: unknown,\n allowedValues: readonly TValue[],\n): value is TValue => {\n return typeof value === \"string\" && allowedValues.includes(value as TValue);\n};\n\nconst isEmail = (value: unknown): value is string => {\n if (!isNonEmptyString(value)) {\n return false;\n }\n\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value.trim());\n};\n\nconst isUuid = (value: unknown): value is string => {\n if (!isNonEmptyString(value)) {\n return false;\n }\n\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(\n value,\n );\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@turndown/library",
3
- "version": "0.1.64",
4
- "description": "Shared TypeScript library for the Turndown suite.",
3
+ "version": "0.1.65",
4
+ "description": "Shared TypeScript contracts and validation utilities for the Turndown suite.",
5
5
  "keywords": [
6
6
  "types",
7
7
  "typescript",
@@ -16,15 +16,33 @@
16
16
  "access": "public"
17
17
  },
18
18
  "type": "module",
19
- "main": "./dist/index.cjs",
20
- "module": "./dist/index.mjs",
21
- "types": "./dist/index.d.ts",
19
+ "main": "./dist/types/index.cjs",
20
+ "module": "./dist/types/index.mjs",
21
+ "types": "./dist/types/index.d.ts",
22
22
  "exports": {
23
23
  ".": {
24
- "types": "./dist/index.d.ts",
25
- "import": "./dist/index.mjs",
26
- "require": "./dist/index.cjs",
27
- "default": "./dist/index.mjs"
24
+ "types": "./dist/types/index.d.ts",
25
+ "import": "./dist/types/index.mjs",
26
+ "require": "./dist/types/index.cjs",
27
+ "default": "./dist/types/index.mjs"
28
+ },
29
+ "./types": {
30
+ "types": "./dist/types/index.d.ts",
31
+ "import": "./dist/types/index.mjs",
32
+ "require": "./dist/types/index.cjs",
33
+ "default": "./dist/types/index.mjs"
34
+ },
35
+ "./validation": {
36
+ "types": "./dist/types/validation/index.d.ts",
37
+ "import": "./dist/types/validation/index.mjs",
38
+ "require": "./dist/types/validation/index.cjs",
39
+ "default": "./dist/types/validation/index.mjs"
40
+ },
41
+ "./helpers": {
42
+ "types": "./dist/helpers/index.d.ts",
43
+ "import": "./dist/helpers/index.mjs",
44
+ "require": "./dist/helpers/index.cjs",
45
+ "default": "./dist/helpers/index.mjs"
28
46
  },
29
47
  "./package.json": "./package.json"
30
48
  },
@@ -43,7 +61,9 @@
43
61
  "test:coverage": "jest --coverage",
44
62
  "version:patch": "npm version patch",
45
63
  "version:minor": "npm version minor",
46
- "version:major": "npm version major"
64
+ "version:major": "npm version major",
65
+ "check": "tsc --noEmit",
66
+ "prepublishOnly": "npm test && npm run check && npm run build"
47
67
  },
48
68
  "devDependencies": {
49
69
  "@types/jest": "^30.0.0",