Skip to content

API overview

rosetta-frida ships one canonical namespace, rosetta, with three intentional tiers. Most hooks live entirely in tier 1. Higher tiers exist for the cases tier 1 cannot express, never for cases it can.

import { rosetta } from 'rosetta-frida';

The rosetta namespace composes the underlying primitives with an ambient session. You set the session once via rosetta.session(...) and every subsequent call routes through that session's resolver. If you prefer explicit composition, every function is also available as a direct import (use, hook, createMapApi, ...) — see Advanced composition below.

The three tiers

flowchart TB
    subgraph T1[Tier 1 — declarative]
        H["rosetta.hook(target, impl)"]
        P["rosetta.proceed(...args)"]
        F["rosetta.field(instance, name)"]
        S["rosetta.setField(instance, name, value)"]
    end
    subgraph T2[Tier 2 — Java.use-shaped]
        U["rosetta.use(realFQN)"]
        TY["rosetta.type(realName)"]
    end
    subgraph T3[Tier 3 — escape hatches]
        M["rosetta.map.*"]
        E["rosetta.events.*"]
    end

    SESS["rosetta.session(opts)"]
    RST["rosetta.reset()"]
    R[Resolver]
    JAVA[Frida Java bridge]

    SESS --> R
    RST -. disposes .-> SESS
    T1 --> R
    T2 --> R
    T3 --> R
    R --> JAVA
Tier When to reach for it
Tier 1 The default. One-line declarative hook installation, instance field reads/writes from inside a hook body, calling the next implementation in the chain.
Tier 2 When tier 1 cannot express something — typically overload disambiguation with mixed real/framework types, static field access on a class wrapper, or building chained method calls (e.g. Stub.requestTicket.overload(...).implementation = fn).
Tier 3 Tier-3-and-below operations: raw Java.use(...) against an obfuscated name, adaptive logic that branches on whether a real name is in the loaded map, runtime overrides, diagnostic event subscription.

The three tiers share a single Resolver per session. Switching tiers mid-hook is fine and idiomatic — most non-trivial hooks use a mix.

Three-tier compositional principle

Higher tiers never lock you out of lower tiers. Every tier-1 call is implementable in tier 2 + tier 3 primitives; every tier-2 call is implementable in tier 3 + raw Java.use. When you find yourself fighting tier 1, drop one level down and write the hook explicitly.

For example, tier 1's

rosetta.hook('Foo.bar', function (x) { return rosetta.proceed(x); });

is equivalent to the tier-2 + tier-3 expansion

const Foo = rosetta.use('Foo');
Foo.bar
    .overload(/* args from map */)
    .implementation = function (x) { return this.bar(x); };

which is in turn equivalent to the raw tier-3 + Java.use expansion

const m = rosetta.map.resolveMethod('Foo', 'bar');
Java.use(m.className)[m.obfName]
    .overload(/* translated args */)
    .implementation = function (x) { return this.bar.apply(this, arguments); };

All three install the same hook. The higher tiers are sugar.

Sessions

Every tier-1, tier-2, tier-3 call needs a session in scope. You set the session via:

rosetta.session({ map });

This call replaces any previously-active ambient session. Calling tier-½/3 surfaces before any rosetta.session(...) call throws

no active rosetta session — call rosetta.session({ map }) before using rosetta.*

rosetta.reset() disposes the ambient session (clearing its diagnostic bus) so subsequent tier-½/3 calls throw that same error again; it is idempotent. See rosetta.reset() on the Session API page for when to reach for it.

See the Session API page for the full SessionOptions surface (auto-detect overrides, failure policy, version match mode, health check controls, trace mode).

Advanced composition

If you prefer explicit dependency injection — for example, you want two sessions in one script targeting two different apps — bypass the ambient namespace and use the underlying functions directly:

import {
    createSession,
    use,
    hook,
    createMapApi,
    createEventsApi,
} from 'rosetta-frida';

const sessionA = createSession({ map: mapA });
const sessionB = createSession({ map: mapB });

// Tier 2 against session A:
const Stub = use('com.example.app.IRemoteService$Stub', {
    resolver: sessionA.resolver,
});

// Tier 1 against session B:
hook(
    'com.example.app.IRemoteService$Stub.requestTicket',
    function (b, c) { return this.requestTicket(b, c); },
    { resolver: sessionB.resolver },
);

// Tier 3 — bound to a session each:
const mapA_api = createMapApi(sessionA);
const eventsB = createEventsApi(sessionB);

Each function takes { resolver } (tier 1, tier 2) or a session (createMapApi, createEventsApi) explicitly. The rosetta namespace is sugar that closes over a single ambient session.

V1 ships only the single-ambient-session form on the namespace. Multi-session scripts must use the explicit composition above.

Symbol cheatsheet

Every exported name from rosetta-frida, grouped:

Symbol Kind Where to read
rosetta Object This page; tier-1, tier-2, tier-3
createSession, RosettaSession Function, class Session
detectAppAndVersion, pickMapForVersion, runHealthCheck, DEFAULT_HEALTH_CHECK_THRESHOLD Function, constant Session
use, type Function Tier 2
hook, proceed, field, setField Function Tier 1
createMapApi, createEventsApi Function Tier 3
makeClassProxy, makeMethodHandle, makeFieldAccessor, makeInstanceProxy Function Tier 3, proxy types
createResolver, ResolverImpl, makeSentinel, isSentinel Function, class Tier 3, design
loadMap, parseJson, looksLikeJsonSource Function Maps — format
validateMap, rosettaMapSchema Function, schema Maps — format
yamlToMap, convertToJson, renderJson Function Maps — conversion
BEGIN_MARKER, END_MARKER, BEGIN_REGISTRY, END_REGISTRY, MARKER_REGEX, emitMarkerBlock, emitMarkerRegistry, parseMarkerBlock, patchMarkerBlock Constants, function Marker block
EventBus, formatEvent, createSilentBus Class, function Events reference
RosettaError, ResolveError, AmbiguousOverloadError, MapValidationError, JsonParseError, MapVersionMismatchError, HealthCheckFailedError, MarkerBlockError, UnresolvedAccessError Error classes Errors

All type aliases are also re-exported from the package root.