rankrunners-cms 0.0.32 → 0.0.34

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 (53) hide show
  1. package/package.json +4 -5
  2. package/src/api/client/form-fills.ts +79 -0
  3. package/src/api/client/index.ts +3 -0
  4. package/src/api/types/form-fills.ts +71 -0
  5. package/src/libs/valibot-serialize/converters/from_valibot.ts +61 -0
  6. package/src/libs/valibot-serialize/converters/index.ts +2 -0
  7. package/src/libs/valibot-serialize/converters/to_valibot.ts +36 -0
  8. package/src/libs/valibot-serialize/index.ts +2 -0
  9. package/src/libs/valibot-serialize/types/any.ts +36 -0
  10. package/src/libs/valibot-serialize/types/array.ts +115 -0
  11. package/src/libs/valibot-serialize/types/bigint.ts +40 -0
  12. package/src/libs/valibot-serialize/types/blob.ts +67 -0
  13. package/src/libs/valibot-serialize/types/boolean.ts +40 -0
  14. package/src/libs/valibot-serialize/types/date.ts +36 -0
  15. package/src/libs/valibot-serialize/types/enum.ts +67 -0
  16. package/src/libs/valibot-serialize/types/exact_optional.ts +58 -0
  17. package/src/libs/valibot-serialize/types/file.ts +86 -0
  18. package/src/libs/valibot-serialize/types/function.ts +32 -0
  19. package/src/libs/valibot-serialize/types/index.ts +39 -0
  20. package/src/libs/valibot-serialize/types/intersect.ts +45 -0
  21. package/src/libs/valibot-serialize/types/lazy.ts +51 -0
  22. package/src/libs/valibot-serialize/types/lib/default_utils.ts +7 -0
  23. package/src/libs/valibot-serialize/types/lib/type_interfaces.ts +41 -0
  24. package/src/libs/valibot-serialize/types/literal.ts +68 -0
  25. package/src/libs/valibot-serialize/types/map.ts +97 -0
  26. package/src/libs/valibot-serialize/types/nan.ts +29 -0
  27. package/src/libs/valibot-serialize/types/never.ts +35 -0
  28. package/src/libs/valibot-serialize/types/non_nullable.ts +41 -0
  29. package/src/libs/valibot-serialize/types/non_nullish.ts +41 -0
  30. package/src/libs/valibot-serialize/types/non_optional.ts +41 -0
  31. package/src/libs/valibot-serialize/types/null.ts +40 -0
  32. package/src/libs/valibot-serialize/types/nullable.ts +44 -0
  33. package/src/libs/valibot-serialize/types/nullish.ts +41 -0
  34. package/src/libs/valibot-serialize/types/number.ts +123 -0
  35. package/src/libs/valibot-serialize/types/object.ts +162 -0
  36. package/src/libs/valibot-serialize/types/optional.ts +44 -0
  37. package/src/libs/valibot-serialize/types/picklist.ts +49 -0
  38. package/src/libs/valibot-serialize/types/promise.ts +31 -0
  39. package/src/libs/valibot-serialize/types/record.ts +53 -0
  40. package/src/libs/valibot-serialize/types/set.ts +89 -0
  41. package/src/libs/valibot-serialize/types/string.ts +416 -0
  42. package/src/libs/valibot-serialize/types/symbol.ts +39 -0
  43. package/src/libs/valibot-serialize/types/tuple.ts +84 -0
  44. package/src/libs/valibot-serialize/types/undefined.ts +39 -0
  45. package/src/libs/valibot-serialize/types/undefinedable.ts +60 -0
  46. package/src/libs/valibot-serialize/types/union.ts +59 -0
  47. package/src/libs/valibot-serialize/types/unknown.ts +37 -0
  48. package/src/libs/valibot-serialize/types/variant.ts +56 -0
  49. package/src/libs/valibot-serialize/types/void.ts +39 -0
  50. package/src/libs/valibot-serialize/types.ts +102 -0
  51. package/src/libs/valibot-serialize/util/guard.ts +62 -0
  52. package/test/test.ts +18 -15
  53. package/bun.lock +0 -419
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "rankrunners-cms",
3
3
  "type": "module",
4
- "version": "0.0.32",
4
+ "version": "0.0.34",
5
5
  "peerDependencies": {
6
6
  "@puckeditor/core": "^0.21.1",
7
- "@tanstack/react-router": "^1.166.6",
8
- "@tanstack/router-core": "^1.166.6",
7
+ "@tanstack/react-router": "^1.166.7",
8
+ "@tanstack/router-core": "^1.166.7",
9
9
  "lucide-react": "^0.577.0",
10
10
  "next": "^16.1.6",
11
11
  "react": "^19.2.4",
@@ -35,7 +35,6 @@
35
35
  "typescript": "^5.9.3"
36
36
  },
37
37
  "dependencies": {
38
- "classnames": "^2.5.1",
39
- "valibot-serialize": "^1.5.0"
38
+ "classnames": "^2.5.1"
40
39
  }
41
40
  }
@@ -0,0 +1,79 @@
1
+ import {
2
+ safeParse,
3
+ type BaseIssue,
4
+ type BaseSchema,
5
+ type InferInput,
6
+ } from "valibot";
7
+ import { CMS_BASE_URL, SITE_ID } from "../constants";
8
+ import type { FormFillSubmissionDTO } from "../types/form-fills";
9
+ import { SendFormFillResult } from "../types/form-fills";
10
+
11
+ export type SendFormFillArgs<
12
+ A,
13
+ B,
14
+ C extends BaseIssue<unknown>,
15
+ T extends BaseSchema<A, B, C>,
16
+ > = {
17
+ type: string;
18
+ schema: T;
19
+ fieldNames: Record<string, string>;
20
+ data: InferInput<T>;
21
+ captchaToken: string;
22
+ test?: boolean;
23
+ };
24
+
25
+ export const sendFormFill = async <
26
+ A,
27
+ B,
28
+ C extends BaseIssue<unknown>,
29
+ T extends BaseSchema<A, B, C>,
30
+ >(
31
+ args: SendFormFillArgs<A, B, C, T>,
32
+ ): Promise<SendFormFillResult> => {
33
+ const formToSend = safeParse(args.schema, args.data);
34
+
35
+ if (!formToSend.success) {
36
+ console.error("Form fill validation failed:", formToSend.issues);
37
+ return SendFormFillResult.ValidationError;
38
+ }
39
+
40
+ const data: FormFillSubmissionDTO = {
41
+ siteId: SITE_ID,
42
+ type: args.type,
43
+ data: args.data as Record<string, string>,
44
+ schema: {
45
+ schema: JSON.stringify(args.schema),
46
+ fields: args.fieldNames,
47
+ },
48
+ captchaToken: args.captchaToken,
49
+ test: args.test,
50
+ };
51
+
52
+ try {
53
+ const response = await fetch(`${CMS_BASE_URL}/form-fills/public`, {
54
+ method: "POST",
55
+ headers: {
56
+ "Content-Type": "application/json",
57
+ },
58
+ body: JSON.stringify(data),
59
+ });
60
+
61
+ if (response.ok) {
62
+ return SendFormFillResult.Success;
63
+ }
64
+
65
+ console.error("Form fill submission failed with status:", response.status);
66
+ return SendFormFillResult.UnknownError;
67
+ } catch (error) {
68
+ console.error("Error sending form fill:", error);
69
+ if (error instanceof Error) {
70
+ if (error.message.includes("validation")) {
71
+ return SendFormFillResult.ValidationError;
72
+ }
73
+ if (error.message.includes("captcha")) {
74
+ return SendFormFillResult.CaptchaError;
75
+ }
76
+ }
77
+ return SendFormFillResult.UnknownError;
78
+ }
79
+ };
@@ -1,3 +1,6 @@
1
+ export * from "./captcha";
2
+ export * from "./form-fills";
3
+ export * from "./pages";
1
4
  export * from "./posts";
2
5
  export * from "./public";
3
6
  export * from "./sitemap";
@@ -0,0 +1,71 @@
1
+ import {
2
+ object,
3
+ record,
4
+ string,
5
+ pipe,
6
+ nonEmpty,
7
+ boolean,
8
+ optional,
9
+ type InferOutput,
10
+ type InferInput,
11
+ } from "valibot";
12
+ import { IdSchema } from "./common";
13
+
14
+ // Schema field definition - describes a single field in the form
15
+ export const FormFillSchemaDataSchema = object({
16
+ schema: string(), // Serialized validation rule (fromValibot / toValibot - rankrunners-cms/src/lib/valibot)
17
+ fields: record(
18
+ string(), // Field key
19
+ pipe(string(), nonEmpty("Field label is required")), // Field label
20
+ ),
21
+ });
22
+
23
+ export type FormFillSchemaDataDTO = InferOutput<
24
+ typeof FormFillSchemaDataSchema
25
+ >;
26
+
27
+ // Form Fill Data Structure - simple key-value record
28
+ export const FormFillDataSchema = record(
29
+ string(), // Field key
30
+ pipe(string(), nonEmpty("Field value is required")), // Field value
31
+ );
32
+
33
+ export type FormFillDataDTO = InferOutput<typeof FormFillDataSchema>;
34
+
35
+ // API Schemas
36
+
37
+ // 1. Submission (Public API)
38
+ export const FormFillSubmissionSchema = object({
39
+ siteId: IdSchema, // Site ID must be passed in the body for public submission
40
+ type: pipe(string(), nonEmpty("Form type is required")),
41
+ data: FormFillDataSchema,
42
+ schema: FormFillSchemaDataSchema,
43
+ captchaToken: pipe(string(), nonEmpty("Captcha token is required")),
44
+ test: optional(boolean()),
45
+ });
46
+
47
+ export type FormFillSubmissionDTO = InferInput<typeof FormFillSubmissionSchema>;
48
+
49
+ export enum SendFormFillResult {
50
+ Success = "success",
51
+ ValidationError = "validation_error",
52
+ CaptchaError = "captcha_error",
53
+ InvalidResponse = "invalid_response",
54
+ UnknownError = "unknown_error",
55
+ }
56
+
57
+ export const formFillResultToMessage = (result: SendFormFillResult): string => {
58
+ switch (result) {
59
+ case SendFormFillResult.Success:
60
+ return "Form submitted successfully!";
61
+ case SendFormFillResult.ValidationError:
62
+ return "There was a validation error with your submission. Please check your input and try again.";
63
+ case SendFormFillResult.CaptchaError:
64
+ return "Captcha verification failed. Please complete the captcha and try again.";
65
+ case SendFormFillResult.InvalidResponse:
66
+ return "An internal error occurred while submitting the form. Please call us instead!";
67
+ case SendFormFillResult.UnknownError:
68
+ default:
69
+ return "An unknown error occurred while submitting the form. Please try again later, or try calling us!";
70
+ }
71
+ };
@@ -0,0 +1,61 @@
1
+ import type { BaseIssue, BaseSchema } from "valibot";
2
+ import {
3
+ FORMAT_VERSION,
4
+ type SchemaNode,
5
+ type SerializedSchema,
6
+ } from "../types.ts";
7
+ import * as codecs from "../types/index.ts";
8
+
9
+ type AnySchema = BaseSchema<unknown, unknown, BaseIssue<unknown>>;
10
+
11
+ // Cache schema encodings to support recursive builders like v.lazy.
12
+ const encodeCache = new WeakMap<AnySchema, SchemaNode>();
13
+
14
+ /**
15
+ * Convert a Valibot schema into a portable JSON-friendly representation.
16
+ *
17
+ * The returned value contains a stable envelope plus an AST `node` that
18
+ * captures the schema structure and constraints without functions.
19
+ *
20
+ * @typeParam T - Any Valibot `BaseSchema`.
21
+ * @param schema - The Valibot schema to convert.
22
+ * @returns A `SerializedSchema` envelope with the encoded AST.
23
+ */
24
+ export function fromValibot<T extends AnySchema>(schema: T): SerializedSchema {
25
+ const node = encodeNode(schema);
26
+ return {
27
+ kind: "schema",
28
+ vendor: "valibot",
29
+ version: 1,
30
+ format: FORMAT_VERSION,
31
+ node,
32
+ };
33
+ }
34
+
35
+ function encodeNode(schema: AnySchema): SchemaNode {
36
+ const cached = encodeCache.get(schema);
37
+ if (cached) return cached;
38
+
39
+ const placeholder = {} as SchemaNode;
40
+ encodeCache.set(schema, placeholder);
41
+
42
+ // Read type directly to avoid JSON.stringify BigInt errors
43
+ const any = schema as unknown as { type?: string } & Record<string, unknown>;
44
+ const type = any.type;
45
+ // Dispatch to codecs that can detect from Valibot schema
46
+ try {
47
+ for (const c of Object.values(codecs)) {
48
+ if (c.matches(schema as never)) {
49
+ const node = c.encode(any as never, { encodeNode });
50
+ return Object.assign(placeholder, node);
51
+ }
52
+ }
53
+ } catch (error) {
54
+ encodeCache.delete(schema);
55
+ throw error;
56
+ }
57
+ encodeCache.delete(schema);
58
+ throw new Error(
59
+ `Unsupported schema type for serialization: ${String(type)}`,
60
+ );
61
+ }
@@ -0,0 +1,2 @@
1
+ export { fromValibot } from "./from_valibot.ts";
2
+ export { toValibot } from "./to_valibot.ts";
@@ -0,0 +1,36 @@
1
+ // import removed; decoders are delegated to codecs now
2
+ import type { BaseIssue, BaseSchema } from "valibot";
3
+ import type { SchemaNode, SerializedSchema } from "../types.ts";
4
+ import { isSerializedSchema } from "../util/guard.ts";
5
+ import * as codecs from "../types/index.ts";
6
+
7
+ type AnySchema = BaseSchema<unknown, unknown, BaseIssue<unknown>>;
8
+
9
+ /**
10
+ * Convert a serialized schema back to a Valibot schema.
11
+ *
12
+ * Validates the envelope shape and converts the contained AST into
13
+ * Valibot builder objects.
14
+ *
15
+ * @param data - A `SerializedSchema` envelope produced by {@link fromValibot}.
16
+ * @returns A Valibot schema equivalent to the original.
17
+ * @throws If the input is not a valid serialized schema.
18
+ */
19
+ export function toValibot(data: SerializedSchema): AnySchema {
20
+ if (!isSerializedSchema(data)) {
21
+ throw new Error("Invalid serialized schema format");
22
+ }
23
+ return decodeNode(data.node);
24
+ }
25
+
26
+ function decodeNode(node: SchemaNode): AnySchema {
27
+ for (const c of Object.values(codecs)) {
28
+ if (c.typeName === node.type) {
29
+ return c.decode(node as never, { decodeNode });
30
+ }
31
+ }
32
+
33
+ throw new Error(
34
+ `Unsupported node type: ${(node as { type: string }).type}`,
35
+ );
36
+ }
@@ -0,0 +1,2 @@
1
+ export { fromValibot } from "./converters/from_valibot.ts";
2
+ export { toValibot } from "./converters/to_valibot.ts";
@@ -0,0 +1,36 @@
1
+ import * as v from "valibot";
2
+ import type { BaseNode, IsSchemaNode } from "./lib/type_interfaces.ts";
3
+ import type {
4
+ AnySchema,
5
+ Decoder,
6
+ Encoder,
7
+ Matches,
8
+ } from "./lib/type_interfaces.ts";
9
+
10
+ export const typeName = "any" as const;
11
+
12
+ // Serialized node shape for "any"
13
+ export interface AnyNode extends BaseNode<typeof typeName> {}
14
+
15
+ export const isSchemaNode: IsSchemaNode<AnyNode> = (
16
+ node: unknown,
17
+ _ctx,
18
+ ): node is AnyNode => {
19
+ return Boolean(
20
+ node &&
21
+ typeof node === "object" &&
22
+ (node as { type?: unknown }).type === typeName,
23
+ );
24
+ };
25
+
26
+ export const matches: Matches = (any: AnySchema): boolean => {
27
+ return any?.type === typeName;
28
+ };
29
+
30
+ export const encode: Encoder<AnyNode> = function encodeAny(): AnyNode {
31
+ return { type: typeName };
32
+ };
33
+
34
+ export const decode: Decoder<AnyNode> = function decodeAny(): AnySchema {
35
+ return v.any();
36
+ };
@@ -0,0 +1,115 @@
1
+ import * as v from "valibot";
2
+ import type { AnyNode, BaseNode, IsSchemaNode } from "./lib/type_interfaces.ts";
3
+ import type {
4
+ AnySchema,
5
+ Decoder,
6
+ Encoder,
7
+ Matches,
8
+ } from "./lib/type_interfaces.ts";
9
+
10
+ export const typeName = "array" as const;
11
+
12
+ // Serialized node shape for "array"
13
+ export interface ArrayNode extends BaseNode<typeof typeName> {
14
+ item: AnyNode;
15
+ minLength?: number;
16
+ maxLength?: number;
17
+ length?: number;
18
+ }
19
+
20
+ export const isSchemaNode: IsSchemaNode<ArrayNode> = (
21
+ node: unknown,
22
+ ctx,
23
+ ): node is ArrayNode => {
24
+ if (!node || typeof node !== "object") return false;
25
+ if ((node as { type?: unknown }).type !== typeName) return false;
26
+ const n = node as Record<string, unknown>;
27
+ if (!ctx.isSchemaNode(n.item)) return false;
28
+ if (n.minLength !== undefined && typeof n.minLength !== "number") {
29
+ return false;
30
+ }
31
+ if (n.maxLength !== undefined && typeof n.maxLength !== "number") {
32
+ return false;
33
+ }
34
+ if (n.length !== undefined && typeof n.length !== "number") return false;
35
+ return true;
36
+ };
37
+
38
+ export const matches: Matches = (any: AnySchema): boolean => {
39
+ return any?.type === typeName;
40
+ };
41
+
42
+ export const encode: Encoder<ArrayNode> = function encodeArray(
43
+ any,
44
+ ctx,
45
+ ): ArrayNode {
46
+ const child = (any as { item?: unknown }).item as AnySchema | undefined;
47
+ if (!child) throw new Error("Unsupported array schema: missing item schema");
48
+ const node: ArrayNode = {
49
+ type: typeName,
50
+ item: ctx.encodeNode(child),
51
+ };
52
+ const pipe = (any as { pipe?: unknown[] }).pipe as
53
+ | Array<Record<string, unknown>>
54
+ | undefined;
55
+ if (Array.isArray(pipe)) {
56
+ for (const step of pipe) {
57
+ if (!step || typeof step !== "object") continue;
58
+ if (step.kind !== "validation") continue;
59
+ switch (step.type) {
60
+ case "min_length": {
61
+ const req = step.requirement as number | undefined;
62
+ if (typeof req === "number") node.minLength = req;
63
+ break;
64
+ }
65
+ case "max_length": {
66
+ const req = step.requirement as number | undefined;
67
+ if (typeof req === "number") node.maxLength = req;
68
+ break;
69
+ }
70
+ case "length": {
71
+ const req = step.requirement as number | undefined;
72
+ if (typeof req === "number") node.length = req;
73
+ break;
74
+ }
75
+ case "non_empty": {
76
+ node.minLength = Math.max(1, node.minLength ?? 0);
77
+ break;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return node;
83
+ };
84
+
85
+ export const decode: Decoder<ArrayNode> = function decodeArray(
86
+ node,
87
+ ctx,
88
+ ): AnySchema {
89
+ const base = v.array(ctx.decodeNode(node.item));
90
+ const validators: unknown[] = [];
91
+ if (node.minLength !== undefined) {
92
+ validators.push(v.minLength(node.minLength));
93
+ }
94
+ if (node.maxLength !== undefined) {
95
+ validators.push(v.maxLength(node.maxLength));
96
+ }
97
+ if (node.length !== undefined) validators.push(v.length(node.length));
98
+ switch (validators.length) {
99
+ case 0:
100
+ return base;
101
+ case 1:
102
+ return v.pipe(base, validators[0] as never);
103
+ case 2:
104
+ return v.pipe(base, validators[0] as never, validators[1] as never);
105
+ case 3:
106
+ return v.pipe(
107
+ base,
108
+ validators[0] as never,
109
+ validators[1] as never,
110
+ validators[2] as never,
111
+ );
112
+ default:
113
+ return v.pipe(base, ...(validators as never[]));
114
+ }
115
+ };
@@ -0,0 +1,40 @@
1
+ import * as v from "valibot";
2
+ import type { BaseNode, IsSchemaNode } from "./lib/type_interfaces.ts";
3
+ import type {
4
+ AnySchema,
5
+ Decoder,
6
+ Encoder,
7
+ Matches,
8
+ } from "./lib/type_interfaces.ts";
9
+
10
+ export const typeName = "bigint" as const;
11
+
12
+ // Serialized node shape for "bigint"
13
+ export interface BigIntNode extends BaseNode<typeof typeName> {}
14
+
15
+ export const isSchemaNode: IsSchemaNode<BigIntNode> = (
16
+ node: unknown,
17
+ _ctx,
18
+ ): node is BigIntNode => {
19
+ return Boolean(
20
+ node &&
21
+ typeof node === "object" &&
22
+ (node as { type?: unknown }).type === typeName,
23
+ );
24
+ };
25
+
26
+ export const matches: Matches = (any: AnySchema): boolean => {
27
+ return any?.type === typeName;
28
+ };
29
+
30
+ export const encode: Encoder<BigIntNode> = function encodeBigInt(
31
+ _any,
32
+ ): BigIntNode {
33
+ return { type: typeName };
34
+ };
35
+
36
+ export const decode: Decoder<BigIntNode> = function decodeBigInt(
37
+ _node,
38
+ ): AnySchema {
39
+ return v.bigint();
40
+ };
@@ -0,0 +1,67 @@
1
+ import * as v from "valibot";
2
+ import type { BaseNode } from "./lib/type_interfaces.ts";
3
+ import type {
4
+ AnySchema,
5
+ Decoder,
6
+ Encoder,
7
+ Matches,
8
+ } from "./lib/type_interfaces.ts";
9
+
10
+ export const typeName = "blob" as const;
11
+
12
+ // Serialized node shape for "blob"
13
+ export interface BlobNode extends BaseNode<typeof typeName> {
14
+ minSize?: number;
15
+ maxSize?: number;
16
+ mimeTypes?: string[];
17
+ }
18
+
19
+ export const matches: Matches = (any: AnySchema): boolean => {
20
+ return any?.type === typeName;
21
+ };
22
+
23
+ export const encode: Encoder<BlobNode> = function encodeBlob(any): BlobNode {
24
+ const node: BlobNode = { type: typeName };
25
+ const pipe = (any as { pipe?: unknown[] }).pipe as
26
+ | Array<Record<string, unknown>>
27
+ | undefined;
28
+ if (Array.isArray(pipe)) {
29
+ for (const step of pipe) {
30
+ if (!step || typeof step !== "object") continue;
31
+ if (step.kind !== "validation") continue;
32
+ switch (step.type) {
33
+ case "min_size": {
34
+ const req = step.requirement as number | undefined;
35
+ if (typeof req === "number") node.minSize = req;
36
+ break;
37
+ }
38
+ case "max_size": {
39
+ const req = step.requirement as number | undefined;
40
+ if (typeof req === "number") node.maxSize = req;
41
+ break;
42
+ }
43
+ case "mime_type": {
44
+ const req = step.requirement as string[] | undefined;
45
+ if (Array.isArray(req)) node.mimeTypes = req;
46
+ break;
47
+ }
48
+ }
49
+ }
50
+ }
51
+ return node;
52
+ };
53
+
54
+ export const decode: Decoder<BlobNode> = function decodeBlob(node): AnySchema {
55
+ let b = v.blob();
56
+ const items: unknown[] = [];
57
+ if (node.minSize !== undefined) items.push(v.minSize(node.minSize));
58
+ if (node.maxSize !== undefined) items.push(v.maxSize(node.maxSize));
59
+ if (node.mimeTypes && node.mimeTypes.length > 0) {
60
+ const mimeType = (
61
+ v as unknown as { mimeType: (req: string[] | string) => unknown }
62
+ ).mimeType;
63
+ items.push(mimeType(node.mimeTypes));
64
+ }
65
+ if (items.length > 0) b = v.pipe(b, ...(items as never[]));
66
+ return b;
67
+ };
@@ -0,0 +1,40 @@
1
+ import * as v from "valibot";
2
+ import type { BaseNode, IsSchemaNode } from "./lib/type_interfaces.ts";
3
+ import type {
4
+ AnySchema,
5
+ Decoder,
6
+ Encoder,
7
+ Matches,
8
+ } from "./lib/type_interfaces.ts";
9
+
10
+ export const typeName = "boolean" as const;
11
+
12
+ // Serialized node shape for "boolean"
13
+ export interface BooleanNode extends BaseNode<typeof typeName> {}
14
+
15
+ export const isSchemaNode: IsSchemaNode<BooleanNode> = (
16
+ node: unknown,
17
+ _ctx,
18
+ ): node is BooleanNode => {
19
+ return Boolean(
20
+ node &&
21
+ typeof node === "object" &&
22
+ (node as { type?: unknown }).type === typeName,
23
+ );
24
+ };
25
+
26
+ export const matches: Matches = (any: AnySchema): boolean => {
27
+ return any?.type === typeName;
28
+ };
29
+
30
+ export const encode: Encoder<BooleanNode> = function encodeBoolean(
31
+ _any,
32
+ ): BooleanNode {
33
+ return { type: typeName };
34
+ };
35
+
36
+ export const decode: Decoder<BooleanNode> = function decodeBoolean(
37
+ _node,
38
+ ): AnySchema {
39
+ return v.boolean();
40
+ };
@@ -0,0 +1,36 @@
1
+ import * as v from "valibot";
2
+ import type { BaseNode, IsSchemaNode } from "./lib/type_interfaces.ts";
3
+ import type {
4
+ AnySchema,
5
+ Decoder,
6
+ Encoder,
7
+ Matches,
8
+ } from "./lib/type_interfaces.ts";
9
+
10
+ export const typeName = "date" as const;
11
+
12
+ // Serialized node shape for "date"
13
+ export interface DateNode extends BaseNode<typeof typeName> {}
14
+
15
+ export const isSchemaNode: IsSchemaNode<DateNode> = (
16
+ node: unknown,
17
+ _ctx,
18
+ ): node is DateNode => {
19
+ return Boolean(
20
+ node &&
21
+ typeof node === "object" &&
22
+ (node as { type?: unknown }).type === typeName,
23
+ );
24
+ };
25
+
26
+ export const matches: Matches = (any: AnySchema): boolean => {
27
+ return any?.type === typeName;
28
+ };
29
+
30
+ export const encode: Encoder<DateNode> = function encodeDate(_any): DateNode {
31
+ return { type: typeName };
32
+ };
33
+
34
+ export const decode: Decoder<DateNode> = function decodeDate(_node): AnySchema {
35
+ return v.date();
36
+ };
@@ -0,0 +1,67 @@
1
+ import * as v from "valibot";
2
+ import type { BaseNode, IsSchemaNode } from "./lib/type_interfaces.ts";
3
+ import type {
4
+ AnySchema,
5
+ Decoder,
6
+ Encoder,
7
+ Matches,
8
+ } from "./lib/type_interfaces.ts";
9
+
10
+ export const typeName = "enum" as const;
11
+
12
+ // Serialized node shape for "enum"
13
+ export interface EnumNode extends BaseNode<typeof typeName> {
14
+ values: Array<string | number | boolean | null>;
15
+ }
16
+
17
+ export const isSchemaNode: IsSchemaNode<EnumNode> = (
18
+ node: unknown,
19
+ _ctx,
20
+ ): node is EnumNode => {
21
+ if (!node || typeof node !== "object") return false;
22
+ if ((node as { type?: unknown }).type !== typeName) return false;
23
+ const values = (node as { values?: unknown }).values as unknown;
24
+ if (!Array.isArray(values)) return false;
25
+ for (const v of values) {
26
+ const t = typeof v;
27
+ if (!(t === "string" || t === "number" || t === "boolean") && v !== null) {
28
+ return false;
29
+ }
30
+ }
31
+ return true;
32
+ };
33
+
34
+ export const matches: Matches = (any: AnySchema): boolean => {
35
+ const type = (any as { type?: string }).type;
36
+ return type === "enum";
37
+ };
38
+
39
+ export const encode: Encoder<EnumNode> = function encodeEnum(any): EnumNode {
40
+ const values = ((any as { options?: unknown[] }).options ??
41
+ (any as { enum?: unknown[] }).enum) as unknown[] | undefined;
42
+ if (!Array.isArray(values)) {
43
+ throw new Error("Unsupported enum schema: missing options");
44
+ }
45
+ const out: Array<string | number | boolean | null> = [];
46
+ for (const val of values) {
47
+ const t = typeof val;
48
+ if (t === "string" || t === "number" || t === "boolean" || val === null) {
49
+ out.push(val as never);
50
+ } else throw new Error("Unsupported enum value type");
51
+ }
52
+ return { type: typeName, values: out };
53
+ };
54
+
55
+ export const decode: Decoder<EnumNode> = function decodeEnum(node): AnySchema {
56
+ const allStrings = node.values.every((val) => typeof val === "string");
57
+ if (allStrings) {
58
+ const mapping = Object.fromEntries(
59
+ (node.values as string[]).map((s) => [s, s] as const),
60
+ );
61
+ // deno-lint-ignore no-explicit-any
62
+ return (v as any).enum(mapping);
63
+ }
64
+ // Fallback: union of literals
65
+ const literals = node.values.map((val) => v.literal(val as never));
66
+ return v.union(literals as never);
67
+ };