Skip to content

Commit

Permalink
Merge pull request #916 from samchon/features/json
Browse files Browse the repository at this point in the history
Lighten up JSON Schema
  • Loading branch information
samchon authored Jan 3, 2024
2 parents a48bdaf + 4c1dbd0 commit d22d2ca
Show file tree
Hide file tree
Showing 1,995 changed files with 117,747 additions and 70,414 deletions.
2 changes: 1 addition & 1 deletion benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@
"suppress-warnings": "^1.0.2",
"tstl": "^2.5.13",
"uuid": "^9.0.1",
"typia": "D:\\github\\samchon\\typia\\typia-5.3.7.tgz"
"typia": "D:\\github\\samchon\\typia\\typia-5.3.8.tgz"
}
}
1 change: 1 addition & 0 deletions benchmark/src/structures/zod/ZodUltimateUnion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const Application: z.ZodType<IJsonApplication> = z.lazy(() =>
components: Components,
purpose: z.union([z.literal("ajv"), z.literal("swagger")]),
prefix: z.string(),
surplus: z.boolean(),
}),
);

Expand Down
2 changes: 1 addition & 1 deletion errors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@
"typescript": "^5.3.2"
},
"dependencies": {
"typia": "D:\\github\\samchon\\typia\\typia-5.3.7.tgz"
"typia": "D:\\github\\samchon\\typia\\typia-5.3.8.tgz"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typia",
"version": "5.3.7",
"version": "5.3.8",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
4 changes: 2 additions & 2 deletions packages/typescript-json/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typescript-json",
"version": "5.3.7",
"version": "5.3.8",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -61,7 +61,7 @@
},
"homepage": "https://typia.io",
"dependencies": {
"typia": "5.3.7"
"typia": "5.3.8"
},
"peerDependencies": {
"typescript": ">=4.8.0 <5.4.0"
Expand Down
39 changes: 24 additions & 15 deletions src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,23 @@ import { Primitive } from "./Primitive";
* JSON Schema Application.
*
* Creates a JSON schema application which contains both main JSON schemas and
* components. Note that, all of the object types are stored in the
* components. Note that, all of the named types are stored in the
* {@link IJsonApplication.components} property for the `$ref` referencing.
*
* Also, `typia.json.application()` has additional generic arguments, *Purpose*.
* As JSON schema definitions used by `swagger` and `ajv` are different a little bit,
* you should configure the *Purpose* appropriately.
* Also, `typia.json.application()` has two additional generic arguments.
*
* For an example, `ajv` has an extra property "$recursiveRef" that are not exists
* in the standard JSON schema definition spec. Otherwise, `swagger` can't identify
* the tuple definition.
* The 1st *Purpose* means the purpose determines the JSON schema definition spec.
* For an example, `ajv` has an extra property "$recursiveRef" that are not
* exists in the standard JSON schema definition spec. Otherwise, `swagger`
* can't identify the tuple definition.
*
* The next *Surplus* means whether to allow surplus properties starting with
* `x-typia-` or not. If `true`, the surplus properties like `x-typia-jsDocTags`
* would be registered into the JSON schema, otherwise, not.
*
* @template Types Tuple of target types
* @template Purpose Purpose of the JSON schema`
* @template Purpose Purpose of the JSON schema
* @template Surplus Allow surplus properties starting with `x-typia-` or not
* @return JSON schema application
*
* @author Jeongho Nam - https://github.com/samchon
Expand All @@ -43,26 +47,31 @@ export function application(): never;
* JSON Schema Application.
*
* Creates a JSON schema application which contains both main JSON schemas and
* components. Note that, all of the object types are stored in the
* components. Note that, all of the named types are stored in the
* {@link IJsonApplication.components} property for the `$ref` referencing.
*
* Also, `typia.json.application()` has additional generic arguments, *Purpose*.
* As JSON schema definitions used by `swagger` and `ajv` are different a little bit,
* you should configure the *Purpose* appropriately.
* Also, `typia.json.application()` has two additional generic arguments.
*
* The 1st *Purpose* means the purpose determines the JSON schema definition spec.
* For an example, `ajv` has an extra property "$recursiveRef" that are not
* exists in the standard JSON schema definition spec. Otherwise, `swagger`
* can't identify the tuple definition.
*
* For an example, `ajv` has an extra property "$recursiveRef" that are not exists
* in the standard JSON schema definition spec. Otherwise, `swagger` can't identify
* the tuple definition.
* The next *Surplus* means whether to allow surplus properties starting with
* `x-typia-` or not. If `true`, the surplus properties like `x-typia-jsDocTags`
* would be registered into the JSON schema, otherwise, not.
*
* @template Types Tuple of target types
* @template Purpose Purpose of the JSON schema
* @template Surplus Allow surplus properties starting with `x-typia-` or not
* @return JSON schema application
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function application<
Types extends unknown[],
Purpose extends "ajv" | "swagger" = "swagger",
Surplus extends boolean = false,
>(): IJsonApplication;

/**
Expand Down
26 changes: 13 additions & 13 deletions src/programmers/internal/application_array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import { application_schema } from "./application_schema";
export const application_array =
(options: JsonApplicationProgrammer.IOptions) =>
(components: IJsonComponents) =>
(array: MetadataArray) =>
(attribute: IJsonSchema.IAttribute): IJsonSchema.IArray[] => {
(array: MetadataArray): IJsonSchema.IArray[] => {
// BASE SCHEMA
const items: IJsonSchema = application_schema(options)(false)(components)(
array.type.value,
)(attribute);
)({});
const base: IJsonSchema.IArray = {
...attribute,
type: "array",
items: null!,
};
Expand All @@ -31,7 +29,7 @@ export const application_array =

// CONSIDER TYPE TAGS
const union: IJsonSchema.IArray[] = array.tags.map(
(row) => application_array_tags({ ...base })(row)!,
(row) => application_array_tags(options)({ ...base })(row)!,
);
const map: Map<string, IJsonSchema.IArray> = new Map(
union.map((u) => [JSON.stringify(u), u]),
Expand All @@ -40,20 +38,22 @@ export const application_array =
};

const application_array_tags =
(options: JsonApplicationProgrammer.IOptions) =>
(schema: IJsonSchema.IArray) =>
(row: IMetadataTypeTag[]): IJsonSchema.IArray => {
for (const tag of row.slice().sort((a, b) => a.kind.localeCompare(b.kind)))
if (tag.kind === "minItems" && typeof tag.value === "number")
schema.minItems = tag.value;
else if (tag.kind === "maxItems" && typeof tag.value === "number")
schema.maxItems = tag.value;
schema["x-typia-typeTags"] = row.map((tag) => ({
target: tag.target,
name: tag.name,
kind: tag.kind,
value: tag.value,
validate: tag.validate,
exclusive: tag.exclusive,
}));
if (options.surplus)
schema["x-typia-typeTags"] = row.map((tag) => ({
target: tag.target,
name: tag.name,
kind: tag.kind,
value: tag.value,
validate: tag.validate,
exclusive: tag.exclusive,
}));
return schema;
};
9 changes: 7 additions & 2 deletions src/programmers/internal/application_boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { IMetadataTypeTag } from "../../schemas/metadata/IMetadataTypeTag";
import { MetadataAtomic } from "../../schemas/metadata/MetadataAtomic";

import { IJsonSchema } from "../../module";
import { JsonApplicationProgrammer } from "../json/JsonApplicationProgrammer";
import { application_default } from "./application_default";

/**
* @internal
*/
export const application_boolean =
(options: JsonApplicationProgrammer.IOptions) =>
(atomic: MetadataAtomic) =>
(attribute: IJsonSchema.IAttribute): IJsonSchema.IBoolean[] => {
const base: IJsonSchema.IBoolean = {
...attribute,
default: application_default(attribute)(
(alias) => alias === "true" || alias === "false",
)((str) => Boolean(str)),
Expand All @@ -25,6 +26,10 @@ export const application_boolean =
return defaultTags.map((tag) => ({
...base,
default: tag.value,
"x-typia-typeTags": defaultTags,
...(options.surplus
? {
"x-typia-typeTags": defaultTags,
}
: {}),
}));
};
1 change: 0 additions & 1 deletion src/programmers/internal/application_constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { application_default } from "./application_default";
export const application_constant =
(constant: MetadataConstant) =>
(attribute: IJsonSchema.IAttribute): IJsonSchema.IEnumeration<any> => ({
...attribute,
type: constant.type,
enum: constant.values as any,
default: application_default(attribute)((alias) =>
Expand Down
5 changes: 2 additions & 3 deletions src/programmers/internal/application_escaped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ export const application_escaped =
(options: JsonApplicationProgrammer.IOptions) =>
<BlockNever extends boolean>(blockNever: BlockNever) =>
(components: IJsonComponents) =>
(resolved: MetadataEscaped) =>
(attribute: IJsonSchema.IAttribute): IJsonSchema[] => {
(resolved: MetadataEscaped): IJsonSchema[] => {
const output = application_schema(options)(blockNever)(components)(
resolved.returns,
)(attribute);
)({});
if (output === null) return [];

if (is_date(new Set())(resolved.original)) {
Expand Down
10 changes: 3 additions & 7 deletions src/programmers/internal/application_native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ export const application_native =
(options: JsonApplicationProgrammer.IOptions) =>
(components: IJsonComponents) =>
(name: string) =>
(props: {
nullable: boolean;
attribute: IJsonSchema.IAttribute;
}): IJsonSchema.IReference => {
(nullable: boolean): IJsonSchema.IReference => {
const key: string =
options.purpose === "ajv"
? name
: `${name}${props.nullable ? ".Nullable" : ""}`;
: `${name}${nullable ? ".Nullable" : ""}`;
if (components.schemas?.[key] === undefined) {
components.schemas ??= {};
components.schemas[key] ??= {
Expand All @@ -28,11 +25,10 @@ export const application_native =
? `${JSON_COMPONENTS_PREFIX}/objects/${key}`
: undefined,
properties: {},
nullable: options.purpose === "swagger" ? props.nullable : undefined,
nullable: options.purpose === "swagger" ? nullable : undefined,
};
}
return {
...props.attribute,
$ref: `${JSON_COMPONENTS_PREFIX}/objects/${key}`,
};
};
23 changes: 13 additions & 10 deletions src/programmers/internal/application_number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IMetadataTypeTag } from "../../schemas/metadata/IMetadataTypeTag";
import { MetadataAtomic } from "../../schemas/metadata/MetadataAtomic";

import { IJsonSchema } from "../../module";
import { JsonApplicationProgrammer } from "../json/JsonApplicationProgrammer";
import { application_default } from "./application_default";

type Schema = IJsonSchema.INumber | IJsonSchema.IInteger;
Expand All @@ -10,11 +11,11 @@ type Schema = IJsonSchema.INumber | IJsonSchema.IInteger;
* @internal
*/
export const application_number =
(options: JsonApplicationProgrammer.IOptions) =>
(atomic: MetadataAtomic) =>
(attribute: IJsonSchema.IAttribute): Array<Schema> => {
// BASE CONFIGURATION
const base: Schema = {
...attribute,
type: "number",
};
const out = (schema: Schema) => {
Expand All @@ -39,7 +40,7 @@ export const application_number =

// CONSIDER TYPE TAGS
const union: Schema[] = atomic.tags.map(
(row) => application_number_tags({ ...base })(row)!,
(row) => application_number_tags(options)({ ...base })(row)!,
);
const map: Map<string, Schema> = new Map(
union.map((u) => [JSON.stringify(u), u]),
Expand All @@ -48,6 +49,7 @@ export const application_number =
};

const application_number_tags =
(options: JsonApplicationProgrammer.IOptions) =>
(base: Schema) =>
(row: IMetadataTypeTag[]): Schema => {
for (const tag of row
Expand Down Expand Up @@ -82,13 +84,14 @@ const application_number_tags =
else if (tag.kind === "default" && typeof tag.value === "number")
base.default = tag.value;
}
base["x-typia-typeTags"] = row.map((tag) => ({
target: tag.target,
name: tag.name,
kind: tag.kind,
value: tag.value,
validate: tag.validate,
exclusive: tag.exclusive,
}));
if (options.surplus)
base["x-typia-typeTags"] = row.map((tag) => ({
target: tag.target,
name: tag.name,
kind: tag.kind,
value: tag.value,
validate: tag.validate,
exclusive: tag.exclusive,
}));
return base;
};
32 changes: 21 additions & 11 deletions src/programmers/internal/application_object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,15 @@ const create_object_schema =
: undefined;
})(),
description: property.description ?? undefined,
"x-typia-jsDocTags": property.jsDocTags.length
? property.jsDocTags
: undefined,
"x-typia-required": property.value.required,
"x-typia-optional": property.value.optional,
...(options.surplus
? {
"x-typia-jsDocTags": property.jsDocTags.length
? property.jsDocTags
: undefined,
"x-typia-required": property.value.required,
"x-typia-optional": property.value.optional,
}
: {}),
});

if (schema === null) continue;
Expand Down Expand Up @@ -122,15 +126,17 @@ const create_object_schema =
nullable: options.purpose === "swagger" ? nullable : undefined,
required: required.length ? required : undefined,
description: obj.description,
"x-typia-jsDocTags": obj.jsDocTags,
...(options.surplus ? { "x-typia-jsDocTags": obj.jsDocTags } : {}),
...(options.purpose === "ajv"
? extraProps
: {
: options.surplus
? {
// swagger can't express patternProperties
"x-typia-additionalProperties": extraProps.additionalProperties,
"x-typia-patternProperties": extraProps.patternProperties,
additionalProperties: join(options)(components)(extraMeta),
}),
}
: {}),
};
};

Expand All @@ -153,9 +159,13 @@ const join =
.map((tuple) => tuple[0])
.reduce((x, y) => Metadata.merge(x, y));
return (
application_schema(options)(true)(components)(meta)({
"x-typia-required": false,
}) ?? undefined
application_schema(options)(true)(components)(meta)(
options.surplus
? {
"x-typia-required": false,
}
: {},
) ?? undefined
);
};

Expand Down
Loading

0 comments on commit d22d2ca

Please sign in to comment.