@webority/mobile-core 0.0.26 → 0.0.28

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.
Files changed (52) hide show
  1. package/README.md +69 -1
  2. package/lib/commonjs/api/retry.js +17 -1
  3. package/lib/commonjs/api/userClient.js +1 -1
  4. package/lib/commonjs/auth/authStore.js +49 -16
  5. package/lib/commonjs/config/index.js +8 -2
  6. package/lib/commonjs/formatters/currency.js +2 -1
  7. package/lib/commonjs/formatters/date.js +5 -6
  8. package/lib/commonjs/formatters/dateMath.js +2 -1
  9. package/lib/commonjs/formatters/intlCache.js +37 -0
  10. package/lib/commonjs/formatters/number.js +4 -3
  11. package/lib/commonjs/index.js +7 -0
  12. package/lib/commonjs/initSDK.js +1 -1
  13. package/lib/commonjs/network/networkStatus.js +40 -2
  14. package/lib/commonjs/push/index.js +18 -0
  15. package/lib/commonjs/push/push.js +177 -15
  16. package/lib/commonjs/push/task.js +43 -0
  17. package/lib/commonjs/utils/base64.js +24 -0
  18. package/lib/module/api/retry.js +17 -1
  19. package/lib/module/api/userClient.js +2 -2
  20. package/lib/module/auth/authStore.js +49 -16
  21. package/lib/module/config/index.js +7 -1
  22. package/lib/module/formatters/currency.js +2 -1
  23. package/lib/module/formatters/date.js +5 -6
  24. package/lib/module/formatters/dateMath.js +2 -1
  25. package/lib/module/formatters/intlCache.js +30 -0
  26. package/lib/module/formatters/number.js +4 -3
  27. package/lib/module/index.js +2 -0
  28. package/lib/module/initSDK.js +2 -2
  29. package/lib/module/network/networkStatus.js +37 -0
  30. package/lib/module/push/index.js +1 -1
  31. package/lib/module/push/push.js +170 -15
  32. package/lib/module/push/task.js +38 -0
  33. package/lib/module/utils/base64.js +19 -0
  34. package/lib/typescript/commonjs/config/index.d.ts +6 -0
  35. package/lib/typescript/commonjs/formatters/intlCache.d.ts +6 -0
  36. package/lib/typescript/commonjs/index.d.ts +1 -0
  37. package/lib/typescript/commonjs/initSDK.d.ts +1 -1
  38. package/lib/typescript/commonjs/network/networkStatus.d.ts +6 -0
  39. package/lib/typescript/commonjs/push/index.d.ts +2 -2
  40. package/lib/typescript/commonjs/push/push.d.ts +88 -0
  41. package/lib/typescript/commonjs/push/task.d.ts +15 -0
  42. package/lib/typescript/commonjs/utils/base64.d.ts +7 -0
  43. package/lib/typescript/module/config/index.d.ts +6 -0
  44. package/lib/typescript/module/formatters/intlCache.d.ts +6 -0
  45. package/lib/typescript/module/index.d.ts +1 -0
  46. package/lib/typescript/module/initSDK.d.ts +1 -1
  47. package/lib/typescript/module/network/networkStatus.d.ts +6 -0
  48. package/lib/typescript/module/push/index.d.ts +2 -2
  49. package/lib/typescript/module/push/push.d.ts +88 -0
  50. package/lib/typescript/module/push/task.d.ts +15 -0
  51. package/lib/typescript/module/utils/base64.d.ts +7 -0
  52. package/package.json +15 -1
@@ -18,6 +18,13 @@ const DEFAULT_STATUS = {
18
18
  * One backend also means one behaviour, rather than each app choosing.
19
19
  */
20
20
  let override = null;
21
+ let lastKnown = null;
22
+ let stopFollowing = null;
23
+ const stopFollowingNetwork = () => {
24
+ stopFollowing?.();
25
+ stopFollowing = null;
26
+ lastKnown = null;
27
+ };
21
28
 
22
29
  /**
23
30
  * Replace the backend. Intended for TESTS and for the showcase, which uses it
@@ -26,6 +33,7 @@ let override = null;
26
33
  */
27
34
  export const setNetworkStatusImplementation = impl => {
28
35
  override = impl;
36
+ stopFollowingNetwork();
29
37
  };
30
38
  const resolveNetInfo = () => override ?? expoNetworkBackend;
31
39
 
@@ -96,6 +104,35 @@ export const addNetworkStatusListener = listener => {
96
104
  return ni.addEventListener(state => listener(normalize(state)));
97
105
  };
98
106
 
107
+ /**
108
+ * The connectivity the HTTP client checks before every request. One listener,
109
+ * attached on first use, keeps it current, so a request costs no native probe:
110
+ * one probe seeds the value and the listener's events replace it.
111
+ */
112
+ export const getLastKnownNetworkStatus = async () => {
113
+ if (!stopFollowing) {
114
+ try {
115
+ stopFollowing = addNetworkStatusListener(status => {
116
+ lastKnown = status;
117
+ });
118
+ } catch (error) {
119
+ Logger.error('[network] could not follow network changes', error);
120
+ return getNetworkStatus();
121
+ }
122
+ }
123
+ if (lastKnown) {
124
+ return lastKnown;
125
+ }
126
+ const following = stopFollowing;
127
+ const probed = await getNetworkStatus();
128
+ if (stopFollowing !== following) {
129
+ return probed;
130
+ }
131
+ // An event that arrived during the probe is newer than the probe.
132
+ lastKnown ??= probed;
133
+ return lastKnown;
134
+ };
135
+
99
136
  /**
100
137
  * React hook that returns the current network status and re-renders on change.
101
138
  * Reports an optimistic online default when NetInfo is not installed.
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
 
3
- export { disablePush, enablePush, initPush, setBadgeCount, setPushImplementation, usePush } from "./push.js";
3
+ export { disablePush, enablePush, initPush, notifyLocal, onAction, setBadgeCount, setCategories, setPushImplementation, usePush } from "./push.js";
4
4
  //# sourceMappingURL=index.js.map
@@ -4,6 +4,12 @@ import { useSyncExternalStore } from 'react';
4
4
  import { Platform } from 'react-native';
5
5
  import { Logger } from "../logger/index.js";
6
6
 
7
+ /** A button on a notification category. */
8
+
9
+ /** A set of buttons a push names through its category id. */
10
+
11
+ /** A button tap on a notification. */
12
+
7
13
  /** The slice of expo-notifications this module calls, so the optional peer stays untyped at build. */
8
14
 
9
15
  let override = null;
@@ -23,9 +29,9 @@ export const setPushImplementation = impl => {
23
29
  /**
24
30
  * The only place expo-notifications and expo-application are named. Expo Go is
25
31
  * checked first because expo-notifications refuses remote push there and its
26
- * import throws on Android.
32
+ * import throws on Android. Exported for `./task`; not on the `/push` entry.
27
33
  */
28
- const resolveModules = () => {
34
+ export const resolveModules = () => {
29
35
  if (override) {
30
36
  return override;
31
37
  }
@@ -67,7 +73,18 @@ const setState = next => {
67
73
  let config = null;
68
74
  let channelsReady = Promise.resolve();
69
75
  let tokenSubscription = null;
70
- let lastOpenedId = null;
76
+ let actionHandler = null;
77
+ let listening = false;
78
+ /** Responses that arrived before anything could handle them, e.g. the launching tap before initPush. */
79
+ const pendingResponses = [];
80
+ /** Response key to when it was routed. */
81
+ const routedAt = new Map();
82
+
83
+ /**
84
+ * The cold-start read, the live listener and, on Android, the background task
85
+ * can each report the same response a moment apart.
86
+ */
87
+ const REPEAT_WINDOW_MS = 10_000;
71
88
  const requireConfig = () => {
72
89
  if (!config) {
73
90
  throw new Error('[@webority/mobile-core] call initPush before enablePush or disablePush.');
@@ -92,22 +109,109 @@ const toStringData = data => {
92
109
  }
93
110
  return out;
94
111
  };
95
- const handleResponse = response => {
96
- if (!response || !config) {
112
+ const responseData = response => {
113
+ const content = response.notification.request.content;
114
+ if (content.data) {
115
+ return toStringData(content.data);
116
+ }
117
+ if (typeof content.dataString === 'string') {
118
+ try {
119
+ return toStringData(JSON.parse(content.dataString));
120
+ } catch (error) {
121
+ Logger.error('[push] notification data is not JSON', error);
122
+ }
123
+ }
124
+ return {};
125
+ };
126
+ export const toPushAction = response => ({
127
+ actionId: response.actionIdentifier,
128
+ ...(response.userText ? {
129
+ userText: response.userText
130
+ } : {}),
131
+ data: responseData(response),
132
+ notificationId: response.notification.request.identifier
133
+ });
134
+ export const toNotificationActions = actions => actions.map(action => ({
135
+ identifier: action.id,
136
+ buttonTitle: action.title,
137
+ ...(action.textInput ? {
138
+ textInput: {
139
+ submitButtonTitle: action.textInput.submitTitle,
140
+ placeholder: action.textInput.placeholder
141
+ }
142
+ } : {}),
143
+ options: {
144
+ opensAppToForeground: action.opensApp ?? true,
145
+ isDestructive: action.destructive ?? false,
146
+ isAuthenticationRequired: action.authenticationRequired ?? false
147
+ }
148
+ }));
149
+
150
+ /**
151
+ * Sends a button tap to the action handler and a plain tap to `onOpen`. A
152
+ * button tap with no action handler opens like a plain tap. Resolves once the
153
+ * handler has finished, so a background task stays alive for its request.
154
+ */
155
+ const routeResponse = async (m, response) => {
156
+ const key = `${response.notification.request.identifier}:${response.actionIdentifier}`;
157
+ const last = routedAt.get(key);
158
+ if (last !== undefined && Date.now() - last < REPEAT_WINDOW_MS) {
97
159
  return;
98
160
  }
99
- // The cold-start read and the listener can both report the launching tap.
100
- const id = `${response.notification.request.identifier}:${response.actionIdentifier}`;
101
- if (id === lastOpenedId) {
161
+ const isAction = response.actionIdentifier !== m.DEFAULT_ACTION_IDENTIFIER;
162
+ if (isAction && actionHandler) {
163
+ routedAt.set(key, Date.now());
164
+ try {
165
+ await actionHandler(toPushAction(response));
166
+ } catch (error) {
167
+ Logger.error('[push] onAction threw', error);
168
+ }
169
+ return;
170
+ }
171
+ if (!config) {
172
+ pendingResponses.push(response);
102
173
  return;
103
174
  }
104
- lastOpenedId = id;
175
+ routedAt.set(key, Date.now());
105
176
  try {
106
- config.onOpen(toStringData(response.notification.request.content.data));
177
+ config.onOpen(responseData(response));
107
178
  } catch (error) {
108
179
  Logger.error('[push] onOpen threw', error);
109
180
  }
110
181
  };
182
+ const drainPending = async m => {
183
+ const queued = pendingResponses.splice(0);
184
+ for (const response of queued) {
185
+ await routeResponse(m, response);
186
+ }
187
+ };
188
+
189
+ /** Follows taps and button taps, including the one that launched the app. Wired once per process. */
190
+ const listenForResponses = m => {
191
+ if (listening) {
192
+ return;
193
+ }
194
+ listening = true;
195
+ m.addNotificationResponseReceivedListener(response => {
196
+ void routeResponse(m, response);
197
+ });
198
+ void m.getLastNotificationResponseAsync().then(async response => {
199
+ if (response) {
200
+ await routeResponse(m, response);
201
+ }
202
+ return m.clearLastNotificationResponseAsync?.();
203
+ }).catch(error => Logger.error('[push] reading the launch notification failed', error));
204
+ };
205
+
206
+ /** The payload of the background notification task: routes button taps, ignores received pushes. */
207
+ export const routeTaskPayload = async payload => {
208
+ const mods = resolveModules();
209
+ const response = payload;
210
+ if (!mods || typeof response?.actionIdentifier !== 'string' || typeof response.notification?.request?.identifier !== 'string') {
211
+ return;
212
+ }
213
+ await routeResponse(mods.notifications, response);
214
+ };
111
215
  const createChannels = async (m, channels) => {
112
216
  for (const channel of channels) {
113
217
  try {
@@ -170,11 +274,8 @@ export const initPush = next => {
170
274
  if (Platform.OS === 'android' && next.androidChannels?.length) {
171
275
  channelsReady = createChannels(m, next.androidChannels);
172
276
  }
173
- m.addNotificationResponseReceivedListener(handleResponse);
174
- void m.getLastNotificationResponseAsync().then(response => {
175
- handleResponse(response);
176
- return m.clearLastNotificationResponseAsync?.();
177
- }).catch(error => Logger.error('[push] reading the launch notification failed', error));
277
+ listenForResponses(m);
278
+ void drainPending(m);
178
279
  void m.getPermissionsAsync().then(response => setState({
179
280
  status: toPermissionStatus(response)
180
281
  })).catch(error => Logger.error('[push] permission check failed', error));
@@ -249,6 +350,60 @@ export const setBadgeCount = async count => {
249
350
  }
250
351
  await mods.notifications.setBadgeCountAsync(count);
251
352
  };
353
+
354
+ /**
355
+ * Registers the button sets a push can name by category id. Call it at every
356
+ * start, with the same ids on both platforms. On Android the buttons show only
357
+ * on notifications expo-notifications draws, such as `notifyLocal`.
358
+ */
359
+ export const setCategories = async categories => {
360
+ const mods = resolveModules();
361
+ if (!mods) {
362
+ return;
363
+ }
364
+ for (const category of categories) {
365
+ await mods.notifications.setNotificationCategoryAsync(category.id, toNotificationActions(category.actions));
366
+ }
367
+ };
368
+
369
+ /**
370
+ * Handles button taps while JavaScript is running; a plain tap still goes to
371
+ * `onOpen`. For taps on a killed app use `definePushActionTask` from
372
+ * `/push/task`, which sets this handler too. Returns a function that removes it.
373
+ */
374
+ export const onAction = handler => {
375
+ actionHandler = handler;
376
+ const mods = resolveModules();
377
+ if (mods) {
378
+ listenForResponses(mods.notifications);
379
+ void drainPending(mods.notifications);
380
+ }
381
+ return () => {
382
+ if (actionHandler === handler) {
383
+ actionHandler = null;
384
+ }
385
+ };
386
+ };
387
+
388
+ /** Shows a notification from the phone itself, for example when a button's request failed. */
389
+ export const notifyLocal = async ({
390
+ title,
391
+ body,
392
+ data
393
+ }) => {
394
+ const mods = resolveModules();
395
+ if (!mods) {
396
+ return;
397
+ }
398
+ await mods.notifications.scheduleNotificationAsync({
399
+ content: {
400
+ title,
401
+ body,
402
+ data: data ?? {}
403
+ },
404
+ trigger: null
405
+ });
406
+ };
252
407
  const subscribe = notify => {
253
408
  subscribers.add(notify);
254
409
  return () => {
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ import { Platform } from 'react-native';
4
+ import { Logger } from "../logger/index.js";
5
+ import { onAction, resolveModules, routeTaskPayload } from "./push.js";
6
+ const PUSH_ACTION_TASK = 'webority-push-action';
7
+ /**
8
+ * Handles notification button taps in every app state, the killed app
9
+ * included. Call it once at module scope in the app's entry file (index.js),
10
+ * before the root component registers: a tap on a killed app starts the
11
+ * JavaScript bundle without the UI, so nothing later in the app runs.
12
+ *
13
+ * Android runs the handler from a background notification task
14
+ * (expo-task-manager). iOS has no such task for button taps: it launches the
15
+ * app in the background and the response reaches the listener this sets up.
16
+ * The handler's promise is awaited, so a request it makes can finish first.
17
+ */
18
+ export const definePushActionTask = handler => {
19
+ onAction(handler);
20
+ const mods = resolveModules();
21
+ if (!mods || Platform.OS !== 'android') {
22
+ return;
23
+ }
24
+ /** The one place `expo-task-manager` is named. Import `/push/task` only if you use button taps. */
25
+ const taskManager = require('expo-task-manager');
26
+ taskManager.defineTask(PUSH_ACTION_TASK, async ({
27
+ data,
28
+ error
29
+ }) => {
30
+ if (error) {
31
+ Logger.error('[push] the notification task failed', error);
32
+ return;
33
+ }
34
+ await routeTaskPayload(data);
35
+ });
36
+ void mods.notifications.registerTaskAsync(PUSH_ACTION_TASK).catch(registerError => Logger.error('[push] registering the notification task failed', registerError));
37
+ };
38
+ //# sourceMappingURL=task.js.map
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ // Small enough for every engine's argument limit in `String.fromCharCode.apply`.
4
+ const CHUNK = 0x2000;
5
+
6
+ /**
7
+ * Base64 of raw bytes, for a `data:` URI. Builds a Latin-1 string a chunk at a
8
+ * time and hands it to the engine's native `btoa` (Hermes ships it), which is
9
+ * many times faster on the JS thread than encoding byte by byte in JS.
10
+ */
11
+ export const encodeBase64 = buffer => {
12
+ const bytes = new Uint8Array(buffer);
13
+ let binary = '';
14
+ for (let i = 0; i < bytes.length; i += CHUNK) {
15
+ binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
16
+ }
17
+ return btoa(binary);
18
+ };
19
+ //# sourceMappingURL=base64.js.map
@@ -34,6 +34,12 @@ export interface Config {
34
34
  */
35
35
  logCurl: boolean;
36
36
  }
37
+ /**
38
+ * Info lines (each token refresh, each unauthorised URL) reach the device log
39
+ * and any remote sink, so a release build that sets nothing logs warnings and
40
+ * errors only.
41
+ */
42
+ export declare const DEFAULT_LOG_LEVEL: LogLevel;
37
43
  /**
38
44
  * Set global configuration for all Webority packages
39
45
  */
@@ -0,0 +1,6 @@
1
+ export declare const numberFormat: (locale: string, options?: Intl.NumberFormatOptions) => Intl.NumberFormat;
2
+ export declare const dateTimeFormat: (locale: string, options: Intl.DateTimeFormatOptions) => Intl.DateTimeFormat;
3
+ export declare const relativeTimeFormat: (RelativeTimeFormat: new (locale: string, options?: {
4
+ numeric?: "always" | "auto";
5
+ }) => Intl.RelativeTimeFormat, locale: string) => Intl.RelativeTimeFormat;
6
+ //# sourceMappingURL=intlCache.d.ts.map
@@ -48,6 +48,7 @@ export type { NetworkConnectionType, NetworkStatus, NetworkStatusLike, NetworkSt
48
48
  export { addNetworkStatusListener, getNetworkStatus, setNetworkStatusImplementation, useNetworkStatus } from './network';
49
49
  export type { AsyncStorageLike, StorageApi } from './storage';
50
50
  export { isStorageAvailable, Storage, setStorageImplementation } from './storage';
51
+ export { encodeBase64 } from './utils/base64';
51
52
  export type { PasswordRules } from './validators';
52
53
  export { isAadhaar, isAlphanumeric, isBetween, isEmail, isGstin, isHexColor, isIfsc, isIndianMobile, isIndianPincode, isInteger, isNonEmpty, isNumeric, isPan, isPhone, isStrongPassword, isUrl, maxLength, minLength } from './validators';
53
54
  export type { DeviceInfoLike, ParsedVersion, Platform, UpdateInfo, UpdateType, VersionCheckResult, VersionCheckType, VersionComparison, VersionData } from './versionCheck';
@@ -11,7 +11,7 @@ export interface InitConfig {
11
11
  baseUrl: string;
12
12
  /** Enable/disable logging. Defaults to `true`. */
13
13
  loggingEnabled?: boolean;
14
- /** Log level. Defaults to `'info'`. */
14
+ /** Log level. Defaults to `'info'` in development and `'warn'` in a release build. */
15
15
  logLevel?: LogLevel;
16
16
  /** Environment. Defaults to `'Development'`. */
17
17
  environment?: Environment;
@@ -34,6 +34,12 @@ export declare const getNetworkStatus: () => Promise<NetworkStatus>;
34
34
  * Subscribe to network status changes. Returns an unsubscribe function.
35
35
  */
36
36
  export declare const addNetworkStatusListener: (listener: NetworkStatusListener) => (() => void);
37
+ /**
38
+ * The connectivity the HTTP client checks before every request. One listener,
39
+ * attached on first use, keeps it current, so a request costs no native probe:
40
+ * one probe seeds the value and the listener's events replace it.
41
+ */
42
+ export declare const getLastKnownNetworkStatus: () => Promise<NetworkStatus>;
37
43
  /**
38
44
  * React hook that returns the current network status and re-renders on change.
39
45
  * Reports an optimistic online default when NetInfo is not installed.
@@ -1,3 +1,3 @@
1
- export type { PushAndroidChannel, PushConfig, PushDevice, PushModules, PushNotificationsLike, PushPlatform, PushState, PushStatus } from './push';
2
- export { disablePush, enablePush, initPush, setBadgeCount, setPushImplementation, usePush } from './push';
1
+ export type { PushAction, PushActionHandler, PushAndroidChannel, PushCategory, PushCategoryAction, PushConfig, PushDevice, PushLocalNotification, PushModules, PushNotificationsLike, PushPlatform, PushState, PushStatus } from './push';
2
+ export { disablePush, enablePush, initPush, notifyLocal, onAction, setBadgeCount, setCategories, setPushImplementation, usePush } from './push';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -26,6 +26,42 @@ export interface PushConfig {
26
26
  /** Whether a notification shown while the app is open plays its sound. Defaults to true. */
27
27
  playSoundInForeground?: () => boolean;
28
28
  }
29
+ /** A button on a notification category. */
30
+ export interface PushCategoryAction {
31
+ id: string;
32
+ title: string;
33
+ /** Whether the tap brings the app to the foreground. Defaults to true. */
34
+ opensApp?: boolean;
35
+ /** iOS only: the title shows in red. */
36
+ destructive?: boolean;
37
+ /** Turns the button into a reply box; the text arrives as `userText`. */
38
+ textInput?: {
39
+ submitTitle: string;
40
+ placeholder: string;
41
+ };
42
+ /** iOS only: the phone must be unlocked before the action runs. */
43
+ authenticationRequired?: boolean;
44
+ }
45
+ /** A set of buttons a push names through its category id. */
46
+ export interface PushCategory {
47
+ id: string;
48
+ actions: PushCategoryAction[];
49
+ }
50
+ /** A button tap on a notification. */
51
+ export interface PushAction {
52
+ actionId: string;
53
+ /** What the person typed, for a `textInput` action. */
54
+ userText?: string;
55
+ data: Record<string, string>;
56
+ /** The notification's identifier on the phone, not a server id in `data`. */
57
+ notificationId: string;
58
+ }
59
+ export type PushActionHandler = (action: PushAction) => Promise<void> | void;
60
+ export interface PushLocalNotification {
61
+ title: string;
62
+ body: string;
63
+ data?: Record<string, string>;
64
+ }
29
65
  export type PushStatus = 'unknown' | 'granted' | 'denied' | 'unavailable';
30
66
  export interface PushState {
31
67
  status: PushStatus;
@@ -41,15 +77,31 @@ interface SubscriptionLike {
41
77
  }
42
78
  interface NotificationResponseLike {
43
79
  actionIdentifier: string;
80
+ userText?: string;
44
81
  notification: {
45
82
  request: {
46
83
  identifier: string;
84
+ /** A background task receives the raw content, where data may still be a JSON `dataString`. */
47
85
  content: {
48
86
  data?: Record<string, unknown> | null;
87
+ dataString?: string;
49
88
  };
50
89
  };
51
90
  };
52
91
  }
92
+ interface NotificationActionLike {
93
+ identifier: string;
94
+ buttonTitle: string;
95
+ textInput?: {
96
+ submitButtonTitle: string;
97
+ placeholder: string;
98
+ };
99
+ options: {
100
+ opensAppToForeground: boolean;
101
+ isDestructive: boolean;
102
+ isAuthenticationRequired: boolean;
103
+ };
104
+ }
53
105
  interface DevicePushTokenLike {
54
106
  type: string;
55
107
  data: unknown;
@@ -76,10 +128,22 @@ export interface PushNotificationsLike {
76
128
  importance: number;
77
129
  }) => Promise<unknown>;
78
130
  setBadgeCountAsync: (count: number) => Promise<boolean>;
131
+ setNotificationCategoryAsync: (identifier: string, actions: NotificationActionLike[]) => Promise<unknown>;
132
+ scheduleNotificationAsync: (request: {
133
+ content: {
134
+ title: string;
135
+ body: string;
136
+ data: Record<string, string>;
137
+ };
138
+ trigger: null;
139
+ }) => Promise<string>;
140
+ registerTaskAsync: (taskName: string) => Promise<unknown>;
79
141
  AndroidImportance: {
80
142
  DEFAULT: number;
81
143
  HIGH: number;
82
144
  };
145
+ /** The `actionIdentifier` of a plain tap on the notification body. */
146
+ DEFAULT_ACTION_IDENTIFIER: string;
83
147
  }
84
148
  export interface PushModules {
85
149
  notifications: PushNotificationsLike;
@@ -92,6 +156,16 @@ export interface PushModules {
92
156
  * included. Pass `null` to restore expo-notifications.
93
157
  */
94
158
  export declare const setPushImplementation: (impl: PushModules | null) => void;
159
+ /**
160
+ * The only place expo-notifications and expo-application are named. Expo Go is
161
+ * checked first because expo-notifications refuses remote push there and its
162
+ * import throws on Android. Exported for `./task`; not on the `/push` entry.
163
+ */
164
+ export declare const resolveModules: () => PushModules | null;
165
+ export declare const toPushAction: (response: NotificationResponseLike) => PushAction;
166
+ export declare const toNotificationActions: (actions: PushCategoryAction[]) => NotificationActionLike[];
167
+ /** The payload of the background notification task: routes button taps, ignores received pushes. */
168
+ export declare const routeTaskPayload: (payload: unknown) => Promise<void>;
95
169
  /**
96
170
  * Wires push at startup: the foreground handler, Android channels, and tap
97
171
  * handling, including the tap that launched the app. Calling it again only
@@ -110,6 +184,20 @@ export declare const enablePush: () => Promise<"granted" | "denied" | "unavailab
110
184
  export declare const disablePush: () => Promise<void>;
111
185
  /** Sets the app icon badge; a no-op where push is unavailable or the launcher has no badge. */
112
186
  export declare const setBadgeCount: (count: number) => Promise<void>;
187
+ /**
188
+ * Registers the button sets a push can name by category id. Call it at every
189
+ * start, with the same ids on both platforms. On Android the buttons show only
190
+ * on notifications expo-notifications draws, such as `notifyLocal`.
191
+ */
192
+ export declare const setCategories: (categories: PushCategory[]) => Promise<void>;
193
+ /**
194
+ * Handles button taps while JavaScript is running; a plain tap still goes to
195
+ * `onOpen`. For taps on a killed app use `definePushActionTask` from
196
+ * `/push/task`, which sets this handler too. Returns a function that removes it.
197
+ */
198
+ export declare const onAction: (handler: PushActionHandler) => (() => void);
199
+ /** Shows a notification from the phone itself, for example when a button's request failed. */
200
+ export declare const notifyLocal: ({ title, body, data }: PushLocalNotification) => Promise<void>;
113
201
  /** Push status for a settings switch, re-rendering when it changes. */
114
202
  export declare const usePush: () => PushState & {
115
203
  enable: () => Promise<void>;
@@ -0,0 +1,15 @@
1
+ import { type PushActionHandler } from './push';
2
+ export type { PushAction, PushActionHandler } from './push';
3
+ /**
4
+ * Handles notification button taps in every app state, the killed app
5
+ * included. Call it once at module scope in the app's entry file (index.js),
6
+ * before the root component registers: a tap on a killed app starts the
7
+ * JavaScript bundle without the UI, so nothing later in the app runs.
8
+ *
9
+ * Android runs the handler from a background notification task
10
+ * (expo-task-manager). iOS has no such task for button taps: it launches the
11
+ * app in the background and the response reaches the listener this sets up.
12
+ * The handler's promise is awaited, so a request it makes can finish first.
13
+ */
14
+ export declare const definePushActionTask: (handler: PushActionHandler) => void;
15
+ //# sourceMappingURL=task.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Base64 of raw bytes, for a `data:` URI. Builds a Latin-1 string a chunk at a
3
+ * time and hands it to the engine's native `btoa` (Hermes ships it), which is
4
+ * many times faster on the JS thread than encoding byte by byte in JS.
5
+ */
6
+ export declare const encodeBase64: (buffer: ArrayBuffer) => string;
7
+ //# sourceMappingURL=base64.d.ts.map
@@ -34,6 +34,12 @@ export interface Config {
34
34
  */
35
35
  logCurl: boolean;
36
36
  }
37
+ /**
38
+ * Info lines (each token refresh, each unauthorised URL) reach the device log
39
+ * and any remote sink, so a release build that sets nothing logs warnings and
40
+ * errors only.
41
+ */
42
+ export declare const DEFAULT_LOG_LEVEL: LogLevel;
37
43
  /**
38
44
  * Set global configuration for all Webority packages
39
45
  */
@@ -0,0 +1,6 @@
1
+ export declare const numberFormat: (locale: string, options?: Intl.NumberFormatOptions) => Intl.NumberFormat;
2
+ export declare const dateTimeFormat: (locale: string, options: Intl.DateTimeFormatOptions) => Intl.DateTimeFormat;
3
+ export declare const relativeTimeFormat: (RelativeTimeFormat: new (locale: string, options?: {
4
+ numeric?: "always" | "auto";
5
+ }) => Intl.RelativeTimeFormat, locale: string) => Intl.RelativeTimeFormat;
6
+ //# sourceMappingURL=intlCache.d.ts.map
@@ -48,6 +48,7 @@ export type { NetworkConnectionType, NetworkStatus, NetworkStatusLike, NetworkSt
48
48
  export { addNetworkStatusListener, getNetworkStatus, setNetworkStatusImplementation, useNetworkStatus } from './network/index.js';
49
49
  export type { AsyncStorageLike, StorageApi } from './storage/index.js';
50
50
  export { isStorageAvailable, Storage, setStorageImplementation } from './storage/index.js';
51
+ export { encodeBase64 } from './utils/base64.js';
51
52
  export type { PasswordRules } from './validators/index.js';
52
53
  export { isAadhaar, isAlphanumeric, isBetween, isEmail, isGstin, isHexColor, isIfsc, isIndianMobile, isIndianPincode, isInteger, isNonEmpty, isNumeric, isPan, isPhone, isStrongPassword, isUrl, maxLength, minLength } from './validators/index.js';
53
54
  export type { DeviceInfoLike, ParsedVersion, Platform, UpdateInfo, UpdateType, VersionCheckResult, VersionCheckType, VersionComparison, VersionData } from './versionCheck/index.js';
@@ -11,7 +11,7 @@ export interface InitConfig {
11
11
  baseUrl: string;
12
12
  /** Enable/disable logging. Defaults to `true`. */
13
13
  loggingEnabled?: boolean;
14
- /** Log level. Defaults to `'info'`. */
14
+ /** Log level. Defaults to `'info'` in development and `'warn'` in a release build. */
15
15
  logLevel?: LogLevel;
16
16
  /** Environment. Defaults to `'Development'`. */
17
17
  environment?: Environment;
@@ -34,6 +34,12 @@ export declare const getNetworkStatus: () => Promise<NetworkStatus>;
34
34
  * Subscribe to network status changes. Returns an unsubscribe function.
35
35
  */
36
36
  export declare const addNetworkStatusListener: (listener: NetworkStatusListener) => (() => void);
37
+ /**
38
+ * The connectivity the HTTP client checks before every request. One listener,
39
+ * attached on first use, keeps it current, so a request costs no native probe:
40
+ * one probe seeds the value and the listener's events replace it.
41
+ */
42
+ export declare const getLastKnownNetworkStatus: () => Promise<NetworkStatus>;
37
43
  /**
38
44
  * React hook that returns the current network status and re-renders on change.
39
45
  * Reports an optimistic online default when NetInfo is not installed.
@@ -1,3 +1,3 @@
1
- export type { PushAndroidChannel, PushConfig, PushDevice, PushModules, PushNotificationsLike, PushPlatform, PushState, PushStatus } from './push.js';
2
- export { disablePush, enablePush, initPush, setBadgeCount, setPushImplementation, usePush } from './push.js';
1
+ export type { PushAction, PushActionHandler, PushAndroidChannel, PushCategory, PushCategoryAction, PushConfig, PushDevice, PushLocalNotification, PushModules, PushNotificationsLike, PushPlatform, PushState, PushStatus } from './push.js';
2
+ export { disablePush, enablePush, initPush, notifyLocal, onAction, setBadgeCount, setCategories, setPushImplementation, usePush } from './push.js';
3
3
  //# sourceMappingURL=index.d.ts.map