@williamthorsen/release-kit 14.2.1 → 14.2.3

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 (91) hide show
  1. package/.meta/changelog.json +77 -0
  2. package/.readyup/kits/default.js +142 -152
  3. package/.readyup/kits/npm-auto-publish.js +183 -184
  4. package/.readyup/manifest.json +83 -9
  5. package/CHANGELOG.md +24 -0
  6. package/README.md +7 -1
  7. package/dist/esm/applyChangelogOverrides.d.ts +50 -0
  8. package/dist/esm/applyChangelogOverrides.js +162 -0
  9. package/dist/esm/attachChangelogDiagnostics.d.ts +1 -1
  10. package/dist/esm/bin/release-kit.js +6 -238
  11. package/dist/esm/buildChangelogEntries.d.ts +20 -31
  12. package/dist/esm/buildChangelogEntries.js +71 -188
  13. package/dist/esm/buildChangelogItem.d.ts +56 -0
  14. package/dist/esm/buildChangelogItem.js +119 -0
  15. package/dist/esm/buildReleasedResult.d.ts +20 -0
  16. package/dist/esm/buildReleasedResult.js +78 -0
  17. package/dist/esm/changelogJsonUtils.d.ts +1 -1
  18. package/dist/esm/changelogOverrides.d.ts +29 -143
  19. package/dist/esm/changelogOverrides.js +35 -467
  20. package/dist/esm/configSchema.d.ts +225 -0
  21. package/dist/esm/configSchema.js +103 -0
  22. package/dist/esm/createGithubReleaseCommand.js +4 -3
  23. package/dist/esm/decideDirectReleases.d.ts +30 -0
  24. package/dist/esm/decideDirectReleases.js +66 -0
  25. package/dist/esm/defaults.js +1 -4
  26. package/dist/esm/defineConfig.d.ts +2 -2
  27. package/dist/esm/deriveReleaseNotesConfig.d.ts +2 -1
  28. package/dist/esm/deriveReleaseNotesConfig.js +1 -1
  29. package/dist/esm/determineBumpType.js +4 -3
  30. package/dist/esm/enumerateReleaseWindows.js +4 -4
  31. package/dist/esm/formatPrepareFiles.d.ts +11 -0
  32. package/dist/esm/formatPrepareFiles.js +55 -0
  33. package/dist/esm/formatPrepareWarnings.d.ts +26 -0
  34. package/dist/esm/formatPrepareWarnings.js +72 -0
  35. package/dist/esm/help/initHelp.d.ts +4 -0
  36. package/dist/esm/help/initHelp.js +31 -0
  37. package/dist/esm/help/overridesHelp.d.ts +4 -0
  38. package/dist/esm/help/overridesHelp.js +32 -0
  39. package/dist/esm/help/releaseStepsHelp.d.ts +12 -0
  40. package/dist/esm/help/releaseStepsHelp.js +91 -0
  41. package/dist/esm/help/syncLabelsHelp.d.ts +8 -0
  42. package/dist/esm/help/syncLabelsHelp.js +55 -0
  43. package/dist/esm/help/usage.d.ts +2 -0
  44. package/dist/esm/help/usage.js +29 -0
  45. package/dist/esm/index.d.ts +1 -1
  46. package/dist/esm/index.js +1 -1
  47. package/dist/esm/init/initCommand.js +3 -7
  48. package/dist/esm/loadChangelogOverrides.d.ts +31 -0
  49. package/dist/esm/loadChangelogOverrides.js +138 -0
  50. package/dist/esm/loadConfig.d.ts +0 -40
  51. package/dist/esm/loadConfig.js +0 -242
  52. package/dist/esm/loadValidatedConfig.d.ts +1 -1
  53. package/dist/esm/mergeConfig.d.ts +41 -0
  54. package/dist/esm/mergeConfig.js +118 -0
  55. package/dist/esm/monorepoConfigAssertions.d.ts +49 -0
  56. package/dist/esm/monorepoConfigAssertions.js +126 -0
  57. package/dist/esm/planChangelogFiles.d.ts +16 -0
  58. package/dist/esm/planChangelogFiles.js +41 -0
  59. package/dist/esm/prepareArgs.d.ts +55 -0
  60. package/dist/esm/prepareArgs.js +69 -0
  61. package/dist/esm/prepareCommand.d.ts +0 -48
  62. package/dist/esm/prepareCommand.js +11 -77
  63. package/dist/esm/prepareComposite.d.ts +16 -0
  64. package/dist/esm/prepareComposite.js +39 -0
  65. package/dist/esm/prepareRunContext.d.ts +38 -0
  66. package/dist/esm/prepareRunContext.js +39 -0
  67. package/dist/esm/prepareTarget.d.ts +2 -7
  68. package/dist/esm/prepareTarget.js +2 -118
  69. package/dist/esm/previewTagPrefixes.d.ts +1 -1
  70. package/dist/esm/releasePrepareTargets.js +13 -207
  71. package/dist/esm/renderReleaseNotes.js +6 -5
  72. package/dist/esm/reportPrepare.js +3 -123
  73. package/dist/esm/resolveCommandTags.d.ts +1 -1
  74. package/dist/esm/resolveCommandTags.js +2 -1
  75. package/dist/esm/resolveReleaseDeclaration.d.ts +2 -1
  76. package/dist/esm/resolveReleaseTargets.d.ts +2 -1
  77. package/dist/esm/resolveReleaseTargets.js +2 -1
  78. package/dist/esm/stripScope.js +4 -3
  79. package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
  80. package/dist/esm/sync-labels/resolveLabels.d.ts +1 -1
  81. package/dist/esm/topologicalSort.d.ts +12 -0
  82. package/dist/esm/topologicalSort.js +60 -0
  83. package/dist/esm/types.d.ts +1 -225
  84. package/dist/esm/types.js +1 -103
  85. package/dist/esm/validateAllChangelogOverrides.d.ts +56 -0
  86. package/dist/esm/validateAllChangelogOverrides.js +138 -0
  87. package/dist/esm/validateConfig.d.ts +1 -1
  88. package/dist/esm/validateConfig.js +7 -9
  89. package/dist/esm/validateOverridesCommand.d.ts +1 -1
  90. package/dist/esm/validateOverridesCommand.js +2 -1
  91. package/package.json +4 -4
@@ -1,5 +1,5 @@
1
1
  import type { BreakingPolicy } from '@williamthorsen/change-grammar';
2
- import { z } from 'zod';
2
+ import type { LegacyIdentity } from './configSchema.js';
3
3
  /** Semver release type for version bumping. */
4
4
  export type ReleaseType = 'major' | 'minor' | 'patch';
5
5
  /** Target audience for a changelog section. */
@@ -365,230 +365,6 @@ export interface VersionPatterns {
365
365
  /** Commit types that trigger a minor bump. */
366
366
  minor: string[];
367
367
  }
368
- /**
369
- * Schema for a single historical identity snapshot for a workspace.
370
- *
371
- * Captures what the package looked like at some earlier point: the full npm `name`
372
- * (e.g., `'@williamthorsen/nmr-core'`) and the `tagPrefix` under which tags were
373
- * published (e.g., `'core-v'`).
374
- */
375
- export declare const legacyIdentitySchema: z.ZodObject<{
376
- name: z.ZodString;
377
- tagPrefix: z.ZodString;
378
- }, z.core.$strict>;
379
- /**
380
- * A single historical identity snapshot for a workspace. Both fields are required, because a full
381
- * tuple stays unambiguous across any number of renames.
382
- */
383
- export type LegacyIdentity = z.infer<typeof legacyIdentitySchema>;
384
- /**
385
- * Schema for a package that once lived in this repo but has since been extracted or removed.
386
- *
387
- * Unlike `legacyIdentitySchema`, retired packages are never consulted for baseline lookup
388
- * or changelog attribution; they acknowledge historical tag prefixes and suppress
389
- * undeclared-candidate warnings.
390
- */
391
- export declare const retiredPackageSchema: z.ZodObject<{
392
- name: z.ZodString;
393
- tagPrefix: z.ZodString;
394
- successor: z.ZodOptional<z.ZodString>;
395
- }, z.core.$strict>;
396
- /** A package that once lived in this repo but has since been extracted or removed. */
397
- export type RetiredPackage = z.infer<typeof retiredPackageSchema>;
398
- /** Schema for a single workspace override entry in the config file. */
399
- export declare const workspaceOverrideSchema: z.ZodObject<{
400
- dir: z.ZodString;
401
- shouldExclude: z.ZodOptional<z.ZodBoolean>;
402
- legacyIdentities: z.ZodOptional<z.ZodArray<z.ZodObject<{
403
- name: z.ZodString;
404
- tagPrefix: z.ZodString;
405
- }, z.core.$strict>>>;
406
- }, z.core.$strict>;
407
- /** Override for a single workspace in the config file. Matches a discovered workspace by `dir`. */
408
- export type WorkspaceOverride = z.infer<typeof workspaceOverrideSchema>;
409
- /** Schema for the optional `project` block. */
410
- export declare const projectConfigSchema: z.ZodObject<{
411
- paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
412
- tagPrefix: z.ZodOptional<z.ZodString>;
413
- }, z.core.$strict>;
414
- /** Consumer-facing project-release config (`project` block). Empty object opts in to project releases. */
415
- export type ProjectConfig = z.infer<typeof projectConfigSchema>;
416
- /**
417
- * Schema for a `releases` entry that names the target at `path`: the discovered workspace there, or otherwise the
418
- * directory, whose `tagPrefix` defaults to its basename followed by `-v`.
419
- */
420
- export declare const pathReleaseSchema: z.ZodObject<{
421
- path: z.ZodString;
422
- tagPrefix: z.ZodOptional<z.ZodString>;
423
- legacyIdentities: z.ZodOptional<z.ZodArray<z.ZodObject<{
424
- name: z.ZodString;
425
- tagPrefix: z.ZodString;
426
- }, z.core.$strict>>>;
427
- }, z.core.$strict>;
428
- /** A `releases` entry that names the discovered workspace or the directory at `path`. */
429
- export type PathRelease = z.infer<typeof pathReleaseSchema>;
430
- /** Schema for a `releases` entry that declares a composite of the named targets. */
431
- export declare const compositeReleaseSchema: z.ZodObject<{
432
- includes: z.ZodArray<z.ZodString>;
433
- tagPrefix: z.ZodOptional<z.ZodString>;
434
- }, z.core.$strict>;
435
- /** A `releases` entry that declares a composite of the named targets, versioned by the root `package.json`. */
436
- export type CompositeRelease = z.infer<typeof compositeReleaseSchema>;
437
- /**
438
- * Schema for one `releases` entry: `false` excludes the discovered workspace of that name, and the object shapes name
439
- * a workspace or a directory, or declare the composite. `validateConfig` relies on this branch order to report a failed entry.
440
- */
441
- export declare const releaseEntrySchema: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
442
- path: z.ZodString;
443
- tagPrefix: z.ZodOptional<z.ZodString>;
444
- legacyIdentities: z.ZodOptional<z.ZodArray<z.ZodObject<{
445
- name: z.ZodString;
446
- tagPrefix: z.ZodString;
447
- }, z.core.$strict>>>;
448
- }, z.core.$strict>, z.ZodObject<{
449
- includes: z.ZodArray<z.ZodString>;
450
- tagPrefix: z.ZodOptional<z.ZodString>;
451
- }, z.core.$strict>]>;
452
- /** One `releases` entry, keyed in the config by the target's name. */
453
- export type ReleaseEntry = z.infer<typeof releaseEntrySchema>;
454
- /** Schema for the optional `changelogJson` block (input shape, all fields optional). */
455
- export declare const changelogJsonInputSchema: z.ZodObject<{
456
- enabled: z.ZodOptional<z.ZodBoolean>;
457
- outputPath: z.ZodOptional<z.ZodString>;
458
- devOnlySections: z.ZodOptional<z.ZodArray<z.ZodString>>;
459
- }, z.core.$strict>;
460
- /** Schema for the optional `changelogMarkdown` block (input shape, all fields optional). */
461
- export declare const changelogMarkdownInputSchema: z.ZodObject<{
462
- keepVersions: z.ZodOptional<z.ZodNumber>;
463
- }, z.core.$strict>;
464
- /** Schema for the optional `releaseNotes` block (input shape, all fields optional). */
465
- export declare const releaseNotesInputSchema: z.ZodObject<{
466
- shouldInjectIntoReadme: z.ZodOptional<z.ZodBoolean>;
467
- }, z.core.$strict>;
468
- /** Schema for a single `workTypes` entry. */
469
- export declare const workTypeConfigSchema: z.ZodObject<{
470
- header: z.ZodString;
471
- aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
472
- excludedFromChangelog: z.ZodOptional<z.ZodBoolean>;
473
- }, z.core.$strict>;
474
- /** Schema for the `versionPatterns` block. Both arrays are required when the field is provided. */
475
- export declare const versionPatternsSchema: z.ZodObject<{
476
- major: z.ZodArray<z.ZodString>;
477
- minor: z.ZodArray<z.ZodString>;
478
- }, z.core.$strict>;
479
- /** Schema for a single `breakingPolicies` value. */
480
- export declare const breakingPolicyValueSchema: z.ZodEnum<{
481
- optional: "optional";
482
- forbidden: "forbidden";
483
- }>;
484
- /** Schema for a single label's spec in the `repoLabels.labels` record. */
485
- export declare const labelSpecSchema: z.ZodObject<{
486
- color: z.ZodString;
487
- description: z.ZodOptional<z.ZodString>;
488
- aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
489
- archived: z.ZodOptional<z.ZodLiteral<true>>;
490
- }, z.core.$strict>;
491
- /**
492
- * A label's color, optional description, optional aliases, and optional archived mark; the name is the record key in
493
- * `repoLabels.labels`. An omitted description resolves to empty, which the generated labels file declares explicitly
494
- * so that a sync clears whatever description the label has on the repo. Each alias names an existing label that the
495
- * sync renames to this one in place, keeping it on the issues and pull requests that have it. `archived: true` retires
496
- * the label: The sync archives it, keeping it on the issues and pull requests that have it.
497
- */
498
- export type LabelSpec = z.infer<typeof labelSpecSchema>;
499
- /**
500
- * Schema for the optional `repoLabels` block, which declares the repository's label registry (the labels defined on
501
- * the GitHub repo, distinct from labels applied to PRs and issues).
502
- *
503
- * Resolution is an ordered fold with last-writer-wins: presets in `extends` order, then the `labels` record,
504
- * in which an entry adds a label, replaces one that an earlier layer defined, or removes it (`null`). Names match
505
- * case-insensitively, as GitHub matches them, and the replacing layer's spelling wins.
506
- * Three misstatements are resolve-time errors (`resolveLabels`), not schema errors: `labels` keys that differ only in
507
- * case, a `null` naming a label not defined by any earlier layer, and an alias that names a resolved label or repeats
508
- * another alias.
509
- */
510
- export declare const repoLabelsSchema: z.ZodObject<{
511
- extends: z.ZodOptional<z.ZodArray<z.ZodString>>;
512
- labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodObject<{
513
- color: z.ZodString;
514
- description: z.ZodOptional<z.ZodString>;
515
- aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
516
- archived: z.ZodOptional<z.ZodLiteral<true>>;
517
- }, z.core.$strict>>>>;
518
- }, z.core.$strict>;
519
- /** Consumer-facing `repoLabels` block: bundled presets to extend plus per-name adjustments. */
520
- export type RepoLabelsConfig = z.infer<typeof repoLabelsSchema>;
521
- /** Schema for the consumer-facing config file shape (`.config/release-kit.config.ts`). */
522
- export declare const releaseKitConfigSchema: z.ZodObject<{
523
- breakingPolicies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
524
- optional: "optional";
525
- forbidden: "forbidden";
526
- }>>>;
527
- changelogJson: z.ZodOptional<z.ZodObject<{
528
- enabled: z.ZodOptional<z.ZodBoolean>;
529
- outputPath: z.ZodOptional<z.ZodString>;
530
- devOnlySections: z.ZodOptional<z.ZodArray<z.ZodString>>;
531
- }, z.core.$strict>>;
532
- changelogMarkdown: z.ZodOptional<z.ZodObject<{
533
- keepVersions: z.ZodOptional<z.ZodNumber>;
534
- }, z.core.$strict>>;
535
- formatCommand: z.ZodOptional<z.ZodString>;
536
- project: z.ZodOptional<z.ZodObject<{
537
- paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
538
- tagPrefix: z.ZodOptional<z.ZodString>;
539
- }, z.core.$strict>>;
540
- releaseNotes: z.ZodOptional<z.ZodObject<{
541
- shouldInjectIntoReadme: z.ZodOptional<z.ZodBoolean>;
542
- }, z.core.$strict>>;
543
- releases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
544
- path: z.ZodString;
545
- tagPrefix: z.ZodOptional<z.ZodString>;
546
- legacyIdentities: z.ZodOptional<z.ZodArray<z.ZodObject<{
547
- name: z.ZodString;
548
- tagPrefix: z.ZodString;
549
- }, z.core.$strict>>>;
550
- }, z.core.$strict>, z.ZodObject<{
551
- includes: z.ZodArray<z.ZodString>;
552
- tagPrefix: z.ZodOptional<z.ZodString>;
553
- }, z.core.$strict>]>>>;
554
- repoLabels: z.ZodOptional<z.ZodObject<{
555
- extends: z.ZodOptional<z.ZodArray<z.ZodString>>;
556
- labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodObject<{
557
- color: z.ZodString;
558
- description: z.ZodOptional<z.ZodString>;
559
- aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
560
- archived: z.ZodOptional<z.ZodLiteral<true>>;
561
- }, z.core.$strict>>>>;
562
- }, z.core.$strict>>;
563
- retiredPackages: z.ZodOptional<z.ZodArray<z.ZodObject<{
564
- name: z.ZodString;
565
- tagPrefix: z.ZodString;
566
- successor: z.ZodOptional<z.ZodString>;
567
- }, z.core.$strict>>>;
568
- scopeAliases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
569
- versionPatterns: z.ZodOptional<z.ZodObject<{
570
- major: z.ZodArray<z.ZodString>;
571
- minor: z.ZodArray<z.ZodString>;
572
- }, z.core.$strict>>;
573
- workspaces: z.ZodOptional<z.ZodArray<z.ZodObject<{
574
- dir: z.ZodString;
575
- shouldExclude: z.ZodOptional<z.ZodBoolean>;
576
- legacyIdentities: z.ZodOptional<z.ZodArray<z.ZodObject<{
577
- name: z.ZodString;
578
- tagPrefix: z.ZodString;
579
- }, z.core.$strict>>>;
580
- }, z.core.$strict>>>;
581
- workTypes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
582
- header: z.ZodString;
583
- aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
584
- excludedFromChangelog: z.ZodOptional<z.ZodBoolean>;
585
- }, z.core.$strict>>>;
586
- }, z.core.$strict>;
587
- /**
588
- * Consumer-facing config file shape (`.config/release-kit.config.ts`).
589
- * All fields are optional; defaults are applied during config merging.
590
- */
591
- export type ReleaseKitConfig = z.infer<typeof releaseKitConfigSchema>;
592
368
  /** A raw commit from the git log. */
593
369
  export interface Commit {
594
370
  /** The commit message, subject and body together. */
package/dist/esm/types.js CHANGED
@@ -1,103 +1 @@
1
- import { z } from 'zod';
2
- export const legacyIdentitySchema = z
3
- .object({
4
- name: z.string().min(1),
5
- tagPrefix: z.string().min(1),
6
- })
7
- .strict();
8
- export const retiredPackageSchema = z
9
- .object({
10
- name: z.string().min(1),
11
- tagPrefix: z.string().min(1),
12
- successor: z.string().min(1).optional(),
13
- })
14
- .strict();
15
- export const workspaceOverrideSchema = z
16
- .object({
17
- dir: z.string().min(1),
18
- shouldExclude: z.boolean().optional(),
19
- legacyIdentities: z.array(legacyIdentitySchema).optional(),
20
- })
21
- .strict();
22
- export const projectConfigSchema = z
23
- .object({
24
- paths: z.array(z.string().min(1)).min(1).optional(),
25
- tagPrefix: z.string().min(1).optional(),
26
- })
27
- .strict();
28
- export const pathReleaseSchema = z
29
- .object({
30
- path: z.string().min(1),
31
- tagPrefix: z.string().min(1).optional(),
32
- legacyIdentities: z.array(legacyIdentitySchema).optional(),
33
- })
34
- .strict();
35
- export const compositeReleaseSchema = z
36
- .object({
37
- includes: z.array(z.string().min(1)).min(1),
38
- tagPrefix: z.string().min(1).optional(),
39
- })
40
- .strict();
41
- export const releaseEntrySchema = z.union([z.literal(false), pathReleaseSchema, compositeReleaseSchema]);
42
- export const changelogJsonInputSchema = z
43
- .object({
44
- enabled: z.boolean().optional(),
45
- outputPath: z.string().optional(),
46
- devOnlySections: z.array(z.string()).optional(),
47
- })
48
- .strict();
49
- export const changelogMarkdownInputSchema = z
50
- .object({
51
- keepVersions: z.number().int().min(1).optional(),
52
- })
53
- .strict();
54
- export const releaseNotesInputSchema = z
55
- .object({
56
- shouldInjectIntoReadme: z.boolean().optional(),
57
- })
58
- .strict();
59
- export const workTypeConfigSchema = z
60
- .object({
61
- header: z.string(),
62
- aliases: z.array(z.string()).optional(),
63
- excludedFromChangelog: z.boolean().optional(),
64
- })
65
- .strict();
66
- export const versionPatternsSchema = z
67
- .object({
68
- major: z.array(z.string()),
69
- minor: z.array(z.string()),
70
- })
71
- .strict();
72
- export const breakingPolicyValueSchema = z.enum(['forbidden', 'optional']);
73
- export const labelSpecSchema = z
74
- .object({
75
- color: z.string().min(1),
76
- description: z.string().optional(),
77
- aliases: z.array(z.string().min(1)).min(1).optional(),
78
- archived: z.literal(true).optional(),
79
- })
80
- .strict();
81
- export const repoLabelsSchema = z
82
- .object({
83
- extends: z.array(z.string().min(1)).optional(),
84
- labels: z.record(z.string().min(1), labelSpecSchema.nullable()).optional(),
85
- })
86
- .strict();
87
- export const releaseKitConfigSchema = z
88
- .object({
89
- breakingPolicies: z.record(z.string(), breakingPolicyValueSchema).optional(),
90
- changelogJson: changelogJsonInputSchema.optional(),
91
- changelogMarkdown: changelogMarkdownInputSchema.optional(),
92
- formatCommand: z.string().min(1).optional(),
93
- project: projectConfigSchema.optional(),
94
- releaseNotes: releaseNotesInputSchema.optional(),
95
- releases: z.record(z.string().min(1), releaseEntrySchema).optional(),
96
- repoLabels: repoLabelsSchema.optional(),
97
- retiredPackages: z.array(retiredPackageSchema).optional(),
98
- scopeAliases: z.record(z.string(), z.string()).optional(),
99
- versionPatterns: versionPatternsSchema.optional(),
100
- workspaces: z.array(workspaceOverrideSchema).optional(),
101
- workTypes: z.record(z.string(), workTypeConfigSchema).optional(),
102
- })
103
- .strict();
1
+ export {};
@@ -0,0 +1,56 @@
1
+ /** Per-scope input to {@link validateAllChangelogOverrides}. */
2
+ export interface ChangelogOverrideScope {
3
+ /**
4
+ * Path to the override file, used to load the file and to attribute findings. A relative path resolves against the
5
+ * process working directory.
6
+ */
7
+ filePath: string;
8
+ /** Items in this scope's history window. Each override key is matched against these. */
9
+ items: readonly OverrideTargetItem[];
10
+ }
11
+ /** A changelog item as override matching sees it. `entry` is absent for a title-derived item. */
12
+ export interface OverrideTargetItem {
13
+ hash: string;
14
+ entry?: number;
15
+ }
16
+ /**
17
+ * Validates every override file against the items of its scopes and returns the aggregated findings. It reads the
18
+ * override files but doesn't collect any items: The caller supplies each scope's items.
19
+ *
20
+ * Items built by `buildChangelogEntries`, as `release-kit prepare` builds them, yield the matches that `prepare`
21
+ * computes, tier asymmetry included: A workspace-tier key is stale when it matches nothing in its own workspace, and a
22
+ * root-tier key only when it matches nothing in any scope, the project release window included.
23
+ *
24
+ * Every returned string is prefixed with the path of the override file to which it pertains.
25
+ *
26
+ * A relative `filePath`, in any scope, resolves against the process working directory, not against the repo root.
27
+ * A caller running from elsewhere passes absolute paths or changes directory first.
28
+ */
29
+ export declare function validateAllChangelogOverrides(inputs: ValidateAllChangelogOverridesInputs): ValidateAllChangelogOverridesResult;
30
+ /** Inputs to {@link validateAllChangelogOverrides}. */
31
+ export interface ValidateAllChangelogOverridesInputs {
32
+ /**
33
+ * Project-tier scope (the override file at the repo root).
34
+ *
35
+ * The file at `filePath` is loaded once and used in two ways:
36
+ * - Its overrides are composed into every workspace's apply (root-tier overrides apply globally).
37
+ * - When `items` is provided, the project map is also applied directly to those items
38
+ * (project release in monorepo mode, or the package's history in single-package mode).
39
+ *
40
+ * Omit when the project file doesn't exist.
41
+ */
42
+ project?: {
43
+ filePath: string;
44
+ items?: readonly OverrideTargetItem[];
45
+ };
46
+ /** Per-workspace scopes. Each workspace's file applies only to its own items. */
47
+ workspaces?: readonly ChangelogOverrideScope[];
48
+ }
49
+ /**
50
+ * Result of {@link validateAllChangelogOverrides}: aggregated errors and warnings, each prefixed with the path of the
51
+ * file to which it pertains.
52
+ */
53
+ export interface ValidateAllChangelogOverridesResult {
54
+ errors: string[];
55
+ warnings: string[];
56
+ }
@@ -0,0 +1,138 @@
1
+ import { applyChangelogOverrides, indexItemPositions, resolveOverrideKey } from "./applyChangelogOverrides.js";
2
+ import { composeOverrides } from "./changelogOverrides.js";
3
+ import { loadChangelogOverrides } from "./loadChangelogOverrides.js";
4
+ export function validateAllChangelogOverrides(inputs) {
5
+ const errors = [];
6
+ const warnings = [];
7
+ const projectFilePath = inputs.project?.filePath;
8
+ const projectMap = loadScopeMap(projectFilePath, errors);
9
+ const workspaceMaps = (inputs.workspaces ?? []).map((scope) => ({
10
+ filePath: scope.filePath,
11
+ items: scope.items,
12
+ map: loadScopeMap(scope.filePath, errors),
13
+ }));
14
+ const globalMatchedRootKeys = new Set();
15
+ for (const workspace of workspaceMaps) {
16
+ processWorkspaceScope({
17
+ workspace,
18
+ projectFilePath,
19
+ projectMap,
20
+ errors,
21
+ warnings,
22
+ globalMatchedRootKeys,
23
+ });
24
+ }
25
+ const projectItems = inputs.project?.items;
26
+ if (projectFilePath !== undefined && projectItems !== undefined) {
27
+ processProjectScope({ projectFilePath, projectMap, projectItems, errors, globalMatchedRootKeys });
28
+ }
29
+ if (projectFilePath !== undefined) {
30
+ collectRootStaleWarnings(projectFilePath, projectMap, globalMatchedRootKeys, warnings);
31
+ }
32
+ return { errors, warnings };
33
+ }
34
+ function collectRootStaleWarnings(projectFilePath, projectMap, globalMatchedRootKeys, warnings) {
35
+ for (const key of projectMap.keys()) {
36
+ if (!globalMatchedRootKeys.has(key)) {
37
+ warnings.push(formatRootStaleWarning(projectFilePath, key));
38
+ }
39
+ }
40
+ }
41
+ function filterShadowedKeys(projectMap, workspaceMap) {
42
+ const result = new Map();
43
+ for (const [key, value] of projectMap) {
44
+ if (workspaceMap.has(key))
45
+ continue;
46
+ result.set(key, value);
47
+ }
48
+ return result;
49
+ }
50
+ function formatRootStaleWarning(filePath, key) {
51
+ return `${filePath}: Override key '${key}' did not match any item in any scope (likely a stale reference)`;
52
+ }
53
+ function formatWorkspaceStaleWarning(filePath, key) {
54
+ return `${filePath}: Override key '${key}' did not match any item in this workspace's history (likely a stale reference)`;
55
+ }
56
+ function hasAnyMatch(key, positionsByHash) {
57
+ return resolveOverrideKey(key, positionsByHash).kind !== 'none';
58
+ }
59
+ function loadScopeMap(filePath, errors) {
60
+ if (filePath === undefined) {
61
+ return new Map();
62
+ }
63
+ const result = loadChangelogOverrides(filePath);
64
+ if ('errors' in result) {
65
+ for (const message of result.errors) {
66
+ errors.push(prefixWithFilePath(filePath, message));
67
+ }
68
+ return new Map();
69
+ }
70
+ return result.overrides;
71
+ }
72
+ function makeValidationEntries(items) {
73
+ return [
74
+ {
75
+ version: '0.0.0',
76
+ date: '0000-00-00',
77
+ sections: [
78
+ {
79
+ title: 'Validation',
80
+ audience: 'all',
81
+ items: items.map((item) => ({ ...item, description: '' })),
82
+ },
83
+ ],
84
+ },
85
+ ];
86
+ }
87
+ function prefixWithFilePath(filePath, message) {
88
+ return `${filePath}: ${message}`;
89
+ }
90
+ function processProjectScope(args) {
91
+ const { projectFilePath, projectMap, projectItems, errors, globalMatchedRootKeys } = args;
92
+ const applied = applyChangelogOverrides(makeValidationEntries(projectItems), projectMap);
93
+ for (const message of applied.errors) {
94
+ errors.push(prefixWithFilePath(projectFilePath, message));
95
+ }
96
+ const positionsByHash = indexItemPositions(projectItems);
97
+ for (const key of projectMap.keys()) {
98
+ if (hasAnyMatch(key, positionsByHash)) {
99
+ globalMatchedRootKeys.add(key);
100
+ }
101
+ }
102
+ }
103
+ function processWorkspaceScope(args) {
104
+ const { workspace, projectFilePath, projectMap, errors, warnings, globalMatchedRootKeys } = args;
105
+ const { filePath, items, map } = workspace;
106
+ const validationEntries = makeValidationEntries(items);
107
+ const positionsByHash = indexItemPositions(items);
108
+ const workspaceApplied = applyChangelogOverrides(validationEntries, map);
109
+ for (const message of workspaceApplied.errors) {
110
+ errors.push(prefixWithFilePath(filePath, message));
111
+ }
112
+ if (projectFilePath !== undefined && projectMap.size > 0) {
113
+ const projectMinusShadowed = filterShadowedKeys(projectMap, map);
114
+ const projectApplied = applyChangelogOverrides(validationEntries, projectMinusShadowed);
115
+ for (const message of projectApplied.errors) {
116
+ errors.push(prefixWithFilePath(projectFilePath, message));
117
+ }
118
+ const perFileErrors = new Set([...workspaceApplied.errors, ...projectApplied.errors]);
119
+ const composedApplied = applyChangelogOverrides(validationEntries, composeOverrides(projectMap, map));
120
+ for (const message of composedApplied.errors) {
121
+ if (!perFileErrors.has(message)) {
122
+ errors.push(prefixWithFilePath(filePath, message));
123
+ }
124
+ }
125
+ }
126
+ for (const key of map.keys()) {
127
+ if (!hasAnyMatch(key, positionsByHash)) {
128
+ warnings.push(formatWorkspaceStaleWarning(filePath, key));
129
+ }
130
+ }
131
+ for (const key of projectMap.keys()) {
132
+ if (map.has(key))
133
+ continue;
134
+ if (hasAnyMatch(key, positionsByHash)) {
135
+ globalMatchedRootKeys.add(key);
136
+ }
137
+ }
138
+ }
@@ -1,4 +1,4 @@
1
- import { type ReleaseKitConfig } from './types.js';
1
+ import { type ReleaseKitConfig } from './configSchema.js';
2
2
  /**
3
3
  * Validates a raw config object loaded from `.config/release-kit.config.ts`. Returns `{}` with the errors for a config
4
4
  * that fails the schema parse, and runs the cross-field checks on one that passes.
@@ -1,5 +1,5 @@
1
+ import { releaseKitConfigSchema } from "./configSchema.js";
1
2
  import { isRecord } from "./typeGuards.js";
2
- import { releaseKitConfigSchema } from "./types.js";
3
3
  export function validateConfig(raw) {
4
4
  if (!isRecord(raw)) {
5
5
  return { config: {}, errors: ['Config must be an object'], warnings: [] };
@@ -162,16 +162,14 @@ function detectReleasesConflicts(config, errors) {
162
162
  function listLegacyIdentityDeclarations(config) {
163
163
  const fromWorkspaces = (config.workspaces ?? []).flatMap((workspace, index) => workspace.legacyIdentities === undefined
164
164
  ? []
165
- : [
166
- {
167
- label: `workspaces[${index}]`,
168
- owner: `workspace '${workspace.dir}'`,
169
- legacyIdentities: workspace.legacyIdentities,
170
- },
171
- ]);
165
+ : {
166
+ label: `workspaces[${index}]`,
167
+ owner: `workspace '${workspace.dir}'`,
168
+ legacyIdentities: workspace.legacyIdentities,
169
+ });
172
170
  const fromReleases = Object.entries(config.releases ?? {}).flatMap(([name, entry]) => entry === false || !('path' in entry) || entry.legacyIdentities === undefined
173
171
  ? []
174
- : [{ label: `releases.${name}`, owner: `release '${name}'`, legacyIdentities: entry.legacyIdentities }]);
172
+ : { label: `releases.${name}`, owner: `release '${name}'`, legacyIdentities: entry.legacyIdentities });
175
173
  return [...fromWorkspaces, ...fromReleases];
176
174
  }
177
175
  function readAtPath(input, path) {
@@ -1,10 +1,10 @@
1
1
  import { type OutputStyle, type StreamStyles } from '@williamthorsen/nmr-core';
2
2
  import { type ReleaseHistoryConfig } from './buildChangelogEntries.js';
3
- import { type ValidateAllChangelogOverridesInputs, type ValidateAllChangelogOverridesResult } from './changelogOverrides.js';
4
3
  import { type WorkspaceDiscovery } from './discoverWorkspaces.js';
5
4
  import type { GenerateChangelogOptions } from './generateChangelogs.js';
6
5
  import { type LoadValidatedConfigResult } from './loadValidatedConfig.js';
7
6
  import type { ChangelogEntry } from './types.js';
7
+ import { type ValidateAllChangelogOverridesInputs, type ValidateAllChangelogOverridesResult } from './validateAllChangelogOverrides.js';
8
8
  /**
9
9
  * Result of {@link validateOverridesCommand}: a tiered exit code paired with a human-readable message.
10
10
  *
@@ -1,11 +1,12 @@
1
1
  import { formatErrorLine, formatStatusLine } from '@williamthorsen/nmr-core';
2
2
  import { describeError } from '@williamthorsen/toolbelt.errors';
3
3
  import { buildChangelogEntries } from "./buildChangelogEntries.js";
4
- import { resolveOverridePath, validateAllChangelogOverrides, } from "./changelogOverrides.js";
4
+ import { resolveOverridePath } from "./changelogOverrides.js";
5
5
  import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
6
6
  import { loadValidatedConfig } from "./loadValidatedConfig.js";
7
7
  import { describeHistoryWindow } from "./prepareTarget.js";
8
8
  import { isSinglePackageTargets, resolveReleaseTargets } from "./resolveReleaseTargets.js";
9
+ import { validateAllChangelogOverrides, } from "./validateAllChangelogOverrides.js";
9
10
  const SYNTHETIC_VALIDATE_TAG = 'validate-only';
10
11
  export async function validateOverridesCommand(styles, configPath, dependencies = {}) {
11
12
  const discover = dependencies.discoverWorkspaces ?? discoverWorkspaces;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "14.2.1",
3
+ "version": "14.2.3",
4
4
  "description": "Version-bumping and changelog-generation toolkit for release workflows",
5
5
  "keywords": [
6
6
  "changelog",
@@ -51,7 +51,7 @@
51
51
  ],
52
52
  "dependencies": {
53
53
  "@williamthorsen/change-grammar": "0.2.1",
54
- "@williamthorsen/nmr-core": "0.13.3",
54
+ "@williamthorsen/nmr-core": "0.13.4",
55
55
  "@williamthorsen/toolbelt.errors": "0.8.3",
56
56
  "json-stringify-pretty-compact": "4.0.0",
57
57
  "semver": "7.8.5",
@@ -65,8 +65,8 @@
65
65
  "@williamthorsen/toolbelt.vitest": "0.10.3",
66
66
  "actionlint": "2.0.6",
67
67
  "esbuild": "0.28.2",
68
- "readyup": "0.40.0",
69
- "smol-toml": "1.9.0",
68
+ "readyup": "0.42.0",
69
+ "smol-toml": "1.9.1",
70
70
  "vitest": "4.1.11"
71
71
  },
72
72
  "engines": {