@uruhalushia/rule-converter-wasm
v0.3.2
Published
WebAssembly bindings for browser usage. This package exposes payload-based conversion only; file and directory APIs stay in the CLI/N-API packages.
Readme
@uruhalushia/rule-converter-wasm
WebAssembly bindings for browser usage. This package exposes payload-based conversion only; file and directory APIs stay in the CLI/N-API packages.
Build
Install the wasm target and wasm-pack first:
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
pnpm --dir wasm buildThe build scripts pass --no-opt because current Rust emits bulk-memory instructions that older bundled wasm-opt builds may reject. Browser runtimes support these instructions.
For bundlers such as Vite/Webpack:
pnpm --dir wasm build:bundlerLocal Usage After Build
After running pnpm --dir wasm build, the browser-ready package is generated in wasm/pkg. The files in pkg are ignored by git and can be served directly during local development.
Browser Without A Bundler
Create an HTML file outside pkg, then import the generated module by relative path:
<!doctype html>
<meta charset="utf-8" />
<button id="convert">convert</button>
<script type="module">
import init, { strToBuf } from './wasm/pkg/rule_converter_wasm.js'
await init()
document.querySelector('#convert').addEventListener('click', () => {
const result = strToBuf(
`payload:\n - DOMAIN,example.com\n - DOMAIN-SUFFIX,example.net\n`,
{
inputTarget: 'mihomo',
inputFormat: 'yaml',
inputBehavior: 'classical',
outputTarget: 'mihomo',
outputFormat: 'mrs',
outputBehavior: 'domain',
},
)
const bytes = result.outputs.domain
console.log(result.info.domain.behavior, result.info.domain.format, result.info.domain.count, bytes)
})
</script>Serve the repository directory with any static server; opening the file directly with file:// usually fails because WebAssembly modules are fetched asynchronously.
python -m http.server 5173
# open http://localhost:5173/your-test.htmlBundler / Vite / Webpack
Build the bundler package first:
pnpm --dir wasm build:bundlerThen install the generated local package in your web app:
pnpm add file:/home/atri/git/xishang/rule-converter/wasm/pkgUse it from app code:
import init, { strToBuf } from '@uruhalushia/rule-converter-wasm'
await init()
const result = strToBuf(
`payload:\n - DOMAIN,example.com\n - DOMAIN-SUFFIX,example.net\n`,
{
inputTarget: 'mihomo',
inputFormat: 'yaml',
inputBehavior: 'classical',
outputTarget: 'mihomo',
outputFormat: 'mrs',
outputBehavior: 'domain',
},
)
for (const [name, bytes] of Object.entries(result.outputs)) {
console.log(name, result.info[name].behavior, result.info[name].format, result.info[name].count, bytes)
}The returned bytes value is a Uint8Array, so it can be downloaded, uploaded, or stored in IndexedDB directly.
MMDB list APIs also accept uploaded file bytes:
import init, { listAsnNumbers, listGeoipCountries, listGeoipDatCountries, listGeositeCodes } from '@uruhalushia/rule-converter-wasm'
await init()
const bytes = new Uint8Array(await file.arrayBuffer())
console.log(listGeoipCountries(bytes))
console.log(listAsnNumbers(bytes))
console.log(listGeoipDatCountries(geoipDatBytes))
console.log(listGeositeCodes(geositeDatBytes))DB conversions use the same bufToBuf / bufToStr functions. For example:
const db = new Uint8Array(await mmdbFile.arrayBuffer())
const cn = bufToStr(db, {
inputTarget: 'geoip',
inputFormat: 'mmdb',
outputTarget: 'general',
outputFormat: 'ipset',
countries: ['cn'],
split: false,
})
const dat = bufToBuf(db, {
inputTarget: 'geoip',
inputFormat: 'mmdb',
outputTarget: 'geoip',
outputFormat: 'dat',
})
const geositeRules = bufToStr(geositeDatBytes, {
inputTarget: 'geosite',
inputFormat: 'dat',
outputTarget: 'general',
outputFormat: 'ruleset',
codes: ['cn'],
split: false,
})Use detectBuf / detectStr to inspect uploaded input without converting it:
import init, { detectBuf } from '@uruhalushia/rule-converter-wasm'
await init()
console.log(detectBuf(new Uint8Array(await file.arrayBuffer())))Rule matching uses the same input options as conversion. Mihomo config input is supported when providers use local path/file:// references supplied by the host application; browser HTTP provider downloads should be handled by the caller before matching:
import init, { matchStr } from '@uruhalushia/rule-converter-wasm'
await init()
const result = matchStr('DOMAIN-SUFFIX,example.com\n', 'ads.example.com', {
inputTarget: 'general',
inputFormat: 'text',
inputBehavior: 'classical',
})
console.log(result.matched, result.rules)Options use the same names as the N-API package:
type RuleTarget = 'mihomo' | 'general' | 'egern' | 'sing-box'
type InputFormat = 'yaml' | 'mrs' | 'text' | 'adguard' | 'json' | 'srs' | 'domainset' | 'ruleset' | 'ipset' | 'mmdb' | 'sing-db' | 'metadb' | 'dat' | 'sing-geosite'
type OutputFormat = 'mrs' | 'text' | 'adguard' | 'yaml' | 'json' | 'srs' | 'domainset' | 'ruleset' | 'ipset' | 'mmdb' | 'sing-db' | 'metadb' | 'dat' | 'sing-geosite'
type InputBehavior = 'auto' | 'domain' | 'ip' | 'classical'
type OutputBehavior = 'domain' | 'ip' | 'classical'Defaults are outputTarget: 'mihomo', outputFormat: 'mrs', and outputBehavior: 'domain'.
