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.
- package/dist/api-client.d.ts +208 -0
- package/dist/api-client.js +237 -0
- package/dist/commands/annotate.d.ts +6 -0
- package/dist/commands/annotate.js +433 -0
- package/dist/commands/audit.d.ts +7 -0
- package/dist/commands/audit.js +82 -0
- package/dist/commands/auto.d.ts +8 -0
- package/dist/commands/auto.js +268 -0
- package/dist/commands/capture.d.ts +14 -0
- package/dist/commands/capture.js +271 -0
- package/dist/commands/check.d.ts +6 -0
- package/dist/commands/check.js +408 -0
- package/dist/commands/codegen.d.ts +21 -0
- package/dist/commands/codegen.js +129 -0
- package/dist/commands/coverage.d.ts +13 -0
- package/dist/commands/coverage.js +126 -0
- package/dist/commands/dashboard.d.ts +1 -0
- package/dist/commands/dashboard.js +83 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.js +319 -0
- package/dist/commands/diff.d.ts +7 -0
- package/dist/commands/diff.js +79 -0
- package/dist/commands/docs.d.ts +13 -0
- package/dist/commands/docs.js +383 -0
- package/dist/commands/errors.d.ts +7 -0
- package/dist/commands/errors.js +180 -0
- package/dist/commands/export.d.ts +18 -0
- package/dist/commands/export.js +238 -0
- package/dist/commands/functions.d.ts +6 -0
- package/dist/commands/functions.js +71 -0
- package/dist/commands/infer.d.ts +14 -0
- package/dist/commands/infer.js +275 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +395 -0
- package/dist/commands/mock.d.ts +5 -0
- package/dist/commands/mock.js +232 -0
- package/dist/commands/openapi.d.ts +8 -0
- package/dist/commands/openapi.js +82 -0
- package/dist/commands/overview.d.ts +11 -0
- package/dist/commands/overview.js +266 -0
- package/dist/commands/pack.d.ts +11 -0
- package/dist/commands/pack.js +133 -0
- package/dist/commands/proxy.d.ts +13 -0
- package/dist/commands/proxy.js +312 -0
- package/dist/commands/replay.d.ts +14 -0
- package/dist/commands/replay.js +289 -0
- package/dist/commands/run.d.ts +17 -0
- package/dist/commands/run.js +997 -0
- package/dist/commands/sample.d.ts +13 -0
- package/dist/commands/sample.js +260 -0
- package/dist/commands/search.d.ts +5 -0
- package/dist/commands/search.js +80 -0
- package/dist/commands/stubs.d.ts +6 -0
- package/dist/commands/stubs.js +187 -0
- package/dist/commands/tail.d.ts +4 -0
- package/dist/commands/tail.js +76 -0
- package/dist/commands/test-gen.d.ts +13 -0
- package/dist/commands/test-gen.js +237 -0
- package/dist/commands/trace.d.ts +14 -0
- package/dist/commands/trace.js +417 -0
- package/dist/commands/types.d.ts +7 -0
- package/dist/commands/types.js +128 -0
- package/dist/commands/unpack.d.ts +11 -0
- package/dist/commands/unpack.js +166 -0
- package/dist/commands/validate.d.ts +13 -0
- package/dist/commands/validate.js +310 -0
- package/dist/commands/watch.d.ts +9 -0
- package/dist/commands/watch.js +267 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +66 -0
- package/dist/formatters/diff-formatter.d.ts +5 -0
- package/dist/formatters/diff-formatter.js +43 -0
- package/dist/formatters/type-formatter.d.ts +22 -0
- package/dist/formatters/type-formatter.js +135 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +419 -0
- package/dist/local-codegen.d.ts +22 -0
- package/dist/local-codegen.js +762 -0
- package/dist/ui/badges.d.ts +16 -0
- package/dist/ui/badges.js +71 -0
- package/dist/ui/helpers.d.ts +13 -0
- package/dist/ui/helpers.js +85 -0
- package/package.json +23 -0
- package/src/api-client.ts +407 -0
- package/src/commands/annotate.ts +450 -0
- package/src/commands/audit.ts +103 -0
- package/src/commands/auto.ts +268 -0
- package/src/commands/capture.ts +257 -0
- package/src/commands/check.ts +437 -0
- package/src/commands/codegen.ts +128 -0
- package/src/commands/coverage.ts +170 -0
- package/src/commands/dashboard.ts +46 -0
- package/src/commands/dev.ts +323 -0
- package/src/commands/diff.ts +99 -0
- package/src/commands/docs.ts +392 -0
- package/src/commands/errors.ts +205 -0
- package/src/commands/export.ts +287 -0
- package/src/commands/functions.ts +81 -0
- package/src/commands/infer.ts +260 -0
- package/src/commands/init.ts +419 -0
- package/src/commands/mock.ts +220 -0
- package/src/commands/openapi.ts +53 -0
- package/src/commands/overview.ts +310 -0
- package/src/commands/pack.ts +139 -0
- package/src/commands/proxy.ts +314 -0
- package/src/commands/replay.ts +356 -0
- package/src/commands/run.ts +1190 -0
- package/src/commands/sample.ts +259 -0
- package/src/commands/search.ts +107 -0
- package/src/commands/stubs.ts +211 -0
- package/src/commands/tail.ts +94 -0
- package/src/commands/test-gen.ts +236 -0
- package/src/commands/trace.ts +440 -0
- package/src/commands/types.ts +161 -0
- package/src/commands/unpack.ts +179 -0
- package/src/commands/validate.ts +368 -0
- package/src/commands/watch.ts +277 -0
- package/src/config.ts +38 -0
- package/src/formatters/diff-formatter.ts +51 -0
- package/src/formatters/type-formatter.ts +161 -0
- package/src/index.ts +454 -0
- package/src/local-codegen.ts +859 -0
- package/src/ui/badges.ts +66 -0
- package/src/ui/helpers.ts +80 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,82 @@
|
|
|
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.openapiCommand = openapiCommand;
|
|
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
|
+
async function openapiCommand(opts) {
|
|
45
|
+
try {
|
|
46
|
+
const spec = await (0, api_client_1.fetchOpenApiSpec)({
|
|
47
|
+
env: opts.env,
|
|
48
|
+
title: opts.title,
|
|
49
|
+
version: opts.apiVersion,
|
|
50
|
+
serverUrl: opts.server,
|
|
51
|
+
});
|
|
52
|
+
const json = JSON.stringify(spec, null, 2) + "\n";
|
|
53
|
+
if (opts.out) {
|
|
54
|
+
const outPath = path.resolve(opts.out);
|
|
55
|
+
const dir = path.dirname(outPath);
|
|
56
|
+
if (!fs.existsSync(dir)) {
|
|
57
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
fs.writeFileSync(outPath, json, "utf-8");
|
|
60
|
+
console.log("");
|
|
61
|
+
console.log(chalk_1.default.green(` OpenAPI spec written to ${chalk_1.default.bold(opts.out)}`));
|
|
62
|
+
// Show summary
|
|
63
|
+
const specObj = spec;
|
|
64
|
+
const paths = specObj.paths || {};
|
|
65
|
+
const pathCount = Object.keys(paths).length;
|
|
66
|
+
let operationCount = 0;
|
|
67
|
+
for (const methods of Object.values(paths)) {
|
|
68
|
+
operationCount += Object.keys(methods).length;
|
|
69
|
+
}
|
|
70
|
+
console.log(chalk_1.default.gray(` ${pathCount} path${pathCount !== 1 ? "s" : ""}, ${operationCount} operation${operationCount !== 1 ? "s" : ""}`));
|
|
71
|
+
console.log("");
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
process.stdout.write(json);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (err instanceof Error) {
|
|
79
|
+
console.error(chalk_1.default.red(`\n Error: ${err.message}\n`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface OverviewOptions {
|
|
2
|
+
env?: string;
|
|
3
|
+
json?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* `trickle overview` — Compact API overview with inline type signatures.
|
|
7
|
+
*
|
|
8
|
+
* Shows all observed routes with their return type shapes, making it easy to
|
|
9
|
+
* understand your entire API surface at a glance. Like `git log --oneline` for APIs.
|
|
10
|
+
*/
|
|
11
|
+
export declare function overviewCommand(opts: OverviewOptions): Promise<void>;
|
|
@@ -0,0 +1,266 @@
|
|
|
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.overviewCommand = overviewCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const api_client_1 = require("../api-client");
|
|
9
|
+
const config_1 = require("../config");
|
|
10
|
+
const helpers_1 = require("../ui/helpers");
|
|
11
|
+
/**
|
|
12
|
+
* `trickle overview` — Compact API overview with inline type signatures.
|
|
13
|
+
*
|
|
14
|
+
* Shows all observed routes with their return type shapes, making it easy to
|
|
15
|
+
* understand your entire API surface at a glance. Like `git log --oneline` for APIs.
|
|
16
|
+
*/
|
|
17
|
+
async function overviewCommand(opts) {
|
|
18
|
+
const backendUrl = (0, config_1.getBackendUrl)();
|
|
19
|
+
// Check backend
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(`${backendUrl}/api/health`, { signal: AbortSignal.timeout(3000) });
|
|
22
|
+
if (!res.ok)
|
|
23
|
+
throw new Error("not ok");
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
console.error(chalk_1.default.red(`\n Cannot reach trickle backend at ${chalk_1.default.bold(backendUrl)}\n`));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
// Fetch all functions
|
|
30
|
+
const result = await (0, api_client_1.listFunctions)({ env: opts.env, limit: 500 });
|
|
31
|
+
const { functions } = result;
|
|
32
|
+
if (functions.length === 0) {
|
|
33
|
+
console.log(chalk_1.default.yellow("\n No observed routes yet."));
|
|
34
|
+
console.log(chalk_1.default.gray(" Run ") + chalk_1.default.white("trickle capture") + chalk_1.default.gray(" or ") + chalk_1.default.white("trickle dev") + chalk_1.default.gray(" to start observing.\n"));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Fetch latest type snapshot for each function
|
|
38
|
+
const routes = [];
|
|
39
|
+
for (const fn of functions) {
|
|
40
|
+
try {
|
|
41
|
+
const typesResult = await (0, api_client_1.listTypes)(fn.id, { env: opts.env, limit: 1 });
|
|
42
|
+
const snapshot = typesResult.snapshots[0];
|
|
43
|
+
const returnType = snapshot
|
|
44
|
+
? (typeof snapshot.return_type === "string"
|
|
45
|
+
? JSON.parse(snapshot.return_type)
|
|
46
|
+
: snapshot.return_type)
|
|
47
|
+
: null;
|
|
48
|
+
const argsType = snapshot
|
|
49
|
+
? (typeof snapshot.args_type === "string"
|
|
50
|
+
? JSON.parse(snapshot.args_type)
|
|
51
|
+
: snapshot.args_type)
|
|
52
|
+
: null;
|
|
53
|
+
const { method, path: routePath } = parseRoute(fn.function_name);
|
|
54
|
+
routes.push({
|
|
55
|
+
name: fn.function_name,
|
|
56
|
+
method,
|
|
57
|
+
path: routePath,
|
|
58
|
+
module: fn.module,
|
|
59
|
+
environment: fn.environment,
|
|
60
|
+
lastSeen: fn.last_seen_at,
|
|
61
|
+
argsSignature: argsType ? compactSignature(argsType, 60) : "",
|
|
62
|
+
returnSignature: returnType ? compactSignature(returnType, 60) : "unknown",
|
|
63
|
+
fieldCount: returnType ? countFields(returnType) : 0,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Skip functions with errors
|
|
68
|
+
const { method, path: routePath } = parseRoute(fn.function_name);
|
|
69
|
+
routes.push({
|
|
70
|
+
name: fn.function_name,
|
|
71
|
+
method,
|
|
72
|
+
path: routePath,
|
|
73
|
+
module: fn.module,
|
|
74
|
+
environment: fn.environment,
|
|
75
|
+
lastSeen: fn.last_seen_at,
|
|
76
|
+
argsSignature: "",
|
|
77
|
+
returnSignature: "?",
|
|
78
|
+
fieldCount: 0,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// JSON output
|
|
83
|
+
if (opts.json) {
|
|
84
|
+
console.log(JSON.stringify({ routes, total: routes.length }, null, 2));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Group by module
|
|
88
|
+
const byModule = new Map();
|
|
89
|
+
for (const r of routes) {
|
|
90
|
+
const mod = r.module || "default";
|
|
91
|
+
if (!byModule.has(mod))
|
|
92
|
+
byModule.set(mod, []);
|
|
93
|
+
byModule.get(mod).push(r);
|
|
94
|
+
}
|
|
95
|
+
console.log("");
|
|
96
|
+
console.log(chalk_1.default.bold(" trickle overview"));
|
|
97
|
+
console.log(chalk_1.default.gray(" " + "─".repeat(60)));
|
|
98
|
+
console.log(chalk_1.default.gray(` ${routes.length} route${routes.length === 1 ? "" : "s"} observed`));
|
|
99
|
+
if (opts.env) {
|
|
100
|
+
console.log(chalk_1.default.gray(` Environment: ${opts.env}`));
|
|
101
|
+
}
|
|
102
|
+
console.log(chalk_1.default.gray(" " + "─".repeat(60)));
|
|
103
|
+
// Find the longest method for alignment
|
|
104
|
+
const maxMethodLen = Math.max(...routes.map((r) => r.method.length));
|
|
105
|
+
const maxPathLen = Math.min(30, Math.max(...routes.map((r) => r.path.length)));
|
|
106
|
+
for (const [mod, modRoutes] of byModule) {
|
|
107
|
+
console.log("");
|
|
108
|
+
if (byModule.size > 1) {
|
|
109
|
+
console.log(chalk_1.default.gray(` ┌─ ${mod}`));
|
|
110
|
+
}
|
|
111
|
+
// Sort routes: GET before POST before PUT before DELETE, then by path
|
|
112
|
+
const methodOrder = { GET: 0, POST: 1, PUT: 2, PATCH: 3, DELETE: 4 };
|
|
113
|
+
modRoutes.sort((a, b) => {
|
|
114
|
+
const orderA = methodOrder[a.method] ?? 5;
|
|
115
|
+
const orderB = methodOrder[b.method] ?? 5;
|
|
116
|
+
if (orderA !== orderB)
|
|
117
|
+
return orderA - orderB;
|
|
118
|
+
return a.path.localeCompare(b.path);
|
|
119
|
+
});
|
|
120
|
+
for (const route of modRoutes) {
|
|
121
|
+
const methodColor = getMethodColor(route.method);
|
|
122
|
+
const methodStr = route.method.padEnd(maxMethodLen);
|
|
123
|
+
const pathStr = route.path.padEnd(maxPathLen);
|
|
124
|
+
const age = (0, helpers_1.relativeTime)(route.lastSeen);
|
|
125
|
+
// Build the line
|
|
126
|
+
const prefix = byModule.size > 1 ? " │ " : " ";
|
|
127
|
+
const line = prefix +
|
|
128
|
+
methodColor(methodStr) +
|
|
129
|
+
" " +
|
|
130
|
+
chalk_1.default.white(pathStr) +
|
|
131
|
+
chalk_1.default.gray(" → ") +
|
|
132
|
+
chalk_1.default.cyan(route.returnSignature) +
|
|
133
|
+
chalk_1.default.gray(` ${age}`);
|
|
134
|
+
console.log(line);
|
|
135
|
+
// Show request body if present and non-empty
|
|
136
|
+
if (route.argsSignature && route.argsSignature !== "{ }") {
|
|
137
|
+
const argsLine = prefix +
|
|
138
|
+
" ".repeat(maxMethodLen) +
|
|
139
|
+
" " +
|
|
140
|
+
" ".repeat(maxPathLen) +
|
|
141
|
+
chalk_1.default.gray(" ← ") +
|
|
142
|
+
chalk_1.default.yellow(route.argsSignature);
|
|
143
|
+
console.log(argsLine);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (byModule.size > 1) {
|
|
147
|
+
console.log(chalk_1.default.gray(" └─"));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
console.log("");
|
|
151
|
+
const totalFields = routes.reduce((sum, r) => sum + r.fieldCount, 0);
|
|
152
|
+
console.log(chalk_1.default.gray(` ${routes.length} routes, ${totalFields} fields observed`) +
|
|
153
|
+
chalk_1.default.gray(` · ${backendUrl}`));
|
|
154
|
+
console.log("");
|
|
155
|
+
}
|
|
156
|
+
function parseRoute(functionName) {
|
|
157
|
+
const spaceIdx = functionName.indexOf(" ");
|
|
158
|
+
if (spaceIdx > 0) {
|
|
159
|
+
return {
|
|
160
|
+
method: functionName.slice(0, spaceIdx),
|
|
161
|
+
path: functionName.slice(spaceIdx + 1),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { method: "", path: functionName };
|
|
165
|
+
}
|
|
166
|
+
function getMethodColor(method) {
|
|
167
|
+
switch (method) {
|
|
168
|
+
case "GET": return chalk_1.default.green;
|
|
169
|
+
case "POST": return chalk_1.default.yellow;
|
|
170
|
+
case "PUT": return chalk_1.default.blue;
|
|
171
|
+
case "PATCH": return chalk_1.default.magenta;
|
|
172
|
+
case "DELETE": return chalk_1.default.red;
|
|
173
|
+
default: return chalk_1.default.white;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Render a compact type signature from a TypeNode.
|
|
178
|
+
* Truncates to maxLen characters.
|
|
179
|
+
*/
|
|
180
|
+
function compactSignature(node, maxLen) {
|
|
181
|
+
const sig = renderCompact(node);
|
|
182
|
+
if (sig.length <= maxLen)
|
|
183
|
+
return sig;
|
|
184
|
+
return sig.slice(0, maxLen - 1) + "…";
|
|
185
|
+
}
|
|
186
|
+
function renderCompact(node) {
|
|
187
|
+
switch (node.kind) {
|
|
188
|
+
case "primitive":
|
|
189
|
+
return node.name;
|
|
190
|
+
case "object": {
|
|
191
|
+
const props = node.properties;
|
|
192
|
+
const keys = Object.keys(props);
|
|
193
|
+
if (keys.length === 0)
|
|
194
|
+
return "{ }";
|
|
195
|
+
const parts = [];
|
|
196
|
+
for (const key of keys) {
|
|
197
|
+
const val = props[key];
|
|
198
|
+
const valStr = renderCompactShort(val);
|
|
199
|
+
parts.push(`${key}: ${valStr}`);
|
|
200
|
+
}
|
|
201
|
+
const full = `{ ${parts.join(", ")} }`;
|
|
202
|
+
if (full.length <= 60)
|
|
203
|
+
return full;
|
|
204
|
+
// Truncate: show first few fields
|
|
205
|
+
let result = "{ ";
|
|
206
|
+
for (let i = 0; i < parts.length; i++) {
|
|
207
|
+
if (i > 0)
|
|
208
|
+
result += ", ";
|
|
209
|
+
if (result.length + parts[i].length > 55 && i > 0) {
|
|
210
|
+
result += `…+${parts.length - i}`;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
result += parts[i];
|
|
214
|
+
}
|
|
215
|
+
return result + " }";
|
|
216
|
+
}
|
|
217
|
+
case "array": {
|
|
218
|
+
const element = node.element;
|
|
219
|
+
return `${renderCompact(element)}[]`;
|
|
220
|
+
}
|
|
221
|
+
case "union": {
|
|
222
|
+
const members = node.members;
|
|
223
|
+
return members.map(renderCompactShort).join(" | ");
|
|
224
|
+
}
|
|
225
|
+
default:
|
|
226
|
+
return node.kind;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function renderCompactShort(node) {
|
|
230
|
+
switch (node.kind) {
|
|
231
|
+
case "primitive":
|
|
232
|
+
return node.name;
|
|
233
|
+
case "object": {
|
|
234
|
+
const props = node.properties;
|
|
235
|
+
const keys = Object.keys(props);
|
|
236
|
+
if (keys.length === 0)
|
|
237
|
+
return "{}";
|
|
238
|
+
if (keys.length <= 3) {
|
|
239
|
+
return `{${keys.join(", ")}}`;
|
|
240
|
+
}
|
|
241
|
+
return `{${keys.slice(0, 2).join(", ")}, …+${keys.length - 2}}`;
|
|
242
|
+
}
|
|
243
|
+
case "array":
|
|
244
|
+
return `${renderCompactShort(node.element)}[]`;
|
|
245
|
+
case "union": {
|
|
246
|
+
const members = node.members;
|
|
247
|
+
return members.map(renderCompactShort).join(" | ");
|
|
248
|
+
}
|
|
249
|
+
default:
|
|
250
|
+
return node.kind;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function countFields(node) {
|
|
254
|
+
if (node.kind === "object") {
|
|
255
|
+
const props = node.properties;
|
|
256
|
+
let count = Object.keys(props).length;
|
|
257
|
+
for (const val of Object.values(props)) {
|
|
258
|
+
count += countFields(val);
|
|
259
|
+
}
|
|
260
|
+
return count;
|
|
261
|
+
}
|
|
262
|
+
if (node.kind === "array") {
|
|
263
|
+
return countFields(node.element);
|
|
264
|
+
}
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PackOptions {
|
|
2
|
+
out?: string;
|
|
3
|
+
env?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* `trickle pack` — Export all observed types as a portable bundle.
|
|
7
|
+
*
|
|
8
|
+
* Creates a JSON file containing all functions and their type snapshots
|
|
9
|
+
* that can be shared, committed to version control, or imported elsewhere.
|
|
10
|
+
*/
|
|
11
|
+
export declare function packCommand(opts: PackOptions): Promise<void>;
|
|
@@ -0,0 +1,133 @@
|
|
|
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.packCommand = packCommand;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const api_client_1 = require("../api-client");
|
|
43
|
+
const config_1 = require("../config");
|
|
44
|
+
/**
|
|
45
|
+
* `trickle pack` — Export all observed types as a portable bundle.
|
|
46
|
+
*
|
|
47
|
+
* Creates a JSON file containing all functions and their type snapshots
|
|
48
|
+
* that can be shared, committed to version control, or imported elsewhere.
|
|
49
|
+
*/
|
|
50
|
+
async function packCommand(opts) {
|
|
51
|
+
const backendUrl = (0, config_1.getBackendUrl)();
|
|
52
|
+
// Check backend
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetch(`${backendUrl}/api/health`, { signal: AbortSignal.timeout(3000) });
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
throw new Error("not ok");
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
console.error(chalk_1.default.red(`\n Cannot reach trickle backend at ${chalk_1.default.bold(backendUrl)}\n`));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
// Use stderr for status when writing JSON to stdout
|
|
63
|
+
const log = opts.out ? console.log : (...args) => process.stderr.write(args.join(" ") + "\n");
|
|
64
|
+
log("");
|
|
65
|
+
log(chalk_1.default.bold(" trickle pack"));
|
|
66
|
+
log(chalk_1.default.gray(" " + "─".repeat(50)));
|
|
67
|
+
// Fetch all functions
|
|
68
|
+
const result = await (0, api_client_1.listFunctions)({ env: opts.env, limit: 10000 });
|
|
69
|
+
const { functions } = result;
|
|
70
|
+
if (functions.length === 0) {
|
|
71
|
+
log(chalk_1.default.yellow(" No observed types to pack."));
|
|
72
|
+
log(chalk_1.default.gray(" Run ") + chalk_1.default.white("trickle capture") + chalk_1.default.gray(" or ") + chalk_1.default.white("trickle dev") + chalk_1.default.gray(" first.\n"));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
log(chalk_1.default.gray(` Packing ${functions.length} functions...`));
|
|
76
|
+
// Fetch snapshots for each function
|
|
77
|
+
const packedFunctions = [];
|
|
78
|
+
let totalSnapshots = 0;
|
|
79
|
+
for (const fn of functions) {
|
|
80
|
+
const typesResult = await (0, api_client_1.listTypes)(fn.id, { env: opts.env, limit: 100 });
|
|
81
|
+
const snapshots = [];
|
|
82
|
+
for (const snap of typesResult.snapshots) {
|
|
83
|
+
snapshots.push({
|
|
84
|
+
typeHash: snap.type_hash,
|
|
85
|
+
env: snap.env,
|
|
86
|
+
argsType: snap.args_type,
|
|
87
|
+
returnType: snap.return_type,
|
|
88
|
+
sampleInput: snap.sample_input || undefined,
|
|
89
|
+
sampleOutput: snap.sample_output || undefined,
|
|
90
|
+
observedAt: snap.observed_at,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (snapshots.length > 0) {
|
|
94
|
+
packedFunctions.push({
|
|
95
|
+
functionName: fn.function_name,
|
|
96
|
+
module: fn.module,
|
|
97
|
+
language: fn.language,
|
|
98
|
+
environment: fn.environment,
|
|
99
|
+
snapshots,
|
|
100
|
+
});
|
|
101
|
+
totalSnapshots += snapshots.length;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const bundle = {
|
|
105
|
+
version: 1,
|
|
106
|
+
createdAt: new Date().toISOString(),
|
|
107
|
+
source: backendUrl,
|
|
108
|
+
functions: packedFunctions,
|
|
109
|
+
stats: {
|
|
110
|
+
totalFunctions: packedFunctions.length,
|
|
111
|
+
totalSnapshots,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
const json = JSON.stringify(bundle, null, 2);
|
|
115
|
+
if (opts.out) {
|
|
116
|
+
fs.writeFileSync(opts.out, json, "utf-8");
|
|
117
|
+
log(chalk_1.default.green(` Packed ${packedFunctions.length} functions (${totalSnapshots} snapshots)`));
|
|
118
|
+
log(chalk_1.default.gray(` Written to ${opts.out}`));
|
|
119
|
+
const sizeKb = (Buffer.byteLength(json, "utf-8") / 1024).toFixed(1);
|
|
120
|
+
log(chalk_1.default.gray(` Size: ${sizeKb}KB`));
|
|
121
|
+
log("");
|
|
122
|
+
log(chalk_1.default.gray(" Share this file or import it with:"));
|
|
123
|
+
log(chalk_1.default.white(` trickle unpack ${opts.out}`));
|
|
124
|
+
log("");
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Write JSON to stdout for piping
|
|
128
|
+
process.stdout.write(json + "\n");
|
|
129
|
+
// Summary to stderr
|
|
130
|
+
log(chalk_1.default.green(` Packed ${packedFunctions.length} functions (${totalSnapshots} snapshots)`));
|
|
131
|
+
log("");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ProxyOptions {
|
|
2
|
+
target: string;
|
|
3
|
+
port?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* `trickle proxy` — Transparent reverse proxy that captures API types.
|
|
7
|
+
*
|
|
8
|
+
* Sits between the frontend and backend, forwarding all requests while
|
|
9
|
+
* observing request/response shapes and sending type observations to
|
|
10
|
+
* the trickle backend. Works with any backend language or framework —
|
|
11
|
+
* no instrumentation needed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function proxyCommand(opts: ProxyOptions): Promise<void>;
|