This run took 58 seconds.
From 4186c6f5e37453f2f941c91f1d0413965d72279c Mon Sep 17 00:00:00 2001
From: libraryupgrader <tools.libraryupgrader@tools.wmflabs.org>
Date: Wed, 26 Nov 2025 04:21:31 +0000
Subject: [PATCH] build: Updating eslint-config-wikimedia to 0.32.2
Change-Id: I08855581b8bb617a368a1dc27533ee681b8fc476
---
package-lock.json | 14 +++++++-------
package.json | 2 +-
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index f8e6138..0d4ecd9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
"babybird": "^0.0.1",
"chai": "^4.2.0",
"domino": "^2.1.6",
- "eslint-config-wikimedia": "0.32.1",
+ "eslint-config-wikimedia": "0.32.2",
"jsdoc": "4.0.5",
"jsdoc-wmf-theme": "1.1.0",
"mocha": "11.1.0",
@@ -1955,9 +1955,9 @@
}
},
"node_modules/eslint-config-wikimedia": {
- "version": "0.32.1",
- "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.1.tgz",
- "integrity": "sha512-gPvhyVFNlpKFOcJfoVTNlzg3A0b6qjhAbjjBIJ9xp5m+om0oqix5gkqIIEav5BaGxdDxYNmrY4ge3DAPP3u/lg==",
+ "version": "0.32.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.2.tgz",
+ "integrity": "sha512-vAGz50AJPk23qQ701sL4tAgaF8FEAkP/E3kgojSTVrGgmDqjnRvq8z3EItDNI/EAkb5Ys15WPPFsoBH8YhTdSg==",
"dev": true,
"dependencies": {
"@stylistic/eslint-plugin": "^3.1.0",
@@ -6890,9 +6890,9 @@
}
},
"eslint-config-wikimedia": {
- "version": "0.32.1",
- "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.1.tgz",
- "integrity": "sha512-gPvhyVFNlpKFOcJfoVTNlzg3A0b6qjhAbjjBIJ9xp5m+om0oqix5gkqIIEav5BaGxdDxYNmrY4ge3DAPP3u/lg==",
+ "version": "0.32.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.2.tgz",
+ "integrity": "sha512-vAGz50AJPk23qQ701sL4tAgaF8FEAkP/E3kgojSTVrGgmDqjnRvq8z3EItDNI/EAkb5Ys15WPPFsoBH8YhTdSg==",
"dev": true,
"requires": {
"@stylistic/eslint-plugin": "^3.1.0",
diff --git a/package.json b/package.json
index 6dd45d0..ec8ac12 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"babybird": "^0.0.1",
"chai": "^4.2.0",
"domino": "^2.1.6",
- "eslint-config-wikimedia": "0.32.1",
+ "eslint-config-wikimedia": "0.32.2",
"jsdoc": "4.0.5",
"jsdoc-wmf-theme": "1.1.0",
"mocha": "11.1.0",
--
2.47.3
$ date
--- stdout ---
Wed Nov 26 04:20:52 UTC 2025
--- end ---
$ git clone file:///srv/git/mediawiki-libs-LangConv.git repo --depth=1 -b master
--- stderr ---
Cloning into 'repo'...
Updating files: 71% (125/175)
Updating files: 72% (126/175)
Updating files: 73% (128/175)
Updating files: 74% (130/175)
Updating files: 75% (132/175)
Updating files: 76% (133/175)
Updating files: 77% (135/175)
Updating files: 78% (137/175)
Updating files: 79% (139/175)
Updating files: 80% (140/175)
Updating files: 81% (142/175)
Updating files: 82% (144/175)
Updating files: 83% (146/175)
Updating files: 84% (147/175)
Updating files: 85% (149/175)
Updating files: 86% (151/175)
Updating files: 87% (153/175)
Updating files: 88% (154/175)
Updating files: 89% (156/175)
Updating files: 90% (158/175)
Updating files: 91% (160/175)
Updating files: 92% (161/175)
Updating files: 93% (163/175)
Updating files: 94% (165/175)
Updating files: 95% (167/175)
Updating files: 96% (168/175)
Updating files: 97% (170/175)
Updating files: 98% (172/175)
Updating files: 99% (174/175)
Updating files: 100% (175/175)
Updating files: 100% (175/175), done.
--- stdout ---
--- end ---
$ git config user.name libraryupgrader
--- stdout ---
--- end ---
$ git config user.email tools.libraryupgrader@tools.wmflabs.org
--- stdout ---
--- end ---
$ git submodule update --init
--- stdout ---
--- end ---
$ grr init
--- stdout ---
Installed commit-msg hook.
--- end ---
$ git show-ref refs/heads/master
--- stdout ---
7d6923d8ca3bad4ee7562ba4d1ad617d210990d9 refs/heads/master
--- end ---
$ /usr/bin/npm audit --json
--- stdout ---
{
"auditReportVersion": 2,
"vulnerabilities": {},
"metadata": {
"vulnerabilities": {
"info": 0,
"low": 0,
"moderate": 0,
"high": 0,
"critical": 0,
"total": 0
},
"dependencies": {
"prod": 1,
"dev": 448,
"optional": 3,
"peer": 1,
"peerOptional": 0,
"total": 448
}
}
}
--- end ---
$ /usr/bin/composer install
--- stderr ---
No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
Loading composer repositories with package information
Updating dependencies
Lock file operations: 67 installs, 0 updates, 0 removals
- Locking composer/pcre (3.3.2)
- Locking composer/semver (3.4.4)
- Locking composer/spdx-licenses (1.5.9)
- Locking composer/xdebug-handler (3.0.5)
- Locking dealerdirect/phpcodesniffer-composer-installer (v1.2.0)
- Locking doctrine/deprecations (1.1.5)
- Locking felixfbecker/advanced-json-rpc (v3.2.1)
- Locking mediawiki/mediawiki-codesniffer (v48.0.0)
- Locking mediawiki/mediawiki-phan-config (0.17.0)
- Locking mediawiki/minus-x (1.1.3)
- Locking mediawiki/phan-taint-check-plugin (7.0.0)
- Locking microsoft/tolerant-php-parser (v0.1.2)
- Locking myclabs/deep-copy (1.13.4)
- Locking netresearch/jsonmapper (v4.5.0)
- Locking nikic/php-parser (v5.6.2)
- Locking ockcyp/covers-validator (v1.7.0)
- Locking phan/phan (5.5.1)
- Locking phar-io/manifest (2.0.4)
- Locking phar-io/version (3.2.1)
- Locking php-parallel-lint/php-console-color (v1.0.1)
- Locking php-parallel-lint/php-console-highlighter (v1.0.0)
- Locking php-parallel-lint/php-parallel-lint (v1.4.0)
- Locking phpcsstandards/phpcsextra (1.4.0)
- Locking phpcsstandards/phpcsutils (1.1.1)
- Locking phpdocumentor/reflection-common (2.2.0)
- Locking phpdocumentor/reflection-docblock (5.6.4)
- Locking phpdocumentor/type-resolver (1.12.0)
- Locking phpstan/phpdoc-parser (2.3.0)
- Locking phpunit/php-code-coverage (10.1.16)
- Locking phpunit/php-file-iterator (4.1.0)
- Locking phpunit/php-invoker (4.0.0)
- Locking phpunit/php-text-template (3.0.1)
- Locking phpunit/php-timer (6.0.0)
- Locking phpunit/phpunit (10.5.58)
- Locking psr/container (2.0.2)
- Locking psr/log (3.0.2)
- Locking sabre/event (5.1.7)
- Locking sebastian/cli-parser (2.0.1)
- Locking sebastian/code-unit (2.0.0)
- Locking sebastian/code-unit-reverse-lookup (3.0.0)
- Locking sebastian/comparator (5.0.4)
- Locking sebastian/complexity (3.2.0)
- Locking sebastian/diff (5.1.1)
- Locking sebastian/environment (6.1.0)
- Locking sebastian/exporter (5.1.4)
- Locking sebastian/global-state (6.0.2)
- Locking sebastian/lines-of-code (2.0.2)
- Locking sebastian/object-enumerator (5.0.0)
- Locking sebastian/object-reflector (3.0.0)
- Locking sebastian/recursion-context (5.0.1)
- Locking sebastian/type (4.0.0)
- Locking sebastian/version (4.0.1)
- Locking squizlabs/php_codesniffer (3.13.2)
- Locking symfony/console (v6.4.27)
- Locking symfony/deprecation-contracts (v3.6.0)
- Locking symfony/polyfill-ctype (v1.33.0)
- Locking symfony/polyfill-intl-grapheme (v1.33.0)
- Locking symfony/polyfill-intl-normalizer (v1.33.0)
- Locking symfony/polyfill-mbstring (v1.33.0)
- Locking symfony/polyfill-php80 (v1.33.0)
- Locking symfony/service-contracts (v3.6.1)
- Locking symfony/string (v7.3.4)
- Locking theseer/tokenizer (1.3.1)
- Locking tysonandre/var_representation_polyfill (0.1.3)
- Locking webmozart/assert (1.12.1)
- Locking wikimedia/assert (v0.5.1)
- Locking wikimedia/update-history (1.0.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 67 installs, 0 updates, 0 removals
0 [>---------------------------] 0 [->--------------------------]
- Installing squizlabs/php_codesniffer (3.13.2): Extracting archive
- Installing dealerdirect/phpcodesniffer-composer-installer (v1.2.0): Extracting archive
- Installing composer/pcre (3.3.2): Extracting archive
- Installing phpcsstandards/phpcsutils (1.1.1): Extracting archive
- Installing phpcsstandards/phpcsextra (1.4.0): Extracting archive
- Installing symfony/polyfill-mbstring (v1.33.0): Extracting archive
- Installing composer/spdx-licenses (1.5.9): Extracting archive
- Installing composer/semver (3.4.4): Extracting archive
- Installing mediawiki/mediawiki-codesniffer (v48.0.0): Extracting archive
- Installing tysonandre/var_representation_polyfill (0.1.3): Extracting archive
- Installing symfony/polyfill-php80 (v1.33.0): Extracting archive
- Installing symfony/polyfill-intl-normalizer (v1.33.0): Extracting archive
- Installing symfony/polyfill-intl-grapheme (v1.33.0): Extracting archive
- Installing symfony/polyfill-ctype (v1.33.0): Extracting archive
- Installing symfony/string (v7.3.4): Extracting archive
- Installing symfony/deprecation-contracts (v3.6.0): Extracting archive
- Installing psr/container (2.0.2): Extracting archive
- Installing symfony/service-contracts (v3.6.1): Extracting archive
- Installing symfony/console (v6.4.27): Extracting archive
- Installing sabre/event (5.1.7): Extracting archive
- Installing netresearch/jsonmapper (v4.5.0): Extracting archive
- Installing microsoft/tolerant-php-parser (v0.1.2): Extracting archive
- Installing webmozart/assert (1.12.1): Extracting archive
- Installing phpstan/phpdoc-parser (2.3.0): Extracting archive
- Installing phpdocumentor/reflection-common (2.2.0): Extracting archive
- Installing doctrine/deprecations (1.1.5): Extracting archive
- Installing phpdocumentor/type-resolver (1.12.0): Extracting archive
- Installing phpdocumentor/reflection-docblock (5.6.4): Extracting archive
- Installing felixfbecker/advanced-json-rpc (v3.2.1): Extracting archive
- Installing psr/log (3.0.2): Extracting archive
- Installing composer/xdebug-handler (3.0.5): Extracting archive
- Installing phan/phan (5.5.1): Extracting archive
- Installing mediawiki/phan-taint-check-plugin (7.0.0): Extracting archive
- Installing mediawiki/mediawiki-phan-config (0.17.0): Extracting archive
- Installing mediawiki/minus-x (1.1.3): Extracting archive
- Installing sebastian/version (4.0.1): Extracting archive
- Installing sebastian/type (4.0.0): Extracting archive
- Installing sebastian/recursion-context (5.0.1): Extracting archive
- Installing sebastian/object-reflector (3.0.0): Extracting archive
- Installing sebastian/object-enumerator (5.0.0): Extracting archive
- Installing sebastian/global-state (6.0.2): Extracting archive
- Installing sebastian/exporter (5.1.4): Extracting archive
- Installing sebastian/environment (6.1.0): Extracting archive
- Installing sebastian/diff (5.1.1): Extracting archive
- Installing sebastian/comparator (5.0.4): Extracting archive
- Installing sebastian/code-unit (2.0.0): Extracting archive
- Installing sebastian/cli-parser (2.0.1): Extracting archive
- Installing phpunit/php-timer (6.0.0): Extracting archive
- Installing phpunit/php-text-template (3.0.1): Extracting archive
- Installing phpunit/php-invoker (4.0.0): Extracting archive
- Installing phpunit/php-file-iterator (4.1.0): Extracting archive
- Installing theseer/tokenizer (1.3.1): Extracting archive
- Installing nikic/php-parser (v5.6.2): Extracting archive
- Installing sebastian/lines-of-code (2.0.2): Extracting archive
- Installing sebastian/complexity (3.2.0): Extracting archive
- Installing sebastian/code-unit-reverse-lookup (3.0.0): Extracting archive
- Installing phpunit/php-code-coverage (10.1.16): Extracting archive
- Installing phar-io/version (3.2.1): Extracting archive
- Installing phar-io/manifest (2.0.4): Extracting archive
- Installing myclabs/deep-copy (1.13.4): Extracting archive
- Installing phpunit/phpunit (10.5.58): Extracting archive
- Installing ockcyp/covers-validator (v1.7.0): Extracting archive
- Installing php-parallel-lint/php-console-color (v1.0.1): Extracting archive
- Installing php-parallel-lint/php-console-highlighter (v1.0.0): Extracting archive
- Installing php-parallel-lint/php-parallel-lint (v1.4.0): Extracting archive
- Installing wikimedia/assert (v0.5.1): Extracting archive
- Installing wikimedia/update-history (1.0.2): Extracting archive
0/65 [>---------------------------] 0%
27/65 [===========>----------------] 41%
45/65 [===================>--------] 69%
60/65 [=========================>--] 92%
65/65 [============================] 100%
3 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating optimized autoload files
Class Wikimedia\LangConv\Test\FstReplacementMachineTest located in ./tests/phpunit/FstReplacementMachineTest.php does not comply with psr-4 autoloading standard (rule: Wikimedia\LangConv\Tests\ => ./tests/phpunit). Skipping.
Class Wikimedia\LangConv\Test\Construct\GenReplFstTest located in ./tests/phpunit/Construct/GenReplFstTest.php does not comply with psr-4 autoloading standard (rule: Wikimedia\LangConv\Tests\ => ./tests/phpunit). Skipping.
Class Wikimedia\LangConv\Test\NullReplacementMachineTest located in ./tests/phpunit/NullReplacementMachineTest.php does not comply with psr-4 autoloading standard (rule: Wikimedia\LangConv\Tests\ => ./tests/phpunit). Skipping.
41 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
--- stdout ---
PHP CodeSniffer Config installed_paths set to ../../mediawiki/mediawiki-codesniffer,../../phpcsstandards/phpcsextra,../../phpcsstandards/phpcsutils
--- end ---
Upgrading n:eslint-config-wikimedia from 0.32.1 -> 0.32.2
$ /usr/bin/npm install
--- stderr ---
npm WARN deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
--- stdout ---
added 447 packages, and audited 448 packages in 5s
96 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
--- end ---
$ package-lock-lint /src/repo/package-lock.json
--- stdout ---
Checking /src/repo/package-lock.json
--- end ---
$ package-lock-lint /src/repo/package-lock.json
--- stdout ---
Checking /src/repo/package-lock.json
--- end ---
$ ./node_modules/.bin/eslint . --fix
--- stdout ---
/src/repo/lib/FST.js
48:1 warning The type 'Utf8Array' is undefined jsdoc/no-undefined-types
52:1 warning The type 'BracketMachine' is undefined jsdoc/no-undefined-types
52:1 warning The type 'ConversionMachine' is undefined jsdoc/no-undefined-types
/src/repo/lib/ReplacementMachine.js
17:1 warning Missing JSDoc @param "baseLanguage" type jsdoc/require-param-type
36:2 warning Missing JSDoc @return declaration jsdoc/require-returns
37:1 warning Missing JSDoc @param "filename" type jsdoc/require-param-type
38:1 warning Missing JSDoc @param "bracket" type jsdoc/require-param-type
59:2 warning Found more than one @return declaration jsdoc/require-returns
59:2 warning Found more than one @return declaration jsdoc/require-returns-check
70:1 warning Missing JSDoc @param "s" type jsdoc/require-param-type
71:1 warning Missing JSDoc @param "destCode" type jsdoc/require-param-type
72:1 warning Missing JSDoc @param "invertCode" type jsdoc/require-param-type
104:1 warning The type 'Node' is undefined jsdoc/no-undefined-types
107:1 warning The type 'Node' is undefined jsdoc/no-undefined-types
136:1 warning The type 'Document' is undefined jsdoc/no-undefined-types
144:1 warning The type 'DocumentFragment' is undefined jsdoc/no-undefined-types
/src/repo/tools/build-langconv-fst.js
1:1 warning ES2023 Hashbang comments are forbidden es-x/no-hashbang
12:1 warning Invalid JSDoc tag name "0@" jsdoc/check-tag-names
16:1 warning Invalid JSDoc tag name "_IDENTITY_SYMBOL_@" jsdoc/check-tag-names
✖ 19 problems (0 errors, 19 warnings)
--- end ---
$ ./node_modules/.bin/eslint . -f json
--- stdout ---
[{"filePath":"/src/repo/.eslintrc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]}]},{"filePath":"/src/repo/composer.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]}]},{"filePath":"/src/repo/jsdoc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]}]},{"filePath":"/src/repo/lib/FST.js","messages":[{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'Utf8Array' is undefined.","line":48,"column":1,"nodeType":"Block","endLine":48,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'BracketMachine' is undefined.","line":52,"column":1,"nodeType":"Block","endLine":52,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'ConversionMachine' is undefined.","line":52,"column":1,"nodeType":"Block","endLine":52,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Load and execute a finite-state transducer (FST) based converter or\n * bracketing machine from a compact JSON description.\n *\n * @module\n */\n\n'use strict';\n\nconst { StringDecoder } = require('string_decoder');\n\nconst MAGIC_BYTES = 8; // 8 byte header w/ magic bytes\n\n// These pseudo-characters appear in the \"output\" side of the FST only.\nconst BYTE_IDENTITY = 0xFF;\nconst BYTE_RBRACKET = 0xFE;\nconst BYTE_LBRACKET = 0xFD;\nconst BYTE_FAIL = 0xFC;\n// These pseudo-characters appear in the \"input\" side of the FST.\nconst BYTE_EOF = 0xF8; // The highest possible input char\nconst BYTE_EPSILON = 0x00; // Always appears first in sorted order\n\n/**\n * A FST conversion machine.\n *\n * @callback module:language/FST~ConversionMachine\n * @param {Buffer} buffer UTF-8 encoded input buffer.\n * @param {number} [start] Start position in the buffer, default 0.\n * @param {number} [end] End position in the buffer, defaults to\n * `buffer.length`.\n * @return {string} The converted string.\n */\n\n/**\n * A FST bracket machine.\n *\n * @callback module:language/FST~BracketMachine\n * @param {Buffer} buffer UTF-8 encoded input buffer.\n * @param {number} [start] Start position in the buffer, default 0.\n * @param {number} [end] End position in the buffer, defaults to\n * `buffer.length`.\n * @return {number[]} An array of bracket locations in the input buffer.\n */\n\n/**\n * Load an FST description and return a function which runs the machine.\n *\n * @param {Buffer|Utf8Array|string} file The FST description, either as a\n * filename (to be loaded synchronously) or a loaded byte array.\n * @param {boolean} [justBrackets] The machine will return an array of\n * bracket locations in the input buffer, instead of a converted buffer.\n * @return {BracketMachine|ConversionMachine}\n */\nfunction compile(file, justBrackets) {\n\tif (typeof file === 'string') {\n\t\tfile = require('fs').readFileSync(file);\n\t}\n\t// Verify the magic number\n\tif (\n\t\tfile.length < (MAGIC_BYTES + 2/* states, min*/) ||\n\t\t\tfile.slice(0,8).toString('utf8') !== 'pFST\\0WM\\0'\n\t) {\n\t\tthrow new Error(\"Invalid pFST file.\");\n\t}\n\tif (justBrackets === 'split') {\n\t\t// Debugging helper: instead of an array of positions, split the\n\t\t// input at the bracket locations and return an array of strings.\n\t\tconst bfunc = compile(file, true);\n\t\treturn (buf,start,end) => {\n\t\t\tend = end === undefined ? buf.length : end;\n\t\t\tconst r = bfunc(buf,start,end);\n\t\t\tr.push(end);\n\t\t\tlet i = 0;\n\t\t\treturn r.map((j) => {\n\t\t\t\tconst b = buf.slice(i,j);\n\t\t\t\ti = j;\n\t\t\t\treturn b.toString('utf8');\n\t\t\t});\n\t\t};\n\t}\n\treturn (buf, start, end, unicode) => {\n\t\tstart = start === undefined ? 0 : start;\n\t\tend = end === undefined ? buf.length : end;\n\t\tconsole.assert(start >= 0 && end <= buf.length, \"Bad start/end\");\n\t\tconst countCodePoints = justBrackets && unicode;\n\t\tconst STATE_INITIAL = MAGIC_BYTES + 2/* eof state*/;\n\t\tlet state = STATE_INITIAL;\n\t\tlet idx = start;\n\t\tlet outpos = 0;\n\t\tconst brackets = [0];\n\t\tconst stack = [];\n\t\tlet chunk = { buf: justBrackets ? null : Buffer.alloc(256), next: null };\n\t\tlet firstChunk = chunk;\n\n\t\t// Read zig-zag encoded variable length integers\n\t\t// (See [en:Variable-length_quantity#Zigzag_encoding])\n\t\tconst readUnsignedV = () => {\n\t\t\tlet b = file[state++];\n\t\t\t/* eslint-disable no-bitwise */\n\t\t\tlet val = b & 127;\n\t\t\twhile (b & 128) {\n\t\t\t\tval += 1;\n\t\t\t\tb = file[state++];\n\t\t\t\tval = (val << 7) + (b & 127);\n\t\t\t}\n\t\t\t/* eslint-enable no-bitwise */\n\t\t\treturn val;\n\t\t};\n\t\tconst readSignedV = () => {\n\t\t\tconst v = readUnsignedV();\n\t\t\t/* eslint-disable no-bitwise */\n\t\t\tif (v & 1) { // sign bit is in LSB\n\t\t\t\treturn -(v >>> 1) - 1;\n\t\t\t} else {\n\t\t\t\treturn (v >>> 1);\n\t\t\t}\n\t\t\t/* eslint-enable no-bitwise */\n\t\t};\n\n\t\t// Add a character to the output.\n\t\tconst emit = justBrackets ? (code) => {\n\t\t\tif (code === BYTE_LBRACKET || code === BYTE_RBRACKET) {\n\t\t\t\tbrackets.push(outpos);\n\t\t\t} else if (countCodePoints && code >= 0x80 && code < 0xC0) {\n\t\t\t\t/* Ignore UTF-8 continuation characters */\n\t\t\t} else {\n\t\t\t\toutpos++;\n\t\t\t}\n\t\t} : (code) => {\n\t\t\t// console.assert(code !== 0 && code < 0xF8, code);\n\t\t\tif (outpos >= chunk.buf.length) {\n\t\t\t\t// Make another chunk, bigger than the last one.\n\t\t\t\tchunk.next = {\n\t\t\t\t\tbuf: Buffer.alloc(chunk.buf.length * 2),\n\t\t\t\t\tnext: null\n\t\t\t\t};\n\t\t\t\tchunk = chunk.next;\n\t\t\t\toutpos = 0;\n\t\t\t}\n\t\t\tchunk.buf[outpos++] = code;\n\t\t};\n\t\t// Save the current machine state before taking a non-deterministic\n\t\t// edge; if the machine fails, restart at the given `state`\n\t\tconst save = (epsEdge) => {\n\t\t\tstack.push({\n\t\t\t\tepsEdge,\n\t\t\t\toutpos,\n\t\t\t\tidx,\n\t\t\t\tchunk,\n\t\t\t\tblen: brackets.length,\n\t\t\t});\n\t\t};\n\t\t// When the machine has failed, restart at the saved state.\n\t\tconst reset = () => {\n\t\t\tconst s = stack.pop();\n\t\t\toutpos = s.outpos;\n\t\t\tchunk = s.chunk;\n\t\t\tchunk.next = null;\n\t\t\tidx = s.idx;\n\t\t\tbrackets.length = s.blen;\n\t\t\t// Get outByte from this edge, then jump to next state\n\t\t\tstate = s.epsEdge + 1/* skip over inByte */;\n\t\t\tconst edgeOut = file[state++];\n\t\t\tif (edgeOut !== BYTE_EPSILON) {\n\t\t\t\temit(edgeOut);\n\t\t\t}\n\t\t\tlet edgeDest = state;\n\t\t\tedgeDest += readSignedV();\n\t\t\tstate = edgeDest;\n\t\t};\n\n\t\t// This runs the machine until we reach the EOF state\n\t\t/* eslint-disable no-labels, no-extra-label */\n\t\tNEXTSTATE:\n\t\twhile (state >= STATE_INITIAL) {\n\t\t\tif (state === STATE_INITIAL) {\n\t\t\t\t// Memory efficiency: since the machine is universal\n\t\t\t\t// we know we'll never fail as long as we're in the\n\t\t\t\t// initial state.\n\t\t\t\tstack.length = 0;\n\t\t\t}\n\t\t\tconst edgeWidth = readUnsignedV();\n\t\t\tlet nEdges = readUnsignedV();\n\t\t\tif (nEdges === 0) {\n\t\t\t\treset();\n\t\t\t\tcontinue NEXTSTATE;\n\t\t\t}\n\t\t\t// Read first edge to see if there are any epsilon edges\n\t\t\tlet edge0 = state;\n\t\t\twhile (file[edge0] === BYTE_EPSILON) {\n\t\t\t\t// If this is an epsilon edge, then save a backtrack state\n\t\t\t\tsave(edge0);\n\t\t\t\tedge0 += edgeWidth;\n\t\t\t\tnEdges--;\n\t\t\t\tif (nEdges === 0) {\n\t\t\t\t\treset();\n\t\t\t\t\tcontinue NEXTSTATE;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Binary search for an edge matching c\n\t\t\tconst c = (idx < end) ? buf[idx++] : /* pseudo-character: */ BYTE_EOF;\n\t\t\tlet minIndex = 0;\n\t\t\tlet maxIndex = nEdges;\n\t\t\twhile (minIndex !== maxIndex) {\n\t\t\t\t/* eslint-disable no-bitwise */\n\t\t\t\tconst currentIndex = (minIndex + maxIndex) >>> 1;\n\t\t\t\tconst targetEdge = edge0 + (edgeWidth * currentIndex);\n\t\t\t\tconst inByte = file[targetEdge];\n\t\t\t\tif (inByte <= c) {\n\t\t\t\t\tminIndex = currentIndex + 1;\n\t\t\t\t} else {\n\t\t\t\t\tmaxIndex = currentIndex;\n\t\t\t\t}\n\t\t\t\t/* eslint-enable no-bitwise */\n\t\t\t}\n\t\t\t// (minIndex-1).inByte <= c, and maxIndex.inByte > c\n\t\t\tconst targetEdge = edge0 + (edgeWidth * (minIndex - 1));\n\t\t\tlet outByte = (minIndex > 0) ? file[targetEdge + 1] : BYTE_FAIL;\n\t\t\tif (outByte === BYTE_FAIL) {\n\t\t\t\treset();\n\t\t\t\tcontinue NEXTSTATE;\n\t\t\t}\n\t\t\tif (outByte !== BYTE_EPSILON) {\n\t\t\t\tif (outByte === BYTE_IDENTITY) {\n\t\t\t\t\toutByte = c; // Copy input byte to output\n\t\t\t\t}\n\t\t\t\temit(outByte);\n\t\t\t}\n\t\t\tstate = targetEdge + 2; // skip over inByte/outByte\n\t\t\tstate = readSignedV() + (targetEdge + 2);\n\t\t}\n\t\t/* eslint-enable no-labels, no-extra-label */\n\n\t\t// Ok, process the final state and return something.\n\t\tif (justBrackets) {\n\t\t\tbrackets.push(outpos);\n\t\t\treturn brackets;\n\t\t}\n\t\t// Convert the chunked UTF-8 output back into a JavaScript string.\n\t\tchunk.buf = chunk.buf.slice(0, outpos);\n\t\tchunk = null; // free memory as we go along\n\t\tvar decoder = new StringDecoder('utf8');\n\t\tvar result = '';\n\t\tfor (; firstChunk; firstChunk = firstChunk.next) {\n\t\t\tresult += decoder.write(firstChunk.buf);\n\t\t}\n\t\tresult += decoder.end();\n\t\treturn result;\n\t};\n}\n\nmodule.exports = {\n\tconstants: {\n\t\tBYTE_IDENTITY,\n\t\tBYTE_RBRACKET,\n\t\tBYTE_LBRACKET,\n\t\tBYTE_FAIL,\n\t\tBYTE_EOF,\n\t\tBYTE_EPSILON,\n\t},\n\tcompile,\n};\n","usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/lib/ReplacementMachine.js","messages":[{"ruleId":"jsdoc/require-param-type","severity":1,"message":"Missing JSDoc @param \"baseLanguage\" type.","line":17,"column":1,"nodeType":"Block","endLine":17,"endColumn":1},{"ruleId":"jsdoc/require-returns","severity":1,"message":"Missing JSDoc @return declaration.","line":36,"column":2,"nodeType":"Block","endLine":40,"endColumn":5},{"ruleId":"jsdoc/require-param-type","severity":1,"message":"Missing JSDoc @param \"filename\" type.","line":37,"column":1,"nodeType":"Block","endLine":37,"endColumn":1},{"ruleId":"jsdoc/require-param-type","severity":1,"message":"Missing JSDoc @param \"bracket\" type.","line":38,"column":1,"nodeType":"Block","endLine":38,"endColumn":1},{"ruleId":"jsdoc/require-returns","severity":1,"message":"Found more than one @return declaration.","line":59,"column":2,"nodeType":"Block","endLine":78,"endColumn":5},{"ruleId":"jsdoc/require-returns-check","severity":1,"message":"Found more than one @return declaration.","line":59,"column":2,"nodeType":"Block","endLine":78,"endColumn":5},{"ruleId":"jsdoc/require-param-type","severity":1,"message":"Missing JSDoc @param \"s\" type.","line":70,"column":1,"nodeType":"Block","endLine":70,"endColumn":1},{"ruleId":"jsdoc/require-param-type","severity":1,"message":"Missing JSDoc @param \"destCode\" type.","line":71,"column":1,"nodeType":"Block","endLine":71,"endColumn":1},{"ruleId":"jsdoc/require-param-type","severity":1,"message":"Missing JSDoc @param \"invertCode\" type.","line":72,"column":1,"nodeType":"Block","endLine":72,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'Node' is undefined.","line":104,"column":1,"nodeType":"Block","endLine":104,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'Node' is undefined.","line":107,"column":1,"nodeType":"Block","endLine":107,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'Document' is undefined.","line":136,"column":1,"nodeType":"Block","endLine":136,"endColumn":1},{"ruleId":"jsdoc/no-undefined-types","severity":1,"message":"The type 'DocumentFragment' is undefined.","line":144,"column":1,"nodeType":"Block","endLine":144,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * This is a wrapper around functionality similar to PHP's `ReplacementArray`,\n * but built on (reversible) Finite-State Transducers.\n *\n * @module\n */\n\n'use strict';\n\nconst FST = require('./FST.js');\n\nclass ReplacementMachine {\n\t/**\n\t * Create a new ReplacementArray, which holds a given source->destination\n\t * transformation.\n\t *\n\t * @param baseLanguage\n\t * @param {...any} codes\n\t */\n\tconstructor(baseLanguage, ...codes) {\n\t\tthis.baseLanguage = baseLanguage;\n\t\tthis.codes = codes.slice(0);\n\t\tthis.machine = new Map(codes.map((c) => [c, {\n\t\t\tconvert: ReplacementMachine.loadFST(`trans-${ c }`),\n\t\t\tbracket: new Map(\n\t\t\t\tcodes.filter((cc) => this.validCodePair(c, cc)).map((cc) => [\n\t\t\t\t\tcc,\n\t\t\t\t\tReplacementMachine.loadFST(\n\t\t\t\t\t\t`brack-${ c }-${ c === cc ? 'noop' : cc }`,\n\t\t\t\t\t\t'bracket'\n\t\t\t\t\t)\n\t\t\t\t])),\n\t\t}]));\n\t}\n\n\t/**\n\t * @param filename\n\t * @param bracket\n\t * @private\n\t */\n\tstatic loadFST(filename, bracket) {\n\t\treturn FST.compile(`${ __dirname }/../fst/${ filename }.pfst`, bracket);\n\t}\n\n\t/**\n\t * Override this method in subclass if you want to limit the\n\t * possible code pairs bracketed. (For example, zh has a large\n\t * number of variants, but we typically want to use only a limited\n\t * number of these as possible invert codes.)\n\t *\n\t * @param {string} destCode\n\t * @param {string} invertCode\n\t * @return {boolean} whether this is a valid bracketing pair.\n\t */\n\tvalidCodePair(destCode, invertCode) {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Quantify a guess about the \"native\" language of string `s`.\n\t * We will be converting *to* `destCode`, and our guess is that\n\t * when we round trip we'll want to convert back to `invertCode`\n\t * (so `invertCode` is our guess about the actual language of `s`).\n\t * If we were to make this encoding, the returned value `unsafe` is\n\t * the number of codepoints we'd have to specially-escape, `safe` is\n\t * the number of codepoints we wouldn't have to escape, and `len` is\n\t * the total number of codepoints in `s`. Generally lower values of\n\t * `nonsafe` indicate a better guess for `invertCode`.\n\t *\n\t * @param s\n\t * @param destCode\n\t * @param invertCode\n\t * @return {Object} Statistics about the given guess.\n\t * @return {number} return.safe\n\t * @return {number} return.unsafe\n\t * @return {number} return.length (Should be `safe+unsafe`.)\n\t * @private\n\t */\n\tcountBrackets(s, destCode, invertCode) {\n\t\tconsole.assert(\n\t\t\tthis.validCodePair(destCode, invertCode),\n\t\t\t`Invalid code pair: ${ destCode }/${ invertCode }`\n\t\t);\n\t\tconst m = this.machine.get(destCode).bracket.get(invertCode);\n\t\tconst buf = Buffer.from(s, 'utf8');\n\t\tconst brackets = m(buf, 0, buf.length, 'unicode'/* codepoints*/);\n\t\tlet safe = 0;\n\t\tlet unsafe = 0;\n\t\tfor (let i = 1; i < brackets.length; i++) {\n\t\t\tsafe += (brackets[i] - brackets[i - 1]);\n\t\t\tif (++i < brackets.length) {\n\t\t\t\tunsafe += (brackets[i] - brackets[i - 1]);\n\t\t\t}\n\t\t}\n\t\t// Note that this is counting codepoints, not UTF-8 code units.\n\t\treturn { safe, unsafe, length: brackets[brackets.length - 1] };\n\t}\n\n\t/**\n\t * Replace the given text Node with converted text, protecting any\n\t * markup which can't be round-tripped back to `invertCode` with\n\t * appropriate synthetic language-converter markup.\n\t *\n\t * @param {Node} textNode\n\t * @param {string} destCode\n\t * @param {string} invertCode\n\t * @return {Node|null} the next sibling of textNode (for traversal)\n\t */\n\treplace(textNode, destCode, invertCode) {\n\t\tconst fragment = this.convert(\n\t\t\ttextNode.ownerDocument, textNode.textContent, destCode, invertCode\n\t\t);\n\t\t// Was a change made?\n\t\tconst next = textNode.nextSibling;\n\t\tif (\n\t\t\t// `fragment` has exactly 1 child.\n\t\t\tfragment.firstChild && !fragment.firstChild.nextSibling &&\n\t\t\t// `fragment.firstChild` is a DOM text node\n\t\t\tfragment.firstChild.nodeType === 3 &&\n\t\t\t// `textNode` is a DOM text node\n\t\t\ttextNode.nodeType === 3 &&\n\t\t\ttextNode.textContent === fragment.textContent\n\t\t) {\n\t\t\treturn next; // No change.\n\t\t}\n\t\ttextNode.replaceWith(fragment);\n\t\treturn next;\n\t}\n\n\t/**\n\t * Convert the given string, protecting any\n\t * markup which can't be round-tripped back to `invertCode` with\n\t * appropriate synthetic language-converter markup. Returns\n\t * a DocumentFragment.\n\t *\n\t * @param {Document} document\n\t * Owner of the resulting DocumentFragment.\n\t * @param {string} s\n\t * Text to convert.\n\t * @param {string} destCode\n\t * Target language code for the conversion.\n\t * @param {string} invertCode\n\t * Language code which will be used to round-trip the result.\n\t * @return {DocumentFragment}\n\t */\n\tconvert(document, s, destCode, invertCode) {\n\t\tconst machine = this.machine.get(destCode);\n\t\tconst convertM = machine.convert;\n\t\tconst bracketM = machine.bracket.get(invertCode);\n\t\tconst result = document.createDocumentFragment();\n\t\tconst buf = Buffer.from(s, 'utf8');\n\t\tconst brackets = bracketM(buf);\n\t\tfor (let i = 1; i < brackets.length; i++) {\n\t\t\t// A safe string\n\t\t\tconst safe = convertM(buf, brackets[i - 1], brackets[i]);\n\t\t\tif (safe.length > 0) {\n\t\t\t\tresult.appendChild(document.createTextNode(safe));\n\t\t\t}\n\t\t\tif (++i < brackets.length) {\n\t\t\t\t// An unsafe string\n\t\t\t\tconst orig = buf.toString('utf8', brackets[i - 1], brackets[i]);\n\t\t\t\tconst unsafe = convertM(buf, brackets[i - 1], brackets[i]);\n\t\t\t\tconst span = document.createElement('span');\n\t\t\t\tspan.textContent = unsafe;\n\t\t\t\tspan.setAttribute('typeof', 'mw:LanguageVariant');\n\t\t\t\t// If this is an anomalous piece of text in a paragraph\n\t\t\t\t// otherwise written in destCode, then it's possible\n\t\t\t\t// invertCode === destCode. In this case try to pick a\n\t\t\t\t// more appropriate invertCode !== destCode.\n\t\t\t\tlet ic = invertCode;\n\t\t\t\tif (ic === destCode) {\n\t\t\t\t\tconst cs = this.codes.filter((c) => c !== destCode).map((code) => ({\n\t\t\t\t\t\tcode,\n\t\t\t\t\t\tstats: this.countBrackets(orig, code, code)\n\t\t\t\t\t})).sort((a,b) => a.stats.unsafe - b.stats.unsafe);\n\t\t\t\t\tif (cs.length === 0) {\n\t\t\t\t\t\tic = '-';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tic = cs[0].code;\n\t\t\t\t\t\tspan.setAttribute('data-mw-variant-lang', ic);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tspan.setAttribute('data-mw-variant', JSON.stringify({\n\t\t\t\t\ttwoway: [\n\t\t\t\t\t\t{ l: ic, t: orig },\n\t\t\t\t\t\t{ l: destCode, t: unsafe },\n\t\t\t\t\t],\n\t\t\t\t\trt: true, /* Synthetic markup used for round-tripping */\n\t\t\t\t}));\n\t\t\t\tif (unsafe.length > 0) {\n\t\t\t\t\tresult.appendChild(span);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Merge Text nodes, just in case we had zero-length brackets.\n\t\tresult.normalize();\n\t\treturn result;\n\t}\n}\n\nmodule.exports.ReplacementMachine = ReplacementMachine;\n","usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/lib/ZhReplacementMachine.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/lib/index.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/package-lock.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]}]},{"filePath":"/src/repo/package.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]},{"ruleId":"comma-dangle","replacedBy":[]},{"ruleId":"no-extra-parens","replacedBy":[]},{"ruleId":"quotes","replacedBy":[]},{"ruleId":"quote-props","replacedBy":[]}]},{"filePath":"/src/repo/tests/mocha/foma.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/tests/mocha/language/crh.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/tests/mocha/language/en.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/tests/mocha/language/ku.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/tests/mocha/language/sr.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/tests/mocha/language/zh.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]},{"filePath":"/src/repo/tools/build-langconv-fst.js","messages":[{"ruleId":"es-x/no-hashbang","severity":1,"message":"ES2023 Hashbang comments are forbidden.","line":1,"column":1,"nodeType":"Shebang","messageId":"forbidden","endLine":1,"endColumn":20},{"ruleId":"jsdoc/check-tag-names","severity":1,"message":"Invalid JSDoc tag name \"0@\".","line":12,"column":1,"nodeType":"Block","endLine":12,"endColumn":1},{"ruleId":"jsdoc/check-tag-names","severity":1,"message":"Invalid JSDoc tag name \"_IDENTITY_SYMBOL_@\".","line":16,"column":1,"nodeType":"Block","endLine":16,"endColumn":1}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"#!/usr/bin/env node\n\n'use strict';\n\n/**\n * Compile an .att-format finite state transducer (as output by foma)\n * into a compact byte-array representation which is directly executable.\n * The input is expected to be a \"byte machine\", that is, unicode code units\n * have already been decomposed into code points corresponding to UTF-8\n * bytes. Symbols used in the ATT file:\n *\n * @0@ Epsilon (\"no character\"). Used in both input and output edges;\n * as an input edge this introduced nondeterminism.\n * <hh> The input byte with hexadecimal value <hh>\n * (\"00\" should never appear in the ATT file; see below.)\n * @_IDENTITY_SYMBOL_@ Any character not named in the (implicit) alphabet\n * [[ Bracket characters, used to delimit \"unsafe\" strings in\n * ]] \"bracket machines'.\n *\n * The output is a byte array. We use a variable-length integer encoding:\n * 0xxx xxxy -> the directly-encoded value (xxx xxxx)\n * 1xxx xxxx -> (xxx xxxx) + 128 * ( 1 + <the value encoded by subsequent bytes>)\n * For signed quantities, the least significant digit is used for a sign\n * bit. That is, to encode first:\n * from_signed(x) = (x >= 0) ? (2*x) : (-2*(x + 1) + 1);\n * and when decoding:\n * to_signed(x) = (x & 1) ? (((x-1)/-2)-1) : (x/2);\n * See [en:Variable-length_quantity#Zigzag_encoding] for details.\n *\n * Byte value 0x00 is used for \"epsilon\" edges. Null characters are\n * disallowed in wikitext, and foma would have trouble handling them\n * natively since it is written in C with null-terminated strings.\n * As an input character this represents a non-deterministic transition;\n * as an output character it represents \"no output\".\n * If you wanted (for some reason) to allow null characters in the\n * input (which are not included in the \"anything else\" case), then\n * encode them as 0xC0 0x80 (aka \"Modified UTF-8\"). [Similarly, if\n * you wanted to emit a null character, you could emit 0xC0 0x80.]\n *\n * Byte values 0xF8 - 0xFF are disallowed in UTF-8. We use them for\n * special cases, as follows:\n * 0xFF: EOF (the end of the input string). Final states in the machine\n * are represented with an inchar=0xFF outchar=0x00 transition to a\n * unique \"stop state\" (aka state #0). Non-final states have no outgoing\n * edge for input 0xFF.\n * 0xFE: IDENTITY. As an output character it copies the input character.\n * 0xFD: ]]\n * 0xFC: [[ These bracketing characters should only appear as output\n * characters; they will never appear in the input.\n *\n * The byte array begins with eight \"magic bytes\" to help identify the\n * file format.\n *\n * Following this, we have an array of states. State #0 is the unique\n * \"final state\"; state #1 is the unique start state. Each state is:\n * <# of bytes in each edge: variable unsigned int>\n * <# edges: variable unsigned int>\n * <edge #0>\n * <edge #1>\n * etc\n * Each edge is:\n * <in byte: 1 byte>\n * <out byte: 1 byte>\n * <target state: variable signed int>\n * <padding, if necessary to reach proper # of bytes in each edge>\n *\n * Edges are sorted by <in byte> to allow binary search. All target\n * states are relative, refer to the start position of that state in\n * the byte array, and are padded to the same size within each state.\n * If the first edge(s) have <in byte> = 0x00 then these edges\n * represent possible epsilon transitions from this state (aka, these\n * edge should be tried if subsequent execution from this state\n * fails).\n */\n\nconst fs = require('fs');\nconst path = require('path');\nconst yargs = require('yargs');\nconst { StringDecoder } = require('string_decoder');\n\nconst FST = require('../lib/FST.js');\n\nconst BYTE_IDENTITY = FST.constants.BYTE_IDENTITY;\nconst BYTE_RBRACKET = FST.constants.BYTE_RBRACKET;\nconst BYTE_LBRACKET = FST.constants.BYTE_LBRACKET;\nconst BYTE_FAIL = FST.constants.BYTE_FAIL;\nconst BYTE_EOF = FST.constants.BYTE_EOF;\nconst BYTE_EPSILON = FST.constants.BYTE_EPSILON;\n\nclass DefaultMap extends Map {\n\tconstructor(makeDefaultValue) {\n\t\tsuper();\n\t\tthis.makeDefaultValue = makeDefaultValue;\n\t}\n\n\tgetDefault(key) {\n\t\tif (!this.has(key)) {\n\t\t\tthis.set(key, this.makeDefaultValue());\n\t\t}\n\t\treturn this.get(key);\n\t}\n}\n\n// Splits input on `\\r\\n?|\\n` without holding entire file in memory at once.\nfunction *readLines(inFile) {\n\tconst fd = fs.openSync(inFile, 'r');\n\ttry {\n\t\tconst buf = Buffer.alloc(1024);\n\t\tconst decoder = new StringDecoder('utf8');\n\t\tlet line = '';\n\t\tlet sawCR = false;\n\t\twhile (true) {\n\t\t\tconst bytesRead = fs.readSync(fd, buf, 0, buf.length);\n\t\t\tif (bytesRead === 0) { break; }\n\t\t\tlet lineStart = 0;\n\t\t\tfor (let i = 0; i < bytesRead; i++) {\n\t\t\t\tif (buf[i] === 13 || buf[i] === 10) {\n\t\t\t\t\tline += decoder.write(buf.slice(lineStart, i));\n\t\t\t\t\tif (!(buf[i] === 10 && sawCR)) {\n\t\t\t\t\t\t// skip over the zero-length \"lines\" caused by \\r\\n\n\t\t\t\t\t\tyield line;\n\t\t\t\t\t}\n\t\t\t\t\tline = '';\n\t\t\t\t\tlineStart = i + 1;\n\t\t\t\t\tsawCR = (buf[i] === 13);\n\t\t\t\t} else {\n\t\t\t\t\tsawCR = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tline += decoder.write(buf.slice(lineStart, bytesRead));\n\t\t}\n\t\tline += decoder.end();\n\t\tyield line;\n\t} finally {\n\t\tfs.closeSync(fd);\n\t}\n}\n\nfunction readAttFile(inFile, handleState, handleFinal) {\n\tlet lastState = 0;\n\tlet edges = [];\n\tconst finalStates = [];\n\tfor (const line of readLines(inFile)) {\n\t\tif (line.length === 0) { continue; }\n\t\tconst fields = line.split(/\\t/g);\n\t\tconst state = +fields[0];\n\t\tif (fields.length === 1 || state !== lastState) {\n\t\t\tif (lastState >= 0) {\n\t\t\t\thandleState(lastState, edges);\n\t\t\t\tedges = [];\n\t\t\t\tlastState = -1;\n\t\t\t}\n\t\t}\n\t\tif (fields.length === 1) {\n\t\t\tfinalStates.push(state);\n\t\t} else {\n\t\t\tconsole.assert(fields.length === 4);\n\t\t\tconst to = +fields[1];\n\t\t\tconst inChar = fields[2];\n\t\t\tconst outChar = fields[3];\n\t\t\tedges.push({ to, inChar, outChar });\n\t\t\tlastState = state;\n\t\t}\n\t}\n\tif (lastState >= 0) {\n\t\thandleState(lastState, edges);\n\t}\n\tif (handleFinal) {\n\t\thandleFinal(finalStates);\n\t}\n}\n\nclass DynamicBuffer {\n\tconstructor(chunkLength) {\n\t\tthis.chunkLength = chunkLength || 16384;\n\t\tthis.currBuff = Buffer.alloc(this.chunkLength);\n\t\tthis.buffNum = 0;\n\t\tthis.offset = 0;\n\t\tthis.buffers = [ this.currBuff ];\n\t\tthis.lastLength = 0;\n\t}\n\n\temit(b) {\n\t\tconsole.assert(b !== undefined);\n\t\tif (this.offset >= this.currBuff.length) {\n\t\t\tthis.buffNum++; this.offset = 0;\n\t\t\tthis._maybeCreateBuffers();\n\t\t\tthis.currBuff = this.buffers[this.buffNum];\n\t\t}\n\t\tthis.currBuff[this.offset++] = b;\n\t\tthis._maybeUpdateLength();\n\t}\n\n\temitUnsignedV(val, pad) {\n\t\tconst o = [];\n\t\t/* eslint-disable no-bitwise */\n\t\to.push(val & 127);\n\t\tfor (val >>>= 7; val; val >>>= 7) {\n\t\t\to.push(128 | (--val & 127));\n\t\t}\n\t\t/* eslint-enable no-bitwise */\n\t\tfor (let j = o.length - 1; j >= 0; j--) {\n\t\t\tthis.emit(o[j]);\n\t\t}\n\t\tif (pad !== undefined) {\n\t\t\tfor (let j = o.length; j < pad; j++) {\n\t\t\t\tthis.emit(0 /* padding */);\n\t\t\t}\n\t\t}\n\t}\n\n\temitSignedV(val, pad) {\n\t\tif (val >= 0) {\n\t\t\tval *= 2;\n\t\t} else {\n\t\t\tval = (-val) * 2 - 1;\n\t\t}\n\t\tthis.emitUnsignedV(val, pad);\n\t}\n\n\tposition() {\n\t\treturn this.offset + this.buffNum * this.chunkLength;\n\t}\n\n\tlength() {\n\t\treturn this.lastLength + (this.buffers.length - 1) * this.chunkLength;\n\t}\n\n\ttruncate() {\n\t\tthis.lastLength = this.offset;\n\t\tthis.buffers.length = this.buffNum + 1;\n\t}\n\n\t_maybeCreateBuffers() {\n\t\twhile (this.buffNum >= this.buffers.length) {\n\t\t\tthis.buffers.push(Buffer.alloc(this.chunkLength));\n\t\t\tthis.lastLength = 0;\n\t\t}\n\t}\n\n\t_maybeUpdateLength() {\n\t\tif (\n\t\t\tthis.offset > this.lastLength &&\n\t\t\tthis.buffNum === this.buffers.length - 1\n\t\t) {\n\t\t\tthis.lastLength = this.offset;\n\t\t}\n\t}\n\n\tseek(pos) {\n\t\tconsole.assert(pos !== undefined);\n\t\tthis.buffNum = Math.floor(pos / this.chunkLength);\n\t\tthis.offset = pos - (this.buffNum * this.chunkLength);\n\t\tthis._maybeCreateBuffers();\n\t\tthis.currBuff = this.buffers[this.buffNum];\n\t\tthis._maybeUpdateLength();\n\t}\n\n\tread() {\n\t\tif (this.offset >= this.currBuff.length) {\n\t\t\tthis.buffNum++; this.offset = 0;\n\t\t\tthis._maybeCreateBuffers();\n\t\t\tthis.currBuff = this.buffers[this.buffNum];\n\t\t}\n\t\tconst b = this.currBuff[this.offset++];\n\t\tthis._maybeUpdateLength();\n\t\treturn b;\n\t}\n\n\treadUnsignedV() {\n\t\tlet b = this.read();\n\t\t/* eslint-disable no-bitwise */\n\t\tlet val = b & 127;\n\t\twhile (b & 128) {\n\t\t\tval += 1;\n\t\t\tb = this.read();\n\t\t\tval = (val << 7) + (b & 127);\n\t\t}\n\t\t/* eslint-enable no-bitwise */\n\t\treturn val;\n\t}\n\n\treadSignedV() {\n\t\tconst v = this.readUnsignedV();\n\t\t/* eslint-disable no-bitwise */\n\t\tif (v & 1) {\n\t\t\treturn -(v >>> 1) - 1;\n\t\t} else {\n\t\t\treturn (v >>> 1);\n\t\t}\n\t\t/* eslint-enable no-bitwise */\n\t}\n\n\twriteFile(outFile) {\n\t\tconst fd = fs.openSync(outFile, 'w');\n\t\ttry {\n\t\t\tlet i;\n\t\t\tfor (i = 0; i < this.buffers.length - 1; i++) {\n\t\t\t\tfs.writeSync(fd, this.buffers[i]);\n\t\t\t}\n\t\t\tfs.writeSync(fd, this.buffers[i], 0, this.lastLength);\n\t\t} finally {\n\t\t\tfs.closeSync(fd);\n\t\t}\n\t}\n}\n\nfunction processOne(inFile, outFile, verbose, justBrackets, maxEdgeBytes) {\n\tif (justBrackets === undefined) {\n\t\tjustBrackets = /\\bbrack-/.test(inFile);\n\t}\n\tif (maxEdgeBytes === undefined) {\n\t\tmaxEdgeBytes = 10;\n\t}\n\n\tlet finalStates;\n\tconst alphabet = new Set();\n\tconst sym2byte = function(sym) {\n\t\tif (sym === '@_IDENTITY_SYMBOL_@') { return BYTE_IDENTITY; }\n\t\tif (sym === '@0@') { return BYTE_EPSILON; }\n\t\tif (sym === '[[') { return BYTE_LBRACKET; }\n\t\tif (sym === ']]') { return BYTE_RBRACKET; }\n\t\tif (/^[0-9A-F][0-9A-F]$/i.test(sym)) {\n\t\t\tconst b = Number.parseInt(sym, 16);\n\t\t\tconsole.assert(b !== 0 && b < 0xF8);\n\t\t\treturn b;\n\t\t}\n\t\tconsole.assert(false, `Bad symbol: ${ sym }`);\n\t};\n\t// Quickly read through once in order to pull out the set of final states\n\t// and the alphabet\n\treadAttFile(inFile, (state, edges) => {\n\t\tfor (const e of edges) {\n\t\t\talphabet.add(sym2byte(e.inChar));\n\t\t\talphabet.add(sym2byte(e.outChar));\n\t\t}\n\t}, (fs) => {\n\t\tfinalStates = new Set(fs);\n\t});\n\t// Anything not in `alphabet` is going to be treated as 'anything else'\n\t// but we want to force 0x00 and 0xF8-0xFF to be treated as 'anything else'\n\talphabet.delete(0);\n\tfor (let i = 0xF8; i <= 0xFF; i++) { alphabet.delete(i); }\n\t// Emit a magic number.\n\tconst out = new DynamicBuffer();\n\tout.emit(0x70); out.emit(0x46); out.emit(0x53); out.emit(0x54);\n\tout.emit(0x00); out.emit(0x57); out.emit(0x4D); out.emit(0x00);\n\t// Ok, now read through and build the output array\n\tlet synState = -1;\n\tconst stateMap = new Map();\n\t// Reserve the EOF state (0 in output)\n\tstateMap.set(synState--, out.position());\n\tout.emitUnsignedV(0);\n\tout.emitUnsignedV(0);\n\tconst processState = (state, edges) => {\n\t\tconsole.assert(!stateMap.has(state));\n\t\tstateMap.set(state, out.position());\n\t\tout.emitUnsignedV(maxEdgeBytes);\n\t\t// First emit epsilon edges\n\t\tconst r = edges.filter((e) => e.inByte === BYTE_EPSILON);\n\t\t// Then emit a sorted table of inByte transitions, omitting repeated\n\t\t// entries (so it's a range map)\n\t\t// Note that BYTE_EOF is always either FAIL or a transition to a unique\n\t\t// state, so we can always treat values lower than the first entry\n\t\t// or higher than the last entry as FAIL.\n\t\tconst edgeMap = new Map(edges.map((e) => [e.inByte, e]));\n\t\tlet lastEdge = { outByte: BYTE_FAIL, to: state };\n\t\tfor (let i = 1; i <= BYTE_EOF; i++) {\n\t\t\tlet e = (alphabet.has(i) || i === BYTE_EOF) ?\n\t\t\t\tedgeMap.get(i) : edgeMap.get(BYTE_IDENTITY);\n\t\t\tif (!e) { e = { outByte: BYTE_FAIL, to: state }; }\n\t\t\t// where possible remap outByte to IDENTITY to maximize chances\n\t\t\t// of adjacent states matching\n\t\t\tconst out = (i === e.outByte) ? BYTE_IDENTITY : e.outByte;\n\t\t\tif (out !== lastEdge.outByte || e.to !== lastEdge.to) {\n\t\t\t\tlastEdge = { inByte: i, outByte: out, to: e.to };\n\t\t\t\tr.push(lastEdge);\n\t\t\t}\n\t\t}\n\t\tout.emitUnsignedV(r.length);\n\t\tr.forEach((e) => {\n\t\t\tout.emit(e.inByte);\n\t\t\tout.emit(e.outByte);\n\t\t\tout.emitSignedV(e.to, maxEdgeBytes - 2 /* for inByte/outByte */);\n\t\t});\n\t};\n\treadAttFile(inFile, (state, edges) => {\n\t\t// Map characters to bytes\n\t\tedges = edges.map((e) => ({\n\t\t\tto: e.to,\n\t\t\tinByte: sym2byte(e.inChar),\n\t\t\toutByte: sym2byte(e.outChar),\n\t\t}));\n\t\t// If this is a final state, add a synthetic EOF edge\n\t\tif (finalStates.has(state)) {\n\t\t\tedges.push({ to: -1, inByte: BYTE_EOF, outByte: BYTE_EPSILON });\n\t\t}\n\t\t// Collect edges and figure out if we need to split the state\n\t\t// (if there are multiple edges with the same non-epsilon inByte).\n\t\tconst edgeMap = new DefaultMap(() => []);\n\t\tfor (const e of edges) {\n\t\t\tedgeMap.getDefault(e.inByte).push(e);\n\t\t}\n\t\t// For each inByte with multiple outgoing edges, replace those\n\t\t// edges with a single edge:\n\t\t// { to: newState, inChar: e.inByte, outChar: BYTE_EPSILON }\n\t\t// ...and then create a new state with edges:\n\t\t// [{ to: e[n].to, inChar: BYTE_EPSILON, outChar: e[n].outChar},...]\n\t\tconst extraStates = [];\n\t\tfor (const [inByte, e] of edgeMap.entries()) {\n\t\t\tif (inByte !== BYTE_EPSILON && e.length > 1) {\n\t\t\t\tconst nstate = synState--;\n\t\t\t\textraStates.push({\n\t\t\t\t\tstate: nstate,\n\t\t\t\t\tedges: e.map((ee) => ({\n\t\t\t\t\t\tto: ee.to,\n\t\t\t\t\t\tinByte: BYTE_EPSILON,\n\t\t\t\t\t\toutByte: ee.outByte,\n\t\t\t\t\t})),\n\t\t\t\t});\n\t\t\t\tedgeMap.set(inByte, [{\n\t\t\t\t\tto: nstate,\n\t\t\t\t\tinByte: inByte,\n\t\t\t\t\toutByte: BYTE_EPSILON\n\t\t\t\t}]);\n\t\t\t}\n\t\t}\n\t\tprocessState(state, [].concat.apply([], Array.from(edgeMap.values())));\n\t\textraStates.forEach((extra) => {\n\t\t\tprocessState(extra.state, extra.edges);\n\t\t});\n\t});\n\t// Rarely a state will not be mentioned in the .att file except\n\t// in the list of final states; check this & process at the end.\n\tfinalStates.forEach((state) => {\n\t\tif (!stateMap.has(state)) {\n\t\t\tprocessState(state, [\n\t\t\t\t{ to: -1, inByte: BYTE_EOF, outByte: BYTE_EPSILON }\n\t\t\t]);\n\t\t}\n\t});\n\t// Fixup buffer to include relative offsets to states\n\tconst state0pos = stateMap.get(-1);\n\tout.seek(state0pos);\n\twhile (out.position() < out.length()) {\n\t\tconst edgeWidth = out.readUnsignedV();\n\t\tconst nEdges = out.readUnsignedV();\n\t\tconst edge0 = out.position();\n\t\tfor (let i = 0; i < nEdges; i++) {\n\t\t\tconst p = edge0 + i * edgeWidth + /* inByte/outByte: */ 2;\n\t\t\tout.seek(p);\n\t\t\tconst state = out.readSignedV();\n\t\t\tout.seek(p);\n\t\t\tconsole.assert(stateMap.has(state), `${ state } not found`);\n\t\t\tout.emitSignedV(stateMap.get(state) - p, edgeWidth - 2);\n\t\t}\n\t\tout.seek(edge0 + nEdges * edgeWidth);\n\t}\n\t// Now iteratively narrow the field widths until the file is as small\n\t// as it can be.\n\twhile (true) {\n\t\tlet trimmed = 0;\n\t\tstateMap.clear();\n\t\tconst widthMap = new Map();\n\t\tout.seek(state0pos);\n\t\twhile (out.position() < out.length()) {\n\t\t\tconst statePos = out.position();\n\t\t\tstateMap.set(statePos, statePos - trimmed);\n\t\t\tconst edgeWidth = out.readUnsignedV();\n\t\t\tconst widthPos = out.position();\n\t\t\tconst nEdges = out.readUnsignedV();\n\t\t\tlet maxWidth = 0;\n\t\t\tconst edge0 = out.position();\n\t\t\tfor (let i = 0; i < nEdges; i++) {\n\t\t\t\tconst p = edge0 + i * edgeWidth;\n\t\t\t\tout.seek(p);\n\t\t\t\tout.read(); out.read(); out.readSignedV();\n\t\t\t\tconst thisWidth = out.position() - p;\n\t\t\t\tmaxWidth = Math.max(maxWidth, thisWidth);\n\t\t\t}\n\t\t\twidthMap.set(statePos, maxWidth);\n\t\t\ttrimmed += (edgeWidth - maxWidth) * nEdges;\n\t\t\tif (maxWidth !== edgeWidth) {\n\t\t\t\tout.seek(statePos);\n\t\t\t\tout.emitUnsignedV(maxWidth);\n\t\t\t\ttrimmed += (out.position() - widthPos);\n\t\t\t\tout.seek(statePos);\n\t\t\t\tout.emitUnsignedV(edgeWidth);\n\t\t\t}\n\t\t\tout.seek(edge0 + nEdges * edgeWidth);\n\t\t}\n\t\tstateMap.set(out.position(), out.position() - trimmed);\n\n\t\tif (trimmed === 0) { break; /* nothing left to do */ }\n\t\tif (verbose) { console.log('.'); }\n\n\t\tout.seek(state0pos);\n\t\twhile (out.position() < out.length()) {\n\t\t\tconst statePos = out.position();\n\t\t\tconsole.assert(stateMap.has(statePos) && widthMap.has(statePos));\n\t\t\tconst nWidth = widthMap.get(statePos);\n\n\t\t\tconst oldWidth = out.readUnsignedV();\n\t\t\tconst nEdges = out.readUnsignedV();\n\t\t\tconst edge0 = out.position();\n\n\t\t\tlet nPos = stateMap.get(statePos);\n\t\t\tout.seek(nPos);\n\t\t\tout.emitUnsignedV(nWidth);\n\t\t\tout.emitUnsignedV(nEdges);\n\t\t\tnPos = out.position();\n\n\t\t\tfor (let i = 0; i < nEdges; i++) {\n\t\t\t\tout.seek(edge0 + i * oldWidth);\n\t\t\t\tconst inByte = out.read();\n\t\t\t\tconst outByte = out.read();\n\t\t\t\tlet toPos = out.position();\n\t\t\t\ttoPos += out.readSignedV();\n\t\t\t\tconsole.assert(stateMap.has(toPos), toPos);\n\t\t\t\ttoPos = stateMap.get(toPos);\n\n\t\t\t\tout.seek(nPos);\n\t\t\t\tout.emit(inByte);\n\t\t\t\tout.emit(outByte);\n\t\t\t\ttoPos -= out.position();\n\t\t\t\tout.emitSignedV(toPos, nWidth - 2);\n\t\t\t\tnPos = out.position();\n\t\t\t}\n\t\t\tout.seek(edge0 + nEdges * oldWidth);\n\t\t}\n\t\tout.seek(stateMap.get(out.position()));\n\t\tout.truncate();\n\t}\n\n\t// Done!\n\tout.writeFile(outFile);\n}\n\nfunction main() {\n\tconst yopts = yargs.usage(\n\t\t'Usage: $0 [options] <conversion> <inverse>\\n' +\n\t\t'Converts a finite-state transducer in .att format.', {\n\t\t\t'output': {\n\t\t\t\tdescription: 'Output filename (or base name)',\n\t\t\t\talias: 'o',\n\t\t\t\tnargs: 1,\n\t\t\t\tnormalize: true,\n\t\t\t},\n\t\t\t'file': {\n\t\t\t\tdescription: 'Input .att filename',\n\t\t\t\talias: 'f',\n\t\t\t\tconflicts: 'language',\n\t\t\t\timplies: 'output',\n\t\t\t\tnargs: 1,\n\t\t\t\tnormalize: true,\n\t\t\t},\n\t\t\t'language': {\n\t\t\t\tdescription: 'Converts trans-{conversion}, brack-{conversion}-noop, and brack-{conversion}-{inverse} in default locations',\n\t\t\t\talias: 'l',\n\t\t\t\tconflicts: 'file',\n\t\t\t\tarray: true,\n\t\t\t},\n\t\t\t'brackets': {\n\t\t\t\tdescription: 'Emit a bracket-location machine',\n\t\t\t\talias: 'b',\n\t\t\t\tboolean: true,\n\t\t\t\tdefault: undefined,\n\t\t\t},\n\t\t\t'verbose': {\n\t\t\t\tdescription: 'Show progress',\n\t\t\t\talias: 'v',\n\t\t\t\tboolean: true,\n\t\t\t},\n\t\t}).example('$0 -l sr-ec sr-el');\n\tconst argv = yopts.argv;\n\tif (argv.help) {\n\t\tyopts.showHelp();\n\t\treturn;\n\t}\n\n\tif (argv.file) {\n\t\tprocessOne(argv.file, argv.output, argv.brackets);\n\t} else if (argv.language) {\n\t\tconst convertLang = argv.language[0];\n\t\tconst inverseLangs = argv.language.slice(1);\n\t\tconst baseDir = path.join(__dirname, '..', 'fst');\n\t\tfor (const f of [\n\t\t\t`trans-${ convertLang }`,\n\t\t\t`brack-${ convertLang }-noop`,\n\t\t].concat(inverseLangs.map((inv) => `brack-${ convertLang }-${ inv }`))) {\n\t\t\tif (argv.verbose) {\n\t\t\t\tconsole.log(f);\n\t\t\t}\n\t\t\tprocessOne(\n\t\t\t\tpath.join(baseDir, `${ f }.att`),\n\t\t\t\tpath.join(baseDir, `${ f }.pfst`),\n\t\t\t\targv.verbose\n\t\t\t);\n\t\t}\n\t} else {\n\t\tyopts.showHelp();\n\t}\n}\n\nif (require.main === module) {\n\tmain();\n}\n","usedDeprecatedRules":[{"ruleId":"indent","replacedBy":[]},{"ruleId":"no-buffer-constructor","replacedBy":[]},{"ruleId":"arrow-parens","replacedBy":[]},{"ruleId":"arrow-spacing","replacedBy":[]},{"ruleId":"lines-between-class-members","replacedBy":[]},{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"template-curly-spacing","replacedBy":[]},{"ruleId":"implicit-arrow-linebreak","replacedBy":[]}]}]
--- end ---
$ /usr/bin/npm ci
--- stderr ---
npm WARN deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
--- stdout ---
added 447 packages, and audited 448 packages in 8s
96 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
--- end ---
$ /usr/bin/npm test
--- stdout ---
> wikimedia-langconv@0.4.1+git test
> npm run eslint && npm run mocha
> wikimedia-langconv@0.4.1+git eslint
> eslint --cache lib
/src/repo/lib/FST.js
48:1 warning The type 'Utf8Array' is undefined jsdoc/no-undefined-types
52:1 warning The type 'BracketMachine' is undefined jsdoc/no-undefined-types
52:1 warning The type 'ConversionMachine' is undefined jsdoc/no-undefined-types
/src/repo/lib/ReplacementMachine.js
17:1 warning Missing JSDoc @param "baseLanguage" type jsdoc/require-param-type
36:2 warning Missing JSDoc @return declaration jsdoc/require-returns
37:1 warning Missing JSDoc @param "filename" type jsdoc/require-param-type
38:1 warning Missing JSDoc @param "bracket" type jsdoc/require-param-type
59:2 warning Found more than one @return declaration jsdoc/require-returns
59:2 warning Found more than one @return declaration jsdoc/require-returns-check
70:1 warning Missing JSDoc @param "s" type jsdoc/require-param-type
71:1 warning Missing JSDoc @param "destCode" type jsdoc/require-param-type
72:1 warning Missing JSDoc @param "invertCode" type jsdoc/require-param-type
104:1 warning The type 'Node' is undefined jsdoc/no-undefined-types
107:1 warning The type 'Node' is undefined jsdoc/no-undefined-types
136:1 warning The type 'Document' is undefined jsdoc/no-undefined-types
144:1 warning The type 'DocumentFragment' is undefined jsdoc/no-undefined-types
✖ 16 problems (0 errors, 16 warnings)
> wikimedia-langconv@0.4.1+git mocha
> nyc --reporter text-summary _mocha --recursive --timeout 10000 --check-leaks tests/mocha
Foma FST verification
- LANGCONV_TEST_FOMA is not set, skipping
Language/CRH tests
✔ general words, covering more of the alphabet (1) [crh-cyrl]
✔ general words, covering more of the alphabet (2) [crh-cyrl]
✔ general words, covering more of the alphabet (3) [crh-cyrl]
✔ general words, covering more of the alphabet (4) [crh-cyrl]
✔ exception words [crh-cyrl]
✔ recent problem words, part 1 [crh-cyrl]
✔ recent problem words, part 2 [crh-cyrl]
✔ recent problem words, part 3 [crh-cyrl]
✔ recent problem words, part 4 [crh-cyrl]
✔ recent problem words, part 5 [crh-cyrl]
✔ recent problem words, part 6 [crh-cyrl]
✔ recent problem words, part 7 [crh-cyrl]
✔ regex pattern words [crh-cyrl]
✔ multi part words [crh-cyrl]
✔ affix patterns [crh-cyrl]
✔ Roman numerals and quotes, esp. single-letter Roman numerals at the end of a string [crh-cyrl]
✔ Roman numerals vs Initials, part 1 - Roman numeral initials without spaces [crh-cyrl]
✔ Roman numerals vs Initials, part 2 - Roman numeral initials with spaces [crh-cyrl]
✔ ALL CAPS, made up acronyms [crh-cyrl]
✔ Many-to-one mappings: many Cyrillic to one Latin [crh-cyrl]
✔ Many-to-one mappings: many Latin to one Cyrillic [crh-cyrl]
✔ general words, covering more of the alphabet (1) [crh-latn]
✔ general words, covering more of the alphabet (2) [crh-latn]
✔ general words, covering more of the alphabet (3) [crh-latn]
✔ general words, covering more of the alphabet (4) [crh-latn]
✔ exception words [crh-latn]
✔ recent problem words, part 1 [crh-latn]
✔ recent problem words, part 2 [crh-latn]
✔ recent problem words, part 3 [crh-latn]
✔ recent problem words, part 4 [crh-latn]
✔ recent problem words, part 5 [crh-latn]
✔ recent problem words, part 6 [crh-latn]
✔ recent problem words, part 7 [crh-latn]
✔ regex pattern words [crh-latn]
✔ multi part words [crh-latn]
✔ affix patterns [crh-latn]
✔ Roman numerals and quotes, esp. single-letter Roman numerals at the end of a string [crh-latn]
✔ Roman numerals vs Initials, part 1 - Roman numeral initials without spaces [crh-latn]
✔ Roman numerals vs Initials, part 2 - Roman numeral initials with spaces [crh-latn]
✔ ALL CAPS, made up acronyms [crh-latn]
✔ Many-to-one mappings: many Cyrillic to one Latin [crh-latn]
✔ Many-to-one mappings: many Latin to one Cyrillic [crh-latn]
LanguageEn tests
✔ Converting to Pig Latin [en]
✔ Converting from Pig Latin [en]
✔ Converting to Pig Latin [en-x-piglatin]
✔ Converting from Pig Latin [en-x-piglatin]
LanguageKu tests
✔ Test (1) [ku-arab]
✔ Test (3) [ku-arab]
✔ Test (1) [ku-latn]
✔ Test (2) [ku-latn]
✔ Test (3) [ku-latn]
LanguageSr tests
✔ A simple conversion of Latin to Cyrillic [sr-ec]
LanguageZh tests
✔ Plain hant -> hans [zh-cn]
✔ Plain hans -> hant [zh-cn]
✔ zh-cn specific [zh-cn]
✔ zh-hk specific [zh-cn]
✔ zh-tw specific [zh-cn]
✔ zh-tw overrides zh-hant [zh-cn]
✔ zh-hk overrides zh-hant [zh-cn]
✔ Plain hant -> hans [zh-sg]
✔ Plain hans -> hant [zh-sg]
✔ zh-cn specific [zh-sg]
✔ zh-hk specific [zh-sg]
✔ zh-tw specific [zh-sg]
✔ zh-tw overrides zh-hant [zh-sg]
✔ zh-hk overrides zh-hant [zh-sg]
✔ Plain hant -> hans [zh-my]
✔ Plain hans -> hant [zh-my]
✔ zh-cn specific [zh-my]
✔ zh-hk specific [zh-my]
✔ zh-tw specific [zh-my]
✔ zh-tw overrides zh-hant [zh-my]
✔ zh-hk overrides zh-hant [zh-my]
✔ Plain hant -> hans [zh-hans]
✔ Plain hans -> hant [zh-hans]
✔ zh-cn specific [zh-hans]
✔ zh-hk specific [zh-hans]
✔ zh-tw specific [zh-hans]
✔ zh-tw overrides zh-hant [zh-hans]
✔ zh-hk overrides zh-hant [zh-hans]
✔ Plain hant -> hans [zh-tw]
✔ Plain hans -> hant [zh-tw]
✔ zh-cn specific [zh-tw]
✔ zh-hk specific [zh-tw]
✔ zh-tw specific [zh-tw]
✔ zh-tw overrides zh-hant [zh-tw]
✔ zh-hk overrides zh-hant [zh-tw]
✔ Plain hant -> hans [zh-hk]
✔ Plain hans -> hant [zh-hk]
✔ zh-cn specific [zh-hk]
✔ zh-hk specific [zh-hk]
✔ zh-tw specific [zh-hk]
✔ zh-tw overrides zh-hant [zh-hk]
✔ zh-hk overrides zh-hant [zh-hk]
✔ Plain hant -> hans [zh-mo]
✔ Plain hans -> hant [zh-mo]
✔ zh-cn specific [zh-mo]
✔ zh-hk specific [zh-mo]
✔ zh-tw specific [zh-mo]
✔ zh-tw overrides zh-hant [zh-mo]
✔ zh-hk overrides zh-hant [zh-mo]
✔ Plain hant -> hans [zh-hant]
✔ Plain hans -> hant [zh-hant]
✔ zh-cn specific [zh-hant]
✔ zh-hk specific [zh-hant]
✔ zh-tw specific [zh-hant]
✔ zh-tw overrides zh-hant [zh-hant]
✔ zh-hk overrides zh-hant [zh-hant]
108 passing (111ms)
1 pending
=============================== Coverage summary ===============================
Statements : 87.37% ( 173/198 )
Branches : 80.64% ( 75/93 )
Functions : 83.33% ( 20/24 )
Lines : 86.77% ( 164/189 )
================================================================================
--- end ---
$ /usr/bin/npm audit --json
--- stdout ---
{
"auditReportVersion": 2,
"vulnerabilities": {},
"metadata": {
"vulnerabilities": {
"info": 0,
"low": 0,
"moderate": 0,
"high": 0,
"critical": 0,
"total": 0
},
"dependencies": {
"prod": 1,
"dev": 448,
"optional": 3,
"peer": 1,
"peerOptional": 0,
"total": 448
}
}
}
--- end ---
$ package-lock-lint /src/repo/package-lock.json
--- stdout ---
Checking /src/repo/package-lock.json
--- end ---
build: Updating eslint-config-wikimedia to 0.32.2
$ git add .
--- stdout ---
--- end ---
$ git commit -F /tmp/tmp0gxu9hn3
--- stdout ---
[master 4186c6f] build: Updating eslint-config-wikimedia to 0.32.2
2 files changed, 8 insertions(+), 8 deletions(-)
--- end ---
$ git format-patch HEAD~1 --stdout
--- stdout ---
From 4186c6f5e37453f2f941c91f1d0413965d72279c Mon Sep 17 00:00:00 2001
From: libraryupgrader <tools.libraryupgrader@tools.wmflabs.org>
Date: Wed, 26 Nov 2025 04:21:31 +0000
Subject: [PATCH] build: Updating eslint-config-wikimedia to 0.32.2
Change-Id: I08855581b8bb617a368a1dc27533ee681b8fc476
---
package-lock.json | 14 +++++++-------
package.json | 2 +-
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index f8e6138..0d4ecd9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
"babybird": "^0.0.1",
"chai": "^4.2.0",
"domino": "^2.1.6",
- "eslint-config-wikimedia": "0.32.1",
+ "eslint-config-wikimedia": "0.32.2",
"jsdoc": "4.0.5",
"jsdoc-wmf-theme": "1.1.0",
"mocha": "11.1.0",
@@ -1955,9 +1955,9 @@
}
},
"node_modules/eslint-config-wikimedia": {
- "version": "0.32.1",
- "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.1.tgz",
- "integrity": "sha512-gPvhyVFNlpKFOcJfoVTNlzg3A0b6qjhAbjjBIJ9xp5m+om0oqix5gkqIIEav5BaGxdDxYNmrY4ge3DAPP3u/lg==",
+ "version": "0.32.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.2.tgz",
+ "integrity": "sha512-vAGz50AJPk23qQ701sL4tAgaF8FEAkP/E3kgojSTVrGgmDqjnRvq8z3EItDNI/EAkb5Ys15WPPFsoBH8YhTdSg==",
"dev": true,
"dependencies": {
"@stylistic/eslint-plugin": "^3.1.0",
@@ -6890,9 +6890,9 @@
}
},
"eslint-config-wikimedia": {
- "version": "0.32.1",
- "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.1.tgz",
- "integrity": "sha512-gPvhyVFNlpKFOcJfoVTNlzg3A0b6qjhAbjjBIJ9xp5m+om0oqix5gkqIIEav5BaGxdDxYNmrY4ge3DAPP3u/lg==",
+ "version": "0.32.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.32.2.tgz",
+ "integrity": "sha512-vAGz50AJPk23qQ701sL4tAgaF8FEAkP/E3kgojSTVrGgmDqjnRvq8z3EItDNI/EAkb5Ys15WPPFsoBH8YhTdSg==",
"dev": true,
"requires": {
"@stylistic/eslint-plugin": "^3.1.0",
diff --git a/package.json b/package.json
index 6dd45d0..ec8ac12 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"babybird": "^0.0.1",
"chai": "^4.2.0",
"domino": "^2.1.6",
- "eslint-config-wikimedia": "0.32.1",
+ "eslint-config-wikimedia": "0.32.2",
"jsdoc": "4.0.5",
"jsdoc-wmf-theme": "1.1.0",
"mocha": "11.1.0",
--
2.47.3
--- end ---