@helpers4/license 3.1.1-alpha.0 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/lib/index.d.ts +3 -3
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/llms.txt +177 -0
- package/meta/api.json +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ A set of helpers for working with URLs.
|
|
|
28
28
|
- [function](../function)
|
|
29
29
|
- [guard](../guard)
|
|
30
30
|
- [id](../id)
|
|
31
|
+
- [json](../json)
|
|
31
32
|
- [map](../map)
|
|
32
33
|
- [markdown](../markdown)
|
|
33
34
|
- [node](../node)
|
|
@@ -56,6 +57,7 @@ A set of helpers for working with URLs.
|
|
|
56
57
|
| function | [@helpers4/function](https://www.npmjs.com/package/@helpers4/function) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/function) | Function utilities and type guards |
|
|
57
58
|
| guard | [@helpers4/guard](https://www.npmjs.com/package/@helpers4/guard) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/guard) | Runtime type guard utilities |
|
|
58
59
|
| id | [@helpers4/id](https://www.npmjs.com/package/@helpers4/id) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/id) | Unique identifier generation utilities |
|
|
60
|
+
| json | [@helpers4/json](https://www.npmjs.com/package/@helpers4/json) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/json) | JSON parsing and serialization, with its common variants |
|
|
59
61
|
| license | [@helpers4/license](https://www.npmjs.com/package/@helpers4/license) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/license) | License string classification and comparison |
|
|
60
62
|
| map | [@helpers4/map](https://www.npmjs.com/package/@helpers4/map) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/map) | Native Map manipulation utilities |
|
|
61
63
|
| markdown | [@helpers4/markdown](https://www.npmjs.com/package/@helpers4/markdown) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/markdown) | Markdown formatting and escaping utilities |
|
package/lib/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* @example
|
|
21
21
|
* agree('custom', 'MIT')
|
|
22
22
|
* // => true (a vague claim is never itself a conflict)
|
|
23
|
-
* @since
|
|
23
|
+
* @since 3.1.2
|
|
24
24
|
*/
|
|
25
25
|
declare function agree(a: string, b: string): boolean;
|
|
26
26
|
|
|
@@ -50,7 +50,7 @@ declare function agree(a: string, b: string): boolean;
|
|
|
50
50
|
* @example
|
|
51
51
|
* families('custom:Acme End User License')
|
|
52
52
|
* // => Set(1) { 'unknown' }
|
|
53
|
-
* @since
|
|
53
|
+
* @since 3.1.2
|
|
54
54
|
*/
|
|
55
55
|
declare function families(raw: string): Set<string>;
|
|
56
56
|
|
|
@@ -71,7 +71,7 @@ declare function families(raw: string): Set<string>;
|
|
|
71
71
|
* @example
|
|
72
72
|
* isKnown('custom:Acme End User License')
|
|
73
73
|
* // => false
|
|
74
|
-
* @since
|
|
74
|
+
* @since 3.1.2
|
|
75
75
|
*/
|
|
76
76
|
declare function isKnown(raw: string): boolean;
|
|
77
77
|
|
package/lib/index.js
CHANGED
|
@@ -156,7 +156,7 @@ function splitTokens(raw) {
|
|
|
156
156
|
* @example
|
|
157
157
|
* families('custom:Acme End User License')
|
|
158
158
|
* // => Set(1) { 'unknown' }
|
|
159
|
-
* @since
|
|
159
|
+
* @since 3.1.2
|
|
160
160
|
*/
|
|
161
161
|
function families(raw) {
|
|
162
162
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -194,7 +194,7 @@ function families(raw) {
|
|
|
194
194
|
* @example
|
|
195
195
|
* agree('custom', 'MIT')
|
|
196
196
|
* // => true (a vague claim is never itself a conflict)
|
|
197
|
-
* @since
|
|
197
|
+
* @since 3.1.2
|
|
198
198
|
*/
|
|
199
199
|
function agree(a, b) {
|
|
200
200
|
const knownA = new Set([...families(a)].filter((f) => f !== UNKNOWN_FAMILY));
|
|
@@ -222,7 +222,7 @@ function agree(a, b) {
|
|
|
222
222
|
* @example
|
|
223
223
|
* isKnown('custom:Acme End User License')
|
|
224
224
|
* // => false
|
|
225
|
-
* @since
|
|
225
|
+
* @since 3.1.2
|
|
226
226
|
*/
|
|
227
227
|
function isKnown(raw) {
|
|
228
228
|
for (const family of families(raw)) if (family !== "unknown") return true;
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../helpers/license/_unknownFamily.ts","../../../helpers/license/families.ts","../../../helpers/license/agree.ts","../../../helpers/license/isKnown.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Sentinel family for a license token that carries no comparable content — `custom`, a bare\n * vendor name inside `custom:<...>`/`LicenseRef-<...>`, or an explicit \"we don't know\". Two\n * unknown tokens are never treated as either agreeing or conflicting with each other, since\n * neither is a real claim to compare against the other.\n *\n * Not exported from the package — `families()`, `agree()` and `isKnown()` already fully\n * encapsulate this concept; a consumer never needs the sentinel's actual value.\n * @ignore\n */\nexport const UNKNOWN_FAMILY = 'unknown';\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { UNKNOWN_FAMILY } from './_unknownFamily';\n\n// Longest/most specific alias keys first within a family so a substring match never lets a\n// shorter, less specific alias shadow a longer one that also matches the same input.\nconst FAMILY_ALIASES: Record<string, readonly string[]> = {\n mit: ['mit-0', 'mit'],\n gpl: [\n 'gpl-3.0-or-later', 'gpl-3.0-only', 'gpl-2.0-or-later', 'gpl-2.0-only',\n 'gpl-1.0-or-later', 'gpl-3.0+', 'gpl-2.0+', 'gpl-3.0', 'gpl-2.0',\n 'gplv3', 'gplv2', 'gpl3+', 'gpl3', 'gpl2', 'gpl-3', 'gpl-2', 'gpl',\n ],\n lgpl: [\n 'lgpl-3.0-or-later', 'lgpl-3.0-only', 'lgpl-2.1-or-later', 'lgpl-2.1-only',\n 'lgpl-2.0-or-later', 'lgpl-2.0-only', 'lgpl-3.0+', 'lgpl-2.1+', 'lgpl-3.0', 'lgpl-2.1',\n 'lgplv3', 'lgplv2.1', 'lgpl3', 'lgpl2.1', 'lgpl2', 'lgpl',\n ],\n agpl: ['agpl-3.0-or-later', 'agpl-3.0-only', 'agpl-3.0', 'agplv3', 'agpl3', 'agpl'],\n bsd: [\n 'bsd-3-clause', 'bsd-2-clause', '0bsd', 'bsd3', 'bsd 3-clause',\n 'bsd 2-clause', 'bsd-3', 'bsd',\n ],\n apache: ['apache-2.0', 'apache license 2.0', 'apache license, version 2.0', 'apache2', 'apache 2.0', 'apache'],\n mpl: ['mpl-2.0', 'mpl2', 'mpl'],\n artistic: ['artistic-2.0', 'artistic2.0', 'perlartistic', 'artistic'],\n isc: ['isc'],\n unlicense: ['unlicense'],\n zlib: ['zlib'],\n wtfpl: ['wtfpl'],\n php: ['php'],\n epl: ['epl'],\n cc: ['cc0-1.0', 'cc0', 'cc-by-sa-4.0', 'cc-by-nc-sa-4.0', 'cc-by-sa', 'ccpl'],\n 'public-domain': ['public domain'],\n boost: ['bsl-1.0', 'boost'],\n eupl: ['eupl-1.2', 'eupl'],\n postgresql: ['postgresql'],\n psf: ['psf-2.0', 'psf'],\n gfdl: ['gfdl-1.3', 'gfdl'],\n sspl: ['sspl-1.0', 'sspl'],\n busl: ['busl-1.1', 'busl'],\n};\n\n// Non-informative tokens — a real license claim was made, but it carries no comparable content\n// (\"custom\", a bare vendor name inside `custom:<...>`/`LicenseRef-<...>`, or an explicit \"we\n// don't know\"). Checked first, before the alias table, so e.g. \"custom:MIT\" reads as\n// UNKNOWN_FAMILY rather than incorrectly matching MIT's alias substring — a `custom:<...>`\n// convention names the *vendor's own license text*, not necessarily the SPDX license the name\n// resembles.\nconst UNKNOWN_PATTERNS = [/^custom\\b/, /^licenseref-/, /unknown/, /^none$/, /^various$/, /^unknow$/];\n\nfunction matchAlias(normalized: string): string | undefined {\n for (const [family, aliases] of Object.entries(FAMILY_ALIASES)) {\n if (aliases.includes(normalized)) return family;\n }\n return undefined;\n}\n\n// Splits an SPDX-ish compound expression (\"GPL-3.0+ AND LGPL-3.0+\", or a lowercase \"GPL-2.0+ and\n// GFDL-1.3\") into its individual license tokens. `WITH` exceptions (\"Apache-2.0 WITH\n// LLVM-exception\") are dropped entirely — the exception clause never itself resembles a distinct\n// license family, so it would otherwise either land in UNKNOWN_FAMILY for no reason or dilute the\n// comparison with a token that isn't really a separate license claim.\nfunction splitTokens(raw: string): string[] {\n return raw\n .split(/\\s+(?:and|or)\\s+/i)\n .map((token) => token.split(/\\s+with\\s+/i)[0].trim())\n .filter((token) => token.length > 0);\n}\n\n/**\n * Groups a raw, possibly compound license string into its coarse family set — e.g.\n * `\"GPL-3.0-or-later AND LGPL-3.0-or-later\"` yields `{\"gpl\", \"lgpl\"}`. Two sources reporting the\n * same real license under different notation (`\"GPL3\"` vs `\"GPL-3.0-or-later\"`, `\"GPL\"` vs\n * `\"GPL2\"`) yield the same family, so they read as agreeing rather than conflicting.\n *\n * Proprietary/custom/unknown tokens all collapse to a single shared unknown-family sentinel\n * rather than each becoming their own one-off family, so a batch of otherwise-identical\n * `\"custom:<vendor>\"` strings don't look like dozens of distinct disagreeing licenses. Use\n * {@link isKnown} to check whether any real (non-sentinel) family was found.\n * @param raw - A raw license string, single (`\"MIT\"`) or compound (`\"GPL-3.0+ AND LGPL-3.0+\"`)\n * @returns The set of families found; empty only when `raw` has no tokens at all (e.g. an empty string)\n * @example\n * families('GPL3')\n * // => Set(1) { 'gpl' }\n * @example\n * families('GPL-3.0-or-later AND LGPL-3.0-or-later')\n * // => Set(2) { 'gpl', 'lgpl' }\n * @example\n * families('custom:Acme End User License')\n * // => Set(1) { 'unknown' }\n * @since next\n */\nexport function families(raw: string): Set<string> {\n const result = new Set<string>();\n for (const token of splitTokens(raw)) {\n const normalized = token.toLowerCase();\n if (UNKNOWN_PATTERNS.some((pattern) => pattern.test(normalized))) {\n result.add(UNKNOWN_FAMILY);\n continue;\n }\n result.add(matchAlias(normalized) ?? UNKNOWN_FAMILY);\n }\n return result;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { UNKNOWN_FAMILY } from './_unknownFamily';\nimport { families } from './families';\n\n/**\n * Whether two raw license strings agree closely enough to count as \"no real conflict\" — true\n * when their family sets share at least one real (known) family, or when either side is entirely\n * unknown (e.g. `\"custom\"`/`\"unknown\"`) — a vague claim is never itself evidence of disagreement.\n * Only two *different*, both-known family sets (e.g. `bsd` vs `apache`) count as a real conflict.\n * @param a - The first raw license string\n * @param b - The second raw license string\n * @returns `true` when the two strings can be treated as the same license (or too vague to tell), `false` on a real family conflict\n * @example\n * agree('GPL3', 'GPL-3.0-or-later')\n * // => true\n * @example\n * agree('BSD-2-Clause', 'Apache-2.0')\n * // => false\n * @example\n * agree('custom', 'MIT')\n * // => true (a vague claim is never itself a conflict)\n * @since next\n */\nexport function agree(a: string, b: string): boolean {\n const knownA = new Set([...families(a)].filter((f) => f !== UNKNOWN_FAMILY));\n const knownB = new Set([...families(b)].filter((f) => f !== UNKNOWN_FAMILY));\n\n if (knownA.size === 0 || knownB.size === 0) return true;\n for (const family of knownA) if (knownB.has(family)) return true;\n return false;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { UNKNOWN_FAMILY } from './_unknownFamily';\nimport { families } from './families';\n\n/**\n * Whether a raw license string resolves to at least one recognized family — `false` for an\n * empty string, a purely non-informative claim (`\"custom\"`, `\"unknown\"`, `\"LicenseRef-EULA\"`,\n * ...), or a compound expression where every token is non-informative.\n * @param raw - The raw license string to check\n * @returns `true` if at least one token in `raw` resolves to a real family\n * @example\n * isKnown('GPL-3.0-or-later')\n * // => true\n * @example\n * isKnown('custom:Acme End User License')\n * // => false\n * @since next\n */\nexport function isKnown(raw: string): boolean {\n for (const family of families(raw)) {\n if (family !== UNKNOWN_FAMILY) return true;\n }\n return false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAa,iBAAiB;;;;;;;;ACN9B,IAAM,iBAAoD;CACxD,KAAK,CAAC,SAAS,KAAK;CACpB,KAAK;EACH;EAAoB;EAAgB;EAAoB;EACxD;EAAoB;EAAY;EAAY;EAAW;EACvD;EAAS;EAAS;EAAS;EAAQ;EAAQ;EAAS;EAAS;CAC/D;CACA,MAAM;EACJ;EAAqB;EAAiB;EAAqB;EAC3D;EAAqB;EAAiB;EAAa;EAAa;EAAY;EAC5E;EAAU;EAAY;EAAS;EAAW;EAAS;CACrD;CACA,MAAM;EAAC;EAAqB;EAAiB;EAAY;EAAU;EAAS;CAAM;CAClF,KAAK;EACH;EAAgB;EAAgB;EAAQ;EAAQ;EAChD;EAAgB;EAAS;CAC3B;CACA,QAAQ;EAAC;EAAc;EAAsB;EAA+B;EAAW;EAAc;CAAQ;CAC7G,KAAK;EAAC;EAAW;EAAQ;CAAK;CAC9B,UAAU;EAAC;EAAgB;EAAe;EAAgB;CAAU;CACpE,KAAK,CAAC,KAAK;CACX,WAAW,CAAC,WAAW;CACvB,MAAM,CAAC,MAAM;CACb,OAAO,CAAC,OAAO;CACf,KAAK,CAAC,KAAK;CACX,KAAK,CAAC,KAAK;CACX,IAAI;EAAC;EAAW;EAAO;EAAgB;EAAmB;EAAY;CAAM;CAC5E,iBAAiB,CAAC,eAAe;CACjC,OAAO,CAAC,WAAW,OAAO;CAC1B,MAAM,CAAC,YAAY,MAAM;CACzB,YAAY,CAAC,YAAY;CACzB,KAAK,CAAC,WAAW,KAAK;CACtB,MAAM,CAAC,YAAY,MAAM;CACzB,MAAM,CAAC,YAAY,MAAM;CACzB,MAAM,CAAC,YAAY,MAAM;AAC3B;AAQA,IAAM,mBAAmB;CAAC;CAAa;CAAgB;CAAW;CAAU;CAAa;AAAU;AAEnG,SAAS,WAAW,YAAwC;CAC1D,KAAK,MAAM,CAAC,QAAQ,YAAY,OAAO,QAAQ,cAAc,GAC3D,IAAI,QAAQ,SAAS,UAAU,GAAG,OAAO;AAG7C;AAOA,SAAS,YAAY,KAAuB;CAC1C,OAAO,IACJ,MAAM,mBAAmB,CAAC,CAC1B,KAAK,UAAU,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CACpD,QAAQ,UAAU,MAAM,SAAS,CAAC;AACvC;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,SAAS,KAA0B;CACjD,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,SAAS,YAAY,GAAG,GAAG;EACpC,MAAM,aAAa,MAAM,YAAY;EACrC,IAAI,iBAAiB,MAAM,YAAY,QAAQ,KAAK,UAAU,CAAC,GAAG;GAChE,OAAO,IAAI,cAAc;GACzB;EACF;EACA,OAAO,IAAI,WAAW,UAAU,KAAA,SAAmB;CACrD;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;AChFA,SAAgB,MAAM,GAAW,GAAoB;CACnD,MAAM,SAAS,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,cAAc,CAAC;CAC3E,MAAM,SAAS,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,cAAc,CAAC;CAE3E,IAAI,OAAO,SAAS,KAAK,OAAO,SAAS,GAAG,OAAO;CACnD,KAAK,MAAM,UAAU,QAAQ,IAAI,OAAO,IAAI,MAAM,GAAG,OAAO;CAC5D,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;ACZA,SAAgB,QAAQ,KAAsB;CAC5C,KAAK,MAAM,UAAU,SAAS,GAAG,GAC/B,IAAI,WAAA,WAA2B,OAAO;CAExC,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../helpers/license/_unknownFamily.ts","../../../helpers/license/families.ts","../../../helpers/license/agree.ts","../../../helpers/license/isKnown.ts"],"sourcesContent":["/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\n/**\n * Sentinel family for a license token that carries no comparable content — `custom`, a bare\n * vendor name inside `custom:<...>`/`LicenseRef-<...>`, or an explicit \"we don't know\". Two\n * unknown tokens are never treated as either agreeing or conflicting with each other, since\n * neither is a real claim to compare against the other.\n *\n * Not exported from the package — `families()`, `agree()` and `isKnown()` already fully\n * encapsulate this concept; a consumer never needs the sentinel's actual value.\n * @ignore\n */\nexport const UNKNOWN_FAMILY = 'unknown';\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { UNKNOWN_FAMILY } from './_unknownFamily';\n\n// Longest/most specific alias keys first within a family so a substring match never lets a\n// shorter, less specific alias shadow a longer one that also matches the same input.\nconst FAMILY_ALIASES: Record<string, readonly string[]> = {\n mit: ['mit-0', 'mit'],\n gpl: [\n 'gpl-3.0-or-later', 'gpl-3.0-only', 'gpl-2.0-or-later', 'gpl-2.0-only',\n 'gpl-1.0-or-later', 'gpl-3.0+', 'gpl-2.0+', 'gpl-3.0', 'gpl-2.0',\n 'gplv3', 'gplv2', 'gpl3+', 'gpl3', 'gpl2', 'gpl-3', 'gpl-2', 'gpl',\n ],\n lgpl: [\n 'lgpl-3.0-or-later', 'lgpl-3.0-only', 'lgpl-2.1-or-later', 'lgpl-2.1-only',\n 'lgpl-2.0-or-later', 'lgpl-2.0-only', 'lgpl-3.0+', 'lgpl-2.1+', 'lgpl-3.0', 'lgpl-2.1',\n 'lgplv3', 'lgplv2.1', 'lgpl3', 'lgpl2.1', 'lgpl2', 'lgpl',\n ],\n agpl: ['agpl-3.0-or-later', 'agpl-3.0-only', 'agpl-3.0', 'agplv3', 'agpl3', 'agpl'],\n bsd: [\n 'bsd-3-clause', 'bsd-2-clause', '0bsd', 'bsd3', 'bsd 3-clause',\n 'bsd 2-clause', 'bsd-3', 'bsd',\n ],\n apache: ['apache-2.0', 'apache license 2.0', 'apache license, version 2.0', 'apache2', 'apache 2.0', 'apache'],\n mpl: ['mpl-2.0', 'mpl2', 'mpl'],\n artistic: ['artistic-2.0', 'artistic2.0', 'perlartistic', 'artistic'],\n isc: ['isc'],\n unlicense: ['unlicense'],\n zlib: ['zlib'],\n wtfpl: ['wtfpl'],\n php: ['php'],\n epl: ['epl'],\n cc: ['cc0-1.0', 'cc0', 'cc-by-sa-4.0', 'cc-by-nc-sa-4.0', 'cc-by-sa', 'ccpl'],\n 'public-domain': ['public domain'],\n boost: ['bsl-1.0', 'boost'],\n eupl: ['eupl-1.2', 'eupl'],\n postgresql: ['postgresql'],\n psf: ['psf-2.0', 'psf'],\n gfdl: ['gfdl-1.3', 'gfdl'],\n sspl: ['sspl-1.0', 'sspl'],\n busl: ['busl-1.1', 'busl'],\n};\n\n// Non-informative tokens — a real license claim was made, but it carries no comparable content\n// (\"custom\", a bare vendor name inside `custom:<...>`/`LicenseRef-<...>`, or an explicit \"we\n// don't know\"). Checked first, before the alias table, so e.g. \"custom:MIT\" reads as\n// UNKNOWN_FAMILY rather than incorrectly matching MIT's alias substring — a `custom:<...>`\n// convention names the *vendor's own license text*, not necessarily the SPDX license the name\n// resembles.\nconst UNKNOWN_PATTERNS = [/^custom\\b/, /^licenseref-/, /unknown/, /^none$/, /^various$/, /^unknow$/];\n\nfunction matchAlias(normalized: string): string | undefined {\n for (const [family, aliases] of Object.entries(FAMILY_ALIASES)) {\n if (aliases.includes(normalized)) return family;\n }\n return undefined;\n}\n\n// Splits an SPDX-ish compound expression (\"GPL-3.0+ AND LGPL-3.0+\", or a lowercase \"GPL-2.0+ and\n// GFDL-1.3\") into its individual license tokens. `WITH` exceptions (\"Apache-2.0 WITH\n// LLVM-exception\") are dropped entirely — the exception clause never itself resembles a distinct\n// license family, so it would otherwise either land in UNKNOWN_FAMILY for no reason or dilute the\n// comparison with a token that isn't really a separate license claim.\nfunction splitTokens(raw: string): string[] {\n return raw\n .split(/\\s+(?:and|or)\\s+/i)\n .map((token) => token.split(/\\s+with\\s+/i)[0].trim())\n .filter((token) => token.length > 0);\n}\n\n/**\n * Groups a raw, possibly compound license string into its coarse family set — e.g.\n * `\"GPL-3.0-or-later AND LGPL-3.0-or-later\"` yields `{\"gpl\", \"lgpl\"}`. Two sources reporting the\n * same real license under different notation (`\"GPL3\"` vs `\"GPL-3.0-or-later\"`, `\"GPL\"` vs\n * `\"GPL2\"`) yield the same family, so they read as agreeing rather than conflicting.\n *\n * Proprietary/custom/unknown tokens all collapse to a single shared unknown-family sentinel\n * rather than each becoming their own one-off family, so a batch of otherwise-identical\n * `\"custom:<vendor>\"` strings don't look like dozens of distinct disagreeing licenses. Use\n * {@link isKnown} to check whether any real (non-sentinel) family was found.\n * @param raw - A raw license string, single (`\"MIT\"`) or compound (`\"GPL-3.0+ AND LGPL-3.0+\"`)\n * @returns The set of families found; empty only when `raw` has no tokens at all (e.g. an empty string)\n * @example\n * families('GPL3')\n * // => Set(1) { 'gpl' }\n * @example\n * families('GPL-3.0-or-later AND LGPL-3.0-or-later')\n * // => Set(2) { 'gpl', 'lgpl' }\n * @example\n * families('custom:Acme End User License')\n * // => Set(1) { 'unknown' }\n * @since 3.1.2\n */\nexport function families(raw: string): Set<string> {\n const result = new Set<string>();\n for (const token of splitTokens(raw)) {\n const normalized = token.toLowerCase();\n if (UNKNOWN_PATTERNS.some((pattern) => pattern.test(normalized))) {\n result.add(UNKNOWN_FAMILY);\n continue;\n }\n result.add(matchAlias(normalized) ?? UNKNOWN_FAMILY);\n }\n return result;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { UNKNOWN_FAMILY } from './_unknownFamily';\nimport { families } from './families';\n\n/**\n * Whether two raw license strings agree closely enough to count as \"no real conflict\" — true\n * when their family sets share at least one real (known) family, or when either side is entirely\n * unknown (e.g. `\"custom\"`/`\"unknown\"`) — a vague claim is never itself evidence of disagreement.\n * Only two *different*, both-known family sets (e.g. `bsd` vs `apache`) count as a real conflict.\n * @param a - The first raw license string\n * @param b - The second raw license string\n * @returns `true` when the two strings can be treated as the same license (or too vague to tell), `false` on a real family conflict\n * @example\n * agree('GPL3', 'GPL-3.0-or-later')\n * // => true\n * @example\n * agree('BSD-2-Clause', 'Apache-2.0')\n * // => false\n * @example\n * agree('custom', 'MIT')\n * // => true (a vague claim is never itself a conflict)\n * @since 3.1.2\n */\nexport function agree(a: string, b: string): boolean {\n const knownA = new Set([...families(a)].filter((f) => f !== UNKNOWN_FAMILY));\n const knownB = new Set([...families(b)].filter((f) => f !== UNKNOWN_FAMILY));\n\n if (knownA.size === 0 || knownB.size === 0) return true;\n for (const family of knownA) if (knownB.has(family)) return true;\n return false;\n}\n","/**\n * This file is part of helpers4.\n * Copyright (C) 2025 baxyz\n * SPDX-License-Identifier: LGPL-3.0-or-later\n */\n\nimport { UNKNOWN_FAMILY } from './_unknownFamily';\nimport { families } from './families';\n\n/**\n * Whether a raw license string resolves to at least one recognized family — `false` for an\n * empty string, a purely non-informative claim (`\"custom\"`, `\"unknown\"`, `\"LicenseRef-EULA\"`,\n * ...), or a compound expression where every token is non-informative.\n * @param raw - The raw license string to check\n * @returns `true` if at least one token in `raw` resolves to a real family\n * @example\n * isKnown('GPL-3.0-or-later')\n * // => true\n * @example\n * isKnown('custom:Acme End User License')\n * // => false\n * @since 3.1.2\n */\nexport function isKnown(raw: string): boolean {\n for (const family of families(raw)) {\n if (family !== UNKNOWN_FAMILY) return true;\n }\n return false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAa,iBAAiB;;;;;;;;ACN9B,IAAM,iBAAoD;CACxD,KAAK,CAAC,SAAS,KAAK;CACpB,KAAK;EACH;EAAoB;EAAgB;EAAoB;EACxD;EAAoB;EAAY;EAAY;EAAW;EACvD;EAAS;EAAS;EAAS;EAAQ;EAAQ;EAAS;EAAS;CAC/D;CACA,MAAM;EACJ;EAAqB;EAAiB;EAAqB;EAC3D;EAAqB;EAAiB;EAAa;EAAa;EAAY;EAC5E;EAAU;EAAY;EAAS;EAAW;EAAS;CACrD;CACA,MAAM;EAAC;EAAqB;EAAiB;EAAY;EAAU;EAAS;CAAM;CAClF,KAAK;EACH;EAAgB;EAAgB;EAAQ;EAAQ;EAChD;EAAgB;EAAS;CAC3B;CACA,QAAQ;EAAC;EAAc;EAAsB;EAA+B;EAAW;EAAc;CAAQ;CAC7G,KAAK;EAAC;EAAW;EAAQ;CAAK;CAC9B,UAAU;EAAC;EAAgB;EAAe;EAAgB;CAAU;CACpE,KAAK,CAAC,KAAK;CACX,WAAW,CAAC,WAAW;CACvB,MAAM,CAAC,MAAM;CACb,OAAO,CAAC,OAAO;CACf,KAAK,CAAC,KAAK;CACX,KAAK,CAAC,KAAK;CACX,IAAI;EAAC;EAAW;EAAO;EAAgB;EAAmB;EAAY;CAAM;CAC5E,iBAAiB,CAAC,eAAe;CACjC,OAAO,CAAC,WAAW,OAAO;CAC1B,MAAM,CAAC,YAAY,MAAM;CACzB,YAAY,CAAC,YAAY;CACzB,KAAK,CAAC,WAAW,KAAK;CACtB,MAAM,CAAC,YAAY,MAAM;CACzB,MAAM,CAAC,YAAY,MAAM;CACzB,MAAM,CAAC,YAAY,MAAM;AAC3B;AAQA,IAAM,mBAAmB;CAAC;CAAa;CAAgB;CAAW;CAAU;CAAa;AAAU;AAEnG,SAAS,WAAW,YAAwC;CAC1D,KAAK,MAAM,CAAC,QAAQ,YAAY,OAAO,QAAQ,cAAc,GAC3D,IAAI,QAAQ,SAAS,UAAU,GAAG,OAAO;AAG7C;AAOA,SAAS,YAAY,KAAuB;CAC1C,OAAO,IACJ,MAAM,mBAAmB,CAAC,CAC1B,KAAK,UAAU,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CACpD,QAAQ,UAAU,MAAM,SAAS,CAAC;AACvC;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,SAAS,KAA0B;CACjD,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,SAAS,YAAY,GAAG,GAAG;EACpC,MAAM,aAAa,MAAM,YAAY;EACrC,IAAI,iBAAiB,MAAM,YAAY,QAAQ,KAAK,UAAU,CAAC,GAAG;GAChE,OAAO,IAAI,cAAc;GACzB;EACF;EACA,OAAO,IAAI,WAAW,UAAU,KAAA,SAAmB;CACrD;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;AChFA,SAAgB,MAAM,GAAW,GAAoB;CACnD,MAAM,SAAS,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,cAAc,CAAC;CAC3E,MAAM,SAAS,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,cAAc,CAAC;CAE3E,IAAI,OAAO,SAAS,KAAK,OAAO,SAAS,GAAG,OAAO;CACnD,KAAK,MAAM,UAAU,QAAQ,IAAI,OAAO,IAAI,MAAM,GAAG,OAAO;CAC5D,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;ACZA,SAAgB,QAAQ,KAAsB;CAC5C,KAAK,MAAM,UAAU,SAAS,GAAG,GAC/B,IAAI,WAAA,WAA2B,OAAO;CAExC,OAAO;AACT"}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# @helpers4/license
|
|
2
|
+
|
|
3
|
+
> Tree-shakable TypeScript utility functions for the `license` domain.
|
|
4
|
+
> Package: `@helpers4/license` — Version: 3.1.3
|
|
5
|
+
> License: LGPL-3.0-or-later
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @helpers4/license
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @helpers4/license
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { agree, families, isKnown } from '@helpers4/license';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Functions
|
|
22
|
+
|
|
23
|
+
| Function | Description |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `agree` | Whether two raw license strings agree closely enough to count as "no real conflict" — true when thei |
|
|
26
|
+
| `families` | Groups a raw, possibly compound license string into its coarse family set — e.g. `"GPL-3.0-or-later |
|
|
27
|
+
| `isKnown` | Whether a raw license string resolves to at least one recognized family — `false` for an empty strin |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## API Reference
|
|
32
|
+
|
|
33
|
+
### `agree`
|
|
34
|
+
|
|
35
|
+
Whether two raw license strings agree closely enough to count as "no real conflict" — true
|
|
36
|
+
when their family sets share at least one real (known) family, or when either side is entirely
|
|
37
|
+
unknown (e.g. `"custom"`/`"unknown"`) — a vague claim is never itself evidence of disagreement.
|
|
38
|
+
Only two *different*, both-known family sets (e.g. `bsd` vs `apache`) count as a real conflict.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { agree } from '@helpers4/license';
|
|
42
|
+
|
|
43
|
+
agree(a: string, b: string): boolean
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Parameters:**
|
|
47
|
+
|
|
48
|
+
- `a: string` — The first raw license string
|
|
49
|
+
- `b: string` — The second raw license string
|
|
50
|
+
|
|
51
|
+
**Returns:** `boolean` — `true` when the two strings can be treated as the same license (or too vague to tell), `false` on a real family conflict
|
|
52
|
+
|
|
53
|
+
**Examples:**
|
|
54
|
+
|
|
55
|
+
*Reconcile notation drift across sources*
|
|
56
|
+
|
|
57
|
+
Two package sources reporting the same real license under different notation.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
agree('GPL3', 'GPL-3.0-or-later')
|
|
61
|
+
// => true
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
*Flag a real license conflict*
|
|
65
|
+
|
|
66
|
+
Two sources genuinely disagreeing on the license family — a real data-quality signal.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
agree('BSD-2-Clause', 'Apache-2.0')
|
|
70
|
+
// => false
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
*A vague claim is never itself a conflict*
|
|
74
|
+
|
|
75
|
+
When either side is non-informative ("custom", "unknown", ...), there is nothing real to disagree with.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
agree('custom', 'MIT')
|
|
79
|
+
// => true
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### `families`
|
|
85
|
+
|
|
86
|
+
Groups a raw, possibly compound license string into its coarse family set — e.g.
|
|
87
|
+
`"GPL-3.0-or-later AND LGPL-3.0-or-later"` yields `{"gpl", "lgpl"}`. Two sources reporting the
|
|
88
|
+
same real license under different notation (`"GPL3"` vs `"GPL-3.0-or-later"`, `"GPL"` vs
|
|
89
|
+
`"GPL2"`) yield the same family, so they read as agreeing rather than conflicting.
|
|
90
|
+
|
|
91
|
+
Proprietary/custom/unknown tokens all collapse to a single shared unknown-family sentinel
|
|
92
|
+
rather than each becoming their own one-off family, so a batch of otherwise-identical
|
|
93
|
+
`"custom:<vendor>"` strings don't look like dozens of distinct disagreeing licenses. Use
|
|
94
|
+
isKnown to check whether any real (non-sentinel) family was found.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { families } from '@helpers4/license';
|
|
98
|
+
|
|
99
|
+
families(raw: string): Set<string>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Parameters:**
|
|
103
|
+
|
|
104
|
+
- `raw: string` — A raw license string, single (`"MIT"`) or compound (`"GPL-3.0+ AND LGPL-3.0+"`)
|
|
105
|
+
|
|
106
|
+
**Returns:** `Set<string>` — The set of families found; empty only when `raw` has no tokens at all (e.g. an empty string)
|
|
107
|
+
|
|
108
|
+
**Examples:**
|
|
109
|
+
|
|
110
|
+
*Collapse notation drift into one family*
|
|
111
|
+
|
|
112
|
+
"GPL3" and "GPL-3.0-or-later" are the same real license, reported differently by two sources.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
families('GPL3')
|
|
116
|
+
// => Set(1) { 'gpl' }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
*Split a compound SPDX expression*
|
|
120
|
+
|
|
121
|
+
"AND"/"OR" (any case) separate multiple license claims; "WITH" exception clauses are dropped.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
families('GPL-3.0-or-later AND LGPL-3.0-or-later')
|
|
125
|
+
// => Set(2) { 'gpl', 'lgpl' }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
*Non-informative claims collapse to one shared "unknown" family*
|
|
129
|
+
|
|
130
|
+
A batch of different custom:<vendor> strings all read the same way — none of them is real evidence either way.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
families('custom:Acme End User License')
|
|
134
|
+
// => Set(1) { 'unknown' }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### `isKnown`
|
|
140
|
+
|
|
141
|
+
Whether a raw license string resolves to at least one recognized family — `false` for an
|
|
142
|
+
empty string, a purely non-informative claim (`"custom"`, `"unknown"`, `"LicenseRef-EULA"`,
|
|
143
|
+
...), or a compound expression where every token is non-informative.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { isKnown } from '@helpers4/license';
|
|
147
|
+
|
|
148
|
+
isKnown(raw: string): boolean
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Parameters:**
|
|
152
|
+
|
|
153
|
+
- `raw: string` — The raw license string to check
|
|
154
|
+
|
|
155
|
+
**Returns:** `boolean` — `true` if at least one token in `raw` resolves to a real family
|
|
156
|
+
|
|
157
|
+
**Examples:**
|
|
158
|
+
|
|
159
|
+
*Filter out entries with no real license information*
|
|
160
|
+
|
|
161
|
+
Handy for flagging catalog entries whose license field is not actually usable.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
['MIT', 'custom:Acme EULA', 'unknown'].filter(isKnown)
|
|
165
|
+
// => ['MIT']
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
*A vendor-specific EULA is not a recognized family*
|
|
169
|
+
|
|
170
|
+
"custom:<vendor>" names the vendor's own license text, not a known open-source family.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
isKnown('custom:Acme End User License')
|
|
174
|
+
// => false
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
package/meta/api.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"category": "license",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"functions": [
|
|
5
5
|
{
|
|
6
6
|
"name": "agree",
|
|
7
7
|
"kind": "function",
|
|
8
8
|
"description": "Whether two raw license strings agree closely enough to count as \"no real conflict\" — true\nwhen their family sets share at least one real (known) family, or when either side is entirely\nunknown (e.g. `\"custom\"`/`\"unknown\"`) — a vague claim is never itself evidence of disagreement.\nOnly two *different*, both-known family sets (e.g. `bsd` vs `apache`) count as a real conflict.",
|
|
9
|
-
"since": "
|
|
9
|
+
"since": "3.1.2",
|
|
10
10
|
"signatures": [
|
|
11
11
|
{
|
|
12
12
|
"signature": "agree(a: string, b: string): boolean",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"name": "families",
|
|
53
53
|
"kind": "function",
|
|
54
54
|
"description": "Groups a raw, possibly compound license string into its coarse family set — e.g.\n`\"GPL-3.0-or-later AND LGPL-3.0-or-later\"` yields `{\"gpl\", \"lgpl\"}`. Two sources reporting the\nsame real license under different notation (`\"GPL3\"` vs `\"GPL-3.0-or-later\"`, `\"GPL\"` vs\n`\"GPL2\"`) yield the same family, so they read as agreeing rather than conflicting.\n\nProprietary/custom/unknown tokens all collapse to a single shared unknown-family sentinel\nrather than each becoming their own one-off family, so a batch of otherwise-identical\n`\"custom:<vendor>\"` strings don't look like dozens of distinct disagreeing licenses. Use\nisKnown to check whether any real (non-sentinel) family was found.",
|
|
55
|
-
"since": "
|
|
55
|
+
"since": "3.1.2",
|
|
56
56
|
"signatures": [
|
|
57
57
|
{
|
|
58
58
|
"signature": "families(raw: string): Set<string>",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"name": "isKnown",
|
|
94
94
|
"kind": "function",
|
|
95
95
|
"description": "Whether a raw license string resolves to at least one recognized family — `false` for an\nempty string, a purely non-informative claim (`\"custom\"`, `\"unknown\"`, `\"LicenseRef-EULA\"`,\n...), or a compound expression where every token is non-informative.",
|
|
96
|
-
"since": "
|
|
96
|
+
"since": "3.1.2",
|
|
97
97
|
"signatures": [
|
|
98
98
|
{
|
|
99
99
|
"signature": "isKnown(raw: string): boolean",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helpers4/license",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"description": "A set of helpers in TS/JS, compatible with tree-shaking, for license.",
|
|
5
5
|
"author": "baxyz <craft@baxyz.dev>",
|
|
6
6
|
"license": "LGPL-3.0",
|
|
@@ -44,4 +44,4 @@
|
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=26.0.0"
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|