optionkit 0.0.3 → 0.0.4

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 (2) hide show
  1. package/README.md +79 -100
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,14 +1,21 @@
1
1
  # optionkit
2
2
 
3
- `optionkit` turns a Zod configuration schema into CLI options. It adds
4
- environment-variable bindings, adds unambiguous short flags, and lazily loads
5
- the CLI stack without making Fireargs, Commander, or Zod runtime dependencies
6
- of this package.
3
+ Optionkit turns a Zod configuration schema into CLI options with useful defaults:
4
+ project-scoped environment variables, conflict-free short flags, structured
5
+ environment parsing, and lazy CLI dependency loading.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ bun add optionkit
11
+ ```
12
+
13
+ ## Start with the defaults
7
14
 
8
15
  ```ts
9
16
  import { createOptionKit } from 'optionkit';
10
17
 
11
- const { f, z } = await createOptionKit({ moduleUrl: import.meta.url });
18
+ const { f, z } = await createOptionKit();
12
19
 
13
20
  const appOptionsSchema = z.object({
14
21
  port: z.number().default(3000).describe('Port to listen on.'),
@@ -27,122 +34,94 @@ const serve = f.command({ name: 'serve', description: 'Start the server.' })
27
34
  await serve.parseAsync(process.argv);
28
35
  ```
29
36
 
30
- If the consuming package is named `my-app`, the fields above receive
31
- `MY_APP_PORT`, `MY_APP_DEBUG`, and `MY_APP_LOG_LEVEL`. `port`, `debug`, and
32
- `logLevel` also receive `-p`, `-d`, and `-l` because their initials do not
33
- conflict. When two options share an initial, neither gets that automatic short
34
- flag.
35
-
36
- Adaptation happens at each command's `.input(...)` boundary. Subcommands need no
37
- special handling: build each leaf from its own schema, then mount the leaves with
38
- `f.program(...).router({ serve, status })`. Environment and short-flag conflicts
39
- are calculated independently for each leaf.
40
-
41
- ## Lazy CLI dependencies
42
-
43
- optionkit first resolves Fireargs and Zod from the consuming CLI package. If
44
- they are unavailable, it detects the package manager from the invocation
45
- directory and installs the pinned Fireargs and Zod ranges from the consuming
46
- package root. `cacheDirectory` opts into an isolated install explicitly; there
47
- is no default versioned cache. Fireargs' package metadata supplies its remaining
48
- peer dependencies. Optionkit restores the consuming manifest and lockfile
49
- byte-for-byte after installation, and installer output stays silent so JSON
50
- output is not polluted.
51
-
52
- Only `@antfu/install-pkg` is a runtime dependency of optionkit. Fireargs,
53
- Zod, and `clean-stack` are development dependencies whose ranges are generated
54
- into the build from the catalog in `pnpm-workspace.yaml`. A matching root
55
- catalog keeps Bun compatible, and verification fails if the two catalogs
56
- diverge. `src/_generated/catalog.ts` is a one-to-one runtime mirror of the root
57
- catalog. The stack cleaner is installed separately and best-effort: a failure
58
- never prevents the CLI from starting, and `formatError` falls back to the
59
- original stack. Fireargs and Zod are also declared as optional peers, so a
60
- consumer's compatible installation is reused without making either package a
61
- required install.
62
-
63
- Use the returned `z` when the consuming package does not otherwise depend on
64
- Zod. It always comes from the same dependency location as the CLI adapter:
37
+ For a package named `my-app`, this creates `MY_APP_PORT`, `MY_APP_DEBUG`, and
38
+ `MY_APP_LOG_LEVEL`. It also assigns `-p`, `-d`, and `-l`; when option initials
39
+ conflict, Optionkit leaves the ambiguous short flags out.
40
+
41
+ `createOptionKit()` finds the consuming package from the executable at
42
+ `process.argv[1]`, falling back to `process.cwd()`, and walks upward to the nearest
43
+ `package.json`. Pass `moduleUrl` or `packageRoot` only when a host needs to make
44
+ that boundary explicit.
45
+
46
+ Each `.input(schema)` is adapted independently, so subcommands need no special
47
+ setup:
65
48
 
66
49
  ```ts
67
- const { f, z } = await createOptionKit({ moduleUrl: import.meta.url });
68
- const input = z.object({ verbose: z.boolean().default(false) });
50
+ const program = f.program({ name: 'app' }).router({ serve, status });
51
+ await program.parseAsync(process.argv);
69
52
  ```
70
53
 
71
- Use `environmentPrefix` or `projectName` to override automatic package-name
72
- translation. Per-field `env` and `short` values can be overridden or disabled:
54
+ ### Environment values
73
55
 
74
- ```ts
75
- await createOptionKit({
76
- environmentPrefix: 'SERVICE',
77
- fields: {
78
- debug: { env: false, short: 'x' },
79
- },
80
- moduleUrl: import.meta.url,
81
- });
56
+ Scalar values keep Fireargs' normal coercion. Array, tuple, object, and record
57
+ environment values try JSON first:
58
+
59
+ ```text
60
+ MY_APP_TAGS='["one","two"]'
61
+ MY_APP_ENV='{"PORT":"3000"}'
82
62
  ```
83
63
 
84
- Array, tuple, object, and record options with generated environment bindings try
85
- JSON first because an environment variable supplies only one string. For example,
86
- `APP_TAGS='["one","two"]'` and `APP_ENV='{"PORT":"3000"}'` become their
87
- corresponding Zod input values. Arrays also accept comma- or whitespace-delimited
88
- shell words, including quotes and backslash escapes. If decoding does not apply,
89
- the original string reaches Zod as the final authority. Decoded array values are
90
- fed through Fireargs' native variadic option handling, so repeated flags and
91
- scalar coercion keep their normal behavior. Object and record flags accept their
92
- structured value as one token. A consumer-supplied Fireargs `parse` function is
93
- forwarded unchanged and takes precedence over this augmentation. Every other
94
- per-option Fireargs configuration field is forwarded as well; command and program
95
- configuration remains available through the returned `f` namespace.
96
-
97
- For wrapper commands, `passThroughArguments: true` supplies Fireargs' pass-through
98
- settings and adds a typed trailing `args: string[]` input in one place:
64
+ Arrays also accept comma- or whitespace-delimited shell words with quotes and
65
+ backslash escapes. Decoded arrays still use native variadic option handling, so
66
+ repeated CLI flags work normally. If decoding does not apply, Zod receives the
67
+ original value and remains the final validator.
68
+
69
+ ### Wrapper commands
70
+
71
+ `passThroughArguments` adds the pass-through command settings and a typed trailing
72
+ `args: string[]` input:
99
73
 
100
74
  ```ts
101
75
  const run = f.command({ name: 'run', passThroughArguments: true })
102
- .input(z.object(options))
76
+ .input(appOptionsSchema)
103
77
  .output(z.void())
104
78
  .handler(async ({ args, ...config }) => runExecutable(config, args));
105
79
  ```
106
80
 
107
- Pass `{ description: '...' }` instead of `true` to override the generated
108
- positional help text. Explicit Fireargs command settings still override the
109
- shortcut defaults.
110
-
111
- Fireargs programs currently show help when invoked without a subcommand. Optionkit
112
- does not override that dispatch: Commander only exposes a clean default-command
113
- switch while adding a command, whereas Fireargs owns that mounting step. A shim
114
- would either depend on Commander internals or duplicate Fireargs routing.
115
-
116
- `resolvePackage` and `importPackageIfAvailable` expose the same resolve-first
117
- behavior for other optional integrations. They only suppress a genuinely
118
- missing package; initialization errors from an installed package still surface.
119
-
120
- ## Pyfig migration
81
+ Use `{ description: '...' }` instead of `true` to change the generated positional
82
+ help text.
121
83
 
122
- Pyfig can keep `pyfigOptionsSchema` as its API source of truth. Its CLI only
123
- needs to replace the manual loader; Optionkit adapts every command input:
84
+ ## Override only what differs
124
85
 
125
86
  ```ts
126
- const { f, z } = await createOptionKit({
127
- log,
87
+ const kit = await createOptionKit({
88
+ environmentPrefix: 'SERVICE',
89
+ fields: {
90
+ debug: { env: false, short: 'x' },
91
+ },
128
92
  moduleUrl: import.meta.url,
129
- });
130
-
131
- const createInput = (passthrough = false) => pyfigOptionsSchema.unwrap().extend({
132
- ...(passthrough ? { args: f.argument().schema(passthroughArguments) } : {}),
93
+ reservedShortFlags: ['v'],
133
94
  });
134
95
  ```
135
96
 
136
- The command, router, output schemas, handlers, and passthrough behavior do not
137
- change. `PYFIG_*` names and conflict-free short flags are derived automatically.
138
- Pyfig needs only `optionkit` for its CLI integration; it does not need its own
139
- Fireargs peer metadata, Fireargs development dependency, or install helper.
140
-
141
- ## Install
142
-
143
- ```sh
144
- bun add optionkit
145
- ```
97
+ Available Optionkit-level controls include `projectName`, `environmentPrefix`,
98
+ `shortFlags`, `reservedShortFlags`, per-field metadata, `invocationDirectory`, and
99
+ an explicit `cacheDirectory`. A parser registered through `f.option({ parse })`
100
+ or the `fields` setting is forwarded unchanged and bypasses automatic structured
101
+ decoding.
102
+
103
+ The returned `f` keeps the underlying command, program, option, and argument
104
+ configuration available. Refer to the [Fireargs package](https://www.npmjs.com/package/fireargs)
105
+ for those builder options instead of learning a second configuration vocabulary.
106
+
107
+ ## Dependency loading and errors
108
+
109
+ Optionkit first reuses compatible Fireargs and Zod installations from the
110
+ consuming package. If either is missing, it detects the caller's package manager
111
+ and installs the catalog ranges from the consuming package root without changing
112
+ the manifest or lockfile. `cacheDirectory` explicitly opts into an isolated
113
+ installation; there is no default version cache.
114
+
115
+ `@antfu/install-pkg` is Optionkit's only hard runtime dependency. Fireargs, Zod,
116
+ and `clean-stack` are optional peers and development dependencies. Their ranges
117
+ live in the package catalog and are generated into `src/_generated/catalog.ts` as
118
+ a one-to-one runtime mirror. `clean-stack` is best-effort: failure never blocks a
119
+ CLI, and the returned `formatError(error)` falls back to the original stack.
120
+
121
+ Use the returned `z` when the consumer does not otherwise depend on Zod; it is
122
+ resolved from the same location as the CLI adapter. `resolvePackage` and
123
+ `importPackageIfAvailable` expose the same resolve-first behavior for other
124
+ optional integrations and only suppress a genuinely missing package.
146
125
 
147
126
  ## License
148
127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "optionkit",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Turn Zod configuration schemas into zero-setup CLI options.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",