@kamod-ch/otok-validation 1.0.0
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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/adapters/arktype.d.ts +5 -0
- package/dist/adapters/arktype.d.ts.map +1 -0
- package/dist/adapters/arktype.js +27 -0
- package/dist/adapters/arktype.js.map +1 -0
- package/dist/adapters/valibot.d.ts +8 -0
- package/dist/adapters/valibot.d.ts.map +1 -0
- package/dist/adapters/valibot.js +5 -0
- package/dist/adapters/valibot.js.map +1 -0
- package/dist/adapters/zod.d.ts +6 -0
- package/dist/adapters/zod.d.ts.map +1 -0
- package/dist/adapters/zod.js +5 -0
- package/dist/adapters/zod.js.map +1 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +15 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +22 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +38 -0
- package/dist/loader.js.map +1 -0
- package/dist/parse/form-data-to-record.d.ts +6 -0
- package/dist/parse/form-data-to-record.d.ts.map +1 -0
- package/dist/parse/form-data-to-record.js +33 -0
- package/dist/parse/form-data-to-record.js.map +1 -0
- package/dist/parse/form-data.d.ts +5 -0
- package/dist/parse/form-data.d.ts.map +1 -0
- package/dist/parse/form-data.js +19 -0
- package/dist/parse/form-data.js.map +1 -0
- package/dist/parse/json.d.ts +5 -0
- package/dist/parse/json.d.ts.map +1 -0
- package/dist/parse/json.js +33 -0
- package/dist/parse/json.js.map +1 -0
- package/dist/parse/params.d.ts +5 -0
- package/dist/parse/params.d.ts.map +1 -0
- package/dist/parse/params.js +13 -0
- package/dist/parse/params.js.map +1 -0
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +18 -0
- package/dist/plugin.js.map +1 -0
- package/dist/standard.d.ts +15 -0
- package/dist/standard.d.ts.map +1 -0
- package/dist/standard.js +70 -0
- package/dist/standard.js.map +1 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/values.d.ts +4 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +27 -0
- package/dist/values.js.map +1 -0
- package/package.json +95 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kamod
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @kamod-ch/otok-validation
|
|
2
|
+
|
|
3
|
+
Standard Schema validation for [Otok](https://github.com/kamod-ch/otok) — typed inputs, field/form errors, and `defineAction` integration. Works with Zod, Valibot, and ArkType without forcing a schema library in Otok core.
|
|
4
|
+
|
|
5
|
+
## defineAction with schema
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { defineAction } from "@kamod-ch/otok-validation/loader";
|
|
9
|
+
import { contactSchema } from "./schemas/contact";
|
|
10
|
+
|
|
11
|
+
export const action = defineAction({
|
|
12
|
+
schema: contactSchema,
|
|
13
|
+
handler: async ({ input, db }) => {
|
|
14
|
+
return db.insertInto("contacts").values(input).executeTakeFirstOrThrow();
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Input is automatically parsed from `FormData` (HTML forms) or JSON (`Content-Type: application/json`).
|
|
20
|
+
|
|
21
|
+
## Manual parsing
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { parseFormData, parseJson, parseParams } from "@kamod-ch/otok-validation";
|
|
25
|
+
|
|
26
|
+
const input = await parseFormData(formData, contactSchema);
|
|
27
|
+
const body = await parseJson(request, contactSchema);
|
|
28
|
+
const { id } = await parseParams(params, z.object({ id: z.coerce.number() }));
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Error format
|
|
32
|
+
|
|
33
|
+
All parsers throw Otok `validationError()` with a consistent shape:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
{
|
|
37
|
+
status: 400,
|
|
38
|
+
message: "Validation failed",
|
|
39
|
+
fieldErrors: { email: ["Invalid email address"] },
|
|
40
|
+
formErrors?: ["Root-level error"],
|
|
41
|
+
values?: { email: "bad", name: "Ada" } // safe redisplay values
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use `actionData.fieldErrors` and `actionData.values` in your page component for progressive enhancement.
|
|
46
|
+
|
|
47
|
+
## Schema libraries
|
|
48
|
+
|
|
49
|
+
All libraries implementing [Standard Schema V1](https://github.com/standard-schema/standard-schema) work natively:
|
|
50
|
+
|
|
51
|
+
| Library | Import |
|
|
52
|
+
| ------- | -------------------------------------------- |
|
|
53
|
+
| Zod 4+ | Pass schema directly |
|
|
54
|
+
| Valibot | `@kamod-ch/otok-validation/adapters/valibot` |
|
|
55
|
+
| ArkType | `@kamod-ch/otok-validation/adapters/arktype` |
|
|
56
|
+
|
|
57
|
+
Legacy Zod 3 `safeParse` schemas are also supported.
|
|
58
|
+
|
|
59
|
+
## Client-side validation
|
|
60
|
+
|
|
61
|
+
Same contract on server and client — no schema library in Otok core:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { safeValidate } from "@kamod-ch/otok-validation/client";
|
|
65
|
+
|
|
66
|
+
const result = await safeValidate(contactSchema, formValues);
|
|
67
|
+
if (!result.success) {
|
|
68
|
+
showErrors(result.fieldErrors);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Unknown fields
|
|
73
|
+
|
|
74
|
+
Pass `stripUnknown: true` in parse options to remove fields not defined in your schema before validation.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ValidationSchema } from "../types.js";
|
|
2
|
+
/** Wrap an ArkType type for use with otok-validation parsers. */
|
|
3
|
+
export declare function fromArkType<T>(schema: (input: unknown) => unknown): ValidationSchema<T>;
|
|
4
|
+
export type { ValidationSchema };
|
|
5
|
+
//# sourceMappingURL=arktype.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arktype.d.ts","sourceRoot":"","sources":["../../src/adapters/arktype.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAuBvF;AAED,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { issuesToValidationInput } from "../standard.js";
|
|
2
|
+
/** Wrap an ArkType type for use with otok-validation parsers. */
|
|
3
|
+
export function fromArkType(schema) {
|
|
4
|
+
return {
|
|
5
|
+
safeParse(input) {
|
|
6
|
+
const result = schema(input);
|
|
7
|
+
if (result && typeof result === "object" && "summary" in result) {
|
|
8
|
+
const issues = [{ message: String(result.summary), path: [] }];
|
|
9
|
+
return {
|
|
10
|
+
success: false,
|
|
11
|
+
error: {
|
|
12
|
+
issues,
|
|
13
|
+
flatten: () => {
|
|
14
|
+
const mapped = issuesToValidationInput(issues);
|
|
15
|
+
return {
|
|
16
|
+
fieldErrors: mapped.fieldErrors ?? {},
|
|
17
|
+
formErrors: mapped.formErrors ?? [],
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return { success: true, data: result };
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=arktype.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arktype.js","sourceRoot":"","sources":["../../src/adapters/arktype.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAI,MAAmC;IAChE,OAAO;QACL,SAAS,CAAC,KAAc;YACtB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;gBAChE,MAAM,MAAM,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,CAAE,MAA8B,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,EAAmB,EAAE,CAAC,CAAC;gBACzG,OAAO;oBACL,OAAO,EAAE,KAAc;oBACvB,KAAK,EAAE;wBACL,MAAM;wBACN,OAAO,EAAE,GAAG,EAAE;4BACZ,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;4BAC/C,OAAO;gCACL,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;gCACrC,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,EAAE;6BACpC,CAAC;wBACJ,CAAC;qBACF;iBACF,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,IAAa,EAAE,IAAI,EAAE,MAAW,EAAE,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { StandardSchemaV1, ValidationSchema } from "../types.js";
|
|
2
|
+
/** Wrap a Valibot schema as Standard Schema compatible ValidationSchema. */
|
|
3
|
+
export declare function fromValibot<T>(schema: {
|
|
4
|
+
readonly "~standard": StandardSchemaV1<unknown, T>["~standard"];
|
|
5
|
+
}): ValidationSchema<T>;
|
|
6
|
+
/** Valibot v1+ implements Standard Schema natively. */
|
|
7
|
+
export type { ValidationSchema };
|
|
8
|
+
//# sourceMappingURL=valibot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valibot.d.ts","sourceRoot":"","sources":["../../src/adapters/valibot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtE,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE;IACrC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;CACjE,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAEtB;AAED,uDAAuD;AACvD,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valibot.js","sourceRoot":"","sources":["../../src/adapters/valibot.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAI,MAE9B;IACC,OAAO,MAA6B,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ValidationSchema } from "../types.js";
|
|
2
|
+
/** Zod 4 implements Standard Schema — pass schemas directly. Re-export for explicit imports. */
|
|
3
|
+
export type { ValidationSchema };
|
|
4
|
+
/** Wrap a Zod schema for explicit adapter usage (identity for Zod 4+). */
|
|
5
|
+
export declare function fromZod<T>(schema: ValidationSchema<T>): ValidationSchema<T>;
|
|
6
|
+
//# sourceMappingURL=zod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod.d.ts","sourceRoot":"","sources":["../../src/adapters/zod.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,gGAAgG;AAChG,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAE3E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod.js","sourceRoot":"","sources":["../../src/adapters/zod.ts"],"names":[],"mappings":"AAKA,0EAA0E;AAC1E,MAAM,UAAU,OAAO,CAAI,MAA2B;IACpD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ValidationSchema } from "./types.js";
|
|
2
|
+
/** Client-side validation — same contract as server, throws nothing. */
|
|
3
|
+
export declare function safeValidate<TOutput>(schema: ValidationSchema<TOutput>, input: unknown): Promise<{
|
|
4
|
+
success: true;
|
|
5
|
+
data: TOutput;
|
|
6
|
+
} | {
|
|
7
|
+
success: false;
|
|
8
|
+
fieldErrors?: Record<string, string[]>;
|
|
9
|
+
formErrors?: string[];
|
|
10
|
+
}>;
|
|
11
|
+
export { validateSchema, issuesToValidationInput } from "./standard.js";
|
|
12
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,wEAAwE;AACxE,wBAAsB,YAAY,CAAC,OAAO,EACxC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,EACjC,KAAK,EAAE,OAAO,GACb,OAAO,CACR;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CACrH,CAUA;AAED,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { validateSchema } from "./standard.js";
|
|
2
|
+
/** Client-side validation — same contract as server, throws nothing. */
|
|
3
|
+
export async function safeValidate(schema, input) {
|
|
4
|
+
const result = await validateSchema(schema, input);
|
|
5
|
+
if (result.success) {
|
|
6
|
+
return { success: true, data: result.data };
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
success: false,
|
|
10
|
+
fieldErrors: result.error.fieldErrors,
|
|
11
|
+
formErrors: result.error.formErrors,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export { validateSchema, issuesToValidationInput } from "./standard.js";
|
|
15
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAiC,EACjC,KAAc;IAId,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO;QACL,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,WAAmD;QAC7E,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU;KACpC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { defineAction, defineLoader } from "./loader.js";
|
|
2
|
+
export type { ActionDefinition, InferOutput } from "./loader.js";
|
|
3
|
+
export { parseFormData, formDataToRecord } from "./parse/form-data.js";
|
|
4
|
+
export { parseJson, parseJsonValue } from "./parse/json.js";
|
|
5
|
+
export { parseParams } from "./parse/params.js";
|
|
6
|
+
export { safeValidate, validateSchema, issuesToValidationInput } from "./client.js";
|
|
7
|
+
export type { ValidationSchema, StandardSchemaV1, StandardSchemaIssue, ParseOptions, FormParseOptions, JsonValue, ValidationErrorInput, } from "./types.js";
|
|
8
|
+
export { default } from "./plugin.js";
|
|
9
|
+
export type { ValidationPluginOptions } from "./plugin.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACpF,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { defineAction, defineLoader } from "./loader.js";
|
|
2
|
+
export { parseFormData, formDataToRecord } from "./parse/form-data.js";
|
|
3
|
+
export { parseJson, parseJsonValue } from "./parse/json.js";
|
|
4
|
+
export { parseParams } from "./parse/params.js";
|
|
5
|
+
export { safeValidate, validateSchema, issuesToValidationInput } from "./client.js";
|
|
6
|
+
export { default } from "./plugin.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAUpF,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ActionResult, LoaderResult, OtokActionContext, OtokContext, OtokLoader } from "@kamod-ch/otok/server";
|
|
2
|
+
import type { FormParseOptions, ValidationSchema } from "./types.js";
|
|
3
|
+
type InferOutput<T> = T extends ValidationSchema<infer O> ? O : never;
|
|
4
|
+
type DbContext<DB> = {
|
|
5
|
+
db?: DB;
|
|
6
|
+
};
|
|
7
|
+
type ActionInputContext<TSchema extends ValidationSchema> = {
|
|
8
|
+
input: InferOutput<TSchema>;
|
|
9
|
+
};
|
|
10
|
+
type ActionHandler<TSchema extends ValidationSchema, DB, Result> = (ctx: OtokActionContext & ActionInputContext<TSchema> & DbContext<DB>) => Result | Promise<Result>;
|
|
11
|
+
type ActionDefinition<TSchema extends ValidationSchema, DB, Result> = {
|
|
12
|
+
schema: TSchema;
|
|
13
|
+
handler: ActionHandler<TSchema, DB, Result>;
|
|
14
|
+
parse?: FormParseOptions;
|
|
15
|
+
/** Hono context key for db. Default: `db`. */
|
|
16
|
+
dbContextKey?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function defineAction<TSchema extends ValidationSchema, DB = unknown, Result extends ActionResult = ActionResult>(definition: ActionDefinition<TSchema, DB, Result>): (context: OtokActionContext) => Promise<Result> | Result;
|
|
19
|
+
export declare function defineAction<DB = unknown, Result extends ActionResult = ActionResult>(handler: (ctx: OtokActionContext & DbContext<DB>) => Result | Promise<Result>): (context: OtokActionContext) => Result | Promise<Result>;
|
|
20
|
+
export declare function defineLoader<Data extends LoaderResult, DB = unknown>(handler: (ctx: OtokContext & DbContext<DB>) => Data | Promise<Data>, dbContextKey?: string): OtokLoader<Data>;
|
|
21
|
+
export type { InferOutput, ActionDefinition, FormParseOptions };
|
|
22
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACpH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAIrE,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,gBAAgB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEtE,KAAK,SAAS,CAAC,EAAE,IAAI;IAAE,EAAE,CAAC,EAAE,EAAE,CAAA;CAAE,CAAC;AAEjC,KAAK,kBAAkB,CAAC,OAAO,SAAS,gBAAgB,IAAI;IAC1D,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CAC7B,CAAC;AAsBF,KAAK,aAAa,CAAC,OAAO,SAAS,gBAAgB,EAAE,EAAE,EAAE,MAAM,IAAI,CACjE,GAAG,EAAE,iBAAiB,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC,KACjE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAE9B,KAAK,gBAAgB,CAAC,OAAO,SAAS,gBAAgB,EAAE,EAAE,EAAE,MAAM,IAAI;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAgB,YAAY,CAC1B,OAAO,SAAS,gBAAgB,EAChC,EAAE,GAAG,OAAO,EACZ,MAAM,SAAS,YAAY,GAAG,YAAY,EAC1C,UAAU,EAAE,gBAAgB,CAAC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;AAE/G,wBAAgB,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,SAAS,YAAY,GAAG,YAAY,EACnF,OAAO,EAAE,CAAC,GAAG,EAAE,iBAAiB,GAAG,SAAS,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAC5E,CAAC,OAAO,EAAE,iBAAiB,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AA+B5D,wBAAgB,YAAY,CAAC,IAAI,SAAS,YAAY,EAAE,EAAE,GAAG,OAAO,EAClE,OAAO,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,SAAS,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACnE,YAAY,SAAO,GAClB,UAAU,CAAC,IAAI,CAAC,CAMlB;AAED,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { parseFormData } from "./parse/form-data.js";
|
|
2
|
+
import { parseJson } from "./parse/json.js";
|
|
3
|
+
/** Resolve db from Hono context — set by @kamod-ch/otok-kysely middleware. */
|
|
4
|
+
function resolveDb(hono, contextKey = "db") {
|
|
5
|
+
const db = hono.get(contextKey);
|
|
6
|
+
return db;
|
|
7
|
+
}
|
|
8
|
+
async function parseActionInput(context, schema, options) {
|
|
9
|
+
const contentType = context.request.headers.get("content-type") ?? "";
|
|
10
|
+
if (contentType.includes("application/json")) {
|
|
11
|
+
return parseJson(context.request, schema, options);
|
|
12
|
+
}
|
|
13
|
+
return parseFormData(context.formData, schema, options);
|
|
14
|
+
}
|
|
15
|
+
export function defineAction(definitionOrHandler) {
|
|
16
|
+
if (typeof definitionOrHandler === "function") {
|
|
17
|
+
return (context) => definitionOrHandler({
|
|
18
|
+
...context,
|
|
19
|
+
db: resolveDb(context.hono),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const { schema, handler, parse, dbContextKey } = definitionOrHandler;
|
|
23
|
+
return async (context) => {
|
|
24
|
+
const input = await parseActionInput(context, schema, parse);
|
|
25
|
+
return handler({
|
|
26
|
+
...context,
|
|
27
|
+
input,
|
|
28
|
+
db: resolveDb(context.hono, dbContextKey),
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function defineLoader(handler, dbContextKey = "db") {
|
|
33
|
+
return (context) => handler({
|
|
34
|
+
...context,
|
|
35
|
+
db: resolveDb(context.hono, dbContextKey),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAU5C,8EAA8E;AAC9E,SAAS,SAAS,CAAK,IAAyB,EAAE,UAAU,GAAG,IAAI;IACjE,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,UAAmB,CAAC,CAAC;IACzC,OAAO,EAAoB,CAAC;AAC9B,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,OAA0B,EAC1B,MAAe,EACf,OAA0B;IAE1B,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAEtE,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC7C,OAAO,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAkC,CAAC;IACtF,CAAC;IAED,OAAO,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAkC,CAAC;AAC3F,CAAC;AAwBD,MAAM,UAAU,YAAY,CAK1B,mBAE0E;IAE1E,IAAI,OAAO,mBAAmB,KAAK,UAAU,EAAE,CAAC;QAC9C,OAAO,CAAC,OAAO,EAAE,EAAE,CACjB,mBAAmB,CAAC;YAClB,GAAG,OAAO;YACV,EAAE,EAAE,SAAS,CAAK,OAAO,CAAC,IAAI,CAAC;SAChC,CAAC,CAAC;IACP,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,mBAAmB,CAAC;IAErE,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAC7D,OAAO,OAAO,CAAC;YACb,GAAG,OAAO;YACV,KAAK;YACL,EAAE,EAAE,SAAS,CAAK,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC;SAC9C,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,OAAmE,EACnE,YAAY,GAAG,IAAI;IAEnB,OAAO,CAAC,OAAO,EAAE,EAAE,CACjB,OAAO,CAAC;QACN,GAAG,OAAO;QACV,EAAE,EAAE,SAAS,CAAK,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC;KAC9C,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type FormDataToRecordOptions = {
|
|
2
|
+
arrays?: string[];
|
|
3
|
+
checkboxes?: string[];
|
|
4
|
+
};
|
|
5
|
+
export declare function formDataToRecord(formData: FormData | undefined, options?: FormDataToRecordOptions): Record<string, unknown>;
|
|
6
|
+
//# sourceMappingURL=form-data-to-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-data-to-record.d.ts","sourceRoot":"","sources":["../../src/parse/form-data-to-record.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,OAAO,GAAE,uBAA4B,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoCzB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function formDataToRecord(formData, options = {}) {
|
|
2
|
+
if (!formData)
|
|
3
|
+
return {};
|
|
4
|
+
const arrays = new Set(options.arrays ?? []);
|
|
5
|
+
const checkboxes = new Set(options.checkboxes ?? []);
|
|
6
|
+
const record = {};
|
|
7
|
+
for (const key of checkboxes) {
|
|
8
|
+
record[key] = false;
|
|
9
|
+
}
|
|
10
|
+
for (const [key, value] of formData.entries()) {
|
|
11
|
+
if (value instanceof File) {
|
|
12
|
+
record[key] = value.size > 0 ? value : "";
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (arrays.has(key)) {
|
|
16
|
+
const existing = record[key];
|
|
17
|
+
if (Array.isArray(existing)) {
|
|
18
|
+
existing.push(value);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
record[key] = [value];
|
|
22
|
+
}
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (checkboxes.has(key)) {
|
|
26
|
+
record[key] = value === "on" || value === "true" || value === "1";
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
record[key] = value;
|
|
30
|
+
}
|
|
31
|
+
return record;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=form-data-to-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-data-to-record.js","sourceRoot":"","sources":["../../src/parse/form-data-to-record.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,gBAAgB,CAC9B,QAA8B,EAC9B,UAAmC,EAAE;IAErC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IAEzB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAE3C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,SAAS;QACX,CAAC;QAED,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,GAAG,CAAC;YAClE,SAAS;QACX,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FormParseOptions, ValidationSchema } from "../types.js";
|
|
2
|
+
import { formDataToRecord } from "./form-data-to-record.js";
|
|
3
|
+
export declare function parseFormData<TOutput>(formData: FormData | undefined, schema: ValidationSchema<TOutput>, options?: FormParseOptions): Promise<TOutput>;
|
|
4
|
+
export { formDataToRecord, type FormParseOptions as FormDataToRecordOptions };
|
|
5
|
+
//# sourceMappingURL=form-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-data.d.ts","sourceRoot":"","sources":["../../src/parse/form-data.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,wBAAsB,aAAa,CAAC,OAAO,EACzC,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,EACjC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAiBlB;AAED,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,IAAI,uBAAuB,EAAE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { validationError } from "@kamod-ch/otok/shared";
|
|
2
|
+
import { validateSchema } from "../standard.js";
|
|
3
|
+
import { resolveSubmittedValues } from "../values.js";
|
|
4
|
+
import { formDataToRecord } from "./form-data-to-record.js";
|
|
5
|
+
export async function parseFormData(formData, schema, options = {}) {
|
|
6
|
+
const record = options.input ?? formDataToRecord(formData, options);
|
|
7
|
+
const result = await validateSchema(schema, record);
|
|
8
|
+
if (!result.success) {
|
|
9
|
+
const values = resolveSubmittedValues(record, options.values, true);
|
|
10
|
+
validationError({
|
|
11
|
+
message: options.message ?? "Validation failed",
|
|
12
|
+
...result.error,
|
|
13
|
+
values,
|
|
14
|
+
}, options.status);
|
|
15
|
+
}
|
|
16
|
+
return result.data;
|
|
17
|
+
}
|
|
18
|
+
export { formDataToRecord };
|
|
19
|
+
//# sourceMappingURL=form-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-data.js","sourceRoot":"","sources":["../../src/parse/form-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAA8B,EAC9B,MAAiC,EACjC,UAA4B,EAAE;IAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpE,eAAe,CACb;YACE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,mBAAmB;YAC/C,GAAG,MAAM,CAAC,KAAK;YACf,MAAM;SACP,EACD,OAAO,CAAC,MAAM,CACf,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAoD,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ParseOptions, ValidationSchema } from "../types.js";
|
|
2
|
+
export type ParseJsonOptions = ParseOptions;
|
|
3
|
+
export declare function parseJson<TOutput>(request: Request, schema: ValidationSchema<TOutput>, options?: ParseJsonOptions): Promise<TOutput>;
|
|
4
|
+
export declare function parseJsonValue<TOutput>(value: unknown, schema: ValidationSchema<TOutput>, options?: ParseJsonOptions): Promise<TOutput>;
|
|
5
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/parse/json.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAE5C,wBAAsB,SAAS,CAAC,OAAO,EACrC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,EACjC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CA2BlB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAC1C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,EACjC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAalB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { validationError } from "@kamod-ch/otok/shared";
|
|
2
|
+
import { validateSchema } from "../standard.js";
|
|
3
|
+
export async function parseJson(request, schema, options = {}) {
|
|
4
|
+
let body;
|
|
5
|
+
try {
|
|
6
|
+
body = await request.json();
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
validationError({
|
|
10
|
+
message: options.message ?? "Invalid JSON body",
|
|
11
|
+
formErrors: ["Request body must be valid JSON."],
|
|
12
|
+
}, options.status ?? 400);
|
|
13
|
+
}
|
|
14
|
+
const result = await validateSchema(schema, body);
|
|
15
|
+
if (!result.success) {
|
|
16
|
+
validationError({
|
|
17
|
+
message: options.message ?? "Validation failed",
|
|
18
|
+
...result.error,
|
|
19
|
+
}, options.status);
|
|
20
|
+
}
|
|
21
|
+
return result.data;
|
|
22
|
+
}
|
|
23
|
+
export async function parseJsonValue(value, schema, options = {}) {
|
|
24
|
+
const result = await validateSchema(schema, value);
|
|
25
|
+
if (!result.success) {
|
|
26
|
+
validationError({
|
|
27
|
+
message: options.message ?? "Validation failed",
|
|
28
|
+
...result.error,
|
|
29
|
+
}, options.status);
|
|
30
|
+
}
|
|
31
|
+
return result.data;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/parse/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIhD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAgB,EAChB,MAAiC,EACjC,UAA4B,EAAE;IAE9B,IAAI,IAAa,CAAC;IAElB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,eAAe,CACb;YACE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,mBAAmB;YAC/C,UAAU,EAAE,CAAC,kCAAkC,CAAC;SACjD,EACD,OAAO,CAAC,MAAM,IAAI,GAAG,CACtB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,eAAe,CACb;YACE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,mBAAmB;YAC/C,GAAG,MAAM,CAAC,KAAK;SAChB,EACD,OAAO,CAAC,MAAM,CACf,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAc,EACd,MAAiC,EACjC,UAA4B,EAAE;IAE9B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,eAAe,CACb;YACE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,mBAAmB;YAC/C,GAAG,MAAM,CAAC,KAAK;SAChB,EACD,OAAO,CAAC,MAAM,CACf,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ParseOptions, ValidationSchema } from "../types.js";
|
|
2
|
+
import type { RouteParams } from "@kamod-ch/otok/server";
|
|
3
|
+
export type ParseParamsOptions = ParseOptions;
|
|
4
|
+
export declare function parseParams<TOutput>(params: RouteParams, schema: ValidationSchema<TOutput>, options?: ParseParamsOptions): Promise<TOutput>;
|
|
5
|
+
//# sourceMappingURL=params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../../src/parse/params.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAE9C,wBAAsB,WAAW,CAAC,OAAO,EACvC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,EACjC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,OAAO,CAAC,CAalB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { validationError } from "@kamod-ch/otok/shared";
|
|
2
|
+
import { validateSchema } from "../standard.js";
|
|
3
|
+
export async function parseParams(params, schema, options = {}) {
|
|
4
|
+
const result = await validateSchema(schema, params);
|
|
5
|
+
if (!result.success) {
|
|
6
|
+
validationError({
|
|
7
|
+
message: options.message ?? "Invalid route parameters",
|
|
8
|
+
...result.error,
|
|
9
|
+
}, options.status ?? 400);
|
|
10
|
+
}
|
|
11
|
+
return result.data;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"params.js","sourceRoot":"","sources":["../../src/parse/params.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKhD,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAmB,EACnB,MAAiC,EACjC,UAA8B,EAAE;IAEhC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,eAAe,CACb;YACE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,0BAA0B;YACtD,GAAG,MAAM,CAAC,KAAK;SAChB,EACD,OAAO,CAAC,MAAM,IAAI,GAAG,CACtB,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ValidationPluginOptions = Record<string, never> | void;
|
|
2
|
+
/**
|
|
3
|
+
* Marker plugin so `otok add validation` can register Standard Schema helpers in `otok.config.ts`.
|
|
4
|
+
* Composition APIs remain available via named imports from `@kamod-ch/otok-validation`.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import validation from "@kamod-ch/otok-validation";
|
|
8
|
+
* export default defineConfig({ plugins: [validation()] });
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export default function validation(_options?: ValidationPluginOptions): import("@kamod-ch/otok-config").OtokPlugin<unknown>;
|
|
12
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;AAOnE;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,EAAE,uBAAuB,uDAEpE"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { definePlugin } from "@kamod-ch/otok";
|
|
2
|
+
const validationPluginFactory = definePlugin({
|
|
3
|
+
name: "@kamod-ch/otok-validation",
|
|
4
|
+
version: "1.0.0",
|
|
5
|
+
});
|
|
6
|
+
/**
|
|
7
|
+
* Marker plugin so `otok add validation` can register Standard Schema helpers in `otok.config.ts`.
|
|
8
|
+
* Composition APIs remain available via named imports from `@kamod-ch/otok-validation`.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import validation from "@kamod-ch/otok-validation";
|
|
12
|
+
* export default defineConfig({ plugins: [validation()] });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export default function validation(_options) {
|
|
16
|
+
return validationPluginFactory();
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,MAAM,uBAAuB,GAAG,YAAY,CAAC;IAC3C,IAAI,EAAE,2BAA2B;IACjC,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,QAAkC;IACnE,OAAO,uBAAuB,EAAE,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ValidationErrorInput } from "@kamod-ch/otok/shared";
|
|
2
|
+
import type { LegacyParseableError, StandardSchemaIssue, StandardSchemaV1, ValidationSchema } from "./types.js";
|
|
3
|
+
export declare function isStandardSchema<T>(schema: ValidationSchema<T>): schema is StandardSchemaV1<unknown, T>;
|
|
4
|
+
export declare function isLegacySchema<T>(schema: ValidationSchema<T>): schema is import("./types.js").LegacyParseableSchema<T>;
|
|
5
|
+
export declare function validateSchema<TOutput>(schema: ValidationSchema<TOutput>, input: unknown): Promise<{
|
|
6
|
+
success: true;
|
|
7
|
+
data: TOutput;
|
|
8
|
+
} | {
|
|
9
|
+
success: false;
|
|
10
|
+
error: ValidationErrorInput;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function issuesToValidationInput(issues: ReadonlyArray<StandardSchemaIssue>, values?: Record<string, import("@kamod-ch/otok/shared").JsonValue>): ValidationErrorInput;
|
|
13
|
+
export declare function legacyErrorToValidationInput(error: LegacyParseableError, values?: Record<string, import("@kamod-ch/otok/shared").JsonValue>): ValidationErrorInput;
|
|
14
|
+
export declare function stripUnknownFields(input: Record<string, unknown>, knownKeys: string[]): Record<string, unknown>;
|
|
15
|
+
//# sourceMappingURL=standard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standard.d.ts","sourceRoot":"","sources":["../src/standard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EAEnB,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAQvG;AAED,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC1B,MAAM,IAAI,OAAO,YAAY,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAEzD;AAED,wBAAsB,cAAc,CAAC,OAAO,EAC1C,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,EACjC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,oBAAoB,CAAA;CAAE,CAAC,CAkB7F;AAMD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAC1C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC,GACjE,oBAAoB,CAmBtB;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,oBAAoB,EAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC,GACjE,oBAAoB,CAqBtB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAG/G"}
|
package/dist/standard.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export function isStandardSchema(schema) {
|
|
2
|
+
const candidate = schema;
|
|
3
|
+
return (typeof schema === "object" &&
|
|
4
|
+
schema !== null &&
|
|
5
|
+
"~standard" in schema &&
|
|
6
|
+
typeof candidate["~standard"]?.validate === "function");
|
|
7
|
+
}
|
|
8
|
+
export function isLegacySchema(schema) {
|
|
9
|
+
return typeof schema.safeParse === "function";
|
|
10
|
+
}
|
|
11
|
+
export async function validateSchema(schema, input) {
|
|
12
|
+
if (isStandardSchema(schema)) {
|
|
13
|
+
const result = await schema["~standard"].validate(input);
|
|
14
|
+
if (!isFailure(result)) {
|
|
15
|
+
return { success: true, data: result.value };
|
|
16
|
+
}
|
|
17
|
+
return { success: false, error: issuesToValidationInput(result.issues) };
|
|
18
|
+
}
|
|
19
|
+
if (isLegacySchema(schema)) {
|
|
20
|
+
const result = schema.safeParse(input);
|
|
21
|
+
if (result.success) {
|
|
22
|
+
return { success: true, data: result.data };
|
|
23
|
+
}
|
|
24
|
+
return { success: false, error: legacyErrorToValidationInput(result.error) };
|
|
25
|
+
}
|
|
26
|
+
throw new Error("otok-validation: schema must implement Standard Schema or safeParse().");
|
|
27
|
+
}
|
|
28
|
+
function isFailure(result) {
|
|
29
|
+
return "issues" in result && Array.isArray(result.issues);
|
|
30
|
+
}
|
|
31
|
+
export function issuesToValidationInput(issues, values) {
|
|
32
|
+
const fieldErrors = {};
|
|
33
|
+
const formErrors = [];
|
|
34
|
+
for (const issue of issues) {
|
|
35
|
+
const path = issue.path?.filter((segment) => typeof segment === "string" || typeof segment === "number") ?? [];
|
|
36
|
+
if (path.length === 0) {
|
|
37
|
+
formErrors.push(issue.message);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const key = path.map(String).join(".");
|
|
41
|
+
(fieldErrors[key] ??= []).push(issue.message);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
fieldErrors: Object.keys(fieldErrors).length > 0 ? fieldErrors : undefined,
|
|
45
|
+
formErrors: formErrors.length > 0 ? formErrors : undefined,
|
|
46
|
+
values,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function legacyErrorToValidationInput(error, values) {
|
|
50
|
+
if (typeof error.flatten === "function") {
|
|
51
|
+
const flattened = error.flatten();
|
|
52
|
+
const fieldErrors = {};
|
|
53
|
+
for (const [field, messages] of Object.entries(flattened.fieldErrors)) {
|
|
54
|
+
if (!messages)
|
|
55
|
+
continue;
|
|
56
|
+
fieldErrors[field] = Array.isArray(messages) ? [...messages] : [messages];
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
fieldErrors: Object.keys(fieldErrors).length > 0 ? fieldErrors : undefined,
|
|
60
|
+
formErrors: flattened.formErrors.length > 0 ? [...flattened.formErrors] : undefined,
|
|
61
|
+
values,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return issuesToValidationInput(error.issues.map((issue) => ({ message: issue.message, path: issue.path })), values);
|
|
65
|
+
}
|
|
66
|
+
export function stripUnknownFields(input, knownKeys) {
|
|
67
|
+
const allowed = new Set(knownKeys);
|
|
68
|
+
return Object.fromEntries(Object.entries(input).filter(([key]) => allowed.has(key)));
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=standard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standard.js","sourceRoot":"","sources":["../src/standard.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { JsonValue, ValidationErrorInput } from "@kamod-ch/otok/shared";
|
|
2
|
+
/** Standard Schema V1 — cross-library validation interface. */
|
|
3
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
4
|
+
readonly "~standard": StandardSchemaV1Props<Input, Output>;
|
|
5
|
+
}
|
|
6
|
+
export interface StandardSchemaV1Props<Input = unknown, Output = Input> {
|
|
7
|
+
readonly version: 1;
|
|
8
|
+
readonly vendor: string;
|
|
9
|
+
validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
|
|
10
|
+
readonly types?: {
|
|
11
|
+
readonly input: Input;
|
|
12
|
+
readonly output: Output;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export type StandardSchemaResult<Output> = StandardSchemaSuccessResult<Output> | StandardSchemaFailureResult;
|
|
16
|
+
export interface StandardSchemaSuccessResult<Output> {
|
|
17
|
+
readonly value: Output;
|
|
18
|
+
readonly issues?: undefined;
|
|
19
|
+
}
|
|
20
|
+
export interface StandardSchemaFailureResult {
|
|
21
|
+
readonly issues: ReadonlyArray<StandardSchemaIssue>;
|
|
22
|
+
readonly value?: undefined;
|
|
23
|
+
}
|
|
24
|
+
export interface StandardSchemaIssue {
|
|
25
|
+
readonly message: string;
|
|
26
|
+
readonly path?: ReadonlyArray<PropertyKey> | undefined;
|
|
27
|
+
}
|
|
28
|
+
/** Any schema Otok validation can consume. */
|
|
29
|
+
export type ValidationSchema<TOutput = unknown> = StandardSchemaV1<unknown, TOutput> | LegacyParseableSchema<TOutput>;
|
|
30
|
+
/** Legacy safeParse-style schemas (Zod 3, custom). */
|
|
31
|
+
export interface LegacyParseableSchema<TOutput> {
|
|
32
|
+
safeParse(input: unknown): {
|
|
33
|
+
success: true;
|
|
34
|
+
data: TOutput;
|
|
35
|
+
} | {
|
|
36
|
+
success: false;
|
|
37
|
+
error: LegacyParseableError;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface LegacyParseableError {
|
|
41
|
+
issues: ReadonlyArray<{
|
|
42
|
+
path: PropertyKey[];
|
|
43
|
+
message: string;
|
|
44
|
+
}>;
|
|
45
|
+
flatten?: () => {
|
|
46
|
+
fieldErrors: Record<string, string[] | string | undefined>;
|
|
47
|
+
formErrors: string[];
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface ParseOptions {
|
|
51
|
+
message?: string;
|
|
52
|
+
status?: 400 | 422;
|
|
53
|
+
/** Include submitted values in validation failure for safe redisplay. */
|
|
54
|
+
values?: boolean | Record<string, JsonValue>;
|
|
55
|
+
/** Strip unknown fields before validation. Default: false. */
|
|
56
|
+
stripUnknown?: boolean;
|
|
57
|
+
/** Coerce string form values (e.g. "42" → 42) when schema supports it. Default: true for forms. */
|
|
58
|
+
coerce?: boolean;
|
|
59
|
+
}
|
|
60
|
+
export interface FormParseOptions extends ParseOptions {
|
|
61
|
+
/** Field names that collect repeated values into a string array. */
|
|
62
|
+
arrays?: string[];
|
|
63
|
+
/** Checkbox fields: missing => false, "on"/"true"/"1" => true. */
|
|
64
|
+
checkboxes?: string[];
|
|
65
|
+
/** When set, parse this record instead of converting formData. */
|
|
66
|
+
input?: Record<string, unknown>;
|
|
67
|
+
}
|
|
68
|
+
export type { JsonValue, ValidationErrorInput };
|
|
69
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/values.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { JsonValue } from "@kamod-ch/otok/shared";
|
|
2
|
+
export declare function toJsonValues(record: Record<string, unknown>): Record<string, JsonValue>;
|
|
3
|
+
export declare function resolveSubmittedValues(record: Record<string, unknown>, valuesOption: boolean | Record<string, JsonValue> | undefined, defaultInclude: boolean): Record<string, JsonValue> | undefined;
|
|
4
|
+
//# sourceMappingURL=values.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAoBvF;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,SAAS,EAC7D,cAAc,EAAE,OAAO,GACtB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,SAAS,CAKvC"}
|
package/dist/values.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function toJsonValues(record) {
|
|
2
|
+
const values = {};
|
|
3
|
+
for (const [key, value] of Object.entries(record)) {
|
|
4
|
+
if (value instanceof File) {
|
|
5
|
+
values[key] = value.name;
|
|
6
|
+
continue;
|
|
7
|
+
}
|
|
8
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
9
|
+
values[key] = value;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
13
|
+
values[key] = value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return values;
|
|
17
|
+
}
|
|
18
|
+
export function resolveSubmittedValues(record, valuesOption, defaultInclude) {
|
|
19
|
+
if (valuesOption === false)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (valuesOption && typeof valuesOption === "object")
|
|
22
|
+
return valuesOption;
|
|
23
|
+
if (valuesOption === true || defaultInclude)
|
|
24
|
+
return toJsonValues(record);
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.js","sourceRoot":"","sources":["../src/values.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAAC,MAA+B;IAC1D,MAAM,MAAM,GAA8B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;YACzB,SAAS;QACX,CAAC;QAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;YAC3G,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACpB,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC9E,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAA+B,EAC/B,YAA6D,EAC7D,cAAuB;IAEvB,IAAI,YAAY,KAAK,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7C,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC;IAC1E,IAAI,YAAY,KAAK,IAAI,IAAI,cAAc;QAAE,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;IACzE,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kamod-ch/otok-validation",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Standard Schema validation for Otok — typed inputs, field errors, and defineAction integration.",
|
|
5
|
+
"homepage": "https://github.com/kamod-ch/otok/tree/main/packages/otok-validation#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/kamod-ch/otok/issues"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/kamod-ch/otok.git",
|
|
13
|
+
"directory": "packages/otok-validation"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./loader": {
|
|
26
|
+
"types": "./dist/loader.d.ts",
|
|
27
|
+
"default": "./dist/loader.js"
|
|
28
|
+
},
|
|
29
|
+
"./form": {
|
|
30
|
+
"types": "./dist/parse/form-data.d.ts",
|
|
31
|
+
"default": "./dist/parse/form-data.js"
|
|
32
|
+
},
|
|
33
|
+
"./json": {
|
|
34
|
+
"types": "./dist/parse/json.d.ts",
|
|
35
|
+
"default": "./dist/parse/json.js"
|
|
36
|
+
},
|
|
37
|
+
"./params": {
|
|
38
|
+
"types": "./dist/parse/params.d.ts",
|
|
39
|
+
"default": "./dist/parse/params.js"
|
|
40
|
+
},
|
|
41
|
+
"./client": {
|
|
42
|
+
"types": "./dist/client.d.ts",
|
|
43
|
+
"default": "./dist/client.js"
|
|
44
|
+
},
|
|
45
|
+
"./adapters/zod": {
|
|
46
|
+
"types": "./dist/adapters/zod.d.ts",
|
|
47
|
+
"default": "./dist/adapters/zod.js"
|
|
48
|
+
},
|
|
49
|
+
"./adapters/valibot": {
|
|
50
|
+
"types": "./dist/adapters/valibot.d.ts",
|
|
51
|
+
"default": "./dist/adapters/valibot.js"
|
|
52
|
+
},
|
|
53
|
+
"./adapters/arktype": {
|
|
54
|
+
"types": "./dist/adapters/arktype.d.ts",
|
|
55
|
+
"default": "./dist/adapters/arktype.js"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@kamod-ch/otok": "1.0.0",
|
|
63
|
+
"@kamod-ch/otok-kysely": "1.0.0",
|
|
64
|
+
"@kamod-ch/otok-test": "1.0.0",
|
|
65
|
+
"@types/node": "^24.12.2",
|
|
66
|
+
"arktype": "^2.1.29",
|
|
67
|
+
"preact": "^10.28.2",
|
|
68
|
+
"typescript": "~6.0.2",
|
|
69
|
+
"valibot": "^1.0.0",
|
|
70
|
+
"vitest": "^4.1.2",
|
|
71
|
+
"zod": "^4.0.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"@kamod-ch/otok": ">=0.6.1"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"zod": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"valibot": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"arktype": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"engines": {
|
|
88
|
+
"node": ">=20"
|
|
89
|
+
},
|
|
90
|
+
"scripts": {
|
|
91
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
92
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
93
|
+
"test": "vitest run"
|
|
94
|
+
}
|
|
95
|
+
}
|