@h-rig/validator-kit 0.0.6-alpha.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/README.md ADDED
@@ -0,0 +1 @@
1
+ # @h-rig/validator-kit
@@ -0,0 +1,66 @@
1
+ // @bun
2
+ // packages/validator-kit/src/checks.ts
3
+ import { existsSync as existsSync2, statSync as statSync2 } from "fs";
4
+ import { resolve as resolve2 } from "path";
5
+
6
+ // packages/validator-kit/src/fs.ts
7
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
8
+ import { resolve } from "path";
9
+ function walkDir(dir, fn) {
10
+ if (!existsSync(dir))
11
+ return;
12
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
13
+ const full = resolve(dir, entry.name);
14
+ if (entry.isDirectory()) {
15
+ if (entry.name === "node_modules" || entry.name === ".git")
16
+ continue;
17
+ walkDir(full, fn);
18
+ } else if (entry.isFile()) {
19
+ fn(full);
20
+ }
21
+ }
22
+ }
23
+ function countTestFiles(dir) {
24
+ let count = 0;
25
+ walkDir(dir, (filePath) => {
26
+ if (filePath.endsWith(".test.ts") || filePath.endsWith(".spec.ts")) {
27
+ count++;
28
+ }
29
+ });
30
+ return count;
31
+ }
32
+
33
+ // packages/validator-kit/src/checks.ts
34
+ function requireFileCheck(checker, path, checkName) {
35
+ if (existsSync2(path)) {
36
+ checker.pass(checkName);
37
+ } else {
38
+ checker.fail(checkName, `file not found: ${path}`);
39
+ }
40
+ }
41
+ function requireDirCheck(checker, path, checkName) {
42
+ if (existsSync2(path) && statSync2(path).isDirectory()) {
43
+ checker.pass(checkName);
44
+ } else {
45
+ checker.fail(checkName, `directory not found: ${path}`);
46
+ }
47
+ }
48
+ function requirePackageStructure(checker, serviceDir, prefix) {
49
+ requireFileCheck(checker, resolve2(serviceDir, "package.json"), `${prefix}-package-json`);
50
+ requireFileCheck(checker, resolve2(serviceDir, "tsconfig.json"), `${prefix}-tsconfig`);
51
+ requireDirCheck(checker, resolve2(serviceDir, "src"), `${prefix}-src-dir`);
52
+ }
53
+ function checkTestsExist(checker, projectDir, checkName) {
54
+ const count = countTestFiles(projectDir);
55
+ if (count > 0) {
56
+ checker.pass(checkName);
57
+ } else {
58
+ checker.fail(checkName, "no test files found");
59
+ }
60
+ }
61
+ export {
62
+ requirePackageStructure,
63
+ requireFileCheck,
64
+ requireDirCheck,
65
+ checkTestsExist
66
+ };
@@ -0,0 +1,81 @@
1
+ // @bun
2
+ // packages/validator-kit/src/content.ts
3
+ import { basename } from "path";
4
+
5
+ // packages/validator-kit/src/fs.ts
6
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
7
+ function readFileSafe(path) {
8
+ try {
9
+ return readFileSync(path, "utf-8");
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+
15
+ // packages/validator-kit/src/content.ts
16
+ function requireMarkdownSections(checker, filePath, sections) {
17
+ const content = readFileSafe(filePath);
18
+ if (content === null) {
19
+ checker.fail("markdown-exists", `file not found: ${filePath}`);
20
+ return;
21
+ }
22
+ for (const section of sections) {
23
+ const regex = new RegExp(`^##+ .*${section}`, "im");
24
+ if (regex.test(content)) {
25
+ checker.pass(`section:${section}`);
26
+ } else {
27
+ checker.fail(`section:${section}`, `missing section '${section}' in ${basename(filePath)}`);
28
+ }
29
+ }
30
+ }
31
+ function requireTerms(checker, filePath, terms) {
32
+ const content = readFileSafe(filePath);
33
+ if (content === null)
34
+ return;
35
+ for (const [checkName, pattern] of Object.entries(terms)) {
36
+ const regex = new RegExp(pattern, "i");
37
+ if (regex.test(content)) {
38
+ checker.pass(checkName);
39
+ } else {
40
+ checker.fail(checkName, `'${checkName}' not found in ${basename(filePath)}`);
41
+ }
42
+ }
43
+ }
44
+ function requireJsonKeys(checker, filePath, keys) {
45
+ const content = readFileSafe(filePath);
46
+ if (content === null) {
47
+ checker.fail("json-exists", `file not found: ${filePath}`);
48
+ return;
49
+ }
50
+ let data;
51
+ try {
52
+ data = JSON.parse(content);
53
+ checker.pass("json-valid");
54
+ } catch {
55
+ checker.fail("json-valid", `invalid JSON: ${filePath}`);
56
+ return;
57
+ }
58
+ for (const key of keys) {
59
+ const parts = key.replace(/^\./, "").split(".");
60
+ let current = data;
61
+ let found = true;
62
+ for (const part of parts) {
63
+ if (current && typeof current === "object" && part in current) {
64
+ current = current[part];
65
+ } else {
66
+ found = false;
67
+ break;
68
+ }
69
+ }
70
+ if (found && current !== undefined) {
71
+ checker.pass(`key:${key}`);
72
+ } else {
73
+ checker.fail(`key:${key}`, `missing key '${key}' in ${basename(filePath)}`);
74
+ }
75
+ }
76
+ }
77
+ export {
78
+ requireTerms,
79
+ requireMarkdownSections,
80
+ requireJsonKeys
81
+ };
package/dist/src/fs.js ADDED
@@ -0,0 +1,103 @@
1
+ // @bun
2
+ // packages/validator-kit/src/fs.ts
3
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
4
+ import { resolve } from "path";
5
+
6
+ // packages/validator-kit/src/result.ts
7
+ function fail(id, summary, details) {
8
+ const output = details !== undefined ? { id, passed: false, summary, details } : { id, passed: false, summary };
9
+ console.log(JSON.stringify(output));
10
+ process.exit(1);
11
+ }
12
+
13
+ // packages/validator-kit/src/fs.ts
14
+ function findFirstFile(candidates) {
15
+ for (const c of candidates) {
16
+ if (existsSync(c))
17
+ return c;
18
+ }
19
+ return null;
20
+ }
21
+ function readFileSafe(path) {
22
+ try {
23
+ return readFileSync(path, "utf-8");
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+ function requireFile(path, id, description) {
29
+ if (!existsSync(path)) {
30
+ fail(id, `Required file missing: ${description}`, path);
31
+ }
32
+ return readFileSync(path, "utf-8");
33
+ }
34
+ function fileContains(path, pattern) {
35
+ const content = readFileSafe(path);
36
+ return content !== null && pattern.test(content);
37
+ }
38
+ function walkDir(dir, fn) {
39
+ if (!existsSync(dir))
40
+ return;
41
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
42
+ const full = resolve(dir, entry.name);
43
+ if (entry.isDirectory()) {
44
+ if (entry.name === "node_modules" || entry.name === ".git")
45
+ continue;
46
+ walkDir(full, fn);
47
+ } else if (entry.isFile()) {
48
+ fn(full);
49
+ }
50
+ }
51
+ }
52
+ function listSubdirs(dir) {
53
+ if (!existsSync(dir))
54
+ return [];
55
+ return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => resolve(dir, e.name));
56
+ }
57
+ function countTsFiles(dir) {
58
+ let count = 0;
59
+ walkDir(dir, (filePath) => {
60
+ if (filePath.endsWith(".ts") && !filePath.endsWith(".test.ts") && !filePath.endsWith(".spec.ts") && !filePath.includes("/__tests__/")) {
61
+ count++;
62
+ }
63
+ });
64
+ return count;
65
+ }
66
+ function countTestFiles(dir) {
67
+ let count = 0;
68
+ walkDir(dir, (filePath) => {
69
+ if (filePath.endsWith(".test.ts") || filePath.endsWith(".spec.ts")) {
70
+ count++;
71
+ }
72
+ });
73
+ return count;
74
+ }
75
+ function findFirstDir(candidates) {
76
+ for (const c of candidates) {
77
+ if (existsSync(c) && statSync(c).isDirectory())
78
+ return c;
79
+ }
80
+ return null;
81
+ }
82
+ function findFilesByName(dir, namePattern) {
83
+ const results = [];
84
+ walkDir(dir, (filePath) => {
85
+ const base = filePath.split("/").at(-1) ?? "";
86
+ if (namePattern.test(base)) {
87
+ results.push(filePath);
88
+ }
89
+ });
90
+ return results;
91
+ }
92
+ export {
93
+ walkDir,
94
+ requireFile,
95
+ readFileSafe,
96
+ listSubdirs,
97
+ findFirstFile,
98
+ findFirstDir,
99
+ findFilesByName,
100
+ fileContains,
101
+ countTsFiles,
102
+ countTestFiles
103
+ };
@@ -0,0 +1,64 @@
1
+ // @bun
2
+ // packages/validator-kit/src/grep.ts
3
+ import { readFileSync as readFileSync2 } from "fs";
4
+
5
+ // packages/validator-kit/src/fs.ts
6
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
7
+ import { resolve } from "path";
8
+ function walkDir(dir, fn) {
9
+ if (!existsSync(dir))
10
+ return;
11
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
12
+ const full = resolve(dir, entry.name);
13
+ if (entry.isDirectory()) {
14
+ if (entry.name === "node_modules" || entry.name === ".git")
15
+ continue;
16
+ walkDir(full, fn);
17
+ } else if (entry.isFile()) {
18
+ fn(full);
19
+ }
20
+ }
21
+ }
22
+
23
+ // packages/validator-kit/src/grep.ts
24
+ function grepFiles(dir, pattern, extensions) {
25
+ const results = [];
26
+ walkDir(dir, (filePath) => {
27
+ if (extensions && !extensions.some((ext) => filePath.endsWith(ext)))
28
+ return;
29
+ try {
30
+ if (pattern.test(readFileSync2(filePath, "utf-8"))) {
31
+ results.push(filePath);
32
+ }
33
+ } catch {}
34
+ });
35
+ return results;
36
+ }
37
+ function grepCount(dir, pattern, extensions) {
38
+ return grepFiles(dir, pattern, extensions).length;
39
+ }
40
+ function grepLines(dir, pattern, extensions, maxLines = 20) {
41
+ const results = [];
42
+ walkDir(dir, (filePath) => {
43
+ if (results.length >= maxLines)
44
+ return;
45
+ if (extensions && !extensions.some((ext) => filePath.endsWith(ext)))
46
+ return;
47
+ try {
48
+ const lines = readFileSync2(filePath, "utf-8").split(/\r?\n/);
49
+ for (const line of lines) {
50
+ if (results.length >= maxLines)
51
+ break;
52
+ if (pattern.test(line)) {
53
+ results.push(`${filePath}:${line}`);
54
+ }
55
+ }
56
+ } catch {}
57
+ });
58
+ return results;
59
+ }
60
+ export {
61
+ grepLines,
62
+ grepFiles,
63
+ grepCount
64
+ };
@@ -0,0 +1,288 @@
1
+ // @bun
2
+ // packages/validator-kit/src/result.ts
3
+ function pass(id, summary) {
4
+ const output = { id, passed: true, summary };
5
+ console.log(JSON.stringify(output));
6
+ process.exit(0);
7
+ }
8
+ function fail(id, summary, details) {
9
+ const output = details !== undefined ? { id, passed: false, summary, details } : { id, passed: false, summary };
10
+ console.log(JSON.stringify(output));
11
+ process.exit(1);
12
+ }
13
+ function error(id, message) {
14
+ const output = {
15
+ id,
16
+ passed: false,
17
+ summary: `ERROR: ${message}`
18
+ };
19
+ console.log(JSON.stringify(output));
20
+ process.exit(2);
21
+ }
22
+
23
+ class Checker {
24
+ results = [];
25
+ pass(name) {
26
+ this.results.push({ name, passed: true });
27
+ }
28
+ fail(name, reason) {
29
+ this.results.push({ name, passed: false, reason });
30
+ }
31
+ fatal(id, reason) {
32
+ this.fail("fatal", reason);
33
+ this.emit(id);
34
+ }
35
+ emit(id) {
36
+ const passed = this.results.filter((r) => r.passed).length;
37
+ const failed = this.results.filter((r) => !r.passed).length;
38
+ const total = this.results.length;
39
+ const failures = this.results.filter((r) => !r.passed).map((r) => `${r.name}: ${r.reason}`).join(`
40
+ `);
41
+ const output = {
42
+ id,
43
+ passed: failed === 0,
44
+ summary: `${passed} of ${total} checks passed`,
45
+ ...failures ? { details: failures } : {}
46
+ };
47
+ console.log(JSON.stringify(output));
48
+ process.exit(failed === 0 ? 0 : 1);
49
+ }
50
+ }
51
+ // packages/validator-kit/src/fs.ts
52
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
53
+ import { resolve } from "path";
54
+ function findFirstFile(candidates) {
55
+ for (const c of candidates) {
56
+ if (existsSync(c))
57
+ return c;
58
+ }
59
+ return null;
60
+ }
61
+ function readFileSafe(path) {
62
+ try {
63
+ return readFileSync(path, "utf-8");
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+ function requireFile(path, id, description) {
69
+ if (!existsSync(path)) {
70
+ fail(id, `Required file missing: ${description}`, path);
71
+ }
72
+ return readFileSync(path, "utf-8");
73
+ }
74
+ function fileContains(path, pattern) {
75
+ const content = readFileSafe(path);
76
+ return content !== null && pattern.test(content);
77
+ }
78
+ function walkDir(dir, fn) {
79
+ if (!existsSync(dir))
80
+ return;
81
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
82
+ const full = resolve(dir, entry.name);
83
+ if (entry.isDirectory()) {
84
+ if (entry.name === "node_modules" || entry.name === ".git")
85
+ continue;
86
+ walkDir(full, fn);
87
+ } else if (entry.isFile()) {
88
+ fn(full);
89
+ }
90
+ }
91
+ }
92
+ function listSubdirs(dir) {
93
+ if (!existsSync(dir))
94
+ return [];
95
+ return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => resolve(dir, e.name));
96
+ }
97
+ function countTsFiles(dir) {
98
+ let count = 0;
99
+ walkDir(dir, (filePath) => {
100
+ if (filePath.endsWith(".ts") && !filePath.endsWith(".test.ts") && !filePath.endsWith(".spec.ts") && !filePath.includes("/__tests__/")) {
101
+ count++;
102
+ }
103
+ });
104
+ return count;
105
+ }
106
+ function countTestFiles(dir) {
107
+ let count = 0;
108
+ walkDir(dir, (filePath) => {
109
+ if (filePath.endsWith(".test.ts") || filePath.endsWith(".spec.ts")) {
110
+ count++;
111
+ }
112
+ });
113
+ return count;
114
+ }
115
+ function findFirstDir(candidates) {
116
+ for (const c of candidates) {
117
+ if (existsSync(c) && statSync(c).isDirectory())
118
+ return c;
119
+ }
120
+ return null;
121
+ }
122
+ function findFilesByName(dir, namePattern) {
123
+ const results = [];
124
+ walkDir(dir, (filePath) => {
125
+ const base = filePath.split("/").at(-1) ?? "";
126
+ if (namePattern.test(base)) {
127
+ results.push(filePath);
128
+ }
129
+ });
130
+ return results;
131
+ }
132
+ // packages/validator-kit/src/grep.ts
133
+ import { readFileSync as readFileSync2 } from "fs";
134
+ function grepFiles(dir, pattern, extensions) {
135
+ const results = [];
136
+ walkDir(dir, (filePath) => {
137
+ if (extensions && !extensions.some((ext) => filePath.endsWith(ext)))
138
+ return;
139
+ try {
140
+ if (pattern.test(readFileSync2(filePath, "utf-8"))) {
141
+ results.push(filePath);
142
+ }
143
+ } catch {}
144
+ });
145
+ return results;
146
+ }
147
+ function grepCount(dir, pattern, extensions) {
148
+ return grepFiles(dir, pattern, extensions).length;
149
+ }
150
+ function grepLines(dir, pattern, extensions, maxLines = 20) {
151
+ const results = [];
152
+ walkDir(dir, (filePath) => {
153
+ if (results.length >= maxLines)
154
+ return;
155
+ if (extensions && !extensions.some((ext) => filePath.endsWith(ext)))
156
+ return;
157
+ try {
158
+ const lines = readFileSync2(filePath, "utf-8").split(/\r?\n/);
159
+ for (const line of lines) {
160
+ if (results.length >= maxLines)
161
+ break;
162
+ if (pattern.test(line)) {
163
+ results.push(`${filePath}:${line}`);
164
+ }
165
+ }
166
+ } catch {}
167
+ });
168
+ return results;
169
+ }
170
+ // packages/validator-kit/src/content.ts
171
+ import { basename } from "path";
172
+ function requireMarkdownSections(checker, filePath, sections) {
173
+ const content = readFileSafe(filePath);
174
+ if (content === null) {
175
+ checker.fail("markdown-exists", `file not found: ${filePath}`);
176
+ return;
177
+ }
178
+ for (const section of sections) {
179
+ const regex = new RegExp(`^##+ .*${section}`, "im");
180
+ if (regex.test(content)) {
181
+ checker.pass(`section:${section}`);
182
+ } else {
183
+ checker.fail(`section:${section}`, `missing section '${section}' in ${basename(filePath)}`);
184
+ }
185
+ }
186
+ }
187
+ function requireTerms(checker, filePath, terms) {
188
+ const content = readFileSafe(filePath);
189
+ if (content === null)
190
+ return;
191
+ for (const [checkName, pattern] of Object.entries(terms)) {
192
+ const regex = new RegExp(pattern, "i");
193
+ if (regex.test(content)) {
194
+ checker.pass(checkName);
195
+ } else {
196
+ checker.fail(checkName, `'${checkName}' not found in ${basename(filePath)}`);
197
+ }
198
+ }
199
+ }
200
+ function requireJsonKeys(checker, filePath, keys) {
201
+ const content = readFileSafe(filePath);
202
+ if (content === null) {
203
+ checker.fail("json-exists", `file not found: ${filePath}`);
204
+ return;
205
+ }
206
+ let data;
207
+ try {
208
+ data = JSON.parse(content);
209
+ checker.pass("json-valid");
210
+ } catch {
211
+ checker.fail("json-valid", `invalid JSON: ${filePath}`);
212
+ return;
213
+ }
214
+ for (const key of keys) {
215
+ const parts = key.replace(/^\./, "").split(".");
216
+ let current = data;
217
+ let found = true;
218
+ for (const part of parts) {
219
+ if (current && typeof current === "object" && part in current) {
220
+ current = current[part];
221
+ } else {
222
+ found = false;
223
+ break;
224
+ }
225
+ }
226
+ if (found && current !== undefined) {
227
+ checker.pass(`key:${key}`);
228
+ } else {
229
+ checker.fail(`key:${key}`, `missing key '${key}' in ${basename(filePath)}`);
230
+ }
231
+ }
232
+ }
233
+ // packages/validator-kit/src/checks.ts
234
+ import { existsSync as existsSync2, statSync as statSync2 } from "fs";
235
+ import { resolve as resolve2 } from "path";
236
+ function requireFileCheck(checker, path, checkName) {
237
+ if (existsSync2(path)) {
238
+ checker.pass(checkName);
239
+ } else {
240
+ checker.fail(checkName, `file not found: ${path}`);
241
+ }
242
+ }
243
+ function requireDirCheck(checker, path, checkName) {
244
+ if (existsSync2(path) && statSync2(path).isDirectory()) {
245
+ checker.pass(checkName);
246
+ } else {
247
+ checker.fail(checkName, `directory not found: ${path}`);
248
+ }
249
+ }
250
+ function requirePackageStructure(checker, serviceDir, prefix) {
251
+ requireFileCheck(checker, resolve2(serviceDir, "package.json"), `${prefix}-package-json`);
252
+ requireFileCheck(checker, resolve2(serviceDir, "tsconfig.json"), `${prefix}-tsconfig`);
253
+ requireDirCheck(checker, resolve2(serviceDir, "src"), `${prefix}-src-dir`);
254
+ }
255
+ function checkTestsExist(checker, projectDir, checkName) {
256
+ const count = countTestFiles(projectDir);
257
+ if (count > 0) {
258
+ checker.pass(checkName);
259
+ } else {
260
+ checker.fail(checkName, "no test files found");
261
+ }
262
+ }
263
+ export {
264
+ walkDir,
265
+ requireTerms,
266
+ requirePackageStructure,
267
+ requireMarkdownSections,
268
+ requireJsonKeys,
269
+ requireFileCheck,
270
+ requireFile,
271
+ requireDirCheck,
272
+ readFileSafe,
273
+ pass,
274
+ listSubdirs,
275
+ grepLines,
276
+ grepFiles,
277
+ grepCount,
278
+ findFirstFile,
279
+ findFirstDir,
280
+ findFilesByName,
281
+ fileContains,
282
+ fail,
283
+ error,
284
+ countTsFiles,
285
+ countTestFiles,
286
+ checkTestsExist,
287
+ Checker
288
+ };
@@ -0,0 +1,56 @@
1
+ // @bun
2
+ // packages/validator-kit/src/result.ts
3
+ function pass(id, summary) {
4
+ const output = { id, passed: true, summary };
5
+ console.log(JSON.stringify(output));
6
+ process.exit(0);
7
+ }
8
+ function fail(id, summary, details) {
9
+ const output = details !== undefined ? { id, passed: false, summary, details } : { id, passed: false, summary };
10
+ console.log(JSON.stringify(output));
11
+ process.exit(1);
12
+ }
13
+ function error(id, message) {
14
+ const output = {
15
+ id,
16
+ passed: false,
17
+ summary: `ERROR: ${message}`
18
+ };
19
+ console.log(JSON.stringify(output));
20
+ process.exit(2);
21
+ }
22
+
23
+ class Checker {
24
+ results = [];
25
+ pass(name) {
26
+ this.results.push({ name, passed: true });
27
+ }
28
+ fail(name, reason) {
29
+ this.results.push({ name, passed: false, reason });
30
+ }
31
+ fatal(id, reason) {
32
+ this.fail("fatal", reason);
33
+ this.emit(id);
34
+ }
35
+ emit(id) {
36
+ const passed = this.results.filter((r) => r.passed).length;
37
+ const failed = this.results.filter((r) => !r.passed).length;
38
+ const total = this.results.length;
39
+ const failures = this.results.filter((r) => !r.passed).map((r) => `${r.name}: ${r.reason}`).join(`
40
+ `);
41
+ const output = {
42
+ id,
43
+ passed: failed === 0,
44
+ summary: `${passed} of ${total} checks passed`,
45
+ ...failures ? { details: failures } : {}
46
+ };
47
+ console.log(JSON.stringify(output));
48
+ process.exit(failed === 0 ? 0 : 1);
49
+ }
50
+ }
51
+ export {
52
+ pass,
53
+ fail,
54
+ error,
55
+ Checker
56
+ };
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@h-rig/validator-kit",
3
+ "version": "0.0.6-alpha.0",
4
+ "type": "module",
5
+ "description": "Rig package",
6
+ "license": "UNLICENSED",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/src/index.js"
14
+ }
15
+ },
16
+ "engines": {
17
+ "bun": ">=1.3.11"
18
+ },
19
+ "main": "./dist/src/index.js",
20
+ "module": "./dist/src/index.js",
21
+ "dependencies": {
22
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.0",
23
+ "effect": "4.0.0-beta.78"
24
+ }
25
+ }