@ankhorage/paradox 0.0.4 → 0.0.6

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.
@@ -0,0 +1,317 @@
1
+ /***
2
+ * Renders a deterministic static HTML documentation app.
3
+ */
4
+ export function renderHtml({ diagrams, model, }) {
5
+ const navigationItems = [
6
+ ...model.exports.map((item) => ({
7
+ href: `#symbol-${toAnchorId(item.name)}`,
8
+ label: item.name,
9
+ meta: `${item.kind} • ${item.modulePath}`,
10
+ })),
11
+ ...model.components.map((component) => ({
12
+ href: `#component-${toAnchorId(component.name)}`,
13
+ label: component.name,
14
+ meta: `component • ${component.modulePath}`,
15
+ })),
16
+ ].sort((left, right) => left.label.localeCompare(right.label));
17
+ const exportsByModule = groupBy(model.exports, (item) => item.modulePath);
18
+ return {
19
+ indexHtml: `<!doctype html>
20
+ <html lang="en">
21
+ <head>
22
+ <meta charset="utf-8" />
23
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
24
+ <title>${escapeHtml(model.packageName)} • Paradox</title>
25
+ <style>
26
+ :root {
27
+ color-scheme: light;
28
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
29
+ background: #f5f7fb;
30
+ color: #11203b;
31
+ }
32
+ * { box-sizing: border-box; }
33
+ body { margin: 0; }
34
+ a { color: #2459d3; text-decoration: none; }
35
+ a:hover { text-decoration: underline; }
36
+ code, pre { font-family: "SFMono-Regular", ui-monospace, SFMono-Regular, Menlo, monospace; }
37
+ .layout {
38
+ display: grid;
39
+ grid-template-columns: minmax(16rem, 22rem) minmax(0, 1fr);
40
+ min-height: 100vh;
41
+ }
42
+ .sidebar {
43
+ padding: 1.5rem;
44
+ border-right: 1px solid #d8dfec;
45
+ background: #ffffff;
46
+ position: sticky;
47
+ top: 0;
48
+ max-height: 100vh;
49
+ overflow: auto;
50
+ }
51
+ .content {
52
+ padding: 2rem;
53
+ }
54
+ .panel, .item {
55
+ background: #ffffff;
56
+ border: 1px solid #d8dfec;
57
+ border-radius: 0.9rem;
58
+ padding: 1rem 1.2rem;
59
+ margin-bottom: 1rem;
60
+ box-shadow: 0 8px 24px rgba(17, 32, 59, 0.06);
61
+ }
62
+ .summary {
63
+ display: grid;
64
+ gap: 1rem;
65
+ grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
66
+ }
67
+ .summary strong {
68
+ display: block;
69
+ font-size: 1.5rem;
70
+ }
71
+ .search {
72
+ width: 100%;
73
+ padding: 0.75rem 0.9rem;
74
+ border-radius: 0.75rem;
75
+ border: 1px solid #c7d2e5;
76
+ margin: 1rem 0 1.5rem;
77
+ }
78
+ .nav-list, .meta-list {
79
+ list-style: none;
80
+ padding: 0;
81
+ margin: 0;
82
+ }
83
+ .nav-list li + li { margin-top: 0.8rem; }
84
+ .muted { color: #5e6d8c; }
85
+ .chips { display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; list-style: none; }
86
+ .chip {
87
+ border-radius: 999px;
88
+ background: #e8eefb;
89
+ color: #24427a;
90
+ padding: 0.25rem 0.65rem;
91
+ font-size: 0.85rem;
92
+ }
93
+ table { width: 100%; border-collapse: collapse; margin-top: 0.8rem; }
94
+ th, td { padding: 0.6rem; border-bottom: 1px solid #e2e7f2; text-align: left; vertical-align: top; }
95
+ pre {
96
+ overflow: auto;
97
+ border-radius: 0.75rem;
98
+ background: #0f1729;
99
+ color: #e7edf7;
100
+ padding: 1rem;
101
+ }
102
+ .empty { font-style: italic; color: #6f7f9c; }
103
+ @media (max-width: 900px) {
104
+ .layout { grid-template-columns: 1fr; }
105
+ .sidebar { position: static; max-height: none; border-right: 0; border-bottom: 1px solid #d8dfec; }
106
+ .content { padding: 1rem; }
107
+ }
108
+ </style>
109
+ </head>
110
+ <body>
111
+ <div class="layout">
112
+ <aside class="sidebar">
113
+ <p class="muted">Generated with Paradox</p>
114
+ <h1>${escapeHtml(model.packageName)}</h1>
115
+ <p>${escapeHtml(model.description ?? 'Deterministic package documentation.')}</p>
116
+ <input id="search" class="search" type="search" placeholder="Search symbols, files, and metadata" />
117
+ <ul class="nav-list">
118
+ ${navigationItems
119
+ .map((item) => `<li><a href="${escapeAttribute(item.href)}">${escapeHtml(item.label)}</a><div class="muted">${escapeHtml(item.meta)}</div></li>`)
120
+ .join('')}
121
+ </ul>
122
+ </aside>
123
+ <main class="content">
124
+ <section class="panel">
125
+ <h2>Package overview</h2>
126
+ <div class="summary">
127
+ <div><strong>${model.exports.length}</strong><span class="muted">public exports</span></div>
128
+ <div><strong>${model.components.length}</strong><span class="muted">components</span></div>
129
+ <div><strong>${model.modules.length}</strong><span class="muted">modules</span></div>
130
+ <div><strong>${model.entrypoints.length}</strong><span class="muted">entrypoints</span></div>
131
+ </div>
132
+ <h3>Entrypoints</h3>
133
+ <ul class="meta-list">
134
+ ${model.entrypoints.map((entrypoint) => `<li><code>${escapeHtml(entrypoint)}</code></li>`).join('')}
135
+ </ul>
136
+ </section>
137
+ <section class="panel">
138
+ <h2>Modules</h2>
139
+ ${model.modules.map(renderModuleCard).join('')}
140
+ </section>
141
+ <section class="panel">
142
+ <h2>Exports by module</h2>
143
+ ${[...exportsByModule.entries()]
144
+ .map(([modulePath, exports]) => `
145
+ <section>
146
+ <h3>${escapeHtml(modulePath)}</h3>
147
+ ${exports.map((item) => renderExportCard(item)).join('')}
148
+ </section>`)
149
+ .join('')}
150
+ </section>
151
+ <section class="panel">
152
+ <h2>Component registry</h2>
153
+ ${model.components.length === 0 ? '<p class="empty">No components were detected.</p>' : model.components.map(renderComponentCard).join('')}
154
+ </section>
155
+ <section class="panel">
156
+ <h2>Diagrams</h2>
157
+ ${diagrams.map(renderDiagramCard).join('')}
158
+ </section>
159
+ </main>
160
+ </div>
161
+ <script>
162
+ const search = document.getElementById('search');
163
+ const items = Array.from(document.querySelectorAll('[data-search]'));
164
+ search?.addEventListener('input', () => {
165
+ const value = search.value.trim().toLowerCase();
166
+ for (const item of items) {
167
+ const haystack = (item.getAttribute('data-search') || '').toLowerCase();
168
+ item.style.display = value === '' || haystack.includes(value) ? '' : 'none';
169
+ }
170
+ });
171
+ </script>
172
+ </body>
173
+ </html>
174
+ `,
175
+ };
176
+ }
177
+ function renderModuleCard(module) {
178
+ return `<article class="item" data-search="${escapeAttribute([module.path, ...module.dependencies, ...module.exports].join(' '))}">
179
+ <h3>${escapeHtml(module.path)}</h3>
180
+ <p class="muted">${module.isEntrypoint ? 'Configured entrypoint' : 'Referenced module'}</p>
181
+ <p><strong>Dependencies:</strong> ${renderInlineCodeList(module.dependencies)}</p>
182
+ <p><strong>Exports:</strong> ${renderInlineCodeList(module.exports)}</p>
183
+ </article>`;
184
+ }
185
+ function renderExportCard(item) {
186
+ return `<article class="item" id="symbol-${toAnchorId(item.name)}" data-search="${escapeAttribute([
187
+ item.name,
188
+ item.kind,
189
+ item.modulePath,
190
+ item.description ?? '',
191
+ ...item.relatedSymbols,
192
+ ...item.signatures.map((signature) => signature.label),
193
+ ].join(' '))}">
194
+ <h4>${escapeHtml(item.name)}</h4>
195
+ <p class="muted">${escapeHtml(item.kind)} • <code>${escapeHtml(item.sourceLocation.filePath)}:${item.sourceLocation.line}:${item.sourceLocation.column}</code></p>
196
+ ${item.description ? `<p>${escapeHtml(item.description)}</p>` : ''}
197
+ <p><strong>Export paths:</strong> ${renderInlineCodeList(item.exportPaths)}</p>
198
+ <div><strong>Related symbols:</strong> ${item.relatedSymbols.length > 0 ? renderChipList(item.relatedSymbols) : '<span class="empty">None</span>'}</div>
199
+ ${item.signatures.length > 0 ? renderSignatureBlock(item) : ''}
200
+ ${item.members.length > 0 ? renderMemberTable(item) : ''}
201
+ </article>`;
202
+ }
203
+ function renderSignatureBlock(item) {
204
+ return item.signatures
205
+ .map((signature) => `<div>
206
+ <h5>Signature</h5>
207
+ <pre>${escapeHtml(signature.label)}</pre>
208
+ ${signature.parameters.length > 0
209
+ ? `<table>
210
+ <thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead>
211
+ <tbody>
212
+ ${signature.parameters
213
+ .map((parameter) => `<tr>
214
+ <td><code>${escapeHtml(parameter.name)}</code></td>
215
+ <td><code>${escapeHtml(parameter.type)}</code></td>
216
+ <td>${parameter.required ? 'yes' : 'no'}</td>
217
+ <td>${escapeHtml(parameter.description ?? '')}</td>
218
+ </tr>`)
219
+ .join('')}
220
+ </tbody>
221
+ </table>`
222
+ : ''}
223
+ <p><strong>Returns:</strong> <code>${escapeHtml(signature.returnType ?? 'void')}</code>${signature.returnDescription ? ` — ${escapeHtml(signature.returnDescription)}` : ''}</p>
224
+ </div>`)
225
+ .join('');
226
+ }
227
+ function renderMemberTable(item) {
228
+ return `<table>
229
+ <thead><tr><th>Member</th><th>Kind</th><th>Type</th><th>Required</th><th>Description</th></tr></thead>
230
+ <tbody>
231
+ ${item.members
232
+ .map((member) => `<tr>
233
+ <td><code>${escapeHtml(member.name)}</code></td>
234
+ <td>${escapeHtml(member.kind)}</td>
235
+ <td><code>${escapeHtml(member.type)}</code></td>
236
+ <td>${member.required ? 'yes' : 'no'}</td>
237
+ <td>${escapeHtml(member.description ?? '')}</td>
238
+ </tr>`)
239
+ .join('')}
240
+ </tbody>
241
+ </table>`;
242
+ }
243
+ function renderComponentCard(component) {
244
+ return `<article class="item" id="component-${toAnchorId(component.name)}" data-search="${escapeAttribute([
245
+ component.name,
246
+ component.modulePath,
247
+ component.description ?? '',
248
+ ...component.props.map((prop) => `${prop.name} ${prop.type}`),
249
+ ].join(' '))}">
250
+ <h3>${escapeHtml(component.name)}</h3>
251
+ <p class="muted"><code>${escapeHtml(component.sourceLocation.filePath)}:${component.sourceLocation.line}:${component.sourceLocation.column}</code></p>
252
+ ${component.description ? `<p>${escapeHtml(component.description)}</p>` : ''}
253
+ <p><strong>Export paths:</strong> ${renderInlineCodeList(component.exportPaths)}</p>
254
+ <table>
255
+ <thead><tr><th>Prop</th><th>Type</th><th>Required</th><th>Description</th></tr></thead>
256
+ <tbody>
257
+ ${component.props
258
+ .map((prop) => `<tr>
259
+ <td><code>${escapeHtml(prop.name)}</code></td>
260
+ <td><code>${escapeHtml(prop.type)}</code></td>
261
+ <td>${prop.required ? 'yes' : 'no'}</td>
262
+ <td>${escapeHtml(prop.description ?? '')}</td>
263
+ </tr>`)
264
+ .join('')}
265
+ </tbody>
266
+ </table>
267
+ </article>`;
268
+ }
269
+ function renderDiagramCard(diagram) {
270
+ return `<article class="item" data-search="${escapeAttribute(`${diagram.title} ${diagram.path}`)}">
271
+ <h3>${escapeHtml(diagram.title)}</h3>
272
+ <p class="muted"><code>${escapeHtml(diagram.path)}</code></p>
273
+ <pre>${escapeHtml(diagram.content)}</pre>
274
+ </article>`;
275
+ }
276
+ function renderInlineCodeList(values) {
277
+ if (values.length === 0) {
278
+ return '<span class="empty">None</span>';
279
+ }
280
+ return values.map((value) => `<code>${escapeHtml(value)}</code>`).join(', ');
281
+ }
282
+ function renderChipList(values) {
283
+ return `<ul class="chips">${values
284
+ .map((value) => `<li class="chip">${escapeHtml(value)}</li>`)
285
+ .join('')}</ul>`;
286
+ }
287
+ function groupBy(items, key) {
288
+ const groups = new Map();
289
+ for (const item of items) {
290
+ const groupKey = key(item);
291
+ const group = groups.get(groupKey);
292
+ if (group) {
293
+ group.push(item);
294
+ }
295
+ else {
296
+ groups.set(groupKey, [item]);
297
+ }
298
+ }
299
+ return new Map([...groups.entries()].sort(([left], [right]) => left.localeCompare(right)));
300
+ }
301
+ function toAnchorId(value) {
302
+ return value
303
+ .toLowerCase()
304
+ .replace(/[^a-z0-9]+/g, '-')
305
+ .replace(/^-|-$/g, '');
306
+ }
307
+ function escapeHtml(value) {
308
+ return value
309
+ .replaceAll('&', '&amp;')
310
+ .replaceAll('<', '&lt;')
311
+ .replaceAll('>', '&gt;')
312
+ .replaceAll('"', '&quot;')
313
+ .replaceAll("'", '&#39;');
314
+ }
315
+ function escapeAttribute(value) {
316
+ return escapeHtml(value).replaceAll('\n', '&#10;');
317
+ }
@@ -0,0 +1,5 @@
1
+ import type { RenderContext } from '../types.js';
2
+ /***
3
+ * Renders markdown artifacts from the documentation model.
4
+ */
5
+ export declare function renderMarkdown({ diagrams, model, outputDir, }: RenderContext): Pick<RenderContext['result'], 'components' | 'exportsMarkdown' | 'readme'>;
@@ -0,0 +1,141 @@
1
+ /***
2
+ * Renders markdown artifacts from the documentation model.
3
+ */
4
+ export function renderMarkdown({ diagrams, model, outputDir, }) {
5
+ return {
6
+ readme: renderReadme(model, outputDir, diagrams),
7
+ exportsMarkdown: renderExports(model),
8
+ components: renderComponents(model),
9
+ };
10
+ }
11
+ function renderReadme(model, outputDir, diagrams) {
12
+ const lines = [`# ${model.packageName}`, ''];
13
+ if (model.description) {
14
+ lines.push(model.description, '');
15
+ }
16
+ if (model.usage !== null) {
17
+ lines.push('## Usage', '');
18
+ lines.push('```bash');
19
+ for (const command of model.usage.commands) {
20
+ lines.push(command.command);
21
+ }
22
+ lines.push('```', '');
23
+ }
24
+ if (model.config !== null) {
25
+ lines.push('## Configuration', '');
26
+ lines.push(`Create a \`${model.config.configFile}\` file:`, '');
27
+ lines.push('```ts');
28
+ if (model.config.factoryName !== null) {
29
+ lines.push(`import { ${model.config.factoryName} } from '${model.packageId}';`);
30
+ lines.push('');
31
+ lines.push(`export default ${model.config.factoryName}({`);
32
+ lines.push(' // ...');
33
+ lines.push('});');
34
+ }
35
+ else {
36
+ lines.push(`import type { ${model.config.exportName} } from '${model.packageId}';`);
37
+ lines.push('');
38
+ lines.push('const config = {');
39
+ lines.push(' // ...');
40
+ lines.push(`} satisfies ${model.config.exportName};`);
41
+ lines.push('');
42
+ lines.push('export default config;');
43
+ }
44
+ lines.push('```', '');
45
+ }
46
+ lines.push('## Generated documentation', '');
47
+ lines.push(`- [Interactive documentation app](./${outputDir}/index.html)`);
48
+ lines.push(`- [Public API reference](./${outputDir}/exports.md)`);
49
+ lines.push(`- [Component registry](./${outputDir}/components.md)`);
50
+ for (const diagram of diagrams) {
51
+ lines.push(`- [${diagram.title}](./${outputDir}/${diagram.path})`);
52
+ }
53
+ lines.push('');
54
+ lines.push('## Architecture preview', '');
55
+ if (diagrams.length > 0) {
56
+ lines.push('```mermaid');
57
+ lines.push(diagrams[0].content.trimEnd());
58
+ lines.push('```', '');
59
+ }
60
+ lines.push('## Path resolution', '');
61
+ lines.push('- Config discovery: searches upward from `process.cwd()` for `paradox.config.ts/js/mjs/cjs` (required; no fallback).');
62
+ lines.push('- Package root: defaults to the directory containing `paradox.config.*`; `package.root` (when relative) resolves relative to that directory.');
63
+ lines.push('- Output directory: defaults to `paradox/`; `output.dir` (when relative) resolves relative to the resolved package root and must stay inside it.');
64
+ lines.push('- Modes:');
65
+ lines.push(' - `safe`: writes generated artifacts only under the output directory');
66
+ lines.push(' - `write`: additionally updates `<packageRoot>/README.md`', '');
67
+ if (model.exports.length > 0) {
68
+ lines.push('## Public API', '');
69
+ for (const item of model.exports) {
70
+ lines.push(`### ${item.name}`, '');
71
+ lines.push(item.description ?? `\`${item.kind}\` export.`, '');
72
+ lines.push(`- Kind: \`${item.kind}\``);
73
+ lines.push(`- Module: \`${item.modulePath}\``);
74
+ lines.push(`- Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``);
75
+ lines.push(`- Export paths: ${item.exportPaths.map((path) => `\`${path}\``).join(', ')}`);
76
+ if (item.relatedSymbols.length > 0) {
77
+ lines.push(`- Related symbols: ${item.relatedSymbols.map((symbol) => `\`${symbol}\``).join(', ')}`);
78
+ }
79
+ lines.push('');
80
+ }
81
+ }
82
+ return `${lines.join('\n').trimEnd()}\n`;
83
+ }
84
+ function renderExports(model) {
85
+ const lines = ['# Public API', ''];
86
+ for (const item of model.exports) {
87
+ lines.push(`## ${item.name}`, '');
88
+ lines.push(`Kind: \`${item.kind}\``);
89
+ lines.push(`Module: \`${item.modulePath}\``);
90
+ lines.push(`Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``, '');
91
+ if (item.description) {
92
+ lines.push(item.description, '');
93
+ }
94
+ if (item.signatures.length > 0) {
95
+ lines.push('### Signatures', '');
96
+ for (const signature of item.signatures) {
97
+ lines.push(`- \`${signature.label}\``);
98
+ for (const parameter of signature.parameters) {
99
+ lines.push(` - ${parameter.name}: \`${parameter.type}\`${parameter.required ? '' : ' (optional)'}${parameter.description ? ` — ${parameter.description}` : ''}`);
100
+ }
101
+ lines.push(` - returns: \`${signature.returnType ?? 'void'}\`${signature.returnDescription ? ` — ${signature.returnDescription}` : ''}`);
102
+ }
103
+ lines.push('');
104
+ }
105
+ if (item.members.length > 0) {
106
+ lines.push('### Members', '');
107
+ lines.push('| Name | Kind | Type | Required | Description |');
108
+ lines.push('| --- | --- | --- | --- | --- |');
109
+ for (const member of item.members) {
110
+ lines.push(`| ${escapeTableCell(member.name)} | ${member.kind} | \`${escapeTableCell(member.type)}\` | ${member.required ? 'yes' : 'no'} | ${escapeTableCell(member.description ?? '')} |`);
111
+ }
112
+ lines.push('');
113
+ }
114
+ }
115
+ return `${lines.join('\n').trimEnd()}\n`;
116
+ }
117
+ function renderComponents(model) {
118
+ const lines = ['# Components', ''];
119
+ for (const component of model.components) {
120
+ lines.push(`## ${component.name}`, '');
121
+ lines.push(`Source: \`${component.sourceLocation.filePath}:${component.sourceLocation.line}:${component.sourceLocation.column}\``, '');
122
+ if (component.description) {
123
+ lines.push(component.description, '');
124
+ }
125
+ if (component.exportPaths.length > 0) {
126
+ lines.push(`Export paths: ${component.exportPaths.map((path) => `\`${path}\``).join(', ')}`, '');
127
+ }
128
+ if (component.props.length > 0) {
129
+ lines.push('| Prop | Type | Required | Description |');
130
+ lines.push('| --- | --- | --- | --- |');
131
+ for (const prop of component.props) {
132
+ lines.push(`| ${escapeTableCell(prop.name)} | \`${escapeTableCell(prop.type)}\` | ${prop.required ? 'yes' : 'no'} | ${escapeTableCell(prop.description ?? '')} |`);
133
+ }
134
+ lines.push('');
135
+ }
136
+ }
137
+ return `${lines.join('\n').trimEnd()}\n`;
138
+ }
139
+ function escapeTableCell(value) {
140
+ return value.replaceAll('|', '\\|');
141
+ }
@@ -1,10 +1,24 @@
1
+ import type { DocumentationModel } from '../model/types.js';
1
2
  /***
2
3
  * Rendered documentation files ready to be written to disk.
3
4
  */
5
+ export interface DiagramArtifact {
6
+ path: string;
7
+ title: string;
8
+ content: string;
9
+ }
4
10
  export interface RenderResult {
5
11
  readme: string;
6
12
  exportsMarkdown: string;
7
13
  components: string;
8
14
  exportsJson: string;
9
15
  paradoxJson: string;
16
+ indexHtml: string;
17
+ diagrams: DiagramArtifact[];
18
+ }
19
+ export interface RenderContext {
20
+ model: DocumentationModel;
21
+ outputDir: string;
22
+ diagrams: DiagramArtifact[];
23
+ result: RenderResult;
10
24
  }
@@ -1,5 +1,5 @@
1
1
  import { mkdir, writeFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
2
+ import { dirname, join } from 'node:path';
3
3
  /***
4
4
  * Writes generated documentation artifacts to the configured output paths.
5
5
  */
@@ -12,6 +12,12 @@ export async function write(result, config, runtime) {
12
12
  await writeFile(join(outputRoot, 'components.md'), result.components);
13
13
  await writeFile(join(outputRoot, 'exports.json'), result.exportsJson);
14
14
  await writeFile(join(outputRoot, 'paradox.json'), result.paradoxJson);
15
+ await writeFile(join(outputRoot, 'index.html'), result.indexHtml);
16
+ for (const diagram of result.diagrams) {
17
+ const diagramPath = join(outputRoot, diagram.path);
18
+ await mkdir(dirname(diagramPath), { recursive: true });
19
+ await writeFile(diagramPath, diagram.content);
20
+ }
15
21
  if (mode === 'write') {
16
22
  await writeFile(join(root, 'README.md'), result.readme);
17
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/paradox",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Deterministic documentation generator for TypeScript packages.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -54,7 +54,8 @@
54
54
  "devDependencies": {
55
55
  "@ankhorage/devtools": "^1.0.0",
56
56
  "@changesets/cli": "^2.31.0",
57
- "@types/bun": "^1.1.14",
57
+ "@types/bun": "^1.3.13",
58
58
  "typescript": "^5.6.3"
59
- }
59
+ },
60
+ "packageManager": "bun@1.3.13"
60
61
  }