@rune-cli/rune 0.0.5 → 0.0.7

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/dist/cli.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { i as successResult, n as runManifestCommand, r as failureResult, t as writeCommandExecutionResult } from "./write-result-qylmqXvG.mjs";
2
+ import "./dist-FWLEc-op.mjs";
3
+ import { n as isHelpFlag, r as isVersionFlag, t as runManifestCommand } from "./run-manifest-command-BmeVwPA5.mjs";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import { build } from "esbuild";
5
6
  import { cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
6
7
  import path from "node:path";
7
8
  import ts from "typescript";
8
9
  //#region package.json
9
- var version = "0.0.5";
10
+ var version = "0.0.7";
10
11
  //#endregion
11
12
  //#region src/manifest/generate-manifest.ts
12
13
  const COMMAND_ENTRY_FILE = "index.ts";
@@ -116,20 +117,25 @@ function resolveCommandsDirectory(projectRoot) {
116
117
  function resolveDistDirectory(projectRoot) {
117
118
  return path.join(projectRoot, DIST_DIRECTORY_NAME);
118
119
  }
119
- async function readProjectCliName(projectRoot) {
120
+ async function readProjectCliInfo(projectRoot) {
120
121
  const packageJsonPath = path.join(projectRoot, "package.json");
121
122
  try {
122
123
  const packageJsonContents = await readFile(packageJsonPath, "utf8");
123
124
  const packageJson = JSON.parse(packageJsonContents);
125
+ let name;
124
126
  if (packageJson.bin && typeof packageJson.bin === "object") {
125
127
  const binNames = Object.keys(packageJson.bin).sort((left, right) => left.localeCompare(right));
126
- if (binNames.length > 0) return binNames[0];
128
+ if (binNames.length > 0) name = binNames[0];
127
129
  }
128
- if (packageJson.name && packageJson.name.length > 0) return packageJson.name.split("/").at(-1) ?? packageJson.name;
130
+ if (!name && packageJson.name && packageJson.name.length > 0) name = packageJson.name.split("/").at(-1) ?? packageJson.name;
131
+ return {
132
+ name: name ?? path.basename(projectRoot),
133
+ version: packageJson.version
134
+ };
129
135
  } catch (error) {
130
136
  if (error.code !== "ENOENT") throw error;
131
137
  }
132
- return path.basename(projectRoot);
138
+ return { name: path.basename(projectRoot) };
133
139
  }
134
140
  async function assertCommandsDirectoryExists(commandsDirectory) {
135
141
  if (!(await stat(commandsDirectory).catch((error) => {
@@ -138,6 +144,32 @@ async function assertCommandsDirectoryExists(commandsDirectory) {
138
144
  }))?.isDirectory()) throw new Error(`Commands directory not found at ${COMMANDS_DIRECTORY_NAME}. Create it or check the --project <path> option.`);
139
145
  }
140
146
  //#endregion
147
+ //#region src/cli/write-result.ts
148
+ async function writeStream(stream, contents) {
149
+ if (contents.length === 0) return;
150
+ await new Promise((resolve, reject) => {
151
+ stream.write(contents, (error) => {
152
+ if (error) {
153
+ reject(error);
154
+ return;
155
+ }
156
+ resolve();
157
+ });
158
+ });
159
+ }
160
+ function ensureTrailingNewline(contents) {
161
+ return contents.endsWith("\n") ? contents : `${contents}\n`;
162
+ }
163
+ async function writeStdout(contents) {
164
+ await writeStream(process.stdout, contents);
165
+ }
166
+ async function writeStderr(contents) {
167
+ await writeStream(process.stderr, contents);
168
+ }
169
+ async function writeStderrLine(contents) {
170
+ await writeStderr(ensureTrailingNewline(contents));
171
+ }
172
+ //#endregion
141
173
  //#region src/cli/build-command.ts
142
174
  const BUILD_CLI_FILENAME = "cli.mjs";
143
175
  const BUILD_MANIFEST_FILENAME = "manifest.json";
@@ -200,13 +232,14 @@ async function copyBuiltAssets(sourceDirectory, distDirectory) {
200
232
  await cp(sourceEntryPath, distEntryPath);
201
233
  }));
202
234
  }
203
- function renderBuiltCliEntry(cliName, runtimeImportPath) {
235
+ function renderBuiltCliEntry(cliName, version, runtimeImportPath) {
204
236
  return `import { readFile } from "node:fs/promises";
205
237
  import { fileURLToPath } from "node:url";
206
238
 
207
- import { runManifestCommand, writeCommandExecutionResult } from ${JSON.stringify(runtimeImportPath)};
239
+ import { runManifestCommand } from ${JSON.stringify(runtimeImportPath)};
208
240
 
209
241
  const cliName = ${JSON.stringify(cliName)};
242
+ const version = ${JSON.stringify(version)};
210
243
  const distDirectoryUrl = new URL("./", import.meta.url);
211
244
  const manifestPath = fileURLToPath(new URL("./${BUILD_MANIFEST_FILENAME}", distDirectoryUrl));
212
245
  const manifestContents = await readFile(manifestPath, "utf8");
@@ -222,14 +255,13 @@ const runtimeManifest = {
222
255
  : node,
223
256
  ),
224
257
  };
225
- const result = await runManifestCommand({
258
+ process.exitCode = await runManifestCommand({
226
259
  manifest: runtimeManifest,
227
260
  rawArgs: process.argv.slice(2),
228
261
  cliName,
262
+ version,
229
263
  cwd: process.cwd(),
230
264
  });
231
-
232
- await writeCommandExecutionResult(result);
233
265
  `;
234
266
  }
235
267
  function collectCommandEntryPoints(manifest) {
@@ -292,12 +324,12 @@ async function buildCommandEntries(projectRoot, sourceDirectory, distDirectory,
292
324
  write: true
293
325
  });
294
326
  }
295
- async function buildCliEntry(projectRoot, distDirectory, cliName) {
327
+ async function buildCliEntry(projectRoot, distDirectory, cliName, version) {
296
328
  const runtimeHelperEntryPath = await resolveRuntimeHelperEntryPath();
297
329
  await build({
298
330
  absWorkingDir: projectRoot,
299
331
  stdin: {
300
- contents: renderBuiltCliEntry(cliName, `./${path.basename(runtimeHelperEntryPath)}`),
332
+ contents: renderBuiltCliEntry(cliName, version, `./${path.basename(runtimeHelperEntryPath)}`),
301
333
  loader: "ts",
302
334
  resolveDir: path.dirname(runtimeHelperEntryPath),
303
335
  sourcefile: "rune-built-cli-entry.ts"
@@ -341,7 +373,7 @@ async function runBuildCommand(options) {
341
373
  await assertCommandsDirectoryExists(commandsDirectory);
342
374
  const sourceManifest = await generateCommandManifest({ commandsDirectory });
343
375
  const builtManifest = createBuiltManifest(sourceManifest, sourceDirectory);
344
- const cliName = await readProjectCliName(projectRoot);
376
+ const cliInfo = await readProjectCliInfo(projectRoot);
345
377
  await rm(distDirectory, {
346
378
  recursive: true,
347
379
  force: true
@@ -349,13 +381,18 @@ async function runBuildCommand(options) {
349
381
  await writeBuiltRuntimeFiles(distDirectory, builtManifest);
350
382
  await Promise.all([
351
383
  buildCommandEntries(projectRoot, sourceDirectory, distDirectory, sourceManifest),
352
- buildCliEntry(projectRoot, distDirectory, cliName),
384
+ buildCliEntry(projectRoot, distDirectory, cliInfo.name, cliInfo.version),
353
385
  copyBuiltAssets(sourceDirectory, distDirectory)
354
386
  ]);
355
- return successResult(`Built CLI to ${path.join(distDirectory, BUILD_CLI_FILENAME)}\n`);
387
+ await writeStdout(`Built CLI to ${path.join(distDirectory, BUILD_CLI_FILENAME)}\n`);
388
+ return 0;
356
389
  } catch (error) {
357
- if (isBuildFailure(error)) return failureResult(formatBuildFailure(projectRoot, error));
358
- return failureResult(error instanceof Error ? error.message : "Failed to run rune build");
390
+ if (isBuildFailure(error)) {
391
+ await writeStderrLine(formatBuildFailure(projectRoot, error));
392
+ return 1;
393
+ }
394
+ await writeStderrLine(error instanceof Error ? error.message : "Failed to run rune build");
395
+ return 1;
359
396
  }
360
397
  }
361
398
  //#endregion
@@ -386,6 +423,11 @@ Examples:
386
423
  async function runDevCommand(options) {
387
424
  try {
388
425
  const projectRoot = resolveProjectPath(options);
426
+ const cliInfo = await readProjectCliInfo(projectRoot);
427
+ if (cliInfo.version && options.rawArgs.length === 1 && isVersionFlag(options.rawArgs[0])) {
428
+ await writeStdout(`${cliInfo.name} v${cliInfo.version}\n`);
429
+ return 0;
430
+ }
389
431
  const commandsDirectory = resolveCommandsDirectory(projectRoot);
390
432
  await assertCommandsDirectoryExists(commandsDirectory);
391
433
  const manifest = await generateCommandManifest({ commandsDirectory });
@@ -393,36 +435,44 @@ async function runDevCommand(options) {
393
435
  return runManifestCommand({
394
436
  manifest,
395
437
  rawArgs: options.rawArgs,
396
- cliName: await readProjectCliName(projectRoot),
438
+ cliName: cliInfo.name,
439
+ version: cliInfo.version,
397
440
  cwd: options.cwd
398
441
  });
399
442
  } catch (error) {
400
- return failureResult(error instanceof Error ? error.message : "Failed to run rune dev");
443
+ await writeStderrLine(error instanceof Error ? error.message : "Failed to run rune dev");
444
+ return 1;
401
445
  }
402
446
  }
403
447
  //#endregion
404
448
  //#region src/cli/rune-cli.ts
449
+ async function writeEarlyExit(exit) {
450
+ if (exit.stream === "stdout") await writeStdout(exit.output);
451
+ else await writeStderrLine(exit.output);
452
+ return exit.exitCode;
453
+ }
405
454
  function tryParseProjectOption(argv, index) {
406
455
  const token = argv[index];
407
456
  if (token.startsWith("--project=")) return {
457
+ ok: true,
408
458
  projectPath: token.slice(10),
409
459
  nextIndex: index + 1
410
460
  };
411
461
  if (token === "--project") {
412
462
  const nextToken = argv[index + 1];
413
- if (!nextToken) return failureResult("Missing value for --project. Usage: --project <path>");
463
+ if (!nextToken) return {
464
+ ok: false,
465
+ exitCode: 1,
466
+ output: "Missing value for --project. Usage: --project <path>",
467
+ stream: "stderr"
468
+ };
414
469
  return {
470
+ ok: true,
415
471
  projectPath: nextToken,
416
472
  nextIndex: index + 2
417
473
  };
418
474
  }
419
475
  }
420
- function isHelpFlag(token) {
421
- return token === "--help" || token === "-h";
422
- }
423
- function isVersionFlag(token) {
424
- return token === "--version" || token === "-V";
425
- }
426
476
  function getRuneVersion() {
427
477
  return version;
428
478
  }
@@ -434,25 +484,33 @@ function parseDevArgs(argv) {
434
484
  if (token === "--") {
435
485
  commandArgs.push(...argv.slice(index + 1));
436
486
  return {
487
+ ok: true,
437
488
  projectPath,
438
489
  commandArgs
439
490
  };
440
491
  }
441
- if (isHelpFlag(token)) return successResult(renderRuneDevHelp());
492
+ if (isHelpFlag(token)) return {
493
+ ok: false,
494
+ exitCode: 0,
495
+ output: renderRuneDevHelp(),
496
+ stream: "stdout"
497
+ };
442
498
  const projectResult = tryParseProjectOption(argv, index);
443
499
  if (projectResult) {
444
- if ("exitCode" in projectResult) return projectResult;
500
+ if (!projectResult.ok) return projectResult;
445
501
  projectPath = projectResult.projectPath;
446
502
  index = projectResult.nextIndex - 1;
447
503
  continue;
448
504
  }
449
505
  commandArgs.push(token, ...argv.slice(index + 1));
450
506
  return {
507
+ ok: true,
451
508
  projectPath,
452
509
  commandArgs
453
510
  };
454
511
  }
455
512
  return {
513
+ ok: true,
456
514
  projectPath,
457
515
  commandArgs
458
516
  };
@@ -461,17 +519,30 @@ function parseBuildArgs(argv) {
461
519
  let projectPath;
462
520
  for (let index = 0; index < argv.length; index += 1) {
463
521
  const token = argv[index];
464
- if (isHelpFlag(token)) return successResult(renderRuneBuildHelp());
522
+ if (isHelpFlag(token)) return {
523
+ ok: false,
524
+ exitCode: 0,
525
+ output: renderRuneBuildHelp(),
526
+ stream: "stdout"
527
+ };
465
528
  const projectResult = tryParseProjectOption(argv, index);
466
529
  if (projectResult) {
467
- if ("exitCode" in projectResult) return projectResult;
530
+ if (!projectResult.ok) return projectResult;
468
531
  projectPath = projectResult.projectPath;
469
532
  index = projectResult.nextIndex - 1;
470
533
  continue;
471
534
  }
472
- return failureResult(`Unexpected argument for rune build: ${token}`);
535
+ return {
536
+ ok: false,
537
+ exitCode: 1,
538
+ output: `Unexpected argument for rune build: ${token}`,
539
+ stream: "stderr"
540
+ };
473
541
  }
474
- return { projectPath };
542
+ return {
543
+ ok: true,
544
+ projectPath
545
+ };
475
546
  }
476
547
  function renderRuneCliHelp() {
477
548
  return `\
@@ -488,11 +559,17 @@ Options:
488
559
  }
489
560
  async function runRuneCli(options) {
490
561
  const [subcommand, ...restArgs] = options.argv;
491
- if (!subcommand || isHelpFlag(subcommand)) return successResult(renderRuneCliHelp());
492
- if (isVersionFlag(subcommand)) return successResult(`rune v${getRuneVersion()}\n`);
562
+ if (!subcommand || isHelpFlag(subcommand)) {
563
+ await writeStdout(renderRuneCliHelp());
564
+ return 0;
565
+ }
566
+ if (isVersionFlag(subcommand)) {
567
+ await writeStdout(`rune v${getRuneVersion()}\n`);
568
+ return 0;
569
+ }
493
570
  if (subcommand === "dev") {
494
571
  const parsedDevArgs = parseDevArgs(restArgs);
495
- if ("exitCode" in parsedDevArgs) return parsedDevArgs;
572
+ if (!parsedDevArgs.ok) return writeEarlyExit(parsedDevArgs);
496
573
  return runDevCommand({
497
574
  rawArgs: parsedDevArgs.commandArgs,
498
575
  projectPath: parsedDevArgs.projectPath,
@@ -501,19 +578,20 @@ async function runRuneCli(options) {
501
578
  }
502
579
  if (subcommand === "build") {
503
580
  const parsedBuildArgs = parseBuildArgs(restArgs);
504
- if ("exitCode" in parsedBuildArgs) return parsedBuildArgs;
581
+ if (!parsedBuildArgs.ok) return writeEarlyExit(parsedBuildArgs);
505
582
  return runBuildCommand({
506
583
  projectPath: parsedBuildArgs.projectPath,
507
584
  cwd: options.cwd
508
585
  });
509
586
  }
510
- return failureResult(`Unknown command: ${subcommand}. Available commands: build, dev`);
587
+ await writeStderrLine(`Unknown command: ${subcommand}. Available commands: build, dev`);
588
+ return 1;
511
589
  }
512
590
  //#endregion
513
591
  //#region src/cli.ts
514
- await writeCommandExecutionResult(await runRuneCli({
592
+ process.exitCode = await runRuneCli({
515
593
  argv: process.argv.slice(2),
516
594
  cwd: process.cwd()
517
- }));
595
+ });
518
596
  //#endregion
519
597
  export {};
@@ -3,7 +3,7 @@ import { format, parseArgs } from "node:util";
3
3
  function isSchemaField(field) {
4
4
  return "schema" in field && field.schema !== void 0;
5
5
  }
6
- const EMPTY_FIELDS = [];
6
+ const DEFINED_COMMAND_BRAND = Symbol.for("@rune-cli/defined-command");
7
7
  function isOptionalArg(field) {
8
8
  if (isSchemaField(field)) return;
9
9
  return field.required !== true || field.default !== void 0;
@@ -70,18 +70,43 @@ function validateArgOrdering(args) {
70
70
  */
71
71
  function defineCommand(input) {
72
72
  if (input.args) validateArgOrdering(input.args);
73
- return {
73
+ const command = {
74
74
  description: input.description,
75
- args: input.args ?? EMPTY_FIELDS,
76
- options: input.options ?? EMPTY_FIELDS,
75
+ args: input.args ?? [],
76
+ options: input.options ?? [],
77
77
  run: input.run
78
78
  };
79
+ Object.defineProperty(command, DEFINED_COMMAND_BRAND, {
80
+ value: true,
81
+ enumerable: false
82
+ });
83
+ return command;
84
+ }
85
+ function isDefinedCommand(value) {
86
+ return typeof value === "object" && value !== null && value[DEFINED_COMMAND_BRAND] === true;
79
87
  }
80
88
  function formatExecutionError(error) {
81
89
  if (error instanceof Error) return error.message === "" ? "" : error.message || error.name || "Unknown error";
82
90
  if (typeof error === "string") return error;
83
91
  return "Unknown error";
84
92
  }
93
+ async function executeCommand(command, input = {}) {
94
+ try {
95
+ await command.run({
96
+ options: input.options ?? {},
97
+ args: input.args ?? {},
98
+ cwd: input.cwd ?? process.cwd(),
99
+ rawArgs: input.rawArgs ?? []
100
+ });
101
+ return { exitCode: 0 };
102
+ } catch (error) {
103
+ const message = formatExecutionError(error);
104
+ return message ? {
105
+ exitCode: 1,
106
+ errorMessage: message
107
+ } : { exitCode: 1 };
108
+ }
109
+ }
85
110
  async function captureProcessOutput(action) {
86
111
  const stdoutChunks = [];
87
112
  const stderrChunks = [];
@@ -119,17 +144,18 @@ async function captureProcessOutput(action) {
119
144
  ]) console[method] = (...args) => captureConsole(stdoutChunks, args);
120
145
  for (const method of ["warn", "error"]) console[method] = (...args) => captureConsole(stderrChunks, args);
121
146
  try {
122
- const value = await action();
123
147
  return {
148
+ ok: true,
149
+ value: await action(),
124
150
  stdout: stdoutChunks.join(""),
125
- stderr: stderrChunks.join(""),
126
- value
151
+ stderr: stderrChunks.join("")
127
152
  };
128
153
  } catch (error) {
129
154
  return {
155
+ ok: false,
156
+ error,
130
157
  stdout: stdoutChunks.join(""),
131
- stderr: stderrChunks.join(""),
132
- error
158
+ stderr: stderrChunks.join("")
133
159
  };
134
160
  } finally {
135
161
  process.stdout.write = originalStdoutWrite;
@@ -137,34 +163,11 @@ async function captureProcessOutput(action) {
137
163
  Object.assign(console, originalConsoleMethods);
138
164
  }
139
165
  }
140
- const EMPTY_ARGS = [];
141
- async function executeCommand(command, input = {}) {
142
- const result = await captureProcessOutput(async () => {
143
- await command.run({
144
- options: input.options ?? {},
145
- args: input.args ?? {},
146
- cwd: input.cwd ?? process.cwd(),
147
- rawArgs: input.rawArgs ?? EMPTY_ARGS
148
- });
149
- });
150
- if (result.error === void 0) return {
151
- exitCode: 0,
152
- stdout: result.stdout,
153
- stderr: result.stderr
154
- };
155
- const message = formatExecutionError(result.error);
156
- return {
157
- exitCode: 1,
158
- stdout: result.stdout,
159
- stderr: `${result.stderr}${message ? `${message}\n` : ""}`
160
- };
161
- }
162
- function formatFieldTypeHint(field) {
163
- if (isSchemaField(field)) return "";
164
- return ` <${field.type}>`;
166
+ function formatTypeHint(field) {
167
+ return isSchemaField(field) ? "" : ` <${field.type}>`;
165
168
  }
166
169
  function formatOptionLabel(field) {
167
- return `--${field.name}${formatFieldTypeHint(field)}`;
170
+ return `--${field.name}${formatTypeHint(field)}`;
168
171
  }
169
172
  function formatArgumentLabel(field) {
170
173
  return field.name;
@@ -397,4 +400,4 @@ async function parseCommand(command, rawArgs) {
397
400
  };
398
401
  }
399
402
  //#endregion
400
- export { parseCommand as a, isSchemaField as i, executeCommand as n, formatFieldTypeHint as r, defineCommand as t };
403
+ export { isSchemaField as a, isDefinedCommand as i, defineCommand as n, parseCommand as o, executeCommand as r, captureProcessOutput as t };
@@ -269,7 +269,8 @@ interface DefinedCommand<TArgsFields extends readonly CommandArgField[] = readon
269
269
  * a variable without a concrete type), optionality information is lost and
270
270
  * the ordering check is skipped for that field.
271
271
  */
272
- declare function defineCommand<const TArgsFields extends readonly CommandArgField[] | undefined = undefined, const TOptionsFields extends readonly CommandOptionField[] | undefined = undefined>(input: DefineCommandInput<TArgsFields, TOptionsFields> & ValidateArgOrder<TArgsFields>): DefinedCommand<NormalizeFields<TArgsFields, CommandArgField>, NormalizeFields<TOptionsFields, CommandOptionField>>; //#endregion
272
+ declare function defineCommand<const TArgsFields extends readonly CommandArgField[] | undefined = undefined, const TOptionsFields extends readonly CommandOptionField[] | undefined = undefined>(input: DefineCommandInput<TArgsFields, TOptionsFields> & ValidateArgOrder<TArgsFields>): DefinedCommand<NormalizeFields<TArgsFields, CommandArgField>, NormalizeFields<TOptionsFields, CommandOptionField>>;
273
+ //#endregion
273
274
  //#region src/execute-command.d.ts
274
275
  interface ExecuteCommandInput<TOptions, TArgs> {
275
276
  readonly options?: TOptions | undefined;
@@ -277,10 +278,5 @@ interface ExecuteCommandInput<TOptions, TArgs> {
277
278
  readonly cwd?: string | undefined;
278
279
  readonly rawArgs?: readonly string[] | undefined;
279
280
  }
280
- interface CommandExecutionResult {
281
- readonly exitCode: number;
282
- readonly stdout: string;
283
- readonly stderr: string;
284
- }
285
281
  //#endregion
286
- export { DefinedCommand as a, PrimitiveArgField as c, SchemaArgField as d, SchemaOptionField as f, CommandOptionField as i, PrimitiveFieldType as l, CommandContext as n, ExecuteCommandInput as o, defineCommand as p, CommandExecutionResult as r, InferExecutionFields as s, CommandArgField as t, PrimitiveOptionField as u };
282
+ export { ExecuteCommandInput as a, PrimitiveFieldType as c, SchemaOptionField as d, defineCommand as f, DefinedCommand as i, PrimitiveOptionField as l, CommandContext as n, InferExecutionFields as o, CommandOptionField as r, PrimitiveArgField as s, CommandArgField as t, SchemaArgField as u };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as DefinedCommand, c as PrimitiveArgField, d as SchemaArgField, f as SchemaOptionField, i as CommandOptionField, l as PrimitiveFieldType, n as CommandContext, o as ExecuteCommandInput, p as defineCommand, r as CommandExecutionResult, s as InferExecutionFields, t as CommandArgField, u as PrimitiveOptionField } from "./index-BF5_G9J2.mjs";
2
- export { type CommandArgField, type CommandContext, type CommandExecutionResult, type CommandOptionField, type DefinedCommand, type ExecuteCommandInput, type InferExecutionFields, type PrimitiveArgField, type PrimitiveFieldType, type PrimitiveOptionField, type SchemaArgField, type SchemaOptionField, defineCommand };
1
+ import { a as ExecuteCommandInput, c as PrimitiveFieldType, d as SchemaOptionField, f as defineCommand, i as DefinedCommand, l as PrimitiveOptionField, n as CommandContext, o as InferExecutionFields, r as CommandOptionField, s as PrimitiveArgField, t as CommandArgField, u as SchemaArgField } from "./index-B6XsJ6ON.mjs";
2
+ export { type CommandArgField, type CommandContext, type CommandOptionField, type DefinedCommand, type ExecuteCommandInput, type InferExecutionFields, type PrimitiveArgField, type PrimitiveFieldType, type PrimitiveOptionField, type SchemaArgField, type SchemaOptionField, defineCommand };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as defineCommand } from "./dist-Bcn0FpHi.mjs";
1
+ import { n as defineCommand } from "./dist-FWLEc-op.mjs";
2
2
  export { defineCommand };
@@ -1,110 +1,33 @@
1
- import { a as parseCommand, i as isSchemaField, n as executeCommand, r as formatFieldTypeHint } from "./dist-Bcn0FpHi.mjs";
1
+ import { a as isSchemaField, i as isDefinedCommand, o as parseCommand, r as executeCommand } from "./dist-FWLEc-op.mjs";
2
2
  import { pathToFileURL } from "node:url";
3
- //#region src/cli/result.ts
4
- function successResult(stdout) {
5
- return {
6
- exitCode: 0,
7
- stdout,
8
- stderr: ""
9
- };
10
- }
11
- function failureResult(stderr) {
12
- return {
13
- exitCode: 1,
14
- stdout: "",
15
- stderr: stderr.endsWith("\n") ? stderr : `${stderr}\n`
16
- };
17
- }
18
- //#endregion
19
- //#region src/manifest/manifest-map.ts
20
- function commandManifestPathToKey(pathSegments) {
21
- return pathSegments.join(" ");
3
+ //#region src/cli/flags.ts
4
+ function isHelpFlag(token) {
5
+ return token === "--help" || token === "-h";
22
6
  }
23
- function createCommandManifestNodeMap(manifest) {
24
- return Object.fromEntries(manifest.nodes.map((node) => [commandManifestPathToKey(node.pathSegments), node]));
7
+ function isVersionFlag(token) {
8
+ return token === "--version" || token === "-V";
25
9
  }
26
10
  //#endregion
27
- //#region src/manifest/render-help.ts
11
+ //#region src/manifest/command-loader.ts
28
12
  async function loadCommandFromModule(sourceFilePath) {
29
13
  const loadedModule = await import(pathToFileURL(sourceFilePath).href);
30
14
  if (loadedModule.default === void 0) throw new Error(`Command module did not export a default command: ${sourceFilePath}`);
15
+ if (!isDefinedCommand(loadedModule.default)) throw new Error(`Command module must export a value created with defineCommand(). Got ${describeCommandModuleExport(loadedModule.default)}.`);
31
16
  return loadedModule.default;
32
17
  }
33
- const defaultLoadCommand = (node) => loadCommandFromModule(node.sourceFilePath);
34
- function formatCommandName(cliName, pathSegments) {
35
- return pathSegments.length === 0 ? cliName : `${cliName} ${pathSegments.join(" ")}`;
36
- }
37
- function formatSectionEntries(entries) {
38
- return entries.map(({ label, description }) => ` ${label}${description ? ` ${description}` : ""}`).join("\n");
39
- }
40
- function formatArgumentLabel(field) {
41
- return `${field.name}${formatFieldTypeHint(field)}`;
42
- }
43
- function formatOptionLabel(field) {
44
- const longOptionLabel = `--${field.name}${formatFieldTypeHint(field)}`;
45
- if (!field.alias) return longOptionLabel;
46
- return `-${field.alias}, ${longOptionLabel}`;
47
- }
48
- async function isFieldRequired(field) {
49
- if (!isSchemaField(field)) return field.required === true && field.default === void 0;
50
- return !("value" in await field.schema["~standard"].validate(void 0));
51
- }
52
- async function formatUsageArguments(fields) {
53
- const usageParts = [];
54
- for (const field of fields) {
55
- const required = await isFieldRequired(field);
56
- usageParts.push(required ? `<${field.name}>` : `[${field.name}]`);
57
- }
58
- return usageParts.join(" ");
59
- }
60
- function getOptionUsageSuffix(fields) {
61
- return fields.length === 0 ? "" : "[options]";
62
- }
63
- function renderGroupHelp(manifest, node, cliName) {
64
- const nodeMap = createCommandManifestNodeMap(manifest);
65
- const entries = node.childNames.map((childName) => {
66
- return {
67
- label: childName,
68
- description: nodeMap[commandManifestPathToKey([...node.pathSegments, childName])]?.description
69
- };
70
- });
71
- const parts = [`Usage: ${formatCommandName(cliName, node.pathSegments)} <command>`];
72
- if (entries.length > 0) parts.push(`Subcommands:\n${formatSectionEntries(entries)}`);
73
- return `${parts.join("\n\n")}\n`;
74
- }
75
- async function renderCommandHelp(command, pathSegments, cliName) {
76
- const usageArguments = await formatUsageArguments(command.args);
77
- const optionUsageSuffix = getOptionUsageSuffix(command.options);
78
- const parts = [`Usage: ${[
79
- formatCommandName(cliName, pathSegments),
80
- usageArguments,
81
- optionUsageSuffix
82
- ].filter((part) => part.length > 0).join(" ")}`];
83
- if (command.description) parts.push(`Description:\n ${command.description}`);
84
- if (command.args.length > 0) parts.push(`Arguments:\n${formatSectionEntries(command.args.map((field) => ({
85
- label: formatArgumentLabel(field),
86
- description: field.description
87
- })))}`);
88
- const optionEntries = [...command.options.map((field) => ({
89
- label: formatOptionLabel(field),
90
- description: field.description
91
- })), {
92
- label: "-h, --help",
93
- description: "Show help"
94
- }];
95
- parts.push(`Options:\n${formatSectionEntries(optionEntries)}`);
96
- return `${parts.join("\n\n")}\n`;
97
- }
98
- function renderUnknownCommandMessage(route, cliName) {
99
- const parts = [`Unknown command: ${formatCommandName(cliName, route.attemptedPath)}`];
100
- if (route.suggestions.length > 0) parts.push(`Did you mean?\n${route.suggestions.map((name) => ` ${name}`).join("\n")}`);
101
- return `${parts.join("\n\n")}\n`;
102
- }
103
- async function renderResolvedHelp(options) {
104
- if (options.route.kind === "unknown") return renderUnknownCommandMessage(options.route, options.cliName);
105
- if (options.route.kind === "group") return renderGroupHelp(options.manifest, options.route.node, options.cliName);
106
- return renderCommandHelp(await (options.loadCommand ?? defaultLoadCommand)(options.route.node), options.route.matchedPath, options.cliName);
18
+ function describeCommandModuleExport(value) {
19
+ if (value === null) return "null";
20
+ if (Array.isArray(value)) return "an array";
21
+ if (typeof value === "object") return "a plain object";
22
+ if (typeof value === "string") return "a string";
23
+ if (typeof value === "number") return "a number";
24
+ if (typeof value === "boolean") return "a boolean";
25
+ if (typeof value === "bigint") return "a bigint";
26
+ if (typeof value === "symbol") return "a symbol";
27
+ if (typeof value === "function") return "a function";
28
+ return "an unsupported value";
107
29
  }
30
+ const defaultLoadCommand = (node) => loadCommandFromModule(node.sourceFilePath);
108
31
  //#endregion
109
32
  //#region src/manifest/damerau-levenshtein.ts
110
33
  function damerauLevenshteinDistance(left, right) {
@@ -121,12 +44,20 @@ function damerauLevenshteinDistance(left, right) {
121
44
  return matrix[left.length][right.length];
122
45
  }
123
46
  //#endregion
47
+ //#region src/manifest/manifest-map.ts
48
+ function commandManifestPathToKey(pathSegments) {
49
+ return pathSegments.join(" ");
50
+ }
51
+ function createCommandManifestNodeMap(manifest) {
52
+ return Object.fromEntries(manifest.nodes.map((node) => [commandManifestPathToKey(node.pathSegments), node]));
53
+ }
54
+ //#endregion
124
55
  //#region src/manifest/resolve-command-path.ts
125
56
  function isOptionLikeToken(token) {
126
57
  return token === "--" || token.startsWith("-");
127
58
  }
128
59
  function getHelpRequested(args) {
129
- return args.includes("--help") || args.includes("-h");
60
+ return args.some(isHelpFlag);
130
61
  }
131
62
  function getSuggestionThreshold(candidate) {
132
63
  return Math.max(2, Math.floor(candidate.length / 3));
@@ -180,46 +111,150 @@ function resolveCommandPath(manifest, rawArgs) {
180
111
  };
181
112
  }
182
113
  //#endregion
183
- //#region src/manifest/run-manifest-command.ts
184
- async function runManifestCommand(options) {
185
- const route = resolveCommandPath(options.manifest, options.rawArgs);
186
- if (route.kind === "unknown" || route.kind === "group" || route.helpRequested) {
187
- const output = await renderResolvedHelp({
188
- manifest: options.manifest,
189
- route,
190
- cliName: options.cliName,
191
- loadCommand: options.loadCommand
192
- });
193
- return route.kind === "unknown" ? failureResult(output) : successResult(output);
114
+ //#region src/manifest/render-help.ts
115
+ function formatCommandName(cliName, pathSegments) {
116
+ return pathSegments.length === 0 ? cliName : `${cliName} ${pathSegments.join(" ")}`;
117
+ }
118
+ function formatSectionEntries(entries) {
119
+ return entries.map(({ label, description }) => ` ${label}${description ? ` ${description}` : ""}`).join("\n");
120
+ }
121
+ function formatTypeHint(field) {
122
+ return isSchemaField(field) ? "" : ` <${field.type}>`;
123
+ }
124
+ function formatArgumentLabel(field) {
125
+ return `${field.name}${formatTypeHint(field)}`;
126
+ }
127
+ function formatOptionLabel(field) {
128
+ const longOptionLabel = `--${field.name}${formatTypeHint(field)}`;
129
+ if (!field.alias) return longOptionLabel;
130
+ return `-${field.alias}, ${longOptionLabel}`;
131
+ }
132
+ async function isFieldRequired(field) {
133
+ if (!isSchemaField(field)) return field.required === true && field.default === void 0;
134
+ return !("value" in await field.schema["~standard"].validate(void 0));
135
+ }
136
+ async function formatUsageArguments(fields) {
137
+ const usageParts = [];
138
+ for (const field of fields) {
139
+ const required = await isFieldRequired(field);
140
+ usageParts.push(required ? `<${field.name}>` : `[${field.name}]`);
194
141
  }
195
- const command = await (options.loadCommand ?? defaultLoadCommand)(route.node);
196
- const parsed = await parseCommand(command, route.remainingArgs);
197
- if (!parsed.ok) return failureResult(parsed.error.message);
198
- return executeCommand(command, {
199
- options: parsed.value.options,
200
- args: parsed.value.args,
201
- cwd: options.cwd,
202
- rawArgs: parsed.value.rawArgs
142
+ return usageParts.join(" ");
143
+ }
144
+ function getOptionUsageSuffix(fields) {
145
+ return fields.length === 0 ? "" : "[options]";
146
+ }
147
+ function renderGroupHelp(options) {
148
+ const { manifest, node, cliName, version } = options;
149
+ const nodeMap = createCommandManifestNodeMap(manifest);
150
+ const entries = node.childNames.map((childName) => {
151
+ return {
152
+ label: childName,
153
+ description: nodeMap[commandManifestPathToKey([...node.pathSegments, childName])]?.description
154
+ };
203
155
  });
156
+ const parts = [`Usage: ${formatCommandName(cliName, node.pathSegments)} <command>`];
157
+ if (entries.length > 0) parts.push(`Subcommands:\n${formatSectionEntries(entries)}`);
158
+ const optionEntries = [{
159
+ label: "-h, --help",
160
+ description: "Show help"
161
+ }, ...node.pathSegments.length === 0 && version ? [{
162
+ label: "-V, --version",
163
+ description: "Show the version number"
164
+ }] : []];
165
+ parts.push(`Options:\n${formatSectionEntries(optionEntries)}`);
166
+ return `${parts.join("\n\n")}\n`;
167
+ }
168
+ async function renderCommandHelp(command, pathSegments, cliName) {
169
+ const usageArguments = await formatUsageArguments(command.args);
170
+ const optionUsageSuffix = getOptionUsageSuffix(command.options);
171
+ const parts = [`Usage: ${[
172
+ formatCommandName(cliName, pathSegments),
173
+ usageArguments,
174
+ optionUsageSuffix
175
+ ].filter((part) => part.length > 0).join(" ")}`];
176
+ if (command.description) parts.push(`Description:\n ${command.description}`);
177
+ if (command.args.length > 0) parts.push(`Arguments:\n${formatSectionEntries(command.args.map((field) => ({
178
+ label: formatArgumentLabel(field),
179
+ description: field.description
180
+ })))}`);
181
+ const optionEntries = [...command.options.map((field) => ({
182
+ label: formatOptionLabel(field),
183
+ description: field.description
184
+ })), {
185
+ label: "-h, --help",
186
+ description: "Show help"
187
+ }];
188
+ parts.push(`Options:\n${formatSectionEntries(optionEntries)}`);
189
+ return `${parts.join("\n\n")}\n`;
190
+ }
191
+ function renderUnknownCommandMessage(route, cliName) {
192
+ const parts = [`Unknown command: ${formatCommandName(cliName, route.attemptedPath)}`];
193
+ if (route.suggestions.length > 0) parts.push(`Did you mean?\n${route.suggestions.map((name) => ` ${name}`).join("\n")}`);
194
+ return `${parts.join("\n\n")}\n`;
204
195
  }
205
196
  //#endregion
206
- //#region src/cli/write-result.ts
207
- async function writeStream(stream, contents) {
208
- if (contents.length === 0) return;
209
- await new Promise((resolve, reject) => {
210
- stream.write(contents, (error) => {
211
- if (error) {
212
- reject(error);
213
- return;
214
- }
215
- resolve();
216
- });
197
+ //#region src/manifest/resolve-help.ts
198
+ async function renderResolvedHelp(options) {
199
+ if (options.route.kind === "unknown") return renderUnknownCommandMessage(options.route, options.cliName);
200
+ if (options.route.kind === "group") return renderGroupHelp({
201
+ manifest: options.manifest,
202
+ node: options.route.node,
203
+ cliName: options.cliName,
204
+ version: options.version
217
205
  });
206
+ return renderCommandHelp(await (options.loadCommand ?? defaultLoadCommand)(options.route.node), options.route.matchedPath, options.cliName);
218
207
  }
219
- async function writeCommandExecutionResult(result) {
220
- await writeStream(process.stdout, result.stdout);
221
- await writeStream(process.stderr, result.stderr);
222
- process.exitCode = result.exitCode;
208
+ //#endregion
209
+ //#region src/manifest/run-manifest-command.ts
210
+ function ensureTrailingNewline(text) {
211
+ return text.endsWith("\n") ? text : `${text}\n`;
212
+ }
213
+ function formatRuntimeError(error) {
214
+ if (error instanceof Error) return error.message || error.name || "Failed to run command";
215
+ if (typeof error === "string" && error.length > 0) return error;
216
+ return "Failed to run command";
217
+ }
218
+ async function runManifestCommand(options) {
219
+ try {
220
+ if (options.version && options.rawArgs.length === 1 && isVersionFlag(options.rawArgs[0])) {
221
+ process.stdout.write(`${options.cliName} v${options.version}\n`);
222
+ return 0;
223
+ }
224
+ const route = resolveCommandPath(options.manifest, options.rawArgs);
225
+ if (route.kind === "unknown" || route.kind === "group" || route.helpRequested) {
226
+ const output = await renderResolvedHelp({
227
+ manifest: options.manifest,
228
+ route,
229
+ cliName: options.cliName,
230
+ version: options.version,
231
+ loadCommand: options.loadCommand
232
+ });
233
+ if (route.kind === "unknown") {
234
+ process.stderr.write(ensureTrailingNewline(output));
235
+ return 1;
236
+ }
237
+ process.stdout.write(output);
238
+ return 0;
239
+ }
240
+ const command = await (options.loadCommand ?? defaultLoadCommand)(route.node);
241
+ const parsed = await parseCommand(command, route.remainingArgs);
242
+ if (!parsed.ok) {
243
+ process.stderr.write(ensureTrailingNewline(parsed.error.message));
244
+ return 1;
245
+ }
246
+ const result = await executeCommand(command, {
247
+ options: parsed.value.options,
248
+ args: parsed.value.args,
249
+ cwd: options.cwd,
250
+ rawArgs: parsed.value.rawArgs
251
+ });
252
+ if (result.errorMessage) process.stderr.write(ensureTrailingNewline(result.errorMessage));
253
+ return result.exitCode;
254
+ } catch (error) {
255
+ process.stderr.write(ensureTrailingNewline(formatRuntimeError(error)));
256
+ return 1;
257
+ }
223
258
  }
224
259
  //#endregion
225
- export { successResult as i, runManifestCommand as n, failureResult as r, writeCommandExecutionResult as t };
260
+ export { isHelpFlag as n, isVersionFlag as r, runManifestCommand as t };
@@ -1,4 +1,4 @@
1
- import { a as DefinedCommand, i as CommandOptionField, r as CommandExecutionResult, t as CommandArgField } from "./index-BF5_G9J2.mjs";
1
+ import { i as DefinedCommand, r as CommandOptionField, t as CommandArgField } from "./index-B6XsJ6ON.mjs";
2
2
 
3
3
  //#region src/manifest/manifest-types.d.ts
4
4
  type CommandManifestPath = readonly string[];
@@ -22,7 +22,7 @@ interface CommandManifest {
22
22
  readonly nodes: readonly CommandManifestNode[];
23
23
  }
24
24
  //#endregion
25
- //#region src/manifest/render-help.d.ts
25
+ //#region src/manifest/command-loader.d.ts
26
26
  type LoadCommandFn = (node: CommandManifestCommandNode) => Promise<DefinedCommand<readonly CommandArgField[], readonly CommandOptionField[]>>;
27
27
  //#endregion
28
28
  //#region src/manifest/run-manifest-command.d.ts
@@ -30,12 +30,10 @@ interface RunManifestCommandOptions {
30
30
  readonly manifest: CommandManifest;
31
31
  readonly rawArgs: readonly string[];
32
32
  readonly cliName: string;
33
+ readonly version?: string | undefined;
33
34
  readonly cwd?: string | undefined;
34
35
  readonly loadCommand?: LoadCommandFn | undefined;
35
36
  }
36
- declare function runManifestCommand(options: RunManifestCommandOptions): Promise<CommandExecutionResult>;
37
+ declare function runManifestCommand(options: RunManifestCommandOptions): Promise<number>;
37
38
  //#endregion
38
- //#region src/cli/write-result.d.ts
39
- declare function writeCommandExecutionResult(result: CommandExecutionResult): Promise<void>;
40
- //#endregion
41
- export { runManifestCommand, writeCommandExecutionResult };
39
+ export { runManifestCommand };
package/dist/runtime.mjs CHANGED
@@ -1,2 +1,3 @@
1
- import { n as runManifestCommand, t as writeCommandExecutionResult } from "./write-result-qylmqXvG.mjs";
2
- export { runManifestCommand, writeCommandExecutionResult };
1
+ import "./dist-FWLEc-op.mjs";
2
+ import { t as runManifestCommand } from "./run-manifest-command-BmeVwPA5.mjs";
3
+ export { runManifestCommand };
package/dist/test.d.mts CHANGED
@@ -1,7 +1,50 @@
1
- import { a as DefinedCommand, i as CommandOptionField, o as ExecuteCommandInput, r as CommandExecutionResult, s as InferExecutionFields, t as CommandArgField } from "./index-BF5_G9J2.mjs";
1
+ import { a as ExecuteCommandInput, i as DefinedCommand, o as InferExecutionFields, r as CommandOptionField, t as CommandArgField } from "./index-B6XsJ6ON.mjs";
2
2
 
3
3
  //#region src/test.d.ts
4
4
  type RunCommandOptions<TOptions, TArgs> = ExecuteCommandInput<TOptions, TArgs>;
5
+ interface CommandExecutionResult {
6
+ readonly exitCode: number;
7
+ readonly stdout: string;
8
+ readonly stderr: string;
9
+ readonly errorMessage?: string | undefined;
10
+ }
11
+ /**
12
+ * Runs a command definition directly in-process for testing.
13
+ *
14
+ * This helper bypasses Rune's CLI parser and validation layers. Callers
15
+ * provide already-normalized `options` and `args` values, and the command's
16
+ * `run` function is executed with those values injected into the context.
17
+ *
18
+ * All output written to `process.stdout`, `process.stderr`, and `console` is
19
+ * captured and returned as strings so tests can assert on them.
20
+ *
21
+ * @param command - A command created with {@link defineCommand}.
22
+ * @param options - Pre-validated options, args, cwd, and rawArgs to inject.
23
+ * @returns The exit code, captured stdout/stderr, and an optional error message.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { defineCommand } from "rune";
28
+ * import { runCommand } from "rune/test";
29
+ * import { expect, test } from "vitest";
30
+ *
31
+ * const hello = defineCommand({
32
+ * options: [{ name: "name", type: "string", required: true }],
33
+ * run(ctx) {
34
+ * console.log(`Hello, ${ctx.options.name}!`);
35
+ * },
36
+ * });
37
+ *
38
+ * test("hello command", async () => {
39
+ * const result = await runCommand(hello, {
40
+ * options: { name: "Rune" },
41
+ * });
42
+ *
43
+ * expect(result.exitCode).toBe(0);
44
+ * expect(result.stdout).toBe("Hello, Rune!\n");
45
+ * });
46
+ * ```
47
+ */
5
48
  declare function runCommand<TArgsFields extends readonly CommandArgField[], TOptionsFields extends readonly CommandOptionField[]>(command: DefinedCommand<TArgsFields, TOptionsFields>, options?: RunCommandOptions<InferExecutionFields<TOptionsFields>, InferExecutionFields<TArgsFields>>): Promise<CommandExecutionResult>;
6
49
  //#endregion
7
- export { RunCommandOptions, runCommand };
50
+ export { CommandExecutionResult, RunCommandOptions, runCommand };
package/dist/test.mjs CHANGED
@@ -1,7 +1,51 @@
1
- import { n as executeCommand } from "./dist-Bcn0FpHi.mjs";
1
+ import { r as executeCommand, t as captureProcessOutput } from "./dist-FWLEc-op.mjs";
2
2
  //#region src/test.ts
3
+ /**
4
+ * Runs a command definition directly in-process for testing.
5
+ *
6
+ * This helper bypasses Rune's CLI parser and validation layers. Callers
7
+ * provide already-normalized `options` and `args` values, and the command's
8
+ * `run` function is executed with those values injected into the context.
9
+ *
10
+ * All output written to `process.stdout`, `process.stderr`, and `console` is
11
+ * captured and returned as strings so tests can assert on them.
12
+ *
13
+ * @param command - A command created with {@link defineCommand}.
14
+ * @param options - Pre-validated options, args, cwd, and rawArgs to inject.
15
+ * @returns The exit code, captured stdout/stderr, and an optional error message.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { defineCommand } from "rune";
20
+ * import { runCommand } from "rune/test";
21
+ * import { expect, test } from "vitest";
22
+ *
23
+ * const hello = defineCommand({
24
+ * options: [{ name: "name", type: "string", required: true }],
25
+ * run(ctx) {
26
+ * console.log(`Hello, ${ctx.options.name}!`);
27
+ * },
28
+ * });
29
+ *
30
+ * test("hello command", async () => {
31
+ * const result = await runCommand(hello, {
32
+ * options: { name: "Rune" },
33
+ * });
34
+ *
35
+ * expect(result.exitCode).toBe(0);
36
+ * expect(result.stdout).toBe("Hello, Rune!\n");
37
+ * });
38
+ * ```
39
+ */
3
40
  async function runCommand(command, options = {}) {
4
- return executeCommand(command, options);
41
+ const captured = await captureProcessOutput(() => executeCommand(command, options));
42
+ if (!captured.ok) throw captured.error;
43
+ return {
44
+ exitCode: captured.value.exitCode,
45
+ stdout: captured.stdout,
46
+ stderr: captured.stderr,
47
+ errorMessage: captured.value.errorMessage
48
+ };
5
49
  }
6
50
  //#endregion
7
51
  export { runCommand };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rune-cli/rune",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Rune is a CLI framework built around the concept of file-based command routing.",
5
5
  "homepage": "https://github.com/morinokami/rune#readme",
6
6
  "bugs": {
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "24.12.0",
46
- "@typescript/native-preview": "7.0.0-dev.20260317.1",
46
+ "@typescript/native-preview": "7.0.0-dev.20260322.1",
47
47
  "typescript": "5.9.3",
48
48
  "vite-plus": "v0.1.13",
49
49
  "@rune-cli/core": "0.0.0"