storybooker 0.0.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/.oxlintrc.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": ["../../.oxlintrc.json"],
3
+ "env": {
4
+ "builtin": true,
5
+ "node": true
6
+ },
7
+ "rules": {
8
+ "no-console": "allow",
9
+ "no-process-exit": "allow",
10
+ "no-unsafe-call": "allow",
11
+ "sort-keys": "allow"
12
+ },
13
+ "ignorePatterns": [],
14
+ "overrides": [
15
+ {
16
+ "files": ["*.d.ts"],
17
+ "rules": {
18
+ "max-lines": "allow",
19
+ "consistent-indexed-object-style": "allow",
20
+ "no-redundant-type-constituents": "allow"
21
+ }
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,11 @@
1
+
2
+ ℹ tsdown v0.14.2 powered by rolldown v1.0.0-beta.36
3
+ ℹ Using tsdown config: /Users/siddhant.c.gupta/Projects/GS/storybooker/packages/cli/tsdown.config.ts
4
+ ℹ entry: src/index.ts
5
+ ℹ tsconfig: tsconfig.json
6
+ ℹ Build start
7
+ ℹ Cleaning 1 files
8
+ ℹ Granting execute permission to dist/index.js
9
+ ℹ dist/index.js 18.25 kB │ gzip: 5.14 kB
10
+ ℹ 1 files, total: 18.25 kB
11
+ ✔ Build complete in 53ms
@@ -0,0 +1,2 @@
1
+
2
+ 
@@ -0,0 +1,3 @@
1
+
2
+ Found 0 warnings and 0 errors.
3
+ Finished in 785ms on 13 files using 10 threads.
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # storybooker
2
+
3
+ ## 0.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 6578fe4: Init StoryBooker packages
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # StoryBooker CLI
2
+
3
+ A NodeJS CLI to sync with StoryBooker service. The CLI can be used to build, test, upload StoryBook to the service.
4
+
5
+ ## Usage
6
+
7
+ See commands + help
8
+
9
+ ```sh
10
+ npx -y storybooker -h
11
+ ```
12
+
13
+ ### Build+Upload assets
14
+
15
+ ```sh
16
+ npx -y storybooker create \
17
+ -u https://<storybooker-service> \
18
+ -p <project-id> \
19
+ --id <build-id> \
20
+ --test \
21
+ -l <branch-name> -l <another-label> \
22
+ --authorName <your-name> \
23
+ --authorEmail <your-email> \
24
+ --message "<readable message>"
25
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,554 @@
1
+ #!/usr/bin/env node
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ import * as fs$1 from "node:fs";
5
+ import fs from "node:fs";
6
+ import * as path$1 from "node:path";
7
+ import path from "node:path";
8
+ import { styleText } from "node:util";
9
+ import createClient from "openapi-fetch";
10
+ import z from "zod";
11
+ import { ChildProcess, execSync, spawnSync } from "node:child_process";
12
+ import * as process$1 from "node:process";
13
+ import { tmpdir } from "node:os";
14
+
15
+ //#region package.json
16
+ var name = "storybooker";
17
+ var version = "0.0.0";
18
+
19
+ //#endregion
20
+ //#region src/utils/auth-utils.ts
21
+ function createAuthMiddleware(options) {
22
+ const { authType, authValue } = options;
23
+ return { onRequest: ({ request }) => {
24
+ if (!authValue) return request;
25
+ if (!authType) return request;
26
+ switch (authType) {
27
+ case "auth-header":
28
+ request.headers.set("Authorization", authValue);
29
+ return request;
30
+ default: return request;
31
+ }
32
+ } };
33
+ }
34
+
35
+ //#endregion
36
+ //#region src/utils/pkg-utils.ts
37
+ function detectPackageManager(startDir = process.cwd(), maxDepth = 5) {
38
+ let currentDir = startDir;
39
+ let depth = 0;
40
+ while (depth < maxDepth) {
41
+ if (fs$1.existsSync(path$1.join(currentDir, `yarn.lock`))) return "yarn";
42
+ if (fs$1.existsSync(path$1.join(currentDir, `pnpm-lock.yaml`))) return "pnpm";
43
+ if (fs$1.existsSync(path$1.join(currentDir, `package-lock.json`))) return "npm";
44
+ if (fs$1.existsSync(path$1.join(currentDir, `bun.lock`))) return "bun";
45
+ const parentDir = path$1.dirname(currentDir);
46
+ if (parentDir === currentDir) break;
47
+ currentDir = parentDir;
48
+ }
49
+ throw new Error("Package manager could not be determined.");
50
+ }
51
+
52
+ //#endregion
53
+ //#region src/utils/sb-build.ts
54
+ function buildStoryBook({ build, cwd, silent }) {
55
+ if (build === false) {
56
+ console.log("> Skipping StoryBook Build.");
57
+ return;
58
+ }
59
+ let output = "";
60
+ if (typeof build === "string" && build.trim() !== "") {
61
+ console.log("> Building StoryBook with script: %s", build);
62
+ const pkgManager = detectPackageManager(cwd);
63
+ output = spawnSync(pkgManager, ["run", build], {
64
+ cwd,
65
+ shell: true,
66
+ stdio: silent ? void 0 : "inherit",
67
+ encoding: "utf8"
68
+ }).stdout;
69
+ } else {
70
+ console.log("> Building StoryBook with storybook CLI");
71
+ output = spawnSync("npx", [
72
+ "-y",
73
+ "storybook",
74
+ "build"
75
+ ], {
76
+ cwd,
77
+ stdio: silent ? void 0 : "inherit",
78
+ encoding: "utf8"
79
+ }).stdout;
80
+ }
81
+ const outputDirpath = output?.split("Output directory: ").at(1)?.trim();
82
+ if (!outputDirpath || !fs$1.existsSync(outputDirpath)) {
83
+ console.error(`Could not find build output at '${outputDirpath}'.`);
84
+ return;
85
+ }
86
+ console.log("> Built StoryBook to dir: '%s'.", path$1.relative(cwd, outputDirpath));
87
+ return outputDirpath;
88
+ }
89
+
90
+ //#endregion
91
+ //#region src/utils/sb-test.ts
92
+ function testStoryBook({ test, cwd, silent, testReportDir = ".test/report", testCoverageDir = ".test/coverage" }) {
93
+ if (test) try {
94
+ runTest(test, {
95
+ cwd,
96
+ silent,
97
+ testCoverageDir,
98
+ testReportDir
99
+ });
100
+ } catch (error) {
101
+ console.error(error);
102
+ }
103
+ else console.log("> Skipping tests");
104
+ const testReportDirpath = path$1.join(cwd, testReportDir);
105
+ const existsTestReportDirpath = fs$1.existsSync(testReportDirpath);
106
+ if (existsTestReportDirpath) console.log("> Test report saved at '%s'.", path$1.relative(cwd, testReportDirpath));
107
+ else console.warn("> Test report was not created'.");
108
+ const testCoverageDirpath = path$1.join(cwd, testCoverageDir);
109
+ const existsTestCoverageDirpath = fs$1.existsSync(testCoverageDirpath);
110
+ if (existsTestCoverageDirpath) console.log("> Test coverage saved at '%s'.", path$1.relative(cwd, testCoverageDirpath));
111
+ else console.warn("> Test coverage was not created'.");
112
+ return {
113
+ testCoverageDirpath: existsTestCoverageDirpath ? testCoverageDirpath : void 0,
114
+ testReportDirpath: existsTestReportDirpath ? testReportDirpath : void 0
115
+ };
116
+ }
117
+ function runTest(test, options) {
118
+ const { cwd, silent, testCoverageDir, testReportDir } = options;
119
+ if (typeof test === "string" && test.trim() !== "") {
120
+ console.log("> Testing StoryBook with script: %s", test);
121
+ const pkgManager = detectPackageManager(cwd);
122
+ spawnSync(pkgManager, ["run", test], {
123
+ cwd,
124
+ shell: true,
125
+ stdio: silent ? void 0 : "inherit"
126
+ });
127
+ return;
128
+ }
129
+ console.log("> Testing StoryBook with Vitest");
130
+ spawnSync("npx", [
131
+ "-y",
132
+ "vitest",
133
+ "run",
134
+ "--reporter=default",
135
+ "--reporter=html",
136
+ `--outputFile.html=${path$1.join(testReportDir, "index.html")}`,
137
+ "--reporter=json",
138
+ `--outputFile.json=${path$1.join(testReportDir, "report.json")}`,
139
+ "--coverage",
140
+ "--coverage.provider=v8",
141
+ "--coverage.reportOnFailure",
142
+ `--coverage.reportsDirectory=${testCoverageDir}`,
143
+ "--coverage.reporter=text",
144
+ "--coverage.reporter=html",
145
+ "--coverage.reporter=text-summary",
146
+ "--coverage.reporter=json-summary"
147
+ ], {
148
+ cwd,
149
+ stdio: silent ? void 0 : "inherit"
150
+ });
151
+ }
152
+
153
+ //#endregion
154
+ //#region src/utils/schema-utils.ts
155
+ const sharedSchemas = {
156
+ project: z.string({ error: "ProjectID is required to match with project." }).meta({
157
+ alias: ["p"],
158
+ description: "Project ID associated with the StoryBook."
159
+ }),
160
+ url: z.url({ error: "URL is required to connect to the service." }).meta({
161
+ alias: ["u"],
162
+ description: "URL of the StoryBooker service."
163
+ }),
164
+ cwd: z.string().optional().meta({ description: "Change the working directory for the command." }),
165
+ testReportDir: z.string().optional().meta({ description: "Relative path of the test report directory to upload." }),
166
+ testCoverageDir: z.string().optional().meta({ description: "Relative path of the test coverage directory to upload." }),
167
+ authType: z.enum(["auth-header"]).optional().meta({ description: "Enable auth for outgoing requests." }),
168
+ authValue: z.string().optional().meta({
169
+ description: "Auth value set for outgoing requests.",
170
+ implies: "authType"
171
+ })
172
+ };
173
+ function zodSchemaToCommandBuilder(objectSchema) {
174
+ const builder = {};
175
+ for (const [key, schema$1] of Object.entries(objectSchema._zod.def.shape)) {
176
+ const meta = schema$1 instanceof z.ZodType ? schema$1.meta() : void 0;
177
+ let optional = false;
178
+ try {
179
+ if (schema$1["~standard"].validate(void 0, {}) instanceof Promise) throw new TypeError("Cannot handle async schema");
180
+ optional = true;
181
+ } catch {
182
+ optional = false;
183
+ }
184
+ let errorMessage = schema$1._zod.def.error?.(void 0);
185
+ if (typeof errorMessage === "object") errorMessage = errorMessage?.message;
186
+ let defaultValue = schema$1 instanceof z.core.$ZodDefault ? schema$1._zod.def.defaultValue : void 0;
187
+ if (typeof defaultValue === "function") defaultValue = String(defaultValue());
188
+ const description = String(schema$1._zod.def.description ?? schema$1.description);
189
+ builder[key] = {
190
+ alias: meta?.alias,
191
+ description,
192
+ demandOption: optional ? false : errorMessage ?? true,
193
+ type: zodSchemaTypeToYargsBuilderType(schema$1),
194
+ default: defaultValue,
195
+ deprecated: meta?.deprecated,
196
+ hidden: meta?.hidden,
197
+ implies: meta?.implies
198
+ };
199
+ }
200
+ return builder;
201
+ }
202
+ function zodSchemaTypeToYargsBuilderType(schema$1) {
203
+ if (schema$1 instanceof z.core.$ZodArray) return "array";
204
+ if (schema$1 instanceof z.core.$ZodBoolean) return "boolean";
205
+ if (schema$1 instanceof z.core.$ZodNumber) return "number";
206
+ if (schema$1 instanceof z.core.$ZodOptional) return zodSchemaTypeToYargsBuilderType(schema$1._zod.def.innerType);
207
+ if (schema$1 instanceof z.core.$ZodDefault) return zodSchemaTypeToYargsBuilderType(schema$1._zod.def.innerType);
208
+ if (schema$1 instanceof z.core.$ZodUnion) return;
209
+ return "string";
210
+ }
211
+
212
+ //#endregion
213
+ //#region src/utils/stream-utils.ts
214
+ function toReadableStream(readable, filesize, onProgress = onUploadProgress) {
215
+ let uploaded = 0;
216
+ return new ReadableStream({
217
+ start: (controller) => {
218
+ function onData(chunk) {
219
+ try {
220
+ uploaded += chunk.length;
221
+ controller.enqueue(chunk);
222
+ onProgress?.(uploaded, filesize);
223
+ } catch {
224
+ cleanup();
225
+ readable.destroy();
226
+ }
227
+ }
228
+ function onEnd() {
229
+ cleanup();
230
+ controller.close();
231
+ }
232
+ function onError(err) {
233
+ cleanup();
234
+ controller.error(err);
235
+ }
236
+ function cleanup() {
237
+ readable.off("data", onData);
238
+ readable.off("end", onEnd);
239
+ readable.off("error", onError);
240
+ console.log("");
241
+ }
242
+ readable.on("data", onData);
243
+ readable.on("end", onEnd);
244
+ readable.on("error", onError);
245
+ },
246
+ cancel: () => {
247
+ readable.destroy();
248
+ }
249
+ });
250
+ }
251
+ /** Function to handle upload progress */
252
+ function onUploadProgress(uploaded, total) {
253
+ const percentStr = (uploaded / total * 100).toFixed(2).padStart(6, " ");
254
+ const totalStr = (total / 1e3).toFixed(2);
255
+ const uploadedStr = (uploaded / 1e3).toFixed(2).padStart(totalStr.length, " ");
256
+ process$1.stdout.write(styleText("dim", `\r - Uploaded: ${uploadedStr} / ${totalStr} KB (${percentStr}%)`));
257
+ }
258
+
259
+ //#endregion
260
+ //#region src/utils/zip.ts
261
+ const isWindows = process.platform === "win32";
262
+ /**
263
+ * Cross-platform sync zip utility that works on Windows and Unix-like systems.
264
+ * It uses the `zip` command on Unix-like systems and PowerShell on Windows.
265
+ * It throws an error if the `zip` command is not available on Unix-like systems.
266
+ */
267
+ function zip(inPath, outPath) {
268
+ if (isWindows) {
269
+ if (fs.statSync(inPath).isFile()) {
270
+ const inFile = fs.readFileSync(inPath);
271
+ const tmpPath = path.join(tmpdir(), `cross-zip-${Date.now()}`);
272
+ fs.mkdirSync(tmpPath);
273
+ fs.writeFileSync(path.join(tmpPath, path.basename(inPath)), inFile);
274
+ inPath = tmpPath;
275
+ }
276
+ fs.rmdirSync(outPath, {
277
+ recursive: true,
278
+ maxRetries: 3
279
+ });
280
+ }
281
+ const cwd = path.dirname(inPath);
282
+ const zipCmd = getZipCommand();
283
+ const zipCmdArgs = getZipArgs(inPath, outPath);
284
+ try {
285
+ execSync([zipCmd, ...zipCmdArgs].join(" "), {
286
+ cwd,
287
+ maxBuffer: Infinity,
288
+ encoding: "utf8"
289
+ });
290
+ } catch (error) {
291
+ if (error instanceof ChildProcess) {
292
+ console.error("STDOUT:", error.stdout);
293
+ console.error("STDERR:", error.stderr);
294
+ }
295
+ throw error;
296
+ }
297
+ }
298
+ function getZipCommand() {
299
+ if (isWindows) return "powershell.exe";
300
+ return "zip";
301
+ }
302
+ function getZipArgs(inPath, outPath) {
303
+ if (isWindows) return [
304
+ "-nologo",
305
+ "-noprofile",
306
+ "-command",
307
+ "& { param([String]$myInPath, [String]$myOutPath); Add-Type -A \"System.IO.Compression.FileSystem\"; [IO.Compression.ZipFile]::CreateFromDirectory($myInPath, $myOutPath); exit !$? }",
308
+ "-myInPath",
309
+ quotePath(inPath),
310
+ "-myOutPath",
311
+ quotePath(outPath)
312
+ ];
313
+ const dirname = path.basename(inPath.toString());
314
+ return [
315
+ "-r",
316
+ "-y",
317
+ outPath.toString(),
318
+ dirname
319
+ ];
320
+ }
321
+ function quotePath(pathToTransform) {
322
+ return `"${pathToTransform.toString()}"`;
323
+ }
324
+
325
+ //#endregion
326
+ //#region src/commands/create.ts
327
+ const CreateSchema = z.object({
328
+ project: sharedSchemas.project,
329
+ url: sharedSchemas.url,
330
+ cwd: sharedSchemas.cwd,
331
+ sha: z.string({ error: "URL is required to connect to the service." }).meta({
332
+ alias: ["id"],
333
+ description: "Unique ID of the build."
334
+ }),
335
+ message: z.string().optional().meta({
336
+ alias: ["m"],
337
+ description: "Readable message for the build."
338
+ }),
339
+ labels: z.string().array().meta({
340
+ alias: ["l"],
341
+ description: "Labels associated with the build."
342
+ }),
343
+ build: z.union([z.string(), z.boolean()]).optional().meta({
344
+ alias: ["b"],
345
+ description: "Name of the script in package.json to build the StoryBook."
346
+ }),
347
+ test: z.union([z.string(), z.boolean()]).optional().meta({
348
+ alias: ["t"],
349
+ description: "Name of the script in package.json to test the StoryBook."
350
+ }),
351
+ testCoverageDir: sharedSchemas.testCoverageDir,
352
+ testReportDir: sharedSchemas.testReportDir,
353
+ silent: z.boolean().default(false).meta({
354
+ alias: ["s"],
355
+ description: "Silent the logs and only show final error/status."
356
+ }),
357
+ authorName: z.string().optional().meta({ description: "Name of the author of the build." }),
358
+ authorEmail: z.email().optional().meta({ description: "Email of the author of the build." }),
359
+ ignoreError: z.boolean().default(false).meta({ hidden: true }),
360
+ authType: sharedSchemas.authType,
361
+ authValue: sharedSchemas.authValue
362
+ });
363
+ const createCommandModule = {
364
+ command: "create",
365
+ describe: "Create and upload StoryBook assets to the service.",
366
+ builder: zodSchemaToCommandBuilder(CreateSchema),
367
+ handler: async (args) => {
368
+ const result = CreateSchema.safeParse(args);
369
+ if (!result.success) throw new Error(z.prettifyError(result.error));
370
+ const cwd = result.data.cwd ? path$1.resolve(result.data.cwd) : process.cwd();
371
+ if (cwd && !fs$1.existsSync(cwd)) throw new Error(`Path provided to CWD does not exists: '${cwd}'`);
372
+ const { build, silent, url, project, sha, ignoreError, test, testCoverageDir, testReportDir, authType, authValue } = result.data;
373
+ const client = createClient({ baseUrl: url });
374
+ client.use(createAuthMiddleware({
375
+ authType,
376
+ authValue
377
+ }));
378
+ try {
379
+ console.group(styleText("bold", "\nCreate Build Entry"));
380
+ await createSBRBuild(client, result.data, ignoreError);
381
+ console.groupEnd();
382
+ console.group(styleText("bold", "\nBuild StoryBook"));
383
+ const buildDirpath = buildStoryBook({
384
+ build,
385
+ cwd,
386
+ silent
387
+ });
388
+ if (buildDirpath) {
389
+ await uploadSBRBuild(client, {
390
+ project,
391
+ sha,
392
+ dirpath: buildDirpath,
393
+ variant: "storybook",
394
+ cwd
395
+ });
396
+ console.groupEnd();
397
+ }
398
+ console.group(styleText("bold", "\nTest StoryBook"));
399
+ const { testCoverageDirpath, testReportDirpath } = testStoryBook({
400
+ cwd,
401
+ test,
402
+ silent,
403
+ testCoverageDir,
404
+ testReportDir
405
+ });
406
+ if (testReportDirpath) await uploadSBRBuild(client, {
407
+ project,
408
+ sha,
409
+ dirpath: testReportDirpath,
410
+ cwd,
411
+ variant: "testReport"
412
+ });
413
+ if (testCoverageDirpath) await uploadSBRBuild(client, {
414
+ project,
415
+ sha,
416
+ dirpath: testCoverageDirpath,
417
+ cwd,
418
+ variant: "coverage"
419
+ });
420
+ console.groupEnd();
421
+ console.log(styleText("green", "Created build successfully."));
422
+ } catch (error) {
423
+ console.error(error);
424
+ process.exit(1);
425
+ }
426
+ }
427
+ };
428
+ async function createSBRBuild(client, { project, sha, message, labels }, ignorePrevious) {
429
+ const { error, response } = await client.POST("/projects/{projectId}/builds/create", {
430
+ params: { path: { projectId: project } },
431
+ body: {
432
+ authorEmail: "Siddhant@asd.com",
433
+ authorName: "Siddhant",
434
+ labels,
435
+ sha,
436
+ message
437
+ },
438
+ headers: {
439
+ Accept: "application/json",
440
+ "Content-Type": "application/x-www-form-urlencoded"
441
+ }
442
+ });
443
+ if (error) if (ignorePrevious) console.warn(styleText("yellow", "> StoryBooker Build entry already exits."));
444
+ else throw new Error(error.errorMessage || `Request to service failed with status: ${response.status}.`);
445
+ else console.log("New Build entry created '%s / %s'", project, sha);
446
+ }
447
+ async function uploadSBRBuild(client, { project, sha, dirpath, variant, cwd }) {
448
+ const zipFilepath = path$1.join(cwd, `${variant}.zip`);
449
+ console.log(`> Compressing directory '%s' to file '%s'...`, path$1.relative(cwd, dirpath), path$1.relative(cwd, zipFilepath));
450
+ zip(path$1.join(dirpath, "*"), zipFilepath);
451
+ const fileSize = fs$1.statSync(zipFilepath).size;
452
+ console.log(`> Uploading file '%s'...`, path$1.relative(cwd, zipFilepath));
453
+ const { error, response } = await client.POST("/projects/{projectId}/builds/{buildSHA}/upload", {
454
+ params: {
455
+ path: {
456
+ projectId: project,
457
+ buildSHA: sha
458
+ },
459
+ query: { variant }
460
+ },
461
+ body: toReadableStream(fs$1.createReadStream(zipFilepath), fileSize),
462
+ bodySerializer: (body) => body,
463
+ headers: {
464
+ Accept: "application/json",
465
+ "Content-Type": "application/zip",
466
+ "Content-Length": fileSize.toString()
467
+ },
468
+ duplex: "half"
469
+ });
470
+ if (error) throw new Error(error.errorMessage || `Request to service failed with status: ${response.status}.`);
471
+ else {
472
+ console.log("> Uploaded '%s / %s / %s'.", project, sha, variant);
473
+ fs$1.rmSync(zipFilepath);
474
+ }
475
+ }
476
+
477
+ //#endregion
478
+ //#region src/commands/purge.ts
479
+ const PurgeSchema = z.object({
480
+ project: sharedSchemas.project,
481
+ url: sharedSchemas.url,
482
+ label: z.string().meta({
483
+ alias: ["l"],
484
+ description: "The label slug to purge associated builds."
485
+ }),
486
+ authType: sharedSchemas.authType,
487
+ authValue: sharedSchemas.authValue
488
+ });
489
+ const purgeCommandModule = {
490
+ command: "purge",
491
+ describe: "Purge StoryBook assets from the service.",
492
+ builder: zodSchemaToCommandBuilder(PurgeSchema),
493
+ handler: async (args) => {
494
+ const result = PurgeSchema.safeParse(args);
495
+ if (!result.success) throw new Error(z.prettifyError(result.error));
496
+ const { label, project, url, authType, authValue } = result.data;
497
+ const client = createClient({ baseUrl: url });
498
+ client.use(createAuthMiddleware({
499
+ authType,
500
+ authValue
501
+ }));
502
+ try {
503
+ console.group(styleText("bold", "\nPurge Label '%s' (project: %s)"), label, project);
504
+ console.log(`> Deleting label '%s' and associated builds...`, label);
505
+ const { error, response } = await client.DELETE("/projects/{projectId}/labels/{labelSlug}", {
506
+ params: { path: {
507
+ labelSlug: label,
508
+ projectId: project
509
+ } },
510
+ headers: { Accept: "application/json" }
511
+ });
512
+ if (error) throw new Error(error.errorMessage || `Request to service failed with status: ${response.status}.`);
513
+ else console.log("> Purged '%s / %s'.", project, label);
514
+ console.groupEnd();
515
+ } catch (error) {
516
+ console.error(error);
517
+ process.exit(1);
518
+ }
519
+ }
520
+ };
521
+
522
+ //#endregion
523
+ //#region src/commands/test.ts
524
+ const schema = z.object({
525
+ cwd: sharedSchemas.cwd,
526
+ testCoverageDir: sharedSchemas.testCoverageDir,
527
+ testReportDir: sharedSchemas.testReportDir
528
+ });
529
+ const testCommandModule = {
530
+ command: "test",
531
+ describe: "Run test on StoryBook with Vitest",
532
+ builder: zodSchemaToCommandBuilder(schema),
533
+ handler(args) {
534
+ const result = schema.safeParse(args);
535
+ if (!result.success) throw new Error(z.prettifyError(result.error));
536
+ const cwd = result.data.cwd ? path$1.resolve(result.data.cwd) : process.cwd();
537
+ if (cwd && !fs$1.existsSync(cwd)) throw new Error(`Path provided to CWD does not exists: '${cwd}'`);
538
+ console.group(styleText("bold", "\nTest StoryBook"));
539
+ testStoryBook({
540
+ cwd,
541
+ test: true,
542
+ testCoverageDir: result.data.testCoverageDir,
543
+ testReportDir: result.data.testReportDir
544
+ });
545
+ console.groupEnd();
546
+ }
547
+ };
548
+
549
+ //#endregion
550
+ //#region src/index.ts
551
+ await yargs(hideBin(process.argv)).scriptName(name).usage(`npx -y $0 [command] (options)`).version(version).command(createCommandModule).command(purgeCommandModule).command(testCommandModule).alias("h", "help").alias("v", "version").parse();
552
+
553
+ //#endregion
554
+ export { };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "storybooker",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "bin": "./dist/index.js",
6
+ "description": "Storybooker CLI for uploading builds and files.",
7
+ "author": {
8
+ "name": "Siddhant Gupta",
9
+ "url": "https://guptasiddhant.com"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/guptasiddhant/storybooker",
14
+ "directory": "packages/cli"
15
+ },
16
+ "license": "MIT",
17
+ "sideEffects": false,
18
+ "scripts": {
19
+ "build": "tsdown",
20
+ "check": "tsgo --noEmit",
21
+ "cli": "node ./dist/index.js --cwd ../../apps/test-ui",
22
+ "dev": "tsdown -w ./src",
23
+ "fmt": "prettier src --write --config ../../.prettierrc",
24
+ "lint": "oxlint --type-aware ./src",
25
+ "lint:fix": "yarn lint --fix",
26
+ "openapi": "openapi-typescript ../core/dist/openapi.json -o ./src/service-schema.d.ts",
27
+ "start": "node --watch ./dist/index.js --cwd ../../apps/test-ui"
28
+ },
29
+ "dependencies": {
30
+ "openapi-fetch": "^0.14.0",
31
+ "yargs": "^18.0.0",
32
+ "zod": "4.1.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.0.0",
36
+ "@types/yargs": "^17.0.33",
37
+ "@typescript/native-preview": "^7.0.0-dev.20250823.8",
38
+ "openapi-typescript": "^7.9.1",
39
+ "oxlint": "^1.12.0",
40
+ "oxlint-tsgolint": "^0.0.4",
41
+ "prettier": "^3.6.2",
42
+ "tsdown": "^0.14.1",
43
+ "typescript": "^5.9.2"
44
+ }
45
+ }