@webority-technologies/mobile-core 0.0.1 → 0.0.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.
Files changed (146) hide show
  1. package/README.md +22 -1
  2. package/lib/commonjs/api/baseUrl.js +34 -0
  3. package/lib/commonjs/api/curl.js +109 -0
  4. package/lib/commonjs/api/publicClient.js +11 -20
  5. package/lib/commonjs/api/retry.js +75 -0
  6. package/lib/commonjs/api/userClient.js +46 -22
  7. package/lib/commonjs/auth/authStore.js +101 -33
  8. package/lib/commonjs/auth/jwt.js +32 -8
  9. package/lib/commonjs/config/index.js +2 -1
  10. package/lib/commonjs/deepLink/index.js +286 -0
  11. package/lib/commonjs/download/adapters/expoFs.js +100 -0
  12. package/lib/commonjs/download/adapters/shared.js +13 -0
  13. package/lib/commonjs/download/index.js +272 -0
  14. package/lib/commonjs/formatters/date.js +1 -1
  15. package/lib/commonjs/formatters/index.js +24 -0
  16. package/lib/commonjs/formatters/phone.js +132 -15
  17. package/lib/commonjs/geolocation/adapters/expoLocation.js +126 -0
  18. package/lib/commonjs/geolocation/index.js +339 -0
  19. package/lib/commonjs/index.js +151 -48
  20. package/lib/commonjs/initSDK.js +23 -2
  21. package/lib/commonjs/initUserAuth.js +2 -1
  22. package/lib/commonjs/logger/index.js +159 -5
  23. package/lib/commonjs/network/index.js +6 -0
  24. package/lib/commonjs/network/networkStatus.js +50 -15
  25. package/lib/commonjs/permissions/index.js +24 -1
  26. package/lib/commonjs/sqlite/adapters/expo.js +53 -0
  27. package/lib/commonjs/sqlite/adapters/op.js +43 -0
  28. package/lib/commonjs/sqlite/adapters/rows.js +33 -0
  29. package/lib/commonjs/sqlite/adapters/storage.js +50 -0
  30. package/lib/commonjs/sqlite/adapters/sync.js +45 -0
  31. package/lib/commonjs/sqlite/index.js +610 -0
  32. package/lib/commonjs/sqlite/nitro.js +17 -0
  33. package/lib/commonjs/sqlite/op.js +18 -0
  34. package/lib/commonjs/sqlite/quick.js +14 -0
  35. package/lib/commonjs/sqlite/storage.js +14 -0
  36. package/lib/commonjs/storage/index.js +41 -20
  37. package/lib/commonjs/types/globals.d.js +6 -0
  38. package/lib/commonjs/utils/imageCompression.js +51 -14
  39. package/lib/commonjs/utils/index.js +6 -0
  40. package/lib/commonjs/versionCheck/index.js +28 -4
  41. package/lib/module/api/baseUrl.js +30 -0
  42. package/lib/module/api/curl.js +104 -0
  43. package/lib/module/api/publicClient.js +11 -20
  44. package/lib/module/api/retry.js +69 -0
  45. package/lib/module/api/userClient.js +45 -21
  46. package/lib/module/auth/authStore.js +99 -32
  47. package/lib/module/auth/jwt.js +32 -8
  48. package/lib/module/config/index.js +2 -1
  49. package/lib/module/deepLink/index.js +280 -0
  50. package/lib/module/download/adapters/expoFs.js +95 -0
  51. package/lib/module/download/adapters/shared.js +8 -0
  52. package/lib/module/download/index.js +264 -0
  53. package/lib/module/formatters/date.js +1 -1
  54. package/lib/module/formatters/index.js +1 -1
  55. package/lib/module/formatters/phone.js +110 -13
  56. package/lib/module/geolocation/adapters/expoLocation.js +121 -0
  57. package/lib/module/geolocation/index.js +332 -0
  58. package/lib/module/index.js +28 -11
  59. package/lib/module/initSDK.js +23 -2
  60. package/lib/module/initUserAuth.js +2 -1
  61. package/lib/module/logger/index.js +156 -4
  62. package/lib/module/network/index.js +1 -1
  63. package/lib/module/network/networkStatus.js +47 -14
  64. package/lib/module/permissions/index.js +22 -0
  65. package/lib/module/sqlite/adapters/expo.js +48 -0
  66. package/lib/module/sqlite/adapters/op.js +38 -0
  67. package/lib/module/sqlite/adapters/rows.js +28 -0
  68. package/lib/module/sqlite/adapters/storage.js +46 -0
  69. package/lib/module/sqlite/adapters/sync.js +41 -0
  70. package/lib/module/sqlite/index.js +600 -0
  71. package/lib/module/sqlite/nitro.js +12 -0
  72. package/lib/module/sqlite/op.js +13 -0
  73. package/lib/module/sqlite/quick.js +9 -0
  74. package/lib/module/sqlite/storage.js +9 -0
  75. package/lib/module/storage/index.js +38 -19
  76. package/lib/module/types/globals.d.js +10 -0
  77. package/lib/module/utils/imageCompression.js +49 -13
  78. package/lib/module/utils/index.js +1 -1
  79. package/lib/module/versionCheck/index.js +27 -4
  80. package/lib/typescript/commonjs/api/baseUrl.d.ts +20 -0
  81. package/lib/typescript/commonjs/api/curl.d.ts +18 -0
  82. package/lib/typescript/commonjs/api/retry.d.ts +27 -0
  83. package/lib/typescript/commonjs/api/userClient.d.ts +11 -0
  84. package/lib/typescript/commonjs/auth/authStore.d.ts +29 -6
  85. package/lib/typescript/commonjs/config/index.d.ts +6 -0
  86. package/lib/typescript/commonjs/deepLink/index.d.ts +54 -0
  87. package/lib/typescript/commonjs/download/adapters/expoFs.d.ts +45 -0
  88. package/lib/typescript/commonjs/download/adapters/shared.d.ts +3 -0
  89. package/lib/typescript/commonjs/download/index.d.ts +76 -0
  90. package/lib/typescript/commonjs/formatters/index.d.ts +2 -1
  91. package/lib/typescript/commonjs/formatters/phone.d.ts +27 -1
  92. package/lib/typescript/commonjs/geolocation/adapters/expoLocation.d.ts +42 -0
  93. package/lib/typescript/commonjs/geolocation/index.d.ts +89 -0
  94. package/lib/typescript/commonjs/index.d.ts +30 -13
  95. package/lib/typescript/commonjs/initSDK.d.ts +32 -0
  96. package/lib/typescript/commonjs/logger/index.d.ts +39 -0
  97. package/lib/typescript/commonjs/network/index.d.ts +2 -2
  98. package/lib/typescript/commonjs/network/networkStatus.d.ts +16 -0
  99. package/lib/typescript/commonjs/permissions/index.d.ts +35 -0
  100. package/lib/typescript/commonjs/sqlite/adapters/expo.d.ts +30 -0
  101. package/lib/typescript/commonjs/sqlite/adapters/op.d.ts +27 -0
  102. package/lib/typescript/commonjs/sqlite/adapters/rows.d.ts +7 -0
  103. package/lib/typescript/commonjs/sqlite/adapters/storage.d.ts +30 -0
  104. package/lib/typescript/commonjs/sqlite/adapters/sync.d.ts +31 -0
  105. package/lib/typescript/commonjs/sqlite/index.d.ts +121 -0
  106. package/lib/typescript/commonjs/sqlite/nitro.d.ts +7 -0
  107. package/lib/typescript/commonjs/sqlite/op.d.ts +8 -0
  108. package/lib/typescript/commonjs/sqlite/quick.d.ts +4 -0
  109. package/lib/typescript/commonjs/sqlite/storage.d.ts +4 -0
  110. package/lib/typescript/commonjs/storage/index.d.ts +25 -3
  111. package/lib/typescript/commonjs/utils/imageCompression.d.ts +17 -0
  112. package/lib/typescript/commonjs/utils/index.d.ts +2 -2
  113. package/lib/typescript/module/api/baseUrl.d.ts +20 -0
  114. package/lib/typescript/module/api/curl.d.ts +18 -0
  115. package/lib/typescript/module/api/retry.d.ts +27 -0
  116. package/lib/typescript/module/api/userClient.d.ts +11 -0
  117. package/lib/typescript/module/auth/authStore.d.ts +29 -6
  118. package/lib/typescript/module/config/index.d.ts +6 -0
  119. package/lib/typescript/module/deepLink/index.d.ts +54 -0
  120. package/lib/typescript/module/download/adapters/expoFs.d.ts +45 -0
  121. package/lib/typescript/module/download/adapters/shared.d.ts +3 -0
  122. package/lib/typescript/module/download/index.d.ts +76 -0
  123. package/lib/typescript/module/formatters/index.d.ts +2 -1
  124. package/lib/typescript/module/formatters/phone.d.ts +27 -1
  125. package/lib/typescript/module/geolocation/adapters/expoLocation.d.ts +42 -0
  126. package/lib/typescript/module/geolocation/index.d.ts +89 -0
  127. package/lib/typescript/module/index.d.ts +30 -13
  128. package/lib/typescript/module/initSDK.d.ts +32 -0
  129. package/lib/typescript/module/logger/index.d.ts +39 -0
  130. package/lib/typescript/module/network/index.d.ts +2 -2
  131. package/lib/typescript/module/network/networkStatus.d.ts +16 -0
  132. package/lib/typescript/module/permissions/index.d.ts +35 -0
  133. package/lib/typescript/module/sqlite/adapters/expo.d.ts +30 -0
  134. package/lib/typescript/module/sqlite/adapters/op.d.ts +27 -0
  135. package/lib/typescript/module/sqlite/adapters/rows.d.ts +7 -0
  136. package/lib/typescript/module/sqlite/adapters/storage.d.ts +30 -0
  137. package/lib/typescript/module/sqlite/adapters/sync.d.ts +31 -0
  138. package/lib/typescript/module/sqlite/index.d.ts +121 -0
  139. package/lib/typescript/module/sqlite/nitro.d.ts +7 -0
  140. package/lib/typescript/module/sqlite/op.d.ts +8 -0
  141. package/lib/typescript/module/sqlite/quick.d.ts +4 -0
  142. package/lib/typescript/module/sqlite/storage.d.ts +4 -0
  143. package/lib/typescript/module/storage/index.d.ts +25 -3
  144. package/lib/typescript/module/utils/imageCompression.d.ts +17 -0
  145. package/lib/typescript/module/utils/index.d.ts +2 -2
  146. package/package.json +102 -13
@@ -6,6 +6,22 @@ interface AsyncStorageLike {
6
6
  getItem: (key: string) => Promise<string | null>;
7
7
  setItem: (key: string, value: string) => Promise<void>;
8
8
  removeItem: (key: string) => Promise<void>;
9
+ /**
10
+ * The batch API exists under TWO names, and both are live in this fleet.
11
+ *
12
+ * `@react-native-async-storage/async-storage` 3 renamed the v1 trio and
13
+ * changed their shapes: `getMany` returns a Record where `multiGet` returned
14
+ * pairs, and `setMany` takes a Record where `multiSet` took pairs. The v1
15
+ * names are still what `expo-sqlite/kv-store` and AsyncStorage 2 expose.
16
+ *
17
+ * Declaring only one set is not a compile error and not a runtime error — it
18
+ * silently falls through to the per-key loop below. The fleet pins
19
+ * AsyncStorage 3, so recognising only the v1 names put every consumer on N
20
+ * round-trips per batch with nothing to notice.
21
+ */
22
+ getMany?: (keys: string[]) => Promise<Record<string, string | null>>;
23
+ setMany?: (entries: Record<string, string>) => Promise<void>;
24
+ removeMany?: (keys: string[]) => Promise<void>;
9
25
  multiGet?: (keys: readonly string[]) => Promise<readonly [string, string | null][]>;
10
26
  multiSet?: (kvs: readonly [string, string][]) => Promise<void>;
11
27
  multiRemove?: (keys: readonly string[]) => Promise<void>;
@@ -13,8 +29,14 @@ interface AsyncStorageLike {
13
29
  getAllKeys?: () => Promise<readonly string[]>;
14
30
  }
15
31
  /**
16
- * Inject a custom storage backend (e.g. MMKV adapter) at runtime.
17
- * Pass `null` to reset to the default AsyncStorage backend.
32
+ * Always true now that the backend ships with the library. Kept because callers
33
+ * whose use of storage is an OPTIMISATION branch on it, and removing it would
34
+ * be a breaking change for no gain.
35
+ */
36
+ export declare const isStorageAvailable: () => boolean;
37
+ /**
38
+ * Replace the storage backend. Intended for TESTS and for the showcase; an app
39
+ * gets expo-sqlite/kv-store without wiring anything. Pass `null` to restore it.
18
40
  */
19
41
  export declare const setStorageImplementation: (impl: AsyncStorageLike | null) => void;
20
42
  export interface StorageApi {
@@ -34,7 +56,7 @@ export interface StorageApi {
34
56
  * All operations swallow errors (logging them) and return a falsy result
35
57
  * so that storage problems never crash the app.
36
58
  *
37
- * Backed by `@react-native-async-storage/async-storage` by default;
59
+ * Backed by `expo-sqlite/kv-store` by default;
38
60
  * override with `setStorageImplementation()` for MMKV or custom adapters.
39
61
  */
40
62
  export declare const Storage: StorageApi;
@@ -16,6 +16,22 @@ export interface CompressImageOptions {
16
16
  */
17
17
  fileSize?: number;
18
18
  }
19
+ export interface CompressorModule {
20
+ Image: {
21
+ compress: (uri: string, options: {
22
+ maxWidth?: number;
23
+ maxHeight?: number;
24
+ quality?: number;
25
+ compressionMethod?: 'auto' | 'manual';
26
+ output?: 'jpg' | 'png' | 'webp';
27
+ }) => Promise<string>;
28
+ };
29
+ }
30
+ /**
31
+ * Replace the compressor. Intended for TESTS and for the showcase; an app gets
32
+ * expo-image-manipulator without wiring anything. Pass `null` to restore it.
33
+ */
34
+ export declare const setImageCompressorImplementation: (impl: CompressorModule | null) => void;
19
35
  /**
20
36
  * Bigger source files get compressed harder — a fixed quality either over-compresses
21
37
  * small photos or leaves large ones too heavy to upload quickly. Unknown size falls
@@ -28,4 +44,5 @@ export declare const qualityForFileSize: (fileSize?: number) => number;
28
44
  * dependency is missing — so callers can rely on always getting a usable URI.
29
45
  */
30
46
  export declare const compressImage: (uri: string, options?: CompressImageOptions) => Promise<string>;
47
+ export type { CompressorModule as ImageCompressorLike };
31
48
  //# sourceMappingURL=imageCompression.d.ts.map
@@ -1,3 +1,3 @@
1
- export type { CompressImageOptions } from './imageCompression';
2
- export { compressImage } from './imageCompression';
1
+ export type { CompressImageOptions, ImageCompressorLike } from './imageCompression';
2
+ export { compressImage, setImageCompressorImplementation } from './imageCompression';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,20 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ /**
3
+ * Resolves `baseURL` PER REQUEST rather than at module evaluation.
4
+ *
5
+ * `axios.create({ baseURL: getConfigValue('baseUrl') })` reads the value once,
6
+ * when the module is first imported — which happens the moment anything touches
7
+ * the package barrel, long before an app's startup code can call `setConfig`.
8
+ * The result was a client permanently pinned to `undefined` and a `setConfig`
9
+ * that appeared to do nothing, with no error anywhere: requests just went to a
10
+ * relative URL.
11
+ *
12
+ * A consumer cannot fix that by ordering its own imports, because the import
13
+ * that loses the race is usually one it never wrote. So the clients stop
14
+ * capturing the value and ask for it per request instead.
15
+ *
16
+ * An explicit per-request `baseURL` still wins, which is what keeps a one-off
17
+ * call to a different host possible.
18
+ */
19
+ export declare const applyConfiguredBaseUrl: (client: AxiosInstance) => void;
20
+ //# sourceMappingURL=baseUrl.d.ts.map
@@ -0,0 +1,18 @@
1
+ import type { AxiosRequestConfig } from 'axios';
2
+ /**
3
+ * Builds a copy-pasteable curl command for a request, with every credential
4
+ * replaced by a placeholder.
5
+ *
6
+ * The redaction is the point, not a nicety. An unredacted version of this in a
7
+ * consumer app put `Authorization: Bearer <token>` and the login request's
8
+ * plaintext password into the device console of a *release* staging build.
9
+ * A curl line is worth having; a curl line that has to be trusted not to be
10
+ * enabled in the wrong build is not.
11
+ */
12
+ export declare const buildCurl: (config: AxiosRequestConfig) => string;
13
+ /**
14
+ * Logs the curl equivalent of a request. Never throws: a failure to describe a
15
+ * request must not be able to fail the request itself.
16
+ */
17
+ export declare const logCurl: (config: AxiosRequestConfig) => void;
18
+ //# sourceMappingURL=curl.d.ts.map
@@ -0,0 +1,27 @@
1
+ import type { AxiosError, AxiosInstance } from 'axios';
2
+ export interface RetryPolicy {
3
+ retries?: number;
4
+ /** `attempt` is 1-based: 1 is the first retry, not the original request. */
5
+ retryDelayMs?: (attempt: number) => number;
6
+ shouldRetry?: (error: AxiosError) => boolean;
7
+ onRetry?: (attempt: number, error: AxiosError) => void;
8
+ }
9
+ declare module 'axios' {
10
+ interface AxiosRequestConfig {
11
+ /** `false` opts a single request out of retrying; an object overrides the client policy. */
12
+ retry?: boolean | RetryPolicy;
13
+ }
14
+ }
15
+ /**
16
+ * Status-based ONLY, deliberately: a bare network error (no `error.response`)
17
+ * is never retryable. Both clients carry non-idempotent requests, login and OTP
18
+ * on the public one and every write on the user one, and a broken connection
19
+ * says nothing about whether the request reached the server. Retrying can
20
+ * therefore create a second session, or a duplicate record, that the user never
21
+ * sees. A generic network-error retry rule is not a substitute: it decides on
22
+ * the transport alone and never looks at the HTTP method, so it retries POSTs
23
+ * as readily as GETs.
24
+ */
25
+ export declare const isRetryableStatus: (status: number | undefined) => boolean;
26
+ export declare const attachRetry: (client: AxiosInstance, policy?: RetryPolicy) => void;
27
+ //# sourceMappingURL=retry.d.ts.map
@@ -45,5 +45,16 @@ export interface RefreshableStore {
45
45
  declare const userClient: AxiosInstance;
46
46
  export declare const injectStore: <S>(_store: S) => void;
47
47
  export declare const injectAuthActions: (actions: AuthActions) => void;
48
+ /**
49
+ * Clear any in-flight token refresh and reject everything queued behind it.
50
+ * Call this on logout.
51
+ *
52
+ * Without it, a refresh that was already in flight when the user logged out
53
+ * resolves afterwards and replays every queued request with a token for a
54
+ * session that has ended. The queue is module state, so it survives the logout
55
+ * entirely: nothing else clears it. A consumer app hit this and hand-wrote the
56
+ * same function locally, which is how it was found.
57
+ */
58
+ export declare const resetRefreshState: () => void;
48
59
  export default userClient;
49
60
  //# sourceMappingURL=userClient.d.ts.map
@@ -2,6 +2,15 @@ import type * as KeychainModule from 'react-native-keychain';
2
2
  /**
3
3
  * Token data persisted in the keychain.
4
4
  */
5
+ /**
6
+ * The two keys the library itself reads. Everything in the library that needs
7
+ * the bearer token, the HTTP client, the download module and the authenticated
8
+ * image cache, resolves it through these rather than repeating the literal, so
9
+ * one of them cannot silently drift and start reading a key nothing writes.
10
+ * Apps are free to store their own tokens under any other key.
11
+ */
12
+ export declare const ACCESS_TOKEN_KEY = "accessToken";
13
+ export declare const REFRESH_TOKEN_KEY = "refreshToken";
5
14
  export interface StoredToken {
6
15
  token: string;
7
16
  expiry: string;
@@ -11,20 +20,34 @@ export interface StoredToken {
11
20
  * Lets us avoid a hard import on `react-native-keychain`, so an app that never
12
21
  * stores a token does not have to install it.
13
22
  */
23
+ /**
24
+ * The GENERIC password API, not the internet-credentials one.
25
+ *
26
+ * `kSecClassInternetPassword` exists for credentials bound to a server, protocol
27
+ * and port, and it integrates with Safari and AutoFill. A bearer token keyed by
28
+ * a name like "accessToken" is none of those things, and the library was only
29
+ * satisfying that API's required `server` argument by passing the key twice,
30
+ * once as the server and once as the service. That is misuse of the class.
31
+ *
32
+ * `kSecClassGenericPassword` is the class for app-local secrets, which is what a
33
+ * token is. It is what expo-secure-store uses underneath, and what the consumer
34
+ * app being migrated had already chosen for itself.
35
+ */
14
36
  interface KeychainLike {
15
- setInternetCredentials: (server: string, username: string, password: string, options?: {
37
+ setGenericPassword: (username: string, password: string, options?: {
16
38
  service?: string;
17
39
  }) => Promise<unknown>;
18
- getInternetCredentials: (server: string, options?: {
40
+ getGenericPassword: (options?: {
19
41
  service?: string;
20
42
  }) => Promise<KeychainModule.UserCredentials | false>;
21
- resetInternetCredentials: (options: {
22
- service: string;
43
+ resetGenericPassword: (options?: {
44
+ service?: string;
23
45
  }) => Promise<unknown>;
24
46
  }
25
47
  /**
26
- * Inject a custom keychain backend at runtime.
27
- * Pass `null` to reset to the default react-native-keychain backend.
48
+ * Replace the secure-store backend. Intended for TESTS and for the showcase,
49
+ * which injects a failing store to demo the write-failure path; it is not how
50
+ * an app configures the library. Pass `null` to restore expo-secure-store.
28
51
  */
29
52
  export declare const setKeychainImplementation: (impl: KeychainLike | null) => void;
30
53
  /**
@@ -27,6 +27,12 @@ export interface Config {
27
27
  appId: string | null;
28
28
  /** The app's user-visible version string, e.g. `'1.4.2'`. Same rationale as `appId`. */
29
29
  appVersion: string | null;
30
+ /**
31
+ * Log a copy-pasteable curl command for every API request. Credentials are
32
+ * redacted either way, but this is still request-level noise, so it stays off
33
+ * unless an app asks for it while debugging.
34
+ */
35
+ logCurl: boolean;
30
36
  }
31
37
  /**
32
38
  * Set global configuration for all Webority packages
@@ -0,0 +1,54 @@
1
+ export interface ParsedLink {
2
+ /** The original URL, trimmed. */
3
+ url: string;
4
+ /** Lower-cased scheme without `://`, or '' when the URL carried none. */
5
+ scheme: string;
6
+ /**
7
+ * The authority for http(s) URLs. Custom schemes put the first route segment
8
+ * there (`myapp://order/12`), so for those `host` is `segments[0]` and stays
9
+ * part of `path` so one route pattern matches both link shapes.
10
+ */
11
+ host: string;
12
+ /** Route path with no leading or trailing slash. */
13
+ path: string;
14
+ segments: string[];
15
+ /** Query values, decoded. A repeated key keeps its last occurrence. */
16
+ query: Record<string, string>;
17
+ }
18
+ export interface DeepLinkRoute {
19
+ /** A path pattern with `:param` segments (`order/:id`), or a RegExp on `path`. */
20
+ match: string | RegExp;
21
+ requiresAuth?: boolean;
22
+ handler: (link: ParsedLink, params: Record<string, string>) => void;
23
+ }
24
+ export interface DeepLinkConfig {
25
+ routes: DeepLinkRoute[];
26
+ /** Navigator readiness. Links arriving before this is true are queued. */
27
+ isReady?: () => boolean;
28
+ isAuthenticated?: () => boolean;
29
+ /** Called when an auth-gated link is stashed; the app sends the user to login. */
30
+ onUnauthenticated?: (link: ParsedLink) => void;
31
+ onUnmatched?: (link: ParsedLink) => void;
32
+ }
33
+ /**
34
+ * Parse a deep-link URL. Returns null for anything that is not a usable URL so
35
+ * a hostile or truncated link can never throw on the native event path.
36
+ */
37
+ export declare const parseDeepLink: (url: string) => ParsedLink | null;
38
+ export declare const DeepLink: {
39
+ configure: (next: DeepLinkConfig) => void;
40
+ start: () => Promise<void>;
41
+ stop: () => void;
42
+ handleUrl: (url: string) => void;
43
+ setReady: (ready: boolean) => void;
44
+ /** Replay the stashed link, once. Call after a successful login. */
45
+ replayPending: () => void;
46
+ getPending: () => ParsedLink | null;
47
+ };
48
+ /**
49
+ * Configure and start the deep-link service for the lifetime of a component.
50
+ * The stored config delegates to the latest render, so new route closures do
51
+ * not tear down and re-subscribe the native listener.
52
+ */
53
+ export declare const useDeepLink: (deepLinkConfig: DeepLinkConfig) => void;
54
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,45 @@
1
+ import type { FileSystemLike } from '../index.js';
2
+ /**
3
+ * expo-file-system. Shape verified against 57.0.6, where the legacy function
4
+ * API (`downloadAsync`, `getInfoAsync`) still exists on the main entry point
5
+ * but every one of those functions THROWS at runtime; they now live behind
6
+ * `expo-file-system/legacy`. This adapter therefore uses the class API only.
7
+ *
8
+ * The REQUIRE lives in the entry point beside this file, never here.
9
+ */
10
+ interface ExpoFileLike {
11
+ readonly uri: string;
12
+ readonly exists: boolean;
13
+ readonly size: number;
14
+ delete: () => void;
15
+ }
16
+ interface ExpoDirectoryLike {
17
+ readonly uri: string;
18
+ create: (options?: {
19
+ intermediates?: boolean;
20
+ idempotent?: boolean;
21
+ }) => void;
22
+ }
23
+ interface ExpoDownloadTaskLike {
24
+ downloadAsync: () => Promise<ExpoFileLike | null>;
25
+ cancel: () => void;
26
+ }
27
+ export interface ExpoFileSystemModuleLike {
28
+ File: (new (uri: string) => ExpoFileLike) & {
29
+ createDownloadTask: (url: string, destination: ExpoFileLike, options?: {
30
+ headers?: Record<string, string>;
31
+ onProgress?: (progress: {
32
+ bytesWritten: number;
33
+ totalBytes: number;
34
+ }) => void;
35
+ }) => ExpoDownloadTaskLike;
36
+ };
37
+ Directory: new (uri: string) => ExpoDirectoryLike;
38
+ Paths: {
39
+ document: ExpoDirectoryLike;
40
+ cache: ExpoDirectoryLike;
41
+ };
42
+ }
43
+ export declare const adaptExpoFileSystem: (mod: unknown) => FileSystemLike | null;
44
+ export {};
45
+ //# sourceMappingURL=expoFs.d.ts.map
@@ -0,0 +1,3 @@
1
+ /** Coerce a driver-supplied size, which some report as a string. */
2
+ export declare const asNumber: (value: unknown) => number;
3
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1,76 @@
1
+ export interface FileSystemDirs {
2
+ documents: string;
3
+ cache: string;
4
+ downloads: string;
5
+ }
6
+ export interface FileSystemDownloadRequest {
7
+ url: string;
8
+ path: string;
9
+ headers: Record<string, string>;
10
+ onProgress?: (received: number, total: number) => void;
11
+ }
12
+ export interface FileSystemDownloadHandle {
13
+ promise: Promise<{
14
+ status?: number;
15
+ bytes?: number;
16
+ }>;
17
+ cancel?: () => void;
18
+ }
19
+ /**
20
+ * The slice of a native filesystem module this module actually uses. Both
21
+ * `react-native-blob-util` and `react-native-fs` are adapted onto it, and an app
22
+ * can inject its own with `setFileSystemImplementation`.
23
+ */
24
+ export interface FileSystemLike {
25
+ dirs: FileSystemDirs;
26
+ download: (request: FileSystemDownloadRequest) => FileSystemDownloadHandle;
27
+ exists: (path: string) => Promise<boolean>;
28
+ stat: (path: string) => Promise<{
29
+ size: number;
30
+ }>;
31
+ unlink: (path: string) => Promise<void>;
32
+ mkdir: (path: string) => Promise<void>;
33
+ }
34
+ export type DownloadDestination = 'documents' | 'cache' | 'downloads';
35
+ export interface DownloadOptions {
36
+ url: string;
37
+ fileName?: string;
38
+ destination?: DownloadDestination;
39
+ headers?: Record<string, string>;
40
+ authenticated?: boolean;
41
+ mimeType?: string;
42
+ onProgress?: (received: number, total: number, fraction: number) => void;
43
+ overwrite?: boolean;
44
+ signal?: AbortSignal;
45
+ }
46
+ export interface DownloadResult {
47
+ path: string;
48
+ bytes: number;
49
+ mimeType?: string;
50
+ fromCache: boolean;
51
+ }
52
+ export type DownloadErrorCode = 'unsupported' | 'permission-denied' | 'network' | 'http-error' | 'write-failed' | 'aborted' | 'empty-file';
53
+ export declare class DownloadError extends Error {
54
+ readonly code: DownloadErrorCode;
55
+ readonly status?: number;
56
+ constructor(code: DownloadErrorCode, message: string, status?: number);
57
+ }
58
+ /**
59
+ * Replace the filesystem backend. Intended for TESTS and for the showcase,
60
+ * which injects a denying backend to demo the permission error; an app gets
61
+ * expo-file-system without wiring anything. Pass `null` to restore it.
62
+ */
63
+ export declare const setFileSystemImplementation: (impl: FileSystemLike | null) => void;
64
+ /**
65
+ * Download a file to the device, attaching the stored bearer token by default.
66
+ *
67
+ * A finished download is always verified to be non-empty: a 0-byte file looks
68
+ * like a success to every caller and then wedges whatever consumes it, so it is
69
+ * deleted and reported as `empty-file`.
70
+ */
71
+ export declare const downloadFile: (options: DownloadOptions) => Promise<DownloadResult>;
72
+ /**
73
+ * Hand a downloaded file to the OS, to open in whichever app claims it.
74
+ */
75
+ export declare const openFile: (path: string, mimeType?: string) => Promise<void>;
76
+ //# sourceMappingURL=index.d.ts.map
@@ -4,5 +4,6 @@ export type { DateInput, DateStyle, TimeStyle } from './date.js';
4
4
  export { formatDate, formatDateTime, formatRelativeTime, formatTime } from './date.js';
5
5
  export { getInitials } from './initials.js';
6
6
  export { formatCompactNumber, formatNumber, formatPercent } from './number.js';
7
- export { formatPhone, normalizePhone } from './phone.js';
7
+ export type { FormatPhoneOptions, PhoneCountry, PhoneNumber } from './phone.js';
8
+ export { detectPhoneCountry, formatPhone, isValidPhoneNumber, normalizePhone, parsePhoneNumber, parsePhoneNumberFromString } from './phone.js';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1,13 +1,39 @@
1
+ /**
2
+ * Real cross-country validation, re-exported rather than reimplemented.
3
+ * formatPhone/normalizePhone/detectPhoneCountry below are a fast, dependency-
4
+ * light DISPLAY formatter for a fixed set of countries; they are not a
5
+ * validator. Real phone number plans have country-specific area-code and
6
+ * mobile-prefix rules that only a maintained metadata table gets right -
7
+ * hand-rolling that risks silently-wrong validation for a country nobody
8
+ * tests against. Both live here so consumers get one phone module, not two.
9
+ */
10
+ export type { PhoneNumber } from 'libphonenumber-js';
11
+ export { isValidPhoneNumber, parsePhoneNumber, parsePhoneNumberFromString } from 'libphonenumber-js';
12
+ export type PhoneCountry = 'IN' | 'US' | 'GB' | 'AE' | 'AU' | 'SG' | 'CA';
13
+ export interface FormatPhoneOptions {
14
+ defaultCountryCode?: string;
15
+ country?: PhoneCountry;
16
+ includeCountryCode?: boolean;
17
+ }
1
18
  /**
2
19
  * Strip everything except digits and a leading `+` (preserved if present).
3
20
  */
4
21
  export declare const normalizePhone: (input: string) => string;
22
+ /**
23
+ * Detect the country of a phone number: from its own `+` country code when
24
+ * present, otherwise from `defaultCountryCode`. Returns null when neither
25
+ * carries a recognised calling code.
26
+ */
27
+ export declare const detectPhoneCountry: (input: string, defaultCountryCode?: string) => PhoneCountry | null;
5
28
  /**
6
29
  * Format a phone number for human display.
7
30
  *
8
31
  * - Indian mobile (10 digits) → `XXXXX XXXXX`
9
32
  * - With `+91` country code → `+91 XXXXX XXXXX`
33
+ * - A number that already carries a recognised country code is grouped for
34
+ * that country regardless of `defaultCountryCode`.
10
35
  * - Other inputs are returned as `+CC NNNN NNNN…` best-effort grouping.
11
36
  */
12
- export declare const formatPhone: (input: string, defaultCountryCode?: string) => string;
37
+ export declare function formatPhone(input: string, defaultCountryCode?: string): string;
38
+ export declare function formatPhone(input: string, options?: FormatPhoneOptions): string;
13
39
  //# sourceMappingURL=phone.d.ts.map
@@ -0,0 +1,42 @@
1
+ import type { GeolocationLike } from '../index.js';
2
+ /**
3
+ * The slice of expo-location this adapter uses. Verified against 57.0.14.
4
+ *
5
+ * The REQUIRE lives in the entry point beside this file, never here.
6
+ */
7
+ export interface ExpoLocationObject {
8
+ coords?: {
9
+ latitude?: number;
10
+ longitude?: number;
11
+ altitude?: number | null;
12
+ accuracy?: number | null;
13
+ heading?: number | null;
14
+ speed?: number | null;
15
+ } | null;
16
+ timestamp?: number;
17
+ }
18
+ export interface ExpoLocationSubscription {
19
+ remove: () => void;
20
+ }
21
+ export interface ExpoLocationLike {
22
+ getCurrentPositionAsync: (options?: Record<string, unknown>) => Promise<ExpoLocationObject>;
23
+ watchPositionAsync: (options: Record<string, unknown>, callback: (location: ExpoLocationObject) => void) => Promise<ExpoLocationSubscription>;
24
+ getForegroundPermissionsAsync?: () => Promise<{
25
+ granted: boolean;
26
+ }>;
27
+ hasServicesEnabledAsync?: () => Promise<boolean>;
28
+ }
29
+ /**
30
+ * Adapts expo-location to the library's geolocation seam.
31
+ *
32
+ * The interesting half is `watchPosition`. The seam must hand back a numeric
33
+ * watch id SYNCHRONOUSLY, while `watchPositionAsync` only resolves its
34
+ * subscription later. So the adapter mints its own id immediately and stores
35
+ * the subscription against it on arrival. `clearWatch` called during that
36
+ * window records the id as cancelled, and the subscription is removed the
37
+ * moment it lands — without that, a screen that unmounts before the watch
38
+ * starts leaks a live GPS listener for the rest of the session, which is a
39
+ * battery drain nobody would attribute to this code.
40
+ */
41
+ export declare const adaptExpoLocation: (mod: ExpoLocationLike) => GeolocationLike;
42
+ //# sourceMappingURL=expoLocation.d.ts.map
@@ -0,0 +1,89 @@
1
+ export interface GeoCoordinates {
2
+ latitude: number;
3
+ longitude: number;
4
+ accuracy: number;
5
+ altitude: number | null;
6
+ heading: number | null;
7
+ speed: number | null;
8
+ }
9
+ export interface GeoPosition {
10
+ coords: GeoCoordinates;
11
+ timestamp: number;
12
+ }
13
+ export type GeoErrorCode = 'permission-denied' | 'position-unavailable' | 'timeout' | 'services-disabled' | 'unsupported';
14
+ export declare class GeolocationError extends Error {
15
+ readonly code: GeoErrorCode;
16
+ constructor(code: GeoErrorCode, message: string);
17
+ }
18
+ export interface GeoOptions {
19
+ /** `fine` maps to the platform's high-accuracy (GPS) provider. Default `fine`. */
20
+ accuracy?: 'fine' | 'coarse';
21
+ timeoutMs?: number;
22
+ maximumAgeMs?: number;
23
+ /** Extra attempts after the first, used only for `timeout` / `position-unavailable`. */
24
+ retries?: number;
25
+ /** A fix with a worse (larger) accuracy radius counts as `position-unavailable`. */
26
+ minimumAccuracyM?: number;
27
+ /**
28
+ * Pause between retries. Android's fused provider is frequently not ready on
29
+ * the first request after the permission grant or after location is switched
30
+ * on, and retrying immediately just re-collects the same failure.
31
+ */
32
+ retryDelayMs?: number;
33
+ }
34
+ export interface GeoWatchOptions extends GeoOptions {
35
+ /** Metres the device must move before the listener fires again. */
36
+ distanceFilterM?: number;
37
+ onError?: (error: GeolocationError) => void;
38
+ }
39
+ interface NativeGeoCoords {
40
+ latitude?: number | null;
41
+ longitude?: number | null;
42
+ accuracy?: number | null;
43
+ altitude?: number | null;
44
+ heading?: number | null;
45
+ speed?: number | null;
46
+ }
47
+ interface NativeGeoPosition {
48
+ coords?: NativeGeoCoords | null;
49
+ timestamp?: number | null;
50
+ }
51
+ interface NativeGeoError {
52
+ code?: number;
53
+ message?: string;
54
+ }
55
+ interface NativeGeoOptions {
56
+ enableHighAccuracy?: boolean;
57
+ timeout?: number;
58
+ maximumAge?: number;
59
+ distanceFilter?: number;
60
+ }
61
+ /** Minimal shape of the native geolocation module we depend on. */
62
+ export interface GeolocationLike {
63
+ getCurrentPosition: (onSuccess: (position: NativeGeoPosition) => void, onError?: (error: NativeGeoError) => void, options?: NativeGeoOptions) => void;
64
+ watchPosition: (onSuccess: (position: NativeGeoPosition) => void, onError?: (error: NativeGeoError) => void, options?: NativeGeoOptions) => number;
65
+ clearWatch: (watchId: number) => void;
66
+ }
67
+ /**
68
+ * Replace the geolocation provider. Intended for TESTS and for the showcase,
69
+ * which injects a denying provider to demo the permission and services-off
70
+ * errors; an app gets expo-location without wiring anything. Pass `null` to
71
+ * restore it.
72
+ */
73
+ export declare const setGeolocationImplementation: (impl: GeolocationLike | null) => void;
74
+ export declare const Geolocation: {
75
+ getCurrentPosition: (options?: GeoOptions) => Promise<GeoPosition>;
76
+ watchPosition: (listener: (position: GeoPosition) => void, options?: GeoWatchOptions) => (() => void);
77
+ isAvailable: () => boolean;
78
+ };
79
+ export interface UseCurrentPositionResult {
80
+ position: GeoPosition | null;
81
+ error: GeolocationError | null;
82
+ loading: boolean;
83
+ refresh: () => void;
84
+ }
85
+ export declare const useCurrentPosition: (options?: GeoOptions & {
86
+ enabled?: boolean;
87
+ }) => UseCurrentPositionResult;
88
+ export {};
89
+ //# sourceMappingURL=index.d.ts.map