@youcan/cli-kit 2.8.1 → 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,114 +1,96 @@
1
- import { vi, describe, beforeEach, afterAll, it, expect } from 'vitest';
2
- import 'change-case';
3
- import '../node/cli.js';
4
- import 'conf';
5
- import 'env-paths';
6
- import { isExecutable } from '../node/filesystem.js';
7
- import 'formdata-node';
8
- import 'formdata-node/file-from-path';
9
- import 'simple-git';
10
- import { exec } from '../node/system.js';
11
- import 'node-fetch';
12
- import 'ramda';
13
- import 'kleur';
14
- import 'dayjs';
15
- import { Cloudflared } from './cloudflared.js';
16
- import '../ui/components/DevOutput.js';
17
- import 'react';
18
- import 'ink';
19
-
20
- vi.mock('..', () => ({
21
- System: {
22
- exec: vi.fn(),
23
- },
24
- Filesystem: {
25
- isExecutable: vi.fn(),
26
- },
27
- Path: {
28
- join: vi.fn().mockReturnValue('/mock/path/cloudflared'),
29
- },
1
+ import { isExecutable } from "../node/filesystem.js";
2
+ import { exec } from "../node/system.js";
3
+ import { Cloudflared } from "./cloudflared.js";
4
+ import "../index.js";
5
+ import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
6
+ //#region lib/services/cloudflared.test.ts
7
+ vi.mock("..", () => ({
8
+ System: { exec: vi.fn() },
9
+ Filesystem: { isExecutable: vi.fn() },
10
+ Path: { join: vi.fn().mockReturnValue("/mock/path/cloudflared") }
30
11
  }));
31
12
  const originalPlatform = process.platform;
32
13
  const originalArch = process.arch;
33
- describe('cloudflared', () => {
34
- let cloudflared;
35
- let mockAbortController;
36
- beforeEach(() => {
37
- vi.clearAllMocks();
38
- Object.defineProperty(process, 'platform', {
39
- value: 'darwin',
40
- configurable: true,
41
- });
42
- Object.defineProperty(process, 'arch', {
43
- value: 'arm64',
44
- configurable: true,
45
- });
46
- vi.mocked(isExecutable).mockResolvedValue(true);
47
- mockAbortController = new AbortController();
48
- cloudflared = new Cloudflared();
49
- });
50
- afterAll(() => {
51
- Object.defineProperty(process, 'platform', {
52
- value: originalPlatform,
53
- configurable: true,
54
- });
55
- Object.defineProperty(process, 'arch', {
56
- value: originalArch,
57
- configurable: true,
58
- });
59
- });
60
- describe('tunnel', () => {
61
- it('should pass abort signal to System.exec', async () => {
62
- vi.mocked(exec).mockResolvedValue();
63
- await cloudflared.tunnel(3000, 'localhost', mockAbortController.signal);
64
- expect(exec).toHaveBeenCalledWith(expect.any(String), expect.any(Array), expect.objectContaining({
65
- signal: mockAbortController.signal,
66
- }));
67
- });
68
- it('should use correct cloudflared arguments', async () => {
69
- vi.mocked(exec).mockResolvedValue();
70
- await cloudflared.tunnel(3001, 'localhost', mockAbortController.signal);
71
- expect(exec).toHaveBeenCalledWith(expect.any(String), ['tunnel', '--url=localhost:3001', '--no-autoupdate'], expect.any(Object));
72
- });
73
- it('should work without abort signal', async () => {
74
- vi.mocked(exec).mockResolvedValue();
75
- await expect(cloudflared.tunnel(3000)).resolves.toBeUndefined();
76
- expect(exec).toHaveBeenCalledWith(expect.any(String), expect.any(Array), expect.objectContaining({
77
- signal: undefined,
78
- }));
79
- });
80
- it('should use default host when not provided', async () => {
81
- vi.mocked(exec).mockResolvedValue();
82
- await cloudflared.tunnel(3000, undefined, mockAbortController.signal);
83
- expect(exec).toHaveBeenCalledWith(expect.any(String), ['tunnel', '--url=localhost:3000', '--no-autoupdate'], expect.any(Object));
84
- });
85
- it('should check if cloudflared is executable before running', async () => {
86
- vi.mocked(exec).mockResolvedValue();
87
- await cloudflared.tunnel(3000, 'localhost', mockAbortController.signal);
88
- expect(isExecutable).toHaveBeenCalledWith('/mock/path/cloudflared');
89
- });
90
- });
91
- describe('exec retry logic with signal', () => {
92
- it('should retry with the same signal on failure', async () => {
93
- let callCount = 0;
94
- vi.mocked(exec).mockImplementation(async (bin, args, options) => {
95
- callCount++;
96
- if (callCount < 2) {
97
- if (options?.errorHandler) {
98
- await options.errorHandler(new Error('Connection failed'));
99
- }
100
- }
101
- });
102
- await cloudflared.tunnel(3000, 'localhost', mockAbortController.signal);
103
- expect(exec).toHaveBeenCalledTimes(2);
104
- expect(exec).toHaveBeenNthCalledWith(1, expect.any(String), expect.any(Array), expect.objectContaining({ signal: mockAbortController.signal }));
105
- expect(exec).toHaveBeenNthCalledWith(2, expect.any(String), expect.any(Array), expect.objectContaining({ signal: mockAbortController.signal }));
106
- });
107
- it('should handle errors without crashing', async () => {
108
- vi.mocked(exec).mockResolvedValue();
109
- await expect(cloudflared.tunnel(3000, 'localhost', mockAbortController.signal))
110
- .resolves
111
- .toBeUndefined();
112
- });
113
- });
14
+ describe("cloudflared", () => {
15
+ let cloudflared;
16
+ let mockAbortController;
17
+ beforeEach(() => {
18
+ vi.clearAllMocks();
19
+ Object.defineProperty(process, "platform", {
20
+ value: "darwin",
21
+ configurable: true
22
+ });
23
+ Object.defineProperty(process, "arch", {
24
+ value: "arm64",
25
+ configurable: true
26
+ });
27
+ vi.mocked(isExecutable).mockResolvedValue(true);
28
+ mockAbortController = new AbortController();
29
+ cloudflared = new Cloudflared();
30
+ });
31
+ afterAll(() => {
32
+ Object.defineProperty(process, "platform", {
33
+ value: originalPlatform,
34
+ configurable: true
35
+ });
36
+ Object.defineProperty(process, "arch", {
37
+ value: originalArch,
38
+ configurable: true
39
+ });
40
+ });
41
+ describe("tunnel", () => {
42
+ it("should pass abort signal to System.exec", async () => {
43
+ vi.mocked(exec).mockResolvedValue();
44
+ await cloudflared.tunnel(3e3, "localhost", mockAbortController.signal);
45
+ expect(exec).toHaveBeenCalledWith(expect.any(String), expect.any(Array), expect.objectContaining({ signal: mockAbortController.signal }));
46
+ });
47
+ it("should use correct cloudflared arguments", async () => {
48
+ vi.mocked(exec).mockResolvedValue();
49
+ await cloudflared.tunnel(3001, "localhost", mockAbortController.signal);
50
+ expect(exec).toHaveBeenCalledWith(expect.any(String), [
51
+ "tunnel",
52
+ "--url=localhost:3001",
53
+ "--no-autoupdate"
54
+ ], expect.any(Object));
55
+ });
56
+ it("should work without abort signal", async () => {
57
+ vi.mocked(exec).mockResolvedValue();
58
+ await expect(cloudflared.tunnel(3e3)).resolves.toBeUndefined();
59
+ expect(exec).toHaveBeenCalledWith(expect.any(String), expect.any(Array), expect.objectContaining({ signal: void 0 }));
60
+ });
61
+ it("should use default host when not provided", async () => {
62
+ vi.mocked(exec).mockResolvedValue();
63
+ await cloudflared.tunnel(3e3, void 0, mockAbortController.signal);
64
+ expect(exec).toHaveBeenCalledWith(expect.any(String), [
65
+ "tunnel",
66
+ "--url=localhost:3000",
67
+ "--no-autoupdate"
68
+ ], expect.any(Object));
69
+ });
70
+ it("should check if cloudflared is executable before running", async () => {
71
+ vi.mocked(exec).mockResolvedValue();
72
+ await cloudflared.tunnel(3e3, "localhost", mockAbortController.signal);
73
+ expect(isExecutable).toHaveBeenCalledWith("/mock/path/cloudflared");
74
+ });
75
+ });
76
+ describe("exec retry logic with signal", () => {
77
+ it("should retry with the same signal on failure", async () => {
78
+ let callCount = 0;
79
+ vi.mocked(exec).mockImplementation(async (bin, args, options) => {
80
+ callCount++;
81
+ if (callCount < 2) {
82
+ if (options?.errorHandler) await options.errorHandler(/* @__PURE__ */ new Error("Connection failed"));
83
+ }
84
+ });
85
+ await cloudflared.tunnel(3e3, "localhost", mockAbortController.signal);
86
+ expect(exec).toHaveBeenCalledTimes(2);
87
+ expect(exec).toHaveBeenNthCalledWith(1, expect.any(String), expect.any(Array), expect.objectContaining({ signal: mockAbortController.signal }));
88
+ expect(exec).toHaveBeenNthCalledWith(2, expect.any(String), expect.any(Array), expect.objectContaining({ signal: mockAbortController.signal }));
89
+ });
90
+ it("should handle errors without crashing", async () => {
91
+ vi.mocked(exec).mockResolvedValue();
92
+ await expect(cloudflared.tunnel(3e3, "localhost", mockAbortController.signal)).resolves.toBeUndefined();
93
+ });
94
+ });
114
95
  });
96
+ //#endregion
@@ -1 +1,6 @@
1
- export { Cloudflared } from './cloudflared.js';
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { Cloudflared } from "./cloudflared.js";
3
+ //#region lib/services/index.ts
4
+ var services_exports = /* @__PURE__ */ __exportAll({ Cloudflared: () => Cloudflared });
5
+ //#endregion
6
+ export { Cloudflared, services_exports };
@@ -1,60 +1,56 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { render, useInput, Static, Box, Text } from 'ink';
3
- import { HotKeys } from './HotKeys.js';
4
- import { VerticalDivider } from './utils/symbols.js';
5
- import { map } from 'rxjs/operators';
6
- import { ReplaySubject } from 'rxjs';
7
-
8
- class OutputSubject {
9
- subject = new ReplaySubject;
10
- listen(handler) {
11
- let lastLineKey = null;
12
- this.subject.pipe(map(item => {
13
- const currentLineKey = `${item.label}-${item.color}`;
14
- if (currentLineKey !== lastLineKey) {
15
- lastLineKey = currentLineKey;
16
- return { ...item, label: this.pad(item.label, 10) };
17
- }
18
- return { ...item, label: this.pad('', 10) };
19
- }))
20
- .subscribe(handler);
21
- }
22
- emit(data) {
23
- this.subject.next(data);
24
- }
25
- pad(subject, length, char = ' ') {
26
- return subject.padEnd(length, char);
27
- }
28
- }
29
- const outputSubject = new OutputSubject;
1
+ import { VerticalDivider } from "./utils/symbols.js";
2
+ import { HotKeys } from "./HotKeys.js";
3
+ import React, { useEffect, useState } from "react";
4
+ import { Box, Static, Text, render, useInput } from "ink";
5
+ import { map } from "rxjs/operators";
6
+ import { ReplaySubject } from "rxjs";
7
+ //#region lib/ui/components/DevOutput.tsx
8
+ var OutputSubject = class {
9
+ subject = new ReplaySubject();
10
+ listen(handler) {
11
+ let lastLineKey = null;
12
+ this.subject.pipe(map((item) => {
13
+ const currentLineKey = `${item.label}-${item.color}`;
14
+ if (currentLineKey !== lastLineKey) {
15
+ lastLineKey = currentLineKey;
16
+ return {
17
+ ...item,
18
+ label: this.pad(item.label, 10)
19
+ };
20
+ }
21
+ return {
22
+ ...item,
23
+ label: this.pad("", 10)
24
+ };
25
+ })).subscribe(handler);
26
+ }
27
+ emit(data) {
28
+ this.subject.next(data);
29
+ }
30
+ pad(subject, length, char = " ") {
31
+ return subject.padEnd(length, char);
32
+ }
33
+ };
34
+ const outputSubject = new OutputSubject();
30
35
  const DevOutput = ({ cmd, hotKeys = [] }) => {
31
- const [linesBuffers, setLinesBuffers] = useState([]);
32
- useInput((input, key) => {
33
- if (input === 'c' && key.ctrl)
34
- cmd.exit(130);
35
- });
36
- useEffect(() => {
37
- outputSubject.listen((data) => {
38
- setLinesBuffers((previousLines) => [...previousLines, data]);
39
- });
40
- }, []);
41
- return (React.createElement(React.Fragment, null,
42
- React.createElement(Static, { items: linesBuffers }, (line, i) => (React.createElement(Box, { flexDirection: 'column', key: i },
43
- React.createElement(Text, null,
44
- React.createElement(Text, { dimColor: true }, line.timestamp),
45
- ' ',
46
- React.createElement(VerticalDivider, null),
47
- ' ',
48
- React.createElement(Text, { color: line.color }, line.label),
49
- React.createElement(Text, null,
50
- ' ',
51
- React.createElement(VerticalDivider, null),
52
- ' ',
53
- line.buffer))))),
54
- React.createElement(Box, { flexDirection: 'column', paddingTop: 1 },
55
- React.createElement(HotKeys, { hotKeys: hotKeys }))));
36
+ const [linesBuffers, setLinesBuffers] = useState([]);
37
+ useInput((input, key) => {
38
+ if (input === "c" && key.ctrl) cmd.exit(130);
39
+ });
40
+ useEffect(() => {
41
+ outputSubject.listen((data) => {
42
+ setLinesBuffers((previousLines) => [...previousLines, data]);
43
+ });
44
+ }, []);
45
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Static, { items: linesBuffers }, (line, i) => /* @__PURE__ */ React.createElement(Box, {
46
+ flexDirection: "column",
47
+ key: i
48
+ }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, line.timestamp), " ", /* @__PURE__ */ React.createElement(VerticalDivider, null), " ", /* @__PURE__ */ React.createElement(Text, { color: line.color }, line.label), /* @__PURE__ */ React.createElement(Text, null, " ", /* @__PURE__ */ React.createElement(VerticalDivider, null), " ", line.buffer)))), /* @__PURE__ */ React.createElement(Box, {
49
+ flexDirection: "column",
50
+ paddingTop: 1
51
+ }, /* @__PURE__ */ React.createElement(HotKeys, { hotKeys })));
56
52
  };
57
- const renderDevOutput = ((props) => render(React.createElement(DevOutput, { ...props }), { exitOnCtrlC: false }));
53
+ const renderDevOutput = ((props) => render(/* @__PURE__ */ React.createElement(DevOutput, props), { exitOnCtrlC: false }));
58
54
  renderDevOutput.outputSubject = outputSubject;
59
-
55
+ //#endregion
60
56
  export { DevOutput, renderDevOutput };
@@ -1,18 +1,17 @@
1
- import React from 'react';
2
- import { render, Newline, Box, Text } from 'ink';
3
-
1
+ import React from "react";
2
+ import { Box, Newline, Text, render } from "ink";
3
+ //#region lib/ui/components/Error.tsx
4
4
  const Error = ({ message, suggestions = [] }) => {
5
- return (React.createElement(React.Fragment, null,
6
- React.createElement(Newline, null),
7
- React.createElement(Box, { flexDirection: 'column', borderStyle: 'round', borderColor: 'red', paddingLeft: 1 },
8
- React.createElement(Text, { color: 'redBright' }, "Error"),
9
- React.createElement(Text, { color: 'white' }, message),
10
- suggestions.length > 0 && (React.createElement(Box, { marginTop: 1, flexDirection: 'column' },
11
- React.createElement(Text, { color: 'yellow' }, "Suggestions:"),
12
- suggestions.map((suggestion, index) => (React.createElement(Text, { key: index },
13
- " - ",
14
- suggestion))))))));
5
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Newline, null), /* @__PURE__ */ React.createElement(Box, {
6
+ flexDirection: "column",
7
+ borderStyle: "round",
8
+ borderColor: "red",
9
+ paddingLeft: 1
10
+ }, /* @__PURE__ */ React.createElement(Text, { color: "redBright" }, "Error"), /* @__PURE__ */ React.createElement(Text, { color: "white" }, message), suggestions.length > 0 && /* @__PURE__ */ React.createElement(Box, {
11
+ marginTop: 1,
12
+ flexDirection: "column"
13
+ }, /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "Suggestions:"), suggestions.map((suggestion, index) => /* @__PURE__ */ React.createElement(Text, { key: index }, " - ", suggestion)))));
15
14
  };
16
- const renderError = (props) => render(React.createElement(Error, { ...props }));
17
-
15
+ const renderError = (props) => render(/* @__PURE__ */ React.createElement(Error, props));
16
+ //#endregion
18
17
  export { renderError };
@@ -1,25 +1,19 @@
1
- import React from 'react';
2
- import { RightChevron, VerticalDivider } from './utils/symbols.js';
3
- import { render, Box, useInput, Text } from 'ink';
4
-
1
+ import { RightChevron, VerticalDivider } from "./utils/symbols.js";
2
+ import React from "react";
3
+ import { Box, Text, render, useInput } from "ink";
4
+ //#region lib/ui/components/HotKeys.tsx
5
5
  const HotKey = ({ keyboardKey, description, handler }) => {
6
- useInput((input) => {
7
- if (input === keyboardKey)
8
- handler();
9
- });
10
- return (React.createElement(Box, { flexDirection: 'column' },
11
- React.createElement(Text, null,
12
- React.createElement(RightChevron, null),
13
- " Press ",
14
- React.createElement(Text, { dimColor: true }, keyboardKey),
15
- " ",
16
- React.createElement(VerticalDivider, null),
17
- " ",
18
- description)));
6
+ useInput((input) => {
7
+ if (input === keyboardKey) handler();
8
+ });
9
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(RightChevron, null), " Press ", /* @__PURE__ */ React.createElement(Text, { dimColor: true }, keyboardKey), " ", /* @__PURE__ */ React.createElement(VerticalDivider, null), " ", description));
19
10
  };
20
11
  const HotKeys = ({ hotKeys }) => {
21
- return (React.createElement(Box, { flexDirection: 'column' }, hotKeys.map((hotKey) => React.createElement(HotKey, { key: hotKey.keyboardKey, ...hotKey }))));
12
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, hotKeys.map((hotKey) => /* @__PURE__ */ React.createElement(HotKey, {
13
+ key: hotKey.keyboardKey,
14
+ ...hotKey
15
+ })));
22
16
  };
23
- const renderHotKeys = (props) => render(React.createElement(HotKeys, { ...props }), { exitOnCtrlC: false });
24
-
17
+ const renderHotKeys = (props) => render(/* @__PURE__ */ React.createElement(HotKeys, props), { exitOnCtrlC: false });
18
+ //#endregion
25
19
  export { HotKeys, renderHotKeys };
@@ -1,7 +1,7 @@
1
- import { Text } from 'ink';
2
- import React from 'react';
3
-
4
- const VerticalDivider = () => React.createElement(Text, null, "\u2502");
5
- const RightChevron = () => React.createElement(Text, null, "\u203A");
6
-
1
+ import React from "react";
2
+ import { Text } from "ink";
3
+ //#region lib/ui/components/utils/symbols.tsx
4
+ const VerticalDivider = () => /* @__PURE__ */ React.createElement(Text, null, "");
5
+ const RightChevron = () => /* @__PURE__ */ React.createElement(Text, null, "");
6
+ //#endregion
7
7
  export { RightChevron, VerticalDivider };
package/dist/ui/index.js CHANGED
@@ -1,3 +1,12 @@
1
- export { renderDevOutput } from './components/DevOutput.js';
2
- export { renderError } from './components/Error.js';
3
- export { renderHotKeys } from './components/HotKeys.js';
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { renderHotKeys } from "./components/HotKeys.js";
3
+ import { renderDevOutput } from "./components/DevOutput.js";
4
+ import { renderError } from "./components/Error.js";
5
+ //#region lib/ui/index.ts
6
+ var ui_exports = /* @__PURE__ */ __exportAll({
7
+ renderDevOutput: () => renderDevOutput,
8
+ renderError: () => renderError,
9
+ renderHotKeys: () => renderHotKeys
10
+ });
11
+ //#endregion
12
+ export { renderDevOutput, renderError, renderHotKeys, ui_exports };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@youcan/cli-kit",
3
3
  "type": "module",
4
- "version": "2.8.1",
4
+ "version": "2.8.2",
5
5
  "description": "Utilities for the YouCan CLI",
6
6
  "author": "YouCan <contact@youcan.shop> (https://youcan.shop)",
7
7
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "execa": "^6.1.0",
38
38
  "find-process": "^1.4.11",
39
39
  "formdata-node": "^6.0.3",
40
- "fs-extra": "^11.3.4",
40
+ "fs-extra": "^11.3.5",
41
41
  "get-port": "^7.2.0",
42
42
  "glob": "^11.0.0",
43
43
  "ink": "^5.2.1",
@@ -50,7 +50,7 @@
50
50
  "react": "^18.3.1",
51
51
  "rxjs": "^7.8.2",
52
52
  "simple-git": "^3.36.0",
53
- "tar": "^7.5.13",
53
+ "tar": "^7.5.15",
54
54
  "tcp-port-used": "^1.0.2",
55
55
  "tempy": "^3.2.0",
56
56
  "worker": "^0.4.0"
@@ -66,8 +66,8 @@
66
66
  "shx": "^0.3.4"
67
67
  },
68
68
  "scripts": {
69
- "build": "shx rm -rf dist && tsc --noEmit && rollup --config rollup.config.js",
70
- "dev": "shx rm -rf dist && rollup --config rollup.config.js --watch",
69
+ "build": "shx rm -rf dist && tsc --emitDeclarationOnly --outDir dist && rolldown --config rolldown.config.js",
70
+ "dev": "shx rm -rf dist && rolldown --config rolldown.config.js --watch",
71
71
  "test": "vitest run",
72
72
  "test:watch": "vitest",
73
73
  "release": "pnpm publish --access public",