trickle-cli 0.1.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.
Files changed (125) hide show
  1. package/dist/api-client.d.ts +208 -0
  2. package/dist/api-client.js +237 -0
  3. package/dist/commands/annotate.d.ts +6 -0
  4. package/dist/commands/annotate.js +433 -0
  5. package/dist/commands/audit.d.ts +7 -0
  6. package/dist/commands/audit.js +82 -0
  7. package/dist/commands/auto.d.ts +8 -0
  8. package/dist/commands/auto.js +268 -0
  9. package/dist/commands/capture.d.ts +14 -0
  10. package/dist/commands/capture.js +271 -0
  11. package/dist/commands/check.d.ts +6 -0
  12. package/dist/commands/check.js +408 -0
  13. package/dist/commands/codegen.d.ts +21 -0
  14. package/dist/commands/codegen.js +129 -0
  15. package/dist/commands/coverage.d.ts +13 -0
  16. package/dist/commands/coverage.js +126 -0
  17. package/dist/commands/dashboard.d.ts +1 -0
  18. package/dist/commands/dashboard.js +83 -0
  19. package/dist/commands/dev.d.ts +14 -0
  20. package/dist/commands/dev.js +319 -0
  21. package/dist/commands/diff.d.ts +7 -0
  22. package/dist/commands/diff.js +79 -0
  23. package/dist/commands/docs.d.ts +13 -0
  24. package/dist/commands/docs.js +383 -0
  25. package/dist/commands/errors.d.ts +7 -0
  26. package/dist/commands/errors.js +180 -0
  27. package/dist/commands/export.d.ts +18 -0
  28. package/dist/commands/export.js +238 -0
  29. package/dist/commands/functions.d.ts +6 -0
  30. package/dist/commands/functions.js +71 -0
  31. package/dist/commands/infer.d.ts +14 -0
  32. package/dist/commands/infer.js +275 -0
  33. package/dist/commands/init.d.ts +5 -0
  34. package/dist/commands/init.js +395 -0
  35. package/dist/commands/mock.d.ts +5 -0
  36. package/dist/commands/mock.js +232 -0
  37. package/dist/commands/openapi.d.ts +8 -0
  38. package/dist/commands/openapi.js +82 -0
  39. package/dist/commands/overview.d.ts +11 -0
  40. package/dist/commands/overview.js +266 -0
  41. package/dist/commands/pack.d.ts +11 -0
  42. package/dist/commands/pack.js +133 -0
  43. package/dist/commands/proxy.d.ts +13 -0
  44. package/dist/commands/proxy.js +312 -0
  45. package/dist/commands/replay.d.ts +14 -0
  46. package/dist/commands/replay.js +289 -0
  47. package/dist/commands/run.d.ts +17 -0
  48. package/dist/commands/run.js +997 -0
  49. package/dist/commands/sample.d.ts +13 -0
  50. package/dist/commands/sample.js +260 -0
  51. package/dist/commands/search.d.ts +5 -0
  52. package/dist/commands/search.js +80 -0
  53. package/dist/commands/stubs.d.ts +6 -0
  54. package/dist/commands/stubs.js +187 -0
  55. package/dist/commands/tail.d.ts +4 -0
  56. package/dist/commands/tail.js +76 -0
  57. package/dist/commands/test-gen.d.ts +13 -0
  58. package/dist/commands/test-gen.js +237 -0
  59. package/dist/commands/trace.d.ts +14 -0
  60. package/dist/commands/trace.js +417 -0
  61. package/dist/commands/types.d.ts +7 -0
  62. package/dist/commands/types.js +128 -0
  63. package/dist/commands/unpack.d.ts +11 -0
  64. package/dist/commands/unpack.js +166 -0
  65. package/dist/commands/validate.d.ts +13 -0
  66. package/dist/commands/validate.js +310 -0
  67. package/dist/commands/watch.d.ts +9 -0
  68. package/dist/commands/watch.js +267 -0
  69. package/dist/config.d.ts +1 -0
  70. package/dist/config.js +66 -0
  71. package/dist/formatters/diff-formatter.d.ts +5 -0
  72. package/dist/formatters/diff-formatter.js +43 -0
  73. package/dist/formatters/type-formatter.d.ts +22 -0
  74. package/dist/formatters/type-formatter.js +135 -0
  75. package/dist/index.d.ts +2 -0
  76. package/dist/index.js +419 -0
  77. package/dist/local-codegen.d.ts +22 -0
  78. package/dist/local-codegen.js +762 -0
  79. package/dist/ui/badges.d.ts +16 -0
  80. package/dist/ui/badges.js +71 -0
  81. package/dist/ui/helpers.d.ts +13 -0
  82. package/dist/ui/helpers.js +85 -0
  83. package/package.json +23 -0
  84. package/src/api-client.ts +407 -0
  85. package/src/commands/annotate.ts +450 -0
  86. package/src/commands/audit.ts +103 -0
  87. package/src/commands/auto.ts +268 -0
  88. package/src/commands/capture.ts +257 -0
  89. package/src/commands/check.ts +437 -0
  90. package/src/commands/codegen.ts +128 -0
  91. package/src/commands/coverage.ts +170 -0
  92. package/src/commands/dashboard.ts +46 -0
  93. package/src/commands/dev.ts +323 -0
  94. package/src/commands/diff.ts +99 -0
  95. package/src/commands/docs.ts +392 -0
  96. package/src/commands/errors.ts +205 -0
  97. package/src/commands/export.ts +287 -0
  98. package/src/commands/functions.ts +81 -0
  99. package/src/commands/infer.ts +260 -0
  100. package/src/commands/init.ts +419 -0
  101. package/src/commands/mock.ts +220 -0
  102. package/src/commands/openapi.ts +53 -0
  103. package/src/commands/overview.ts +310 -0
  104. package/src/commands/pack.ts +139 -0
  105. package/src/commands/proxy.ts +314 -0
  106. package/src/commands/replay.ts +356 -0
  107. package/src/commands/run.ts +1190 -0
  108. package/src/commands/sample.ts +259 -0
  109. package/src/commands/search.ts +107 -0
  110. package/src/commands/stubs.ts +211 -0
  111. package/src/commands/tail.ts +94 -0
  112. package/src/commands/test-gen.ts +236 -0
  113. package/src/commands/trace.ts +440 -0
  114. package/src/commands/types.ts +161 -0
  115. package/src/commands/unpack.ts +179 -0
  116. package/src/commands/validate.ts +368 -0
  117. package/src/commands/watch.ts +277 -0
  118. package/src/config.ts +38 -0
  119. package/src/formatters/diff-formatter.ts +51 -0
  120. package/src/formatters/type-formatter.ts +161 -0
  121. package/src/index.ts +454 -0
  122. package/src/local-codegen.ts +859 -0
  123. package/src/ui/badges.ts +66 -0
  124. package/src/ui/helpers.ts +80 -0
  125. package/tsconfig.json +8 -0
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.watchCommand = watchCommand;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const api_client_1 = require("../api-client");
44
+ const config_1 = require("../config");
45
+ /**
46
+ * Detect which codegen formats are relevant based on package.json dependencies.
47
+ */
48
+ function detectFormats(projectDir) {
49
+ const pkgPath = path.join(projectDir, "package.json");
50
+ if (!fs.existsSync(pkgPath)) {
51
+ return [
52
+ { format: "", fileName: "types.d.ts", label: "TypeScript types" },
53
+ { format: "guards", fileName: "guards.ts", label: "Type guards" },
54
+ ];
55
+ }
56
+ let pkg;
57
+ try {
58
+ pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
59
+ }
60
+ catch {
61
+ return [
62
+ { format: "", fileName: "types.d.ts", label: "TypeScript types" },
63
+ { format: "guards", fileName: "guards.ts", label: "Type guards" },
64
+ ];
65
+ }
66
+ const deps = {
67
+ ...(pkg.dependencies || {}),
68
+ ...(pkg.devDependencies || {}),
69
+ };
70
+ const formats = [];
71
+ formats.push({ format: "", fileName: "types.d.ts", label: "TypeScript types" });
72
+ if (deps["axios"]) {
73
+ formats.push({ format: "axios", fileName: "axios-client.ts", label: "Axios client" });
74
+ }
75
+ else {
76
+ formats.push({ format: "client", fileName: "api-client.ts", label: "Fetch API client" });
77
+ }
78
+ if (deps["@tanstack/react-query"] || deps["react-query"]) {
79
+ formats.push({ format: "react-query", fileName: "hooks.ts", label: "React Query hooks" });
80
+ }
81
+ if (deps["swr"]) {
82
+ formats.push({ format: "swr", fileName: "swr-hooks.ts", label: "SWR hooks" });
83
+ }
84
+ if (deps["zod"]) {
85
+ formats.push({ format: "zod", fileName: "schemas.ts", label: "Zod schemas" });
86
+ }
87
+ if (deps["@trpc/server"] || deps["@trpc/client"]) {
88
+ formats.push({ format: "trpc", fileName: "trpc-router.ts", label: "tRPC router" });
89
+ }
90
+ if (deps["class-validator"] || deps["@nestjs/common"]) {
91
+ formats.push({ format: "class-validator", fileName: "dtos.ts", label: "class-validator DTOs" });
92
+ }
93
+ if (deps["express"] || deps["@types/express"]) {
94
+ formats.push({ format: "handlers", fileName: "handlers.d.ts", label: "Express handler types" });
95
+ }
96
+ if (deps["msw"]) {
97
+ formats.push({ format: "msw", fileName: "msw-handlers.ts", label: "MSW mock handlers" });
98
+ }
99
+ formats.push({ format: "guards", fileName: "guards.ts", label: "Type guards" });
100
+ return formats;
101
+ }
102
+ /**
103
+ * Build a fingerprint from the functions list to detect changes.
104
+ * Uses function names + type hashes + last_seen timestamps.
105
+ */
106
+ function buildFingerprint(functions) {
107
+ const parts = functions
108
+ .map((f) => `${f.function_name}:${f.last_seen_at}`)
109
+ .sort();
110
+ return parts.join("|");
111
+ }
112
+ /**
113
+ * Generate all relevant type files to the output directory.
114
+ */
115
+ async function regenerate(formats, outDir, env) {
116
+ if (!fs.existsSync(outDir)) {
117
+ fs.mkdirSync(outDir, { recursive: true });
118
+ }
119
+ const files = [];
120
+ let generated = 0;
121
+ for (const f of formats) {
122
+ try {
123
+ const result = await (0, api_client_1.fetchCodegen)({
124
+ env,
125
+ format: f.format || undefined,
126
+ });
127
+ const content = result.types;
128
+ if (!content || content.includes("No functions found") || content.includes("No API routes found")) {
129
+ continue;
130
+ }
131
+ const filePath = path.join(outDir, f.fileName);
132
+ fs.writeFileSync(filePath, content, "utf-8");
133
+ generated++;
134
+ files.push(f.fileName);
135
+ }
136
+ catch {
137
+ // Skip failed formats silently
138
+ }
139
+ }
140
+ return { generated, files };
141
+ }
142
+ function timestamp() {
143
+ const now = new Date();
144
+ return now.toLocaleTimeString("en-US", { hour12: false });
145
+ }
146
+ /**
147
+ * `trickle watch` — Watch for new type observations and auto-regenerate type files.
148
+ */
149
+ async function watchCommand(opts) {
150
+ const backendUrl = (0, config_1.getBackendUrl)();
151
+ const projectDir = process.cwd();
152
+ const outDir = path.resolve(opts.dir || ".trickle");
153
+ const intervalMs = parseInterval(opts.interval || "3s");
154
+ // Check backend connectivity
155
+ try {
156
+ const res = await fetch(`${backendUrl}/api/health`, { signal: AbortSignal.timeout(3000) });
157
+ if (!res.ok)
158
+ throw new Error("not ok");
159
+ }
160
+ catch {
161
+ console.error(chalk_1.default.red(`\n Cannot reach trickle backend at ${chalk_1.default.bold(backendUrl)}\n`));
162
+ process.exit(1);
163
+ }
164
+ // Detect formats from project deps
165
+ const formats = detectFormats(projectDir);
166
+ console.log("");
167
+ console.log(chalk_1.default.bold(" trickle watch"));
168
+ console.log(chalk_1.default.gray(" " + "─".repeat(50)));
169
+ console.log(chalk_1.default.gray(` Backend: ${backendUrl}`));
170
+ console.log(chalk_1.default.gray(` Output: ${outDir}`));
171
+ console.log(chalk_1.default.gray(` Interval: ${opts.interval || "3s"}`));
172
+ if (opts.env) {
173
+ console.log(chalk_1.default.gray(` Env: ${opts.env}`));
174
+ }
175
+ console.log(chalk_1.default.gray(` Formats: ${formats.map((f) => f.fileName).join(", ")}`));
176
+ console.log(chalk_1.default.gray(" " + "─".repeat(50)));
177
+ console.log("");
178
+ // Initial generation
179
+ console.log(chalk_1.default.gray(` [${timestamp()}]`) + " Performing initial type generation...");
180
+ let lastFingerprint = "";
181
+ try {
182
+ const { functions } = await (0, api_client_1.listFunctions)({ env: opts.env, limit: 1000 });
183
+ if (functions.length > 0) {
184
+ lastFingerprint = buildFingerprint(functions);
185
+ const { generated, files } = await regenerate(formats, outDir, opts.env);
186
+ if (generated > 0) {
187
+ console.log(chalk_1.default.green(` [${timestamp()}]`) +
188
+ ` Generated ${generated} files: ${chalk_1.default.white(files.join(", "))}`);
189
+ }
190
+ else {
191
+ console.log(chalk_1.default.gray(` [${timestamp()}]`) + " No types to generate yet.");
192
+ }
193
+ }
194
+ else {
195
+ console.log(chalk_1.default.gray(` [${timestamp()}]`) + " No observed functions yet. Waiting...");
196
+ }
197
+ }
198
+ catch {
199
+ console.log(chalk_1.default.yellow(` [${timestamp()}]`) + " Could not fetch initial types. Will retry...");
200
+ }
201
+ console.log("");
202
+ console.log(chalk_1.default.gray(" Watching for type changes... (Ctrl+C to stop)"));
203
+ console.log("");
204
+ // Poll loop
205
+ const poll = async () => {
206
+ try {
207
+ const { functions } = await (0, api_client_1.listFunctions)({ env: opts.env, limit: 1000 });
208
+ const fingerprint = buildFingerprint(functions);
209
+ if (fingerprint !== lastFingerprint && fingerprint !== "") {
210
+ // Types changed — find what's new
211
+ const newFunctions = functions.filter((f) => {
212
+ // A function is "new" if it wasn't in the last fingerprint
213
+ return !lastFingerprint.includes(`${f.function_name}:`);
214
+ });
215
+ const updatedFunctions = functions.filter((f) => {
216
+ // A function is "updated" if its timestamp changed
217
+ const oldEntry = `${f.function_name}:${f.last_seen_at}`;
218
+ return lastFingerprint.includes(`${f.function_name}:`) && !lastFingerprint.includes(oldEntry);
219
+ });
220
+ lastFingerprint = fingerprint;
221
+ // Show what changed
222
+ for (const f of newFunctions) {
223
+ console.log(chalk_1.default.cyan(` [${timestamp()}]`) +
224
+ chalk_1.default.gray(" New: ") +
225
+ chalk_1.default.white(f.function_name));
226
+ }
227
+ for (const f of updatedFunctions) {
228
+ console.log(chalk_1.default.blue(` [${timestamp()}]`) +
229
+ chalk_1.default.gray(" Updated: ") +
230
+ chalk_1.default.white(f.function_name));
231
+ }
232
+ // Regenerate
233
+ const { generated, files } = await regenerate(formats, outDir, opts.env);
234
+ if (generated > 0) {
235
+ console.log(chalk_1.default.green(` [${timestamp()}]`) +
236
+ ` Regenerated ${generated} files: ${chalk_1.default.white(files.join(", "))}`);
237
+ }
238
+ }
239
+ }
240
+ catch {
241
+ // Silently skip poll failures
242
+ }
243
+ };
244
+ // Set up interval
245
+ const timer = setInterval(poll, intervalMs);
246
+ // Handle graceful shutdown
247
+ const cleanup = () => {
248
+ clearInterval(timer);
249
+ console.log(chalk_1.default.gray(`\n [${timestamp()}]`) + " Watch stopped.\n");
250
+ process.exit(0);
251
+ };
252
+ process.on("SIGINT", cleanup);
253
+ process.on("SIGTERM", cleanup);
254
+ }
255
+ function parseInterval(input) {
256
+ const match = input.match(/^(\d+)(s|ms|m)?$/);
257
+ if (!match)
258
+ return 3000;
259
+ const value = parseInt(match[1], 10);
260
+ const unit = match[2] || "s";
261
+ switch (unit) {
262
+ case "ms": return value;
263
+ case "s": return value * 1000;
264
+ case "m": return value * 60 * 1000;
265
+ default: return value * 1000;
266
+ }
267
+ }
@@ -0,0 +1 @@
1
+ export declare function getBackendUrl(): string;
package/dist/config.js ADDED
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getBackendUrl = getBackendUrl;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const os = __importStar(require("os"));
40
+ const DEFAULT_BACKEND_URL = "http://localhost:4888";
41
+ function loadConfigFile() {
42
+ const configPath = path.join(os.homedir(), ".trickle", "config.json");
43
+ try {
44
+ if (fs.existsSync(configPath)) {
45
+ const raw = fs.readFileSync(configPath, "utf-8");
46
+ return JSON.parse(raw);
47
+ }
48
+ }
49
+ catch {
50
+ // Ignore invalid config file
51
+ }
52
+ return null;
53
+ }
54
+ function getBackendUrl() {
55
+ // 1. Environment variable takes priority
56
+ if (process.env.TRICKLE_BACKEND_URL) {
57
+ return process.env.TRICKLE_BACKEND_URL.replace(/\/+$/, "");
58
+ }
59
+ // 2. Config file
60
+ const config = loadConfigFile();
61
+ if (config?.backendUrl) {
62
+ return config.backendUrl.replace(/\/+$/, "");
63
+ }
64
+ // 3. Default
65
+ return DEFAULT_BACKEND_URL;
66
+ }
@@ -0,0 +1,5 @@
1
+ import { TypeDiff } from "../api-client";
2
+ /**
3
+ * Format an array of TypeDiff entries as colorized diff output.
4
+ */
5
+ export declare function formatDiffs(diffs: TypeDiff[]): string;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatDiffs = formatDiffs;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const type_formatter_1 = require("./type-formatter");
9
+ /**
10
+ * Format an array of TypeDiff entries as colorized diff output.
11
+ */
12
+ function formatDiffs(diffs) {
13
+ if (diffs.length === 0) {
14
+ return chalk_1.default.gray(" No differences found.");
15
+ }
16
+ const lines = [];
17
+ for (const diff of diffs) {
18
+ const pathStr = chalk_1.default.gray(diff.path);
19
+ switch (diff.kind) {
20
+ case "added":
21
+ lines.push(chalk_1.default.green(" + added ") +
22
+ pathStr +
23
+ chalk_1.default.gray(": ") +
24
+ (0, type_formatter_1.formatType)(diff.type, 0));
25
+ break;
26
+ case "removed":
27
+ lines.push(chalk_1.default.red(" - removed ") +
28
+ pathStr +
29
+ chalk_1.default.gray(": ") +
30
+ (0, type_formatter_1.formatType)(diff.type, 0));
31
+ break;
32
+ case "changed":
33
+ lines.push(chalk_1.default.yellow(" ~ changed ") +
34
+ pathStr +
35
+ chalk_1.default.gray(": ") +
36
+ (0, type_formatter_1.formatType)(diff.from, 0) +
37
+ chalk_1.default.gray(" -> ") +
38
+ (0, type_formatter_1.formatType)(diff.to, 0));
39
+ break;
40
+ }
41
+ }
42
+ return lines.join("\n");
43
+ }
@@ -0,0 +1,22 @@
1
+ interface TypeNode {
2
+ kind: string;
3
+ name?: string;
4
+ element?: TypeNode;
5
+ properties?: Record<string, TypeNode>;
6
+ members?: TypeNode[];
7
+ params?: TypeNode[];
8
+ returnType?: TypeNode;
9
+ resolved?: TypeNode;
10
+ key?: TypeNode;
11
+ value?: TypeNode;
12
+ elements?: TypeNode[];
13
+ }
14
+ /**
15
+ * Format a TypeNode as colorized pseudo-TypeScript.
16
+ */
17
+ export declare function formatType(node: TypeNode | unknown, indent?: number): string;
18
+ /**
19
+ * Format a type node as a compact single-line string (no colors).
20
+ */
21
+ export declare function formatTypePlain(node: TypeNode | unknown): string;
22
+ export {};
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatType = formatType;
7
+ exports.formatTypePlain = formatTypePlain;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const INDENT_SIZE = 2;
10
+ function primitiveColor(name) {
11
+ switch (name) {
12
+ case "string":
13
+ return chalk_1.default.green(name);
14
+ case "number":
15
+ case "bigint":
16
+ return chalk_1.default.yellow(name);
17
+ case "boolean":
18
+ return chalk_1.default.blue(name);
19
+ case "null":
20
+ case "undefined":
21
+ return chalk_1.default.gray(name);
22
+ case "symbol":
23
+ return chalk_1.default.magenta(name);
24
+ default:
25
+ return chalk_1.default.white(name);
26
+ }
27
+ }
28
+ /**
29
+ * Format a TypeNode as colorized pseudo-TypeScript.
30
+ */
31
+ function formatType(node, indent = 0) {
32
+ if (!node || typeof node !== "object") {
33
+ return chalk_1.default.gray("unknown");
34
+ }
35
+ const n = node;
36
+ const pad = " ".repeat(indent);
37
+ const innerPad = " ".repeat(indent + INDENT_SIZE);
38
+ switch (n.kind) {
39
+ case "primitive":
40
+ return primitiveColor(n.name || "unknown");
41
+ case "array":
42
+ if (n.element) {
43
+ const inner = formatType(n.element, indent);
44
+ // Wrap complex types in parens for array notation
45
+ if (n.element.kind === "union") {
46
+ return `(${inner})[]`;
47
+ }
48
+ return `${inner}[]`;
49
+ }
50
+ return chalk_1.default.gray("unknown[]");
51
+ case "object": {
52
+ if (!n.properties)
53
+ return chalk_1.default.gray("{}");
54
+ const keys = Object.keys(n.properties);
55
+ if (keys.length === 0)
56
+ return chalk_1.default.gray("{}");
57
+ // Inline for small objects (2 or fewer properties)
58
+ if (keys.length <= 2) {
59
+ const props = keys.map((key) => `${chalk_1.default.white(key)}: ${formatType(n.properties[key], 0)}`);
60
+ return `{ ${props.join(", ")} }`;
61
+ }
62
+ // Multi-line for larger objects
63
+ const lines = keys.map((key) => `${innerPad}${chalk_1.default.white(key)}: ${formatType(n.properties[key], indent + INDENT_SIZE)}`);
64
+ return `{\n${lines.join(",\n")}\n${pad}}`;
65
+ }
66
+ case "union": {
67
+ if (!n.members || n.members.length === 0)
68
+ return chalk_1.default.gray("never");
69
+ return n.members.map((m) => formatType(m, indent)).join(chalk_1.default.gray(" | "));
70
+ }
71
+ case "function": {
72
+ const params = (n.params || [])
73
+ .map((p, i) => `${chalk_1.default.white(`arg${i}`)}: ${formatType(p, indent)}`)
74
+ .join(", ");
75
+ const ret = n.returnType ? formatType(n.returnType, indent) : chalk_1.default.gray("void");
76
+ return `(${params}) => ${ret}`;
77
+ }
78
+ case "promise": {
79
+ const resolved = n.resolved ? formatType(n.resolved, indent) : chalk_1.default.gray("unknown");
80
+ return `${chalk_1.default.cyan("Promise")}<${resolved}>`;
81
+ }
82
+ case "map": {
83
+ const key = n.key ? formatType(n.key, indent) : chalk_1.default.gray("unknown");
84
+ const value = n.value ? formatType(n.value, indent) : chalk_1.default.gray("unknown");
85
+ return `${chalk_1.default.cyan("Map")}<${key}, ${value}>`;
86
+ }
87
+ case "set": {
88
+ const element = n.element ? formatType(n.element, indent) : chalk_1.default.gray("unknown");
89
+ return `${chalk_1.default.cyan("Set")}<${element}>`;
90
+ }
91
+ case "tuple": {
92
+ if (!n.elements || n.elements.length === 0)
93
+ return "[]";
94
+ const elems = n.elements.map((e) => formatType(e, indent)).join(", ");
95
+ return `[${elems}]`;
96
+ }
97
+ case "unknown":
98
+ return chalk_1.default.gray("unknown");
99
+ default:
100
+ return chalk_1.default.gray("unknown");
101
+ }
102
+ }
103
+ /**
104
+ * Format a type node as a compact single-line string (no colors).
105
+ */
106
+ function formatTypePlain(node) {
107
+ if (!node || typeof node !== "object")
108
+ return "unknown";
109
+ const n = node;
110
+ switch (n.kind) {
111
+ case "primitive":
112
+ return n.name || "unknown";
113
+ case "array":
114
+ return n.element ? `${formatTypePlain(n.element)}[]` : "unknown[]";
115
+ case "object": {
116
+ if (!n.properties)
117
+ return "{}";
118
+ const keys = Object.keys(n.properties);
119
+ if (keys.length === 0)
120
+ return "{}";
121
+ const props = keys.map((k) => `${k}: ${formatTypePlain(n.properties[k])}`);
122
+ return `{ ${props.join(", ")} }`;
123
+ }
124
+ case "union":
125
+ return (n.members || []).map(formatTypePlain).join(" | ");
126
+ case "function": {
127
+ const params = (n.params || []).map((p, i) => `arg${i}: ${formatTypePlain(p)}`).join(", ");
128
+ return `(${params}) => ${n.returnType ? formatTypePlain(n.returnType) : "void"}`;
129
+ }
130
+ case "promise":
131
+ return `Promise<${n.resolved ? formatTypePlain(n.resolved) : "unknown"}>`;
132
+ default:
133
+ return "unknown";
134
+ }
135
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};