champollion 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/LICENSE +133 -0
  2. package/README.md +387 -0
  3. package/bin/cli.js +278 -0
  4. package/index.js +135 -0
  5. package/lib/api-key.js +127 -0
  6. package/lib/autofix.js +432 -0
  7. package/lib/bridge/method_bridge.py +430 -0
  8. package/lib/card-source-resolution.mjs +284 -0
  9. package/lib/cards/cache.js +169 -0
  10. package/lib/cards/env.js +82 -0
  11. package/lib/cards/fetch-card-child.js +38 -0
  12. package/lib/cards/reader.js +435 -0
  13. package/lib/cards/refresh.js +111 -0
  14. package/lib/cards/remote.js +387 -0
  15. package/lib/cldf-export.mjs +540 -0
  16. package/lib/cldf-terms.mjs +62 -0
  17. package/lib/command-help.js +790 -0
  18. package/lib/commands/audit.js +49 -0
  19. package/lib/commands/card.js +454 -0
  20. package/lib/commands/doctor.js +559 -0
  21. package/lib/commands/fonts.js +489 -0
  22. package/lib/commands/help.js +91 -0
  23. package/lib/commands/init.js +1259 -0
  24. package/lib/commands/integrity.js +148 -0
  25. package/lib/commands/leaderboard.js +478 -0
  26. package/lib/commands/lint.js +30 -0
  27. package/lib/commands/models.js +177 -0
  28. package/lib/commands/plugin.js +103 -0
  29. package/lib/commands/provenance.js +45 -0
  30. package/lib/commands/recommend.js +75 -0
  31. package/lib/commands/register-corpus.js +678 -0
  32. package/lib/commands/repair-script.js +42 -0
  33. package/lib/commands/seal-corpus.js +355 -0
  34. package/lib/commands/seo.js +72 -0
  35. package/lib/commands/serve.js +147 -0
  36. package/lib/commands/status.js +265 -0
  37. package/lib/commands/submit.js +332 -0
  38. package/lib/commands/sync.js +89 -0
  39. package/lib/commands/tm.js +573 -0
  40. package/lib/commands/verify.js +39 -0
  41. package/lib/commands/watch.js +20 -0
  42. package/lib/commands/wrap.js +138 -0
  43. package/lib/commands/xliff.js +327 -0
  44. package/lib/commercial-eligibility.js +235 -0
  45. package/lib/concurrent.js +87 -0
  46. package/lib/config.js +523 -0
  47. package/lib/contamination-lane.js +76 -0
  48. package/lib/content-sync.js +731 -0
  49. package/lib/content.js +733 -0
  50. package/lib/corpus-registration.mjs +608 -0
  51. package/lib/cost-report.js +346 -0
  52. package/lib/diff.js +155 -0
  53. package/lib/docusaurus-sync.js +1256 -0
  54. package/lib/flatten.js +55 -0
  55. package/lib/format.js +954 -0
  56. package/lib/hash.js +159 -0
  57. package/lib/icu.js +473 -0
  58. package/lib/integrity.js +689 -0
  59. package/lib/license-gate.mjs +478 -0
  60. package/lib/license-identify.mjs +229 -0
  61. package/lib/lint.js +629 -0
  62. package/lib/method-manifest.js +60 -0
  63. package/lib/methods/anthropic.js +140 -0
  64. package/lib/methods/apertium.js +163 -0
  65. package/lib/methods/api.js +316 -0
  66. package/lib/methods/base.js +184 -0
  67. package/lib/methods/content-separator.js +45 -0
  68. package/lib/methods/deepl.js +426 -0
  69. package/lib/methods/direct-llm.js +586 -0
  70. package/lib/methods/external.js +332 -0
  71. package/lib/methods/fetch-with-retry.js +124 -0
  72. package/lib/methods/gemini.js +147 -0
  73. package/lib/methods/google-translate.js +402 -0
  74. package/lib/methods/http-utils.js +122 -0
  75. package/lib/methods/libretranslate.js +314 -0
  76. package/lib/methods/llm-coached.js +670 -0
  77. package/lib/methods/llm.js +592 -0
  78. package/lib/methods/local.js +76 -0
  79. package/lib/methods/microsoft-translator.js +331 -0
  80. package/lib/methods/openai.js +131 -0
  81. package/lib/methods/openrouter-client.js +327 -0
  82. package/lib/methods/openrouter-pricing.js +156 -0
  83. package/lib/methods/provider-env.js +115 -0
  84. package/lib/methods/provider-pricing.js +310 -0
  85. package/lib/methods/tilde.js +150 -0
  86. package/lib/methods/translated.js +229 -0
  87. package/lib/methods/translation-error.js +80 -0
  88. package/lib/models.js +258 -0
  89. package/lib/no-translate.js +233 -0
  90. package/lib/output.js +238 -0
  91. package/lib/pairs.js +547 -0
  92. package/lib/plugins.js +447 -0
  93. package/lib/provenance.js +323 -0
  94. package/lib/recommend.js +648 -0
  95. package/lib/registers.js +1185 -0
  96. package/lib/repair-script.js +266 -0
  97. package/lib/scripts.js +994 -0
  98. package/lib/seal.mjs +464 -0
  99. package/lib/sealed-qualifier.mjs +211 -0
  100. package/lib/security.js +59 -0
  101. package/lib/segment.js +369 -0
  102. package/lib/seo.js +275 -0
  103. package/lib/serve.js +854 -0
  104. package/lib/string-classify.js +85 -0
  105. package/lib/submit.mjs +344 -0
  106. package/lib/sync.js +969 -0
  107. package/lib/tags/bcp47.js +202 -0
  108. package/lib/tags/resolve.js +314 -0
  109. package/lib/terminology.js +111 -0
  110. package/lib/tm-seed.js +294 -0
  111. package/lib/tm.js +515 -0
  112. package/lib/translate-pair.js +197 -0
  113. package/lib/translate.js +203 -0
  114. package/lib/types.js +230 -0
  115. package/lib/validate.js +510 -0
  116. package/lib/verify.js +451 -0
  117. package/lib/watch.js +145 -0
  118. package/lib/xliff.js +184 -0
  119. package/package.json +93 -0
  120. package/shared/ATTRIBUTION.md +145 -0
  121. package/shared/CORPORA-CARDS.md +288 -0
  122. package/shared/DATA-SOVEREIGNTY.md +500 -0
  123. package/shared/LANGUAGE-CARD-FIELDS.md +532 -0
  124. package/shared/card-lint-baseline.json +3189 -0
  125. package/shared/cards-fallback.json +1 -0
  126. package/shared/catalogue/card-config.json +6091 -0
  127. package/shared/catalogue/external-results.json +3888 -0
  128. package/shared/catalogue/gender-guidance.json +1038 -0
  129. package/shared/catalogue/method-coverage.json +1751 -0
  130. package/shared/catalogue/metric-coverage.json +170 -0
  131. package/shared/catalogue/metric-reliability.json +1 -0
  132. package/shared/catalogue/register-presets.json +3180 -0
  133. package/shared/catalogue/vitality-scales.json +55 -0
  134. package/shared/cldr-index.json +1115 -0
  135. package/shared/code-bridge.json +253 -0
  136. package/shared/corpora-cards-v1-reference.md +281 -0
  137. package/shared/curated-dictionary-flags.json +35 -0
  138. package/shared/curated-endonyms.json +35 -0
  139. package/shared/curated-fsts.json +51 -0
  140. package/shared/curated-orthography-conventions.json +26 -0
  141. package/shared/curated-sil-resources.json +374 -0
  142. package/shared/curated-tools.json +41 -0
  143. package/shared/docent/corpus.json +11333 -0
  144. package/shared/docent/faq.en.json +564 -0
  145. package/shared/docent/register-blocks.json +60 -0
  146. package/shared/docent/system-prompt.md +144 -0
  147. package/shared/domain-taxonomy.json +35 -0
  148. package/shared/explainers/glossary.json +2975 -0
  149. package/shared/explainers/tc-features.json +20112 -0
  150. package/shared/explainers/term-watchlist.json +147 -0
  151. package/shared/human-services.json +59 -0
  152. package/shared/license-corrections.json +261 -0
  153. package/shared/license-evidence.json +13452 -0
  154. package/shared/licenses.json +6781 -0
  155. package/shared/method-registry.json +236 -0
  156. package/shared/metric-registry.json +620 -0
  157. package/shared/model-aliases.json +7 -0
  158. package/shared/schemas/champollion-plugin.schema.json +206 -0
  159. package/shared/schemas/corpora-card.schema.json +957 -0
  160. package/shared/schemas/domain-taxonomy.schema.json +64 -0
  161. package/shared/schemas/external-results.schema.json +314 -0
  162. package/shared/schemas/human-services.schema.json +90 -0
  163. package/shared/schemas/language-card.schema.json +1308 -0
  164. package/shared/schemas/licenses.schema.json +155 -0
  165. package/shared/schemas/method-card.schema.json +412 -0
  166. package/shared/schemas/method-registry.schema.json +85 -0
  167. package/shared/schemas/metric-registry.schema.json +96 -0
  168. package/shared/schemas/metric-reliability.schema.json +178 -0
  169. package/shared/schemas/model-aliases.schema.json +27 -0
  170. package/shared/schemas/source-snapshot.schema.json +96 -0
@@ -0,0 +1,229 @@
1
+ /**
2
+ * license-identify.mjs — turn a raw upstream licence string into an SPDX id,
3
+ * or REFUSE.
4
+ *
5
+ * WHY THIS EXISTS
6
+ * It replaces `toSpdx()` in cli/scripts/populate-licenses.mjs, which wrote
7
+ * 212 of the register's 333 entries. That function was an ordered chain of
8
+ * `String.includes()` tests, and it failed in three ways at once:
9
+ *
10
+ * 1. VERSION FABRICATION. `if (s.includes('by-nc')) return 'CC-BY-NC-4.0'`
11
+ * stamps 4.0 on every BY-NC licence of any version. `nts` and
12
+ * `uclaphoneticslabarchive` are licensed BY-NC-2.0 and were recorded as
13
+ * 4.0 — with the correct URL sitting in the adjacent license_url column.
14
+ *
15
+ * 2. CLAUSE LOSS. There was no `-nd` branch anywhere. A ND licence hit the
16
+ * `by-nc` or `by` test first and came out as CC-BY-NC-4.0 or CC-BY-4.0.
17
+ * Zenodo says `nts` is cc-by-nc-**nd**-2.0; the register says
18
+ * CC-BY-NC-4.0 with allows_redistribution = 1. NoDerivatives vanished
19
+ * because nothing looked for it.
20
+ *
21
+ * 3. RAW PASSTHROUGH. `return raw` shipped unparsed strings as SPDX ids —
22
+ * which is how `hayniecolorterms` carries the literal `"CC"`.
23
+ *
24
+ * The fix is not a longer chain of guesses. It is a parser that reads the
25
+ * version and the clauses out of the string, and returns null when it cannot
26
+ * read them with certainty. A null becomes UNVERIFIED, which quarantines the
27
+ * claim (founder ruling 2026-08-01) rather than inventing a permissive one.
28
+ *
29
+ * THE RULE: never emit an SPDX id containing information the input did not
30
+ * contain. If the input has no version, the output has no version — and an
31
+ * unversioned CC licence is not identifiable, so it is refused.
32
+ *
33
+ * @see cli/scripts/harvest-zenodo-cldf.mjs (authoritative licence evidence)
34
+ * @see docs/LICENSING.md
35
+ */
36
+
37
+ /** Canonical SPDX clause order for Creative Commons: BY, NC, ND, SA. */
38
+ const CC_CLAUSE_ORDER = ['by', 'nc', 'nd', 'sa'];
39
+
40
+ /** CC versions that actually exist as SPDX identifiers. */
41
+ const CC_VERSIONS = new Set(['1.0', '2.0', '2.5', '3.0', '4.0']);
42
+
43
+ /**
44
+ * Non-CC licences we accept, keyed by the exact lowercased token. Explicit,
45
+ * because "contains 'mit'" matches "transmitted", "limits" and "submitted" —
46
+ * substring matching on short identifiers is how the old function justified
47
+ * itself and how it went wrong.
48
+ */
49
+ const EXACT_IDS = new Map(Object.entries({
50
+ 'mit': 'MIT',
51
+ 'apache-2.0': 'Apache-2.0',
52
+ 'apache 2.0': 'Apache-2.0',
53
+ 'apache2.0': 'Apache-2.0',
54
+ 'apache license 2.0': 'Apache-2.0',
55
+ 'gpl-3.0': 'GPL-3.0-only',
56
+ 'gpl-3.0-only': 'GPL-3.0-only',
57
+ 'gpl-3.0-or-later': 'GPL-3.0-or-later',
58
+ 'gpl-3': 'GPL-3.0-only',
59
+ 'gpl3': 'GPL-3.0-only',
60
+ 'agpl-3.0': 'AGPL-3.0-only',
61
+ 'agpl-3.0-only': 'AGPL-3.0-only',
62
+ 'agpl-3.0-or-later': 'AGPL-3.0-or-later',
63
+ 'lgpl-3.0': 'LGPL-3.0-only',
64
+ 'bsd-3-clause': 'BSD-3-Clause',
65
+ 'bsd-2-clause': 'BSD-2-Clause',
66
+ 'cc0-1.0': 'CC0-1.0',
67
+ 'cc0': 'CC0-1.0',
68
+ 'unicode-3.0': 'Unicode-3.0',
69
+ }));
70
+
71
+ /** URL forms that identify a licence exactly. */
72
+ const EXACT_URLS = [
73
+ [/^https?:\/\/(www\.)?apache\.org\/licenses\/LICENSE-2\.0/i, 'Apache-2.0'],
74
+ [/^https?:\/\/(www\.)?opensource\.org\/licenses\/MIT\b/i, 'MIT'],
75
+ [/^https?:\/\/(www\.)?opensource\.org\/licenses\/GPL-3\.0\b/i, 'GPL-3.0-only'],
76
+ [/^https?:\/\/(www\.)?gnu\.org\/licenses\/gpl-3\.0/i, 'GPL-3.0-only'],
77
+ [/^https?:\/\/(www\.)?gnu\.org\/licenses\/agpl-3\.0/i, 'AGPL-3.0-only'],
78
+ [/creativecommons\.org\/publicdomain\/zero\/1\.0/i, 'CC0-1.0'],
79
+ ];
80
+
81
+ /**
82
+ * Identify a licence string.
83
+ *
84
+ * @param {unknown} raw the upstream string (dc:license, a LICENSE header, …)
85
+ * @returns {{spdx: string|null, reason: string, raw: string|null}}
86
+ * `spdx` is null whenever the string cannot be identified exactly; `reason`
87
+ * always explains the outcome so a reviewer can see WHY something is
88
+ * UNVERIFIED without re-deriving it.
89
+ */
90
+ export function identifyLicense(raw) {
91
+ if (raw == null || raw === '') {
92
+ return { spdx: null, reason: 'no licence string present', raw: null };
93
+ }
94
+
95
+ // CLDF metadata sometimes carries an object: {url, name, icon}.
96
+ if (typeof raw === 'object') {
97
+ const url = raw.url || raw.href || null;
98
+ if (!url) {
99
+ return { spdx: null, reason: 'licence object with no url', raw: JSON.stringify(raw) };
100
+ }
101
+ const inner = identifyLicense(url);
102
+ return { ...inner, raw: JSON.stringify(raw) };
103
+ }
104
+
105
+ const text = String(raw).trim();
106
+ const s = text.toLowerCase();
107
+
108
+ for (const [re, spdx] of EXACT_URLS) {
109
+ if (re.test(text)) return { spdx, reason: 'exact URL match', raw: text };
110
+ }
111
+
112
+ // Creative Commons DEED TITLE, as it appears at the top of a LICENSE file
113
+ // shipped by the dataset itself: "Attribution-ShareAlike 4.0 International".
114
+ // This is an exact form, not a resemblance — the clause words and the version
115
+ // are both read from the title. It is how cli/data/segbo/LICENSE.txt resolves,
116
+ // the file whose existence the register denies ("No LICENSE file found in
117
+ // GitHub repo") while it sits in the tree.
118
+ const deed = /\b(Attribution(?:-(?:NonCommercial|ShareAlike|NoDerivatives|NoDerivs))*)\s+(\d(?:\.\d)?)\s+International/i.exec(text);
119
+ if (deed) {
120
+ const words = deed[1].toLowerCase().split('-');
121
+ const map = {
122
+ attribution: 'by', noncommercial: 'nc',
123
+ sharealike: 'sa', noderivatives: 'nd', noderivs: 'nd',
124
+ };
125
+ const clauses = words.map((w) => map[w]).filter(Boolean);
126
+ let version = deed[2];
127
+ if (!version.includes('.')) version = `${version}.0`;
128
+ if (clauses.length === words.length && CC_VERSIONS.has(version)) {
129
+ const ordered = CC_CLAUSE_ORDER.filter((c) => clauses.includes(c));
130
+ return {
131
+ spdx: `CC-${ordered.map((c) => c.toUpperCase()).join('-')}-${version}`,
132
+ reason: 'exact Creative Commons deed title',
133
+ raw: text.slice(0, 120),
134
+ };
135
+ }
136
+ }
137
+ if (/^\s*CC0\s+1\.0\s+Universal/im.test(text)) {
138
+ return { spdx: 'CC0-1.0', reason: 'exact CC0 deed title', raw: text.slice(0, 120) };
139
+ }
140
+
141
+ if (EXACT_IDS.has(s)) {
142
+ return { spdx: EXACT_IDS.get(s), reason: 'exact identifier match', raw: text };
143
+ }
144
+
145
+ // Creative Commons: read the clauses AND the version out of the string.
146
+ // Both a URL (creativecommons.org/licenses/by-nc-sa/3.0/) and a bare id
147
+ // (cc-by-nc-sa-3.0) are handled by the same extraction, so neither can
148
+ // produce a version the input did not state.
149
+ const ccUrl = /creativecommons\.org\/licenses\/([a-z-]+)\/(\d(?:\.\d)?)/i.exec(text);
150
+ const ccId = /^cc-((?:by|nc|nd|sa)(?:-(?:by|nc|nd|sa))*)-(\d(?:\.\d)?)$/i.exec(s);
151
+ const m = ccUrl || ccId;
152
+ if (m) {
153
+ const clauses = m[1].toLowerCase().split('-').filter(Boolean);
154
+ let version = m[2];
155
+ if (!version.includes('.')) version = `${version}.0`;
156
+
157
+ if (!CC_VERSIONS.has(version)) {
158
+ return { spdx: null, reason: `CC version ${version} is not a known SPDX version`, raw: text };
159
+ }
160
+ const unknown = clauses.filter((c) => !CC_CLAUSE_ORDER.includes(c));
161
+ if (unknown.length) {
162
+ return { spdx: null, reason: `unrecognised CC clause(s): ${unknown.join(', ')}`, raw: text };
163
+ }
164
+ if (!clauses.includes('by')) {
165
+ // CC licences without BY exist only in 1.0. Refuse rather than assume.
166
+ return { spdx: null, reason: 'CC licence without a BY clause — needs review', raw: text };
167
+ }
168
+ const ordered = CC_CLAUSE_ORDER.filter((c) => clauses.includes(c));
169
+ return {
170
+ spdx: `CC-${ordered.map((c) => c.toUpperCase()).join('-')}-${version}`,
171
+ reason: 'parsed CC clauses and version from the string',
172
+ raw: text,
173
+ };
174
+ }
175
+
176
+ // A CC licence we can see but cannot version. This is the `hayniecolorterms`
177
+ // case: the metadata says only "CC". It grants nothing determinable, and the
178
+ // old function shipped the raw string as an SPDX id.
179
+ if (/\bcc\b|creativecommons/i.test(s)) {
180
+ return {
181
+ spdx: null,
182
+ reason: 'Creative Commons licence with no identifiable version — grants nothing determinable',
183
+ raw: text,
184
+ };
185
+ }
186
+
187
+ return { spdx: null, reason: 'unrecognised licence string', raw: text };
188
+ }
189
+
190
+ /**
191
+ * Derive obligation flags from an SPDX id.
192
+ *
193
+ * Structural, not heuristic: every flag reads a clause that is actually present
194
+ * in the identifier. The old `licenseFlags()` set
195
+ * `redistribution: !s.includes('PROPRIETARY')` — i.e. everything on earth was
196
+ * redistributable unless the string happened to contain that one word. Nothing
197
+ * upstream ever says "PROPRIETARY", so the flag was effectively a constant.
198
+ *
199
+ * `noDerivatives` is NEW. The register had no field for it, which is why ND
200
+ * silently vanished from `nts`: there was nowhere to record it even if the
201
+ * parser had seen it.
202
+ *
203
+ * @param {string|null} spdx
204
+ * @returns {{redistribution: boolean|null, attribution: boolean, sharealike: boolean,
205
+ * nonCommercial: boolean, noDerivatives: boolean}}
206
+ */
207
+ export function deriveFlags(spdx) {
208
+ if (!spdx) {
209
+ // UNVERIFIED grants nothing. `redistribution: null` is "unknown", which is
210
+ // NOT the same as false and must not read as a permission either way.
211
+ return {
212
+ redistribution: null, attribution: false, sharealike: false,
213
+ nonCommercial: false, noDerivatives: false,
214
+ };
215
+ }
216
+ const s = spdx.toUpperCase();
217
+ const nd = /-ND(-|$)/.test(s);
218
+ return {
219
+ // ND permits verbatim redistribution but forbids distributing derivatives.
220
+ // For a project whose whole output is derived data, treating an ND source
221
+ // as redistributable is the error we are removing, so this is false.
222
+ redistribution: !nd,
223
+ attribution: s.startsWith('CC-BY') || s.startsWith('APACHE')
224
+ || s === 'MIT' || s.startsWith('BSD'),
225
+ sharealike: /-SA(-|$)/.test(s) || s.includes('GPL'),
226
+ nonCommercial: /-NC(-|$)/.test(s),
227
+ noDerivatives: nd,
228
+ };
229
+ }