Worklet Bundler Configuration
Configure wdk.config.js and generate Bare worklet bundles with @tetherto/wdk-worklet-bundler
This page shows how to install @tetherto/wdk-worklet-bundler and its Pear runtime, shape wdk.config.js, and generate a Bare worklet bundle for your WDK modules.
Install the packages
Install the bundler as a development dependency and Pear Worklet as a runtime dependency in the host project:
npm install @tetherto/pear-wrk-wdk@1.0.0-beta.11
npm install --save-dev @tetherto/wdk-worklet-bundler@1.0.0-beta.10Worklet Bundler beta.10 generates entrypoints that import Pear Worklet, but generate --install does not add that package. Pear Worklet beta.11 enforces the wallet, protocol, and generic-module method allowlists documented here. The GitHub beta.8 release introduced the configuration but has no public npm artifact.
Create wdk.config.js
Use init to create a starter config, or write the file yourself:
npx wdk-worklet-bundler initThe published config surface accepts networks, optional protocols, HRPC-only generic modules, dynamic-call allowlists, transport, preloadModules, output, and options:
module.exports = {
networks: {
ethereum: {
package: '@tetherto/wdk-wallet-evm-erc-4337'
},
bitcoin: {
package: '@tetherto/wdk-wallet-btc'
}
},
protocols: {
moonpay: {
package: '@tetherto/wdk-protocol-fiat-moonpay'
}
},
modules: {
preferences: {
package: '@your-org/wdk-module-preferences',
factory: 'createModule',
events: ['changed']
}
},
allowedMethods: {
ethereum: {
methods: ['getAddress', 'getBalance'],
protocols: {
fiat: {
moonpay: { methods: [] }
}
}
}
},
allowedModuleMethods: {
preferences: {
methods: ['getTheme', 'setTheme']
}
},
transport: 'hrpc',
preloadModules: [
'spark-frost-bare-addon'
],
output: {
bundle: './.wdk-bundle/wdk-worklet.bundle.js'
},
options: {
targets: ['ios-arm64', 'android-arm64'],
convertEsmToCjs: true
}
}This example enables ESM-to-CJS conversion because its targets include iOS. Leave conversion disabled only when every target engine can load ES modules, such as an Android V8-only build.
Required fields
networks
networks is required. Each key is a logical network name, and each value must provide a package string for the wallet module to bundle.
The loader also accepts local paths that resolve relative to the config file’s directory:
module.exports = {
networks: {
local_dev: {
package: './local-packages/my-custom-wallet'
}
}
}Optional fields
protocols (optional)
Use protocols when the worklet should preload WDK protocol packages alongside wallet modules.
allowedMethods (optional)
Use allowedMethods to restrict dynamic callMethod() dispatch on generated HRPC and JSON-RPC worklets. Account methods are keyed by the logical networks name. Protocol methods are nested under that network by protocol type and configured protocol name:
module.exports = {
networks: {
ethereum: { package: '@tetherto/wdk-wallet-evm' }
},
protocols: {
aave: { package: '@tetherto/wdk-protocol-lending-aave-evm' }
},
allowedMethods: {
ethereum: {
methods: ['getAddress', 'getBalance', 'sendTransaction'],
protocols: {
lending: {
aave: { methods: ['supply', 'withdraw'] }
}
}
}
}
}Restrictions are opt-in per exact surface:
- Omitting
allowedMethods, a network,protocols, a protocol type, a protocol name, or itsmethodsfield leaves that surface unrestricted. methods: []denies every dynamic call on the exact account or protocol surface where it appears.- A protocol restriction does not inherit the network account
methodslist. Add the nested protocol entry explicitly. - A denied call fails with
METHOD_NOT_ALLOWEDbefore the target method runs.
This configuration is not default-deny. List every wallet and protocol surface exposed to an untrusted host, and use an explicit empty list when that surface should accept no dynamic calls.
allowedModuleMethods (optional, HRPC only)
Use allowedModuleMethods to restrict HRPC callModule() dispatch. Keys must match the names under modules:
module.exports = {
modules: {
preferences: {
package: '@your-org/wdk-module-preferences'
}
},
allowedModuleMethods: {
preferences: {
methods: ['getTheme', 'setTheme']
}
}
}Omitting a module or its methods field leaves that module unrestricted. Set methods: [] to deny every dynamic call on that module. JSON-RPC worklets do not generate generic modules or receive allowedModuleMethods.
modules (optional, HRPC only)
Use modules for named generic packages that expose a module factory. Each entry supports:
package(string, required): Package name or local path.factory(string, optional): Named factory export. When omitted, the package's callable default export is used.events(string[], optional): Event names forwarded from the worklet to the host.
The factory receives { seed, config, capabilities, emit } and can return the module instance or a promise for it. A module that needs the seed must consume it synchronously rather than retain it. The build-time name, such as preferences, must match the key in the host's runtime module config.
Through beta.10, generic modules are included only in generated HRPC entrypoints. Do not configure modules with transport: 'jsonrpc'; the current schema accepts the field, but JSON-RPC generation ignores it.
transport (optional)
Choose hrpc or jsonrpc. HRPC is the default and is the transport used by React Native Core. JSON-RPC produces a length-prefixed bundle for a native host and enables addon linking by default. In beta.10, ESM-to-CJS conversion is an independent, explicit option for both transports.
preloadModules (optional)
Use preloadModules for native addons or other modules that must be required before the generated worklet starts.
output (optional)
Supported output fields are:
bundle: Bundle path. Defaults to./.wdk-bundle/wdk-worklet.bundle.jsfor HRPC and./.wdk-bundle/wdk-worklet.bundlefor JSON-RPC.types: Declared in the beta.10 config type, but the generator does not honor a custom value and always writes declarations to./.wdk/index.d.ts.addons.ios,addons.macos,addons.android: Platform addon directories. Defaults are./ios-addons,./mac-addons, and./android-addons.addonsYml: BareKit Swift dependency file. Defaults to./ios-addons/addons.ymland is generated when iOS addons are linked.
options (optional)
The published config type supports these build options:
minify(boolean): Declared in the beta.10 config type but not read by the bundle path. ESM-to-CJS conversion minifies independently of this field.sourceMaps(boolean): Declared in the beta.10 config type but not read by the bundle path, so it does not produce source maps.targets(string[]): Override the default Bare build hosts. The shipped defaults cover iOS arm64 and simulator targets plus Android arm, arm64, ia32, and x64 hosts.linkAddons(boolean): Link native addons withbare-link. Defaults totruefor JSON-RPC andfalsefor HRPC.platforms(('ios' | 'macos' | 'android')[]): Addon platforms. Defaults to all three when addon linking is active.swiftTarget(string): Xcode target written toaddons.yml. Defaults toapp.convertEsmToCjs(boolean): Convert the bundle for a Bare port whose JavaScript engine cannot load ES modules, such as JSC or QuickJS. Defaults tofalsefor both transports. Leave it off only when every target uses an ESM-capable engine such as V8.
Configure ESM-to-CJS conversion
Enable conversion in wdk.config.js when any target engine cannot load ESM:
module.exports = {
networks: {
ethereum: { package: '@tetherto/wdk-wallet-evm' }
},
options: {
convertEsmToCjs: true
}
}In the normal generate or generateBundle() path, beta.10 uses one flag for both build-time conversion and the generated runtime loader:
- After
bare-pack, it converts.js,.mjs, and.cjsfiles to CommonJS, lowers dynamicimport(), removes"type": "module"from bundled package manifests, and rebuilds bundle offsets withbare-bundle. - It preserves raw bundles and the default UTF-8 CommonJS, ESM, or JSON string wrappers produced by supported
bare-packoutput suffixes. Wrappers created with a non-UTF-8--encoding, such as Base64, are not supported. - It validates the rewritten file map, offsets, lengths, CommonJS syntax, entrypoint, package manifests, and absence of dynamic imports before generation succeeds.
- The generated HRPC or JSON-RPC entrypoint routes
.mjsfiles through the CommonJS loader only when conversion is enabled.
There is no beta.10 CLI flag that overrides this setting. Set options.convertEsmToCjs in the config used for the build.
Configure JSON-RPC and native addons
Use JSON-RPC when a native host will implement Pear Worklet's length-prefixed JSON-RPC protocol:
module.exports = {
networks: {
ethereum: {
package: '@tetherto/wdk-wallet-evm'
}
},
transport: 'jsonrpc',
output: {
addons: {
ios: './ios-addons',
macos: './mac-addons',
android: './android-addons'
},
addonsYml: './ios-addons/addons.yml'
},
options: {
platforms: ['ios', 'android'],
swiftTarget: 'app',
convertEsmToCjs: true
}
}JSON-RPC generation defaults to a bundle without a .js suffix and enables addon linking. The example opts into ESM-to-CJS conversion because its targets include iOS; omit that option only when every target engine can load ESM. Addon linking includes bare-posix with the other required Bare modules. See the Pear Worklet API reference for framing and supported methods.
Validate dependencies
Use validate to check the config and dependency resolution before you generate the bundle:
npx wdk-worklet-bundler validateHandle peer dependencies
Worklet Bundler beta.10 separates required peers from peers explicitly marked optional through peerDependenciesMeta:
- During bundle generation, missing required peers are offered for installation. In a non-interactive environment without
--install, the CLI prints the exact manual install command and continues;bare-packcan still fail if the bundle needs that peer. The peer scan is skipped by--source-only. - Missing optional peers are not prompted for or installed. By default, the bundler passes each one to
bare-pack --defer, so an unused optional feature does not block the build. - Optional peers detected anywhere in the scanned package tree are not deferred. Beta.10 matches package names across root, nested, scoped, and symlinked package trees; install a peer in an ancestor
node_modulesvisible to the package that imports it, because the same package name elsewhere in the tree can suppress deferral without making that import resolvable. A peer that is required anywhere else in the scanned dependency tree is treated as required and is not deferred.
Use --no-defer-optional-peers when the app uses an optional feature and you want a missing peer to fail during the build:
npx wdk-worklet-bundler generate --no-defer-optional-peersFor example, a bundle can build while a missing @ledgerhq/ledger-bitcoin peer is deferred, then fail at runtime if the app uses the corresponding Ledger feature. Install the peer explicitly, or use --no-defer-optional-peers to surface the missing import during bundling.
This behavior is a per-run setting. wdk.config.js has no deferOptionalPeers field. Programmatic callers can set GenerateBundleOptions.deferOptionalPeers to false; omitting it or setting it to true keeps the default deferral behavior.
If a core configured dependency is missing, a non-interactive generate run that will invoke bare-pack exits with status 1 and tells you to install it manually or rerun with --install. A --source-only run can still emit the generated source files.
Generate the bundle
Use generate to build the worklet artifact:
npx wdk-worklet-bundler generate --installgenerate --install can auto-install missing configured wallet, protocol, generic, and preload modules after the package manager is detected from the project root. In beta.10 it does not install the Pear Worklet runtime imported by generated entrypoints; install Pear explicitly as shown above.
Use --source-only when you want the generated .wdk/wdk-worklet.generated.js entrypoint and related artifacts without running bare-pack. This mode also skips bundle conversion. If options.convertEsmToCjs is true and you run bare-pack yourself, convert the packed bundle with convertBundleEsmToCjs() before deployment; the source-only entrypoint already contains the matching .mjs loader patch. Keep the same conversion setting when generating the entrypoint and converting the bundle.
The generate command also accepts --transport hrpc|jsonrpc, --link-addons, --skip-link-addons, --platforms ios,macos,android, and --no-defer-optional-peers. Set transport in wdk.config.js when you rely on its transport-specific default bundle path. In beta.10, --transport changes the generated transport after output paths have already been resolved, so it does not switch .js HRPC output to the extensionless JSON-RPC default, or vice versa. If you use the flag, set output.bundle explicitly to the intended path.
--skip-generation reuses an existing generated entrypoint. If options.convertEsmToCjs changed since that entrypoint was created, regenerate without --skip-generation before building so the runtime .mjs loader matches the bundle conversion setting.
Suspend and resume behavior
Generated HRPC worklet entrypoints register Bare lifecycle handlers for suspend and resume, then apply those handlers to both the bare-http1 and bare-https global agents. No config flag is required for this behavior. Beta.10 also logs the generated worklet's Bare suspend, resume, and idle events.
This matters when a generated HRPC worklet performs HTTPS-backed fetches. Starting in beta.3, those generated entrypoints suspend and resume both agents together.
Troubleshooting
- If
loadConfig()cannot find a config file, runnpx wdk-worklet-bundler initor pass--config \<path\>. - If
validateorgeneratereports missing modules, usegenerate --installor inspect the install command from the exported helper APIs in the API Reference. - If you need to inspect generated source before bundling, use
generate --source-onlyand review the files in.wdk/. For a CommonJS-only target, pack and convert that source as described above before running it.