@ollie-shop/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.
@@ -0,0 +1,13 @@
1
+
2
+ > @ollie-shop/cli@0.1.1 build /home/runner/work/ollie-shop/ollie-shop/packages/cli
3
+ > tsup
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.0
8
+ CLI Using tsup config: /home/runner/work/ollie-shop/ollie-shop/packages/cli/tsup.config.ts
9
+ CLI Target: esnext
10
+ CLI Cleaning output folder
11
+ CJS Build start
12
+ CJS dist/index.js 7.47 KB
13
+ CJS ⚡️ Build success in 470ms
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # Ollie Shop CLI [WIP]
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
+
5
+ ## Overview
6
+
7
+ `ollie-shop` is a development dependency package that provides a command-line interface (CLI) for local development of Ollie Shop templates and components. This package is currently a work in progress (WIP) and aims to streamline the development workflow for Ollie Shop projects.
8
+
9
+ ## Contributing
10
+
11
+ Contributions are welcome! Please feel free to submit a Pull Request.
12
+
13
+ ## License
14
+
15
+ This project is licensed under the MIT License - see the LICENSE file for details.
package/dist/index.js ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var commander = require('commander');
5
+ var child_process = require('child_process');
6
+ var crypto = require('crypto');
7
+ var fs = require('fs/promises');
8
+ var http = require('http');
9
+ var os = require('os');
10
+ var path = require('path');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
15
+ var path__default = /*#__PURE__*/_interopDefault(path);
16
+
17
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
+ }) : x)(function(x) {
20
+ if (typeof require !== "undefined") return require.apply(this, arguments);
21
+ throw Error('Dynamic require of "' + x + '" is not supported');
22
+ });
23
+ var DEFAULT_CALLBACK_PORT = 7777;
24
+ var AUTH_ENDPOINT = "https://admin.ollie.shop/auth/login";
25
+ function configureLoginCommand(program) {
26
+ return program.command("login").description("Log in to your Ollie Shop account").option(
27
+ "-p, --port <port>",
28
+ "Port to use for the local callback server",
29
+ DEFAULT_CALLBACK_PORT.toString()
30
+ ).option("--auth-url <url>", "Custom authorization URL", AUTH_ENDPOINT).action(async (options) => {
31
+ console.log("\u{1F510} Initiating Ollie Shop login flow...");
32
+ try {
33
+ const token = await startWebAuthFlow(options);
34
+ if (token) {
35
+ await saveCredentials(token);
36
+ console.log("\u2705 Successfully logged in!");
37
+ return;
38
+ }
39
+ console.error("\u274C Authentication failed. Please try again.");
40
+ process.exit(1);
41
+ } catch (error) {
42
+ console.error(
43
+ `\u274C Login failed: ${error instanceof Error ? error.message : "Unknown error"}`
44
+ );
45
+ process.exit(1);
46
+ }
47
+ });
48
+ }
49
+ async function handleAuthCallback(req, res, state, resolve, reject) {
50
+ const socket = req.socket;
51
+ const url = new URL(
52
+ req.url || "/",
53
+ `http://localhost:${socket.localPort || 3e3}`
54
+ );
55
+ const params = url.searchParams;
56
+ const returnedState = params.get("state");
57
+ if (returnedState !== state) {
58
+ sendErrorResponse(
59
+ res,
60
+ 400,
61
+ "Invalid state parameter",
62
+ "Authentication failed. Please try again."
63
+ );
64
+ reject(new Error("Invalid state parameter"));
65
+ return;
66
+ }
67
+ let formData = "";
68
+ req.on("data", (chunk) => {
69
+ formData += chunk.toString();
70
+ });
71
+ await new Promise((formResolve) => {
72
+ req.on("end", () => {
73
+ formResolve();
74
+ });
75
+ });
76
+ const formParams = new URLSearchParams(formData);
77
+ const accessToken = formParams.get("access_token");
78
+ const refreshToken = formParams.get("refresh_token") || "";
79
+ const expiresAt = formParams.get("expires_at") || new Date(Date.now() + 36e5).toISOString();
80
+ if (!accessToken) {
81
+ sendErrorResponse(
82
+ res,
83
+ 400,
84
+ "Missing authentication token",
85
+ "Authentication failed. Please try again."
86
+ );
87
+ reject(new Error("Missing authentication token"));
88
+ return;
89
+ }
90
+ try {
91
+ const token = {
92
+ accessToken,
93
+ refreshToken,
94
+ expiresAt
95
+ };
96
+ sendSuccessResponse(res);
97
+ resolve(token);
98
+ } catch (error) {
99
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
100
+ sendErrorResponse(
101
+ res,
102
+ 500,
103
+ "Authentication failed",
104
+ `Error: ${errorMessage}`
105
+ );
106
+ reject(new Error(errorMessage));
107
+ }
108
+ }
109
+ function sendErrorResponse(res, statusCode, title, message) {
110
+ res.writeHead(statusCode, { "Content-Type": "text/html" });
111
+ res.end(`<h1>${title}</h1><p>${message}</p>`);
112
+ }
113
+ function sendSuccessResponse(res) {
114
+ res.writeHead(200, { "Content-Type": "text/html" });
115
+ res.end(
116
+ "<h1>Authentication successful!</h1><p>You can now close this window and return to the CLI.</p>"
117
+ );
118
+ }
119
+ function sendWaitingResponse(res) {
120
+ res.writeHead(200, { "Content-Type": "text/html" });
121
+ res.end(
122
+ "<h1>Ollie Shop CLI Authentication</h1><p>Waiting for authentication response...</p>"
123
+ );
124
+ }
125
+ async function startWebAuthFlow(options) {
126
+ const state = crypto.randomBytes(16).toString("hex");
127
+ const port = Number.parseInt(options.port, 10);
128
+ const baseUrl = options.authUrl;
129
+ return new Promise((resolve, reject) => {
130
+ const server = http.createServer(async (req, res) => {
131
+ try {
132
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
133
+ if (url.pathname === "/callback") {
134
+ await handleAuthCallback(
135
+ req,
136
+ res,
137
+ state,
138
+ (token) => {
139
+ server.close(() => {
140
+ console.log("\u{1F510} Local authentication server closed");
141
+ resolve(token);
142
+ });
143
+ },
144
+ (error) => {
145
+ server.close(() => {
146
+ console.log("\u{1F510} Local authentication server closed");
147
+ reject(error);
148
+ });
149
+ }
150
+ );
151
+ } else {
152
+ sendWaitingResponse(res);
153
+ }
154
+ } catch (error) {
155
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
156
+ sendErrorResponse(res, 500, "Server Error", errorMessage);
157
+ server.close(() => {
158
+ console.log("\u{1F510} Local authentication server closed");
159
+ reject(new Error(errorMessage));
160
+ });
161
+ }
162
+ });
163
+ server.listen(port, () => {
164
+ const redirectUrl = `http://localhost:${port}/callback`;
165
+ const authUrl = new URL(baseUrl);
166
+ authUrl.searchParams.set("flow", "cli");
167
+ authUrl.searchParams.set("state", state);
168
+ authUrl.searchParams.set("redirect_to", redirectUrl);
169
+ console.log("\n\u{1F512} Please authenticate in your browser...\n");
170
+ console.log(`Opening: ${authUrl}
171
+ `);
172
+ openBrowser(authUrl.toString());
173
+ });
174
+ server.on("error", (err) => {
175
+ if (err.code === "EADDRINUSE") {
176
+ reject(
177
+ new Error(
178
+ `Port ${port} is already in use. Please specify a different port using the --port option.`
179
+ )
180
+ );
181
+ } else {
182
+ reject(err);
183
+ }
184
+ server.close();
185
+ });
186
+ const timeoutId = setTimeout(
187
+ () => {
188
+ server.close(() => {
189
+ console.log("\u{1F510} Local authentication server closed due to timeout");
190
+ reject(new Error("Authentication timed out. Please try again."));
191
+ });
192
+ },
193
+ 5 * 60 * 1e3
194
+ );
195
+ server.on("close", () => {
196
+ clearTimeout(timeoutId);
197
+ });
198
+ });
199
+ }
200
+ function openBrowser(url) {
201
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
202
+ child_process.spawn(command, [url], { detached: true }).unref();
203
+ }
204
+ async function saveCredentials(token) {
205
+ console.log("Saving credentials...");
206
+ const configDir = path__default.default.join(os.homedir(), ".ollie-shop");
207
+ const credentialsPath = path__default.default.join(configDir, "credentials.json");
208
+ try {
209
+ await fs__default.default.mkdir(configDir, { recursive: true });
210
+ } catch (error) {
211
+ if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) {
212
+ throw error;
213
+ }
214
+ }
215
+ await fs__default.default.writeFile(credentialsPath, JSON.stringify(token, null, 2));
216
+ return true;
217
+ }
218
+
219
+ // src/commands/index.ts
220
+ function registerCommands(program) {
221
+ configureLoginCommand(program);
222
+ }
223
+
224
+ // src/index.ts
225
+ function createProgram() {
226
+ const program = new commander.Command();
227
+ program.name("ollie").description("Ollie Shop CLI tools").version("0.1.0");
228
+ registerCommands(program);
229
+ return program;
230
+ }
231
+ if (__require.main === module) {
232
+ const program = createProgram();
233
+ program.parse();
234
+ }
235
+
236
+ exports.createProgram = createProgram;
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@ollie-shop/cli",
3
+ "version": "0.1.1",
4
+ "description": "CLI tools for Ollie Shop",
5
+ "bin": {
6
+ "ollie-shop": "./dist/index.js"
7
+ },
8
+ "dependencies": {
9
+ "commander": "^11.1.0"
10
+ },
11
+ "devDependencies": {
12
+ "@types/node": "^22.15.23",
13
+ "tsup": "^8.4.0",
14
+ "typescript": "^5.7.3",
15
+ "vitest": "^3.0.4"
16
+ },
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "test": "vitest",
20
+ "dev": "tsup --watch"
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ import type { Command } from "commander";
2
+ import { configureLoginCommand } from "./login";
3
+
4
+ /**
5
+ * Register all CLI commands with the program
6
+ * @param program The commander program instance
7
+ */
8
+ export function registerCommands(program: Command): void {
9
+ // Register individual commands
10
+ configureLoginCommand(program);
11
+
12
+ // Add more commands here as they are implemented
13
+ }
@@ -0,0 +1,340 @@
1
+ import { spawn } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import fs from "node:fs/promises";
4
+ import type { IncomingMessage, ServerResponse } from "node:http";
5
+ import { createServer } from "node:http";
6
+ import { homedir } from "node:os";
7
+ import path from "node:path";
8
+ import type { Command } from "commander";
9
+
10
+ /**
11
+ * Default port for the local callback server
12
+ */
13
+ const DEFAULT_CALLBACK_PORT = 7777;
14
+
15
+ /**
16
+ * Default Ollie Shop authorization endpoint
17
+ */
18
+ const AUTH_ENDPOINT = "https://admin.ollie.shop/auth/login";
19
+
20
+ /**
21
+ * Configure the login command
22
+ * @param program The commander program instance
23
+ * @returns The configured command
24
+ */
25
+ export function configureLoginCommand(program: Command): Command {
26
+ return program
27
+ .command("login")
28
+ .description("Log in to your Ollie Shop account")
29
+ .option(
30
+ "-p, --port <port>",
31
+ "Port to use for the local callback server",
32
+ DEFAULT_CALLBACK_PORT.toString(),
33
+ )
34
+ .option("--auth-url <url>", "Custom authorization URL", AUTH_ENDPOINT)
35
+ .action(async (options) => {
36
+ console.log("🔐 Initiating Ollie Shop login flow...");
37
+
38
+ try {
39
+ const token = await startWebAuthFlow(options);
40
+
41
+ if (token) {
42
+ await saveCredentials(token);
43
+
44
+ console.log("✅ Successfully logged in!");
45
+ // Process continues normally after login
46
+ return;
47
+ }
48
+
49
+ console.error("❌ Authentication failed. Please try again.");
50
+ process.exit(1);
51
+ } catch (error) {
52
+ console.error(
53
+ `❌ Login failed: ${error instanceof Error ? error.message : "Unknown error"}`,
54
+ );
55
+ process.exit(1);
56
+ }
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Authentication response type
62
+ */
63
+ type AuthToken = {
64
+ accessToken: string;
65
+ refreshToken: string;
66
+ expiresAt: string;
67
+ };
68
+
69
+ /**
70
+ * Handle the callback endpoint for web authentication flow
71
+ *
72
+ * This now expects to receive tokens directly from the Next.js app with Supabase auth
73
+ */
74
+ async function handleAuthCallback(
75
+ req: IncomingMessage,
76
+ res: ServerResponse,
77
+ state: string,
78
+ resolve: (token: AuthToken | null) => void,
79
+ reject: (err: Error) => void,
80
+ ): Promise<void> {
81
+ // TypeScript doesn't know the socket will always have localPort, so we cast
82
+ const socket = req.socket as { localPort?: number };
83
+ const url = new URL(
84
+ req.url || "/",
85
+ `http://localhost:${socket.localPort || 3000}`,
86
+ );
87
+ const params = url.searchParams;
88
+
89
+ // Verify state from query parameters to prevent CSRF attacks
90
+ const returnedState = params.get("state");
91
+ if (returnedState !== state) {
92
+ sendErrorResponse(
93
+ res,
94
+ 400,
95
+ "Invalid state parameter",
96
+ "Authentication failed. Please try again.",
97
+ );
98
+ reject(new Error("Invalid state parameter"));
99
+ return;
100
+ }
101
+
102
+ // Parse the request body to get the access_token from form data
103
+ let formData = "";
104
+ req.on("data", (chunk) => {
105
+ formData += chunk.toString();
106
+ });
107
+
108
+ await new Promise<void>((formResolve) => {
109
+ req.on("end", () => {
110
+ formResolve();
111
+ });
112
+ });
113
+
114
+ // Parse form data
115
+ const formParams = new URLSearchParams(formData);
116
+ const accessToken = formParams.get("access_token");
117
+ const refreshToken = formParams.get("refresh_token") || "";
118
+ const expiresAt =
119
+ formParams.get("expires_at") ||
120
+ new Date(Date.now() + 3600000).toISOString();
121
+
122
+ if (!accessToken) {
123
+ sendErrorResponse(
124
+ res,
125
+ 400,
126
+ "Missing authentication token",
127
+ "Authentication failed. Please try again.",
128
+ );
129
+ reject(new Error("Missing authentication token"));
130
+ return;
131
+ }
132
+
133
+ try {
134
+ // Create token object
135
+ const token: AuthToken = {
136
+ accessToken,
137
+ refreshToken,
138
+ expiresAt,
139
+ };
140
+
141
+ // Send success response to browser
142
+ sendSuccessResponse(res);
143
+
144
+ // Resolve the promise with the token
145
+ resolve(token);
146
+ } catch (error) {
147
+ const errorMessage =
148
+ error instanceof Error ? error.message : "Unknown error";
149
+ sendErrorResponse(
150
+ res,
151
+ 500,
152
+ "Authentication failed",
153
+ `Error: ${errorMessage}`,
154
+ );
155
+ reject(new Error(errorMessage));
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Send HTML error response
161
+ */
162
+ function sendErrorResponse(
163
+ res: ServerResponse,
164
+ statusCode: number,
165
+ title: string,
166
+ message: string,
167
+ ): void {
168
+ res.writeHead(statusCode, { "Content-Type": "text/html" });
169
+ res.end(`<h1>${title}</h1><p>${message}</p>`);
170
+ }
171
+
172
+ /**
173
+ * Send HTML success response
174
+ */
175
+ function sendSuccessResponse(res: ServerResponse): void {
176
+ res.writeHead(200, { "Content-Type": "text/html" });
177
+ res.end(
178
+ "<h1>Authentication successful!</h1><p>You can now close this window and return to the CLI.</p>",
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Send HTML waiting page response
184
+ */
185
+ function sendWaitingResponse(res: ServerResponse): void {
186
+ res.writeHead(200, { "Content-Type": "text/html" });
187
+ res.end(
188
+ "<h1>Ollie Shop CLI Authentication</h1><p>Waiting for authentication response...</p>",
189
+ );
190
+ }
191
+
192
+ /**
193
+ * Start the web-based authentication flow
194
+ */
195
+ async function startWebAuthFlow(options: {
196
+ port: string;
197
+ authUrl: string;
198
+ }): Promise<AuthToken | null> {
199
+ const state = randomBytes(16).toString("hex");
200
+ const port = Number.parseInt(options.port, 10);
201
+ const baseUrl = options.authUrl;
202
+
203
+ return new Promise<AuthToken | null>((resolve, reject) => {
204
+ // Create a local server to receive the callback
205
+ const server = createServer(async (req, res) => {
206
+ try {
207
+ // Parse the URL and query parameters
208
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
209
+
210
+ // Check if this is a callback from the Next.js app
211
+ if (url.pathname === "/callback") {
212
+ await handleAuthCallback(
213
+ req,
214
+ res,
215
+ state,
216
+ (token) => {
217
+ // Wrap the resolve to ensure server is closed
218
+ server.close(() => {
219
+ console.log("🔐 Local authentication server closed");
220
+ resolve(token);
221
+ });
222
+ },
223
+ (error) => {
224
+ // Wrap the reject to ensure server is closed
225
+ server.close(() => {
226
+ console.log("🔐 Local authentication server closed");
227
+ reject(error);
228
+ });
229
+ },
230
+ );
231
+ } else {
232
+ // Serve a simple page for any other path
233
+ sendWaitingResponse(res);
234
+ }
235
+ } catch (error) {
236
+ const errorMessage =
237
+ error instanceof Error ? error.message : "Unknown error";
238
+ sendErrorResponse(res, 500, "Server Error", errorMessage);
239
+ server.close(() => {
240
+ console.log("🔐 Local authentication server closed");
241
+ reject(new Error(errorMessage));
242
+ });
243
+ }
244
+ });
245
+
246
+ // Start the server
247
+ server.listen(port, () => {
248
+ // Build the URL to your Next.js app with necessary parameters
249
+ const redirectUrl = `http://localhost:${port}/callback`;
250
+
251
+ const authUrl = new URL(baseUrl);
252
+
253
+ authUrl.searchParams.set("flow", "cli");
254
+ authUrl.searchParams.set("state", state);
255
+ authUrl.searchParams.set("redirect_to", redirectUrl);
256
+
257
+ console.log("\n🔒 Please authenticate in your browser...\n");
258
+ console.log(`Opening: ${authUrl}\n`);
259
+
260
+ // Open the browser with the authorization URL
261
+ openBrowser(authUrl.toString());
262
+ });
263
+
264
+ // Handle server errors
265
+ server.on("error", (err: Error) => {
266
+ if ((err as NodeJS.ErrnoException).code === "EADDRINUSE") {
267
+ reject(
268
+ new Error(
269
+ `Port ${port} is already in use. Please specify a different port using the --port option.`,
270
+ ),
271
+ );
272
+ } else {
273
+ reject(err);
274
+ }
275
+ server.close();
276
+ });
277
+
278
+ // Set a timeout to prevent hanging indefinitely
279
+ const timeoutId = setTimeout(
280
+ () => {
281
+ server.close(() => {
282
+ console.log("🔐 Local authentication server closed due to timeout");
283
+ reject(new Error("Authentication timed out. Please try again."));
284
+ });
285
+ },
286
+ 5 * 60 * 1000,
287
+ ); // 5 minutes timeout
288
+
289
+ // Clean up the timeout if the server closes for other reasons
290
+ server.on("close", () => {
291
+ clearTimeout(timeoutId);
292
+ });
293
+ });
294
+ }
295
+
296
+ /**
297
+ * Open the default browser with the given URL
298
+ */
299
+ function openBrowser(url: string) {
300
+ const command =
301
+ process.platform === "darwin"
302
+ ? "open"
303
+ : process.platform === "win32"
304
+ ? "start"
305
+ : "xdg-open";
306
+
307
+ spawn(command, [url], { detached: true }).unref();
308
+ }
309
+
310
+ /**
311
+ * Save authentication credentials locally
312
+ *
313
+ * @todo Use a more secure storage method like OS keychain
314
+ */
315
+ async function saveCredentials(token: {
316
+ accessToken: string;
317
+ refreshToken: string;
318
+ expiresAt: string;
319
+ }) {
320
+ console.log("Saving credentials...");
321
+
322
+ const configDir = path.join(homedir(), ".ollie-shop");
323
+ const credentialsPath = path.join(configDir, "credentials.json");
324
+
325
+ // Create the .ollie-shop directory if it doesn't exist
326
+ try {
327
+ await fs.mkdir(configDir, { recursive: true });
328
+ } catch (error) {
329
+ // Ignore error if directory already exists
330
+ if (
331
+ !(error instanceof Error && "code" in error && error.code === "EEXIST")
332
+ ) {
333
+ throw error;
334
+ }
335
+ }
336
+
337
+ await fs.writeFile(credentialsPath, JSON.stringify(token, null, 2));
338
+
339
+ return true;
340
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { Command } from "commander";
2
+ import { registerCommands } from "./commands";
3
+
4
+ /**
5
+ * Create and configure the CLI program
6
+ */
7
+ export function createProgram() {
8
+ const program = new Command();
9
+ program.name("ollie").description("Ollie Shop CLI tools").version("0.1.0");
10
+
11
+ // Register all commands
12
+ registerCommands(program);
13
+
14
+ return program;
15
+ }
16
+
17
+ // If this file is run directly, execute the CLI
18
+ if (require.main === module) {
19
+ const program = createProgram();
20
+ program.parse();
21
+ }
@@ -0,0 +1,7 @@
1
+ export const SRC_DIR = "src";
2
+ export const COMPONENTS_DIR = "components";
3
+ export const DIST_DIR = "dist";
4
+ export const META_FILE = "meta.json";
5
+ export const CONFIG_FILE = "ollie.json";
6
+ export const OLLIE_SHOP_ASSETS_DIR = "node_modules/.ollie-shop";
7
+ export const TEMPLATES_DIR = "templates";
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext", "DOM"],
4
+ "module": "ESNext",
5
+ "target": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "declaration": true,
10
+ "noUncheckedIndexedAccess": true,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": true,
13
+ "resolveJsonModule": true,
14
+ "types": ["node", "vitest/globals"],
15
+ "paths": {
16
+ "@/*": ["./src/*"]
17
+ }
18
+ },
19
+ "exclude": ["node_modules", "dist"]
20
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["./src/index.ts"],
5
+ bundle: true,
6
+ splitting: false,
7
+ minify: false,
8
+ sourcemap: false,
9
+ platform: "node",
10
+ format: "cjs",
11
+ clean: true,
12
+ treeshake: true,
13
+ dts: false,
14
+ banner: { js: "#!/usr/bin/env node" },
15
+ });
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ },
7
+ });