@wp-typia/block-runtime 0.2.3 → 0.3.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.
@@ -1 +1,206 @@
1
- export { createAttributeUpdater, createNestedAttributeUpdater, formatValidationError, formatValidationErrors, mergeNestedAttributeUpdate, normalizeValidationError, toAttributePatch, toNestedAttributePatch, toValidationResult, toValidationState, } from "@wp-typia/create/runtime/validation";
1
+ import { applyTemplateDefaultsFromManifest } from "./defaults.js";
2
+ import { isPlainObject as isRecord } from "./object-utils.js";
3
+ const UNSAFE_PATH_SEGMENTS = new Set(["__proto__", "constructor", "prototype"]);
4
+ function getValueType(value) {
5
+ if (value === null) {
6
+ return "null";
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return "array";
10
+ }
11
+ return typeof value;
12
+ }
13
+ function redactValidationErrors(errors) {
14
+ return errors.map(({ description, expected, path }) => ({
15
+ description,
16
+ expected,
17
+ path,
18
+ }));
19
+ }
20
+ export function normalizeValidationError(error) {
21
+ const raw = error !== null && typeof error === "object"
22
+ ? error
23
+ : {};
24
+ return {
25
+ description: typeof raw.description === "string" ? raw.description : undefined,
26
+ expected: typeof raw.expected === "string" ? raw.expected : "unknown",
27
+ path: typeof raw.path === "string" && raw.path.length > 0 ? raw.path : "(root)",
28
+ value: Object.prototype.hasOwnProperty.call(raw, "value") ? raw.value : undefined,
29
+ };
30
+ }
31
+ export function toValidationResult(result) {
32
+ const raw = result !== null && typeof result === "object"
33
+ ? result
34
+ : undefined;
35
+ if (raw?.success === true) {
36
+ return {
37
+ data: raw.data,
38
+ errors: [],
39
+ isValid: true,
40
+ };
41
+ }
42
+ const rawErrors = Array.isArray(raw?.errors) ? raw.errors : [];
43
+ return {
44
+ data: undefined,
45
+ errors: rawErrors.map(normalizeValidationError),
46
+ isValid: false,
47
+ };
48
+ }
49
+ export function formatValidationError(error) {
50
+ return `${error.path}: ${error.expected} expected, got ${getValueType(error.value)}`;
51
+ }
52
+ export function formatValidationErrors(errors) {
53
+ return errors.map(formatValidationError);
54
+ }
55
+ export function toValidationState(result) {
56
+ return {
57
+ ...result,
58
+ errorMessages: formatValidationErrors(result.errors),
59
+ };
60
+ }
61
+ export function createUseTypiaValidationHook({ useMemo, }) {
62
+ return function useTypiaValidation(value, validator) {
63
+ return useMemo(() => toValidationState(validator(value)), [value, validator]);
64
+ };
65
+ }
66
+ export function createScaffoldValidatorToolkit({ assert, clone, finalize, is, manifest, onValidationError = (validation, key) => {
67
+ console.error(`Validation failed for ${String(key)}:`, redactValidationErrors(validation.errors));
68
+ }, prune, random, validate, }) {
69
+ const validateAttributes = (value) => toValidationResult(validate(value));
70
+ const normalizeAttributes = (value) => {
71
+ const normalized = applyTemplateDefaultsFromManifest(manifest, value);
72
+ return finalize ? finalize(normalized) : normalized;
73
+ };
74
+ const sanitizeAttributes = (value) => assert(normalizeAttributes(value));
75
+ const validateSanitizedAttributes = (value) => {
76
+ const normalized = normalizeAttributes(value);
77
+ try {
78
+ const data = assert(normalized);
79
+ return {
80
+ data,
81
+ errors: [],
82
+ isValid: true,
83
+ };
84
+ }
85
+ catch {
86
+ return validateAttributes(value);
87
+ }
88
+ };
89
+ const createScaffoldAttributeUpdater = (attributes, setAttributes, validator = validateSanitizedAttributes) => createAttributeUpdater(attributes, setAttributes, validator, onValidationError);
90
+ return {
91
+ createAttributeUpdater: createScaffoldAttributeUpdater,
92
+ sanitizeAttributes,
93
+ validateAttributes,
94
+ validators: {
95
+ assert,
96
+ clone,
97
+ is,
98
+ prune,
99
+ random,
100
+ validate: validateAttributes,
101
+ },
102
+ };
103
+ }
104
+ function toChangedAttributePatch(attributes, nextAttributes) {
105
+ const patch = {};
106
+ const keys = new Set([
107
+ ...Object.keys(attributes),
108
+ ...Object.keys(nextAttributes),
109
+ ]);
110
+ for (const key of keys) {
111
+ if (!(key in nextAttributes)) {
112
+ patch[key] = undefined;
113
+ continue;
114
+ }
115
+ if (!Object.is(attributes[key], nextAttributes[key])) {
116
+ patch[key] = nextAttributes[key];
117
+ }
118
+ }
119
+ return patch;
120
+ }
121
+ function mergeAttributeUpdate(attributes, key, value) {
122
+ return {
123
+ ...attributes,
124
+ [key]: value,
125
+ };
126
+ }
127
+ function getNestedPathSegments(path) {
128
+ const segments = path.split(".").filter(Boolean);
129
+ if (segments.length === 0) {
130
+ throw new Error("Nested attribute paths must contain at least one segment.");
131
+ }
132
+ if (segments.some((segment) => UNSAFE_PATH_SEGMENTS.has(segment))) {
133
+ throw new Error(`Unsupported nested attribute path: ${path}`);
134
+ }
135
+ return segments;
136
+ }
137
+ function updateNestedRecord(current, segments, value) {
138
+ const [head, ...rest] = segments;
139
+ if (typeof head !== "string") {
140
+ return current ?? {};
141
+ }
142
+ if (rest.length === 0) {
143
+ return {
144
+ ...(current ?? {}),
145
+ [head]: value,
146
+ };
147
+ }
148
+ const nextCurrent = current && isRecord(current[head]) ? current[head] : undefined;
149
+ return {
150
+ ...(current ?? {}),
151
+ [head]: updateNestedRecord(nextCurrent, rest, value),
152
+ };
153
+ }
154
+ export function createAttributeUpdater(attributes, setAttributes, validate, onValidationError) {
155
+ return (key, value) => {
156
+ const nextAttributes = mergeAttributeUpdate(attributes, key, value);
157
+ const validation = validate(nextAttributes);
158
+ if (validation.isValid) {
159
+ const patch = validation.data && typeof validation.data === "object"
160
+ ? toChangedAttributePatch(attributes, validation.data)
161
+ : toAttributePatch(key, value);
162
+ setAttributes(patch);
163
+ return true;
164
+ }
165
+ onValidationError?.(validation, key);
166
+ return false;
167
+ };
168
+ }
169
+ export function mergeNestedAttributeUpdate(attributes, path, value) {
170
+ return {
171
+ ...attributes,
172
+ ...toNestedAttributePatch(attributes, path, value),
173
+ };
174
+ }
175
+ export function toAttributePatch(key, value) {
176
+ return {
177
+ [key]: value,
178
+ };
179
+ }
180
+ export function toNestedAttributePatch(attributes, path, value) {
181
+ const [root, ...rest] = getNestedPathSegments(path);
182
+ if (rest.length === 0) {
183
+ return toAttributePatch(root, value);
184
+ }
185
+ const currentRoot = isRecord(attributes[root])
186
+ ? attributes[root]
187
+ : undefined;
188
+ return {
189
+ [root]: updateNestedRecord(currentRoot, rest, value),
190
+ };
191
+ }
192
+ export function createNestedAttributeUpdater(attributes, setAttributes, validate, onValidationError) {
193
+ return (path, value) => {
194
+ const nextAttributes = mergeNestedAttributeUpdate(attributes, path, value);
195
+ const validation = validate(nextAttributes);
196
+ if (validation.isValid) {
197
+ const patch = validation.data && typeof validation.data === "object"
198
+ ? toChangedAttributePatch(attributes, validation.data)
199
+ : toNestedAttributePatch(attributes, path, value);
200
+ setAttributes(patch);
201
+ return true;
202
+ }
203
+ onValidationError?.(validation, path);
204
+ return false;
205
+ };
206
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wp-typia/block-runtime",
3
- "version": "0.2.3",
4
- "description": "Prototype block runtime facade for wp-typia generated-project helpers",
3
+ "version": "0.3.0",
4
+ "description": "Generated-project runtime and metadata sync helpers for wp-typia",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -11,6 +11,16 @@
11
11
  "import": "./dist/index.js",
12
12
  "default": "./dist/index.js"
13
13
  },
14
+ "./blocks": {
15
+ "types": "./dist/blocks.d.ts",
16
+ "import": "./dist/blocks.js",
17
+ "default": "./dist/blocks.js"
18
+ },
19
+ "./metadata-core": {
20
+ "types": "./dist/metadata-core.d.ts",
21
+ "import": "./dist/metadata-core.js",
22
+ "default": "./dist/metadata-core.js"
23
+ },
14
24
  "./defaults": {
15
25
  "types": "./dist/defaults.d.ts",
16
26
  "import": "./dist/defaults.js",
@@ -41,7 +51,7 @@
41
51
  "scripts": {
42
52
  "build": "rm -rf dist && tsc -p tsconfig.build.json",
43
53
  "clean": "rm -rf dist",
44
- "test:prepare": "bun run --filter @wp-typia/create build && bun run build",
54
+ "test:prepare": "bun run build",
45
55
  "test": "bun run test:prepare && bun test tests",
46
56
  "test:coverage": "bun run test:prepare && bun test tests --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage",
47
57
  "typecheck": "tsc -p tsconfig.json --noEmit"
@@ -74,9 +84,10 @@
74
84
  }
75
85
  },
76
86
  "dependencies": {
77
- "@wp-typia/create": "^0.10.1"
87
+ "typescript": "^5.9.2"
78
88
  },
79
89
  "devDependencies": {
80
- "typescript": "^5.9.2"
90
+ "react": "^19.2.0",
91
+ "typia": "^12.0.1"
81
92
  }
82
93
  }