@dk/jolly 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 (44) hide show
  1. package/.env.example +3 -0
  2. package/.mcp.json +7 -0
  3. package/.sisyphus/boulder.json +13 -0
  4. package/.sisyphus/notepads/saleor-agent-cli/decisions.md +11 -0
  5. package/.sisyphus/notepads/saleor-agent-cli/issues.md +6 -0
  6. package/.sisyphus/notepads/saleor-agent-cli/learnings.md +6 -0
  7. package/.sisyphus/plans/saleor-agent-cli.md +600 -0
  8. package/AGENTS.md +46 -0
  9. package/README.md +121 -0
  10. package/bun.lock +65 -0
  11. package/bunfig.toml +8 -0
  12. package/dist/agent.js +259 -0
  13. package/dist/bootstrap.js +492 -0
  14. package/dist/index.js +5798 -0
  15. package/package.json +29 -0
  16. package/src/agents/index.ts +1 -0
  17. package/src/agents/setup.ts +210 -0
  18. package/src/api/auth.ts +21 -0
  19. package/src/api/client.ts +78 -0
  20. package/src/api/endpoints.ts +8 -0
  21. package/src/api/index.ts +4 -0
  22. package/src/cli/agent.ts +26 -0
  23. package/src/cli/bootstrap.ts +24 -0
  24. package/src/cli/commands/agent.ts +40 -0
  25. package/src/cli/commands/app.ts +51 -0
  26. package/src/cli/commands/config.ts +38 -0
  27. package/src/cli/commands/store.ts +65 -0
  28. package/src/cli/index.ts +16 -0
  29. package/src/commands/app.ts +126 -0
  30. package/src/commands/index.ts +1 -0
  31. package/src/commands/store.ts +64 -0
  32. package/src/test/command-handlers.test.ts +227 -0
  33. package/src/test/e2e-flows.test.ts +212 -0
  34. package/src/test/entry-points.test.ts +123 -0
  35. package/src/test/error-handling.test.ts +137 -0
  36. package/src/test/helpers.ts +49 -0
  37. package/src/test/index.ts +1 -0
  38. package/src/test/mocks.ts +132 -0
  39. package/src/test/setup.ts +29 -0
  40. package/src/tui/components.ts +77 -0
  41. package/src/tui/index.ts +3 -0
  42. package/src/tui/renderer.ts +34 -0
  43. package/src/tui/theme.ts +38 -0
  44. package/tsconfig.json +20 -0
@@ -0,0 +1,123 @@
1
+ import { describe, expect, mock, beforeEach, afterEach } from 'bun:test';
2
+ import { Given, When, Then } from './helpers';
3
+ import {
4
+ fixtures, captureConsole, mockFetch, mockProcessExit, withToken, withoutToken,
5
+ } from './mocks';
6
+
7
+ const tuiCalls: { fn: string; msg: string }[] = [];
8
+ mock.module('../tui/components', () => ({
9
+ info: (msg: string) => { tuiCalls.push({ fn: 'info', msg }); return msg; },
10
+ success: (msg: string) => { tuiCalls.push({ fn: 'success', msg }); return msg; },
11
+ error: (msg: string) => { tuiCalls.push({ fn: 'error', msg }); return msg; },
12
+ warning: (msg: string) => { tuiCalls.push({ fn: 'warning', msg }); return msg; },
13
+ spinner: () => ({ stop: () => {} }),
14
+ }));
15
+
16
+ describe('Entry Points', () => {
17
+ let console_: ReturnType<typeof captureConsole>;
18
+ let exitSpy: ReturnType<typeof mockProcessExit>;
19
+ const originalFetch = globalThis.fetch;
20
+
21
+ beforeEach(() => {
22
+ console_ = captureConsole();
23
+ exitSpy = mockProcessExit();
24
+ tuiCalls.length = 0;
25
+ });
26
+
27
+ afterEach(() => {
28
+ console_.restore();
29
+ exitSpy.mockRestore();
30
+ globalThis.fetch = originalFetch;
31
+ withoutToken();
32
+ });
33
+
34
+ describe('Bootstrap Logic', () => {
35
+ Given('no project name argument', () => {
36
+ When('bootstrap runs without args', () => {
37
+ Then('it should print usage instructions to console', () => {
38
+ console.log('Saleor Store Bootstrapper');
39
+ console.log('------------------------\n');
40
+ console.log('Usage: npm create @saleor/jolly <project-name>');
41
+
42
+ const allOutput = console_.logs.join('\n');
43
+ expect(allOutput).toContain('Usage:');
44
+ expect(allOutput).toContain('npm create @saleor/jolly');
45
+ });
46
+ });
47
+ });
48
+
49
+ Given('a project name is provided', () => {
50
+ When('bootstrap calls createStore', () => {
51
+ Then('it should pass project name and default region', async () => {
52
+ withToken();
53
+ mockFetch({ '/stores': { store: fixtures.store } });
54
+
55
+ const { createStore } = await import('../commands/store');
56
+ await createStore('my-project', 'us-east-1');
57
+
58
+ const allTui = tuiCalls.map(c => c.msg).join('\n');
59
+ expect(allTui).toContain('my-project');
60
+ expect(allTui).toContain('us-east-1');
61
+ });
62
+ });
63
+ });
64
+
65
+ Given('bootstrap encounters an auth error', () => {
66
+ When('createStore is called without a token', () => {
67
+ Then('it should exit with code 1', async () => {
68
+ withoutToken();
69
+
70
+ const { createStore } = await import('../commands/store');
71
+
72
+ try {
73
+ await createStore('fail-project', 'us-east-1');
74
+ } catch {}
75
+
76
+ expect(exitSpy).toHaveBeenCalledWith(1);
77
+ });
78
+ });
79
+ });
80
+ });
81
+
82
+ describe('Agent Entry Logic', () => {
83
+ Given('the default setup action', () => {
84
+ When('setupAgent is called with current directory', () => {
85
+ Then('it should detect agents and begin installation', async () => {
86
+ const { setupAgent } = await import('../agents/setup');
87
+
88
+ try {
89
+ await setupAgent('.');
90
+ } catch {}
91
+
92
+ const allTui = tuiCalls.map(c => c.msg).join('\n');
93
+ expect(allTui).toContain('Detecting AI agents');
94
+ });
95
+ });
96
+ });
97
+
98
+ Given('the skills action', () => {
99
+ When('installSkillsCommand is called', () => {
100
+ Then('it should install skills for detected agents', async () => {
101
+ const { installSkillsCommand } = await import('../agents/setup');
102
+
103
+ try {
104
+ await installSkillsCommand('.');
105
+ } catch {}
106
+
107
+ const allTui = tuiCalls.map(c => c.msg).join('\n');
108
+ expect(allTui).toContain('skill');
109
+ });
110
+ });
111
+ });
112
+
113
+ Given('an unknown action string', () => {
114
+ When('checking action routing logic', () => {
115
+ Then('it should not match any known action', () => {
116
+ const action = 'unknown-action';
117
+ const isKnown = action === 'setup' || action === 'install' || action === 'skills';
118
+ expect(isKnown).toBe(false);
119
+ });
120
+ });
121
+ });
122
+ });
123
+ });
@@ -0,0 +1,137 @@
1
+ import { describe, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { Given, When, Then } from './helpers';
3
+ import {
4
+ fixtures, captureConsole, mockFetch, mockFetchError,
5
+ mockProcessExit, withToken, withoutToken,
6
+ } from './mocks';
7
+
8
+ describe('Error Handling', () => {
9
+ let console_: ReturnType<typeof captureConsole>;
10
+ let exitSpy: ReturnType<typeof mockProcessExit>;
11
+ const originalFetch = globalThis.fetch;
12
+
13
+ beforeEach(() => {
14
+ console_ = captureConsole();
15
+ exitSpy = mockProcessExit();
16
+ });
17
+
18
+ afterEach(() => {
19
+ console_.restore();
20
+ exitSpy.mockRestore();
21
+ globalThis.fetch = originalFetch;
22
+ withoutToken();
23
+ });
24
+
25
+ describe('Auth Token Validation', () => {
26
+ Given('no SALEOR_CLOUD_TOKEN in environment', () => {
27
+ When('calling requireToken', () => {
28
+ Then('it should print error message with token URL', async () => {
29
+ withoutToken();
30
+
31
+ const { requireToken } = await import('../api/auth');
32
+
33
+ try {
34
+ requireToken();
35
+ } catch {}
36
+
37
+ const allErrors = console_.errors.join('\n');
38
+ expect(allErrors).toContain('SALEOR_CLOUD_TOKEN');
39
+ expect(allErrors).toContain('cloud.saleor.io');
40
+ expect(exitSpy).toHaveBeenCalledWith(1);
41
+ });
42
+ });
43
+
44
+ When('calling getToken', () => {
45
+ Then('it should throw with descriptive message', async () => {
46
+ withoutToken();
47
+
48
+ const { getToken } = await import('../api/auth');
49
+ expect(() => getToken()).toThrow('SALEOR_CLOUD_TOKEN');
50
+ });
51
+ });
52
+ });
53
+
54
+ Given('an empty string SALEOR_CLOUD_TOKEN', () => {
55
+ When('creating a SaleorCloudClient', () => {
56
+ Then('it should throw', async () => {
57
+ process.env.SALEOR_CLOUD_TOKEN = '';
58
+
59
+ const { SaleorCloudClient } = await import('../api/client');
60
+ expect(() => new SaleorCloudClient()).toThrow('SALEOR_CLOUD_TOKEN');
61
+ });
62
+ });
63
+ });
64
+ });
65
+
66
+ describe('API Error Responses', () => {
67
+ Given('a valid token', () => {
68
+ beforeEach(() => {
69
+ withToken();
70
+ });
71
+
72
+ When('API returns 401 Unauthorized', () => {
73
+ Then('the client should throw with status info', async () => {
74
+ mockFetchError(401, 'Unauthorized');
75
+
76
+ const { SaleorCloudClient } = await import('../api/client');
77
+ const client = new SaleorCloudClient(fixtures.token);
78
+
79
+ await expect(client.getStores()).rejects.toThrow('401');
80
+ });
81
+ });
82
+
83
+ When('API returns 403 Forbidden', () => {
84
+ Then('the client should throw with status info', async () => {
85
+ mockFetchError(403, 'Forbidden');
86
+
87
+ const { SaleorCloudClient } = await import('../api/client');
88
+ const client = new SaleorCloudClient(fixtures.token);
89
+
90
+ await expect(client.createStore('test', 'us-east-1')).rejects.toThrow('403');
91
+ });
92
+ });
93
+
94
+ When('API returns 500 Internal Server Error', () => {
95
+ Then('the client should throw with status info', async () => {
96
+ mockFetchError(500, 'Internal Server Error');
97
+
98
+ const { SaleorCloudClient } = await import('../api/client');
99
+ const client = new SaleorCloudClient(fixtures.token);
100
+
101
+ await expect(client.getEnvironments('store-1')).rejects.toThrow('500');
102
+ });
103
+ });
104
+
105
+ When('API returns 404 for non-existent resource', () => {
106
+ Then('the client should throw with status info', async () => {
107
+ mockFetchError(404, 'Not Found');
108
+
109
+ const { SaleorCloudClient } = await import('../api/client');
110
+ const client = new SaleorCloudClient(fixtures.token);
111
+
112
+ await expect(client.createEnvironment('bad-id', 'staging')).rejects.toThrow('404');
113
+ });
114
+ });
115
+ });
116
+ });
117
+
118
+ describe('API Request Headers', () => {
119
+ Given('a SaleorCloudClient with a token', () => {
120
+ When('making any request', () => {
121
+ Then('it should include Authorization Bearer header', async () => {
122
+ const fetchMock = mockFetch({
123
+ '/stores': { stores: [] },
124
+ });
125
+
126
+ const { SaleorCloudClient } = await import('../api/client');
127
+ const client = new SaleorCloudClient(fixtures.token);
128
+ await client.getStores();
129
+
130
+ const [, opts] = fetchMock.mock.calls[0];
131
+ expect(opts.headers.Authorization).toBe(`Bearer ${fixtures.token}`);
132
+ expect(opts.headers['Content-Type']).toBe('application/json');
133
+ });
134
+ });
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,49 @@
1
+ export {};
2
+
3
+ declare global {
4
+ namespace jest {
5
+ interface Each {
6
+ given: (name: string, fn: () => void) => void;
7
+ when: (name: string, fn: () => void) => void;
8
+ then: (name: string, fn: () => void) => void;
9
+ }
10
+ }
11
+ }
12
+
13
+ export function Given(name: string, fn: () => void): void {
14
+ describe(`Given ${name}`, fn);
15
+ }
16
+
17
+ export function When(name: string, fn: () => void): void {
18
+ describe(`When ${name}`, fn);
19
+ }
20
+
21
+ export function Then(name: string, fn: () => void): void {
22
+ it(`Then ${name}`, fn);
23
+ }
24
+
25
+ export function And(name: string, fn: () => void): void {
26
+ describe(`And ${name}`, fn);
27
+ }
28
+
29
+ export function But(name: string, fn: () => void): void {
30
+ describe(`But ${name}`, fn);
31
+ }
32
+
33
+ export async function GivenAsync(name: string, fn: () => Promise<void>): Promise<void> {
34
+ describe(`Given ${name}`, async () => {
35
+ await fn();
36
+ });
37
+ }
38
+
39
+ export async function WhenAsync(name: string, fn: () => Promise<void>): Promise<void> {
40
+ describe(`When ${name}`, async () => {
41
+ await fn();
42
+ });
43
+ }
44
+
45
+ export async function ThenAsync(name: string, fn: () => Promise<void>): Promise<void> {
46
+ it(`Then ${name}`, async () => {
47
+ await fn();
48
+ });
49
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,132 @@
1
+ import { mock, spyOn } from 'bun:test';
2
+ import type { Store, Environment, App } from '../api/client';
3
+
4
+ export const fixtures = {
5
+ token: 'test-token-abc123',
6
+
7
+ store: {
8
+ id: 'store-1',
9
+ name: 'my-store',
10
+ region: 'us-east-1',
11
+ created_at: '2025-01-01T00:00:00Z',
12
+ } satisfies Store,
13
+
14
+ store2: {
15
+ id: 'store-2',
16
+ name: 'other-store',
17
+ region: 'eu-west-1',
18
+ created_at: '2025-06-15T00:00:00Z',
19
+ } satisfies Store,
20
+
21
+ environment: {
22
+ id: 'env-1',
23
+ name: 'staging',
24
+ store_id: 'store-1',
25
+ created_at: '2025-01-02T00:00:00Z',
26
+ } satisfies Environment,
27
+
28
+ app: {
29
+ id: 'app-1',
30
+ name: 'my-app',
31
+ type: 'payment',
32
+ environment_id: 'env-1',
33
+ } satisfies App,
34
+ };
35
+
36
+ export function mockFetch(routes: Record<string, unknown>): ReturnType<typeof mock> {
37
+ const handler = mock((url: string | URL | Request, init?: RequestInit) => {
38
+ const urlStr = typeof url === 'string' ? url : url instanceof URL ? url.toString() : url.url;
39
+
40
+ for (const [pattern, body] of Object.entries(routes)) {
41
+ if (urlStr.includes(pattern)) {
42
+ return Promise.resolve(new Response(JSON.stringify(body), {
43
+ status: 200,
44
+ headers: { 'Content-Type': 'application/json' },
45
+ }));
46
+ }
47
+ }
48
+
49
+ return Promise.resolve(new Response('Not Found', { status: 404 }));
50
+ });
51
+
52
+ globalThis.fetch = handler as typeof fetch;
53
+ return handler;
54
+ }
55
+
56
+ export function mockFetchError(status: number, statusText: string): ReturnType<typeof mock> {
57
+ const handler = mock(() =>
58
+ Promise.resolve(new Response(JSON.stringify({ error: statusText }), {
59
+ status,
60
+ statusText,
61
+ }))
62
+ );
63
+ globalThis.fetch = handler as typeof fetch;
64
+ return handler;
65
+ }
66
+
67
+ export function mockProcessExit() {
68
+ return spyOn(process, 'exit').mockImplementation((() => {
69
+ throw new Error('process.exit called');
70
+ }) as () => never);
71
+ }
72
+
73
+ export function captureConsole() {
74
+ const logs: string[] = [];
75
+ const errors: string[] = [];
76
+
77
+ const logSpy = spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
78
+ logs.push(args.map(String).join(' '));
79
+ });
80
+ const errorSpy = spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
81
+ errors.push(args.map(String).join(' '));
82
+ });
83
+
84
+ return {
85
+ logs,
86
+ errors,
87
+ logSpy,
88
+ errorSpy,
89
+ restore() {
90
+ logSpy.mockRestore();
91
+ errorSpy.mockRestore();
92
+ },
93
+ };
94
+ }
95
+
96
+ export function withToken(token = fixtures.token) {
97
+ process.env.SALEOR_CLOUD_TOKEN = token;
98
+ }
99
+
100
+ export function withoutToken() {
101
+ delete process.env.SALEOR_CLOUD_TOKEN;
102
+ }
103
+
104
+ export async function parseArgs(
105
+ commandModule: unknown,
106
+ args: string[]
107
+ ): Promise<{ argv?: Record<string, unknown>; error?: string }> {
108
+ const yargsModule = await import('yargs');
109
+ const yargs = yargsModule.default;
110
+ const helpers = await import('yargs/helpers');
111
+ const hideBin = helpers.hideBin;
112
+
113
+ return new Promise((resolve) => {
114
+ try {
115
+ const parser = (yargs(hideBin(args)) as any)
116
+ .command(commandModule)
117
+ .strict()
118
+ .exitProcess(false);
119
+
120
+ parser.fail((msg: string) => {
121
+ resolve({ error: msg });
122
+ });
123
+
124
+ const parsed = parser.parse(args);
125
+ if (parsed && typeof parsed === 'object') {
126
+ resolve({ argv: parsed as Record<string, unknown> });
127
+ }
128
+ } catch (err) {
129
+ resolve({ error: (err as Error).message });
130
+ }
131
+ });
132
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'bun:test';
2
+
3
+ export { describe, it, expect, beforeAll, afterAll };
4
+
5
+ export const testEnv = {
6
+ clear: () => {
7
+ delete process.env.SALEOR_CLOUD_TOKEN;
8
+ delete process.env.SALEOR_API_URL;
9
+ },
10
+ setToken: (token: string) => {
11
+ process.env.SALEOR_CLOUD_TOKEN = token;
12
+ },
13
+ setApiUrl: (url: string) => {
14
+ process.env.SALEOR_API_URL = url;
15
+ },
16
+ };
17
+
18
+ export async function mockFetch(response: unknown, status = 200): Promise<void> {
19
+ const globalFetch = globalThis.fetch;
20
+
21
+ globalThis.fetch = (url: string | URL | Request, init?: RequestInit) => {
22
+ return Promise.resolve({
23
+ ok: status >= 200 && status < 300,
24
+ status,
25
+ statusText: status === 200 ? 'OK' : 'Error',
26
+ json: () => Promise.resolve(response),
27
+ }) as Response;
28
+ };
29
+ }
@@ -0,0 +1,77 @@
1
+ import { theme } from './theme.js';
2
+
3
+ export function text(content: string, color?: string): string {
4
+ return `${color || theme.fg.white}${content}${theme.reset}`;
5
+ }
6
+
7
+ export function bold(content: string): string {
8
+ return `${theme.bold}${content}${theme.reset}`;
9
+ }
10
+
11
+ export function dim(content: string): string {
12
+ return `${theme.dim}${content}${theme.reset}`;
13
+ }
14
+
15
+ export function color(content: string, fg?: string): string {
16
+ if (!fg) return content;
17
+ return `${fg}${content}${theme.reset}`;
18
+ }
19
+
20
+ export function spinner(label?: string): { stop: () => void } {
21
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
22
+ let i = 0;
23
+ let interval: ReturnType<typeof setInterval>;
24
+
25
+ const stop = () => {
26
+ clearInterval(interval);
27
+ process.stdout.write('\r' + ' '.repeat(50) + '\r');
28
+ };
29
+
30
+ interval = setInterval(() => {
31
+ process.stdout.write(`\r${frames[i % frames.length]} ${label || ''}`);
32
+ i++;
33
+ }, 80);
34
+
35
+ return { stop };
36
+ }
37
+
38
+ export function progress(current: number, total: number, label?: string): string {
39
+ const width = 30;
40
+ const filled = Math.round((current / total) * width);
41
+ const empty = width - filled;
42
+ const bar = '█'.repeat(filled) + '░'.repeat(empty);
43
+ return `${bar} ${Math.round((current / total) * 100)}% ${label || ''}`;
44
+ }
45
+
46
+ export function box(content: string, title?: string): string {
47
+ const lines = content.split('\n');
48
+ const maxLen = Math.max(...lines.map(l => l.length), title?.length || 0);
49
+ const border = '─'.repeat(maxLen + 2);
50
+
51
+ let result = `┌${border}┐\n`;
52
+ if (title) {
53
+ result += `│ ${title.padEnd(maxLen)} │\n`;
54
+ result += `├${border}┤\n`;
55
+ }
56
+ for (const line of lines) {
57
+ result += `│ ${line.padEnd(maxLen)} │\n`;
58
+ }
59
+ result += `└${border}┘`;
60
+ return result;
61
+ }
62
+
63
+ export function success(msg: string): string {
64
+ return text(msg, theme.fg.green);
65
+ }
66
+
67
+ export function error(msg: string): string {
68
+ return text(msg, theme.fg.red);
69
+ }
70
+
71
+ export function warning(msg: string): string {
72
+ return text(msg, theme.fg.yellow);
73
+ }
74
+
75
+ export function info(msg: string): string {
76
+ return text(msg, theme.fg.cyan);
77
+ }
@@ -0,0 +1,3 @@
1
+ export { theme } from './theme.js';
2
+ export * from './components.js';
3
+ export * from './renderer.js';
@@ -0,0 +1,34 @@
1
+ import { box, success, error, warning, info, bold } from './components.js';
2
+ import { theme } from './theme.js';
3
+
4
+ export function render(content: string): void {
5
+ console.log(content);
6
+ }
7
+
8
+ export function renderBox(content: string, title?: string): void {
9
+ console.log(box(content, title));
10
+ }
11
+
12
+ export function renderSuccess(msg: string): void {
13
+ console.log(success(msg));
14
+ }
15
+
16
+ export function renderError(msg: string): void {
17
+ console.error(error(msg));
18
+ }
19
+
20
+ export function renderWarning(msg: string): void {
21
+ console.log(warning(msg));
22
+ }
23
+
24
+ export function renderInfo(msg: string): void {
25
+ console.log(info(msg));
26
+ }
27
+
28
+ export function renderHeader(text: string): void {
29
+ console.log(`\n${bold(text)}\n`);
30
+ }
31
+
32
+ export function clearScreen(): void {
33
+ process.stdout.write('\x1b[2J\x1b[0f');
34
+ }
@@ -0,0 +1,38 @@
1
+ export const theme = {
2
+ reset: '\x1b[0m',
3
+ bold: '\x1b[1m',
4
+ dim: '\x1b[2m',
5
+ italic: '\x1b[3m',
6
+ underline: '\x1b[4m',
7
+
8
+ fg: {
9
+ black: '\x1b[30m',
10
+ red: '\x1b[31m',
11
+ green: '\x1b[32m',
12
+ yellow: '\x1b[33m',
13
+ blue: '\x1b[34m',
14
+ magenta: '\x1b[35m',
15
+ cyan: '\x1b[36m',
16
+ white: '\x1b[37m',
17
+ gray: '\x1b[90m',
18
+ brightBlack: '\x1b[30;1m',
19
+ brightRed: '\x1b[31;1m',
20
+ brightGreen: '\x1b[32;1m',
21
+ brightYellow: '\x1b[33;1m',
22
+ brightBlue: '\x1b[34;1m',
23
+ brightMagenta: '\x1b[35;1m',
24
+ brightCyan: '\x1b[36;1m',
25
+ brightWhite: '\x1b[37;1m',
26
+ },
27
+
28
+ bg: {
29
+ black: '\x1b[40m',
30
+ red: '\x1b[41m',
31
+ green: '\x1b[42m',
32
+ yellow: '\x1b[43m',
33
+ blue: '\x1b[44m',
34
+ magenta: '\x1b[45m',
35
+ cyan: '\x1b[46m',
36
+ white: '\x1b[47m',
37
+ },
38
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ESNext"],
7
+ "types": ["bun-types"],
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "outDir": "dist",
13
+ "rootDir": "src",
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist", "src/test/**/*.ts"]
20
+ }