@symbiote-native/engine 1.4.0 → 1.5.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.
@@ -0,0 +1,4 @@
1
+ import type { IEventSubscription } from './native-events';
2
+ export type IEventValueSource<TValue, TEvent extends string = string> = {
3
+ addListener: (event: TEvent, listener: (value: TValue) => void) => IEventSubscription;
4
+ };
@@ -0,0 +1,3 @@
1
+ // A source that emits a named event carrying its own next value (`useAudioPlayerStatus`, ...)
2
+ // `TEvent` generic so a `SharedObject`'s own literal-keyed `addListener` structurally matches
3
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare function expoViewManagerName(moduleName: string): string;
2
+ export declare function tryRegisterNativeView(register: () => unknown): boolean;
@@ -0,0 +1,19 @@
1
+ // The Fabric name `requireNativeViewManager` registers: the adapter prefix plus the module name,
2
+ // and the app identifier when the host sets one
3
+ export function expoViewManagerName(moduleName) {
4
+ const appIdentifier = Reflect.get(Reflect.get(globalThis, 'expo') ?? {}, '__expo_app_identifier__');
5
+ const suffix = typeof appIdentifier === 'string' && appIdentifier !== ''
6
+ ? `_${appIdentifier}`
7
+ : '';
8
+ return `ViewManagerAdapter_${moduleName}${suffix}`;
9
+ }
10
+ // A missing view config must not crash the render, the view just renders nothing
11
+ export function tryRegisterNativeView(register) {
12
+ try {
13
+ register();
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
package/build/index.d.ts CHANGED
@@ -52,6 +52,7 @@ export { reportUncaughtError } from './report-error';
52
52
  export type { IUncaughtErrorInfo } from './report-error';
53
53
  export { getNativeModule, getEnforcingNativeModule } from './native-modules';
54
54
  export { installDeviceEventHub, NativeEventEmitter, setDeviceEventSource, } from './native-events';
55
+ export type { IEventValueSource } from './event-value-source';
55
56
  export type { IEventSubscription, IEventEmitterModule, INativeEventListener, IDeviceEventSource, } from './native-events';
56
57
  export { AnimatedNode, AnimatedWithChildren, AnimatedValue, AnimatedValueXY, AnimatedColor, AnimatedInterpolation, AnimatedAddition, AnimatedSubtraction, AnimatedMultiplication, AnimatedDivision, AnimatedModulo, AnimatedDiffClamp, AnimatedEvent, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent, attachNativeEvent, attachNativeEventHandler, flushValue, Easing, createNumericInterpolation, checkValidRanges, TimingAnimation, SpringAnimation, DecayAnimation, AnimatedTracking, timing, spring, decay, parallel, sequence, stagger, loop, delay, nativeAnimated, isNativeAnimatedAvailable, AnimatedProps, createAnimatedLeafLifecycle, AnimatedStyle, AnimatedTransform, AnimatedMock, reduceProps, isAnimatedNode, readPassthroughStyle, resolveHostNode, } from './animated';
57
58
  export type { IAnimatedLeafLifecycle, IValueXY, IRgbaValue, IColorInput, IEventConfig, IEventListener, IAnimatedEventHandler, INativeEventAttachment, IValueListener, IEasing, IEasingFunction, IInterpolationConfig, IExtrapolateType, IAnimation, IEndCallback, IEndResult, ICompositeAnimation, ITimingConfig, ISpringConfig, IDecayConfig, IParallelConfig, ILoopAnimationConfig, INativeNodeConfig, INativeAnimationConfig, INativeEventMapping, IPlatformConfig, } from './animated';
@@ -106,3 +107,9 @@ export type { IClaimMode, IHostBehavior, IPayloadFold } from './host-behavior';
106
107
  export { setBehaviorListener, markPropsDirty, setNodeDispatch } from './node';
107
108
  export { hasListenerFor, listenerFor } from './node';
108
109
  export type { IEventDispatch } from './node';
110
+ export { createPermissionApi, resolveInitialPermission, fetchInitialPermission, splitWriteOnlyPermissionOptions, } from './permission-hook-runtime';
111
+ export type { IWriteOnlyPermissionOptions } from './permission-hook-runtime';
112
+ export { expoViewManagerName, tryRegisterNativeView } from './expo-native-view';
113
+ export { createResourceController } from './resource-controller';
114
+ export type { IResourceController } from './resource-controller';
115
+ export type { IPermissionHookBehavior, IPermissionHookMethods, IPermissionHookOptions, IPermissionApi, IPermissionHookResult, } from './permission-hook-runtime';
package/build/index.js CHANGED
@@ -139,3 +139,6 @@ export { setBehaviorListener, markPropsDirty, setNodeDispatch } from './node.js'
139
139
  // The only correct way to ask what would receive an event: a behavior may answer a name through
140
140
  // one shared `IEventDispatch` rather than a closure in `node.listeners`
141
141
  export { hasListenerFor, listenerFor } from './node.js';
142
+ export { createPermissionApi, resolveInitialPermission, fetchInitialPermission, splitWriteOnlyPermissionOptions, } from './permission-hook-runtime.js';
143
+ export { expoViewManagerName, tryRegisterNativeView } from './expo-native-view.js';
144
+ export { createResourceController } from './resource-controller.js';
@@ -0,0 +1,27 @@
1
+ export type IPermissionHookBehavior = {
2
+ /** Auto-fetch the current status on mount without asking the user. @default true */
3
+ get?: boolean;
4
+ /** Auto-request the user to grant permission on mount. @default false */
5
+ request?: boolean;
6
+ };
7
+ export type IPermissionHookMethods<TPermission, TOptions extends object> = {
8
+ getMethod: (options?: TOptions) => Promise<TPermission>;
9
+ requestMethod: (options?: TOptions) => Promise<TPermission>;
10
+ };
11
+ export type IPermissionHookOptions<TOptions extends object> = IPermissionHookBehavior & TOptions;
12
+ export type IPermissionApi<TPermission, TOptions extends object> = {
13
+ get: (methodOptions?: TOptions) => Promise<TPermission>;
14
+ request: (methodOptions?: TOptions) => Promise<TPermission>;
15
+ };
16
+ export declare function createPermissionApi<TPermission, TOptions extends object>(methods: IPermissionHookMethods<TPermission, TOptions>, setStatus: (value: TPermission) => void): IPermissionApi<TPermission, TOptions>;
17
+ export declare function resolveInitialPermission<TPermission, TOptions extends object>(methods: IPermissionHookMethods<TPermission, TOptions>, behavior: IPermissionHookBehavior, methodOptions: TOptions | undefined): Promise<TPermission | null>;
18
+ export type IPermissionHookResult<TPermission> = [
19
+ TPermission | null,
20
+ () => Promise<TPermission>,
21
+ () => Promise<TPermission>
22
+ ];
23
+ export type IWriteOnlyPermissionOptions = {
24
+ writeOnly?: boolean;
25
+ };
26
+ export declare function splitWriteOnlyPermissionOptions(options?: IPermissionHookOptions<IWriteOnlyPermissionOptions>): [IPermissionHookBehavior, IWriteOnlyPermissionOptions | undefined];
27
+ export declare function fetchInitialPermission<TPermission, TOptions extends object>(methods: IPermissionHookMethods<TPermission, TOptions>, behavior: IPermissionHookBehavior | undefined, methodOptions: TOptions | undefined, commit: (value: TPermission) => void): void;
@@ -0,0 +1,37 @@
1
+ // Framework-agnostic twin of expo-modules-core's `usePermission` method-dispatch. Every Expo
2
+ // wrapper package's hook/composable/primitive/service imports this, no per-package copy left
3
+ // The one piece every adapter's hook/composable/primitive would otherwise re-wrap by hand:
4
+ // call the method, commit its response to whichever state primitive `setStatus` closes over
5
+ export function createPermissionApi(methods, setStatus) {
6
+ async function run(method, methodOptions) {
7
+ const response = await method(methodOptions);
8
+ setStatus(response);
9
+ return response;
10
+ }
11
+ return {
12
+ get: methodOptions => run(methods.getMethod, methodOptions),
13
+ request: methodOptions => run(methods.requestMethod, methodOptions),
14
+ };
15
+ }
16
+ export async function resolveInitialPermission(methods, behavior, methodOptions) {
17
+ const { get = true, request = false } = behavior;
18
+ if (!request && !get)
19
+ return null;
20
+ const method = request ? methods.requestMethod : methods.getMethod;
21
+ return methodOptions !== undefined && Object.keys(methodOptions).length > 0
22
+ ? method(methodOptions)
23
+ : method();
24
+ }
25
+ export function splitWriteOnlyPermissionOptions(options) {
26
+ const { get, request, writeOnly } = options ?? {};
27
+ return [
28
+ { get, request },
29
+ writeOnly === undefined ? undefined : { writeOnly },
30
+ ];
31
+ }
32
+ export function fetchInitialPermission(methods, behavior, methodOptions, commit) {
33
+ resolveInitialPermission(methods, { get: behavior?.get ?? true, request: behavior?.request ?? false }, methodOptions).then(response => {
34
+ if (response)
35
+ commit(response);
36
+ });
37
+ }
@@ -0,0 +1,6 @@
1
+ export type IResourceController<TKey, TResource> = {
2
+ resolve: (key: TKey) => TResource;
3
+ flushDispose: () => void;
4
+ dispose: () => void;
5
+ };
6
+ export declare function createResourceController<TKey, TResource>(create: (key: TKey) => TResource, dispose: (resource: TResource) => void, isSameKey?: (a: TKey, b: TKey) => boolean): IResourceController<TKey, TResource>;
@@ -0,0 +1,30 @@
1
+ // Generic twin of expo-modules-core's `useReleasingSharedObject`. `resolve()` never disposes
2
+ // inline - the stale resource waits in `flushDispose()`, called from each adapter's own
3
+ // commit/effect phase, matching upstream's own "release outside of render" invariant
4
+ export function createResourceController(create, dispose, isSameKey = Object.is) {
5
+ let current = null;
6
+ let pendingDispose = null;
7
+ function resolve(key) {
8
+ if (current && isSameKey(current.key, key))
9
+ return current.resource;
10
+ if (current)
11
+ pendingDispose = current.resource;
12
+ const resource = create(key);
13
+ current = { key, resource };
14
+ return resource;
15
+ }
16
+ function flushDispose() {
17
+ if (pendingDispose) {
18
+ dispose(pendingDispose);
19
+ pendingDispose = null;
20
+ }
21
+ }
22
+ function disposeAll() {
23
+ flushDispose();
24
+ if (current) {
25
+ dispose(current.resource);
26
+ current = null;
27
+ }
28
+ }
29
+ return { resolve, flushDispose, dispose: disposeAll };
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/engine",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "SymbioteNative's retained shadow-tree engine — clone-on-write commit path + event normalization over React Native Fabric, shared by every framework adapter.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -71,7 +71,7 @@
71
71
  },
72
72
  "peerDependenciesMeta": {},
73
73
  "devDependencies": {
74
- "@symbiote-native/test-utils": "0.4.5"
74
+ "@symbiote-native/test-utils": "0.4.6"
75
75
  },
76
76
  "scripts": {
77
77
  "typecheck": "tsc --build",