Skip to content

Commit

Permalink
Enhance Metadata validator
Browse files Browse the repository at this point in the history
  • Loading branch information
samchon committed Aug 14, 2023
1 parent 2dad0ca commit 469a7a1
Show file tree
Hide file tree
Showing 105 changed files with 5,605 additions and 1,591 deletions.
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.0.0-dev.20230811",
"version": "5.0.0-dev.20230814",
"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.0.0-dev.20230811",
"version": "5.0.0-dev.20230814",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -68,7 +68,7 @@
},
"homepage": "https://typia.io",
"dependencies": {
"typia": "5.0.0-dev.20230811"
"typia": "5.0.0-dev.20230814"
},
"peerDependencies": {
"typescript": ">= 4.7.4"
Expand Down
5 changes: 5 additions & 0 deletions src/factories/MetadataCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export class MetadataCollection {
private recursive_array_index_: number;
private recursive_tuple_index_: number;

/**
* @internal
*/
public readonly entire_: Set<Metadata> = new Set();

public constructor(
private readonly options?: Partial<MetadataCollection.IOptions>,
) {
Expand Down
4 changes: 4 additions & 0 deletions src/factories/MetadataFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export namespace MetadataFactory {
)(type, false);
iterate_metadata_collection(collection);
iterate_metadata_sort(collection)(meta);

if (options.validate)
for (const elem of collection.entire_) options.validate(elem);
collection.entire_.clear();
return meta;
};
}
163 changes: 81 additions & 82 deletions src/factories/ProtobufFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ import ts from "typescript";

import { Metadata } from "../schemas/metadata/Metadata";
import { MetadataObject } from "../schemas/metadata/MetadataObject";
import { IProtocolMessage } from "../schemas/protobuf/IProtocolMessage";

import { Escaper } from "../utils/Escaper";

import { MetadataCollection } from "./MetadataCollection";
import { MetadataFactory } from "./MetadataFactory";
import { emplace_protocol_object } from "./internal/protocols/emplace_protocol_object";
import { iterate_protocol_main } from "./internal/protocols/iterate_protocol_main";

export namespace ProtobufFactory {
export const metadata =
Expand Down Expand Up @@ -44,37 +41,70 @@ export namespace ProtobufFactory {
return top;
};

export const analyze =
(collection: MetadataCollection) =>
(dict: Map<string, IProtocolMessage>) =>
(meta: Metadata) => {
// EMPLACE OBJECTS
for (const obj of collection.objects())
emplace_protocol_object(dict)(obj);

// WHEN NOT OBJECT, WRAP IT INTO A FAKE MAIN OBJECT
iterate_protocol_main(dict)(meta);
};

const validate = (method: string) => (meta: Metadata) => {
//----
// NOT ALLOWED TYPES
// NOT SUPPORTED TYPES
//----
// PROHIBIT ANY TYPE
if (meta.any) throw notSupportedError({ method })("any type");
// PROHIBIT FUNCTIONAL TYPE
else if (meta.functional)
throw notSupportedError({ method })("functional type");
// PROHIBIT TUPLE TYPE
else if (meta.tuples.length)
throw notSupportedError({ method })("tuple type");
// PROHIBIT SET TYPE
else if (meta.sets.length)
throw notSupportedError({ method })("Set type");
// NATIVE TYPE, BUT NOT Uint8Array
else if (meta.natives.length) {
const banned = meta.natives
.map((n) => [n, BANNED_NATIVE_TYPES.get(n)] as const)
.filter(([_n, b]) => b !== undefined)[0];
if (banned !== undefined)
throw notSupportedError({ method })(
banned[1] === null
? banned[0]
: `${banned[0]}. Use ${banned[1]} instead.`,
);
}
//----
// ARRRAY CASES
//----
// DO NOT ALLOW MULTI-DIMENTIONAL ARRAY
else if (
meta.arrays.length &&
meta.arrays.some((array) => !!array.value.arrays.length)
)
throw notSupportedError({ method })("two dimenstional array type");
// CHILD OF ARRAY TYPE MUST BE REQUIRED
else if (
meta.arrays.length &&
meta.arrays.some(
(array) =>
array.value.isRequired() === false ||
array.value.nullable === true,
)
)
throw notSupportedError({ method })("optional type in array");
// UNION IN ARRAY
else if (
meta.arrays.length &&
meta.arrays.some((a) => a.value.size() > 1)
)
throw notSupportedError({ method })("union type in array");
// UNION WITH ARRAY
else if (meta.size() > 1 && meta.arrays.length)
throw notSupportedError({ method })("union type with array type");
//----
// SPECIAL CASES
// OBJECT CASES
//----
// EMPTY PROPERTY
else if (
meta.objects.length &&
meta.objects.some((obj) => obj.properties.length === 0)
)
throw notSupportedError({ method })("empty object type");
// MULTIPLE DYNAMIC KEY TYPED PROPERTIES
else if (
meta.objects.length &&
Expand All @@ -92,7 +122,6 @@ export namespace ProtobufFactory {
meta.objects.length &&
meta.objects.some(
(obj) =>
obj.properties.length &&
obj.properties.some((p) => p.key.isSoleLiteral()) &&
obj.properties.some((p) => !p.key.isSoleLiteral()),
)
Expand All @@ -114,29 +143,7 @@ export namespace ProtobufFactory {
throw notSupportedError({ method })(
`object type with invalid static key name.`,
);
// NATIVE TYPE, BUT NOT Uint8Array
else if (meta.natives.length) {
const banned = meta.natives
.map((n) => [n, BANNED_NATIVE_TYPES.get(n)] as const)
.filter(([_n, b]) => b !== undefined)[0];
if (banned !== undefined)
throw notSupportedError({ method })(
banned[1] === null
? banned[0]
: `${banned[0]}. Use ${banned[1]} instead.`,
);
}
// MAP TYPE, BUT PROPERTY KEY TYPE IS NOT ATOMIC
else if (meta.maps.length && meta.maps.some((m) => !isAtomicKey(m.key)))
throw notSupportedError({ method })("");
// MAP TYPE, BUT VALUE TYPE IS ARRAY
else if (
meta.maps.length &&
meta.maps.some((m) => !!m.value.arrays.length)
)
throw notSupportedError({ method })(
"map type with array value type",
);
// DYNAMIC OBJECT, BUT PROPERTY VALUE TYPE IS ARRAY
else if (
meta.objects.length &&
isDynamicObject(meta.objects[0]!) &&
Expand All @@ -145,18 +152,6 @@ export namespace ProtobufFactory {
throw notSupportedError({ method })(
"dynamic object with array value type",
);
//----
// UNION TYPES
//----
// UNION IN ARRAY
else if (
meta.arrays.length &&
meta.arrays.some((a) => a.value.size() > 1)
)
throw notSupportedError({ method })("union type in array");
// UNION WITH ARRAY
else if (meta.size() > 1 && meta.arrays.length)
throw notSupportedError({ method })("union type with array type");
// UNION WITH DYNAMIC OBJECT
else if (
meta.size() > 1 &&
Expand All @@ -178,6 +173,37 @@ export namespace ProtobufFactory {
throw notSupportedError({ method })(
"union type in dynamic property",
);
//----
// MAP CASES
//----
// MAP TYPE, BUT PROPERTY KEY TYPE IS NOT STRING
else if (
meta.maps.length &&
meta.maps.some(
(m) =>
m.key.isBinaryUnion() ||
(m.key.atomics.find((v) => v === "string") === undefined &&
m.key.constants.find((c) => c.type === "string") ===
undefined),
)
)
throw notSupportedError({ method })("non-string key typed map");
// MAP TYPE, BUT PROPERTY KEY TYPE IS OPTIONAL
else if (
meta.maps.length &&
meta.maps.some(
(m) => m.key.isRequired() === false || m.key.nullable,
)
)
throw notSupportedError({ method })("optional key typed map");
// MAP TYPE, BUT VALUE TYPE IS ARRAY
else if (
meta.maps.length &&
meta.maps.some((m) => !!m.value.arrays.length)
)
throw notSupportedError({ method })(
"map type with array value type",
);
// UNION WITH MAP
else if (meta.size() > 1 && meta.maps.length)
throw notSupportedError({ method })("union type with map type");
Expand All @@ -197,35 +223,8 @@ const notSupportedError = (p: { method: string }) => (title: string) =>
`${prefix(p.method)}: protocol buffer does not support ${title}.`,
);

const isAtomicKey = (key: Metadata) => {
if (
key.required &&
key.nullable === false &&
key.functional === false &&
key.resolved === null &&
key.size() ===
key.atomics.length +
key.constants
.map((c) => c.values.length)
.reduce((a, b) => a + b, 0) +
key.templates.length
) {
const set: Set<string> = new Set();
for (const atomic of key.atomics) set.add(atomic);
for (const constant of key.constants) set.add(constant.type);
if (key.templates.length) set.add("string");

return set.size === 1;
}
return false;
};

const isDynamicObject = (obj: MetadataObject): boolean => {
return (
obj.properties.length > 0 &&
obj.properties[0]!.key.isSoleLiteral() === false
);
};
const isDynamicObject = (obj: MetadataObject): boolean =>
obj.properties[0]!.key.isSoleLiteral() === false;

const BANNED_NATIVE_TYPES: Map<string, string | null> = new Map([
["Date", "string"],
Expand Down
16 changes: 16 additions & 0 deletions src/factories/StatementFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import ts from "typescript";

export namespace StatementFactory {
export const mut = (name: string, initializer?: ts.Expression) =>
ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[
ts.factory.createVariableDeclaration(
name,
undefined,
undefined,
initializer,
),
],
ts.NodeFlags.Let,
),
);

export const constant = (name: string, initializer?: ts.Expression) =>
ts.factory.createVariableStatement(
undefined,
Expand Down
10 changes: 4 additions & 6 deletions src/factories/internal/metadata/explore_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ export const explore_metadata =
): Metadata => {
// CONSTRUCT METADATA
const meta: Metadata = Metadata.initialize(parentResolved);
const out = (meta: Metadata) => {
if (options.validate) options.validate(meta);
return meta;
};
if (type === null) return out(meta);
collection.entire_.add(meta);

if (type === null) return meta;

// ITERATE TYPESCRIPT TYPES
iterate_metadata(checker)(options)(collection)(
Expand All @@ -36,5 +34,5 @@ export const explore_metadata =
emend_metadata_atomics(meta.resolved.original);
emend_metadata_atomics(meta.resolved.returns);
}
return out(meta);
return meta;
};
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const iterate_metadata_intersection =
throw new Error(message(children));
});
Object.assign(meta, Metadata.merge(meta, least));
collection.entire_.add(least);
return true;
};

Expand Down
Loading

0 comments on commit 469a7a1

Please sign in to comment.