@raystack/chronicle 0.1.0-canary.0efaef0

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.
Files changed (115) hide show
  1. package/bin/chronicle.js +2 -0
  2. package/dist/cli/index.js +276 -0
  3. package/package.json +71 -0
  4. package/src/cli/commands/build.ts +34 -0
  5. package/src/cli/commands/dev.ts +32 -0
  6. package/src/cli/commands/init.ts +69 -0
  7. package/src/cli/commands/serve.ts +40 -0
  8. package/src/cli/commands/start.ts +28 -0
  9. package/src/cli/index.ts +21 -0
  10. package/src/cli/utils/config.ts +42 -0
  11. package/src/cli/utils/index.ts +3 -0
  12. package/src/cli/utils/resolve.ts +10 -0
  13. package/src/cli/utils/scaffold.ts +18 -0
  14. package/src/components/api/code-snippets.module.css +7 -0
  15. package/src/components/api/code-snippets.tsx +76 -0
  16. package/src/components/api/endpoint-page.module.css +58 -0
  17. package/src/components/api/endpoint-page.tsx +283 -0
  18. package/src/components/api/field-row.module.css +126 -0
  19. package/src/components/api/field-row.tsx +204 -0
  20. package/src/components/api/field-section.module.css +24 -0
  21. package/src/components/api/field-section.tsx +100 -0
  22. package/src/components/api/index.ts +8 -0
  23. package/src/components/api/json-editor.module.css +9 -0
  24. package/src/components/api/json-editor.tsx +61 -0
  25. package/src/components/api/key-value-editor.module.css +13 -0
  26. package/src/components/api/key-value-editor.tsx +62 -0
  27. package/src/components/api/method-badge.module.css +4 -0
  28. package/src/components/api/method-badge.tsx +29 -0
  29. package/src/components/api/response-panel.module.css +8 -0
  30. package/src/components/api/response-panel.tsx +44 -0
  31. package/src/components/common/breadcrumb.tsx +3 -0
  32. package/src/components/common/button.tsx +3 -0
  33. package/src/components/common/callout.module.css +7 -0
  34. package/src/components/common/callout.tsx +27 -0
  35. package/src/components/common/code-block.tsx +3 -0
  36. package/src/components/common/dialog.tsx +3 -0
  37. package/src/components/common/index.ts +10 -0
  38. package/src/components/common/input-field.tsx +3 -0
  39. package/src/components/common/sidebar.tsx +3 -0
  40. package/src/components/common/switch.tsx +3 -0
  41. package/src/components/common/table.tsx +3 -0
  42. package/src/components/common/tabs.tsx +3 -0
  43. package/src/components/mdx/code.module.css +42 -0
  44. package/src/components/mdx/code.tsx +27 -0
  45. package/src/components/mdx/details.module.css +37 -0
  46. package/src/components/mdx/details.tsx +18 -0
  47. package/src/components/mdx/image.tsx +9 -0
  48. package/src/components/mdx/index.tsx +35 -0
  49. package/src/components/mdx/link.tsx +41 -0
  50. package/src/components/mdx/mermaid.module.css +9 -0
  51. package/src/components/mdx/mermaid.tsx +37 -0
  52. package/src/components/mdx/paragraph.module.css +8 -0
  53. package/src/components/mdx/paragraph.tsx +19 -0
  54. package/src/components/mdx/table.tsx +40 -0
  55. package/src/components/ui/breadcrumbs.tsx +72 -0
  56. package/src/components/ui/client-theme-switcher.tsx +18 -0
  57. package/src/components/ui/footer.module.css +27 -0
  58. package/src/components/ui/footer.tsx +30 -0
  59. package/src/components/ui/search.module.css +111 -0
  60. package/src/components/ui/search.tsx +218 -0
  61. package/src/lib/api-routes.ts +120 -0
  62. package/src/lib/config.ts +58 -0
  63. package/src/lib/get-llm-text.ts +10 -0
  64. package/src/lib/head.tsx +49 -0
  65. package/src/lib/index.ts +2 -0
  66. package/src/lib/openapi.ts +188 -0
  67. package/src/lib/page-context.tsx +117 -0
  68. package/src/lib/remark-unused-directives.ts +30 -0
  69. package/src/lib/schema.ts +99 -0
  70. package/src/lib/snippet-generators.ts +87 -0
  71. package/src/lib/source.ts +186 -0
  72. package/src/pages/ApiLayout.module.css +22 -0
  73. package/src/pages/ApiLayout.tsx +33 -0
  74. package/src/pages/ApiPage.tsx +73 -0
  75. package/src/pages/DocsLayout.tsx +18 -0
  76. package/src/pages/DocsPage.tsx +43 -0
  77. package/src/pages/NotFound.tsx +17 -0
  78. package/src/server/App.tsx +67 -0
  79. package/src/server/api/apis-proxy.ts +69 -0
  80. package/src/server/api/health.ts +5 -0
  81. package/src/server/api/page/[...slug].ts +18 -0
  82. package/src/server/api/search.ts +170 -0
  83. package/src/server/api/specs.ts +9 -0
  84. package/src/server/build-search-index.ts +117 -0
  85. package/src/server/entry-client.tsx +73 -0
  86. package/src/server/entry-server.tsx +95 -0
  87. package/src/server/routes/llms.txt.ts +61 -0
  88. package/src/server/routes/og.tsx +75 -0
  89. package/src/server/routes/robots.txt.ts +11 -0
  90. package/src/server/routes/sitemap.xml.ts +39 -0
  91. package/src/server/utils/safe-path.ts +17 -0
  92. package/src/server/vite-config.ts +71 -0
  93. package/src/themes/default/Layout.module.css +81 -0
  94. package/src/themes/default/Layout.tsx +160 -0
  95. package/src/themes/default/Page.module.css +46 -0
  96. package/src/themes/default/Page.tsx +19 -0
  97. package/src/themes/default/Toc.module.css +48 -0
  98. package/src/themes/default/Toc.tsx +68 -0
  99. package/src/themes/default/index.ts +11 -0
  100. package/src/themes/paper/ChapterNav.module.css +71 -0
  101. package/src/themes/paper/ChapterNav.tsx +115 -0
  102. package/src/themes/paper/Layout.module.css +33 -0
  103. package/src/themes/paper/Layout.tsx +37 -0
  104. package/src/themes/paper/Page.module.css +181 -0
  105. package/src/themes/paper/Page.tsx +126 -0
  106. package/src/themes/paper/ReadingProgress.module.css +132 -0
  107. package/src/themes/paper/ReadingProgress.tsx +315 -0
  108. package/src/themes/paper/index.ts +8 -0
  109. package/src/themes/registry.ts +14 -0
  110. package/src/types/config.ts +80 -0
  111. package/src/types/content.ts +36 -0
  112. package/src/types/globals.d.ts +4 -0
  113. package/src/types/index.ts +3 -0
  114. package/src/types/theme.ts +22 -0
  115. package/tsconfig.json +29 -0
@@ -0,0 +1,315 @@
1
+ 'use client';
2
+
3
+ import { cx } from 'class-variance-authority';
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
+ import type { TocItem } from '@/types';
6
+ import styles from './ReadingProgress.module.css';
7
+
8
+ interface Heading {
9
+ title: string;
10
+ level: number;
11
+ id: string;
12
+ url: string;
13
+ yPosition: number;
14
+ }
15
+
16
+ const ARTICLE_SELECTOR = '[data-article-content]';
17
+ const TICK_HEIGHT = 20;
18
+ const NAV_HEIGHT = 60;
19
+
20
+ function calculateTickBounds(containerHeight: number) {
21
+ const numTicks = Math.floor(containerHeight / TICK_HEIGHT) + 1;
22
+ const maxPosition = (numTicks - 1) * TICK_HEIGHT;
23
+ return { numTicks, maxPosition };
24
+ }
25
+
26
+ function snapToTick(value: number, maxPosition: number): number {
27
+ const snapped = Math.round(value / TICK_HEIGHT) * TICK_HEIGHT;
28
+ return Math.max(0, Math.min(snapped, maxPosition));
29
+ }
30
+
31
+ function resolveOverlaps(headings: Heading[], maxPosition: number): Heading[] {
32
+ if (headings.length <= 1) return headings;
33
+
34
+ const resolved: Heading[] = [];
35
+ let lastUsedPos = -TICK_HEIGHT;
36
+
37
+ for (const heading of headings) {
38
+ let newPos = heading.yPosition;
39
+ if (newPos <= lastUsedPos) {
40
+ newPos = lastUsedPos + TICK_HEIGHT;
41
+ }
42
+ resolved.push({ ...heading, yPosition: newPos });
43
+ lastUsedPos = newPos;
44
+ }
45
+
46
+ // Backward pass: clamp-and-shift to prevent overlapping positions
47
+ for (let i = resolved.length - 1; i >= 0; i--) {
48
+ const maxAllowed =
49
+ i === resolved.length - 1
50
+ ? maxPosition
51
+ : resolved[i + 1].yPosition - TICK_HEIGHT;
52
+
53
+ const clampedPos = Math.max(0, maxAllowed);
54
+ if (resolved[i].yPosition > clampedPos) {
55
+ resolved[i] = { ...resolved[i], yPosition: clampedPos };
56
+ for (let j = i - 1; j >= 0; j--) {
57
+ const upperBound = resolved[j + 1].yPosition - TICK_HEIGHT;
58
+ if (resolved[j].yPosition > upperBound) {
59
+ resolved[j] = { ...resolved[j], yPosition: Math.max(0, upperBound) };
60
+ } else {
61
+ break;
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ return resolved;
68
+ }
69
+
70
+ interface ReadingProgressProps {
71
+ items: TocItem[];
72
+ }
73
+
74
+ export function ReadingProgress({ items }: ReadingProgressProps) {
75
+ const [headings, setHeadings] = useState<Heading[]>([]);
76
+ const [containerHeight, setContainerHeight] = useState<number>(0);
77
+ const [ready, setReady] = useState<boolean>(false);
78
+ const [isScrollable, setIsScrollable] = useState<boolean>(true);
79
+ const containerRef = useRef<HTMLDivElement>(null);
80
+ const scrollMarkerRef = useRef<HTMLDivElement>(null);
81
+ const scrollPosRef = useRef<number>(0);
82
+
83
+ const { numTicks, maxPosition } = useMemo(
84
+ () => calculateTickBounds(containerHeight),
85
+ [containerHeight]
86
+ );
87
+
88
+ const recalcHeadings = useCallback(() => {
89
+ const article = document.querySelector(ARTICLE_SELECTOR);
90
+ const container = containerRef.current;
91
+ if (!article || !container || !items.length) return;
92
+
93
+ const articleBox = article.getBoundingClientRect();
94
+ const containerBox = container.getBoundingClientRect();
95
+ const articleTop = articleBox.top + window.scrollY;
96
+
97
+ const hasScroll = articleBox.height > window.innerHeight;
98
+ setIsScrollable(hasScroll);
99
+ setContainerHeight(containerBox.height);
100
+
101
+ const { maxPosition: maxPos } = calculateTickBounds(containerBox.height);
102
+
103
+ const mapped = items
104
+ .map(tocItem => {
105
+ const id = tocItem.url.startsWith('#')
106
+ ? tocItem.url.slice(1)
107
+ : tocItem.url;
108
+ const node = document.getElementById(id);
109
+ if (!node) return null;
110
+
111
+ const { top } = node.getBoundingClientRect();
112
+ const headingPosInArticle = top + window.scrollY - articleTop;
113
+ const progress = headingPosInArticle / articleBox.height;
114
+ const rawY = progress * maxPos;
115
+ const yPos = snapToTick(rawY, maxPos);
116
+
117
+ return {
118
+ title: tocItem.title,
119
+ level: tocItem.depth,
120
+ id,
121
+ url: tocItem.url,
122
+ yPosition: yPos
123
+ };
124
+ })
125
+ .filter((item): item is Heading => item !== null);
126
+
127
+ const resolvedItems = resolveOverlaps(mapped, maxPos);
128
+ setHeadings(resolvedItems);
129
+ }, [items]);
130
+
131
+ // Imperative DOM updates to avoid React re-render on every scroll event
132
+ const handleScroll = useCallback(() => {
133
+ const article = document.querySelector(ARTICLE_SELECTOR);
134
+ const container = containerRef.current;
135
+ const scrollMarker = scrollMarkerRef.current;
136
+ if (!article || !container || !scrollMarker) return;
137
+
138
+ const { top, height } = article.getBoundingClientRect();
139
+ const { height: cHeight } = container.getBoundingClientRect();
140
+ const viewportHeight = window.innerHeight;
141
+ const { maxPosition: maxPos } = calculateTickBounds(cHeight);
142
+
143
+ let newScrollPos: number;
144
+ if (top > 0) {
145
+ newScrollPos = 0;
146
+ } else {
147
+ const scrolled = Math.abs(top);
148
+ const scrollRange = height - viewportHeight;
149
+ const progress =
150
+ scrollRange > 0 ? Math.min(1, scrolled / scrollRange) : 0;
151
+ const rawPos = progress * maxPos;
152
+ newScrollPos = snapToTick(rawPos, maxPos);
153
+ }
154
+
155
+ const prevScrollPos = scrollPosRef.current;
156
+ if (newScrollPos !== prevScrollPos) {
157
+ scrollPosRef.current = newScrollPos;
158
+ scrollMarker.style.top = `${newScrollPos}px`;
159
+
160
+ const textEl = scrollMarker.querySelector('[data-scroll-text]');
161
+ if (textEl) {
162
+ textEl.textContent = (maxPos > 0 ? newScrollPos / maxPos : 0).toFixed(
163
+ 2
164
+ );
165
+ }
166
+
167
+ const tickLines = container.querySelectorAll('[data-tick-line]');
168
+ tickLines.forEach(tick => {
169
+ const tickPos = Number(tick.getAttribute('data-tick-pos'));
170
+ if (tickPos < newScrollPos) {
171
+ tick.classList.remove(styles.tickLineAfter);
172
+ tick.classList.add(styles.tickLineBefore);
173
+ } else {
174
+ tick.classList.remove(styles.tickLineBefore);
175
+ tick.classList.add(styles.tickLineAfter);
176
+ }
177
+ });
178
+ }
179
+ }, []);
180
+
181
+ useEffect(() => {
182
+ recalcHeadings();
183
+ handleScroll();
184
+ setReady(true);
185
+
186
+ const article = document.querySelector(ARTICLE_SELECTOR);
187
+ let ro: ResizeObserver | undefined;
188
+ if (article) {
189
+ ro = new ResizeObserver(() => {
190
+ recalcHeadings();
191
+ handleScroll();
192
+ });
193
+ ro.observe(article);
194
+ }
195
+
196
+ window.addEventListener('resize', recalcHeadings);
197
+ window.addEventListener('scroll', handleScroll, { passive: true });
198
+
199
+ return () => {
200
+ ro?.disconnect();
201
+ window.removeEventListener('resize', recalcHeadings);
202
+ window.removeEventListener('scroll', handleScroll);
203
+ };
204
+ }, [recalcHeadings, handleScroll]);
205
+
206
+ const scrollToTick = (y: number): void => {
207
+ const article = document.querySelector(ARTICLE_SELECTOR);
208
+ if (!article || maxPosition === 0) return;
209
+
210
+ const articleBox = article.getBoundingClientRect();
211
+ const articleTop = articleBox.top + window.scrollY;
212
+ const progress = y / maxPosition;
213
+ const articlePos = progress * articleBox.height;
214
+ const targetScroll = articleTop + articlePos - NAV_HEIGHT;
215
+
216
+ window.scrollTo({ top: Math.max(0, targetScroll), behavior: 'smooth' });
217
+ };
218
+
219
+ const scrollToHeading = (id: string): void => {
220
+ const element = document.getElementById(id);
221
+ if (!element) return;
222
+
223
+ const elementTop = element.getBoundingClientRect().top + window.scrollY;
224
+ window.scrollTo({
225
+ top: Math.max(0, elementTop - NAV_HEIGHT),
226
+ behavior: 'smooth'
227
+ });
228
+ };
229
+
230
+ const ticks = useMemo(
231
+ () => Array.from({ length: numTicks }, (_, i) => i * TICK_HEIGHT),
232
+ [numTicks]
233
+ );
234
+
235
+ if (!isScrollable || ticks.length < 2) {
236
+ return <div ref={containerRef} className={styles.container} />;
237
+ }
238
+
239
+ return (
240
+ <div ref={containerRef} className={styles.container}>
241
+ <div className={styles.inner}>
242
+ {ticks.map((y, i) => (
243
+ <div
244
+ key={`tick-${i}`}
245
+ style={{ top: `${y}px` }}
246
+ className={styles.tickContainer}
247
+ >
248
+ <div
249
+ data-tick-line
250
+ data-tick-pos={y}
251
+ className={cx(styles.tickLine, styles.tickLineAfter)}
252
+ />
253
+ <div
254
+ className={styles.tickClickable}
255
+ onClick={() => scrollToTick(y)}
256
+ />
257
+ </div>
258
+ ))}
259
+
260
+ {headings.map((h, i) => (
261
+ <div key={h.id || i} className={styles.headingContainer}>
262
+ <div
263
+ className={styles.headingLabel}
264
+ style={{
265
+ top: `${h.yPosition - 6}px`,
266
+ right: '24px',
267
+ zIndex: h.level < 4 ? 10 : 0,
268
+ transitionDelay: `${50 * i}ms`
269
+ }}
270
+ >
271
+ <a
272
+ href={h.url}
273
+ className={styles.headingLink}
274
+ onClick={e => {
275
+ e.preventDefault();
276
+ e.stopPropagation();
277
+ scrollToHeading(h.id);
278
+ }}
279
+ >
280
+ {h.title}
281
+ </a>
282
+ </div>
283
+ </div>
284
+ ))}
285
+
286
+ {headings.map((h, i) => (
287
+ <div
288
+ key={`line-${i}`}
289
+ className={styles.connectingLine}
290
+ style={{
291
+ top: `${h.yPosition}px`,
292
+ width: `${Math.max(4, (3 - h.level) * 4 + 12)}px`
293
+ }}
294
+ />
295
+ ))}
296
+
297
+ <div
298
+ ref={scrollMarkerRef}
299
+ className={cx(
300
+ styles.scrollMarkerContainer,
301
+ ready
302
+ ? styles.scrollMarkerContainerReady
303
+ : styles.scrollMarkerContainerNotReady
304
+ )}
305
+ style={{ top: '0px' }}
306
+ >
307
+ <div className={styles.scrollMarkerLine} />
308
+ <span data-scroll-text className={styles.scrollMarkerText}>
309
+ 0.00
310
+ </span>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ );
315
+ }
@@ -0,0 +1,8 @@
1
+ import type { Theme } from '@/types';
2
+ import { Layout } from './Layout';
3
+ import { Page } from './Page';
4
+
5
+ export const paperTheme: Theme = {
6
+ Layout,
7
+ Page
8
+ };
@@ -0,0 +1,14 @@
1
+ import type { Theme } from '@/types';
2
+ import { defaultTheme } from './default';
3
+ import { paperTheme } from './paper';
4
+
5
+ const themes: Record<string, Theme> = {
6
+ default: defaultTheme,
7
+ paper: paperTheme
8
+ };
9
+
10
+ export function getTheme(name?: string): Theme {
11
+ if (!name || !themes[name]) return defaultTheme;
12
+
13
+ return themes[name];
14
+ }
@@ -0,0 +1,80 @@
1
+ export interface ChronicleConfig {
2
+ title: string
3
+ description?: string
4
+ url?: string
5
+ logo?: LogoConfig
6
+ theme?: ThemeConfig
7
+ navigation?: NavigationConfig
8
+ search?: SearchConfig
9
+ footer?: FooterConfig
10
+ api?: ApiConfig[]
11
+ llms?: LlmsConfig
12
+ analytics?: AnalyticsConfig
13
+ }
14
+
15
+ export interface LlmsConfig {
16
+ enabled?: boolean
17
+ }
18
+
19
+ export interface AnalyticsConfig {
20
+ enabled?: boolean
21
+ googleAnalytics?: GoogleAnalyticsConfig
22
+ }
23
+
24
+ export interface GoogleAnalyticsConfig {
25
+ measurementId: string
26
+ }
27
+
28
+ export interface ApiConfig {
29
+ name: string
30
+ spec: string
31
+ basePath: string
32
+ server: ApiServerConfig
33
+ auth?: ApiAuthConfig
34
+ }
35
+
36
+ export interface ApiServerConfig {
37
+ url: string
38
+ description?: string
39
+ }
40
+
41
+ export interface ApiAuthConfig {
42
+ type: string
43
+ header: string
44
+ placeholder?: string
45
+ }
46
+
47
+ export interface LogoConfig {
48
+ light?: string
49
+ dark?: string
50
+ }
51
+
52
+ export interface ThemeConfig {
53
+ name: 'default' | 'paper'
54
+ colors?: Record<string, string>
55
+ }
56
+
57
+ export interface NavigationConfig {
58
+ links?: NavLink[]
59
+ social?: SocialLink[]
60
+ }
61
+
62
+ export interface NavLink {
63
+ label: string
64
+ href: string
65
+ }
66
+
67
+ export interface SocialLink {
68
+ type: 'github' | 'twitter' | 'discord' | string
69
+ href: string
70
+ }
71
+
72
+ export interface SearchConfig {
73
+ enabled?: boolean
74
+ placeholder?: string
75
+ }
76
+
77
+ export interface FooterConfig {
78
+ copyright?: string
79
+ links?: NavLink[]
80
+ }
@@ -0,0 +1,36 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export interface Frontmatter {
4
+ title: string
5
+ description?: string
6
+ order?: number
7
+ icon?: string
8
+ lastModified?: string
9
+ }
10
+
11
+ export interface Page {
12
+ slug: string[]
13
+ frontmatter: Frontmatter
14
+ content: ReactNode
15
+ toc: TocItem[]
16
+ }
17
+
18
+ export interface TocItem {
19
+ title: string
20
+ url: string
21
+ depth: number
22
+ }
23
+
24
+ export interface PageTreeItem {
25
+ type: 'page' | 'folder' | 'separator'
26
+ name: string
27
+ url?: string
28
+ order?: number
29
+ icon?: string
30
+ children?: PageTreeItem[]
31
+ }
32
+
33
+ export interface PageTree {
34
+ name: string
35
+ children: PageTreeItem[]
36
+ }
@@ -0,0 +1,4 @@
1
+ // Vite build-time constants (injected via define in vite-config.ts)
2
+ declare const __CHRONICLE_CONTENT_DIR__: string
3
+ declare const __CHRONICLE_PROJECT_ROOT__: string
4
+ declare const __CHRONICLE_PACKAGE_ROOT__: string
@@ -0,0 +1,3 @@
1
+ export * from './config'
2
+ export * from './content'
3
+ export * from './theme'
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from 'react'
2
+ import type { ChronicleConfig } from './config'
3
+ import type { Page, PageTree } from './content'
4
+
5
+ export interface ThemeLayoutProps {
6
+ children: ReactNode
7
+ config: ChronicleConfig
8
+ tree: PageTree
9
+ classNames?: { layout?: string; body?: string; sidebar?: string; content?: string }
10
+ }
11
+
12
+ export interface ThemePageProps {
13
+ page: Page
14
+ config: ChronicleConfig
15
+ tree: PageTree
16
+ }
17
+
18
+ export interface Theme {
19
+ Layout: React.ComponentType<ThemeLayoutProps>
20
+ Page: React.ComponentType<ThemePageProps>
21
+ className?: string
22
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": false,
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "esModuleInterop": true,
7
+ "forceConsistentCasingInFileNames": true,
8
+ "inlineSources": false,
9
+ "isolatedModules": true,
10
+ "noUnusedLocals": false,
11
+ "noUnusedParameters": false,
12
+ "preserveWatchOutput": true,
13
+ "skipLibCheck": true,
14
+ "strict": true,
15
+ "jsx": "react-jsx",
16
+ "module": "ESNext",
17
+ "target": "es6",
18
+ "outDir": "dist",
19
+ "rootDir": ".",
20
+ "baseUrl": ".",
21
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
22
+ "moduleResolution": "bundler",
23
+ "paths": {
24
+ "@/*": ["./src/*"]
25
+ }
26
+ },
27
+ "include": ["src"],
28
+ "exclude": ["node_modules", "dist"]
29
+ }