apitweet-cli 0.1.1

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/src/utils.js ADDED
@@ -0,0 +1,164 @@
1
+ export function exitWithError(message, details) {
2
+ console.error(message);
3
+ if (details) {
4
+ console.error(details);
5
+ }
6
+ process.exit(1);
7
+ }
8
+
9
+ export function parseJson(label, value) {
10
+ try {
11
+ return JSON.parse(value);
12
+ } catch (error) {
13
+ exitWithError(`Invalid JSON for ${label}.`, error.message);
14
+ }
15
+ }
16
+
17
+ export function parseHeader(value) {
18
+ const separator = value.indexOf(":");
19
+ if (separator === -1) {
20
+ exitWithError(`Invalid header format: ${value}`, 'Expected "Name: Value".');
21
+ }
22
+
23
+ const name = value.slice(0, separator).trim();
24
+ const headerValue = value.slice(separator + 1).trim();
25
+ if (!name) {
26
+ exitWithError(`Invalid header format: ${value}`, "Header name cannot be empty.");
27
+ }
28
+
29
+ return [name, headerValue];
30
+ }
31
+
32
+ export function findOption(args, name) {
33
+ const index = args.indexOf(name);
34
+ if (index === -1) {
35
+ return "";
36
+ }
37
+
38
+ const value = args[index + 1];
39
+ if (!value) {
40
+ exitWithError(`Missing value for ${name}.`);
41
+ }
42
+ return value;
43
+ }
44
+
45
+ export function findAllOptions(args, name) {
46
+ const values = [];
47
+ for (let index = 0; index < args.length; index += 1) {
48
+ if (args[index] !== name) {
49
+ continue;
50
+ }
51
+
52
+ const value = args[index + 1];
53
+ if (!value) {
54
+ exitWithError(`Missing value for ${name}.`);
55
+ }
56
+ values.push(value);
57
+ index += 1;
58
+ }
59
+ return values;
60
+ }
61
+
62
+ export function hasFlag(args, ...flags) {
63
+ return flags.some((flag) => args.includes(flag));
64
+ }
65
+
66
+ export function collectPositionals(args, startIndex, optionsWithValues = [], booleanFlags = []) {
67
+ const values = [];
68
+
69
+ for (let index = startIndex; index < args.length; index += 1) {
70
+ const current = args[index];
71
+ if (optionsWithValues.includes(current)) {
72
+ index += 1;
73
+ continue;
74
+ }
75
+ if (booleanFlags.includes(current)) {
76
+ continue;
77
+ }
78
+ if (current.startsWith("--")) {
79
+ continue;
80
+ }
81
+ values.push(current);
82
+ }
83
+
84
+ return values;
85
+ }
86
+
87
+ export function readCountOption(args, fallback) {
88
+ const value = findOption(args, "--count");
89
+ if (!value) {
90
+ return fallback;
91
+ }
92
+
93
+ const count = Number.parseInt(value, 10);
94
+ if (!Number.isFinite(count) || count <= 0) {
95
+ exitWithError(`Invalid --count value: ${value}`);
96
+ }
97
+ return count;
98
+ }
99
+
100
+ export function requireCommandValue(value, usage) {
101
+ if (!value) {
102
+ exitWithError(usage);
103
+ }
104
+ return value;
105
+ }
106
+
107
+ export function maskSecret(value) {
108
+ if (!value) {
109
+ return "";
110
+ }
111
+ if (value.length <= 8) {
112
+ return "*".repeat(value.length);
113
+ }
114
+ return `${value.slice(0, 4)}...${value.slice(-4)}`;
115
+ }
116
+
117
+ export function sanitizeForOutput(value, key = "") {
118
+ if (Array.isArray(value)) {
119
+ return value.map((item) => sanitizeForOutput(item, key));
120
+ }
121
+
122
+ if (value && typeof value === "object") {
123
+ return Object.fromEntries(
124
+ Object.entries(value).map(([entryKey, entryValue]) => [entryKey, sanitizeForOutput(entryValue, entryKey)]),
125
+ );
126
+ }
127
+
128
+ if (typeof value === "string") {
129
+ const lowered = key.toLowerCase();
130
+ if (
131
+ lowered.includes("authorization") ||
132
+ (lowered.includes("api") && lowered.includes("key")) ||
133
+ lowered.includes("cookie") ||
134
+ lowered.includes("token")
135
+ ) {
136
+ return maskSecret(value);
137
+ }
138
+ }
139
+
140
+ return value;
141
+ }
142
+
143
+ export function printJson(value) {
144
+ console.log(JSON.stringify(value, null, 2));
145
+ }
146
+
147
+ export function normalizePath(requestPath) {
148
+ if (/^https?:\/\//i.test(requestPath)) {
149
+ return requestPath;
150
+ }
151
+
152
+ return requestPath.startsWith("/") ? requestPath : `/${requestPath}`;
153
+ }
154
+
155
+ export function apiPath(requestPath) {
156
+ const normalized = normalizePath(requestPath);
157
+ if (/^https?:\/\//i.test(normalized)) {
158
+ return normalized;
159
+ }
160
+ if (normalized.startsWith("/api/") || normalized === "/api") {
161
+ return normalized;
162
+ }
163
+ return `/api${normalized}`;
164
+ }