@unbound-app/api 3.0.0 → 3.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbound-app/api",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
4
4
  "author": {
5
5
  "name": "Mario P.",
6
6
  "email": "contact@marioparaschiv.com",
@@ -2,7 +2,7 @@
2
2
 
3
3
  import type { LiteralUnion } from './_types';
4
4
  import * as React$1 from 'react';
5
- import { ComponentType, FunctionComponent, ReactElement, ReactNode } from 'react';
5
+ import { ComponentType, Context, FunctionComponent, ReactElement, ReactNode } from 'react';
6
6
  import * as RN from 'react-native';
7
7
  import { ImageSourcePropType, ImageStyle, TextStyle, ViewStyle } from 'react-native';
8
8
 
@@ -87,12 +87,20 @@ export declare const NATIVE_BRIDGE_CAPABILITIES: readonly [
87
87
  'native.objc.ivars',
88
88
  'native.objc.associations',
89
89
  'native.objc.hooks',
90
+ 'native.objc.blocks',
91
+ 'native.objc.classDefinition',
92
+ 'native.objc.classSearch',
93
+ 'native.objc.inspect',
90
94
  'native.ffi.symbols',
91
95
  'native.ffi.call',
96
+ 'native.ffi.memory',
97
+ 'native.ffi.callbacks',
98
+ 'native.ffi.rebind',
92
99
  'native.fabric.mount',
93
100
  'native.fabric.async',
94
101
  'native.runtime.events',
95
102
  'native.java.classes',
103
+ 'native.java.classSearch',
96
104
  'native.java.construct',
97
105
  'native.java.fields',
98
106
  'native.java.invoke',
@@ -135,8 +143,25 @@ export declare const NATIVE_BRIDGE_METHOD_CAPABILITIES: {
135
143
  readonly 'objc.array': 'native.objc.classes';
136
144
  readonly 'objc.data': 'native.objc.classes';
137
145
  readonly 'objc.hook': 'native.objc.hooks';
146
+ readonly 'objc.block': 'native.objc.blocks';
147
+ readonly 'objc.callBlock': 'native.objc.blocks';
148
+ readonly 'objc.defineClass': 'native.objc.classDefinition';
149
+ readonly 'objc.findClasses': 'native.objc.classSearch';
150
+ readonly 'objc.inspectClass': 'native.objc.inspect';
151
+ readonly 'objc.inspectMembers': 'native.objc.inspect';
152
+ readonly 'objc.inspectValues': 'native.objc.inspect';
153
+ readonly 'objc.inspectImages': 'native.objc.inspect';
154
+ readonly 'objc.inspectClasses': 'native.objc.inspect';
138
155
  readonly 'ffi.symbol': 'native.ffi.symbols';
139
156
  readonly 'ffi.call': 'native.ffi.call';
157
+ readonly 'ffi.alloc': 'native.ffi.memory';
158
+ readonly 'ffi.free': 'native.ffi.memory';
159
+ readonly 'ffi.read': 'native.ffi.memory';
160
+ readonly 'ffi.write': 'native.ffi.memory';
161
+ readonly 'ffi.callback': 'native.ffi.callbacks';
162
+ readonly 'ffi.releaseCallback': 'native.ffi.callbacks';
163
+ readonly 'ffi.rebind': 'native.ffi.rebind';
164
+ readonly 'ffi.restore': 'native.ffi.rebind';
140
165
  readonly 'fabric.mount': 'native.fabric.mount';
141
166
  readonly 'fabric.update': 'native.fabric.mount';
142
167
  readonly 'fabric.setSize': 'native.fabric.mount';
@@ -156,6 +181,7 @@ export declare const NATIVE_BRIDGE_METHOD_CAPABILITIES: {
156
181
  readonly 'java.getClass': 'native.java.classes';
157
182
  readonly 'java.className': 'native.java.classes';
158
183
  readonly 'java.describe': 'native.java.classes';
184
+ readonly 'java.findClasses': 'native.java.classSearch';
159
185
  readonly 'java.construct': 'native.java.construct';
160
186
  readonly 'java.respondsTo': 'native.java.classes';
161
187
  readonly 'java.invoke': 'native.java.invoke';
@@ -180,6 +206,7 @@ export declare const NATIVE_BRIDGE_METHOD_CAPABILITIES: {
180
206
  readonly 'hooks.removeReturnValue': 'native.java.hooks';
181
207
  readonly 'hooks.clearReturnValues': 'native.java.hooks';
182
208
  readonly 'hooks.remove': 'native.java.hooks';
209
+ readonly 'hooks.proxy': 'native.java.hooks';
183
210
  };
184
211
  /** The host platforms a native bridge runs on. */
185
212
  export declare const NATIVE_BRIDGE_PLATFORMS: readonly ['ios', 'android'];
@@ -194,8 +221,15 @@ export declare const NATIVE_BRIDGE_PLATFORM_CAPABILITIES: {
194
221
  'native.objc.ivars',
195
222
  'native.objc.associations',
196
223
  'native.objc.hooks',
224
+ 'native.objc.blocks',
225
+ 'native.objc.classDefinition',
226
+ 'native.objc.classSearch',
227
+ 'native.objc.inspect',
197
228
  'native.ffi.symbols',
198
229
  'native.ffi.call',
230
+ 'native.ffi.memory',
231
+ 'native.ffi.callbacks',
232
+ 'native.ffi.rebind',
199
233
  'native.fabric.mount',
200
234
  'native.fabric.async',
201
235
  'native.runtime.events',
@@ -205,9 +239,13 @@ export declare const NATIVE_BRIDGE_PLATFORM_CAPABILITIES: {
205
239
  readonly android: readonly [
206
240
  'native.ffi.symbols',
207
241
  'native.ffi.call',
242
+ 'native.ffi.memory',
243
+ 'native.ffi.callbacks',
244
+ 'native.ffi.rebind',
208
245
  'native.fabric.async',
209
246
  'native.runtime.events',
210
247
  'native.java.classes',
248
+ 'native.java.classSearch',
211
249
  'native.java.construct',
212
250
  'native.java.fields',
213
251
  'native.java.invoke',
@@ -1353,6 +1391,8 @@ export type DesignHelpers = {
1353
1391
  TextStyleSheet: Record<TextVariant, TextStyle>;
1354
1392
  /** Hook that resolves a semantic colour token, such as `Theme.colors.TEXT_DEFAULT`, to a colour for the current theme. */
1355
1393
  useToken: (token: SemanticColorToken) => string;
1394
+ /** Whether a row sits in a `TableRowGroup`, which then rounds the group's card itself; rows outside one round their own. */
1395
+ TableRowGroupContext: React$1.Context<boolean>;
1356
1396
  /** Hook that builds the state a `SegmentedControl` and its `SegmentedControlPages` share. */
1357
1397
  useSegmentedControlState: (options: SegmentedControlStateOptions) => SegmentedControlState;
1358
1398
  /** Opens an alert as `openAlert(key, element)`; pass the same key to `dismissAlert` to close it. */
@@ -3415,7 +3455,9 @@ export type LoaderNativeBridge = Pick<
3415
3455
  readonly java?: NativeJavaBridge & NativeUIScopeFactory;
3416
3456
  };
3417
3457
  /** The `objc` namespace the loader publishes, before the client adds `create` and scopes it to a plugin. */
3418
- export type LoaderObjCBridge = Omit<NativeObjCBridge, 'create'> & NativeUIScopeFactory;
3458
+ export type LoaderObjCBridge = Omit<NativeObjCBridge, 'create'> &
3459
+ Partial<NativeObjCInspector> &
3460
+ NativeUIScopeFactory;
3419
3461
  /**
3420
3462
  * Props of `LoadingButtonPill` (function). Exports: LoadingButtonPill.
3421
3463
  * @see design/components/Button/native/ButtonPill.native.tsx
@@ -3835,6 +3877,9 @@ export type NativeBridgeError = {
3835
3877
  };
3836
3878
  /** The stable `code` on an error the native bridge or its plugin scope throws. */
3837
3879
  export type NativeBridgeErrorCode =
3880
+ | 'INSPECTOR_DISABLED'
3881
+ | 'INSPECTOR_UNAVAILABLE'
3882
+ | 'INSPECTOR_UNKNOWN_CLASS'
3838
3883
  | 'NATIVE_BRIDGE_ERROR'
3839
3884
  | 'NATIVE_PLUGIN_API_VERSION_UNSUPPORTED'
3840
3885
  | 'NATIVE_PLUGIN_CALL_CANCELLED'
@@ -3842,10 +3887,12 @@ export type NativeBridgeErrorCode =
3842
3887
  | 'NATIVE_PLUGIN_CAPABILITY_DENIED'
3843
3888
  | 'NATIVE_PLUGIN_SCOPE_DISPOSED'
3844
3889
  | 'NATIVE_PLUGIN_UNAVAILABLE';
3845
- /** A native bridge method by the path a plugin calls it through, such as `objc.invoke`. */
3846
- export type NativeBridgeMethod = {
3847
- [N in NativeBridgeNamespace]: `${N}.${keyof NativeBridge[N] & string}`;
3848
- }[NativeBridgeNamespace];
3890
+ /** A native bridge method by the path a plugin calls it through, such as `objc.invoke`, or the debugger's `objc.inspect*`. */
3891
+ export type NativeBridgeMethod =
3892
+ | {
3893
+ [N in NativeBridgeNamespace]: `${N}.${keyof NativeBridge[N] & string}`;
3894
+ }[NativeBridgeNamespace]
3895
+ | `objc.${keyof NativeObjCInspector}`;
3849
3896
  /** A {@link NativeBridge} namespace whose methods a plugin calls, such as `objc` or `java`. */
3850
3897
  export type NativeBridgeNamespace = Exclude<
3851
3898
  keyof NativeBridge,
@@ -3931,12 +3978,89 @@ export type NativeDebugEvaluationLog = {
3931
3978
  /** The printed text. */
3932
3979
  message: string;
3933
3980
  };
3981
+ /** Memory and thermal pressure, as each platform reports it. */
3982
+ export type NativeDeviceState = {
3983
+ /** Memory available to apps, in megabytes. */
3984
+ availableMB?: number;
3985
+ /** Android: the system considers itself low on memory. */
3986
+ lowMemory?: boolean;
3987
+ /** iOS: memory warnings Discord received this session. */
3988
+ memoryWarnings?: number;
3989
+ /** How hot the device runs, as the system rates it. */
3990
+ thermal?: 'nominal' | 'fair' | 'serious' | 'critical';
3991
+ /** Free space on the volume holding Discord's documents, and the size of Unbound's folder there. */
3992
+ freeMB?: number;
3993
+ /** The size of Unbound's folder, in megabytes. */
3994
+ unboundMB?: number;
3995
+ /** The active connection, and whether a VPN, a proxy, a metered plan or Low Data Mode (Data Saver) applies. */
3996
+ network?: NativeNetworkState;
3997
+ /** Other tweaks or modules loaded into Discord: iOS dylib names, Android packages or Magisk modules; older loaders omit it. */
3998
+ injected?: string[];
3999
+ };
4000
+ /**
4001
+ * The install, binary, device and network facts `/debug` shows, each already a display string.
4002
+ * @internal
4003
+ */
4004
+ type NativeEnvironmentReport = {
4005
+ /** iOS: the signing certificate's SHA-256 prefix and issuer. */
4006
+ signer?: string;
4007
+ /** iOS: encryption, UUID, minimum OS, SDK and dylib count of Discord's executable. */
4008
+ binary?: string;
4009
+ /** Android: the APK signer, patch mode, splits and profile. */
4010
+ install?: string;
4011
+ markers?: string[];
4012
+ /** iOS: the OS build number when it is a beta. */
4013
+ osBeta?: string;
4014
+ /** Whether Discord's HTTPS traffic reaches a certificate from a known public CA. */
4015
+ https?: {
4016
+ intercepted: boolean;
4017
+ issuer?: string;
4018
+ failure?: string;
4019
+ };
4020
+ };
3934
4021
  /** Calls C functions in images Discord already loaded. An unsigned plugin needs the user's consent to use it. */
3935
4022
  export type NativeFFIBridge = {
3936
4023
  /** Resolves a symbol in a loaded image, or in every loaded image when `image` is omitted; `null` when not found. */
3937
4024
  symbol(name: string, image?: string): NativePointer | null;
3938
4025
  /** Calls a symbol from `symbol` with an explicit signature. */
3939
4026
  call(pointer: NativePointer, signature: NativeFFISignature, ...args: unknown[]): unknown;
4027
+ /** Allocates zeroed memory for out-parameters and buffers; free it with `free`. Needs `native.ffi.memory`. */
4028
+ alloc(size: number): NativePointer;
4029
+ /** Frees memory from `alloc`; `false` for any other pointer. Needs `native.ffi.memory`. */
4030
+ free(pointer: NativePointer): boolean;
4031
+ /** Reads a value of a type at a pointer, `offset` bytes in, failing safely on an unreadable address. Needs `native.ffi.memory`. */
4032
+ read(pointer: NativePointer, type: NativeFFIType, offset?: number): NativeFFIValue;
4033
+ /** Writes a value of a type into memory from `alloc`, `offset` bytes in. Needs `native.ffi.memory`. */
4034
+ write(
4035
+ pointer: NativePointer,
4036
+ type: NativeFFIType,
4037
+ value: NativeFFIValue,
4038
+ offset?: number,
4039
+ ): void;
4040
+ /**
4041
+ * Makes a C function pointer that runs `implementation` on the JavaScript thread; 32 per return kind live at
4042
+ * once, so release each with `releaseCallback`. Needs `native.ffi.callbacks`.
4043
+ */
4044
+ callback(
4045
+ signature: NativeFFISignature,
4046
+ implementation: (...args: never[]) => NativeFFIValue | void,
4047
+ ): NativePointer;
4048
+ /** Frees a pointer from `callback`. Needs `native.ffi.callbacks`. */
4049
+ releaseCallback(pointer: NativePointer): boolean;
4050
+ /**
4051
+ * Points every image's imports of a C function at `replacement`, such as a pointer from `callback`, and returns
4052
+ * the original to call through; the loader's own calls keep the original. Needs `native.ffi.rebind`.
4053
+ */
4054
+ rebind(name: string, replacement: NativePointer): NativeFFIRebinding;
4055
+ /** Puts back every import `rebind` changed for a symbol. Needs `native.ffi.rebind`. */
4056
+ restore(name: string): boolean;
4057
+ };
4058
+ /** What `rebind` changed: the original function and how many import slots now point at the replacement. */
4059
+ export type NativeFFIRebinding = {
4060
+ /** The function the import slots pointed at before. */
4061
+ original: NativePointer;
4062
+ /** How many import slots now point at the replacement. */
4063
+ slots: number;
3940
4064
  };
3941
4065
  /** A non-variadic C function signature for `ffi.call`. */
3942
4066
  export type NativeFFISignature = {
@@ -3970,6 +4094,15 @@ export type NativeFFITypeName =
3970
4094
  | 'object'
3971
4095
  | 'class'
3972
4096
  | 'selector';
4097
+ /** A value an FFI type reads or writes: a number, `BigInt` for 64-bit integers, a boolean, a string or a pointer. */
4098
+ export type NativeFFIValue =
4099
+ | number
4100
+ | bigint
4101
+ | boolean
4102
+ | string
4103
+ | NativePointer
4104
+ | NativeStruct
4105
+ | null;
3973
4106
  /** One surface's changes in a `batchAsync` call, applied as properties, then size, then frame. */
3974
4107
  export type NativeFabricBatchMutation = {
3975
4108
  /** The surface these changes apply to. */
@@ -4131,6 +4264,10 @@ export type NativeHookOptions<Receiver extends boolean = boolean> = {
4131
4264
  /** Removes the hook after its first call. */
4132
4265
  once?: boolean;
4133
4266
  payload?: NativeHookPayload<Receiver>;
4267
+ /** Also hooks every loaded subclass that implements the selector itself. */
4268
+ subclasses?: boolean;
4269
+ /** Waits for a class that is not loaded yet, retrying with backoff for up to ten minutes. */
4270
+ waitForClass?: boolean;
4134
4271
  } & NativeCallOptions;
4135
4272
  /** Trims what a hook sends to JavaScript per call, to cut crossing cost on hot methods. */
4136
4273
  export type NativeHookPayload<Receiver extends boolean = boolean> = {
@@ -4154,6 +4291,123 @@ export type NativeHookToken = {
4154
4291
  /** Removes the hook; calling it again does nothing. */
4155
4292
  remove(): void;
4156
4293
  };
4294
+ /** A kind of a class's own members `objc.inspectMembers` lists. */
4295
+ export type NativeInspectKind = 'properties' | 'ivars' | 'methods' | 'classMethods' | 'protocols';
4296
+ /** Narrows a page of inspected members, images or classes. */
4297
+ export type NativeInspectPage = {
4298
+ /** How many matches to skip. */
4299
+ offset?: number;
4300
+ /** At most 200. */
4301
+ limit?: number;
4302
+ /** A case-insensitive substring of the name. */
4303
+ filter?: string;
4304
+ };
4305
+ /** Options for `objc.inspectValues`. */
4306
+ export type NativeInspectValuesOptions = {
4307
+ /** Runs the getters of computed properties, which automatic reads skip. */
4308
+ invokeGetters?: boolean;
4309
+ };
4310
+ /** One class of an inspected class chain, from the class itself to its root. */
4311
+ export type NativeInspectedClass = {
4312
+ /** The class's name. */
4313
+ name: string;
4314
+ /** The superclass's name, or `null` at the root. */
4315
+ superclass: string | null;
4316
+ /** The path of the image that defines the class, or `null` when unknown. */
4317
+ image: string | null;
4318
+ /** The size of an instance, in bytes. */
4319
+ instanceSize: number;
4320
+ /** How many of each kind of member the class itself declares. */
4321
+ counts: Record<NativeInspectKind, number>;
4322
+ };
4323
+ /** One page of an image's class names, and how many match in all. */
4324
+ export type NativeInspectedClasses = {
4325
+ /** How many classes match in all. */
4326
+ total: number;
4327
+ /** This page's class names. */
4328
+ classes: string[];
4329
+ };
4330
+ /** One loaded image that defines Objective-C classes. */
4331
+ export type NativeInspectedImage = {
4332
+ /** The image's path on the device. */
4333
+ path: string;
4334
+ /** The image's file name. */
4335
+ name: string;
4336
+ /** How many classes the image defines. */
4337
+ classCount: number;
4338
+ };
4339
+ /** One page of loaded images, and how many match in all. */
4340
+ export type NativeInspectedImages = {
4341
+ /** How many images match in all. */
4342
+ total: number;
4343
+ /** This page's images. */
4344
+ images: NativeInspectedImage[];
4345
+ };
4346
+ /** One member of a class; fields that do not apply to its kind are omitted. */
4347
+ export type NativeInspectedMember = {
4348
+ /** The member's name, or a method's selector. */
4349
+ name: string;
4350
+ /** The kind of member. */
4351
+ kind: NativeInspectKind;
4352
+ /** The decoded type, such as `NSString *` or `CGRect`. */
4353
+ type?: string;
4354
+ /** The declaration, such as `- (void)setFoo:(id)foo`. */
4355
+ signature?: string;
4356
+ /** A method's decoded return type. */
4357
+ returnType?: string;
4358
+ /** A method's decoded parameter types, in order. */
4359
+ parameters?: string[];
4360
+ /** The raw Objective-C type encoding. */
4361
+ encoding?: string;
4362
+ /** Property attributes, such as `nonatomic`, `strong`, `readonly`, `weak`, `copy` or `class`. */
4363
+ attributes?: string[];
4364
+ /** A property's getter selector. */
4365
+ getter?: string;
4366
+ /** A property's setter selector, absent when it is read-only. */
4367
+ setter?: string;
4368
+ /** The instance variable backing a property. */
4369
+ ivar?: string;
4370
+ /** An instance variable's offset in the object, in bytes. */
4371
+ offset?: number;
4372
+ /** An instance variable's size, in bytes. */
4373
+ size?: number;
4374
+ /** The path of the image that defines a method. */
4375
+ image?: string;
4376
+ /** Whether reading the value runs no code (`auto`), runs a getter (`manual`), or is unsafe (`never`). */
4377
+ read: 'auto' | 'manual' | 'never';
4378
+ };
4379
+ /** One page of a class's members of one kind, and how many match in all. */
4380
+ export type NativeInspectedMembers = {
4381
+ /** How many members match in all. */
4382
+ total: number;
4383
+ /** This page's members. */
4384
+ members: NativeInspectedMember[];
4385
+ };
4386
+ /** One value read by `objc.inspectValues`; `text` describes it for display. */
4387
+ export type NativeInspectedValue = {
4388
+ /** The member the value was read from. */
4389
+ name: string;
4390
+ /** What the value is; `skipped` was not read and `error` failed to read. */
4391
+ kind:
4392
+ | 'nil'
4393
+ | 'number'
4394
+ | 'bool'
4395
+ | 'string'
4396
+ | 'object'
4397
+ | 'class'
4398
+ | 'struct'
4399
+ | 'pointer'
4400
+ | 'selector'
4401
+ | 'other'
4402
+ | 'skipped'
4403
+ | 'error';
4404
+ /** The value as text, strings cut to 500 characters. */
4405
+ text: string;
4406
+ /** A handle to the value, for `kind: 'object'`. */
4407
+ object?: NativeObjectHandle;
4408
+ /** The class of an object or class value. */
4409
+ className?: string;
4410
+ };
4157
4411
  /** An argument to a Java call: a tagged value or a handle. */
4158
4412
  export type NativeJavaArgument = NativeJavaValue | NativeJavaHandle;
4159
4413
  /** A key from `java.createAssociationKey` that names one associated value. */
@@ -4177,6 +4431,11 @@ export type NativeJavaBridge = {
4177
4431
  getClass(name: string): NativeJavaHandle;
4178
4432
  /** Lists a class's constructors, methods and fields. */
4179
4433
  describe(name: string): NativeJavaClassDescription;
4434
+ /**
4435
+ * Finds Discord's classes by shape, so a lookup survives the obfuscated names changing between builds; results
4436
+ * are cached per Discord build. Needs `native.java.classSearch`.
4437
+ */
4438
+ findClasses(query: NativeJavaClassQuery): NativeCancelablePromise<string[]>;
4180
4439
  /** Runs independent operations in one call on the JavaScript thread and returns every result in order. */
4181
4440
  batch(operations: NativeJavaBatchOperation[]): NativeJavaValue[];
4182
4441
  /** Runs the operations like `batch`, on the main thread, so views can be read safely. */
@@ -4264,6 +4523,21 @@ export type NativeJavaClassDescription = {
4264
4523
  /** The fields the class declares. */
4265
4524
  fields: NativeJavaFieldDescription[];
4266
4525
  };
4526
+ /** What `java.findClasses` matches; library packages are skipped unless `package` names one. */
4527
+ export type NativeJavaClassQuery = {
4528
+ /** The class name starts with it, such as `com.discord.`. */
4529
+ package?: string;
4530
+ /** The class inherits from it, directly or further up. */
4531
+ extends?: string;
4532
+ /** The class implements every one, directly or through a superclass. */
4533
+ implements?: string[];
4534
+ /** Methods the class must have, every one matching. */
4535
+ methods?: NativeJavaMethodShape[];
4536
+ /** Fields the class must declare, every one matching. */
4537
+ fields?: NativeJavaFieldShape[];
4538
+ /** At most this many names, 50 by default and 1000 at most. */
4539
+ limit?: number;
4540
+ };
4267
4541
  /** One field a Java class declares, from `java.describe`. */
4268
4542
  export type NativeJavaFieldDescription = {
4269
4543
  /** The field name. */
@@ -4273,6 +4547,15 @@ export type NativeJavaFieldDescription = {
4273
4547
  /** Whether the field is static. */
4274
4548
  static: boolean;
4275
4549
  };
4550
+ /** A field a class from `java.findClasses` must declare. */
4551
+ export type NativeJavaFieldShape = {
4552
+ /** The field's name. */
4553
+ name?: string;
4554
+ /** The field type's fully qualified name. */
4555
+ type?: string;
4556
+ /** Whether the field is static. */
4557
+ static?: boolean;
4558
+ };
4276
4559
  /** A handle to a Java object or class; the plugin scope releases it on dispose. */
4277
4560
  export type NativeJavaHandle = NativeHandle;
4278
4561
  /** A Java object reference the bridge returns inside a result. */
@@ -4301,8 +4584,19 @@ export type NativeJavaHookOptions<Receiver extends boolean = boolean> = {
4301
4584
  replace?: boolean;
4302
4585
  /** Whether the listener hears calls: before the method by default, `after` once it has run, `false` never. */
4303
4586
  notify?: boolean | 'after';
4587
+ /** Lets a `before` listener hooked from a UI scope return `{ arguments }` to run the method with them instead. */
4588
+ rewriteArguments?: boolean;
4304
4589
  /** Trims the receiver, arguments and phase each event sends, to cut crossing cost on hot methods. */
4305
4590
  payload?: NativeHookPayload<Receiver>;
4591
+ /** Also hooks every subclass that declares the method itself, found by `java.findClasses` once it answers. */
4592
+ subclasses?: boolean;
4593
+ /** Waits for a class that is not loaded yet, retrying with backoff for up to ten minutes. */
4594
+ waitForClass?: boolean;
4595
+ };
4596
+ /** What a `rewriteArguments` listener returns to run the hooked method with other arguments, one per parameter. */
4597
+ export type NativeJavaHookRewrite = {
4598
+ /** The arguments to run the method with, in parameter order. */
4599
+ arguments: NativeJavaArgument[];
4306
4600
  };
4307
4601
  /** An installed Java hook; the plugin scope removes it on dispose. */
4308
4602
  export type NativeJavaHookToken = {
@@ -4317,7 +4611,7 @@ export type NativeJavaHooksBridge = {
4317
4611
  methodName: string,
4318
4612
  parameterTypes: string[],
4319
4613
  options?: NativeJavaHookOptions<Receiver>,
4320
- listener?: (event: NativeJavaHookEvent<Receiver>) => void,
4614
+ listener?: (event: NativeJavaHookEvent<Receiver>) => NativeJavaHookRewrite | void,
4321
4615
  ): NativeJavaHookToken;
4322
4616
  /** Sets the result a `replace` hook returns for calls with this receiver and these arguments. */
4323
4617
  setReturnValue(
@@ -4334,8 +4628,16 @@ export type NativeJavaHooksBridge = {
4334
4628
  ): void;
4335
4629
  /** Removes every result set on a hook. */
4336
4630
  clearReturnValues(token: NativeJavaHookToken): void;
4337
- /** Removes a hook. */
4631
+ /** Removes a hook or a proxy. */
4338
4632
  remove(token: NativeJavaHookToken): void;
4633
+ /**
4634
+ * Creates a Java object implementing `interfaces`, from a UI scope only. Calls on the main thread run `handler`
4635
+ * inline and return its value; calls on other threads reach it later and return the type's default.
4636
+ */
4637
+ proxy(
4638
+ interfaces: string[],
4639
+ handler: (call: NativeJavaProxyCall) => NativeJavaArgument | void,
4640
+ ): NativeJavaProxy;
4339
4641
  };
4340
4642
  /** One method a Java class declares, from `java.describe`. */
4341
4643
  export type NativeJavaMethodDescription = {
@@ -4348,6 +4650,33 @@ export type NativeJavaMethodDescription = {
4348
4650
  /** Whether the method is static. */
4349
4651
  static: boolean;
4350
4652
  };
4653
+ /** A method a class from `java.findClasses` must have; every given part must match. */
4654
+ export type NativeJavaMethodShape = {
4655
+ /** The method's name. */
4656
+ name?: string;
4657
+ /** The parameter types' fully qualified names, in order. */
4658
+ parameters?: string[];
4659
+ /** The return type's fully qualified name. */
4660
+ returns?: string;
4661
+ /** Whether the method is static. */
4662
+ static?: boolean;
4663
+ /** Whether the class itself declares it rather than inheriting it; `true` by default. */
4664
+ declared?: boolean;
4665
+ };
4666
+ /** A Java object implementing interfaces through a handler; remove its token to stop it answering. */
4667
+ export type NativeJavaProxy = {
4668
+ /** Removes the proxy with `hooks.remove`; the UI scope removes it on dispose. */
4669
+ token: NativeJavaHookToken;
4670
+ /** The object to pass wherever Java expects one of the interfaces. */
4671
+ object: NativeJavaHandle;
4672
+ };
4673
+ /** What a proxy handler receives for one call of an interface method. */
4674
+ export type NativeJavaProxyCall = {
4675
+ /** The interface method's name, such as `onPreDraw`. */
4676
+ method: string;
4677
+ /** The call's arguments. */
4678
+ args: NativeJavaValue[];
4679
+ };
4351
4680
  /** A Java object, as a handle or as a tagged handle value from a result. */
4352
4681
  export type NativeJavaReference = NativeJavaHandle | NativeJavaHandleValue;
4353
4682
  /** A Java value tagged with its type, as the Java bridge returns and accepts it. */
@@ -4420,6 +4749,19 @@ export type NativeMainWorkCounts = {
4420
4749
  /** The most items one drain ran. */
4421
4750
  maxDrainItems: number;
4422
4751
  };
4752
+ /** The device's active connection. */
4753
+ export type NativeNetworkState = {
4754
+ /** The kind of connection, or `offline` with none. */
4755
+ type: 'wifi' | 'cellular' | 'ethernet' | 'other' | 'offline';
4756
+ /** The connection bills by data. */
4757
+ metered?: boolean;
4758
+ /** Low Data Mode (iOS) or Data Saver (Android) is on. */
4759
+ lowData?: boolean;
4760
+ /** Traffic goes through a VPN. */
4761
+ vpn?: boolean;
4762
+ /** A system HTTP proxy is set. */
4763
+ proxy?: boolean;
4764
+ };
4423
4765
  /** The iOS bridge to the Objective-C runtime, scoped to the plugin's capabilities. */
4424
4766
  export type NativeObjCBridge = {
4425
4767
  /** Looks up a class by name, or returns `null` when none is registered. */
@@ -4535,11 +4877,89 @@ export type NativeObjCBridge = {
4535
4877
  handlers: NativeHookHandlers<Receiver>,
4536
4878
  options?: NativeHookOptions<Receiver>,
4537
4879
  ): NativeHookToken;
4880
+ /**
4881
+ * Makes an Objective-C block that calls `implementation` on the JavaScript thread, for completion handlers,
4882
+ * notification observers and enumeration blocks. Needs `native.objc.blocks`.
4883
+ */
4884
+ block(
4885
+ signature: NativeFFISignature,
4886
+ implementation: (...args: never[]) => NativeObjCResult | void,
4887
+ ): NativeObjectHandle;
4888
+ /** Calls a block, such as a completion handler a hook received, with an explicit signature. Needs `native.objc.blocks`. */
4889
+ callBlock(
4890
+ block: NativeObjectHandle,
4891
+ signature: NativeFFISignature,
4892
+ ...args: NativeObjCResult[]
4893
+ ): NativeObjCResult;
4894
+ /** Finds loaded classes by shape, so a lookup survives renamed classes. Needs `native.objc.classSearch`. */
4895
+ findClasses(query: NativeObjCClassQuery): string[];
4896
+ /**
4897
+ * Defines an Objective-C class at runtime whose methods run JavaScript, for delegates, data sources and
4898
+ * target-action receivers; defining it again replaces its methods. Needs `native.objc.classDefinition`.
4899
+ */
4900
+ defineClass(
4901
+ name: string,
4902
+ superclass: string,
4903
+ methods: Record<string, NativeObjCMethodDefinition>,
4904
+ protocols?: string[],
4905
+ ): NativeClassHandle;
4538
4906
  };
4539
4907
  /** A selector and method signature resolved once by `objc.prepare`, for calling many times without the lookup. */
4540
4908
  export type NativeObjCCallPlan = NativeHandle & {
4541
4909
  readonly [nativeObjCCallPlan]: true;
4542
4910
  };
4911
+ /** What `objc.findClasses` matches; every given part must match, and names come back sorted. */
4912
+ export type NativeObjCClassQuery = {
4913
+ /** The class name starts with it. */
4914
+ prefix?: string;
4915
+ /** The path of the image the class is in contains it, such as `Discord.app/Discord`. */
4916
+ image?: string;
4917
+ /** The class inherits from it, directly or further up. */
4918
+ superclass?: string;
4919
+ /** The class conforms to every one. */
4920
+ protocols?: string[];
4921
+ /** The class responds to every instance selector, its own or inherited. */
4922
+ methods?: string[];
4923
+ /** The class itself implements every instance selector. */
4924
+ ownMethods?: string[];
4925
+ /** The class responds to every class selector. */
4926
+ classMethods?: string[];
4927
+ /** At most this many names, 50 by default and 1000 at most. */
4928
+ limit?: number;
4929
+ };
4930
+ /**
4931
+ * The debugger's Objective-C reflection, iOS only and never in a plugin's scope. Needs `native.objc.inspect`.
4932
+ * Throws `INSPECTOR_DISABLED` without developer mode or the debugger, `INSPECTOR_UNAVAILABLE` when the library cannot load.
4933
+ */
4934
+ export type NativeObjCInspector = {
4935
+ /** Describes a class, or an object's class, and its superclasses; throws `INSPECTOR_UNKNOWN_CLASS` for an unknown name. */
4936
+ inspectClass(target: NativeObjectHandle | string): NativeInspectedClass[];
4937
+ /** Lists one page of one kind of a class's own members. */
4938
+ inspectMembers(
4939
+ className: string,
4940
+ kind: NativeInspectKind,
4941
+ page?: NativeInspectPage,
4942
+ ): NativeInspectedMembers;
4943
+ /** Reads up to 50 properties or ivars of an object on the main thread; getters run only with `invokeGetters`. */
4944
+ inspectValues(
4945
+ object: NativeObjectHandle,
4946
+ className: string,
4947
+ kind: 'properties' | 'ivars',
4948
+ names: string[],
4949
+ options?: NativeInspectValuesOptions,
4950
+ ): NativeCancelablePromise<NativeInspectedValue[]>;
4951
+ /** Lists one page of the loaded images that define Objective-C classes. */
4952
+ inspectImages(page?: NativeInspectPage): NativeInspectedImages;
4953
+ /** Lists one page of the class names an image defines, sorted. */
4954
+ inspectClasses(imagePath: string, page?: NativeInspectPage): NativeInspectedClasses;
4955
+ };
4956
+ /** One method of a class `objc.defineClass` makes: its signature without `self` and `_cmd`, and what runs. */
4957
+ export type NativeObjCMethodDefinition = {
4958
+ /** The method's argument and return types, without `self` and `_cmd`. */
4959
+ signature: NativeFFISignature;
4960
+ /** Runs on the JavaScript thread with the receiver and the method's arguments. */
4961
+ implementation: (self: NativeObjectHandle, ...args: never[]) => NativeObjCResult | void;
4962
+ };
4543
4963
  /** What an Objective-C call returns: a plain value, a handle to an object, a 64-bit integer, or nothing for `void`. */
4544
4964
  export type NativeObjCResult = NativeSerializable | NativeHandle | bigint | undefined;
4545
4965
  /** A retained handle to an Objective-C object. */
@@ -4577,6 +4997,10 @@ export type NativePlatformAndroidInstaller = {
4577
4997
  export type NativePlatformApp = {
4578
4998
  /** Where Discord was installed from, such as `App Store` or `Google Play`. */
4579
4999
  getAppSource(): string;
5000
+ /** How an iOS install was signed: certificate kind, team, profile expiry, sideload tool marker and entitlement source. */
5001
+ getInstallDetails?(): string;
5002
+ /** What the loader read about the install, the binary and the device for `/debug`; missing on older loaders. */
5003
+ getEnvironment?(): NativeEnvironmentReport;
4580
5004
  /** The framework that injected the loader, such as Vector or LSPosed on Android and ElleKit on iOS, or an empty string. */
4581
5005
  getFramework(): string;
4582
5006
  /** Whether the loader's embedded attestation verified against Unbound's pinned key. False for development builds. */
@@ -4640,6 +5064,8 @@ export type NativePlatformDevice = {
4640
5064
  isSystemApp(): boolean;
4641
5065
  /** Whether Discord runs in a simulator, emulator or virtual machine. */
4642
5066
  isVirtualDevice(): boolean;
5067
+ /** Memory and thermal pressure right now; empty on loaders older than 1.4.0. */
5068
+ getState?(): NativeDeviceState;
4643
5069
  };
4644
5070
  /** File operations inside the Unbound folder in Discord's documents directory. */
4645
5071
  export type NativePlatformFiles = {
@@ -6516,6 +6942,21 @@ export type UnboundAsset = DiscordAsset & {
6516
6942
  /** The URI of a registered plugin asset at each scale; set while it is registered. */
6517
6943
  unboundSources?: Record<number, string>;
6518
6944
  };
6945
+ /** One design axis of a variable font, such as `wght` from 100 to 900. */
6946
+ export type UnboundFontAxis = {
6947
+ /** The four-letter tag, such as `wght`, `wdth` or `slnt`. */
6948
+ tag: string;
6949
+ /** The name the font gives it, such as `Weight`. */
6950
+ name: string;
6951
+ /** The lowest value the axis accepts. */
6952
+ min: number;
6953
+ /** The value a face draws at when the axis is not set. */
6954
+ default: number;
6955
+ /** The highest value the axis accepts. */
6956
+ max: number;
6957
+ /** The font asks apps not to offer it. */
6958
+ hidden: boolean;
6959
+ };
6519
6960
  /**
6520
6961
  * One installed face in the loader's font store.
6521
6962
  * @internal
@@ -6533,6 +6974,10 @@ type UnboundInstalledFont = {
6533
6974
  /** The file's absolute path; `list()` adds it. */
6534
6975
  path?: string;
6535
6976
  source: UnboundFontSource;
6977
+ /** A variable font's design axes; absent for a static face. */
6978
+ axes?: UnboundFontAxis[];
6979
+ /** The coordinate on each axis this face draws at, such as `{ wght: 650 }`; absent for a static face. */
6980
+ variations?: Record<string, number>;
6536
6981
  };
6537
6982
  /** A value the generator could not type with confidence, so a loose prop accepts anything for it. */
6538
6983
  export type UncertainValue = any;
package/src/flux.d.ts CHANGED
@@ -73,6 +73,10 @@ export declare function useStore<const S extends FluxStore | readonly FluxStore[
73
73
  * `declare module '@unbound-app/api/flux' { interface FluxActions { MY_ACTION: { id: string } } }`.
74
74
  */
75
75
  export interface FluxActions {
76
+ /** Discord moved to the front, the background, or in between, as React Native's `AppState` reports it. */
77
+ APP_STATE_UPDATE: {
78
+ state: 'active' | 'background' | 'inactive';
79
+ };
76
80
  /** A message arrived or was sent; `optimistic` is `true` for the local copy before Discord confirms it. */
77
81
  MESSAGE_CREATE: {
78
82
  channelId: string;
package/src/global.d.ts CHANGED
@@ -262,10 +262,13 @@ export type {
262
262
  NativeDebugEnvironmentOptions,
263
263
  NativeDebugEvaluation,
264
264
  NativeDebugEvaluationLog,
265
+ NativeDeviceState,
265
266
  NativeFFIBridge,
267
+ NativeFFIRebinding,
266
268
  NativeFFISignature,
267
269
  NativeFFIType,
268
270
  NativeFFITypeName,
271
+ NativeFFIValue,
269
272
  NativeFabricBatchMutation,
270
273
  NativeFabricBridge,
271
274
  NativeFabricFrame,
@@ -281,26 +284,46 @@ export type {
281
284
  NativeHookOptions,
282
285
  NativeHookPayload,
283
286
  NativeHookToken,
287
+ NativeInspectKind,
288
+ NativeInspectPage,
289
+ NativeInspectValuesOptions,
290
+ NativeInspectedClass,
291
+ NativeInspectedClasses,
292
+ NativeInspectedImage,
293
+ NativeInspectedImages,
294
+ NativeInspectedMember,
295
+ NativeInspectedMembers,
296
+ NativeInspectedValue,
284
297
  NativeJavaArgument,
285
298
  NativeJavaAssociationKey,
286
299
  NativeJavaBatchOperation,
287
300
  NativeJavaBridge,
288
301
  NativeJavaCallPlan,
289
302
  NativeJavaClassDescription,
303
+ NativeJavaClassQuery,
290
304
  NativeJavaFieldDescription,
305
+ NativeJavaFieldShape,
291
306
  NativeJavaHandle,
292
307
  NativeJavaHandleValue,
293
308
  NativeJavaHookEvent,
294
309
  NativeJavaHookOptions,
310
+ NativeJavaHookRewrite,
295
311
  NativeJavaHookToken,
296
312
  NativeJavaHooksBridge,
297
313
  NativeJavaMethodDescription,
314
+ NativeJavaMethodShape,
315
+ NativeJavaProxy,
316
+ NativeJavaProxyCall,
298
317
  NativeJavaReference,
299
318
  NativeJavaValue,
300
319
  NativeMainWaitCounts,
301
320
  NativeMainWorkCounts,
321
+ NativeNetworkState,
302
322
  NativeObjCBridge,
303
323
  NativeObjCCallPlan,
324
+ NativeObjCClassQuery,
325
+ NativeObjCInspector,
326
+ NativeObjCMethodDefinition,
304
327
  NativeObjCResult,
305
328
  NativeObjectHandle,
306
329
  NativePlatformAddons,
@@ -513,6 +536,7 @@ export type {
513
536
  TransitionState,
514
537
  UnboundAppIcon,
515
538
  UnboundAsset,
539
+ UnboundFontAxis,
516
540
  UncertainValue,
517
541
  VisualEffectViewProps,
518
542
  VoidIconProps,
@@ -28,10 +28,10 @@ export type CatalogQuery = {
28
28
  sort?: CatalogSort;
29
29
  };
30
30
  /**
31
- * `name` sorts alphabetically, `popular` puts the most installed addons first, `updated` the most
32
- * recently published versions.
31
+ * `new` puts the most recently listed addons first, `name` sorts alphabetically, `popular` the most
32
+ * installed addons first, `updated` the most recently published versions.
33
33
  */
34
- export type CatalogSort = 'name' | 'popular' | 'updated';
34
+ export type CatalogSort = 'new' | 'name' | 'popular' | 'updated';
35
35
  /** One addon the marketplace lists, at its newest version. */
36
36
  export type MarketplaceListing = {
37
37
  /** The addon's id from its manifest, which installs and updates key on. */
@@ -4,7 +4,14 @@ export type { DesignComponentName, DesignModule } from '../_internal';
4
4
  export { DESIGN_COMPONENTS } from '../_internal';
5
5
  import type { DesignComponentName, DesignModule } from '../_internal';
6
6
  import * as React$1 from 'react';
7
- import { ComponentProps, ComponentType, ElementType, FunctionComponent, ReactNode } from 'react';
7
+ import {
8
+ ComponentProps,
9
+ ComponentType,
10
+ Context,
11
+ ElementType,
12
+ FunctionComponent,
13
+ ReactNode,
14
+ } from 'react';
8
15
  import * as RN from 'react-native';
9
16
  import { ColorValue, ImageStyle, TextStyle, ViewStyle } from 'react-native';
10
17
 
@@ -149,7 +149,14 @@ import type {
149
149
  } from '../_internal';
150
150
  import type { LiteralUnion, PredicateResult } from '../_types';
151
151
  import * as React$1 from 'react';
152
- import { ComponentProps, ComponentType, ElementType, FunctionComponent, ReactNode } from 'react';
152
+ import {
153
+ ComponentProps,
154
+ ComponentType,
155
+ Context,
156
+ ElementType,
157
+ FunctionComponent,
158
+ ReactNode,
159
+ } from 'react';
153
160
  import * as RN from 'react-native';
154
161
  import {
155
162
  ColorValue,
package/src/native.d.ts CHANGED
@@ -43,9 +43,12 @@ export type {
43
43
  NativeJavaHandleValue,
44
44
  NativeJavaHookEvent,
45
45
  NativeJavaHookOptions,
46
+ NativeJavaHookRewrite,
46
47
  NativeJavaHookToken,
47
48
  NativeJavaHooksBridge,
48
49
  NativeJavaMethodDescription,
50
+ NativeJavaProxy,
51
+ NativeJavaProxyCall,
49
52
  NativeJavaReference,
50
53
  NativeJavaValue,
51
54
  NativeObjCBridge,
@@ -72,6 +75,11 @@ import type {
72
75
  NativeBridgeCapability,
73
76
  NativeBridgeMethod,
74
77
  NativeBridgePlatform,
78
+ NativeJavaBridge,
79
+ NativeJavaHandleValue,
80
+ NativeJavaReference,
81
+ NativeObjCBridge,
82
+ NativeObjectHandle,
75
83
  NativePlatformBridge,
76
84
  NativeResourceCounts,
77
85
  } from './_internal';
@@ -164,6 +172,28 @@ export declare function reload(): Promise<void>;
164
172
  * @returns Whether the loader implements it.
165
173
  */
166
174
  export declare function supports(method: NativeBridgeMethod): boolean;
175
+ /**
176
+ * @description Holds a Java object weakly, through a `java.lang.ref.WeakReference`, so caching a view does not keep
177
+ * it alive after Discord drops it.
178
+ * @param java The plugin's Java bridge.
179
+ * @param handle The object to hold.
180
+ * @returns A weak handle whose `deref` gives the object while it lives, then `null`.
181
+ */
182
+ export declare function weakJava(
183
+ java: Pick<NativeJavaBridge, 'construct' | 'invoke'>,
184
+ handle: NativeJavaReference,
185
+ ): NativeWeakHandle<NativeJavaHandleValue>;
186
+ /**
187
+ * @description Holds an Objective-C object weakly, through a `strongToWeakObjectsMapTable`, so caching a view or
188
+ * controller does not keep it alive after Discord releases it.
189
+ * @param objc The plugin's Objective-C bridge.
190
+ * @param handle The object to hold.
191
+ * @returns A weak handle whose `deref` gives the object while it lives, then `null`.
192
+ */
193
+ export declare function weakObjC(
194
+ objc: Pick<NativeObjCBridge, 'getClass' | 'call'>,
195
+ handle: NativeObjectHandle,
196
+ ): NativeWeakHandle<NativeObjectHandle>;
167
197
  /** Build and release metadata reported by the native client info module. */
168
198
  export type BundleInfoType = {
169
199
  /** Discord's app version, shown in Unbound's settings beside `Build`. */
@@ -265,6 +295,11 @@ export type DeviceInfoType = {
265
295
  };
266
296
  /** What `HermesInternal.getRuntimeProperties()` reports; empty when the engine is not Hermes. */
267
297
  export type HermesRuntimeProperties = ReturnType<HermesInternalObject['getRuntimeProperties']>;
298
+ /** A reference that does not keep its native object alive; `deref` reads `null` once the object is gone. */
299
+ export type NativeWeakHandle<T> = {
300
+ /** The object while it lives, else `null`. */
301
+ deref(): T | null;
302
+ };
268
303
  /** One Platform handler per platform an addon runs on. */
269
304
  export type PlatformHandlers<Handler> = Partial<Record<NativeBridgePlatform, Handler>>;
270
305
  /** A native method Discord exposes without documenting what its promise resolves to. */
package/src/ui.d.ts CHANGED
@@ -10,9 +10,17 @@ import type {
10
10
  } from './_internal';
11
11
  import type { Without } from './_types';
12
12
  import * as React$1 from 'react';
13
- import { ComponentProps, ComponentType, FunctionComponent, ReactElement, ReactNode } from 'react';
13
+ import {
14
+ ComponentProps,
15
+ ComponentType,
16
+ Context,
17
+ FunctionComponent,
18
+ ReactElement,
19
+ ReactNode,
20
+ Ref,
21
+ } from 'react';
14
22
  import * as RN from 'react-native';
15
- import { ImageStyle, TextStyle, ViewStyle } from 'react-native';
23
+ import { ImageStyle, ScrollView, ScrollViewProps, TextStyle, ViewStyle } from 'react-native';
16
24
  /**
17
25
  * @description A pressable `TableRow` in its settings group, as Discord draws actions such as Log Out; it asks first when
18
26
  * `confirmation` is set. `danger` draws the label red behind a red trash icon.
@@ -150,7 +158,7 @@ export declare function NumberRow({
150
158
  * inset and a 24px gap between form groups, matching how Discord lays out its own settings screens. Use
151
159
  * it as the root of every settings page.
152
160
  */
153
- export declare function Page({ children }: PageProps): React$1.ReactElement;
161
+ export declare function Page({ children, ...scroll }: PageProps): React$1.ReactElement;
154
162
  /**
155
163
  * @description A row showing the picked option that opens Discord's action sheet listing every option, for choices too many for a radio group.
156
164
  * @param props The row's label, picked value, options and change handler.
@@ -236,7 +244,7 @@ export declare function SegmentedRow({
236
244
  }: SegmentedRowProps): React$1.ReactElement;
237
245
  /**
238
246
  * @description Discord's `TableSliderRow`, as its own settings draw one: the label and live value over the slider
239
- * with its default mark. A sub-label, which that row cannot show, moves it to a `TableRow` holding the same slider.
247
+ * with its default mark. A sub-label, which that row cannot show, draws the same layout by hand with it under the label.
240
248
  * The value is saved when the drag ends; Android's slider and iOS's take differently named props, so it passes both.
241
249
  * @param props The row's label, value, default, range and change handler.
242
250
  * @returns The row, or `null` when Discord's slider is unavailable.
@@ -471,9 +479,14 @@ export type NumberRowProps = NumberConstraints & {
471
479
  onChange: (value: number) => void;
472
480
  };
473
481
  /** The props of {@link Page}. */
474
- export type PageProps = {
482
+ export type PageProps = Pick<
483
+ ScrollViewProps,
484
+ 'onScrollEndDrag' | 'onMomentumScrollEnd' | 'onContentSizeChange'
485
+ > & {
475
486
  /** The page's form groups, laid out top to bottom with a 24px gap. */
476
487
  children: React$1.ReactNode;
488
+ /** The page's scroll view, for a page that restores its scroll offset. */
489
+ ref?: React$1.Ref<ScrollView>;
477
490
  };
478
491
  /** Props for `PickerRow`. */
479
492
  export type PickerRowProps = {
@@ -16,9 +16,12 @@ const BAGS = [
16
16
  'Discord',
17
17
  'FlashList',
18
18
  'Forms',
19
+ 'GorhomSheet',
19
20
  'Media',
20
21
  'Portal',
21
22
  'SafeArea',
23
+ 'SafeAreaContext',
24
+ 'TableRowDividers',
22
25
  'Video',
23
26
  ] as const;
24
27
 
@@ -72,10 +72,18 @@ export type FakeJava = {
72
72
  registerClass: (name: string, spec?: JavaClassSpec) => void;
73
73
  /** Makes an object of a registered class, with its fields, as the plugin sees it: a tagged `handle` value. */
74
74
  create: (className: string, fields?: Record<string, JavaPlain>) => NativeJavaValue;
75
- /** Forgets every class, object and hook. */
75
+ /** Calls `method` on a proxy made with `hooks.proxy`, as Java would, and returns what its handler returned. */
76
+ callProxy: (
77
+ proxy: NativeJavaArgument,
78
+ method: string,
79
+ args?: JavaPlain[],
80
+ ) => NativeJavaArgument | undefined;
81
+ /** Forgets every class, object, hook and proxy. */
76
82
  reset: () => void;
77
83
  };
78
84
 
85
+ type ProxyHandler = Parameters<NativeJavaHooksBridge['proxy']>[1];
86
+
79
87
  type HookEntry = {
80
88
  className: string;
81
89
  methodName: string;
@@ -139,6 +147,8 @@ export function createFakeJava(log: NativeCallLog = createCallLog()): FakeJava {
139
147
  const classes = new Map<string, JavaClassSpec>();
140
148
  const objects = new Map<string, JavaObject>();
141
149
  const hookEntries = new Map<string, HookEntry>();
150
+ const proxyHandlers = new Map<string, ProxyHandler>();
151
+ const proxyTokens = new Map<string, string>();
142
152
  let next = 0;
143
153
 
144
154
  function chain(className: string): string[] {
@@ -496,18 +506,54 @@ export function createFakeJava(log: NativeCallLog = createCallLog()): FakeJava {
496
506
  removeReturnValue: (token, self) =>
497
507
  void hookEntries.get(token.id)?.results.delete(tagKey(self)),
498
508
  clearReturnValues: (token) => hookEntries.get(token.id)?.results.clear(),
499
- remove: (token) => void hookEntries.delete(token.id),
509
+ remove(token) {
510
+ hookEntries.delete(token.id);
511
+
512
+ const object = proxyTokens.get(token.id);
513
+
514
+ if (!object) {
515
+ return;
516
+ }
517
+
518
+ proxyHandlers.delete(object);
519
+ proxyTokens.delete(token.id);
520
+ },
521
+ proxy(interfaces, handler) {
522
+ const object = spawn(interfaces[0] ?? 'java.lang.Object', false);
523
+ const id = `proxy-${++next}`;
524
+
525
+ log.record({
526
+ bridge: 'hooks',
527
+ method: 'proxy',
528
+ target: interfaces.join(','),
529
+ name: 'proxy',
530
+ args: [],
531
+ });
532
+ proxyHandlers.set(object.id, handler);
533
+ proxyTokens.set(id, object.id);
534
+
535
+ return { token: { id }, object: brand<NativeJavaHandle>(object) };
536
+ },
500
537
  });
501
538
 
539
+ function callProxy(proxy: NativeJavaArgument, method: string, args: JavaPlain[] = []) {
540
+ const object = resolve(proxy as NativeJavaReference, 'callProxy');
541
+
542
+ return proxyHandlers.get(object.id)?.({ method, args: args.map(tagged) }) ?? undefined;
543
+ }
544
+
502
545
  return {
503
546
  bridge,
504
547
  hooks,
505
548
  registerClass: (name, spec = {}) => void classes.set(name, spec),
506
549
  create: (className, fields) => tagged(brand<NativeJavaHandle>(instance(className, fields))),
550
+ callProxy,
507
551
  reset() {
508
552
  classes.clear();
509
553
  objects.clear();
510
554
  hookEntries.clear();
555
+ proxyHandlers.clear();
556
+ proxyTokens.clear();
511
557
  },
512
558
  };
513
559
  }