@wp-typia/project-tools 0.15.1 → 0.15.3

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.
@@ -1,233 +1,7 @@
1
- import { cloneJsonValue } from "./json-utils.js";
2
- import { getWordPressKind, } from "./metadata-model.js";
3
- export function createBlockJsonAttribute(node, warnings) {
4
- const attribute = {
5
- type: getWordPressKind(node),
6
- };
7
- if (node.defaultValue !== undefined) {
8
- attribute.default = cloneJsonValue(node.defaultValue);
9
- }
10
- if (node.enumValues !== null && node.enumValues.length > 0) {
11
- attribute.enum = [...node.enumValues];
12
- }
13
- if (node.wp.source !== null) {
14
- attribute.source = node.wp.source;
15
- }
16
- if (node.wp.selector !== null) {
17
- attribute.selector = node.wp.selector;
18
- }
19
- const reasons = [];
20
- if (node.constraints.exclusiveMaximum !== null)
21
- reasons.push("exclusiveMaximum");
22
- if (node.constraints.exclusiveMinimum !== null)
23
- reasons.push("exclusiveMinimum");
24
- if (node.constraints.format !== null)
25
- reasons.push("format");
26
- if (node.constraints.maxLength !== null)
27
- reasons.push("maxLength");
28
- if (node.constraints.maxItems !== null)
29
- reasons.push("maxItems");
30
- if (node.constraints.maximum !== null)
31
- reasons.push("maximum");
32
- if (node.constraints.minLength !== null)
33
- reasons.push("minLength");
34
- if (node.constraints.minItems !== null)
35
- reasons.push("minItems");
36
- if (node.constraints.minimum !== null)
37
- reasons.push("minimum");
38
- if (node.constraints.multipleOf !== null)
39
- reasons.push("multipleOf");
40
- if (node.constraints.pattern !== null)
41
- reasons.push("pattern");
42
- if (node.constraints.typeTag !== null)
43
- reasons.push("typeTag");
44
- if (node.kind === "array" && node.items !== undefined)
45
- reasons.push("items");
46
- if (node.kind === "object" && node.properties !== undefined)
47
- reasons.push("properties");
48
- if (node.kind === "union" && node.union !== null)
49
- reasons.push("union");
50
- if (reasons.length > 0) {
51
- warnings.push(`${node.path}: ${reasons.join(", ")}`);
52
- }
53
- return attribute;
54
- }
55
- export function createManifestAttribute(node) {
56
- return {
57
- typia: {
58
- constraints: { ...node.constraints },
59
- defaultValue: node.defaultValue === undefined ? null : cloneJsonValue(node.defaultValue),
60
- hasDefault: node.defaultValue !== undefined,
61
- },
62
- ts: {
63
- items: node.items ? createManifestAttribute(node.items) : null,
64
- kind: node.kind,
65
- properties: node.properties
66
- ? Object.fromEntries(Object.entries(node.properties).map(([key, property]) => [
67
- key,
68
- createManifestAttribute(property),
69
- ]))
70
- : null,
71
- required: node.required,
72
- union: node.union
73
- ? {
74
- branches: Object.fromEntries(Object.entries(node.union.branches).map(([key, branch]) => [
75
- key,
76
- createManifestAttribute(branch),
77
- ])),
78
- discriminator: node.union.discriminator,
79
- }
80
- : null,
81
- },
82
- wp: {
83
- defaultValue: node.defaultValue === undefined ? null : cloneJsonValue(node.defaultValue),
84
- enum: node.enumValues ? [...node.enumValues] : null,
85
- hasDefault: node.defaultValue !== undefined,
86
- ...(node.wp.selector !== null ? { selector: node.wp.selector } : {}),
87
- ...(node.wp.source !== null ? { source: node.wp.source } : {}),
88
- type: getWordPressKind(node),
89
- },
90
- };
91
- }
92
- export function createManifestDocument(sourceTypeName, attributes) {
93
- return {
94
- attributes: Object.fromEntries(Object.entries(attributes).map(([key, node]) => [
95
- key,
96
- createManifestAttribute(node),
97
- ])),
98
- manifestVersion: 2,
99
- sourceType: sourceTypeName,
100
- };
101
- }
102
- export function validateWordPressExtractionAttributes(attributes) {
103
- for (const attribute of Object.values(attributes)) {
104
- validateWordPressExtractionAttribute(attribute, true);
105
- }
106
- }
107
- export function validateWordPressExtractionAttribute(node, isTopLevel = false) {
108
- const hasSelector = node.wp.selector !== null;
109
- const hasSource = node.wp.source !== null;
110
- if (hasSelector || hasSource) {
111
- if (!isTopLevel) {
112
- throw new Error(`WordPress extraction tags are only supported on top-level block attributes at ${node.path}`);
113
- }
114
- if (!hasSelector || !hasSource) {
115
- throw new Error(`WordPress extraction tags require both Source and Selector at ${node.path}`);
116
- }
117
- if (node.kind !== "string") {
118
- throw new Error(`WordPress extraction tags are only supported on string attributes at ${node.path}`);
119
- }
120
- }
121
- if (node.items !== undefined) {
122
- validateWordPressExtractionAttribute(node.items, false);
123
- }
124
- if (node.properties !== undefined) {
125
- for (const property of Object.values(node.properties)) {
126
- validateWordPressExtractionAttribute(property, false);
127
- }
128
- }
129
- if (node.union?.branches) {
130
- for (const branch of Object.values(node.union.branches)) {
131
- validateWordPressExtractionAttribute(branch, false);
132
- }
133
- }
134
- }
135
- export function createExampleValue(node, key) {
136
- if (node.defaultValue !== undefined) {
137
- return cloneJsonValue(node.defaultValue);
138
- }
139
- if (node.enumValues !== null && node.enumValues.length > 0) {
140
- return cloneJsonValue(node.enumValues[0]);
141
- }
142
- switch (node.kind) {
143
- case "string":
144
- return fitStringExampleToConstraints(createFormattedStringExample(node.constraints.format, key), node.constraints);
145
- case "number":
146
- return createNumericExample(node);
147
- case "boolean":
148
- return true;
149
- case "array":
150
- return createArrayExample(node, key);
151
- case "object":
152
- return Object.fromEntries(Object.entries(node.properties ?? {}).map(([propertyKey, propertyNode]) => [
153
- propertyKey,
154
- createExampleValue(propertyNode, propertyKey),
155
- ]));
156
- case "union": {
157
- const firstBranch = node.union
158
- ? Object.values(node.union.branches)[0]
159
- : undefined;
160
- if (!firstBranch || firstBranch.kind !== "object") {
161
- return {};
162
- }
163
- return Object.fromEntries(Object.entries(firstBranch.properties ?? {}).map(([propertyKey, propertyNode]) => [
164
- propertyKey,
165
- createExampleValue(propertyNode, propertyKey),
166
- ]));
167
- }
168
- }
169
- }
170
- function createFormattedStringExample(format, key) {
171
- switch (format) {
172
- case "uuid":
173
- return "00000000-0000-4000-8000-000000000000";
174
- case "email":
175
- return "example@example.com";
176
- case "url":
177
- case "uri":
178
- return "https://example.com";
179
- case "ipv4":
180
- return "127.0.0.1";
181
- case "ipv6":
182
- return "::1";
183
- case "date-time":
184
- return "2024-01-01T00:00:00Z";
185
- default:
186
- return `Example ${key}`;
187
- }
188
- }
189
- function fitStringExampleToConstraints(value, constraints) {
190
- let nextValue = value;
191
- if (constraints.maxLength !== null &&
192
- nextValue.length > constraints.maxLength) {
193
- nextValue = nextValue.slice(0, constraints.maxLength);
194
- }
195
- if (constraints.minLength !== null &&
196
- nextValue.length < constraints.minLength) {
197
- nextValue = nextValue.padEnd(constraints.minLength, "x");
198
- }
199
- return nextValue;
200
- }
201
- function createNumericExample(node) {
202
- const { exclusiveMaximum, exclusiveMinimum, maximum, minimum, multipleOf, } = node.constraints;
203
- const step = multipleOf && multipleOf !== 0 ? multipleOf : 1;
204
- const minCandidate = minimum ?? (exclusiveMinimum !== null ? exclusiveMinimum + step : null);
205
- const maxCandidate = maximum ?? (exclusiveMaximum !== null ? exclusiveMaximum - step : null);
206
- let candidate = multipleOf && multipleOf !== 0
207
- ? minCandidate !== null
208
- ? Math.ceil(minCandidate / multipleOf) * multipleOf
209
- : maxCandidate !== null
210
- ? Math.floor(maxCandidate / multipleOf) * multipleOf
211
- : multipleOf
212
- : minCandidate ?? maxCandidate ?? 42;
213
- if (maxCandidate !== null && candidate > maxCandidate) {
214
- candidate =
215
- multipleOf && multipleOf !== 0
216
- ? Math.floor(maxCandidate / multipleOf) * multipleOf
217
- : maxCandidate;
218
- }
219
- if (minCandidate !== null && candidate < minCandidate) {
220
- candidate =
221
- multipleOf && multipleOf !== 0
222
- ? Math.ceil(minCandidate / multipleOf) * multipleOf
223
- : minCandidate;
224
- }
225
- return candidate;
226
- }
227
- function createArrayExample(node, key) {
228
- const minimumItems = Math.max(node.constraints.minItems ?? 0, 0);
229
- if (minimumItems === 0 || node.items === undefined) {
230
- return [];
231
- }
232
- return Array.from({ length: minimumItems }, (_value, index) => createExampleValue(node.items, `${key}${index + 1}`));
233
- }
1
+ /**
2
+ * Re-exports metadata projection utilities from `@wp-typia/block-runtime`.
3
+ * This adapter keeps the public project-tools runtime path stable while the
4
+ * implementation is consolidated in block-runtime.
5
+ * @module
6
+ */
7
+ export * from "@wp-typia/block-runtime/metadata-projection";
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export type UnknownRecord = Record<string, unknown>;
5
5
  /**
6
- * Check whether a value is a plain object record.
6
+ * Delegate plain-object detection to the shared runtime primitive owner.
7
7
  *
8
8
  * @param value Runtime value to inspect.
9
9
  * @returns `true` when the value is a non-null plain object with an
@@ -1,14 +1,11 @@
1
+ import { isPlainObject as isSharedPlainObject } from "@wp-typia/api-client/runtime-primitives";
1
2
  /**
2
- * Check whether a value is a plain object record.
3
+ * Delegate plain-object detection to the shared runtime primitive owner.
3
4
  *
4
5
  * @param value Runtime value to inspect.
5
6
  * @returns `true` when the value is a non-null plain object with an
6
7
  * `Object.prototype` or `null` prototype.
7
8
  */
8
9
  export function isPlainObject(value) {
9
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
10
- return false;
11
- }
12
- const prototype = Object.getPrototypeOf(value);
13
- return prototype === Object.prototype || prototype === null;
10
+ return isSharedPlainObject(value);
14
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-typia/project-tools",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "Project orchestration and programmatic tooling for wp-typia",
5
5
  "packageManager": "bun@1.3.11",
6
6
  "type": "module",
@@ -26,9 +26,9 @@
26
26
  "package.json"
27
27
  ],
28
28
  "scripts": {
29
- "build": "bun run --filter @wp-typia/block-runtime build && rm -rf dist && tsc -p tsconfig.runtime.json",
30
- "test": "bun run --filter @wp-typia/block-runtime build && bun run build && bun test tests/*.test.ts",
31
- "test:coverage": "bun run --filter @wp-typia/block-runtime build && bun run build && bun test tests/*.test.ts --coverage --coverage-reporter=lcov --coverage-dir=coverage",
29
+ "build": "bun run --filter @wp-typia/api-client build && bun run --filter @wp-typia/block-runtime build && rm -rf dist && tsc -p tsconfig.runtime.json",
30
+ "test": "bun run build && bun test tests/*.test.ts",
31
+ "test:coverage": "bun run build && bun test tests/*.test.ts --coverage --coverage-reporter=lcov --coverage-dir=coverage",
32
32
  "clean": "rm -rf dist",
33
33
  "prepack": "bun run build"
34
34
  },
@@ -61,10 +61,10 @@
61
61
  "bun": ">=1.3.11"
62
62
  },
63
63
  "dependencies": {
64
- "@wp-typia/api-client": "^0.4.0",
65
- "@wp-typia/block-runtime": "^0.4.0",
66
- "@wp-typia/rest": "^0.3.4",
67
- "@wp-typia/block-types": "^0.2.0",
64
+ "@wp-typia/api-client": "^0.4.2",
65
+ "@wp-typia/block-runtime": "^0.4.3",
66
+ "@wp-typia/rest": "^0.3.5",
67
+ "@wp-typia/block-types": "^0.2.1",
68
68
  "mustache": "^4.2.0",
69
69
  "npm-package-arg": "^13.0.0",
70
70
  "semver": "^7.7.3",
@@ -1,30 +1,44 @@
1
- import typia from 'typia';
2
-
3
1
  import type { ManifestDefaultsDocument } from '@wp-typia/block-runtime/defaults';
4
2
  import {
5
3
  createScaffoldValidatorToolkit,
6
4
  type ScaffoldValidatorToolkitOptions,
7
5
  } from '@wp-typia/block-runtime/validation';
8
6
 
9
- interface TemplateValidatorToolkitOptions< T extends object > {
7
+ interface TemplateValidatorFunctions< T extends object > {
8
+ assert: ScaffoldValidatorToolkitOptions< T >['assert'];
9
+ clone: ScaffoldValidatorToolkitOptions< T >['clone'];
10
+ is: ScaffoldValidatorToolkitOptions< T >['is'];
11
+ prune: ScaffoldValidatorToolkitOptions< T >['prune'];
12
+ random: ScaffoldValidatorToolkitOptions< T >['random'];
13
+ validate: ScaffoldValidatorToolkitOptions< T >['validate'];
14
+ }
15
+
16
+ interface TemplateValidatorToolkitOptions< T extends object >
17
+ extends TemplateValidatorFunctions< T > {
10
18
  finalize?: ScaffoldValidatorToolkitOptions< T >['finalize'];
11
19
  manifest: unknown;
12
20
  onValidationError?: ScaffoldValidatorToolkitOptions< T >['onValidationError'];
13
21
  }
14
22
 
15
23
  export function createTemplateValidatorToolkit< T extends object >( {
24
+ assert,
25
+ clone,
16
26
  finalize,
27
+ is,
17
28
  manifest,
18
29
  onValidationError,
30
+ prune,
31
+ random,
32
+ validate,
19
33
  }: TemplateValidatorToolkitOptions< T > ) {
20
34
  return createScaffoldValidatorToolkit< T >( {
21
35
  manifest: manifest as ManifestDefaultsDocument,
22
- validate: typia.createValidate< T >(),
23
- assert: typia.createAssert< T >(),
24
- is: typia.createIs< T >(),
25
- random: typia.createRandom< T >() as ( ...args: unknown[] ) => T,
26
- clone: typia.misc.createClone< T >() as ( value: T ) => T,
27
- prune: typia.misc.createPrune< T >(),
36
+ validate,
37
+ assert,
38
+ is,
39
+ random,
40
+ clone,
41
+ prune,
28
42
  finalize,
29
43
  onValidationError,
30
44
  } );
@@ -95,5 +95,6 @@ module.exports = async () => {
95
95
  getArtifactEntries,
96
96
  getOptionalModuleEntries: () => optionalModuleEntries,
97
97
  importTypiaWebpackPlugin: () => import("@typia/unplugin/webpack"),
98
+ projectRoot: process.cwd(),
98
99
  });
99
100
  };
@@ -137,5 +137,6 @@ module.exports = async () => {
137
137
  importTypiaWebpackPlugin: () => import( '@typia/unplugin/webpack' ),
138
138
  moduleEntriesMode: 'replace',
139
139
  nonModuleEntriesMode: 'replace',
140
+ projectRoot: process.cwd(),
140
141
  } );
141
142
  };
@@ -1,3 +1,4 @@
1
+ import typia from 'typia';
1
2
  import currentManifest from './typia.manifest.json';
2
3
  import type {
3
4
  {{pascalCase}}Attributes,
@@ -7,7 +8,16 @@ import { generateResourceKey } from '@wp-typia/block-runtime/identifiers';
7
8
  import { createTemplateValidatorToolkit } from '../../validator-toolkit';
8
9
 
9
10
  const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attributes >( {
11
+ assert: typia.createAssert< {{pascalCase}}Attributes >(),
12
+ clone: typia.misc.createClone< {{pascalCase}}Attributes >() as (
13
+ value: {{pascalCase}}Attributes,
14
+ ) => {{pascalCase}}Attributes,
15
+ is: typia.createIs< {{pascalCase}}Attributes >(),
10
16
  manifest: currentManifest,
17
+ prune: typia.misc.createPrune< {{pascalCase}}Attributes >(),
18
+ random: typia.createRandom< {{pascalCase}}Attributes >() as (
19
+ ...args: unknown[]
20
+ ) => {{pascalCase}}Attributes,
11
21
  finalize: ( normalized ) => ( {
12
22
  ...normalized,
13
23
  resourceKey:
@@ -15,6 +25,7 @@ const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attribu
15
25
  ? normalized.resourceKey
16
26
  : generateResourceKey( '{{slugKebabCase}}' ),
17
27
  } ),
28
+ validate: typia.createValidate< {{pascalCase}}Attributes >(),
18
29
  } );
19
30
 
20
31
  export const validators = scaffoldValidators.validators;
@@ -1,3 +1,4 @@
1
+ import typia from 'typia';
1
2
  import currentManifest from './typia.manifest.json';
2
3
  import type {
3
4
  {{pascalCase}}Attributes,
@@ -7,7 +8,16 @@ import { generateResourceKey } from '@wp-typia/block-runtime/identifiers';
7
8
  import { createTemplateValidatorToolkit } from './validator-toolkit';
8
9
 
9
10
  const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attributes >( {
11
+ assert: typia.createAssert< {{pascalCase}}Attributes >(),
12
+ clone: typia.misc.createClone< {{pascalCase}}Attributes >() as (
13
+ value: {{pascalCase}}Attributes,
14
+ ) => {{pascalCase}}Attributes,
15
+ is: typia.createIs< {{pascalCase}}Attributes >(),
10
16
  manifest: currentManifest,
17
+ prune: typia.misc.createPrune< {{pascalCase}}Attributes >(),
18
+ random: typia.createRandom< {{pascalCase}}Attributes >() as (
19
+ ...args: unknown[]
20
+ ) => {{pascalCase}}Attributes,
11
21
  finalize: ( normalized ) => ( {
12
22
  ...normalized,
13
23
  resourceKey:
@@ -15,6 +25,7 @@ const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attribu
15
25
  ? normalized.resourceKey
16
26
  : generateResourceKey( '{{slugKebabCase}}' ),
17
27
  } ),
28
+ validate: typia.createValidate< {{pascalCase}}Attributes >(),
18
29
  } );
19
30
 
20
31
  export const validators = scaffoldValidators.validators;
@@ -1,14 +1,25 @@
1
+ import typia from 'typia';
1
2
  import currentManifest from "./typia.manifest.json";
2
3
  import { {{pascalCase}}Attributes, {{pascalCase}}ValidationResult } from "./types";
3
4
  import { generateBlockId } from "@wp-typia/block-runtime/identifiers";
4
5
  import { createTemplateValidatorToolkit } from "./validator-toolkit";
5
6
 
6
7
  const scaffoldValidators = createTemplateValidatorToolkit<{{pascalCase}}Attributes>({
8
+ assert: typia.createAssert<{{pascalCase}}Attributes>(),
9
+ clone: typia.misc.createClone<{{pascalCase}}Attributes>() as (
10
+ value: {{pascalCase}}Attributes,
11
+ ) => {{pascalCase}}Attributes,
12
+ is: typia.createIs<{{pascalCase}}Attributes>(),
7
13
  manifest: currentManifest,
14
+ prune: typia.misc.createPrune<{{pascalCase}}Attributes>(),
15
+ random: typia.createRandom<{{pascalCase}}Attributes>() as (
16
+ ...args: unknown[]
17
+ ) => {{pascalCase}}Attributes,
8
18
  finalize: (normalized) => ({
9
19
  ...normalized,
10
20
  id: normalized.id && normalized.id.length > 0 ? normalized.id : generateBlockId(),
11
21
  }),
22
+ validate: typia.createValidate<{{pascalCase}}Attributes>(),
12
23
  });
13
24
 
14
25
  export const validate{{pascalCase}}Attributes =
@@ -1,9 +1,20 @@
1
+ import typia from 'typia';
1
2
  import currentManifest from './typia.manifest.json';
2
3
  import type { {{pascalCase}}Attributes } from './types';
3
4
  import { createTemplateValidatorToolkit } from '../../validator-toolkit';
4
5
 
5
6
  const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}Attributes >( {
7
+ assert: typia.createAssert< {{pascalCase}}Attributes >(),
8
+ clone: typia.misc.createClone< {{pascalCase}}Attributes >() as (
9
+ value: {{pascalCase}}Attributes,
10
+ ) => {{pascalCase}}Attributes,
11
+ is: typia.createIs< {{pascalCase}}Attributes >(),
6
12
  manifest: currentManifest,
13
+ prune: typia.misc.createPrune< {{pascalCase}}Attributes >(),
14
+ random: typia.createRandom< {{pascalCase}}Attributes >() as (
15
+ ...args: unknown[]
16
+ ) => {{pascalCase}}Attributes,
17
+ validate: typia.createValidate< {{pascalCase}}Attributes >(),
7
18
  } );
8
19
 
9
20
  export const validate{{pascalCase}}Attributes =
@@ -1,9 +1,20 @@
1
+ import typia from 'typia';
1
2
  import currentManifest from './typia.manifest.json';
2
3
  import type { {{pascalCase}}ItemAttributes } from './types';
3
4
  import { createTemplateValidatorToolkit } from '../../validator-toolkit';
4
5
 
5
6
  const scaffoldValidators = createTemplateValidatorToolkit< {{pascalCase}}ItemAttributes >( {
7
+ assert: typia.createAssert< {{pascalCase}}ItemAttributes >(),
8
+ clone: typia.misc.createClone< {{pascalCase}}ItemAttributes >() as (
9
+ value: {{pascalCase}}ItemAttributes,
10
+ ) => {{pascalCase}}ItemAttributes,
11
+ is: typia.createIs< {{pascalCase}}ItemAttributes >(),
6
12
  manifest: currentManifest,
13
+ prune: typia.misc.createPrune< {{pascalCase}}ItemAttributes >(),
14
+ random: typia.createRandom< {{pascalCase}}ItemAttributes >() as (
15
+ ...args: unknown[]
16
+ ) => {{pascalCase}}ItemAttributes,
17
+ validate: typia.createValidate< {{pascalCase}}ItemAttributes >(),
7
18
  } );
8
19
 
9
20
  export const validate{{pascalCase}}ItemAttributes =
@@ -1,10 +1,20 @@
1
+ import typia from 'typia';
1
2
  import currentManifest from "./typia.manifest.json";
2
3
  import { {{pascalCase}}Attributes, {{pascalCase}}ValidationResult } from "./types";
3
4
  import { generateScopedClientId } from "@wp-typia/block-runtime/identifiers";
4
5
  import { createTemplateValidatorToolkit } from "./validator-toolkit";
5
6
 
6
7
  const scaffoldValidators = createTemplateValidatorToolkit<{{pascalCase}}Attributes>({
8
+ assert: typia.createAssert<{{pascalCase}}Attributes>(),
9
+ clone: typia.misc.createClone<{{pascalCase}}Attributes>() as (
10
+ value: {{pascalCase}}Attributes,
11
+ ) => {{pascalCase}}Attributes,
12
+ is: typia.createIs<{{pascalCase}}Attributes>(),
7
13
  manifest: currentManifest,
14
+ prune: typia.misc.createPrune<{{pascalCase}}Attributes>(),
15
+ random: typia.createRandom<{{pascalCase}}Attributes>() as (
16
+ ...args: unknown[]
17
+ ) => {{pascalCase}}Attributes,
8
18
  finalize: (normalized) => ({
9
19
  ...normalized,
10
20
  uniqueId:
@@ -12,6 +22,7 @@ const scaffoldValidators = createTemplateValidatorToolkit<{{pascalCase}}Attribut
12
22
  ? normalized.uniqueId
13
23
  : generateScopedClientId("{{slugKebabCase}}"),
14
24
  }),
25
+ validate: typia.createValidate<{{pascalCase}}Attributes>(),
15
26
  });
16
27
 
17
28
  export const validate{{pascalCase}}Attributes =