Journal

Architecture / engineering note

Zero config, for real

A resolver, transparent development shims and a release guard — the architecture behind one command.8 min read · NativeScope engineering

The standard

Setup is part of the product

A storage inspector is most useful in the moment you did not plan to need it. If adopting it means restructuring the root component, threading providers through the tree or keeping an instance registry up to date, the tool arrives after the debugging session is over.

NativeScope starts with a stricter constraint: the app source should not know it exists.

The attachment point

Meet the app where its imports resolve

When Metro resolves react-native-mmkv, expo-sqlite or@react-native-async-storage/async-storage in development, NativeScope returns a small shim from its own package. The shim loads the real module, wraps the behavior needed for inspection, and re-exports the public API transparently.

APP SOURCE · UNCHANGEDimport AsyncStoragealready in your codeMetroresolveRequestdevNativeScope shimobserve · re-exportreleasereal moduledirect resolutionTHE CONTRACTsame importsame public APIinspection attached in dev
Fig. 1 Metro changes the route, not the app. Development imports pass through a transparent shim; release imports do not.

Composition

Custom Metro configs remain custom

The resolver composes with an existing resolveRequest instead of replacing it. Symlinked local packages add their real package root to Metro watch folders, while React and React Native always resolve from the consuming app to preserve singleton identity.

Requests originating inside a shim bypass interception. That anti-loop rule lets the shim import the real storage library without resolving itself forever.

COMPOSED RESOLVERmodule requestname · platformwithNativeScopeintercepts known moduleskeeps the previous resolverstorage shimprevious resolvereverything elseANTI-LOOP + SINGLETONSrequest originates in shimdo not intercept againbypassresolve real moduleReact · React Nativeresolved by the host app
Fig. 2 NativeScope composes with the project. Existing resolution stays authoritative, while shim-originated requests bypass interception.

Discovery

The storage registry builds itself

AsyncStorage is available as soon as the intercepted module is used. MMKV instances are observed as they are created. SQLite databases are registered when the app opens them. The developer does not need to repeat names or query keys in a second configuration surface.

THE APP USES STORAGE NORMALLYAsyncStoragegetItem · setItem · removeMMKVnew MMKV({ id: 'cache' })SQLiteopenDatabaseAsync('app.db')auto registryprovider · scopeinstance · dbSTUDIOASYNCSTORAGEdefaultMMKVcacheSQLITEapp.dbno provider list · no instance names · no query keys copied into NativeScope
Fig. 3 The registry is a consequence of normal app behavior. Instances appear when the app uses them, with no second inventory to maintain.

The boundary

Development is not a runtime flag

When Metro resolves a release bundle, the real module passes through directly. NativeScope adds a second, independent belt: every shim contains a marker and CI scans the exported release bundle for it. If the marker is found, the build fails.

DEVELOPMENT LANEMetro devNativeScope oninstrumented bundle__RNSI_SHIM__ oklocal debugging onlybrowser + device, localRELEASE LANEMetro releasereal modules onlyexported bundlesecond boundary: scanmarker not found · passmarker found → build failsdev-only by resolution · enforced again by CI
Fig. 4 Two independent boundaries: release resolution skips instrumentation, then CI verifies the exported bundle anyway.

The escape hatch

Configuration stays optional

A root nativescope.config.ts exists for behavior the tool cannot infer safely, such as invalidating a client-side cache layer after a dashboard edit. Storage discovery itself never depends on that file.

AUTOMATIC · DEFAULT PATHprovidersAsyncStorage · MMKV · SQLiteinstancesobserved when the app creates themdatabasesregistered when the app opens themzero app-owned NativeScope codeOPTIONALnativescope.config.tscache invalidationclient state refreshbehavior that cannotbe inferred safelystorage discovery works identically with or without this file
Fig. 5 Configuration describes behavior NativeScope cannot infer. It never becomes an inventory of the app's storage.

Plug-and-play is not the absence of architecture. It is architecture that accepts the integration cost on behalf of every person who installs the tool.