@crustjs/extensions 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chenxin Yan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # @crustjs/extensions
2
+
3
+ Official Extensions for the Crust CLI framework
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ bun add @crustjs/extensions
9
+ ```
10
+
11
+ ## Documentation
12
+
13
+ Full docs: [crustjs.com/docs/modules/extensions](https://crustjs.com/docs/modules/extensions)
@@ -0,0 +1,287 @@
1
+ import { CommandDefinition, CommandSnapshot, Extension, ExtensionContext, ExtensionFactory, ExtensionId, RootMetaKey } from "@crustjs/core";
2
+ //#region src/completion/index.d.ts
3
+ /** The set of shells supported by the v1 completion extension. */
4
+ type CompletionShell = "bash" | "zsh" | "fish";
5
+ /** Options for the completion Extension. */
6
+ interface CompletionOptions {
7
+ /**
8
+ * Subcommand name. Override only if the default conflicts with an existing user-defined command.
9
+ *
10
+ * @default "completion"
11
+ */
12
+ command?: string;
13
+ /**
14
+ * Binary name embedded in generated scripts (the `complete -F` target,
15
+ * the `#compdef` line, the `complete -c <bin>` rules).
16
+ * Applies to the runtime command and build hook; set it when `crust build --name`
17
+ * or the npm bin key installs the CLI under a different name.
18
+ *
19
+ * @default The root command's `meta.name`
20
+ */
21
+ binName?: string;
22
+ /**
23
+ * Free-form version string embedded in generated script headers. The
24
+ * walker does not parse it.
25
+ *
26
+ * @default The root command's `meta.version`
27
+ */
28
+ version?: string;
29
+ }
30
+ /** Render inputs shared by the pure shell renderers. */
31
+ type CompletionRenderOptions = Pick<CompletionOptions, "binName" | "version">;
32
+ /** Render a bash completion script from a prepared root Command Snapshot. */
33
+ export declare function renderBashCompletion(root: CommandSnapshot, options?: CompletionRenderOptions): string;
34
+ /** Render a zsh completion script from a prepared root Command Snapshot. */
35
+ export declare function renderZshCompletion(root: CommandSnapshot, options?: CompletionRenderOptions): string;
36
+ /** Render a fish completion script from a prepared root Command Snapshot. */
37
+ export declare function renderFishCompletion(root: CommandSnapshot, options?: CompletionRenderOptions): string;
38
+ /**
39
+ * Build an Extension that contributes a `completion <shell>` command
40
+ * which emits a tab-completion script for bash, zsh, or fish.
41
+ *
42
+ * **Strategy: pure-static.** The action walks the final root snapshot, so
43
+ * registration order is irrelevant — any commands or recursive flags added
44
+ * by other Extensions are visible by the time we generate the script. The
45
+ * walker projects Core's documentation model to a small completion model; per-shell
46
+ * renderers turn that into a self-contained shell script with no runtime
47
+ * callbacks.
48
+ *
49
+ * **Print vs `--output-dir`.**
50
+ * - With no `--output-dir`: print the script for the requested `<shell>`
51
+ * to stdout (the install pattern is
52
+ * `mycli completion bash > ~/.local/share/...`).
53
+ * - With `--output-dir <path>`: write **all** supported shells' files
54
+ * into the directory using the canonical per-shell filename
55
+ * (`<bin>` for bash, `_<bin>` for zsh, `<bin>.fish` for fish). This
56
+ * is the artifact-generation path used by Homebrew, Nix, and similar
57
+ * distribution channels — distributors run it once at packaging time
58
+ * and the resulting files become drop-ins.
59
+ *
60
+ * **Build hook.** `crust build` writes the same three files under
61
+ * `<outDir>/completions/`; `--package` stages that directory. The binary name
62
+ * defaults to the snapshot's `meta.name`, unless `options.binName` is set.
63
+ */
64
+ export declare const completion: ExtensionFactory<[options?: CompletionOptions], {}, [], [], readonly CommandDefinition<any, any, any, any>[]>;
65
+ //#endregion
66
+ //#region src/did-you-mean.d.ts
67
+ interface DidYouMeanOptions {
68
+ /**
69
+ * Presentation mode for command-not-found errors.
70
+ *
71
+ * `"error"` writes the message and visible commands to stderr. `"help"`
72
+ * writes the message and parent command help to stdout.
73
+ *
74
+ * @default "error"
75
+ */
76
+ mode?: "error" | "help";
77
+ }
78
+ export declare const didYouMean: ExtensionFactory<[options?: DidYouMeanOptions]>;
79
+ //#endregion
80
+ //#region src/help.d.ts
81
+ export declare function renderHelp(command: CommandSnapshot, path?: readonly string[]): string;
82
+ declare const helpFlags: readonly [{
83
+ readonly name: "help";
84
+ readonly type: "boolean";
85
+ readonly short: "h";
86
+ readonly noNegate: true;
87
+ readonly description: "Show help";
88
+ }];
89
+ export declare const help: ExtensionFactory<[], {}, [], typeof helpFlags>;
90
+ //#endregion
91
+ //#region src/no-color.d.ts
92
+ declare const colorFlags: readonly [{
93
+ readonly name: "color";
94
+ readonly type: "boolean";
95
+ readonly description: "Enable colored output";
96
+ }];
97
+ /**
98
+ * Adds a recursive `--color` / `--no-color` flag pair that scopes the
99
+ * standard color environment variables around command execution:
100
+ *
101
+ * - `--color` sets `FORCE_COLOR=3` (and clears `NO_COLOR`, so strict
102
+ * no-color.org-only child processes also comply) — forces all ANSI on
103
+ * (truecolor), overriding non-TTY detection. Any color library that
104
+ * honors `FORCE_COLOR` (including `@crustjs/style` and chalk) obeys it,
105
+ * and child processes inherit it.
106
+ * - `--no-color` sets `NO_COLOR=1` (and clears `FORCE_COLOR` so the flag
107
+ * wins over ambient env) — suppresses colors while non-color modifiers
108
+ * and hyperlinks keep following TTY detection, per
109
+ * [no-color.org](https://no-color.org/).
110
+ *
111
+ * Previous values are restored after the command finishes. When overlapping
112
+ * programmatic runs in one process use opposite flags, the later run wins
113
+ * mid-flight (the env is process-global); the ambient values are restored
114
+ * once all runs finish.
115
+ */
116
+ export declare const noColor: ExtensionFactory<[], {}, [], typeof colorFlags>;
117
+ //#endregion
118
+ //#region src/update-notifier.d.ts
119
+ type UpdateNotifierPackageManager = "npm" | "pnpm" | "yarn" | "bun";
120
+ type UpdateCommandResolver = (info: {
121
+ packageName: string;
122
+ packageManager: UpdateNotifierPackageManager;
123
+ }) => string;
124
+ interface UpdateNotifierState {
125
+ lastCheckedAt: number;
126
+ latestVersion?: string;
127
+ lastNotifiedVersion?: string;
128
+ }
129
+ interface UpdateNotifierCacheAdapter {
130
+ read(): Promise<UpdateNotifierState | null | undefined>;
131
+ write(state: UpdateNotifierState): Promise<void>;
132
+ }
133
+ /**
134
+ * Cache configuration for the update notifier extension.
135
+ *
136
+ * Wraps a {@link UpdateNotifierCacheAdapter} with cache-specific settings.
137
+ */
138
+ interface UpdateNotifierCacheConfig {
139
+ /**
140
+ * Persistence adapter for reading and writing notifier state.
141
+ *
142
+ * When omitted, the built-in `@crustjs/store` persistence is used, so
143
+ * `intervalMs` can be tuned without reimplementing storage.
144
+ */
145
+ adapter?: UpdateNotifierCacheAdapter;
146
+ /**
147
+ * Minimum interval in milliseconds between network update checks.
148
+ *
149
+ * Cached results are reused until this interval elapses.
150
+ *
151
+ * @default 86_400_000 (24 hours)
152
+ */
153
+ intervalMs?: number;
154
+ }
155
+ /**
156
+ * Configuration options for the update notifier extension.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * import { updateNotifier } from "@crustjs/extensions";
161
+ *
162
+ * updateNotifier({ packageName: "my-cli" });
163
+ * ```
164
+ */
165
+ interface UpdateNotifierOptions {
166
+ /**
167
+ * Override the current version of the CLI package.
168
+ *
169
+ * @default The root command's `meta.version`
170
+ */
171
+ currentVersion?: string;
172
+ /**
173
+ * The npm package name to check for updates.
174
+ */
175
+ packageName: string;
176
+ /**
177
+ * Network request timeout in milliseconds for the registry check.
178
+ *
179
+ * If the check does not complete within this duration, it is silently
180
+ * aborted and treated as a soft failure. This timeout does not bound cache
181
+ * operations or the entire postRun hook.
182
+ *
183
+ * @default 5_000 (5 seconds)
184
+ */
185
+ timeoutMs?: number;
186
+ /**
187
+ * Custom npm registry URL to query for the latest version.
188
+ *
189
+ * @default "https://registry.npmjs.org"
190
+ */
191
+ registryUrl?: string;
192
+ /**
193
+ * Upgrade command shown in the notice.
194
+ *
195
+ * Pass a string for a fixed command, a callback to build one from the
196
+ * package name and detected package manager, or a scope to generate the
197
+ * package manager's standard local/global command. When omitted, the notice
198
+ * does not suggest a command.
199
+ */
200
+ updateCommand?: string | UpdateCommandResolver | {
201
+ scope: "global" | "local";
202
+ };
203
+ /**
204
+ * Documentation URL shown after the update notice.
205
+ */
206
+ updateDocsUrl?: string;
207
+ /**
208
+ * Cache configuration for cross-run persistence.
209
+ *
210
+ * By default, notifier state is persisted in the platform-standard state
211
+ * directory for {@link packageName}. Set to `false` to disable persistence,
212
+ * provide `intervalMs` alone to tune the built-in cache interval, or
213
+ * provide a custom adapter to control storage.
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * cache: false
218
+ * ```
219
+ */
220
+ cache?: false | UpdateNotifierCacheConfig;
221
+ }
222
+ /**
223
+ * Creates an update notifier extension that checks the npm registry after a
224
+ * successful command action and displays a notice when a newer version is available.
225
+ *
226
+ * **Behavior:**
227
+ * - By default, checks are cached for 24 hours in the package's state directory.
228
+ * - `cache: false` disables cross-run persistence.
229
+ * - A custom cache adapter can override the built-in persistence.
230
+ * - The notice is command-less unless `updateCommand` is configured.
231
+ * - The postRun hook awaits the network check and cache reads and writes,
232
+ * delaying invocation completion after the action.
233
+ * - `timeoutMs` bounds only the network request, defaulting to 5 seconds.
234
+ * - Network, cache, and parsing errors are silently swallowed. A missing
235
+ * current version throws a DEFINITION error before that recovery block.
236
+ * - Update notices are intentionally written to the invocation's stderr callback.
237
+ * - Duplicate notifications for the same version are suppressed.
238
+ *
239
+ * @param options - Extension configuration. `packageName` is required.
240
+ * @returns An Extension registered with `.extend()`.
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * import { Crust } from "@crustjs/core";
245
+ * import { updateNotifier } from "@crustjs/extensions";
246
+ *
247
+ * const app = new Crust("my-cli", { description: "My awesome CLI", version: "1.2.3" })
248
+ * .extend(updateNotifier({ packageName: "my-cli" }))
249
+ * .action(() => {
250
+ * console.log("Hello!");
251
+ * });
252
+ *
253
+ * await app.execute();
254
+ * ```
255
+ */
256
+ export declare const updateNotifier: ExtensionFactory<[options: UpdateNotifierOptions]>;
257
+ //#endregion
258
+ //#region src/version.d.ts
259
+ type VersionValue = string | (() => string);
260
+ interface VersionOptions {
261
+ /**
262
+ * Output format. `"plain"` prints the bare version (script-friendly:
263
+ * `$(cli --version)`); a function receives the resolved version and the
264
+ * extension context and returns the line to print.
265
+ *
266
+ * @default `${rootName} v${version}`
267
+ */
268
+ readonly format?: "plain" | ((version: string, context: ExtensionContext) => string);
269
+ }
270
+ declare const versionFlags: readonly [{
271
+ readonly name: "version";
272
+ readonly type: "boolean";
273
+ readonly short: "v";
274
+ readonly noNegate: true;
275
+ readonly description: "Show version number";
276
+ readonly recursive: false;
277
+ }];
278
+ type VersionRegistration<K extends RootMetaKey> = Extension<{}, [], typeof versionFlags, [], K>;
279
+ /** Explicit values supply their own version; omitted values require root metadata. */
280
+ interface VersionExtension {
281
+ (value: VersionValue, options?: VersionOptions): VersionRegistration<never>;
282
+ (value?: VersionValue, options?: VersionOptions): VersionRegistration<"version">;
283
+ readonly id: ExtensionId;
284
+ }
285
+ export declare const version: VersionExtension;
286
+ //#endregion
287
+ export type { CompletionOptions, CompletionRenderOptions, CompletionShell, DidYouMeanOptions, UpdateCommandResolver, UpdateNotifierCacheAdapter, UpdateNotifierCacheConfig, UpdateNotifierOptions, UpdateNotifierPackageManager, UpdateNotifierState, VersionExtension, VersionOptions, VersionValue };