@urbicon-ui/mcp-server 6.1.4
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 +161 -0
- package/dist/data/catalog-loader.d.ts +37 -0
- package/dist/data/catalog-loader.d.ts.map +1 -0
- package/dist/data/catalog-loader.js +15 -0
- package/dist/data/catalog-loader.js.map +1 -0
- package/dist/data/component-loader.d.ts +2 -0
- package/dist/data/component-loader.d.ts.map +1 -0
- package/dist/data/component-loader.js +17 -0
- package/dist/data/component-loader.js.map +1 -0
- package/dist/data/recipe-loader.d.ts +4 -0
- package/dist/data/recipe-loader.d.ts.map +1 -0
- package/dist/data/recipe-loader.js +102 -0
- package/dist/data/recipe-loader.js.map +1 -0
- package/dist/data/template-loader.d.ts +8 -0
- package/dist/data/template-loader.d.ts.map +1 -0
- package/dist/data/template-loader.js +33 -0
- package/dist/data/template-loader.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/catalog.d.ts +3 -0
- package/dist/resources/catalog.d.ts.map +1 -0
- package/dist/resources/catalog.js +20 -0
- package/dist/resources/catalog.js.map +1 -0
- package/dist/resources/component.d.ts +3 -0
- package/dist/resources/component.d.ts.map +1 -0
- package/dist/resources/component.js +29 -0
- package/dist/resources/component.js.map +1 -0
- package/dist/resources/guides.d.ts +3 -0
- package/dist/resources/guides.d.ts.map +1 -0
- package/dist/resources/guides.js +36 -0
- package/dist/resources/guides.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/find-components.d.ts +3 -0
- package/dist/tools/find-components.d.ts.map +1 -0
- package/dist/tools/find-components.js +21 -0
- package/dist/tools/find-components.js.map +1 -0
- package/dist/tools/get-recipe.d.ts +3 -0
- package/dist/tools/get-recipe.d.ts.map +1 -0
- package/dist/tools/get-recipe.js +48 -0
- package/dist/tools/get-recipe.js.map +1 -0
- package/dist/tools/suggest-implementation.d.ts +3 -0
- package/dist/tools/suggest-implementation.d.ts.map +1 -0
- package/dist/tools/suggest-implementation.js +178 -0
- package/dist/tools/suggest-implementation.js.map +1 -0
- package/dist/transports/http.d.ts +2 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +77 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +3 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +6 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils/format-catalog.d.ts +7 -0
- package/dist/utils/format-catalog.d.ts.map +1 -0
- package/dist/utils/format-catalog.js +93 -0
- package/dist/utils/format-catalog.js.map +1 -0
- package/dist/utils/paths.d.ts +7 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +23 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/search.d.ts +3 -0
- package/dist/utils/search.d.ts.map +1 -0
- package/dist/utils/search.js +44 -0
- package/dist/utils/search.js.map +1 -0
- package/package.json +42 -0
- package/src/data/catalog-loader.test.ts +42 -0
- package/src/data/catalog-loader.ts +78 -0
- package/src/data/component-loader.ts +68 -0
- package/src/data/design-system-loader.test.ts +82 -0
- package/src/data/design-system-loader.ts +125 -0
- package/src/data/icon-loader.test.ts +85 -0
- package/src/data/icon-loader.ts +90 -0
- package/src/data/recipe-loader.test.ts +49 -0
- package/src/data/recipe-loader.ts +131 -0
- package/src/data/template-loader.ts +55 -0
- package/src/design-linter/heuristics.ts +162 -0
- package/src/design-linter/index.ts +14 -0
- package/src/design-linter/linter.test.ts +257 -0
- package/src/design-linter/linter.ts +62 -0
- package/src/design-linter/rules.ts +348 -0
- package/src/design-linter/tokens.test.ts +80 -0
- package/src/design-linter/tokens.ts +203 -0
- package/src/design-linter/types.ts +66 -0
- package/src/design-manifest/index.ts +20 -0
- package/src/design-manifest/manifest.test.ts +175 -0
- package/src/design-manifest/manifest.ts +250 -0
- package/src/design-manifest/scan.test.ts +51 -0
- package/src/design-manifest/scan.ts +74 -0
- package/src/design-manifest/types.ts +40 -0
- package/src/design-rubric/rubric.test.ts +43 -0
- package/src/design-rubric/rubric.ts +140 -0
- package/src/eval/briefs.ts +104 -0
- package/src/eval/eval.test.ts +99 -0
- package/src/eval/index.ts +11 -0
- package/src/eval/score.ts +112 -0
- package/src/index.ts +75 -0
- package/src/prompts/design-prompts.test.ts +51 -0
- package/src/prompts/design-prompts.ts +127 -0
- package/src/resources/catalog.ts +23 -0
- package/src/resources/guides.ts +60 -0
- package/src/server.test.ts +69 -0
- package/src/server.ts +48 -0
- package/src/tools/find-components.ts +83 -0
- package/src/tools/find-icons.ts +77 -0
- package/src/tools/get-checklist.ts +139 -0
- package/src/tools/get-component.ts +204 -0
- package/src/tools/get-css-reference.ts +446 -0
- package/src/tools/get-design-context.ts +43 -0
- package/src/tools/get-design-principles.ts +72 -0
- package/src/tools/get-pattern.ts +69 -0
- package/src/tools/get-recipe.ts +80 -0
- package/src/tools/record-design-decision.ts +99 -0
- package/src/tools/suggest-implementation.ts +251 -0
- package/src/tools/sync-design-manifest.ts +92 -0
- package/src/tools/validate-design.ts +84 -0
- package/src/transports/http.ts +79 -0
- package/src/transports/stdio.ts +7 -0
- package/src/utils/format-catalog.test.ts +144 -0
- package/src/utils/format-catalog.ts +130 -0
- package/src/utils/paths.test.ts +101 -0
- package/src/utils/paths.ts +78 -0
- package/src/utils/search.test.ts +141 -0
- package/src/utils/search.ts +106 -0
- package/tsconfig.json +27 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { dirname, normalize, resolve, sep } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const packageRoot = resolve(__dirname, '..', '..');
|
|
6
|
+
|
|
7
|
+
export function getDataDir(): string {
|
|
8
|
+
return process.env.DATA_DIR ?? resolve(packageRoot, '..', '..', 'apps', 'docs', 'static');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getTemplateDir(): string {
|
|
12
|
+
return resolve(packageRoot, '..', 'docs-gen', 'templates');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getRecipeDir(): string {
|
|
16
|
+
return resolve(packageRoot, '..', '..', 'apps', 'docs', 'src', 'routes', 'recipes');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getDesignSystemDir(): string {
|
|
20
|
+
return process.env.DESIGN_SYSTEM_DIR ?? resolve(packageRoot, '..', '..', 'design-system');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Root of the *consumer* project (the app being built with Urbicon UI), used by
|
|
25
|
+
* the design-context tools. Unlike the loaders above — which read the library's
|
|
26
|
+
* own shipped data — these tools read/write the consumer's `design.manifest.md`
|
|
27
|
+
* and scan its source, so the default is the process cwd (the consumer's repo
|
|
28
|
+
* when the server is launched from their editor). Override with env vars.
|
|
29
|
+
*/
|
|
30
|
+
export function getProjectDir(): string {
|
|
31
|
+
return process.env.DESIGN_PROJECT_DIR ?? process.cwd();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getProjectManifestPath(): string {
|
|
35
|
+
return process.env.DESIGN_MANIFEST_PATH ?? resolve(getProjectDir(), 'design.manifest.md');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getProjectSourceDir(): string {
|
|
39
|
+
return process.env.DESIGN_SOURCE_DIR ?? resolve(getProjectDir(), 'src');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Containment check for a caller-supplied manifest path. The write tools accept
|
|
44
|
+
* an LLM-supplied `manifestPath`; restrict it to the project root so a model
|
|
45
|
+
* cannot be steered into writing `.md` files elsewhere. (The user-configured
|
|
46
|
+
* `DESIGN_MANIFEST_PATH` env default is trusted and not run through this.)
|
|
47
|
+
*/
|
|
48
|
+
export function isWithinProjectDir(targetPath: string): boolean {
|
|
49
|
+
const resolved = resolve(targetPath);
|
|
50
|
+
const projectDir = normalize(getProjectDir());
|
|
51
|
+
return resolved === projectDir || resolved.startsWith(projectDir + sep);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function getTemplatePath(): string {
|
|
55
|
+
return resolve(getTemplateDir(), 'llms-full-template.md');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getCatalogPath(): string {
|
|
59
|
+
return resolve(getDataDir(), 'mcp', 'component-catalog.json');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Validate that a slug contains only safe characters (lowercase alphanumeric + hyphens). */
|
|
63
|
+
const SAFE_SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
64
|
+
|
|
65
|
+
export function getComponentLlmPath(group: string, slug: string): string {
|
|
66
|
+
if (!SAFE_SLUG.test(slug)) {
|
|
67
|
+
throw new Error(`Invalid component slug: "${slug}"`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const resolved = resolve(getDataDir(), group, slug, 'llm.txt');
|
|
71
|
+
const dataDir = normalize(getDataDir());
|
|
72
|
+
|
|
73
|
+
if (!resolved.startsWith(dataDir)) {
|
|
74
|
+
throw new Error(`Path traversal blocked for slug: "${slug}"`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return resolved;
|
|
78
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { ComponentCatalogEntry } from '../data/catalog-loader.js';
|
|
3
|
+
import { matchComponents } from './search.js';
|
|
4
|
+
|
|
5
|
+
function makeEntry(
|
|
6
|
+
overrides: Partial<ComponentCatalogEntry> & Pick<ComponentCatalogEntry, 'name' | 'slug'>
|
|
7
|
+
): ComponentCatalogEntry {
|
|
8
|
+
return {
|
|
9
|
+
package: '@urbicon-ui/blocks',
|
|
10
|
+
group: 'primitives',
|
|
11
|
+
description: '',
|
|
12
|
+
tags: [],
|
|
13
|
+
import: `import { ${overrides.name} } from '@urbicon-ui/blocks';`,
|
|
14
|
+
llmTxtPath: '',
|
|
15
|
+
variants: [],
|
|
16
|
+
keyProps: [],
|
|
17
|
+
keyPropTypes: {},
|
|
18
|
+
slots: [],
|
|
19
|
+
hasExamples: false,
|
|
20
|
+
relatedComponents: [],
|
|
21
|
+
...overrides
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const Button = makeEntry({
|
|
26
|
+
name: 'Button',
|
|
27
|
+
slug: 'button',
|
|
28
|
+
description: 'Click to trigger an action',
|
|
29
|
+
tags: ['action'],
|
|
30
|
+
keyProps: ['intent', 'variant', 'size']
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const Input = makeEntry({
|
|
34
|
+
name: 'Input',
|
|
35
|
+
slug: 'input',
|
|
36
|
+
description: 'Single-line text field',
|
|
37
|
+
tags: ['form'],
|
|
38
|
+
keyProps: ['value', 'error']
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const Dialog = makeEntry({
|
|
42
|
+
name: 'Dialog',
|
|
43
|
+
slug: 'dialog',
|
|
44
|
+
description: 'Modal overlay container',
|
|
45
|
+
tags: ['overlay']
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const catalog = [Button, Input, Dialog];
|
|
49
|
+
|
|
50
|
+
describe('matchComponents', () => {
|
|
51
|
+
it('ranks an exact name match first', () => {
|
|
52
|
+
const results = matchComponents(catalog, 'button');
|
|
53
|
+
expect(results[0]?.name).toBe('Button');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('matches by case-insensitive substring of the name', () => {
|
|
57
|
+
const results = matchComponents(catalog, 'Butt');
|
|
58
|
+
expect(results.some((r) => r.name === 'Button')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('fuzz-matches a single-character typo', () => {
|
|
62
|
+
const results = matchComponents(catalog, 'Buton'); // typo: missing "t"
|
|
63
|
+
expect(results[0]?.name).toBe('Button');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('ignores matches that are too distant', () => {
|
|
67
|
+
const results = matchComponents(catalog, 'xyzzy');
|
|
68
|
+
expect(results).toEqual([]);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('scores tag matches when the tag keyword is present in the query', () => {
|
|
72
|
+
const results = matchComponents(catalog, 'form');
|
|
73
|
+
expect(results[0]?.name).toBe('Input');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('filters by the explicit tags argument', () => {
|
|
77
|
+
const results = matchComponents(catalog, 'container', ['overlay']);
|
|
78
|
+
expect(results.some((r) => r.name === 'Dialog')).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('respects the limit', () => {
|
|
82
|
+
const many = Array.from({ length: 10 }, (_, i) =>
|
|
83
|
+
makeEntry({ name: `Button${i}`, slug: `button-${i}`, description: 'click' })
|
|
84
|
+
);
|
|
85
|
+
const results = matchComponents(many, 'button', undefined, 3);
|
|
86
|
+
expect(results).toHaveLength(3);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('drops words shorter than two characters before matching', () => {
|
|
90
|
+
// "a" and "x" below are too short and should be filtered;
|
|
91
|
+
// only "button" remains and should drive the match.
|
|
92
|
+
const results = matchComponents(catalog, 'a x button');
|
|
93
|
+
expect(results[0]?.name).toBe('Button');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('uses the prop-name match as a weak signal', () => {
|
|
97
|
+
const results = matchComponents(catalog, 'intent');
|
|
98
|
+
expect(results.some((r) => r.name === 'Button')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('returns an empty list when the query has no usable keywords', () => {
|
|
102
|
+
const results = matchComponents(catalog, ', , ');
|
|
103
|
+
expect(results).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Regression for the DateGrid/Planner discovery fix: planning-board queries
|
|
108
|
+
// used to steer toward Calendar (a timed-event scheduler). With Planner in the
|
|
109
|
+
// catalog they must land on Planner instead — driven purely by its catalog
|
|
110
|
+
// description/tags/slug, no hardcoded keyword map.
|
|
111
|
+
describe('matchComponents — Planner discovery', () => {
|
|
112
|
+
const Calendar = makeEntry({
|
|
113
|
+
name: 'Calendar',
|
|
114
|
+
slug: 'calendar',
|
|
115
|
+
description: 'Event display and date selection with month, week and day views.',
|
|
116
|
+
tags: ['display'],
|
|
117
|
+
relatedComponents: ['DatePicker']
|
|
118
|
+
});
|
|
119
|
+
const Planner = makeEntry({
|
|
120
|
+
name: 'Planner',
|
|
121
|
+
slug: 'planner',
|
|
122
|
+
description:
|
|
123
|
+
'Date-indexed planning board — a week, month or custom-range grid whose cells hold your domain content (meals, shifts, bookings, content slots) via a generic cell snippet.',
|
|
124
|
+
tags: ['display', 'layout'],
|
|
125
|
+
keyProps: ['items', 'getDate', 'view', 'cell'],
|
|
126
|
+
relatedComponents: ['Calendar', 'DatePicker']
|
|
127
|
+
});
|
|
128
|
+
const dateCatalog = [Calendar, Planner];
|
|
129
|
+
|
|
130
|
+
for (const query of ['planner', 'meal planner', 'weekly plan', 'shift schedule', 'week board']) {
|
|
131
|
+
it(`ranks Planner first for "${query}"`, () => {
|
|
132
|
+
const results = matchComponents(dateCatalog, query);
|
|
133
|
+
expect(results[0]?.name).toBe('Planner');
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
it('still ranks Calendar first for an event/appointment query', () => {
|
|
138
|
+
const results = matchComponents(dateCatalog, 'event calendar');
|
|
139
|
+
expect(results[0]?.name).toBe('Calendar');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { ComponentCatalogEntry } from '../data/catalog-loader.js';
|
|
2
|
+
|
|
3
|
+
interface ScoredEntry {
|
|
4
|
+
entry: ComponentCatalogEntry;
|
|
5
|
+
score: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function matchComponents(
|
|
9
|
+
components: ComponentCatalogEntry[],
|
|
10
|
+
query: string,
|
|
11
|
+
tags?: string[],
|
|
12
|
+
limit = 5
|
|
13
|
+
): ComponentCatalogEntry[] {
|
|
14
|
+
const keywords = query
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.split(/[\s,\-_]+/)
|
|
17
|
+
.filter((w) => w.length > 1);
|
|
18
|
+
|
|
19
|
+
const scored: ScoredEntry[] = components.map((entry) => {
|
|
20
|
+
let score = 0;
|
|
21
|
+
const nameLower = entry.name.toLowerCase();
|
|
22
|
+
const slugLower = entry.slug.toLowerCase();
|
|
23
|
+
const descLower = entry.description.toLowerCase();
|
|
24
|
+
|
|
25
|
+
for (const kw of keywords) {
|
|
26
|
+
// Exact match
|
|
27
|
+
if (nameLower === kw || slugLower === kw) {
|
|
28
|
+
score += 10;
|
|
29
|
+
} else if (nameLower.includes(kw) || slugLower.includes(kw)) {
|
|
30
|
+
score += 7;
|
|
31
|
+
} else {
|
|
32
|
+
// Fuzzy match on name/slug (Levenshtein distance <= 2)
|
|
33
|
+
const nameDist = levenshtein(nameLower, kw);
|
|
34
|
+
const slugDist = levenshtein(slugLower, kw);
|
|
35
|
+
const minDist = Math.min(nameDist, slugDist);
|
|
36
|
+
if (minDist <= 1) {
|
|
37
|
+
score += 6;
|
|
38
|
+
} else if (minDist <= 2) {
|
|
39
|
+
score += 3;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Tag match
|
|
44
|
+
if (entry.tags.some((t) => t.toLowerCase() === kw)) {
|
|
45
|
+
score += 5;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Description match
|
|
49
|
+
if (descLower.includes(kw)) {
|
|
50
|
+
score += 3;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Prop name match
|
|
54
|
+
if (entry.keyProps.some((p) => p.toLowerCase().includes(kw))) {
|
|
55
|
+
score += 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (tags && tags.length > 0) {
|
|
60
|
+
const entryTags = entry.tags.map((t) => t.toLowerCase());
|
|
61
|
+
for (const tag of tags) {
|
|
62
|
+
if (entryTags.includes(tag.toLowerCase())) {
|
|
63
|
+
score += 5;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { entry, score };
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return scored
|
|
72
|
+
.filter((s) => s.score > 0)
|
|
73
|
+
.sort((a, b) => b.score - a.score)
|
|
74
|
+
.slice(0, limit)
|
|
75
|
+
.map((s) => s.entry);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function levenshtein(a: string, b: string): number {
|
|
79
|
+
if (a.length === 0) return b.length;
|
|
80
|
+
if (b.length === 0) return a.length;
|
|
81
|
+
|
|
82
|
+
// Early exit for large length differences
|
|
83
|
+
if (Math.abs(a.length - b.length) > 2) return 3;
|
|
84
|
+
|
|
85
|
+
const matrix: number[][] = [];
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i <= a.length; i++) {
|
|
88
|
+
matrix[i] = [i];
|
|
89
|
+
}
|
|
90
|
+
for (let j = 0; j <= b.length; j++) {
|
|
91
|
+
matrix[0]![j] = j;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (let i = 1; i <= a.length; i++) {
|
|
95
|
+
for (let j = 1; j <= b.length; j++) {
|
|
96
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
97
|
+
matrix[i]![j] = Math.min(
|
|
98
|
+
matrix[i - 1]![j]! + 1,
|
|
99
|
+
matrix[i]![j - 1]! + 1,
|
|
100
|
+
matrix[i - 1]![j - 1]! + cost
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return matrix[a.length]![b.length]!;
|
|
106
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"composite": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"resolveJsonModule": true,
|
|
18
|
+
"verbatimModuleSyntax": true,
|
|
19
|
+
"noImplicitReturns": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedIndexedAccess": true,
|
|
22
|
+
"types": ["node"],
|
|
23
|
+
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
|
24
|
+
},
|
|
25
|
+
"include": ["src/**/*"],
|
|
26
|
+
"exclude": ["node_modules", "dist"]
|
|
27
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
// zod@4's ESM layout needs to be inlined so vitest's vite-ssr runner
|
|
6
|
+
// resolves the named export correctly; otherwise `z.string` comes back
|
|
7
|
+
// as undefined inside the tool modules.
|
|
8
|
+
server: {
|
|
9
|
+
deps: {
|
|
10
|
+
inline: ['zod']
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
include: ['src/**/*.test.ts']
|
|
14
|
+
}
|
|
15
|
+
});
|