@youcan/cli-kit 2.8.0 → 2.8.2

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.
@@ -1,53 +1,37 @@
1
- import { Buffer } from 'node:buffer';
2
- import { Writable } from 'node:stream';
3
- import dayjs from 'dayjs';
4
- import 'change-case';
5
- import './cli.js';
6
- import 'conf';
7
- import 'env-paths';
8
- import './filesystem.js';
9
- import 'formdata-node';
10
- import 'formdata-node/file-from-path';
11
- import 'simple-git';
12
- import 'execa';
13
- import 'find-process';
14
- import 'get-port';
15
- import 'tcp-port-used';
16
- import 'node-fetch';
17
- import 'ramda';
18
- import 'kleur';
19
- import { renderDevOutput } from '../ui/components/DevOutput.js';
20
- import 'react';
21
- import 'ink';
22
-
23
- class Logger extends Writable {
24
- type;
25
- color;
26
- constructor(type, color) {
27
- super();
28
- this.type = type;
29
- this.color = color;
30
- }
31
- write(chunk) {
32
- if (!(chunk instanceof Buffer) && typeof chunk !== 'string') {
33
- return false;
34
- }
35
- const time = dayjs().format('HH:mm:ss:SSS');
36
- const lines = chunk.toString().split('\n').map(s => s.trim()).filter(s => s !== '');
37
- for (const line of lines) {
38
- renderDevOutput.outputSubject.emit({
39
- timestamp: time,
40
- color: this.color,
41
- label: this.type,
42
- buffer: line,
43
- });
44
- }
45
- return true;
46
- }
47
- }
48
- class Abstract {
49
- async cleanup() {
50
- }
51
- }
52
-
53
- export { Abstract, Logger };
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { renderDevOutput } from "../ui/components/DevOutput.js";
3
+ import "../index.js";
4
+ import { Buffer } from "node:buffer";
5
+ import { Writable } from "node:stream";
6
+ import dayjs from "dayjs";
7
+ //#region lib/node/worker.ts
8
+ var worker_exports = /* @__PURE__ */ __exportAll({
9
+ Abstract: () => Abstract,
10
+ Logger: () => Logger
11
+ });
12
+ var Logger = class extends Writable {
13
+ type;
14
+ color;
15
+ constructor(type, color) {
16
+ super();
17
+ this.type = type;
18
+ this.color = color;
19
+ }
20
+ write(chunk) {
21
+ if (!(chunk instanceof Buffer) && typeof chunk !== "string") return false;
22
+ const time = dayjs().format("HH:mm:ss:SSS");
23
+ const lines = chunk.toString().split("\n").map((s) => s.trim()).filter((s) => s !== "");
24
+ for (const line of lines) renderDevOutput.outputSubject.emit({
25
+ timestamp: time,
26
+ color: this.color,
27
+ label: this.type,
28
+ buffer: line
29
+ });
30
+ return true;
31
+ }
32
+ };
33
+ var Abstract = class {
34
+ async cleanup() {}
35
+ };
36
+ //#endregion
37
+ export { Abstract, Logger, worker_exports };
@@ -1,221 +1,180 @@
1
- import { Buffer } from 'node:buffer';
2
- import { createWriteStream } from 'node:fs';
3
- import process from 'node:process';
4
- import { Writable, Readable } from 'node:stream';
5
- import { pipeline } from 'node:stream/promises';
6
- import { fileURLToPath } from 'node:url';
7
- import 'change-case';
8
- import '../node/cli.js';
9
- import 'conf';
10
- import 'env-paths';
11
- import { isExecutable, mkdir, chmod, tapIntoTmp, decompressGzip, extractTar, move } from '../node/filesystem.js';
12
- import 'formdata-node';
13
- import 'formdata-node/file-from-path';
14
- import 'simple-git';
15
- import { exec } from '../node/system.js';
16
- import 'node-fetch';
17
- import 'ramda';
18
- import { join, dirname, basename, resolve } from '../node/path.js';
19
- import 'kleur';
20
- import 'dayjs';
21
- import '../ui/components/DevOutput.js';
22
- import 'react';
23
- import 'ink';
24
-
25
- const LINUX_TARGET_NAMES = {
26
- arm64: 'cloudflared-linux-arm64',
27
- arm: 'cloudflared-linux-arm',
28
- x64: 'cloudflared-linux-amd64',
29
- ia32: 'cloudflared-linux-386',
30
- };
31
- const MACOS_TARGET_NAMES = {
32
- arm64: 'cloudflared-darwin-arm64.tgz',
33
- x64: 'cloudflared-darwin-amd64.tgz',
34
- };
35
- const WINDOWS_TARGET_NAMES = {
36
- x64: 'cloudflared-windows-amd64.exe',
37
- ia32: 'cloudflared-windows-386.exe',
38
- arm64: 'cloudflared-windows-amd64.exe',
39
- };
1
+ import { chmod, decompressGzip, extractTar, isExecutable, mkdir, move, tapIntoTmp } from "../node/filesystem.js";
2
+ import { exec } from "../node/system.js";
3
+ import { basename, dirname, join, resolve } from "../node/path.js";
4
+ import "../index.js";
5
+ import process from "node:process";
6
+ import { createWriteStream } from "node:fs";
7
+ import { pipeline } from "node:stream/promises";
8
+ import { Buffer } from "node:buffer";
9
+ import { Readable, Writable } from "node:stream";
10
+ import { fileURLToPath } from "node:url";
11
+ //#region lib/services/cloudflared.ts
40
12
  const TARGET_NAMES = {
41
- linux: LINUX_TARGET_NAMES,
42
- darwin: MACOS_TARGET_NAMES,
43
- win32: WINDOWS_TARGET_NAMES,
13
+ linux: {
14
+ arm64: "cloudflared-linux-arm64",
15
+ arm: "cloudflared-linux-arm",
16
+ x64: "cloudflared-linux-amd64",
17
+ ia32: "cloudflared-linux-386"
18
+ },
19
+ darwin: {
20
+ arm64: "cloudflared-darwin-arm64.tgz",
21
+ x64: "cloudflared-darwin-amd64.tgz"
22
+ },
23
+ win32: {
24
+ x64: "cloudflared-windows-amd64.exe",
25
+ ia32: "cloudflared-windows-386.exe",
26
+ arm64: "cloudflared-windows-amd64.exe"
27
+ }
44
28
  };
45
29
  function composeDownloadUrl(platform, arch) {
46
- const releaseDownloadUrl = 'https://github.com/cloudflare/cloudflared/releases/download';
47
- const supportedVersion = '2024.11.1';
48
- const filename = TARGET_NAMES[platform][arch];
49
- return `${releaseDownloadUrl}/${supportedVersion}/${filename}`;
30
+ return `https://github.com/cloudflare/cloudflared/releases/download/2024.11.1/${TARGET_NAMES[platform][arch]}`;
50
31
  }
51
32
  function resolveBinaryPath(platform) {
52
- const rootDir = fileURLToPath(new URL('../'.repeat(2), import.meta.url));
53
- return join(rootDir, 'bin', platform === 'win32' ? 'cloudflared.exe' : 'cloudflared');
33
+ return join(fileURLToPath(new URL("../".repeat(2), import.meta.url)), "bin", platform === "win32" ? "cloudflared.exe" : "cloudflared");
54
34
  }
55
35
  function isPlatformSupported(platform) {
56
- return platform in TARGET_NAMES;
36
+ return platform in TARGET_NAMES;
57
37
  }
58
38
  function isArchSupported(arch, platform) {
59
- return arch in TARGET_NAMES[platform];
39
+ return arch in TARGET_NAMES[platform];
60
40
  }
61
41
  async function downloadFromRelease(url, downloadPath) {
62
- const response = await fetch(url, { redirect: 'follow' });
63
- if (!response.ok) {
64
- throw new Error(`failed to download cloudflared: ${response.statusText}`);
65
- }
66
- const { body } = response;
67
- const fileWriteStream = createWriteStream(downloadPath, { mode: 0o664 });
68
- await pipeline(Readable.fromWeb(body), fileWriteStream);
42
+ const response = await fetch(url, { redirect: "follow" });
43
+ if (!response.ok) throw new Error(`failed to download cloudflared: ${response.statusText}`);
44
+ const { body } = response;
45
+ const fileWriteStream = createWriteStream(downloadPath, { mode: 436 });
46
+ await pipeline(Readable.fromWeb(body), fileWriteStream);
69
47
  }
70
48
  async function installForMacOs(url, destination) {
71
- await tapIntoTmp(async (tmpDir) => {
72
- const parentDir = dirname(destination);
73
- const binaryName = basename(destination);
74
- const downloadedFile = resolve(tmpDir, `${binaryName}.tgz`);
75
- const decompressedFile = resolve(tmpDir, `${binaryName}.gz`);
76
- await mkdir(parentDir);
77
- await mkdir(tmpDir);
78
- await downloadFromRelease(url, downloadedFile);
79
- await decompressGzip(downloadedFile, decompressedFile);
80
- await extractTar(decompressedFile, tmpDir, 0o755);
81
- await move(resolve(tmpDir, binaryName), destination, { overwrite: true });
82
- });
49
+ await tapIntoTmp(async (tmpDir) => {
50
+ const parentDir = dirname(destination);
51
+ const binaryName = basename(destination);
52
+ const downloadedFile = resolve(tmpDir, `${binaryName}.tgz`);
53
+ const decompressedFile = resolve(tmpDir, `${binaryName}.gz`);
54
+ await mkdir(parentDir);
55
+ await mkdir(tmpDir);
56
+ await downloadFromRelease(url, downloadedFile);
57
+ await decompressGzip(downloadedFile, decompressedFile);
58
+ await extractTar(decompressedFile, tmpDir, 493);
59
+ await move(resolve(tmpDir, binaryName), destination, { overwrite: true });
60
+ });
83
61
  }
84
62
  async function installForLinux(url, destination) {
85
- const parentDir = dirname(destination);
86
- await mkdir(parentDir);
87
- await downloadFromRelease(url, destination);
88
- await chmod(destination, 0o755);
63
+ await mkdir(dirname(destination));
64
+ await downloadFromRelease(url, destination);
65
+ await chmod(destination, 493);
89
66
  }
90
67
  async function installForWindows(url, destination) {
91
- const parentDir = dirname(destination);
92
- mkdir(parentDir);
93
- await downloadFromRelease(url, destination);
68
+ mkdir(dirname(destination));
69
+ await downloadFromRelease(url, destination);
94
70
  }
95
71
  async function install(platform, downloadUrl, destinationPath) {
96
- switch (platform) {
97
- case 'darwin':
98
- await installForMacOs(downloadUrl, destinationPath);
99
- break;
100
- case 'linux':
101
- await installForLinux(downloadUrl, destinationPath);
102
- break;
103
- case 'win32':
104
- await installForWindows(downloadUrl, destinationPath);
105
- break;
106
- }
107
- }
108
- class OutputStream extends Writable {
109
- tunnelUrl = null;
110
- tunnelError = null;
111
- buffer = '';
112
- static ErrorsRegex = [
113
- /failed to build quick tunnel request/,
114
- /failed to request quick Tunnel/,
115
- /failed to read quick-tunnel response/,
116
- /failed to parse quick Tunnel ID/,
117
- /Couldn't start tunnel/,
118
- ];
119
- write(chunk, encoding, callback) {
120
- if (!(chunk instanceof Buffer) && typeof chunk !== 'string') {
121
- return false;
122
- }
123
- this.buffer += chunk.toString();
124
- if (this.tunnelUrl === null) {
125
- this.tunnelUrl = this.extractTunnelUrl();
126
- }
127
- if (callback && typeof callback === 'function') {
128
- callback();
129
- }
130
- return true;
131
- }
132
- extractTunnelUrl() {
133
- const regex = /https:\/\/(?!api\.trycloudflare\.com)\S+\.trycloudflare\.com/;
134
- return this.buffer.match(regex)?.[0] || null;
135
- }
136
- extractError() {
137
- for (const errorRegex of OutputStream.ErrorsRegex) {
138
- if (errorRegex.test(this.buffer)) {
139
- return errorRegex.source;
140
- }
141
- }
142
- return null;
143
- }
144
- getTunnelUrl() {
145
- return this.tunnelUrl;
146
- }
147
- clearBuffer() {
148
- this.buffer = '';
149
- }
150
- getBuffer() {
151
- return this.buffer;
152
- }
72
+ switch (platform) {
73
+ case "darwin":
74
+ await installForMacOs(downloadUrl, destinationPath);
75
+ break;
76
+ case "linux":
77
+ await installForLinux(downloadUrl, destinationPath);
78
+ break;
79
+ case "win32":
80
+ await installForWindows(downloadUrl, destinationPath);
81
+ break;
82
+ }
153
83
  }
154
- class Cloudflared {
155
- bin;
156
- system;
157
- output = new OutputStream();
158
- constructor() {
159
- const platform = process.platform;
160
- const arch = process.arch;
161
- if (!isPlatformSupported(platform)) {
162
- throw new Error(`unsupported platform: ${platform}`);
163
- }
164
- if (!isArchSupported(arch, platform)) {
165
- throw new Error(`unsupported architecture: ${arch}`);
166
- }
167
- this.bin = resolveBinaryPath(platform);
168
- this.system = { platform, arch };
169
- }
170
- async tunnel(port, host = 'localhost', signal) {
171
- await this.install();
172
- const { bin, args } = this.composeTunnelingCommand(port, host);
173
- this.exec(bin, args, 3, signal);
174
- }
175
- async install() {
176
- if (await isExecutable(this.bin)) {
177
- return;
178
- }
179
- const downloadUrl = composeDownloadUrl(this.system.platform, this.system.arch);
180
- await install(this.system.platform, downloadUrl, this.bin);
181
- if (!(await isExecutable(this.bin))) {
182
- throw new Error(`Failed to install executable cloudflared binary at ${this.bin}. Check file permissions and platform compatibility.`);
183
- }
184
- }
185
- composeTunnelingCommand(port, host = 'localhost') {
186
- return {
187
- bin: this.bin,
188
- args: ['tunnel', `--url=${host}:${port}`, '--no-autoupdate'],
189
- };
190
- }
191
- async exec(bin, args, maxRetries = 3, signal) {
192
- if (this.getUrl()) {
193
- return;
194
- }
195
- if (maxRetries === 0) {
196
- const extractedError = this.output.extractError();
197
- const errorMessage = extractedError
198
- ? `cloudflared failed: ${extractedError}`
199
- : `cloudflared failed for unknown reason. Binary: ${bin}, Args: ${args.join(' ')}, Buffer: ${this.output.getBuffer()}`;
200
- throw new Error(errorMessage);
201
- }
202
- this.output.clearBuffer();
203
- await exec(bin, args, {
204
- // Weird choice of cloudflared to write to stderr.
205
- stderr: this.output,
206
- signal,
207
- errorHandler: async (error) => {
208
- console.error(`cloudflared execution error (retries left: ${maxRetries - 1}):`, error);
209
- await this.exec(bin, args, maxRetries - 1, signal);
210
- },
211
- });
212
- }
213
- getUrl() {
214
- return this.output.getTunnelUrl();
215
- }
216
- getError() {
217
- return this.output.extractError();
218
- }
219
- }
220
-
84
+ var OutputStream = class OutputStream extends Writable {
85
+ tunnelUrl = null;
86
+ tunnelError = null;
87
+ buffer = "";
88
+ static ErrorsRegex = [
89
+ /failed to build quick tunnel request/,
90
+ /failed to request quick Tunnel/,
91
+ /failed to read quick-tunnel response/,
92
+ /failed to parse quick Tunnel ID/,
93
+ /Couldn't start tunnel/
94
+ ];
95
+ write(chunk, encoding, callback) {
96
+ if (!(chunk instanceof Buffer) && typeof chunk !== "string") return false;
97
+ this.buffer += chunk.toString();
98
+ if (this.tunnelUrl === null) this.tunnelUrl = this.extractTunnelUrl();
99
+ if (callback && typeof callback === "function") callback();
100
+ return true;
101
+ }
102
+ extractTunnelUrl() {
103
+ return this.buffer.match(/https:\/\/(?!api\.trycloudflare\.com)\S+\.trycloudflare\.com/)?.[0] || null;
104
+ }
105
+ extractError() {
106
+ for (const errorRegex of OutputStream.ErrorsRegex) if (errorRegex.test(this.buffer)) return errorRegex.source;
107
+ return null;
108
+ }
109
+ getTunnelUrl() {
110
+ return this.tunnelUrl;
111
+ }
112
+ clearBuffer() {
113
+ this.buffer = "";
114
+ }
115
+ getBuffer() {
116
+ return this.buffer;
117
+ }
118
+ };
119
+ var Cloudflared = class {
120
+ bin;
121
+ system;
122
+ output = new OutputStream();
123
+ constructor() {
124
+ const platform = process.platform;
125
+ const arch = process.arch;
126
+ if (!isPlatformSupported(platform)) throw new Error(`unsupported platform: ${platform}`);
127
+ if (!isArchSupported(arch, platform)) throw new Error(`unsupported architecture: ${arch}`);
128
+ this.bin = resolveBinaryPath(platform);
129
+ this.system = {
130
+ platform,
131
+ arch
132
+ };
133
+ }
134
+ async tunnel(port, host = "localhost", signal) {
135
+ await this.install();
136
+ const { bin, args } = this.composeTunnelingCommand(port, host);
137
+ this.exec(bin, args, 3, signal);
138
+ }
139
+ async install() {
140
+ if (await isExecutable(this.bin)) return;
141
+ const downloadUrl = composeDownloadUrl(this.system.platform, this.system.arch);
142
+ await install(this.system.platform, downloadUrl, this.bin);
143
+ if (!await isExecutable(this.bin)) throw new Error(`Failed to install executable cloudflared binary at ${this.bin}. Check file permissions and platform compatibility.`);
144
+ }
145
+ composeTunnelingCommand(port, host = "localhost") {
146
+ return {
147
+ bin: this.bin,
148
+ args: [
149
+ "tunnel",
150
+ `--url=${host}:${port}`,
151
+ "--no-autoupdate"
152
+ ]
153
+ };
154
+ }
155
+ async exec(bin, args, maxRetries = 3, signal) {
156
+ if (this.getUrl()) return;
157
+ if (maxRetries === 0) {
158
+ const extractedError = this.output.extractError();
159
+ const errorMessage = extractedError ? `cloudflared failed: ${extractedError}` : `cloudflared failed for unknown reason. Binary: ${bin}, Args: ${args.join(" ")}, Buffer: ${this.output.getBuffer()}`;
160
+ throw new Error(errorMessage);
161
+ }
162
+ this.output.clearBuffer();
163
+ await exec(bin, args, {
164
+ stderr: this.output,
165
+ signal,
166
+ errorHandler: async (error) => {
167
+ console.error(`cloudflared execution error (retries left: ${maxRetries - 1}):`, error);
168
+ await this.exec(bin, args, maxRetries - 1, signal);
169
+ }
170
+ });
171
+ }
172
+ getUrl() {
173
+ return this.output.getTunnelUrl();
174
+ }
175
+ getError() {
176
+ return this.output.extractError();
177
+ }
178
+ };
179
+ //#endregion
221
180
  export { Cloudflared };