scp-next 1.0.19 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,364 @@
1
+ # Reusable Utility Candidates
2
+
3
+ ## Summary
4
+
5
+ - **Strong candidates:** 3
6
+ - **Possible candidates:** 3
7
+ - **Main overlap:** integer parsing, positive-number validation, test streams, and path
8
+ resolution have smaller duplicated variants.
9
+ - **Intentionally excluded:** SFTP transfer orchestration, CLI operand policy, configuration
10
+ precedence, typed `scp-next` errors, and local-versus-remote path policy.
11
+
12
+ The ranking favors low coupling and clear behavior. Security-sensitive candidates remain
13
+ conditional on stronger specifications and tests.
14
+
15
+ ## 1. `restoreMsysConvertedPosixPath`
16
+
17
+ **Priority:** Strong candidate
18
+ **Current location:** `src/paths/remote-path.ts` as `restoreMsysConvertedRemotePath`
19
+
20
+ ### Purpose
21
+
22
+ Restores POSIX-like arguments that Git Bash/MSYS has rewritten into Windows Git installation
23
+ paths, while leaving ordinary inputs unchanged.
24
+
25
+ ### Why it is reusable
26
+
27
+ Container, cloud-storage, archive, deployment, and remote-execution CLIs often accept POSIX
28
+ paths that cross an MSYS process boundary and encounter the same unwanted argument conversion.
29
+
30
+ ### Duplication or overlap
31
+
32
+ There is no duplicate helper. It has strong independent reuse value and protects both upload
33
+ destinations and download sources through configuration resolution.
34
+
35
+ ### Proposed API
36
+
37
+ ```ts
38
+ export interface MsysPathRestoreOptions {
39
+ installationPrefixes?: readonly string[];
40
+ }
41
+
42
+ export function restoreMsysConvertedPosixPath(
43
+ value: string,
44
+ options?: MsysPathRestoreOptions
45
+ ): string;
46
+ ```
47
+
48
+ ### Generalization required
49
+
50
+ - Remove remote-transfer terminology from the name and documentation.
51
+ - Make recognized Git/MSYS installation prefixes configurable while retaining safe defaults.
52
+ - Specify slash preservation and whether drive-letter matching is case-insensitive.
53
+
54
+ ### Risks and limitations
55
+
56
+ - The heuristic can misclassify a legitimate Windows path beneath a matching installation
57
+ directory.
58
+ - Git installations in uncommon directories require caller configuration.
59
+ - This helper should restore arguments only; it must not perform general path normalization.
60
+
61
+ ## 2. `isDirectlyExecuted`
62
+
63
+ **Priority:** Strong candidate
64
+ **Current location:** `src/cli/index.ts` as `isCliEntrypoint`
65
+
66
+ ### Purpose
67
+
68
+ Determines whether an ESM module is the process entrypoint by resolving both the module URL and
69
+ the executed file to real paths. It correctly recognizes symlinked npm binaries and returns
70
+ `false` when either path cannot be resolved.
71
+
72
+ ### Why it is reusable
73
+
74
+ Any Node.js ESM package that exposes both importable functions and an executable entrypoint needs
75
+ a reliable direct-execution check that works through package-manager-created symlinks.
76
+
77
+ ### Duplication or overlap
78
+
79
+ The same simpler URL-versus-argv comparison appears in `scripts/build-pages.js`, but that variant
80
+ does not resolve symlinks. A shared helper would replace this behavioral overlap.
81
+
82
+ ### Proposed API
83
+
84
+ ```ts
85
+ export function isDirectlyExecuted(moduleUrl: string, argv?: readonly string[]): boolean;
86
+ ```
87
+
88
+ ### Generalization required
89
+
90
+ - Rename the helper to remove `scp-next` CLI terminology.
91
+ - Accept a readonly argv input while defaulting to `process.argv`.
92
+ - Preserve the current fail-closed behavior for missing files and invalid URLs.
93
+
94
+ ### Risks and limitations
95
+
96
+ - Node.js-only: it depends on file URLs and synchronous realpath resolution.
97
+ - Filesystem access occurs during module initialization when callers use it at top level.
98
+ - Non-file module URLs must return `false`, not throw.
99
+
100
+ ## 3. `redactSensitiveValues`
101
+
102
+ **Priority:** Strong candidate
103
+ **Current location:** `src/security/redact.ts`
104
+
105
+ ### Purpose
106
+
107
+ Recursively redacts sensitive object fields and private-key blocks, then removes known secret
108
+ values and recognizable secret options or assignments from text.
109
+
110
+ ### Why it is reusable
111
+
112
+ CLI tools, configuration loaders, deployment systems, and structured loggers all need to
113
+ sanitize nested diagnostic context and free-form error or command output before displaying it.
114
+
115
+ ### Duplication or overlap
116
+
117
+ The module already consolidates several overlapping redaction mechanisms: key matching, recursive
118
+ value transformation, private-key block removal, known-value replacement, CLI option matching,
119
+ and environment-style assignment matching. It is reused across errors, CLI output, transport
120
+ errors, and remote-command results.
121
+
122
+ ### Proposed API
123
+
124
+ ```ts
125
+ export interface RedactionOptions {
126
+ replacement?: string;
127
+ sensitiveKeys?: readonly (string | RegExp)[];
128
+ sensitiveTextPatterns?: readonly RegExp[];
129
+ }
130
+
131
+ export function redactSensitiveValues(
132
+ value: unknown,
133
+ options?: RedactionOptions
134
+ ): unknown;
135
+
136
+ export function redactKnownSensitiveValues(
137
+ text: string,
138
+ source: unknown,
139
+ options?: RedactionOptions
140
+ ): string;
141
+ ```
142
+
143
+ ### Generalization required
144
+
145
+ - Parameterize sensitive keys, replacement text, and text patterns.
146
+ - Separate Node-specific `Buffer` handling from universal recursive redaction.
147
+ - Define behavior for cycles, symbol keys, non-enumerable properties, dates, maps, and sets.
148
+ - Expand tests for mutation, repeated references, quoted values, empty secrets, and overlapping
149
+ secret values.
150
+
151
+ ### Risks and limitations
152
+
153
+ - This is defense in depth, not a guarantee that arbitrary output is secret-free.
154
+ - Broad key substring matching can produce false positives; narrow patterns can miss secrets.
155
+ - The current recursive implementation does not handle cyclic objects.
156
+ - Security defaults must remain conservative and versioned carefully.
157
+
158
+ ## 4. `collectDualStreamOutput`
159
+
160
+ **Priority:** Possible candidate
161
+ **Current location:** `src/client/command-executor.ts` as `collectCommandResult`
162
+
163
+ ### Purpose
164
+
165
+ Collects stdout and stderr from a two-stream channel, enforces a combined byte limit and optional
166
+ timeout, normalizes a missing exit status, closes on failure, and removes listeners when settled.
167
+
168
+ ### Why it is reusable
169
+
170
+ SSH clients, subprocess adapters, terminal gateways, and remote-execution libraries frequently
171
+ need bounded output collection with deterministic cleanup and partial-output error reporting.
172
+
173
+ ### Duplication or overlap
174
+
175
+ There is no exact duplicate. It combines resource-lifecycle operations that otherwise tend to be
176
+ reimplemented around event emitters: settled-state guarding, timer cleanup, listener cleanup,
177
+ stream closure, byte accounting, and exit-result shaping.
178
+
179
+ ### Proposed API
180
+
181
+ ```ts
182
+ export interface DualOutputChannel extends NodeJS.ReadableStream {
183
+ stderr: NodeJS.ReadableStream;
184
+ close(): void;
185
+ }
186
+
187
+ export interface CollectDualStreamOptions {
188
+ timeoutMs?: number;
189
+ maxBytes?: number;
190
+ }
191
+
192
+ export interface CollectedOutput {
193
+ stdout: string;
194
+ stderr: string;
195
+ exitCode: number | null;
196
+ signal?: string;
197
+ }
198
+
199
+ export function collectDualStreamOutput(
200
+ channel: DualOutputChannel,
201
+ options?: CollectDualStreamOptions
202
+ ): Promise<CollectedOutput>;
203
+ ```
204
+
205
+ ### Generalization required
206
+
207
+ - Remove `RemoteCommandError`, command text, and `ssh2` channel types from the core collector.
208
+ - Define a generic close-event adapter or a minimal channel protocol.
209
+ - Expose typed failure details containing partial output and a failure reason.
210
+ - Specify text encoding and combined-versus-per-stream buffer accounting.
211
+
212
+ ### Risks and limitations
213
+
214
+ - Node stream event ordering and close signatures vary between dependencies.
215
+ - String conversion and `Buffer.byteLength()` must not undercount split multibyte input.
216
+ - Cleanup must remain idempotent under timeout, stream error, and close races.
217
+
218
+ ## 5. `normalizeSshSha256Fingerprint`
219
+
220
+ **Priority:** Possible candidate
221
+ **Current location:** `src/security/host-verification.ts`
222
+
223
+ ### Purpose
224
+
225
+ Normalizes OpenSSH-style SHA-256 fingerprints, derives hex and unpadded base64 representations,
226
+ matches case-insensitive hexadecimal forms, and constructs an allow-list verifier.
227
+
228
+ ### Why it is reusable
229
+
230
+ Other SSH clients, deployment agents, and host-key inspection tools need to compare fingerprints
231
+ received in different encodings without weakening host verification.
232
+
233
+ ### Duplication or overlap
234
+
235
+ Normalization is repeated before comparison and candidate generation. Hex and base64 digest
236
+ generation also appears in the host-verification tests. Mazey's public `sha256Hex` overlaps only
237
+ the asynchronous hex digest; it does not provide base64 output, OpenSSH normalization, or
238
+ matching.
239
+
240
+ ### Proposed API
241
+
242
+ ```ts
243
+ export interface SshSha256Fingerprints {
244
+ hex: string;
245
+ base64: string;
246
+ }
247
+
248
+ export function normalizeSshSha256Fingerprint(value: string): string;
249
+
250
+ export function deriveSshSha256Fingerprints(publicKey: Uint8Array): SshSha256Fingerprints;
251
+
252
+ export function createSshFingerprintMatcher(
253
+ allowed: Iterable<string>
254
+ ): (actual: string) => boolean;
255
+ ```
256
+
257
+ ### Generalization required
258
+
259
+ - Separate pure normalization and matching from known-hosts filesystem access.
260
+ - Use runtime-neutral byte input and isolate the Node `crypto` implementation behind an adapter.
261
+ - Specify accepted encodings strictly instead of relying on permissive base64 decoding.
262
+ - Keep host selection and `known_hosts` parsing in an SSH-specific layer.
263
+
264
+ ### Risks and limitations
265
+
266
+ - Security-sensitive parsing must reject malformed or ambiguous values predictably.
267
+ - Host fingerprints are not passwords, but comparison and allow-list behavior still define a
268
+ trust boundary.
269
+ - The current known-hosts parser supports exact plain hosts only, not hashed hosts or all OpenSSH
270
+ marker behavior.
271
+
272
+ ## 6. `validateLocalMarkdownLinks`
273
+
274
+ **Priority:** Possible candidate
275
+ **Current location:** `scripts/validate-doc-links.js`
276
+
277
+ ### Purpose
278
+
279
+ Discovers Markdown files, ignores fenced and inline code, extracts local links, validates
280
+ repository boundaries and target existence, and checks Markdown heading fragments.
281
+
282
+ ### Why it is reusable
283
+
284
+ Documentation-heavy repositories can use the same zero-service validation in local development
285
+ and CI without requiring a full static-site build.
286
+
287
+ ### Duplication or overlap
288
+
289
+ Code stripping and link extraction use overlapping transformations inside the script. There is
290
+ no production duplicate, but the complete validation pipeline has credible reuse across project
291
+ documentation tooling.
292
+
293
+ ### Proposed API
294
+
295
+ ```ts
296
+ export interface MarkdownLinkIssue {
297
+ file: string;
298
+ target?: string;
299
+ message: string;
300
+ }
301
+
302
+ export interface MarkdownLinkValidationOptions {
303
+ rootDirectory: string;
304
+ files: readonly string[];
305
+ }
306
+
307
+ export function validateLocalMarkdownLinks(
308
+ options: MarkdownLinkValidationOptions
309
+ ): MarkdownLinkIssue[];
310
+ ```
311
+
312
+ ### Generalization required
313
+
314
+ - Separate pure Markdown target and heading parsing from filesystem traversal.
315
+ - Make repository-boundary, generated-directory, and source-file policies caller-controlled.
316
+ - Add fixtures for reference links, encoded paths, duplicate headings, Unicode, code blocks,
317
+ images, and malformed Markdown.
318
+
319
+ ### Risks and limitations
320
+
321
+ - Regex extraction is not a complete CommonMark parser.
322
+ - Heading-slug behavior varies between GitHub, documentation generators, and static-site tools.
323
+ - Symlinks can complicate repository-boundary checks based only on resolved path strings.
324
+
325
+ ## Existing Dependency Matches
326
+
327
+ The installed version is `mazey@5.5.0`. Two former candidates now have exact public Mazey APIs
328
+ and are reused by the project:
329
+
330
+ | Current logic | Location | Existing API | Recommendation |
331
+ | ------------------------------- | ------------------------------ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
332
+ | Defined-only shallow assignment | `src/config/resolve-config.ts` | `assignDefined` | Reuse the public Mazey API; it preserves precedence, mutation, and falsy-value behavior. |
333
+ | Binary byte-size formatting | `src/cli/output.ts` | `formatByteSize` | Reuse the public Mazey API with its default 1024 base and one fractional digit. |
334
+ | IIFE global-name normalization | `scripts/rollup.config.mjs` | `toJavaScriptGlobalName` | Use the public Mazey API if this legacy build configuration remains; verify the intentional behavior change for scoped names. |
335
+
336
+ `sha256Hex`, `parseJsonSafe`, `throttle`, `waitTime`, `isNumber`, and `isUdfOrNul` were
337
+ reviewed but do not fully match the remaining ranked helpers' behavior.
338
+
339
+ ## Reviewed but Excluded
340
+
341
+ | Helper or area | Location | Reason |
342
+ | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
343
+ | Integer parsing | `src/config/environment.ts`, `src/cli/commands/shared.ts` | Exact duplication exists around `Number.parseInt`, but the logic is too small and its error policy belongs to each caller. |
344
+ | Positive-number validation | `src/config/validation.ts`, `src/client/client.ts` | Behavior overlaps for timeouts and buffer limits, but each rule has distinct ranges, integer requirements, and typed errors. |
345
+ | Configuration resolution | `src/config/resolve-config.ts` | The precedence order, profile/job selection, and path interpretation are `scp-next` policy. |
346
+ | Post-upload command guards | `src/config/post-upload-commands.ts`, `src/config/load-config.ts`, `src/cli/commands/shared.ts` | Behavioral overlap exists, but the rule is application-specific and the primitive checks are trivial. |
347
+ | Remote path wrappers | `src/paths/remote-path.ts` | `path.posix` already provides the general operations; validation and MSYS restoration carry project policy. |
348
+ | Home expansion and local path resolution | `src/paths/local-path.ts` | Useful but small, Node-specific, and intentionally limited to `~` rather than user-home lookup. |
349
+ | Progress percentage and reporter | `src/client/client.ts`, `src/cli/output.ts` | Percentage math is trivial; the reporter is coupled to transfer fields, terminal wording, and CLI throttling policy. |
350
+ | Error conversion and host-failure classification | `src/errors/index.ts`, `src/client/transport.ts` | Keyword rules map directly to the public `scp-next` error hierarchy and fail-closed SSH behavior. |
351
+ | Local and remote file walking | `src/client/walk.ts` | Traversal is coupled to transfer entry types, SFTP transport behavior, and local/remote path distinctions. |
352
+ | Destination and overwrite resolution | `src/client/client.ts` | Some inline blocks overlap, but they encode `cp`/`scp`-style transfer semantics and typed failures. |
353
+ | `fileExists`, `collectValues`, package-version reading, and `_resolve` | `src/config/load-config.ts`, `src/cli/commands/shared.ts`, `src/cli/index.ts`, `scripts/*` | One-off or one-line native wrappers lack enough independent value. |
354
+ | `MemoryStream`, temp-directory builders, and mocks | `tests/**` | Minor test duplication exists, but these fixtures are short and tied to Vitest or project interfaces. |
355
+ | Release helper deep import | `scripts/release.js` | It consumes an undocumented Mazey path and should not define a new general-purpose extraction. |
356
+
357
+ ## Recommended Extraction Order
358
+
359
+ 1. `restoreMsysConvertedPosixPath`
360
+ 2. `isDirectlyExecuted`
361
+ 3. `redactSensitiveValues`
362
+ 4. `collectDualStreamOutput`
363
+ 5. `normalizeSshSha256Fingerprint`
364
+ 6. `validateLocalMarkdownLinks`