@hyperfrontend/versioning 0.1.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/ARCHITECTURE.md +593 -0
- package/CHANGELOG.md +35 -0
- package/FUNDING.md +141 -0
- package/LICENSE.md +21 -0
- package/README.md +195 -0
- package/SECURITY.md +82 -0
- package/changelog/compare/diff.d.ts +128 -0
- package/changelog/compare/diff.d.ts.map +1 -0
- package/changelog/compare/index.cjs.js +628 -0
- package/changelog/compare/index.cjs.js.map +1 -0
- package/changelog/compare/index.d.ts +4 -0
- package/changelog/compare/index.d.ts.map +1 -0
- package/changelog/compare/index.esm.js +612 -0
- package/changelog/compare/index.esm.js.map +1 -0
- package/changelog/compare/is-equal.d.ts +114 -0
- package/changelog/compare/is-equal.d.ts.map +1 -0
- package/changelog/index.cjs.js +6448 -0
- package/changelog/index.cjs.js.map +1 -0
- package/changelog/index.d.ts +6 -0
- package/changelog/index.d.ts.map +1 -0
- package/changelog/index.esm.js +6358 -0
- package/changelog/index.esm.js.map +1 -0
- package/changelog/models/changelog.d.ts +86 -0
- package/changelog/models/changelog.d.ts.map +1 -0
- package/changelog/models/commit-ref.d.ts +51 -0
- package/changelog/models/commit-ref.d.ts.map +1 -0
- package/changelog/models/entry.d.ts +84 -0
- package/changelog/models/entry.d.ts.map +1 -0
- package/changelog/models/index.cjs.js +2043 -0
- package/changelog/models/index.cjs.js.map +1 -0
- package/changelog/models/index.d.ts +11 -0
- package/changelog/models/index.d.ts.map +1 -0
- package/changelog/models/index.esm.js +2026 -0
- package/changelog/models/index.esm.js.map +1 -0
- package/changelog/models/schema.d.ts +68 -0
- package/changelog/models/schema.d.ts.map +1 -0
- package/changelog/models/section.d.ts +25 -0
- package/changelog/models/section.d.ts.map +1 -0
- package/changelog/operations/add-entry.d.ts +56 -0
- package/changelog/operations/add-entry.d.ts.map +1 -0
- package/changelog/operations/add-item.d.ts +18 -0
- package/changelog/operations/add-item.d.ts.map +1 -0
- package/changelog/operations/filter-by-predicate.d.ts +81 -0
- package/changelog/operations/filter-by-predicate.d.ts.map +1 -0
- package/changelog/operations/filter-by-range.d.ts +63 -0
- package/changelog/operations/filter-by-range.d.ts.map +1 -0
- package/changelog/operations/filter-entries.d.ts +9 -0
- package/changelog/operations/filter-entries.d.ts.map +1 -0
- package/changelog/operations/index.cjs.js +2455 -0
- package/changelog/operations/index.cjs.js.map +1 -0
- package/changelog/operations/index.d.ts +15 -0
- package/changelog/operations/index.d.ts.map +1 -0
- package/changelog/operations/index.esm.js +2411 -0
- package/changelog/operations/index.esm.js.map +1 -0
- package/changelog/operations/merge.d.ts +88 -0
- package/changelog/operations/merge.d.ts.map +1 -0
- package/changelog/operations/remove-entry.d.ts +45 -0
- package/changelog/operations/remove-entry.d.ts.map +1 -0
- package/changelog/operations/remove-section.d.ts +50 -0
- package/changelog/operations/remove-section.d.ts.map +1 -0
- package/changelog/operations/transform.d.ts +143 -0
- package/changelog/operations/transform.d.ts.map +1 -0
- package/changelog/parse/index.cjs.js +1282 -0
- package/changelog/parse/index.cjs.js.map +1 -0
- package/changelog/parse/index.d.ts +5 -0
- package/changelog/parse/index.d.ts.map +1 -0
- package/changelog/parse/index.esm.js +1275 -0
- package/changelog/parse/index.esm.js.map +1 -0
- package/changelog/parse/line.d.ts +48 -0
- package/changelog/parse/line.d.ts.map +1 -0
- package/changelog/parse/parser.d.ts +16 -0
- package/changelog/parse/parser.d.ts.map +1 -0
- package/changelog/parse/tokenizer.d.ts +49 -0
- package/changelog/parse/tokenizer.d.ts.map +1 -0
- package/changelog/serialize/index.cjs.js +574 -0
- package/changelog/serialize/index.cjs.js.map +1 -0
- package/changelog/serialize/index.d.ts +6 -0
- package/changelog/serialize/index.d.ts.map +1 -0
- package/changelog/serialize/index.esm.js +564 -0
- package/changelog/serialize/index.esm.js.map +1 -0
- package/changelog/serialize/templates.d.ts +81 -0
- package/changelog/serialize/templates.d.ts.map +1 -0
- package/changelog/serialize/to-json.d.ts +57 -0
- package/changelog/serialize/to-json.d.ts.map +1 -0
- package/changelog/serialize/to-string.d.ts +30 -0
- package/changelog/serialize/to-string.d.ts.map +1 -0
- package/commits/index.cjs.js +648 -0
- package/commits/index.cjs.js.map +1 -0
- package/commits/index.d.ts +3 -0
- package/commits/index.d.ts.map +1 -0
- package/commits/index.esm.js +629 -0
- package/commits/index.esm.js.map +1 -0
- package/commits/models/breaking.d.ts +39 -0
- package/commits/models/breaking.d.ts.map +1 -0
- package/commits/models/commit-type.d.ts +32 -0
- package/commits/models/commit-type.d.ts.map +1 -0
- package/commits/models/conventional.d.ts +49 -0
- package/commits/models/conventional.d.ts.map +1 -0
- package/commits/models/index.cjs.js +207 -0
- package/commits/models/index.cjs.js.map +1 -0
- package/commits/models/index.d.ts +7 -0
- package/commits/models/index.d.ts.map +1 -0
- package/commits/models/index.esm.js +193 -0
- package/commits/models/index.esm.js.map +1 -0
- package/commits/parse/body.d.ts +18 -0
- package/commits/parse/body.d.ts.map +1 -0
- package/commits/parse/footer.d.ts +16 -0
- package/commits/parse/footer.d.ts.map +1 -0
- package/commits/parse/header.d.ts +15 -0
- package/commits/parse/header.d.ts.map +1 -0
- package/commits/parse/index.cjs.js +505 -0
- package/commits/parse/index.cjs.js.map +1 -0
- package/commits/parse/index.d.ts +5 -0
- package/commits/parse/index.d.ts.map +1 -0
- package/commits/parse/index.esm.js +499 -0
- package/commits/parse/index.esm.js.map +1 -0
- package/commits/parse/message.d.ts +17 -0
- package/commits/parse/message.d.ts.map +1 -0
- package/commits/utils/replace-char.d.ts +19 -0
- package/commits/utils/replace-char.d.ts.map +1 -0
- package/flow/executor/execute.d.ts +72 -0
- package/flow/executor/execute.d.ts.map +1 -0
- package/flow/executor/index.cjs.js +4402 -0
- package/flow/executor/index.cjs.js.map +1 -0
- package/flow/executor/index.d.ts +3 -0
- package/flow/executor/index.d.ts.map +1 -0
- package/flow/executor/index.esm.js +4398 -0
- package/flow/executor/index.esm.js.map +1 -0
- package/flow/factory.d.ts +58 -0
- package/flow/factory.d.ts.map +1 -0
- package/flow/index.cjs.js +8506 -0
- package/flow/index.cjs.js.map +1 -0
- package/flow/index.d.ts +7 -0
- package/flow/index.d.ts.map +1 -0
- package/flow/index.esm.js +8451 -0
- package/flow/index.esm.js.map +1 -0
- package/flow/models/flow.d.ts +130 -0
- package/flow/models/flow.d.ts.map +1 -0
- package/flow/models/index.cjs.js +285 -0
- package/flow/models/index.cjs.js.map +1 -0
- package/flow/models/index.d.ts +7 -0
- package/flow/models/index.d.ts.map +1 -0
- package/flow/models/index.esm.js +268 -0
- package/flow/models/index.esm.js.map +1 -0
- package/flow/models/step.d.ts +108 -0
- package/flow/models/step.d.ts.map +1 -0
- package/flow/models/types.d.ts +150 -0
- package/flow/models/types.d.ts.map +1 -0
- package/flow/presets/conventional.d.ts +59 -0
- package/flow/presets/conventional.d.ts.map +1 -0
- package/flow/presets/independent.d.ts +61 -0
- package/flow/presets/independent.d.ts.map +1 -0
- package/flow/presets/index.cjs.js +3903 -0
- package/flow/presets/index.cjs.js.map +1 -0
- package/flow/presets/index.d.ts +4 -0
- package/flow/presets/index.d.ts.map +1 -0
- package/flow/presets/index.esm.js +3889 -0
- package/flow/presets/index.esm.js.map +1 -0
- package/flow/presets/synced.d.ts +65 -0
- package/flow/presets/synced.d.ts.map +1 -0
- package/flow/steps/analyze-commits.d.ts +19 -0
- package/flow/steps/analyze-commits.d.ts.map +1 -0
- package/flow/steps/calculate-bump.d.ts +27 -0
- package/flow/steps/calculate-bump.d.ts.map +1 -0
- package/flow/steps/create-commit.d.ts +16 -0
- package/flow/steps/create-commit.d.ts.map +1 -0
- package/flow/steps/create-tag.d.ts +22 -0
- package/flow/steps/create-tag.d.ts.map +1 -0
- package/flow/steps/fetch-registry.d.ts +19 -0
- package/flow/steps/fetch-registry.d.ts.map +1 -0
- package/flow/steps/generate-changelog.d.ts +25 -0
- package/flow/steps/generate-changelog.d.ts.map +1 -0
- package/flow/steps/index.cjs.js +3523 -0
- package/flow/steps/index.cjs.js.map +1 -0
- package/flow/steps/index.d.ts +8 -0
- package/flow/steps/index.d.ts.map +1 -0
- package/flow/steps/index.esm.js +3504 -0
- package/flow/steps/index.esm.js.map +1 -0
- package/flow/steps/update-packages.d.ts +25 -0
- package/flow/steps/update-packages.d.ts.map +1 -0
- package/flow/utils/interpolate.d.ts +11 -0
- package/flow/utils/interpolate.d.ts.map +1 -0
- package/git/factory.d.ts +233 -0
- package/git/factory.d.ts.map +1 -0
- package/git/index.cjs.js +2863 -0
- package/git/index.cjs.js.map +1 -0
- package/git/index.d.ts +5 -0
- package/git/index.d.ts.map +1 -0
- package/git/index.esm.js +2785 -0
- package/git/index.esm.js.map +1 -0
- package/git/models/commit.d.ts +129 -0
- package/git/models/commit.d.ts.map +1 -0
- package/git/models/index.cjs.js +755 -0
- package/git/models/index.cjs.js.map +1 -0
- package/git/models/index.d.ts +7 -0
- package/git/models/index.d.ts.map +1 -0
- package/git/models/index.esm.js +729 -0
- package/git/models/index.esm.js.map +1 -0
- package/git/models/ref.d.ts +120 -0
- package/git/models/ref.d.ts.map +1 -0
- package/git/models/tag.d.ts +141 -0
- package/git/models/tag.d.ts.map +1 -0
- package/git/operations/commit.d.ts +97 -0
- package/git/operations/commit.d.ts.map +1 -0
- package/git/operations/head-info.d.ts +29 -0
- package/git/operations/head-info.d.ts.map +1 -0
- package/git/operations/index.cjs.js +1954 -0
- package/git/operations/index.cjs.js.map +1 -0
- package/git/operations/index.d.ts +14 -0
- package/git/operations/index.d.ts.map +1 -0
- package/git/operations/index.esm.js +1903 -0
- package/git/operations/index.esm.js.map +1 -0
- package/git/operations/log.d.ts +104 -0
- package/git/operations/log.d.ts.map +1 -0
- package/git/operations/manage-tags.d.ts +60 -0
- package/git/operations/manage-tags.d.ts.map +1 -0
- package/git/operations/query-tags.d.ts +88 -0
- package/git/operations/query-tags.d.ts.map +1 -0
- package/git/operations/stage.d.ts +66 -0
- package/git/operations/stage.d.ts.map +1 -0
- package/git/operations/status.d.ts +173 -0
- package/git/operations/status.d.ts.map +1 -0
- package/index.cjs.js +16761 -0
- package/index.cjs.js.map +1 -0
- package/index.d.ts +102 -0
- package/index.d.ts.map +1 -0
- package/index.esm.js +16427 -0
- package/index.esm.js.map +1 -0
- package/package.json +200 -0
- package/registry/factory.d.ts +18 -0
- package/registry/factory.d.ts.map +1 -0
- package/registry/index.cjs.js +543 -0
- package/registry/index.cjs.js.map +1 -0
- package/registry/index.d.ts +5 -0
- package/registry/index.d.ts.map +1 -0
- package/registry/index.esm.js +535 -0
- package/registry/index.esm.js.map +1 -0
- package/registry/models/index.cjs.js +69 -0
- package/registry/models/index.cjs.js.map +1 -0
- package/registry/models/index.d.ts +6 -0
- package/registry/models/index.d.ts.map +1 -0
- package/registry/models/index.esm.js +66 -0
- package/registry/models/index.esm.js.map +1 -0
- package/registry/models/package-info.d.ts +55 -0
- package/registry/models/package-info.d.ts.map +1 -0
- package/registry/models/registry.d.ts +62 -0
- package/registry/models/registry.d.ts.map +1 -0
- package/registry/models/version-info.d.ts +67 -0
- package/registry/models/version-info.d.ts.map +1 -0
- package/registry/npm/cache.d.ts +50 -0
- package/registry/npm/cache.d.ts.map +1 -0
- package/registry/npm/client.d.ts +30 -0
- package/registry/npm/client.d.ts.map +1 -0
- package/registry/npm/index.cjs.js +456 -0
- package/registry/npm/index.cjs.js.map +1 -0
- package/registry/npm/index.d.ts +4 -0
- package/registry/npm/index.d.ts.map +1 -0
- package/registry/npm/index.esm.js +451 -0
- package/registry/npm/index.esm.js.map +1 -0
- package/semver/compare/compare.d.ts +100 -0
- package/semver/compare/compare.d.ts.map +1 -0
- package/semver/compare/index.cjs.js +386 -0
- package/semver/compare/index.cjs.js.map +1 -0
- package/semver/compare/index.d.ts +3 -0
- package/semver/compare/index.d.ts.map +1 -0
- package/semver/compare/index.esm.js +370 -0
- package/semver/compare/index.esm.js.map +1 -0
- package/semver/compare/sort.d.ts +36 -0
- package/semver/compare/sort.d.ts.map +1 -0
- package/semver/format/index.cjs.js +58 -0
- package/semver/format/index.cjs.js.map +1 -0
- package/semver/format/index.d.ts +2 -0
- package/semver/format/index.d.ts.map +1 -0
- package/semver/format/index.esm.js +53 -0
- package/semver/format/index.esm.js.map +1 -0
- package/semver/format/to-string.d.ts +31 -0
- package/semver/format/to-string.d.ts.map +1 -0
- package/semver/increment/bump.d.ts +37 -0
- package/semver/increment/bump.d.ts.map +1 -0
- package/semver/increment/index.cjs.js +223 -0
- package/semver/increment/index.cjs.js.map +1 -0
- package/semver/increment/index.d.ts +2 -0
- package/semver/increment/index.d.ts.map +1 -0
- package/semver/increment/index.esm.js +219 -0
- package/semver/increment/index.esm.js.map +1 -0
- package/semver/index.cjs.js +1499 -0
- package/semver/index.cjs.js.map +1 -0
- package/semver/index.d.ts +6 -0
- package/semver/index.d.ts.map +1 -0
- package/semver/index.esm.js +1458 -0
- package/semver/index.esm.js.map +1 -0
- package/semver/models/index.cjs.js +153 -0
- package/semver/models/index.cjs.js.map +1 -0
- package/semver/models/index.d.ts +5 -0
- package/semver/models/index.d.ts.map +1 -0
- package/semver/models/index.esm.js +139 -0
- package/semver/models/index.esm.js.map +1 -0
- package/semver/models/range.d.ts +83 -0
- package/semver/models/range.d.ts.map +1 -0
- package/semver/models/version.d.ts +78 -0
- package/semver/models/version.d.ts.map +1 -0
- package/semver/parse/index.cjs.js +799 -0
- package/semver/parse/index.cjs.js.map +1 -0
- package/semver/parse/index.d.ts +5 -0
- package/semver/parse/index.d.ts.map +1 -0
- package/semver/parse/index.esm.js +793 -0
- package/semver/parse/index.esm.js.map +1 -0
- package/semver/parse/range.d.ts +38 -0
- package/semver/parse/range.d.ts.map +1 -0
- package/semver/parse/version.d.ts +49 -0
- package/semver/parse/version.d.ts.map +1 -0
- package/workspace/discovery/changelog-path.d.ts +21 -0
- package/workspace/discovery/changelog-path.d.ts.map +1 -0
- package/workspace/discovery/dependencies.d.ts +145 -0
- package/workspace/discovery/dependencies.d.ts.map +1 -0
- package/workspace/discovery/discover-changelogs.d.ts +76 -0
- package/workspace/discovery/discover-changelogs.d.ts.map +1 -0
- package/workspace/discovery/index.cjs.js +2300 -0
- package/workspace/discovery/index.cjs.js.map +1 -0
- package/workspace/discovery/index.d.ts +13 -0
- package/workspace/discovery/index.d.ts.map +1 -0
- package/workspace/discovery/index.esm.js +2283 -0
- package/workspace/discovery/index.esm.js.map +1 -0
- package/workspace/discovery/packages.d.ts +83 -0
- package/workspace/discovery/packages.d.ts.map +1 -0
- package/workspace/index.cjs.js +4445 -0
- package/workspace/index.cjs.js.map +1 -0
- package/workspace/index.d.ts +52 -0
- package/workspace/index.d.ts.map +1 -0
- package/workspace/index.esm.js +4394 -0
- package/workspace/index.esm.js.map +1 -0
- package/workspace/models/index.cjs.js +284 -0
- package/workspace/models/index.cjs.js.map +1 -0
- package/workspace/models/index.d.ts +10 -0
- package/workspace/models/index.d.ts.map +1 -0
- package/workspace/models/index.esm.js +261 -0
- package/workspace/models/index.esm.js.map +1 -0
- package/workspace/models/project.d.ts +118 -0
- package/workspace/models/project.d.ts.map +1 -0
- package/workspace/models/workspace.d.ts +139 -0
- package/workspace/models/workspace.d.ts.map +1 -0
- package/workspace/operations/batch-update.d.ts +99 -0
- package/workspace/operations/batch-update.d.ts.map +1 -0
- package/workspace/operations/cascade-bump.d.ts +125 -0
- package/workspace/operations/cascade-bump.d.ts.map +1 -0
- package/workspace/operations/index.cjs.js +2675 -0
- package/workspace/operations/index.cjs.js.map +1 -0
- package/workspace/operations/index.d.ts +12 -0
- package/workspace/operations/index.d.ts.map +1 -0
- package/workspace/operations/index.esm.js +2663 -0
- package/workspace/operations/index.esm.js.map +1 -0
- package/workspace/operations/validate.d.ts +85 -0
- package/workspace/operations/validate.d.ts.map +1 -0
|
@@ -0,0 +1,2043 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new changelog with default values.
|
|
5
|
+
*
|
|
6
|
+
* @param options - Optional configuration to customize the changelog
|
|
7
|
+
* @returns A new Changelog object with the specified options or defaults
|
|
8
|
+
*/
|
|
9
|
+
function createChangelog(options) {
|
|
10
|
+
return {
|
|
11
|
+
source: options?.source,
|
|
12
|
+
header: options?.header ?? {
|
|
13
|
+
title: '# Changelog',
|
|
14
|
+
description: [],
|
|
15
|
+
links: [],
|
|
16
|
+
},
|
|
17
|
+
entries: options?.entries ?? [],
|
|
18
|
+
metadata: options?.metadata ?? {
|
|
19
|
+
format: 'unknown',
|
|
20
|
+
isConventional: false,
|
|
21
|
+
warnings: [],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new empty changelog with standard header.
|
|
27
|
+
*
|
|
28
|
+
* @returns A new empty Changelog with Keep a Changelog format
|
|
29
|
+
*/
|
|
30
|
+
function createEmptyChangelog() {
|
|
31
|
+
return {
|
|
32
|
+
header: {
|
|
33
|
+
title: '# Changelog',
|
|
34
|
+
description: [
|
|
35
|
+
'All notable changes to this project will be documented in this file.',
|
|
36
|
+
'',
|
|
37
|
+
'The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),',
|
|
38
|
+
'and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).',
|
|
39
|
+
],
|
|
40
|
+
links: [],
|
|
41
|
+
},
|
|
42
|
+
entries: [],
|
|
43
|
+
metadata: {
|
|
44
|
+
format: 'keep-a-changelog',
|
|
45
|
+
isConventional: false,
|
|
46
|
+
warnings: [],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Creates a changelog link.
|
|
52
|
+
*
|
|
53
|
+
* @param label - The display text for the link
|
|
54
|
+
* @param url - The URL the link points to
|
|
55
|
+
* @returns A new ChangelogLink object
|
|
56
|
+
*/
|
|
57
|
+
function createChangelogLink(label, url) {
|
|
58
|
+
return { label, url };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Creates a new changelog item.
|
|
63
|
+
*
|
|
64
|
+
* @param description - The description text of the change
|
|
65
|
+
* @param options - Optional configuration for scope, commits, references, and breaking flag
|
|
66
|
+
* @returns A new ChangelogItem object
|
|
67
|
+
*/
|
|
68
|
+
function createChangelogItem(description, options) {
|
|
69
|
+
return {
|
|
70
|
+
description,
|
|
71
|
+
scope: options?.scope,
|
|
72
|
+
commits: options?.commits ?? [],
|
|
73
|
+
references: options?.references ?? [],
|
|
74
|
+
breaking: options?.breaking ?? false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Creates a new changelog section.
|
|
79
|
+
*
|
|
80
|
+
* @param type - The type of section (features, fixes, breaking, etc.)
|
|
81
|
+
* @param heading - The display heading for the section
|
|
82
|
+
* @param items - Optional array of changelog items in this section
|
|
83
|
+
* @returns A new ChangelogSection object
|
|
84
|
+
*/
|
|
85
|
+
function createChangelogSection(type, heading, items = []) {
|
|
86
|
+
return {
|
|
87
|
+
type,
|
|
88
|
+
heading,
|
|
89
|
+
items,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Creates a new changelog entry.
|
|
94
|
+
*
|
|
95
|
+
* @param version - The version string (e.g., '1.0.0')
|
|
96
|
+
* @param options - Optional configuration for date, sections, and other properties
|
|
97
|
+
* @returns A new ChangelogEntry object
|
|
98
|
+
*/
|
|
99
|
+
function createChangelogEntry(version, options) {
|
|
100
|
+
return {
|
|
101
|
+
version,
|
|
102
|
+
date: options?.date ?? null,
|
|
103
|
+
unreleased: options?.unreleased ?? false,
|
|
104
|
+
compareUrl: options?.compareUrl,
|
|
105
|
+
sections: options?.sections ?? [],
|
|
106
|
+
rawContent: options?.rawContent,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Creates an unreleased changelog entry.
|
|
111
|
+
*
|
|
112
|
+
* @param sections - Optional array of changelog sections
|
|
113
|
+
* @returns A new ChangelogEntry object marked as unreleased
|
|
114
|
+
*/
|
|
115
|
+
function createUnreleasedEntry(sections = []) {
|
|
116
|
+
return {
|
|
117
|
+
version: 'Unreleased',
|
|
118
|
+
date: null,
|
|
119
|
+
unreleased: true,
|
|
120
|
+
sections,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Maps section headings to their canonical types.
|
|
126
|
+
* Used during parsing to normalize different heading styles.
|
|
127
|
+
*/
|
|
128
|
+
const SECTION_TYPE_MAP = {
|
|
129
|
+
// Breaking changes
|
|
130
|
+
'breaking changes': 'breaking',
|
|
131
|
+
breaking: 'breaking',
|
|
132
|
+
'breaking change': 'breaking',
|
|
133
|
+
// Features
|
|
134
|
+
features: 'features',
|
|
135
|
+
feature: 'features',
|
|
136
|
+
added: 'features',
|
|
137
|
+
new: 'features',
|
|
138
|
+
// Fixes
|
|
139
|
+
fixes: 'fixes',
|
|
140
|
+
fix: 'fixes',
|
|
141
|
+
'bug fixes': 'fixes',
|
|
142
|
+
bugfixes: 'fixes',
|
|
143
|
+
fixed: 'fixes',
|
|
144
|
+
// Performance
|
|
145
|
+
performance: 'performance',
|
|
146
|
+
'performance improvements': 'performance',
|
|
147
|
+
perf: 'performance',
|
|
148
|
+
// Documentation
|
|
149
|
+
documentation: 'documentation',
|
|
150
|
+
docs: 'documentation',
|
|
151
|
+
// Deprecations
|
|
152
|
+
deprecations: 'deprecations',
|
|
153
|
+
deprecated: 'deprecations',
|
|
154
|
+
// Refactoring
|
|
155
|
+
refactoring: 'refactoring',
|
|
156
|
+
refactor: 'refactoring',
|
|
157
|
+
'code refactoring': 'refactoring',
|
|
158
|
+
// Tests
|
|
159
|
+
tests: 'tests',
|
|
160
|
+
test: 'tests',
|
|
161
|
+
testing: 'tests',
|
|
162
|
+
// Build
|
|
163
|
+
build: 'build',
|
|
164
|
+
'build system': 'build',
|
|
165
|
+
dependencies: 'build',
|
|
166
|
+
// CI
|
|
167
|
+
ci: 'ci',
|
|
168
|
+
'continuous integration': 'ci',
|
|
169
|
+
// Chores
|
|
170
|
+
chores: 'chores',
|
|
171
|
+
chore: 'chores',
|
|
172
|
+
maintenance: 'chores',
|
|
173
|
+
// Other
|
|
174
|
+
other: 'other',
|
|
175
|
+
miscellaneous: 'other',
|
|
176
|
+
misc: 'other',
|
|
177
|
+
changed: 'other',
|
|
178
|
+
changes: 'other',
|
|
179
|
+
removed: 'other',
|
|
180
|
+
security: 'other',
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Standard section headings for serialization.
|
|
184
|
+
* Maps section types to their preferred heading text.
|
|
185
|
+
*/
|
|
186
|
+
const SECTION_HEADINGS = {
|
|
187
|
+
breaking: 'Breaking Changes',
|
|
188
|
+
features: 'Features',
|
|
189
|
+
fixes: 'Bug Fixes',
|
|
190
|
+
performance: 'Performance',
|
|
191
|
+
documentation: 'Documentation',
|
|
192
|
+
deprecations: 'Deprecations',
|
|
193
|
+
refactoring: 'Refactoring',
|
|
194
|
+
tests: 'Tests',
|
|
195
|
+
build: 'Build',
|
|
196
|
+
ci: 'CI',
|
|
197
|
+
chores: 'Chores',
|
|
198
|
+
other: 'Other',
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Determines the section type from a heading string.
|
|
202
|
+
* Returns 'other' if the heading is not recognized.
|
|
203
|
+
*
|
|
204
|
+
* @param heading - The heading string to parse
|
|
205
|
+
* @returns The corresponding ChangelogSectionType
|
|
206
|
+
*/
|
|
207
|
+
function getSectionType(heading) {
|
|
208
|
+
const normalized = heading.toLowerCase().trim();
|
|
209
|
+
return SECTION_TYPE_MAP[normalized] ?? 'other';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Creates a commit reference from a full hash.
|
|
214
|
+
*
|
|
215
|
+
* @param hash - The full commit hash
|
|
216
|
+
* @param url - Optional URL to the commit
|
|
217
|
+
* @returns A new CommitRef object with both full and short hash
|
|
218
|
+
*/
|
|
219
|
+
function createCommitRef(hash, url) {
|
|
220
|
+
return {
|
|
221
|
+
hash,
|
|
222
|
+
shortHash: hash.slice(0, 7),
|
|
223
|
+
url,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Creates an issue reference.
|
|
228
|
+
*
|
|
229
|
+
* @param number - The issue or PR number
|
|
230
|
+
* @param type - The type of reference ('issue' or 'pull-request')
|
|
231
|
+
* @param url - Optional URL to the issue or PR
|
|
232
|
+
* @returns A new IssueRef object
|
|
233
|
+
*/
|
|
234
|
+
function createIssueRef(number, type = 'issue', url) {
|
|
235
|
+
return {
|
|
236
|
+
number,
|
|
237
|
+
type,
|
|
238
|
+
url,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Extracts the short hash from a full commit hash.
|
|
243
|
+
*
|
|
244
|
+
* @param hash - The full commit hash
|
|
245
|
+
* @returns The first 7 characters of the hash
|
|
246
|
+
*/
|
|
247
|
+
function getShortHash(hash) {
|
|
248
|
+
return hash.slice(0, 7);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Safe copies of Set built-in via factory function.
|
|
253
|
+
*
|
|
254
|
+
* Since constructors cannot be safely captured via Object.assign, this module
|
|
255
|
+
* provides a factory function that uses Reflect.construct internally.
|
|
256
|
+
*
|
|
257
|
+
* These references are captured at module initialization time to protect against
|
|
258
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
259
|
+
*
|
|
260
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/set
|
|
261
|
+
*/
|
|
262
|
+
// Capture references at module initialization time
|
|
263
|
+
const _Set = globalThis.Set;
|
|
264
|
+
const _Reflect$5 = globalThis.Reflect;
|
|
265
|
+
/**
|
|
266
|
+
* (Safe copy) Creates a new Set using the captured Set constructor.
|
|
267
|
+
* Use this instead of `new Set()`.
|
|
268
|
+
*
|
|
269
|
+
* @param iterable - Optional iterable of values.
|
|
270
|
+
* @returns A new Set instance.
|
|
271
|
+
*/
|
|
272
|
+
const createSet = (iterable) => _Reflect$5.construct(_Set, iterable ? [iterable] : []);
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Safe copies of Array built-in static methods.
|
|
276
|
+
*
|
|
277
|
+
* These references are captured at module initialization time to protect against
|
|
278
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
279
|
+
*
|
|
280
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/array
|
|
281
|
+
*/
|
|
282
|
+
// Capture references at module initialization time
|
|
283
|
+
const _Array = globalThis.Array;
|
|
284
|
+
/**
|
|
285
|
+
* (Safe copy) Determines whether the passed value is an Array.
|
|
286
|
+
*/
|
|
287
|
+
const isArray = _Array.isArray;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Safe copies of Map built-in via factory function.
|
|
291
|
+
*
|
|
292
|
+
* Since constructors cannot be safely captured via Object.assign, this module
|
|
293
|
+
* provides a factory function that uses Reflect.construct internally.
|
|
294
|
+
*
|
|
295
|
+
* These references are captured at module initialization time to protect against
|
|
296
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
297
|
+
*
|
|
298
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/map
|
|
299
|
+
*/
|
|
300
|
+
// Capture references at module initialization time
|
|
301
|
+
const _Map = globalThis.Map;
|
|
302
|
+
const _Reflect$4 = globalThis.Reflect;
|
|
303
|
+
/**
|
|
304
|
+
* (Safe copy) Creates a new Map using the captured Map constructor.
|
|
305
|
+
* Use this instead of `new Map()`.
|
|
306
|
+
*
|
|
307
|
+
* @param iterable - Optional iterable of key-value pairs.
|
|
308
|
+
* @returns A new Map instance.
|
|
309
|
+
*/
|
|
310
|
+
const createMap = (iterable) => _Reflect$4.construct(_Map, iterable ? [iterable] : []);
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Safe copies of Object built-in methods.
|
|
314
|
+
*
|
|
315
|
+
* These references are captured at module initialization time to protect against
|
|
316
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
317
|
+
*
|
|
318
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/object
|
|
319
|
+
*/
|
|
320
|
+
// Capture references at module initialization time
|
|
321
|
+
const _Object = globalThis.Object;
|
|
322
|
+
const _Reflect$3 = globalThis.Reflect;
|
|
323
|
+
const _ObjectPrototype = _Object.prototype;
|
|
324
|
+
const _hasOwnProperty = _ObjectPrototype.hasOwnProperty;
|
|
325
|
+
/**
|
|
326
|
+
* (Safe copy) Returns the names of the enumerable string properties and methods of an object.
|
|
327
|
+
*/
|
|
328
|
+
const keys = _Object.keys;
|
|
329
|
+
/**
|
|
330
|
+
* (Safe copy) Returns an array of key/values of the enumerable own properties of an object.
|
|
331
|
+
*/
|
|
332
|
+
const entries = _Object.entries;
|
|
333
|
+
/**
|
|
334
|
+
* (Safe copy) Adds a property to an object, or modifies attributes of an existing property.
|
|
335
|
+
*/
|
|
336
|
+
const defineProperty = _Object.defineProperty;
|
|
337
|
+
/**
|
|
338
|
+
* (Safe copy) Safe wrapper for Object.prototype.hasOwnProperty.call().
|
|
339
|
+
* Checks if an object has a property as its own (not inherited) property.
|
|
340
|
+
*
|
|
341
|
+
* @param obj - The object to check.
|
|
342
|
+
* @param key - The property key to check.
|
|
343
|
+
* @returns True if the object has the property as its own property.
|
|
344
|
+
*/
|
|
345
|
+
const hasOwn = (obj, key) => _Reflect$3.apply(_hasOwnProperty, obj, [key]);
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Creates a new validation context.
|
|
349
|
+
*
|
|
350
|
+
* @param rootSchema - The root schema being validated against
|
|
351
|
+
* @param validator - The schema validator function
|
|
352
|
+
* @param collectAllErrors - Whether to collect all errors (default: true)
|
|
353
|
+
* @param strictPatterns - Whether to report errors for invalid regex patterns (default: false)
|
|
354
|
+
* @param patternSafetyChecker - Optional pattern safety checker for ReDoS detection
|
|
355
|
+
* @returns A new validation context
|
|
356
|
+
*/
|
|
357
|
+
function createValidationContext(rootSchema, validator, collectAllErrors = true, strictPatterns = false, patternSafetyChecker) {
|
|
358
|
+
const definitions = createMap();
|
|
359
|
+
// Pre-populate definitions from root schema
|
|
360
|
+
if (rootSchema.definitions) {
|
|
361
|
+
for (const [name, schema] of entries(rootSchema.definitions)) {
|
|
362
|
+
definitions.set(`#/definitions/${name}`, schema);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
path: '',
|
|
367
|
+
errors: [],
|
|
368
|
+
rootSchema,
|
|
369
|
+
definitions,
|
|
370
|
+
collectAllErrors,
|
|
371
|
+
strictPatterns,
|
|
372
|
+
patternSafetyChecker,
|
|
373
|
+
validate: validator,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Creates a child context with updated path.
|
|
378
|
+
*
|
|
379
|
+
* @param ctx - Parent context
|
|
380
|
+
* @param segment - Path segment to append
|
|
381
|
+
* @returns New context with updated path
|
|
382
|
+
*/
|
|
383
|
+
function pushPath(ctx, segment) {
|
|
384
|
+
const escapedSegment = String(segment).replace(/~/g, '~0').replace(/\//g, '~1');
|
|
385
|
+
return {
|
|
386
|
+
...ctx,
|
|
387
|
+
path: `${ctx.path}/${escapedSegment}`,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Adds a validation error to the context.
|
|
392
|
+
*
|
|
393
|
+
* @param ctx - Validation context
|
|
394
|
+
* @param message - Human-readable error message
|
|
395
|
+
* @param instance - The failing value
|
|
396
|
+
* @param code - Optional error code for programmatic handling
|
|
397
|
+
* @param params - Optional additional parameters
|
|
398
|
+
*/
|
|
399
|
+
function addError(ctx, message, instance, code, params) {
|
|
400
|
+
ctx.errors.push({
|
|
401
|
+
message,
|
|
402
|
+
path: ctx.path || '/',
|
|
403
|
+
instance,
|
|
404
|
+
code,
|
|
405
|
+
params,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Checks if we should continue validation after an error.
|
|
410
|
+
*
|
|
411
|
+
* @param ctx - Validation context
|
|
412
|
+
* @returns true if we should continue, false if we should stop
|
|
413
|
+
*/
|
|
414
|
+
function shouldContinue(ctx) {
|
|
415
|
+
return ctx.collectAllErrors || ctx.errors.length === 0;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Performs deep equality check for JSON values.
|
|
420
|
+
*
|
|
421
|
+
* Used for enum validation and uniqueItems validation.
|
|
422
|
+
*
|
|
423
|
+
* @param a - First value to compare
|
|
424
|
+
* @param b - Second value to compare
|
|
425
|
+
* @returns true if values are deeply equal, false otherwise
|
|
426
|
+
*/
|
|
427
|
+
function isEqual(a, b) {
|
|
428
|
+
if (a === b)
|
|
429
|
+
return true;
|
|
430
|
+
if (a === null || b === null)
|
|
431
|
+
return false;
|
|
432
|
+
if (typeof a !== typeof b)
|
|
433
|
+
return false;
|
|
434
|
+
if (typeof a === 'object') {
|
|
435
|
+
if (isArray(a) && isArray(b)) {
|
|
436
|
+
if (a.length !== b.length)
|
|
437
|
+
return false;
|
|
438
|
+
for (let i = 0; i < a.length; i++) {
|
|
439
|
+
if (!isEqual(a[i], b[i]))
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
if (isArray(a) || isArray(b))
|
|
445
|
+
return false;
|
|
446
|
+
const keysA = keys(a);
|
|
447
|
+
const keysB = keys(b);
|
|
448
|
+
if (keysA.length !== keysB.length)
|
|
449
|
+
return false;
|
|
450
|
+
for (const key of keysA) {
|
|
451
|
+
if (!hasOwn(b, key))
|
|
452
|
+
return false;
|
|
453
|
+
if (!isEqual(a[key], b[key]))
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Validates array length and uniqueItems constraints.
|
|
463
|
+
*
|
|
464
|
+
* @param instance - Array being validated
|
|
465
|
+
* @param schema - Schema containing array bounds
|
|
466
|
+
* @param ctx - Validation context
|
|
467
|
+
* @returns true if validation passes, false otherwise
|
|
468
|
+
*/
|
|
469
|
+
function validateArrayBounds(instance, schema, ctx) {
|
|
470
|
+
let valid = true;
|
|
471
|
+
if (schema.minItems !== undefined && instance.length < schema.minItems) {
|
|
472
|
+
addError(ctx, `Array must have at least ${schema.minItems} items, got ${instance.length}`, instance, 'minItems', {
|
|
473
|
+
limit: schema.minItems,
|
|
474
|
+
actual: instance.length,
|
|
475
|
+
});
|
|
476
|
+
valid = false;
|
|
477
|
+
if (!shouldContinue(ctx))
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
if (schema.maxItems !== undefined && instance.length > schema.maxItems) {
|
|
481
|
+
addError(ctx, `Array must have at most ${schema.maxItems} items, got ${instance.length}`, instance, 'maxItems', {
|
|
482
|
+
limit: schema.maxItems,
|
|
483
|
+
actual: instance.length,
|
|
484
|
+
});
|
|
485
|
+
valid = false;
|
|
486
|
+
if (!shouldContinue(ctx))
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
if (schema.uniqueItems === true) {
|
|
490
|
+
// Check for duplicates
|
|
491
|
+
for (let i = 0; i < instance.length; i++) {
|
|
492
|
+
for (let j = i + 1; j < instance.length; j++) {
|
|
493
|
+
if (isEqual(instance[i], instance[j])) {
|
|
494
|
+
addError(ctx, `Array items must be unique. Duplicate found at indices ${i} and ${j}`, instance, 'uniqueItems');
|
|
495
|
+
valid = false;
|
|
496
|
+
if (!shouldContinue(ctx))
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return valid;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Validates 'allOf' keyword - all schemas must match.
|
|
507
|
+
*
|
|
508
|
+
* @param instance - Value being validated
|
|
509
|
+
* @param schema - Schema containing the allOf constraint
|
|
510
|
+
* @param ctx - Validation context
|
|
511
|
+
* @returns true if validation passes, false otherwise
|
|
512
|
+
*/
|
|
513
|
+
function validateAllOf(instance, schema, ctx) {
|
|
514
|
+
const allOf = schema.allOf;
|
|
515
|
+
if (!allOf || allOf.length === 0) {
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
let valid = true;
|
|
519
|
+
for (let i = 0; i < allOf.length; i++) {
|
|
520
|
+
const subSchema = allOf[i];
|
|
521
|
+
/* istanbul ignore if -- defensive null check for sparse arrays */
|
|
522
|
+
if (!subSchema)
|
|
523
|
+
continue;
|
|
524
|
+
if (!ctx.validate(instance, subSchema, ctx)) {
|
|
525
|
+
valid = false;
|
|
526
|
+
if (!shouldContinue(ctx))
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return valid;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Validates 'anyOf' keyword - at least one schema must match.
|
|
534
|
+
*
|
|
535
|
+
* @param instance - Value being validated
|
|
536
|
+
* @param schema - Schema containing the anyOf constraint
|
|
537
|
+
* @param ctx - Validation context
|
|
538
|
+
* @returns true if validation passes, false otherwise
|
|
539
|
+
*/
|
|
540
|
+
function validateAnyOf(instance, schema, ctx) {
|
|
541
|
+
const anyOf = schema.anyOf;
|
|
542
|
+
if (!anyOf || anyOf.length === 0) {
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
// Try each schema - if any matches, we pass
|
|
546
|
+
for (const subSchema of anyOf) {
|
|
547
|
+
const subCtx = createValidationContext(ctx.rootSchema, ctx.validate, false);
|
|
548
|
+
// Copy path from parent context
|
|
549
|
+
defineProperty(subCtx, 'path', { value: ctx.path, writable: false });
|
|
550
|
+
if (ctx.validate(instance, subSchema, subCtx)) {
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
addError(ctx, 'Value does not match any of the allowed schemas (anyOf)', instance, 'anyOf');
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Validates 'oneOf' keyword - exactly one schema must match.
|
|
559
|
+
*
|
|
560
|
+
* @param instance - Value being validated
|
|
561
|
+
* @param schema - Schema containing the oneOf constraint
|
|
562
|
+
* @param ctx - Validation context
|
|
563
|
+
* @returns true if validation passes, false otherwise
|
|
564
|
+
*/
|
|
565
|
+
function validateOneOf(instance, schema, ctx) {
|
|
566
|
+
const oneOf = schema.oneOf;
|
|
567
|
+
if (!oneOf || oneOf.length === 0) {
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
let matchCount = 0;
|
|
571
|
+
for (const subSchema of oneOf) {
|
|
572
|
+
const subCtx = createValidationContext(ctx.rootSchema, ctx.validate, false);
|
|
573
|
+
defineProperty(subCtx, 'path', { value: ctx.path, writable: false });
|
|
574
|
+
if (ctx.validate(instance, subSchema, subCtx)) {
|
|
575
|
+
matchCount++;
|
|
576
|
+
if (matchCount > 1)
|
|
577
|
+
break; // Early exit if more than one match
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (matchCount === 1) {
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
if (matchCount === 0) {
|
|
584
|
+
addError(ctx, 'Value does not match any of the schemas (oneOf)', instance, 'oneOf');
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
addError(ctx, `Value matches ${matchCount} schemas but must match exactly one (oneOf)`, instance, 'oneOf', {
|
|
588
|
+
matches: matchCount,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Validates 'not' keyword - schema must NOT match.
|
|
595
|
+
*
|
|
596
|
+
* @param instance - Value being validated
|
|
597
|
+
* @param schema - Schema containing the not constraint
|
|
598
|
+
* @param ctx - Validation context
|
|
599
|
+
* @returns true if validation passes, false otherwise
|
|
600
|
+
*/
|
|
601
|
+
function validateNot(instance, schema, ctx) {
|
|
602
|
+
const not = schema.not;
|
|
603
|
+
if (!not) {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
const subCtx = createValidationContext(ctx.rootSchema, ctx.validate, false);
|
|
607
|
+
defineProperty(subCtx, 'path', { value: ctx.path, writable: false });
|
|
608
|
+
if (ctx.validate(instance, not, subCtx)) {
|
|
609
|
+
// Schema matched when it shouldn't have
|
|
610
|
+
addError(ctx, 'Value should NOT match the schema', instance, 'not');
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
return true;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Validates object 'dependencies' keyword.
|
|
618
|
+
*
|
|
619
|
+
* @param instance - Object being validated
|
|
620
|
+
* @param schema - Schema containing the dependencies constraint
|
|
621
|
+
* @param ctx - Validation context
|
|
622
|
+
* @returns true if validation passes, false otherwise
|
|
623
|
+
*/
|
|
624
|
+
function validateDependencies(instance, schema, ctx) {
|
|
625
|
+
if (!schema.dependencies) {
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
let valid = true;
|
|
629
|
+
for (const [key, dependency] of entries(schema.dependencies)) {
|
|
630
|
+
// Only check dependency if the key is present
|
|
631
|
+
/* istanbul ignore next -- key presence check */
|
|
632
|
+
if (!hasOwn(instance, key)) {
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
/* istanbul ignore next -- dependency type check */
|
|
636
|
+
if (isArray(dependency)) {
|
|
637
|
+
// Property dependency: if key is present, these properties must also be present
|
|
638
|
+
for (const requiredKey of dependency) {
|
|
639
|
+
/* istanbul ignore next -- required key check */
|
|
640
|
+
if (!hasOwn(instance, requiredKey)) {
|
|
641
|
+
addError(ctx, `Property '${key}' requires property '${requiredKey}' to also be present`, instance, 'dependencies', {
|
|
642
|
+
property: key,
|
|
643
|
+
/* istanbul ignore next -- required key assignment */
|
|
644
|
+
required: requiredKey,
|
|
645
|
+
});
|
|
646
|
+
valid = false;
|
|
647
|
+
/* istanbul ignore if -- early exit tested in validate.spec.ts */
|
|
648
|
+
if (!shouldContinue(ctx))
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
// Schema dependency: if key is present, the object must also validate against this schema
|
|
655
|
+
/* istanbul ignore next -- schema dependency validation */
|
|
656
|
+
if (!ctx.validate(instance, dependency, ctx)) {
|
|
657
|
+
/* istanbul ignore next -- failure path */
|
|
658
|
+
valid = false;
|
|
659
|
+
/* istanbul ignore if -- early exit tested in validate.spec.ts */
|
|
660
|
+
if (!shouldContinue(ctx))
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return valid;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Safe copies of JSON built-in methods.
|
|
670
|
+
*
|
|
671
|
+
* These references are captured at module initialization time to protect against
|
|
672
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
673
|
+
*
|
|
674
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/json
|
|
675
|
+
*/
|
|
676
|
+
// Capture references at module initialization time
|
|
677
|
+
const _JSON = globalThis.JSON;
|
|
678
|
+
/**
|
|
679
|
+
* (Safe copy) Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
|
|
680
|
+
*/
|
|
681
|
+
const stringify = _JSON.stringify;
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Validates enum constraint.
|
|
685
|
+
*
|
|
686
|
+
* @param instance - Value being validated
|
|
687
|
+
* @param schema - Schema containing the enum constraint
|
|
688
|
+
* @param ctx - Validation context
|
|
689
|
+
* @returns true if validation passes, false otherwise
|
|
690
|
+
*/
|
|
691
|
+
function validateEnum(instance, schema, ctx) {
|
|
692
|
+
if (!schema.enum) {
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
for (const enumValue of schema.enum) {
|
|
696
|
+
if (isEqual(instance, enumValue)) {
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
const allowedValues = schema.enum.map((v) => stringify(v)).join(', ');
|
|
701
|
+
addError(ctx, `Value must be one of: ${allowedValues}`, instance, 'enum', {
|
|
702
|
+
allowedValues: schema.enum,
|
|
703
|
+
});
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Safe copies of Date built-in via factory function and static methods.
|
|
709
|
+
*
|
|
710
|
+
* Since constructors cannot be safely captured via Object.assign, this module
|
|
711
|
+
* provides a factory function that uses Reflect.construct internally.
|
|
712
|
+
*
|
|
713
|
+
* These references are captured at module initialization time to protect against
|
|
714
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
715
|
+
*
|
|
716
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/date
|
|
717
|
+
*/
|
|
718
|
+
// Capture references at module initialization time
|
|
719
|
+
const _Date = globalThis.Date;
|
|
720
|
+
const _Reflect$2 = globalThis.Reflect;
|
|
721
|
+
function createDate(...args) {
|
|
722
|
+
return _Reflect$2.construct(_Date, args);
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* (Safe copy) Parses a string representation of a date.
|
|
726
|
+
*/
|
|
727
|
+
const dateParse = _Date.parse;
|
|
728
|
+
/**
|
|
729
|
+
* (Safe copy) Returns the number of milliseconds in a Date object since January 1, 1970 UTC.
|
|
730
|
+
*/
|
|
731
|
+
const dateUTC = _Date.UTC;
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Safe copies of Number built-in methods and constants.
|
|
735
|
+
*
|
|
736
|
+
* These references are captured at module initialization time to protect against
|
|
737
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
738
|
+
*
|
|
739
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/number
|
|
740
|
+
*/
|
|
741
|
+
// Capture references at module initialization time
|
|
742
|
+
const _Number = globalThis.Number;
|
|
743
|
+
const _parseInt = globalThis.parseInt;
|
|
744
|
+
const _isNaN = globalThis.isNaN;
|
|
745
|
+
const _isFinite = globalThis.isFinite;
|
|
746
|
+
/**
|
|
747
|
+
* (Safe copy) Determines whether the passed value is an integer.
|
|
748
|
+
*/
|
|
749
|
+
const isInteger = _Number.isInteger;
|
|
750
|
+
// ============================================================================
|
|
751
|
+
// Parsing
|
|
752
|
+
// ============================================================================
|
|
753
|
+
/**
|
|
754
|
+
* (Safe copy) Parses a string and returns an integer.
|
|
755
|
+
*/
|
|
756
|
+
const parseInt = _parseInt;
|
|
757
|
+
// ============================================================================
|
|
758
|
+
// Global Type Checking (legacy, less strict)
|
|
759
|
+
// ============================================================================
|
|
760
|
+
/**
|
|
761
|
+
* (Safe copy) Global isNaN function (coerces to number first, less strict than Number.isNaN).
|
|
762
|
+
*/
|
|
763
|
+
const globalIsNaN = _isNaN;
|
|
764
|
+
/**
|
|
765
|
+
* (Safe copy) Global isFinite function (coerces to number first, less strict than Number.isFinite).
|
|
766
|
+
*/
|
|
767
|
+
const globalIsFinite = _isFinite;
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Safe copies of RegExp built-in via factory function.
|
|
771
|
+
*
|
|
772
|
+
* Since constructors cannot be safely captured via Object.assign, this module
|
|
773
|
+
* provides a factory function that uses Reflect.construct internally.
|
|
774
|
+
*
|
|
775
|
+
* These references are captured at module initialization time to protect against
|
|
776
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
777
|
+
*
|
|
778
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/regexp
|
|
779
|
+
*/
|
|
780
|
+
// Capture references at module initialization time
|
|
781
|
+
const _RegExp = globalThis.RegExp;
|
|
782
|
+
const _Reflect$1 = globalThis.Reflect;
|
|
783
|
+
/**
|
|
784
|
+
* (Safe copy) Creates a new RegExp using the captured RegExp constructor.
|
|
785
|
+
* Use this instead of `new RegExp()`.
|
|
786
|
+
*
|
|
787
|
+
* @param pattern - The pattern string or RegExp to copy.
|
|
788
|
+
* @param flags - Optional flags string.
|
|
789
|
+
* @returns A new RegExp instance.
|
|
790
|
+
*/
|
|
791
|
+
const createRegExp = (pattern, flags) => _Reflect$1.construct(_RegExp, [pattern, flags]);
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Safe copies of URL built-ins via factory functions.
|
|
795
|
+
*
|
|
796
|
+
* Provides safe references to URL and URLSearchParams.
|
|
797
|
+
* These references are captured at module initialization time to protect against
|
|
798
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
799
|
+
*
|
|
800
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/url
|
|
801
|
+
*/
|
|
802
|
+
// Capture references at module initialization time
|
|
803
|
+
const _URL = globalThis.URL;
|
|
804
|
+
const _Reflect = globalThis.Reflect;
|
|
805
|
+
// ============================================================================
|
|
806
|
+
// URL
|
|
807
|
+
// ============================================================================
|
|
808
|
+
/**
|
|
809
|
+
* (Safe copy) Creates a new URL using the captured URL constructor.
|
|
810
|
+
* Use this instead of `new URL()`.
|
|
811
|
+
*
|
|
812
|
+
* @param url - The URL string to parse.
|
|
813
|
+
* @param base - Optional base URL for relative URLs.
|
|
814
|
+
* @returns A new URL instance.
|
|
815
|
+
*/
|
|
816
|
+
const createURL = (url, base) => _Reflect.construct(_URL, [url, base]);
|
|
817
|
+
/**
|
|
818
|
+
* (Safe copy) Creates an object URL for the given object.
|
|
819
|
+
* Use this instead of `URL.createObjectURL()`.
|
|
820
|
+
*
|
|
821
|
+
* Note: This is a browser-only API. In Node.js environments, this will throw.
|
|
822
|
+
*/
|
|
823
|
+
typeof _URL.createObjectURL === 'function'
|
|
824
|
+
? _URL.createObjectURL.bind(_URL)
|
|
825
|
+
: () => {
|
|
826
|
+
throw new Error('URL.createObjectURL is not available in this environment');
|
|
827
|
+
};
|
|
828
|
+
/**
|
|
829
|
+
* (Safe copy) Revokes an object URL previously created with createObjectURL.
|
|
830
|
+
* Use this instead of `URL.revokeObjectURL()`.
|
|
831
|
+
*
|
|
832
|
+
* Note: This is a browser-only API. In Node.js environments, this will throw.
|
|
833
|
+
*/
|
|
834
|
+
typeof _URL.revokeObjectURL === 'function'
|
|
835
|
+
? _URL.revokeObjectURL.bind(_URL)
|
|
836
|
+
: () => {
|
|
837
|
+
throw new Error('URL.revokeObjectURL is not available in this environment');
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Format validators for common string formats.
|
|
842
|
+
*/
|
|
843
|
+
const formatValidators = {
|
|
844
|
+
'date-time': (v) => {
|
|
845
|
+
// ISO 8601 date-time format - anchored to prevent matching at unexpected locations
|
|
846
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(v))
|
|
847
|
+
return false;
|
|
848
|
+
const date = dateParse(v);
|
|
849
|
+
return !globalIsNaN(date);
|
|
850
|
+
},
|
|
851
|
+
date: (v) => {
|
|
852
|
+
// ISO 8601 date format (YYYY-MM-DD)
|
|
853
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(v))
|
|
854
|
+
return false;
|
|
855
|
+
const [year, month, day] = v.split('-').map(Number);
|
|
856
|
+
const date = createDate(dateUTC(year, month - 1, day));
|
|
857
|
+
// Check that the date components match exactly
|
|
858
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
859
|
+
},
|
|
860
|
+
time: (v) => {
|
|
861
|
+
// ISO 8601 time format (HH:MM:SS or HH:MM:SS.sss with optional timezone)
|
|
862
|
+
// Parse segments to avoid nested quantifiers
|
|
863
|
+
const mainMatch = v.match(/^(\d{2}):(\d{2}):(\d{2})/);
|
|
864
|
+
if (!mainMatch)
|
|
865
|
+
return false;
|
|
866
|
+
const hour = Number(mainMatch[1]);
|
|
867
|
+
const minute = Number(mainMatch[2]);
|
|
868
|
+
const second = Number(mainMatch[3]);
|
|
869
|
+
if (hour > 23 || minute > 59 || second > 59)
|
|
870
|
+
return false;
|
|
871
|
+
// Validate remaining part (fractional seconds and/or timezone)
|
|
872
|
+
const rest = v.slice(8);
|
|
873
|
+
if (rest === '')
|
|
874
|
+
return true;
|
|
875
|
+
// Fractional seconds: .ddd where d is digits (1-6 typically)
|
|
876
|
+
if (rest.match(/^\.\d{1,9}$/))
|
|
877
|
+
return true;
|
|
878
|
+
// Timezone only: Z or +HH:MM or -HH:MM
|
|
879
|
+
if (rest.match(/^(Z|[+-]\d{2}:\d{2})$/))
|
|
880
|
+
return true;
|
|
881
|
+
// Fractional + timezone
|
|
882
|
+
if (rest.match(/^\.\d{1,9}(Z|[+-]\d{2}:\d{2})$/))
|
|
883
|
+
return true;
|
|
884
|
+
return false;
|
|
885
|
+
},
|
|
886
|
+
email: (v) => {
|
|
887
|
+
// Basic email validation - fixed to prevent ReDoS by excluding dots from domain part
|
|
888
|
+
return /^[^\s@]+@[^\s@.]+\.[^\s@.]+$/.test(v);
|
|
889
|
+
},
|
|
890
|
+
hostname: (v) => {
|
|
891
|
+
// RFC 1123 hostname - validated per label to prevent ReDoS
|
|
892
|
+
if (v.length > 253 || v.length === 0)
|
|
893
|
+
return false; // Max hostname length per RFC
|
|
894
|
+
const labels = v.split('.');
|
|
895
|
+
for (const label of labels) {
|
|
896
|
+
if (label.length === 0 || label.length > 63)
|
|
897
|
+
return false;
|
|
898
|
+
// Start and end with alphanumeric
|
|
899
|
+
if (!/^[a-zA-Z0-9]$/.test(label[0]))
|
|
900
|
+
return false;
|
|
901
|
+
if (label.length > 1 && !/^[a-zA-Z0-9]$/.test(label[label.length - 1]))
|
|
902
|
+
return false;
|
|
903
|
+
// Middle can include hyphens
|
|
904
|
+
for (let i = 1; i < label.length - 1; i++) {
|
|
905
|
+
if (!/^[a-zA-Z0-9-]$/.test(label[i]))
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return true;
|
|
910
|
+
},
|
|
911
|
+
ipv4: (v) => {
|
|
912
|
+
// IPv4 address
|
|
913
|
+
const parts = v.split('.');
|
|
914
|
+
if (parts.length !== 4)
|
|
915
|
+
return false;
|
|
916
|
+
return parts.every((part) => {
|
|
917
|
+
const num = parseInt(part, 10);
|
|
918
|
+
return !globalIsNaN(num) && num >= 0 && num <= 255 && part === String(num);
|
|
919
|
+
});
|
|
920
|
+
},
|
|
921
|
+
ipv6: (v) => {
|
|
922
|
+
// Simplified IPv6 validation - procedural approach to avoid ReDoS
|
|
923
|
+
if (v === '::')
|
|
924
|
+
return true;
|
|
925
|
+
// Check for double colon (compressed form)
|
|
926
|
+
const hasDoubleColon = v.includes('::');
|
|
927
|
+
if (!hasDoubleColon) {
|
|
928
|
+
// Full form: exactly 8 groups separated by single colons
|
|
929
|
+
const groups = v.split(':');
|
|
930
|
+
if (groups.length !== 8)
|
|
931
|
+
return false;
|
|
932
|
+
const hexGroup = /^[0-9a-fA-F]{1,4}$/;
|
|
933
|
+
return groups.every((g) => hexGroup.test(g));
|
|
934
|
+
}
|
|
935
|
+
// Compressed form: validate :: appears exactly once and total groups <= 8
|
|
936
|
+
const parts = v.split('::');
|
|
937
|
+
if (parts.length !== 2)
|
|
938
|
+
return false;
|
|
939
|
+
const left = parts[0] ? parts[0].split(':').filter(Boolean) : [];
|
|
940
|
+
const right = parts[1] ? parts[1].split(':').filter(Boolean) : [];
|
|
941
|
+
if (left.length + right.length > 7)
|
|
942
|
+
return false;
|
|
943
|
+
const hexGroup = /^[0-9a-fA-F]{1,4}$/;
|
|
944
|
+
return left.every((g) => hexGroup.test(g)) && right.every((g) => hexGroup.test(g));
|
|
945
|
+
},
|
|
946
|
+
uri: (v) => {
|
|
947
|
+
// Basic URI validation
|
|
948
|
+
try {
|
|
949
|
+
createURL(v);
|
|
950
|
+
return true;
|
|
951
|
+
/* istanbul ignore next -- URL constructor always throws for invalid URI */
|
|
952
|
+
}
|
|
953
|
+
catch {
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
},
|
|
957
|
+
'uri-reference': (v) => {
|
|
958
|
+
// URI or relative reference
|
|
959
|
+
try {
|
|
960
|
+
createURL(v, 'http://example.com');
|
|
961
|
+
/* istanbul ignore next -- success path just returns true */
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
catch {
|
|
965
|
+
/* istanbul ignore next -- URL constructor is very permissive with base URL */
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
},
|
|
969
|
+
uuid: (v) => {
|
|
970
|
+
// UUID format
|
|
971
|
+
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(v);
|
|
972
|
+
},
|
|
973
|
+
regex: (v) => {
|
|
974
|
+
// Valid regex pattern
|
|
975
|
+
try {
|
|
976
|
+
// eslint-disable-next-line workspace/no-unsafe-regex -- intentionally validating user-provided regex patterns
|
|
977
|
+
createRegExp(v);
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
catch {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
},
|
|
984
|
+
'json-pointer': (v) => {
|
|
985
|
+
// JSON Pointer format - validate segment by segment to avoid nested quantifiers
|
|
986
|
+
if (v === '')
|
|
987
|
+
return true;
|
|
988
|
+
if (!v.startsWith('/'))
|
|
989
|
+
return false;
|
|
990
|
+
const segments = v.slice(1).split('/');
|
|
991
|
+
const validSegment = /^([^/~]|~[01])*$/;
|
|
992
|
+
return segments.every((seg) => validSegment.test(seg));
|
|
993
|
+
},
|
|
994
|
+
};
|
|
995
|
+
/**
|
|
996
|
+
* Validates string format constraint.
|
|
997
|
+
*
|
|
998
|
+
* @param instance - String being validated
|
|
999
|
+
* @param schema - Schema containing the format constraint
|
|
1000
|
+
* @param ctx - Validation context
|
|
1001
|
+
* @returns true if validation passes, false otherwise
|
|
1002
|
+
*/
|
|
1003
|
+
function validateFormat(instance, schema, ctx) {
|
|
1004
|
+
if (!schema.format) {
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
const validator = formatValidators[schema.format];
|
|
1008
|
+
if (!validator) {
|
|
1009
|
+
// Unknown format - allow by default (as per JSON Schema spec)
|
|
1010
|
+
return true;
|
|
1011
|
+
}
|
|
1012
|
+
if (!validator(instance)) {
|
|
1013
|
+
addError(ctx, `String does not match format '${schema.format}'`, instance, 'format', {
|
|
1014
|
+
format: schema.format,
|
|
1015
|
+
});
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
return true;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Validates array 'items' keyword.
|
|
1023
|
+
*
|
|
1024
|
+
* @param instance - Array being validated
|
|
1025
|
+
* @param schema - Schema containing the items constraint
|
|
1026
|
+
* @param ctx - Validation context
|
|
1027
|
+
* @returns true if validation passes, false otherwise
|
|
1028
|
+
*/
|
|
1029
|
+
function validateItems(instance, schema, ctx) {
|
|
1030
|
+
const items = schema.items;
|
|
1031
|
+
if (items === undefined) {
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
let valid = true;
|
|
1035
|
+
if (isArray(items)) {
|
|
1036
|
+
// Tuple validation
|
|
1037
|
+
for (let i = 0; i < items.length && i < instance.length; i++) {
|
|
1038
|
+
const itemSchema = items[i];
|
|
1039
|
+
/* istanbul ignore if -- defensive null check for sparse arrays */
|
|
1040
|
+
if (!itemSchema)
|
|
1041
|
+
continue;
|
|
1042
|
+
const itemCtx = pushPath(ctx, i);
|
|
1043
|
+
if (!ctx.validate(instance[i], itemSchema, itemCtx)) {
|
|
1044
|
+
valid = false;
|
|
1045
|
+
/* istanbul ignore if -- early exit already tested in validate.spec.ts */
|
|
1046
|
+
if (!shouldContinue(ctx))
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
// Handle additional items beyond the tuple
|
|
1051
|
+
if (instance.length > items.length) {
|
|
1052
|
+
if (!validateAdditionalItems(instance, schema, ctx, items.length)) {
|
|
1053
|
+
valid = false;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
else {
|
|
1058
|
+
// All items must match the single schema
|
|
1059
|
+
for (let i = 0; i < instance.length; i++) {
|
|
1060
|
+
const itemCtx = pushPath(ctx, i);
|
|
1061
|
+
if (!ctx.validate(instance[i], items, itemCtx)) {
|
|
1062
|
+
valid = false;
|
|
1063
|
+
if (!shouldContinue(ctx))
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return valid;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Validates 'additionalItems' keyword for tuple arrays.
|
|
1072
|
+
*
|
|
1073
|
+
* @param instance - Array being validated
|
|
1074
|
+
* @param schema - Schema containing the additionalItems constraint
|
|
1075
|
+
* @param ctx - Validation context
|
|
1076
|
+
* @param startIndex - Index from which to start checking additional items
|
|
1077
|
+
* @returns true if validation passes, false otherwise
|
|
1078
|
+
*/
|
|
1079
|
+
function validateAdditionalItems(instance, schema, ctx, startIndex) {
|
|
1080
|
+
const additionalItems = schema.additionalItems;
|
|
1081
|
+
// If not specified, additional items are allowed
|
|
1082
|
+
/* istanbul ignore if -- default case handled in items.spec.ts */
|
|
1083
|
+
if (additionalItems === undefined) {
|
|
1084
|
+
return true;
|
|
1085
|
+
}
|
|
1086
|
+
/* istanbul ignore next -- additionalItems initialization and branching */
|
|
1087
|
+
let valid = true;
|
|
1088
|
+
/* istanbul ignore next -- additionalItems branching */
|
|
1089
|
+
if (additionalItems === false) {
|
|
1090
|
+
// No additional items allowed
|
|
1091
|
+
if (instance.length > startIndex) {
|
|
1092
|
+
addError(ctx, `Array has too many items. Expected at most ${startIndex}, got ${instance.length}`, instance, 'additionalItems', {
|
|
1093
|
+
limit: startIndex,
|
|
1094
|
+
actual: instance.length,
|
|
1095
|
+
});
|
|
1096
|
+
valid = false;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
else if (typeof additionalItems === 'object') {
|
|
1100
|
+
// Additional items must match the schema
|
|
1101
|
+
for (let i = startIndex; i < instance.length; i++) {
|
|
1102
|
+
const itemCtx = pushPath(ctx, i);
|
|
1103
|
+
/* istanbul ignore else -- validation failure tested in items.spec.ts */
|
|
1104
|
+
if (!ctx.validate(instance[i], additionalItems, itemCtx)) {
|
|
1105
|
+
valid = false;
|
|
1106
|
+
/* istanbul ignore if -- early exit tested in validate.spec.ts */
|
|
1107
|
+
if (!shouldContinue(ctx))
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
// If additionalItems === true, any additional items are allowed
|
|
1113
|
+
return valid;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Safe copies of Math built-in methods.
|
|
1118
|
+
*
|
|
1119
|
+
* These references are captured at module initialization time to protect against
|
|
1120
|
+
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
1121
|
+
*
|
|
1122
|
+
* @module @hyperfrontend/immutable-api-utils/built-in-copy/math
|
|
1123
|
+
*/
|
|
1124
|
+
// Capture references at module initialization time
|
|
1125
|
+
const _Math = globalThis.Math;
|
|
1126
|
+
// ============================================================================
|
|
1127
|
+
// Basic Operations
|
|
1128
|
+
// ============================================================================
|
|
1129
|
+
/**
|
|
1130
|
+
* (Safe copy) Returns the absolute value of a number.
|
|
1131
|
+
*/
|
|
1132
|
+
const abs = _Math.abs;
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* Validates number range and multipleOf constraints.
|
|
1136
|
+
*
|
|
1137
|
+
* @param instance - Number being validated
|
|
1138
|
+
* @param schema - Schema containing number constraints
|
|
1139
|
+
* @param ctx - Validation context
|
|
1140
|
+
* @returns true if validation passes, false otherwise
|
|
1141
|
+
*/
|
|
1142
|
+
function validateNumberBounds(instance, schema, ctx) {
|
|
1143
|
+
let valid = true;
|
|
1144
|
+
if (schema.minimum !== undefined) {
|
|
1145
|
+
const isExclusive = schema.exclusiveMinimum === true;
|
|
1146
|
+
if (isExclusive ? instance <= schema.minimum : instance < schema.minimum) {
|
|
1147
|
+
const comparison = isExclusive ? 'greater than' : 'at least';
|
|
1148
|
+
addError(ctx, `Number must be ${comparison} ${schema.minimum}, got ${instance}`, instance, 'minimum', {
|
|
1149
|
+
limit: schema.minimum,
|
|
1150
|
+
exclusive: isExclusive,
|
|
1151
|
+
actual: instance,
|
|
1152
|
+
});
|
|
1153
|
+
valid = false;
|
|
1154
|
+
if (!shouldContinue(ctx))
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
if (schema.maximum !== undefined) {
|
|
1159
|
+
const isExclusive = schema.exclusiveMaximum === true;
|
|
1160
|
+
if (isExclusive ? instance >= schema.maximum : instance > schema.maximum) {
|
|
1161
|
+
const comparison = isExclusive ? 'less than' : 'at most';
|
|
1162
|
+
addError(ctx, `Number must be ${comparison} ${schema.maximum}, got ${instance}`, instance, 'maximum', {
|
|
1163
|
+
limit: schema.maximum,
|
|
1164
|
+
exclusive: isExclusive,
|
|
1165
|
+
actual: instance,
|
|
1166
|
+
});
|
|
1167
|
+
valid = false;
|
|
1168
|
+
if (!shouldContinue(ctx))
|
|
1169
|
+
return false;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
if (schema.multipleOf !== undefined && schema.multipleOf > 0) {
|
|
1173
|
+
// Use epsilon comparison for floating point precision
|
|
1174
|
+
const remainder = abs(instance % schema.multipleOf);
|
|
1175
|
+
const epsilon = 1e-10;
|
|
1176
|
+
if (remainder > epsilon && schema.multipleOf - remainder > epsilon) {
|
|
1177
|
+
addError(ctx, `Number must be a multiple of ${schema.multipleOf}, got ${instance}`, instance, 'multipleOf', {
|
|
1178
|
+
multipleOf: schema.multipleOf,
|
|
1179
|
+
actual: instance,
|
|
1180
|
+
});
|
|
1181
|
+
valid = false;
|
|
1182
|
+
if (!shouldContinue(ctx))
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
return valid;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Validates object bounds constraints (minProperties, maxProperties).
|
|
1191
|
+
*
|
|
1192
|
+
* @param instance - Object being validated
|
|
1193
|
+
* @param schema - Schema containing object bounds
|
|
1194
|
+
* @param ctx - Validation context
|
|
1195
|
+
* @returns true if validation passes, false otherwise
|
|
1196
|
+
*/
|
|
1197
|
+
function validateObjectBounds(instance, schema, ctx) {
|
|
1198
|
+
let valid = true;
|
|
1199
|
+
const propertyCount = keys(instance).length;
|
|
1200
|
+
if (schema.minProperties !== undefined && propertyCount < schema.minProperties) {
|
|
1201
|
+
addError(ctx, `Object must have at least ${schema.minProperties} properties, got ${propertyCount}`, instance, 'minProperties', {
|
|
1202
|
+
limit: schema.minProperties,
|
|
1203
|
+
actual: propertyCount,
|
|
1204
|
+
});
|
|
1205
|
+
valid = false;
|
|
1206
|
+
if (!shouldContinue(ctx))
|
|
1207
|
+
return false;
|
|
1208
|
+
}
|
|
1209
|
+
if (schema.maxProperties !== undefined && propertyCount > schema.maxProperties) {
|
|
1210
|
+
addError(ctx, `Object must have at most ${schema.maxProperties} properties, got ${propertyCount}`, instance, 'maxProperties', {
|
|
1211
|
+
limit: schema.maxProperties,
|
|
1212
|
+
actual: propertyCount,
|
|
1213
|
+
});
|
|
1214
|
+
valid = false;
|
|
1215
|
+
if (!shouldContinue(ctx))
|
|
1216
|
+
return false;
|
|
1217
|
+
}
|
|
1218
|
+
return valid;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Validates object 'patternProperties' keyword.
|
|
1223
|
+
*
|
|
1224
|
+
* @param instance - Object being validated
|
|
1225
|
+
* @param schema - Schema containing the patternProperties constraint
|
|
1226
|
+
* @param ctx - Validation context
|
|
1227
|
+
* @returns true if validation passes, false otherwise
|
|
1228
|
+
*/
|
|
1229
|
+
function validatePatternProperties(instance, schema, ctx) {
|
|
1230
|
+
if (!schema.patternProperties) {
|
|
1231
|
+
return true;
|
|
1232
|
+
}
|
|
1233
|
+
let valid = true;
|
|
1234
|
+
const patterns = [];
|
|
1235
|
+
// Pre-compile all patterns
|
|
1236
|
+
for (const [pattern, patternSchema] of entries(schema.patternProperties)) {
|
|
1237
|
+
// Check pattern safety if a checker is configured
|
|
1238
|
+
/* istanbul ignore if -- patternSafetyChecker branch tested in validate.spec.ts */
|
|
1239
|
+
if (ctx.patternSafetyChecker) {
|
|
1240
|
+
const safetyResult = ctx.patternSafetyChecker(pattern);
|
|
1241
|
+
if (!safetyResult.safe) {
|
|
1242
|
+
addError(ctx, `Unsafe regex pattern in patternProperties: ${safetyResult.reason ?? 'Pattern may cause ReDoS'}`, instance, 'patternProperties', {
|
|
1243
|
+
pattern,
|
|
1244
|
+
reason: safetyResult.reason,
|
|
1245
|
+
});
|
|
1246
|
+
valid = false;
|
|
1247
|
+
if (!shouldContinue(ctx))
|
|
1248
|
+
return false;
|
|
1249
|
+
continue; // Skip this unsafe pattern
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
try {
|
|
1253
|
+
// eslint-disable-next-line workspace/no-unsafe-regex -- Pattern safety validated above when safePatterns enabled
|
|
1254
|
+
patterns.push({ regex: createRegExp(pattern), schema: patternSchema });
|
|
1255
|
+
}
|
|
1256
|
+
catch (e) {
|
|
1257
|
+
// Invalid regex
|
|
1258
|
+
/* istanbul ignore next -- strictPatterns mode verified in validate.spec.ts */
|
|
1259
|
+
if (ctx.strictPatterns) {
|
|
1260
|
+
/* istanbul ignore next -- error reporting for invalid regex */
|
|
1261
|
+
addError(ctx, `Invalid regex pattern in patternProperties: ${pattern}`, instance, 'patternProperties', {
|
|
1262
|
+
/* istanbul ignore next -- error message extraction */
|
|
1263
|
+
pattern,
|
|
1264
|
+
/* istanbul ignore next -- ternary expression */
|
|
1265
|
+
error: e instanceof Error ? e.message : 'Invalid regex',
|
|
1266
|
+
});
|
|
1267
|
+
valid = false;
|
|
1268
|
+
/* istanbul ignore if -- early exit tested in validate.spec.ts */
|
|
1269
|
+
if (!shouldContinue(ctx))
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1272
|
+
// Otherwise skip silently
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
// Check each property against matching patterns
|
|
1276
|
+
for (const key of keys(instance)) {
|
|
1277
|
+
for (const { regex, schema: patternSchema } of patterns) {
|
|
1278
|
+
if (regex.test(key)) {
|
|
1279
|
+
const propCtx = pushPath(ctx, key);
|
|
1280
|
+
if (!ctx.validate(instance[key], patternSchema, propCtx)) {
|
|
1281
|
+
valid = false;
|
|
1282
|
+
if (!shouldContinue(ctx))
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
return valid;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
/**
|
|
1292
|
+
* Validates object 'properties' keyword.
|
|
1293
|
+
*
|
|
1294
|
+
* @param instance - Object being validated
|
|
1295
|
+
* @param schema - Schema containing the properties constraint
|
|
1296
|
+
* @param ctx - Validation context
|
|
1297
|
+
* @returns true if validation passes, false otherwise
|
|
1298
|
+
*/
|
|
1299
|
+
function validateProperties(instance, schema, ctx) {
|
|
1300
|
+
if (!schema.properties) {
|
|
1301
|
+
return true;
|
|
1302
|
+
}
|
|
1303
|
+
let valid = true;
|
|
1304
|
+
for (const [key, propSchema] of entries(schema.properties)) {
|
|
1305
|
+
if (hasOwn(instance, key)) {
|
|
1306
|
+
const propCtx = pushPath(ctx, key);
|
|
1307
|
+
if (!ctx.validate(instance[key], propSchema, propCtx)) {
|
|
1308
|
+
valid = false;
|
|
1309
|
+
if (!shouldContinue(ctx))
|
|
1310
|
+
return false;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return valid;
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Validates object 'required' keyword.
|
|
1318
|
+
*
|
|
1319
|
+
* @param instance - Object being validated
|
|
1320
|
+
* @param schema - Schema containing the required constraint
|
|
1321
|
+
* @param ctx - Validation context
|
|
1322
|
+
* @returns true if validation passes, false otherwise
|
|
1323
|
+
*/
|
|
1324
|
+
function validateRequired(instance, schema, ctx) {
|
|
1325
|
+
if (!schema.required) {
|
|
1326
|
+
return true;
|
|
1327
|
+
}
|
|
1328
|
+
let valid = true;
|
|
1329
|
+
for (const key of schema.required) {
|
|
1330
|
+
if (!hasOwn(instance, key)) {
|
|
1331
|
+
addError(ctx, `Missing required property: ${key}`, undefined, 'required', { missing: key });
|
|
1332
|
+
valid = false;
|
|
1333
|
+
if (!shouldContinue(ctx))
|
|
1334
|
+
return false;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
return valid;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Validates object 'additionalProperties' keyword.
|
|
1341
|
+
*
|
|
1342
|
+
* @param instance - Object being validated
|
|
1343
|
+
* @param schema - Schema containing the additionalProperties constraint
|
|
1344
|
+
* @param ctx - Validation context
|
|
1345
|
+
* @returns true if validation passes, false otherwise
|
|
1346
|
+
*/
|
|
1347
|
+
function validateAdditionalProperties(instance, schema, ctx) {
|
|
1348
|
+
const additionalProperties = schema.additionalProperties;
|
|
1349
|
+
if (additionalProperties === undefined) {
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
/* istanbul ignore next -- definedKeys initialization */
|
|
1353
|
+
// Collect defined property names
|
|
1354
|
+
const definedKeys = createSet();
|
|
1355
|
+
/* istanbul ignore next -- schema.properties may not exist */
|
|
1356
|
+
if (schema.properties) {
|
|
1357
|
+
for (const key of keys(schema.properties)) {
|
|
1358
|
+
definedKeys.add(key);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
// Collect pattern property regexes
|
|
1362
|
+
const patterns = [];
|
|
1363
|
+
/* istanbul ignore next -- patternProperties may not always be present */
|
|
1364
|
+
if (schema.patternProperties) {
|
|
1365
|
+
for (const pattern of keys(schema.patternProperties)) {
|
|
1366
|
+
// Skip unsafe patterns if a checker is configured (already reported in patternProperties validator)
|
|
1367
|
+
if (ctx.patternSafetyChecker) {
|
|
1368
|
+
const safetyResult = ctx.patternSafetyChecker(pattern);
|
|
1369
|
+
if (!safetyResult.safe) {
|
|
1370
|
+
continue; // Skip this unsafe pattern
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
try {
|
|
1374
|
+
// eslint-disable-next-line workspace/no-unsafe-regex -- Pattern safety validated above when safePatterns enabled
|
|
1375
|
+
patterns.push(createRegExp(pattern));
|
|
1376
|
+
/* istanbul ignore next -- invalid regex patterns handled in patternProperties validator */
|
|
1377
|
+
}
|
|
1378
|
+
catch {
|
|
1379
|
+
// Invalid regex, skip
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
let valid = true;
|
|
1384
|
+
for (const key of keys(instance)) {
|
|
1385
|
+
// Skip if property is defined in 'properties'
|
|
1386
|
+
if (definedKeys.has(key)) {
|
|
1387
|
+
continue;
|
|
1388
|
+
}
|
|
1389
|
+
// Skip if property matches any pattern in 'patternProperties'
|
|
1390
|
+
if (patterns.some((p) => p.test(key))) {
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
// This is an additional property
|
|
1394
|
+
if (additionalProperties === false) {
|
|
1395
|
+
addError(ctx, `Additional property not allowed: ${key}`, instance[key], 'additionalProperties', {
|
|
1396
|
+
property: key,
|
|
1397
|
+
});
|
|
1398
|
+
valid = false;
|
|
1399
|
+
/* istanbul ignore if -- early exit tested in validate.spec.ts */
|
|
1400
|
+
if (!shouldContinue(ctx))
|
|
1401
|
+
return false;
|
|
1402
|
+
}
|
|
1403
|
+
else if (typeof additionalProperties === 'object') {
|
|
1404
|
+
const propCtx = pushPath(ctx, key);
|
|
1405
|
+
if (!ctx.validate(instance[key], additionalProperties, propCtx)) {
|
|
1406
|
+
valid = false;
|
|
1407
|
+
/* istanbul ignore if -- early exit tested in validate.spec.ts */
|
|
1408
|
+
if (!shouldContinue(ctx))
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
// If additionalProperties === true, any additional property is allowed
|
|
1413
|
+
}
|
|
1414
|
+
return valid;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
/**
|
|
1418
|
+
* Validates string length and pattern constraints.
|
|
1419
|
+
*
|
|
1420
|
+
* @param instance - String being validated
|
|
1421
|
+
* @param schema - Schema containing string constraints
|
|
1422
|
+
* @param ctx - Validation context
|
|
1423
|
+
* @returns true if validation passes, false otherwise
|
|
1424
|
+
*/
|
|
1425
|
+
function validateStringBounds(instance, schema, ctx) {
|
|
1426
|
+
let valid = true;
|
|
1427
|
+
if (schema.minLength !== undefined && instance.length < schema.minLength) {
|
|
1428
|
+
addError(ctx, `String must be at least ${schema.minLength} characters, got ${instance.length}`, instance, 'minLength', {
|
|
1429
|
+
limit: schema.minLength,
|
|
1430
|
+
actual: instance.length,
|
|
1431
|
+
});
|
|
1432
|
+
valid = false;
|
|
1433
|
+
if (!shouldContinue(ctx))
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
if (schema.maxLength !== undefined && instance.length > schema.maxLength) {
|
|
1437
|
+
addError(ctx, `String must be at most ${schema.maxLength} characters, got ${instance.length}`, instance, 'maxLength', {
|
|
1438
|
+
limit: schema.maxLength,
|
|
1439
|
+
actual: instance.length,
|
|
1440
|
+
});
|
|
1441
|
+
valid = false;
|
|
1442
|
+
if (!shouldContinue(ctx))
|
|
1443
|
+
return false;
|
|
1444
|
+
}
|
|
1445
|
+
if (schema.pattern !== undefined) {
|
|
1446
|
+
// Check pattern safety if a checker is configured
|
|
1447
|
+
if (ctx.patternSafetyChecker) {
|
|
1448
|
+
const safetyResult = ctx.patternSafetyChecker(schema.pattern);
|
|
1449
|
+
if (!safetyResult.safe) {
|
|
1450
|
+
addError(ctx, `Unsafe regex pattern: ${safetyResult.reason ?? 'Pattern may cause ReDoS'}`, instance, 'pattern', {
|
|
1451
|
+
pattern: schema.pattern,
|
|
1452
|
+
reason: safetyResult.reason,
|
|
1453
|
+
});
|
|
1454
|
+
valid = false;
|
|
1455
|
+
if (!shouldContinue(ctx))
|
|
1456
|
+
return false;
|
|
1457
|
+
// Skip executing the unsafe pattern
|
|
1458
|
+
return valid;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
try {
|
|
1462
|
+
// eslint-disable-next-line workspace/no-unsafe-regex -- Pattern safety validated above when safePatterns enabled
|
|
1463
|
+
const regex = createRegExp(schema.pattern);
|
|
1464
|
+
if (!regex.test(instance)) {
|
|
1465
|
+
addError(ctx, `String does not match pattern: ${schema.pattern}`, instance, 'pattern', {
|
|
1466
|
+
pattern: schema.pattern,
|
|
1467
|
+
});
|
|
1468
|
+
valid = false;
|
|
1469
|
+
if (!shouldContinue(ctx))
|
|
1470
|
+
return false;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
catch (e) {
|
|
1474
|
+
// Invalid regex pattern
|
|
1475
|
+
/* istanbul ignore next -- strictPatterns mode verified in validate.spec.ts */
|
|
1476
|
+
if (ctx.strictPatterns) {
|
|
1477
|
+
/* istanbul ignore next -- error reporting for invalid regex */
|
|
1478
|
+
addError(ctx, `Invalid regex pattern: ${schema.pattern}`, instance, 'pattern', {
|
|
1479
|
+
pattern: schema.pattern,
|
|
1480
|
+
/* istanbul ignore next -- error message extraction ternary */
|
|
1481
|
+
error: e instanceof Error ? e.message : 'Invalid regex',
|
|
1482
|
+
});
|
|
1483
|
+
valid = false;
|
|
1484
|
+
/* istanbul ignore if -- early exit tested in validate.spec.ts */
|
|
1485
|
+
if (!shouldContinue(ctx))
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1488
|
+
// Otherwise skip validation silently
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
return valid;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* Type checking functions for JSON Schema types.
|
|
1496
|
+
*/
|
|
1497
|
+
const typeCheckers = {
|
|
1498
|
+
string: (v) => typeof v === 'string',
|
|
1499
|
+
number: (v) => typeof v === 'number' && globalIsFinite(v),
|
|
1500
|
+
integer: (v) => typeof v === 'number' && globalIsFinite(v) && isInteger(v),
|
|
1501
|
+
boolean: (v) => typeof v === 'boolean',
|
|
1502
|
+
array: (v) => isArray(v),
|
|
1503
|
+
object: (v) => v !== null && typeof v === 'object' && !isArray(v),
|
|
1504
|
+
null: (v) => v === null,
|
|
1505
|
+
};
|
|
1506
|
+
/**
|
|
1507
|
+
* Gets the actual JSON type of a value for error messages.
|
|
1508
|
+
*
|
|
1509
|
+
* @param value - The value to get the type of
|
|
1510
|
+
* @returns The JSON type as a string
|
|
1511
|
+
*/
|
|
1512
|
+
function getActualType(value) {
|
|
1513
|
+
if (value === null)
|
|
1514
|
+
return 'null';
|
|
1515
|
+
if (isArray(value))
|
|
1516
|
+
return 'array';
|
|
1517
|
+
const t = typeof value;
|
|
1518
|
+
if (t === 'number') {
|
|
1519
|
+
const num = value;
|
|
1520
|
+
/* istanbul ignore next -- NaN/Infinity edge case */
|
|
1521
|
+
if (!globalIsFinite(num))
|
|
1522
|
+
return 'number'; // NaN/Infinity
|
|
1523
|
+
return isInteger(num) ? 'integer' : 'number';
|
|
1524
|
+
}
|
|
1525
|
+
return t;
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Validates the 'type' keyword.
|
|
1529
|
+
*
|
|
1530
|
+
* @param instance - Value being validated
|
|
1531
|
+
* @param schema - Schema containing the type constraint
|
|
1532
|
+
* @param ctx - Validation context
|
|
1533
|
+
* @returns true if validation passes, false otherwise
|
|
1534
|
+
*/
|
|
1535
|
+
function validateType(instance, schema, ctx) {
|
|
1536
|
+
const schemaType = schema.type;
|
|
1537
|
+
if (schemaType === undefined) {
|
|
1538
|
+
return true;
|
|
1539
|
+
}
|
|
1540
|
+
const types = isArray(schemaType) ? schemaType : [schemaType];
|
|
1541
|
+
for (const type of types) {
|
|
1542
|
+
const checker = typeCheckers[type];
|
|
1543
|
+
/* istanbul ignore if -- defensive check for unknown type */
|
|
1544
|
+
if (checker && checker(instance)) {
|
|
1545
|
+
// Special case: 'integer' should also pass 'number' check
|
|
1546
|
+
return true;
|
|
1547
|
+
}
|
|
1548
|
+
// If type is 'number' and value is an integer, it's still valid
|
|
1549
|
+
/* istanbul ignore if -- defensive fallback for integer/number coercion */
|
|
1550
|
+
if (type === 'number' && typeCheckers['integer']?.(instance)) {
|
|
1551
|
+
return true;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
const actualType = getActualType(instance);
|
|
1555
|
+
const expectedTypes = types.join(' or ');
|
|
1556
|
+
addError(ctx, `Expected type ${expectedTypes} but got ${actualType}`, instance, 'type', {
|
|
1557
|
+
expected: types,
|
|
1558
|
+
actual: actualType,
|
|
1559
|
+
});
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* Resolves a $ref JSON Pointer to its target schema.
|
|
1565
|
+
*
|
|
1566
|
+
* @param ref - The $ref string (e.g., '#/definitions/Address')
|
|
1567
|
+
* @param ctx - Validation context containing root schema and definitions
|
|
1568
|
+
* @returns The resolved schema, or undefined if not found
|
|
1569
|
+
*/
|
|
1570
|
+
function resolveRef(ref, ctx) {
|
|
1571
|
+
// Check pre-populated definitions first
|
|
1572
|
+
const cached = ctx.definitions.get(ref);
|
|
1573
|
+
if (cached) {
|
|
1574
|
+
return cached;
|
|
1575
|
+
}
|
|
1576
|
+
// Only support internal references (starting with #)
|
|
1577
|
+
if (!ref.startsWith('#')) {
|
|
1578
|
+
return undefined;
|
|
1579
|
+
}
|
|
1580
|
+
// Handle root reference
|
|
1581
|
+
if (ref === '#') {
|
|
1582
|
+
return ctx.rootSchema;
|
|
1583
|
+
}
|
|
1584
|
+
// Parse JSON Pointer path
|
|
1585
|
+
const path = ref.slice(1); // Remove leading #
|
|
1586
|
+
if (!path.startsWith('/')) {
|
|
1587
|
+
return undefined;
|
|
1588
|
+
}
|
|
1589
|
+
const segments = path
|
|
1590
|
+
.split('/')
|
|
1591
|
+
.slice(1) // Remove empty first segment
|
|
1592
|
+
.map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
1593
|
+
// Navigate to the referenced schema
|
|
1594
|
+
let current = ctx.rootSchema;
|
|
1595
|
+
for (const segment of segments) {
|
|
1596
|
+
if (current === null || typeof current !== 'object') {
|
|
1597
|
+
return undefined;
|
|
1598
|
+
}
|
|
1599
|
+
current = current[segment];
|
|
1600
|
+
}
|
|
1601
|
+
// Cache the resolved schema
|
|
1602
|
+
if (current && typeof current === 'object') {
|
|
1603
|
+
const resolved = current;
|
|
1604
|
+
ctx.definitions.set(ref, resolved);
|
|
1605
|
+
return resolved;
|
|
1606
|
+
}
|
|
1607
|
+
return undefined;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
/**
|
|
1611
|
+
* Validates a value against a JSON Schema.
|
|
1612
|
+
*
|
|
1613
|
+
* @param instance - The value to validate
|
|
1614
|
+
* @param schema - The JSON Schema to validate against
|
|
1615
|
+
* @param options - Validation options
|
|
1616
|
+
* @returns Validation result with valid flag and any errors
|
|
1617
|
+
*
|
|
1618
|
+
* @example
|
|
1619
|
+
* ```typescript
|
|
1620
|
+
* const schema = { type: 'string', minLength: 1 }
|
|
1621
|
+
* const result = validate('hello', schema)
|
|
1622
|
+
* console.log(result.valid) // true
|
|
1623
|
+
* ```
|
|
1624
|
+
*/
|
|
1625
|
+
function validate(instance, schema, options) {
|
|
1626
|
+
// Resolve safePatterns option to a checker function
|
|
1627
|
+
let patternSafetyChecker;
|
|
1628
|
+
const ctx = createValidationContext(schema, validateSchema, true, false, patternSafetyChecker);
|
|
1629
|
+
const valid = validateSchema(instance, schema, ctx);
|
|
1630
|
+
return {
|
|
1631
|
+
valid,
|
|
1632
|
+
errors: ctx.errors,
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Internal recursive validation function.
|
|
1637
|
+
*
|
|
1638
|
+
* @param instance - Value being validated
|
|
1639
|
+
* @param schema - Schema to validate against
|
|
1640
|
+
* @param ctx - Validation context
|
|
1641
|
+
* @returns true if validation passes, false otherwise
|
|
1642
|
+
*/
|
|
1643
|
+
function validateSchema(instance, schema, ctx) {
|
|
1644
|
+
// Handle $ref
|
|
1645
|
+
if (schema.$ref) {
|
|
1646
|
+
const resolved = resolveRef(schema.$ref, ctx);
|
|
1647
|
+
/* istanbul ignore if -- $ref resolution failures are tested in resolve-ref.spec.ts */
|
|
1648
|
+
if (!resolved) {
|
|
1649
|
+
// Could not resolve reference - treat as valid
|
|
1650
|
+
return true;
|
|
1651
|
+
}
|
|
1652
|
+
return validateSchema(instance, resolved, ctx);
|
|
1653
|
+
}
|
|
1654
|
+
let valid = true;
|
|
1655
|
+
// Type validation
|
|
1656
|
+
if (!validateType(instance, schema, ctx)) {
|
|
1657
|
+
valid = false;
|
|
1658
|
+
if (!shouldContinue(ctx))
|
|
1659
|
+
return false;
|
|
1660
|
+
}
|
|
1661
|
+
// Enum validation
|
|
1662
|
+
if (!validateEnum(instance, schema, ctx)) {
|
|
1663
|
+
valid = false;
|
|
1664
|
+
if (!shouldContinue(ctx))
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1667
|
+
// String-specific validations
|
|
1668
|
+
if (typeof instance === 'string') {
|
|
1669
|
+
if (!validateStringBounds(instance, schema, ctx)) {
|
|
1670
|
+
valid = false;
|
|
1671
|
+
if (!shouldContinue(ctx))
|
|
1672
|
+
return false;
|
|
1673
|
+
}
|
|
1674
|
+
if (!validateFormat(instance, schema, ctx)) {
|
|
1675
|
+
valid = false;
|
|
1676
|
+
if (!shouldContinue(ctx))
|
|
1677
|
+
return false;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
// Number-specific validations
|
|
1681
|
+
if (typeof instance === 'number') {
|
|
1682
|
+
if (!validateNumberBounds(instance, schema, ctx)) {
|
|
1683
|
+
valid = false;
|
|
1684
|
+
if (!shouldContinue(ctx))
|
|
1685
|
+
return false;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
// Array-specific validations
|
|
1689
|
+
if (isArray(instance)) {
|
|
1690
|
+
if (!validateItems(instance, schema, ctx)) {
|
|
1691
|
+
valid = false;
|
|
1692
|
+
if (!shouldContinue(ctx))
|
|
1693
|
+
return false;
|
|
1694
|
+
}
|
|
1695
|
+
if (!validateArrayBounds(instance, schema, ctx)) {
|
|
1696
|
+
valid = false;
|
|
1697
|
+
if (!shouldContinue(ctx))
|
|
1698
|
+
return false;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
// Object-specific validations
|
|
1702
|
+
if (instance !== null && typeof instance === 'object' && !isArray(instance)) {
|
|
1703
|
+
const obj = instance;
|
|
1704
|
+
if (!validateProperties(obj, schema, ctx)) {
|
|
1705
|
+
valid = false;
|
|
1706
|
+
if (!shouldContinue(ctx))
|
|
1707
|
+
return false;
|
|
1708
|
+
}
|
|
1709
|
+
if (!validateRequired(obj, schema, ctx)) {
|
|
1710
|
+
valid = false;
|
|
1711
|
+
/* istanbul ignore if -- early exit tested elsewhere */
|
|
1712
|
+
if (!shouldContinue(ctx))
|
|
1713
|
+
return false;
|
|
1714
|
+
}
|
|
1715
|
+
/* istanbul ignore next -- patternProperties validation */
|
|
1716
|
+
if (!validatePatternProperties(obj, schema, ctx)) {
|
|
1717
|
+
valid = false;
|
|
1718
|
+
/* istanbul ignore next -- early exit tested elsewhere */
|
|
1719
|
+
if (!shouldContinue(ctx))
|
|
1720
|
+
return false;
|
|
1721
|
+
}
|
|
1722
|
+
/* istanbul ignore next -- additionalProperties validation */
|
|
1723
|
+
if (!validateAdditionalProperties(obj, schema, ctx)) {
|
|
1724
|
+
valid = false;
|
|
1725
|
+
/* istanbul ignore next -- early exit tested elsewhere */
|
|
1726
|
+
if (!shouldContinue(ctx))
|
|
1727
|
+
return false;
|
|
1728
|
+
}
|
|
1729
|
+
/* istanbul ignore next -- objectBounds validation */
|
|
1730
|
+
if (!validateObjectBounds(obj, schema, ctx)) {
|
|
1731
|
+
valid = false;
|
|
1732
|
+
/* istanbul ignore next -- early exit tested elsewhere */
|
|
1733
|
+
if (!shouldContinue(ctx))
|
|
1734
|
+
return false;
|
|
1735
|
+
}
|
|
1736
|
+
/* istanbul ignore next -- dependencies validation */
|
|
1737
|
+
if (!validateDependencies(obj, schema, ctx)) {
|
|
1738
|
+
valid = false;
|
|
1739
|
+
/* istanbul ignore next -- early exit tested elsewhere */
|
|
1740
|
+
if (!shouldContinue(ctx))
|
|
1741
|
+
return false;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
// Composition keywords
|
|
1745
|
+
if (!validateAllOf(instance, schema, ctx)) {
|
|
1746
|
+
valid = false;
|
|
1747
|
+
if (!shouldContinue(ctx))
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
if (!validateAnyOf(instance, schema, ctx)) {
|
|
1751
|
+
valid = false;
|
|
1752
|
+
if (!shouldContinue(ctx))
|
|
1753
|
+
return false;
|
|
1754
|
+
}
|
|
1755
|
+
if (!validateOneOf(instance, schema, ctx)) {
|
|
1756
|
+
valid = false;
|
|
1757
|
+
if (!shouldContinue(ctx))
|
|
1758
|
+
return false;
|
|
1759
|
+
}
|
|
1760
|
+
if (!validateNot(instance, schema, ctx)) {
|
|
1761
|
+
valid = false;
|
|
1762
|
+
if (!shouldContinue(ctx))
|
|
1763
|
+
return false;
|
|
1764
|
+
}
|
|
1765
|
+
return valid;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/**
|
|
1769
|
+
* Changelog JSON Schema
|
|
1770
|
+
*
|
|
1771
|
+
* JSON Schema definitions for changelog validation and schema compatibility checking.
|
|
1772
|
+
* Uses `@hyperfrontend/json-utils` for validation.
|
|
1773
|
+
*/
|
|
1774
|
+
/**
|
|
1775
|
+
* JSON Schema for a ChangelogLink.
|
|
1776
|
+
*/
|
|
1777
|
+
const changelogLinkSchema = {
|
|
1778
|
+
type: 'object',
|
|
1779
|
+
required: ['label', 'url'],
|
|
1780
|
+
properties: {
|
|
1781
|
+
label: { type: 'string' },
|
|
1782
|
+
url: { type: 'string' },
|
|
1783
|
+
},
|
|
1784
|
+
additionalProperties: false,
|
|
1785
|
+
};
|
|
1786
|
+
/**
|
|
1787
|
+
* JSON Schema for a CommitRef.
|
|
1788
|
+
*/
|
|
1789
|
+
const commitRefSchema = {
|
|
1790
|
+
type: 'object',
|
|
1791
|
+
required: ['hash', 'shortHash'],
|
|
1792
|
+
properties: {
|
|
1793
|
+
hash: { type: 'string', minLength: 7, maxLength: 40 },
|
|
1794
|
+
shortHash: { type: 'string', minLength: 7, maxLength: 7 },
|
|
1795
|
+
url: { type: 'string' },
|
|
1796
|
+
},
|
|
1797
|
+
additionalProperties: false,
|
|
1798
|
+
};
|
|
1799
|
+
/**
|
|
1800
|
+
* JSON Schema for an IssueRef.
|
|
1801
|
+
*/
|
|
1802
|
+
const issueRefSchema = {
|
|
1803
|
+
type: 'object',
|
|
1804
|
+
required: ['number', 'type'],
|
|
1805
|
+
properties: {
|
|
1806
|
+
number: { type: 'integer', minimum: 1 },
|
|
1807
|
+
type: { type: 'string', enum: ['issue', 'pull-request'] },
|
|
1808
|
+
url: { type: 'string' },
|
|
1809
|
+
},
|
|
1810
|
+
additionalProperties: false,
|
|
1811
|
+
};
|
|
1812
|
+
/**
|
|
1813
|
+
* JSON Schema for a ChangelogItem.
|
|
1814
|
+
*/
|
|
1815
|
+
const changelogItemSchema = {
|
|
1816
|
+
type: 'object',
|
|
1817
|
+
required: ['description', 'commits', 'references', 'breaking'],
|
|
1818
|
+
properties: {
|
|
1819
|
+
scope: { type: 'string' },
|
|
1820
|
+
description: { type: 'string' },
|
|
1821
|
+
breaking: { type: 'boolean' },
|
|
1822
|
+
commits: {
|
|
1823
|
+
type: 'array',
|
|
1824
|
+
items: commitRefSchema,
|
|
1825
|
+
},
|
|
1826
|
+
references: {
|
|
1827
|
+
type: 'array',
|
|
1828
|
+
items: issueRefSchema,
|
|
1829
|
+
},
|
|
1830
|
+
},
|
|
1831
|
+
additionalProperties: false,
|
|
1832
|
+
};
|
|
1833
|
+
/**
|
|
1834
|
+
* JSON Schema for a ChangelogSection.
|
|
1835
|
+
*/
|
|
1836
|
+
const changelogSectionSchema = {
|
|
1837
|
+
type: 'object',
|
|
1838
|
+
required: ['type', 'heading', 'items'],
|
|
1839
|
+
properties: {
|
|
1840
|
+
type: {
|
|
1841
|
+
type: 'string',
|
|
1842
|
+
enum: [
|
|
1843
|
+
'breaking',
|
|
1844
|
+
'features',
|
|
1845
|
+
'fixes',
|
|
1846
|
+
'performance',
|
|
1847
|
+
'documentation',
|
|
1848
|
+
'deprecations',
|
|
1849
|
+
'refactoring',
|
|
1850
|
+
'tests',
|
|
1851
|
+
'build',
|
|
1852
|
+
'ci',
|
|
1853
|
+
'chores',
|
|
1854
|
+
'other',
|
|
1855
|
+
],
|
|
1856
|
+
},
|
|
1857
|
+
heading: { type: 'string' },
|
|
1858
|
+
items: {
|
|
1859
|
+
type: 'array',
|
|
1860
|
+
items: changelogItemSchema,
|
|
1861
|
+
},
|
|
1862
|
+
},
|
|
1863
|
+
additionalProperties: false,
|
|
1864
|
+
};
|
|
1865
|
+
/**
|
|
1866
|
+
* JSON Schema for a ChangelogEntry.
|
|
1867
|
+
*/
|
|
1868
|
+
const changelogEntrySchema = {
|
|
1869
|
+
type: 'object',
|
|
1870
|
+
required: ['version', 'date', 'unreleased', 'sections'],
|
|
1871
|
+
properties: {
|
|
1872
|
+
version: { type: 'string' },
|
|
1873
|
+
date: { type: ['string', 'null'] },
|
|
1874
|
+
unreleased: { type: 'boolean' },
|
|
1875
|
+
compareUrl: { type: 'string' },
|
|
1876
|
+
sections: {
|
|
1877
|
+
type: 'array',
|
|
1878
|
+
items: changelogSectionSchema,
|
|
1879
|
+
},
|
|
1880
|
+
rawContent: { type: 'string' },
|
|
1881
|
+
},
|
|
1882
|
+
additionalProperties: false,
|
|
1883
|
+
};
|
|
1884
|
+
/**
|
|
1885
|
+
* JSON Schema for ChangelogHeader.
|
|
1886
|
+
*/
|
|
1887
|
+
const changelogHeaderSchema = {
|
|
1888
|
+
type: 'object',
|
|
1889
|
+
required: ['title', 'description', 'links'],
|
|
1890
|
+
properties: {
|
|
1891
|
+
title: { type: 'string' },
|
|
1892
|
+
description: {
|
|
1893
|
+
type: 'array',
|
|
1894
|
+
items: { type: 'string' },
|
|
1895
|
+
},
|
|
1896
|
+
links: {
|
|
1897
|
+
type: 'array',
|
|
1898
|
+
items: changelogLinkSchema,
|
|
1899
|
+
},
|
|
1900
|
+
},
|
|
1901
|
+
additionalProperties: false,
|
|
1902
|
+
};
|
|
1903
|
+
/**
|
|
1904
|
+
* JSON Schema for ChangelogMetadata.
|
|
1905
|
+
*/
|
|
1906
|
+
const changelogMetadataSchema = {
|
|
1907
|
+
type: 'object',
|
|
1908
|
+
required: ['format', 'isConventional', 'warnings'],
|
|
1909
|
+
properties: {
|
|
1910
|
+
format: {
|
|
1911
|
+
type: 'string',
|
|
1912
|
+
enum: ['keep-a-changelog', 'conventional', 'custom', 'unknown'],
|
|
1913
|
+
},
|
|
1914
|
+
isConventional: { type: 'boolean' },
|
|
1915
|
+
repositoryUrl: { type: 'string' },
|
|
1916
|
+
packageName: { type: 'string' },
|
|
1917
|
+
warnings: {
|
|
1918
|
+
type: 'array',
|
|
1919
|
+
items: { type: 'string' },
|
|
1920
|
+
},
|
|
1921
|
+
},
|
|
1922
|
+
additionalProperties: false,
|
|
1923
|
+
};
|
|
1924
|
+
/**
|
|
1925
|
+
* JSON Schema for a complete Changelog document.
|
|
1926
|
+
* Used for validation and format compatibility checking.
|
|
1927
|
+
*/
|
|
1928
|
+
const changelogSchema = {
|
|
1929
|
+
type: 'object',
|
|
1930
|
+
required: ['header', 'entries', 'metadata'],
|
|
1931
|
+
properties: {
|
|
1932
|
+
source: { type: 'string' },
|
|
1933
|
+
header: changelogHeaderSchema,
|
|
1934
|
+
entries: {
|
|
1935
|
+
type: 'array',
|
|
1936
|
+
items: changelogEntrySchema,
|
|
1937
|
+
},
|
|
1938
|
+
metadata: changelogMetadataSchema,
|
|
1939
|
+
},
|
|
1940
|
+
additionalProperties: false,
|
|
1941
|
+
};
|
|
1942
|
+
/**
|
|
1943
|
+
* Validates a changelog object against the schema.
|
|
1944
|
+
*
|
|
1945
|
+
* @param changelog - The changelog object to validate
|
|
1946
|
+
* @returns Validation result with any errors
|
|
1947
|
+
*
|
|
1948
|
+
* @example
|
|
1949
|
+
* ```ts
|
|
1950
|
+
* const result = validateChangelog(myChangelog)
|
|
1951
|
+
* if (!result.valid) {
|
|
1952
|
+
* console.log('Validation errors:', result.errors)
|
|
1953
|
+
* }
|
|
1954
|
+
* ```
|
|
1955
|
+
*/
|
|
1956
|
+
function validateChangelog(changelog) {
|
|
1957
|
+
return validate(changelog, changelogSchema);
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Checks if two changelogs have compatible schemas.
|
|
1961
|
+
* Used to detect format incompatibilities before merge/compare.
|
|
1962
|
+
*
|
|
1963
|
+
* @param source - The source changelog
|
|
1964
|
+
* @param target - The target changelog
|
|
1965
|
+
* @returns Compatibility result with any differences found
|
|
1966
|
+
*
|
|
1967
|
+
* @example
|
|
1968
|
+
* ```ts
|
|
1969
|
+
* const result = checkSchemaCompatibility(mainChangelog, branchChangelog)
|
|
1970
|
+
* if (!result.compatible) {
|
|
1971
|
+
* console.log('Schema differences:', result.differences)
|
|
1972
|
+
* }
|
|
1973
|
+
* ```
|
|
1974
|
+
*/
|
|
1975
|
+
function checkSchemaCompatibility(source, target) {
|
|
1976
|
+
const differences = [];
|
|
1977
|
+
// Check format compatibility
|
|
1978
|
+
if (source.metadata.format !== target.metadata.format) {
|
|
1979
|
+
differences.push({
|
|
1980
|
+
path: 'metadata.format',
|
|
1981
|
+
type: 'type-mismatch',
|
|
1982
|
+
sourceType: source.metadata.format,
|
|
1983
|
+
targetType: target.metadata.format,
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
// Check header structure
|
|
1987
|
+
if (source.header.title !== target.header.title) ;
|
|
1988
|
+
// Check section types used
|
|
1989
|
+
const sourceSectionTypes = createSet();
|
|
1990
|
+
const targetSectionTypes = createSet();
|
|
1991
|
+
for (const entry of source.entries) {
|
|
1992
|
+
for (const section of entry.sections) {
|
|
1993
|
+
sourceSectionTypes.add(section.type);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
for (const entry of target.entries) {
|
|
1997
|
+
for (const section of entry.sections) {
|
|
1998
|
+
targetSectionTypes.add(section.type);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
// Find section types in source but not in target
|
|
2002
|
+
for (const type of sourceSectionTypes) {
|
|
2003
|
+
if (!targetSectionTypes.has(type)) {
|
|
2004
|
+
differences.push({
|
|
2005
|
+
path: `sections[type=${type}]`,
|
|
2006
|
+
type: 'missing-property',
|
|
2007
|
+
sourceType: type,
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
// Find section types in target but not in source
|
|
2012
|
+
for (const type of targetSectionTypes) {
|
|
2013
|
+
if (!sourceSectionTypes.has(type)) {
|
|
2014
|
+
differences.push({
|
|
2015
|
+
path: `sections[type=${type}]`,
|
|
2016
|
+
type: 'extra-property',
|
|
2017
|
+
targetType: type,
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
return {
|
|
2022
|
+
compatible: differences.length === 0,
|
|
2023
|
+
differences,
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
exports.SECTION_HEADINGS = SECTION_HEADINGS;
|
|
2028
|
+
exports.SECTION_TYPE_MAP = SECTION_TYPE_MAP;
|
|
2029
|
+
exports.changelogSchema = changelogSchema;
|
|
2030
|
+
exports.checkSchemaCompatibility = checkSchemaCompatibility;
|
|
2031
|
+
exports.createChangelog = createChangelog;
|
|
2032
|
+
exports.createChangelogEntry = createChangelogEntry;
|
|
2033
|
+
exports.createChangelogItem = createChangelogItem;
|
|
2034
|
+
exports.createChangelogLink = createChangelogLink;
|
|
2035
|
+
exports.createChangelogSection = createChangelogSection;
|
|
2036
|
+
exports.createCommitRef = createCommitRef;
|
|
2037
|
+
exports.createEmptyChangelog = createEmptyChangelog;
|
|
2038
|
+
exports.createIssueRef = createIssueRef;
|
|
2039
|
+
exports.createUnreleasedEntry = createUnreleasedEntry;
|
|
2040
|
+
exports.getSectionType = getSectionType;
|
|
2041
|
+
exports.getShortHash = getShortHash;
|
|
2042
|
+
exports.validateChangelog = validateChangelog;
|
|
2043
|
+
//# sourceMappingURL=index.cjs.js.map
|