@parallax-rn/sdk 0.0.0-stage → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,72 @@
1
+ PARALLAX LICENSE (BETA TERMS, VERSION 1, 2026-10-28)
2
+
3
+ Copyright (c) 2026 Valiantsin Tsikhamirau. All rights reserved.
4
+ Contact: zemantservices@gmail.com, https://parallax-rn.dev
5
+
6
+ 1. Definitions
7
+ "Software" means this package of Parallax for React Native, including its code, documentation and any updates the Licensor supplies. "You" means the person or
8
+ organization that installs or uses the Software. "Licensor" means Valiantsin Tsikhamirau, the author of the Software. "Your Applications" means software that You
9
+ develop or maintain.
10
+
11
+ 2. Grant of license
12
+ The Licensor grants You a worldwide, non-exclusive, non-transferable, revocable, royalty-free license, free of charge, to install and use the Software
13
+ for Your own personal or internal business purposes, including the commercial development, testing and debugging of Your Applications. This includes:
14
+ (a) adding the in-app SDK and the Babel plugin of the Software to Your Applications as development dependencies;
15
+ (b) building, distributing and selling Your Applications, even when a build of Your Application contains code of the SDK or of the Babel plugin, as long
16
+ as that code stays a part of Your Application and is not offered on its own;
17
+ (c) keeping copies of the Software in Your organization's internal package mirrors, registries, caches, continuous integration systems, lock files and
18
+ vendored dependency folders, for use under this license by Your own staff and contractors;
19
+ (d) installing it on any number of machines used by You, Your staff or Your contractors working on Your Applications.
20
+
21
+ 3. Restrictions
22
+ Except as this license expressly allows or as mandatory law allows despite this limit, You may not:
23
+ (a) modify, adapt, translate or create derivative works of the Software;
24
+ (b) distribute, sublicense, sell, rent, lease, lend, host as a service or otherwise make the Software or any part of it available to others outside Your
25
+ organization, including by publishing it to a public package registry or repository, except as section 2 allows;
26
+ (c) reverse engineer, decompile or disassemble the Software, or try to obtain its source code;
27
+ (d) use the Software, its output or its documentation to build, train or benchmark a product or service that competes with the Software, or give the
28
+ Software to a person for that purpose;
29
+ (e) remove or change any copyright, license or other notice in the Software.
30
+
31
+ 4. Third-party components
32
+ The Software contains third-party components under their own licenses, listed in the file THIRD_PARTY_NOTICES where the package contains one. Those licenses,
33
+ not this one, govern those components.
34
+
35
+ 5. Ownership
36
+ The Software is licensed, not sold. The Licensor keeps all rights not expressly granted. This license grants no right to the Licensor's name or marks.
37
+ Your Applications, their data and the output of Your agents stay Yours.
38
+
39
+ 6. Data
40
+ The Software runs on Your machine. It has no account, no telemetry and no analytics, and it does not send data to the Licensor. What it reads from Your
41
+ application and simulator or emulator (the screen, the component tree, logs, errors, network requests, state and navigation) is returned to the AI agent
42
+ You connect to it, such as Claude Code or Codex, and that agent sends what it receives to its own provider under that provider's terms. The Licensor does
43
+ not control that. The Software removes known secrets from network and state data before an agent receives them; logs and errors are passed on as Your
44
+ application wrote them.
45
+
46
+ 7. Beta software
47
+ The Software is a beta. It may change, stop working or be withdrawn at any time, and later versions may come under different terms, including paid terms
48
+ for some parts. A version You received under this license stays under this license.
49
+
50
+ 8. Termination
51
+ This license ends automatically if You breach it. Then You must stop using the Software and delete all copies, except copies inside builds of Your
52
+ Applications that section 2(b) allows. You may end it at any time by deleting the Software. Sections 3, 5, 9, 10 and 11 survive.
53
+
54
+ 9. No warranty
55
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
56
+ PURPOSE AND NON-INFRINGEMENT.
57
+
58
+ 10. Limitation of liability
59
+ TO THE EXTENT THE LAW ALLOWS, THE LICENSOR IS NOT LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES, OR FOR LOST PROFITS OR DATA,
60
+ ARISING FROM THE SOFTWARE OR ITS USE, AND THE LICENSOR'S TOTAL LIABILITY IS LIMITED TO THE AMOUNT YOU PAID FOR THE SOFTWARE (ZERO FOR THE FREE LICENSE).
61
+ NOTHING IN THIS LICENSE LIMITS LIABILITY FOR INTENT OR GROSS NEGLIGENCE, OR ANY LIABILITY THAT CANNOT BE LIMITED BY LAW.
62
+
63
+ 11. General
64
+ (a) Governing law and courts. This license is governed by the law of the Republic of Belarus, without regard to its conflict of laws rules. The courts of
65
+ Minsk, Belarus, have jurisdiction over any dispute about it. If You are a consumer, nothing in this license takes away a protection that the law of the
66
+ country where You live gives You and that cannot be waived by agreement, including Your right to bring a claim in Your own courts.
67
+ (b) Severability. If a court finds a part of this license invalid, that part is limited to the minimum extent needed and the rest stays in force.
68
+ (c) Entire agreement. This license is the entire agreement between You and the Licensor about the Software and replaces any earlier statement about it.
69
+ A paid tier, if one is offered later, comes with its own terms.
70
+ (d) Export. You must comply with the export control and sanctions laws that apply to You when You use or move the Software.
71
+ (e) No waiver. If the Licensor does not enforce a term, the Licensor keeps the right to enforce it later.
72
+ (f) Language. This license is written in English; a translation is for convenience only.
package/README.md CHANGED
@@ -1,3 +1,32 @@
1
- # Temporary Holding Version
1
+ # @parallax-rn/sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The dev-only code that runs inside the React Native app. Its only dependency is `@parallax-rn/protocol`; importing it has no side effects. Everything the SDK touches (console, `ErrorUtils`, timers, the push binding) goes through one `HostGlobal` interface, so it compiles without Node or DOM types and is tested against a fake host.
4
+
5
+ ## Wiring
6
+ ```js
7
+ if (__DEV__) {
8
+ require('@parallax-rn/sdk').start();
9
+ }
10
+ ```
11
+ Metro removes the whole branch from a release bundle (checked: `node scripts/verify-metro-resolution.mjs --guarded sdk <toolchain dir>`; the release bundle contains no SDK code). As a second guard `start()` does nothing and returns `false` when `__DEV__ === false`. The plugin (`@parallax-rn/babel-plugin`, later step) will emit this wiring itself; `doctor` reports whether it is present.
12
+
13
+ ## What it does
14
+ - **Console capture** for `debug`, `log`, `info`, `warn`, `error`. The original method always runs first, with the same arguments and `this`; capture happens afterwards in a guarded block, so a failing or re-entrant capture never reaches the app. LogBox, Metro logs and the DevTools console behave as without the SDK.
15
+ - **Uncaught errors** through `ErrorUtils.setGlobalHandler`; the previously installed handler is always called (fatal and non-fatal), so the red box still appears.
16
+ - **Ring buffers** (logs 1000, errors 100) are the source of truth. Entries get a `seq` that starts at 1; a server that attaches late, or after a reload or reconnect, drains by cursor.
17
+ - **Push** through the `__PARALLAX_RN_PUSH__` binding (decision 0010): string payloads only, `typeof` checked before every call (the binding does not exist until a session adds it), bursts coalesced for 50 ms (errors at once), a batch split at 100 entries or about 100000 characters. Pushing only lowers latency; a missed push loses nothing.
18
+ - **Caps** per entry: message 4000 and stack 8000 characters; a cut entry is marked `truncated: true`. These are limits on memory and push size, separate from the 500-character limit on what an agent receives from a tool.
19
+ - **Commands**: `registerCommand(name, handler, { description, argsHint })` (registering a name again replaces it; Fast Refresh re-runs modules) and the built-in `parallax.ping`.
20
+ - `stop()` restores the console methods and the error handler (a wrapper installed by someone else after ours is left in place and ours just stops capturing).
21
+
22
+ ## The API object
23
+ `globalThis.__PARALLAX_RN__.sdk` has `handshake()`, `drain(requestJson)`, `commands()` and `call(requestJson)`. Each takes a JSON string (or nothing) and returns a JSON string; `call` returns a promise of one, because command handlers may be async. Note (spike 0007): on RN's Hermes `Runtime.evaluate` ignores `awaitPromise`, so a client cannot simply await `call`; how the client settles it is open in S5.
24
+
25
+ ## Limits
26
+ `DEFAULT_LIMITS` in `src/limits.ts`, overridable per `createSdk({ limits })`. Values come from spike 0007 (about 2 MB worst-case memory, p95 push latency at most 77 ms at 480 lines/s).
27
+
28
+ ## Hermes note
29
+ Do not create closures inside a `for (const x of ...)` loop body in this package: Hermes evaluates code received through `Runtime.evaluate` without per-iteration bindings (found in spike 0007). Put the body in a function.
30
+
31
+ ## License
32
+ Proprietary and free to use (decision 0014): see `LICENSE` in the package. The text is the beta terms, version 1. Tested on React Native 0.87.1.
@@ -0,0 +1,15 @@
1
+ import type { CommandInfo, CommandResult } from '@parallax-rn/protocol';
2
+ export type CommandHandler = (args: unknown) => unknown;
3
+ export interface CommandMeta {
4
+ readonly description?: string;
5
+ /** A short human-readable description of the arguments, for example `{ id: string }`. */
6
+ readonly argsHint?: string;
7
+ }
8
+ export declare class CommandRegistry {
9
+ private readonly entries;
10
+ /** Registers a command; registering the same name again replaces it (Fast Refresh re-runs modules). Returns an unregister function. */
11
+ register(name: string, handler: CommandHandler, meta?: CommandMeta): () => void;
12
+ list(): CommandInfo[];
13
+ call(name: string, args: unknown): Promise<CommandResult>;
14
+ }
15
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommandRegistry = void 0;
4
+ class CommandRegistry {
5
+ constructor() {
6
+ this.entries = new Map();
7
+ }
8
+ /** Registers a command; registering the same name again replaces it (Fast Refresh re-runs modules). Returns an unregister function. */
9
+ register(name, handler, meta = {}) {
10
+ if (typeof name !== 'string' || name.trim() === '')
11
+ throw new TypeError('a command needs a non-empty name');
12
+ if (typeof handler !== 'function')
13
+ throw new TypeError('a command needs a handler function');
14
+ const entry = { handler, meta };
15
+ this.entries.set(name, entry);
16
+ return () => {
17
+ if (this.entries.get(name) === entry)
18
+ this.entries.delete(name);
19
+ };
20
+ }
21
+ list() {
22
+ const list = [];
23
+ this.entries.forEach((entry, name) => {
24
+ const info = { name };
25
+ if (entry.meta.description !== undefined)
26
+ info.description = entry.meta.description;
27
+ if (entry.meta.argsHint !== undefined)
28
+ info.argsHint = entry.meta.argsHint;
29
+ list.push(info);
30
+ });
31
+ return list;
32
+ }
33
+ async call(name, args) {
34
+ const entry = this.entries.get(name);
35
+ if (!entry)
36
+ return { ok: false, error: { code: 'command_not_found', message: `No command named "${name}" is registered.` } };
37
+ try {
38
+ const value = await entry.handler(args);
39
+ const text = JSON.stringify(value === undefined ? null : value);
40
+ if (text === undefined)
41
+ return { ok: false, error: { code: 'command_failed', message: 'The command result is not JSON-serializable.' } };
42
+ return { ok: true, value: JSON.parse(text) };
43
+ }
44
+ catch (error) {
45
+ const message = error instanceof Error ? error.message : typeof error === 'string' ? error : 'The command failed.';
46
+ return { ok: false, error: { code: 'command_failed', message } };
47
+ }
48
+ }
49
+ }
50
+ exports.CommandRegistry = CommandRegistry;
@@ -0,0 +1,7 @@
1
+ import { type HostGlobal } from './host.js';
2
+ import type { LogLevel } from '@parallax-rn/protocol';
3
+ export interface Installed {
4
+ uninstall(): void;
5
+ }
6
+ export declare function installConsoleCapture(host: HostGlobal, onLog: (level: LogLevel, args: unknown[]) => void): Installed;
7
+ //# sourceMappingURL=console-capture.d.ts.map
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installConsoleCapture = installConsoleCapture;
4
+ // Captures console output without disturbing it: the original method always runs first with the same arguments and `this`,
5
+ // so LogBox, Metro logs and the DevTools console behave exactly as without the SDK. A failing capture never reaches the app.
6
+ const host_js_1 = require("./host.js");
7
+ function installConsoleCapture(host, onLog) {
8
+ const target = host.console;
9
+ const restores = [];
10
+ let active = true;
11
+ let capturing = false;
12
+ // One function per level instead of closures created in the loop body: Hermes evaluates code it receives through
13
+ // Runtime.evaluate without per-iteration bindings, so every closure in a `for (const level of ...)` loop saw the last level.
14
+ function wrapLevel(console, level) {
15
+ const original = console[level];
16
+ if (typeof original !== 'function')
17
+ return;
18
+ const wrapper = function (...args) {
19
+ try {
20
+ return original.apply(this, args);
21
+ }
22
+ finally {
23
+ if (active && !capturing) {
24
+ capturing = true;
25
+ try {
26
+ onLog(level, args);
27
+ }
28
+ catch {
29
+ /* capture must never break the app */
30
+ }
31
+ finally {
32
+ capturing = false;
33
+ }
34
+ }
35
+ }
36
+ };
37
+ console[level] = wrapper;
38
+ restores.push(() => {
39
+ // someone may have wrapped console after us: then leave their wrapper in place and just stop capturing
40
+ if (console[level] === wrapper)
41
+ console[level] = original;
42
+ });
43
+ }
44
+ if (target) {
45
+ for (const level of host_js_1.CONSOLE_LEVELS)
46
+ wrapLevel(target, level);
47
+ }
48
+ return {
49
+ uninstall() {
50
+ active = false;
51
+ for (const restore of restores)
52
+ restore();
53
+ restores.length = 0;
54
+ },
55
+ };
56
+ }
@@ -0,0 +1,4 @@
1
+ import type { HostGlobal } from './host.js';
2
+ import type { Installed } from './console-capture.js';
3
+ export declare function installErrorCapture(host: HostGlobal, onError: (error: unknown, isFatal: boolean) => void): Installed | null;
4
+ //# sourceMappingURL=error-capture.d.ts.map
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installErrorCapture = installErrorCapture;
4
+ function installErrorCapture(host, onError) {
5
+ const utils = host.ErrorUtils;
6
+ if (!utils || typeof utils.getGlobalHandler !== 'function' || typeof utils.setGlobalHandler !== 'function')
7
+ return null;
8
+ const previous = utils.getGlobalHandler();
9
+ let active = true;
10
+ const handler = (error, isFatal) => {
11
+ if (active) {
12
+ try {
13
+ onError(error, isFatal === true);
14
+ }
15
+ catch {
16
+ /* capture must never break error handling */
17
+ }
18
+ }
19
+ if (typeof previous === 'function')
20
+ previous(error, isFatal);
21
+ };
22
+ utils.setGlobalHandler(handler);
23
+ return {
24
+ uninstall() {
25
+ active = false;
26
+ if (typeof utils.getGlobalHandler === 'function' && utils.getGlobalHandler() === handler && typeof utils.setGlobalHandler === 'function' && previous) {
27
+ utils.setGlobalHandler(previous);
28
+ }
29
+ },
30
+ };
31
+ }
@@ -0,0 +1,10 @@
1
+ export interface Capped {
2
+ readonly text: string;
3
+ readonly truncated: boolean;
4
+ }
5
+ /** Cuts the text to `max` characters (the last one becomes an ellipsis) and says whether it did. */
6
+ export declare function cap(text: string, max: number): Capped;
7
+ /** One argument as it appears in a console line: strings as they are, Errors with their stack, everything else inspected. */
8
+ export declare function formatArg(value: unknown): string;
9
+ export declare function formatArgs(args: readonly unknown[]): string;
10
+ //# sourceMappingURL=format.d.ts.map
package/dist/format.js ADDED
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ // Turns console arguments and thrown values into bounded text. It must never throw and never loop on cycles.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.cap = cap;
5
+ exports.formatArg = formatArg;
6
+ exports.formatArgs = formatArgs;
7
+ const MAX_NODES = 200;
8
+ const MAX_DEPTH = 3;
9
+ /** Cuts the text to `max` characters (the last one becomes an ellipsis) and says whether it did. */
10
+ function cap(text, max) {
11
+ if (text.length <= max)
12
+ return { text, truncated: false };
13
+ return { text: max > 1 ? text.slice(0, max - 1) + '…' : text.slice(0, max), truncated: true };
14
+ }
15
+ function describeFunction(fn) {
16
+ const name = fn.name;
17
+ return typeof name === 'string' && name ? `[Function ${name}]` : '[Function]';
18
+ }
19
+ function inspect(value, depth, seen, budget) {
20
+ if (budget.nodes-- <= 0)
21
+ return '…';
22
+ switch (typeof value) {
23
+ case 'string':
24
+ return JSON.stringify(value);
25
+ case 'number':
26
+ case 'boolean':
27
+ case 'undefined':
28
+ return String(value);
29
+ case 'bigint':
30
+ return `${String(value)}n`;
31
+ case 'symbol':
32
+ return String(value);
33
+ case 'function':
34
+ return describeFunction(value);
35
+ }
36
+ if (value === null)
37
+ return 'null';
38
+ if (seen.indexOf(value) !== -1)
39
+ return '[Circular]';
40
+ if (value instanceof Error)
41
+ return `${value.name}: ${value.message}`;
42
+ if (value instanceof Date)
43
+ return isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString();
44
+ if (depth >= MAX_DEPTH)
45
+ return Array.isArray(value) ? '[Array]' : '[Object]';
46
+ seen.push(value);
47
+ try {
48
+ if (Array.isArray(value)) {
49
+ const parts = [];
50
+ for (let i = 0; i < value.length && budget.nodes > 0; i++)
51
+ parts.push(inspect(value[i], depth + 1, seen, budget));
52
+ return `[${parts.join(', ')}${parts.length < value.length ? ', …' : ''}]`;
53
+ }
54
+ const keys = Object.keys(value);
55
+ const parts = [];
56
+ for (let i = 0; i < keys.length && budget.nodes > 0; i++) {
57
+ const key = keys[i];
58
+ // an accessor is not called: reading it could run the app's code a second time (a getter with a side effect, or one that is slow)
59
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
60
+ if (descriptor !== undefined && !('value' in descriptor) && (descriptor.get !== undefined || descriptor.set !== undefined)) {
61
+ parts.push(`${key}: [Getter]`);
62
+ continue;
63
+ }
64
+ parts.push(`${key}: ${inspect(value[key], depth + 1, seen, budget)}`);
65
+ }
66
+ const ctor = value.constructor;
67
+ const name = typeof (ctor === null || ctor === void 0 ? void 0 : ctor.name) === 'string' && ctor.name !== 'Object' ? `${ctor.name} ` : '';
68
+ return `${name}{${parts.join(', ')}${parts.length < keys.length ? ', …' : ''}}`;
69
+ }
70
+ finally {
71
+ seen.pop();
72
+ }
73
+ }
74
+ /** One argument as it appears in a console line: strings as they are, Errors with their stack, everything else inspected. */
75
+ function formatArg(value) {
76
+ try {
77
+ if (typeof value === 'string')
78
+ return value;
79
+ if (value instanceof Error)
80
+ return typeof value.stack === 'string' && value.stack ? value.stack : `${value.name}: ${value.message}`;
81
+ return inspect(value, 0, [], { nodes: MAX_NODES });
82
+ }
83
+ catch {
84
+ return '[unprintable]';
85
+ }
86
+ }
87
+ function formatArgs(args) {
88
+ const parts = [];
89
+ for (let i = 0; i < args.length; i++)
90
+ parts.push(formatArg(args[i]));
91
+ return parts.join(' ');
92
+ }
package/dist/host.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { LogLevel } from '@parallax-rn/protocol';
2
+ export type ConsoleMethod = (...args: unknown[]) => unknown;
3
+ export type ErrorHandler = (error: unknown, isFatal?: boolean) => void;
4
+ export interface HostGlobal {
5
+ __DEV__?: boolean;
6
+ console?: {
7
+ [level: string]: unknown;
8
+ };
9
+ ErrorUtils?: {
10
+ getGlobalHandler?: () => ErrorHandler | undefined;
11
+ setGlobalHandler?: (handler: ErrorHandler) => void;
12
+ };
13
+ setTimeout?: (callback: () => void, ms: number) => unknown;
14
+ clearTimeout?: (handle: unknown) => void;
15
+ [name: string]: unknown;
16
+ }
17
+ export declare const CONSOLE_LEVELS: readonly LogLevel[];
18
+ export declare function defaultHost(): HostGlobal;
19
+ //# sourceMappingURL=host.d.ts.map
package/dist/host.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONSOLE_LEVELS = void 0;
4
+ exports.defaultHost = defaultHost;
5
+ exports.CONSOLE_LEVELS = ['debug', 'log', 'info', 'warn', 'error'];
6
+ function defaultHost() {
7
+ return globalThis;
8
+ }
@@ -0,0 +1,24 @@
1
+ import type { CommandHandler, CommandMeta } from './commands.js';
2
+ import type { NavigationRefLike } from './navigation.js';
3
+ import type { StoreOptions } from './stores.js';
4
+ /** Starts capturing console output and uncaught errors. Returns false and does nothing when `__DEV__` is false. */
5
+ export declare const start: () => boolean;
6
+ export declare const stop: () => void;
7
+ export declare const isStarted: () => boolean;
8
+ /** Registers a command the agent can run; registering the same name again replaces it. Returns an unregister function. */
9
+ export declare const registerCommand: (name: string, handler: CommandHandler, meta?: CommandMeta) => (() => void);
10
+ /** Follows a React Navigation container ref (`createNavigationContainerRef()`, Expo Router's `useNavigationContainerRef()`): the agent reads the route and its history. Returns the stop function. */
11
+ export declare const registerNavigation: (ref: NavigationRefLike) => (() => void);
12
+ /** Follows a Redux or Zustand store (anything with `getState()` and `subscribe()`) as the source `state:<name>`; `{ actions: true }` also records the last Redux action type. Returns an unregister function. */
13
+ export declare const registerStore: (name: string, store: unknown, options?: StoreOptions) => (() => void);
14
+ /** Follows a TanStack Query client as the source `query:<name>`. Returns an unregister function. */
15
+ export declare const registerQueryClient: (name: string, client: unknown) => (() => void);
16
+ export { createSdk } from './sdk.js';
17
+ export type { Sdk, SdkOptions } from './sdk.js';
18
+ export type { CommandHandler, CommandMeta } from './commands.js';
19
+ export type { NavigationRefLike } from './navigation.js';
20
+ export type { StoreOptions } from './stores.js';
21
+ export { DEFAULT_LIMITS } from './limits.js';
22
+ export type { SdkLimits } from './limits.js';
23
+ export { SDK_VERSION } from './version.js';
24
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SDK_VERSION = exports.DEFAULT_LIMITS = exports.createSdk = exports.registerQueryClient = exports.registerStore = exports.registerNavigation = exports.registerCommand = exports.isStarted = exports.stop = exports.start = void 0;
4
+ // Public surface of @parallax-rn/sdk. Importing it has no side effects; call `start()` behind `__DEV__`:
5
+ // if (__DEV__) { require('@parallax-rn/sdk').start(); }
6
+ const sdk_js_1 = require("./sdk.js");
7
+ const sdk = (0, sdk_js_1.createSdk)();
8
+ /** Starts capturing console output and uncaught errors. Returns false and does nothing when `__DEV__` is false. */
9
+ const start = () => sdk.start();
10
+ exports.start = start;
11
+ const stop = () => sdk.stop();
12
+ exports.stop = stop;
13
+ const isStarted = () => sdk.started;
14
+ exports.isStarted = isStarted;
15
+ /** Registers a command the agent can run; registering the same name again replaces it. Returns an unregister function. */
16
+ const registerCommand = (name, handler, meta) => sdk.registerCommand(name, handler, meta);
17
+ exports.registerCommand = registerCommand;
18
+ /** Follows a React Navigation container ref (`createNavigationContainerRef()`, Expo Router's `useNavigationContainerRef()`): the agent reads the route and its history. Returns the stop function. */
19
+ const registerNavigation = (ref) => sdk.registerNavigation(ref);
20
+ exports.registerNavigation = registerNavigation;
21
+ /** Follows a Redux or Zustand store (anything with `getState()` and `subscribe()`) as the source `state:<name>`; `{ actions: true }` also records the last Redux action type. Returns an unregister function. */
22
+ const registerStore = (name, store, options) => sdk.registerStore(name, store, options);
23
+ exports.registerStore = registerStore;
24
+ /** Follows a TanStack Query client as the source `query:<name>`. Returns an unregister function. */
25
+ const registerQueryClient = (name, client) => sdk.registerQueryClient(name, client);
26
+ exports.registerQueryClient = registerQueryClient;
27
+ var sdk_js_2 = require("./sdk.js");
28
+ Object.defineProperty(exports, "createSdk", { enumerable: true, get: function () { return sdk_js_2.createSdk; } });
29
+ var limits_js_1 = require("./limits.js");
30
+ Object.defineProperty(exports, "DEFAULT_LIMITS", { enumerable: true, get: function () { return limits_js_1.DEFAULT_LIMITS; } });
31
+ var version_js_1 = require("./version.js");
32
+ Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return version_js_1.SDK_VERSION; } });
@@ -0,0 +1,31 @@
1
+ export interface SdkLimits {
2
+ /** Entries kept per stream: the history a late client can drain. */
3
+ readonly logCapacity: number;
4
+ readonly errorCapacity: number;
5
+ /** Requests kept by the network capture (plan 0013, Part B). */
6
+ readonly networkCapacity: number;
7
+ /** Transitions kept of the `navigation` source. */
8
+ readonly navigationCapacity: number;
9
+ /** Characters of one request or response body (a longer one is cut and says so), of one header value, and of a request's call-site stack. */
10
+ readonly networkBodyChars: number;
11
+ readonly networkHeaderChars: number;
12
+ readonly networkStackChars: number;
13
+ /** Store snapshots (plan 0013, 13.9): entries kept per store, the character budget and depth of one snapshot, the budget of one query's data, and the throttle between snapshots. */
14
+ readonly storeCapacity: number;
15
+ readonly stateMaxChars: number;
16
+ readonly stateMaxDepth: number;
17
+ readonly queryDataMaxChars: number;
18
+ readonly snapshotIntervalMs: number;
19
+ /** Per-entry caps in characters; a cut entry is marked `truncated`. Separate from the tool-output limit an agent sees. */
20
+ readonly maxMessageChars: number;
21
+ readonly maxStackChars: number;
22
+ /** One push carries at most this many entries and about this many characters. */
23
+ readonly maxBatchEntries: number;
24
+ readonly maxBatchChars: number;
25
+ /** Bursts are coalesced for this long before a push (errors are pushed at once). */
26
+ readonly flushIntervalMs: number;
27
+ /** Default and upper bound of entries per stream returned by one drain call. */
28
+ readonly drainLimit: number;
29
+ }
30
+ export declare const DEFAULT_LIMITS: SdkLimits;
31
+ //# sourceMappingURL=limits.d.ts.map
package/dist/limits.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_LIMITS = void 0;
4
+ exports.DEFAULT_LIMITS = {
5
+ logCapacity: 1000,
6
+ errorCapacity: 100,
7
+ // ponytail: a count, not a byte budget (the plan's 2 MB): typical bodies are far under the cap, the worst case is 300 x 2 bodies; measure on a real app (L27) and add a byte budget if it matters
8
+ networkCapacity: 300,
9
+ navigationCapacity: 100,
10
+ networkBodyChars: 32000,
11
+ networkHeaderChars: 2000,
12
+ networkStackChars: 4000,
13
+ storeCapacity: 50,
14
+ stateMaxChars: 32000,
15
+ stateMaxDepth: 6,
16
+ queryDataMaxChars: 2000,
17
+ snapshotIntervalMs: 200,
18
+ maxMessageChars: 4000,
19
+ maxStackChars: 8000,
20
+ maxBatchEntries: 100,
21
+ maxBatchChars: 100000,
22
+ flushIntervalMs: 50,
23
+ drainLimit: 500,
24
+ };
@@ -0,0 +1,26 @@
1
+ import { type NavigationEntry } from '@parallax-rn/protocol';
2
+ /** What a container ref must have: React Navigation's `createNavigationContainerRef()` and Expo Router's `useNavigationContainerRef()` both do. */
3
+ export interface NavigationRefLike {
4
+ getRootState(): unknown;
5
+ addListener(event: 'state', listener: () => void): unknown;
6
+ }
7
+ export declare const NAV_LIMITS: {
8
+ readonly depth: 8;
9
+ readonly routes: 50;
10
+ readonly paramDepth: 3;
11
+ readonly keys: 30;
12
+ readonly chars: 200;
13
+ readonly rootStateChars: 16000;
14
+ };
15
+ /** Params without secrets: keys by the picker's word rule, values shaped like a credential, long strings cut, depth and width capped, getters and functions never run. */
16
+ export declare function scrubParams(value: unknown, depth?: number, seen?: object[]): unknown;
17
+ /** A route's `path` (a deep link) with the value of every secret-looking query parameter masked. */
18
+ export declare function scrubPath(path: string): string;
19
+ /** The entry for one root state: the focused route (the deepest one following each navigator's `index`), its params and path, the route names from the root, the reduced state (dropped past 16,000 characters). Null: no routes. */
20
+ export declare function entryOf(rootState: unknown): NavigationEntry | null;
21
+ /**
22
+ * Follows a container ref: `emit` gets an entry now and after every state change that differs from the last one. Returns the stop function. A ref that throws is ignored (the
23
+ * app must never see the SDK fail).
24
+ */
25
+ export declare function followNavigation(ref: NavigationRefLike, emit: (entry: NavigationEntry) => void): () => void;
26
+ //# sourceMappingURL=navigation.d.ts.map