@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
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ import KvStore from 'expo-sqlite/kv-store';
3
4
  import { Logger } from "../logger/index.js";
4
5
 
5
6
  /**
@@ -7,25 +8,31 @@ import { Logger } from "../logger/index.js";
7
8
  * Lets us avoid a hard import on `@react-native-async-storage/async-storage`.
8
9
  */
9
10
 
11
+ /** Only what a test or the showcase injected; null means use expo-sqlite/kv-store. */
10
12
  let backend = null;
11
- let resolved = false;
12
- const resolveBackend = () => {
13
- if (resolved) {
14
- return backend;
15
- }
16
- resolved = true;
17
- try {
18
- const mod = require('@react-native-async-storage/async-storage');
19
- backend = mod.default ?? mod;
20
- } catch {
21
- backend = null;
22
- }
23
- return backend;
24
- };
13
+
14
+ /**
15
+ * expo-sqlite's key/value store, imported directly.
16
+ *
17
+ * It deliberately implements the AsyncStorage v1 surface — `getItem`,
18
+ * `setItem`, `removeItem`, `multiGet`, `multiSet`, `multiRemove`, `clear`,
19
+ * `getAllKeys`, with v1's shapes — which is exactly what this module already
20
+ * speaks, so there is nothing to convert. Verified against expo-sqlite 57.0.2.
21
+ */
22
+ const resolveBackend = () => backend ?? KvStore;
23
+
24
+ /**
25
+ * Always true now that the backend ships with the library. Kept because callers
26
+ * whose use of storage is an OPTIMISATION branch on it, and removing it would
27
+ * be a breaking change for no gain.
28
+ */
29
+ export const isStorageAvailable = () => resolveBackend() !== null;
25
30
  const requireBackend = op => {
26
31
  const b = resolveBackend();
27
32
  if (!b) {
28
- const err = new Error(`[Storage] @react-native-async-storage/async-storage is not installed; cannot ${op}. ` + 'Install the peer dep or call setStorageImplementation() with a custom backend.');
33
+ // Only reachable if a caller injected something falsy; the default backend
34
+ // ships with the library, so there is no "not installed" case any more.
35
+ const err = new Error(`[Storage] no storage backend available; cannot ${op}.`);
29
36
  Logger.error(err.message);
30
37
  throw err;
31
38
  }
@@ -33,12 +40,11 @@ const requireBackend = op => {
33
40
  };
34
41
 
35
42
  /**
36
- * Inject a custom storage backend (e.g. MMKV adapter) at runtime.
37
- * Pass `null` to reset to the default AsyncStorage backend.
43
+ * Replace the storage backend. Intended for TESTS and for the showcase; an app
44
+ * gets expo-sqlite/kv-store without wiring anything. Pass `null` to restore it.
38
45
  */
39
46
  export const setStorageImplementation = impl => {
40
47
  backend = impl;
41
- resolved = true;
42
48
  };
43
49
  const safeJsonParse = raw => {
44
50
  if (raw == null) {
@@ -92,6 +98,10 @@ const setJson = async (key, value) => {
92
98
  const multiGet = async keys => {
93
99
  try {
94
100
  const b = requireBackend('multiGet');
101
+ // v3 first: its Record is already this function's return shape.
102
+ if (b.getMany) {
103
+ return await b.getMany([...keys]);
104
+ }
95
105
  if (!b.multiGet) {
96
106
  const out = {};
97
107
  await Promise.all(keys.map(async k => {
@@ -113,6 +123,11 @@ const multiGet = async keys => {
113
123
  const multiSet = async entries => {
114
124
  try {
115
125
  const b = requireBackend('multiSet');
126
+ // v3 first: it takes the Record this function already receives.
127
+ if (b.setMany) {
128
+ await b.setMany(entries);
129
+ return true;
130
+ }
116
131
  const pairs = Object.entries(entries);
117
132
  if (!b.multiSet) {
118
133
  await Promise.all(pairs.map(([k, v]) => b.setItem(k, v)));
@@ -128,6 +143,10 @@ const multiSet = async entries => {
128
143
  const multiRemove = async keys => {
129
144
  try {
130
145
  const b = requireBackend('multiRemove');
146
+ if (b.removeMany) {
147
+ await b.removeMany([...keys]);
148
+ return true;
149
+ }
131
150
  if (!b.multiRemove) {
132
151
  await Promise.all(keys.map(k => b.removeItem(k)));
133
152
  return true;
@@ -171,7 +190,7 @@ const getAllKeys = async () => {
171
190
  * All operations swallow errors (logging them) and return a falsy result
172
191
  * so that storage problems never crash the app.
173
192
  *
174
- * Backed by `@react-native-async-storage/async-storage` by default;
193
+ * Backed by `expo-sqlite/kv-store` by default;
175
194
  * override with `setStorageImplementation()` for MMKV or custom adapters.
176
195
  */
177
196
  export const Storage = {
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * The expo-application jest mock is exposed on globalThis so a test can blank a
5
+ * value to reach the "no identity anywhere" path. expo ships these as module
6
+ * constants, which cannot be reassigned through the ES binding from a test file.
7
+ */
8
+
9
+ export {};
10
+ //# sourceMappingURL=globals.d.js.map
@@ -1,20 +1,56 @@
1
1
  "use strict";
2
2
 
3
+ import { ImageManipulator } from 'expo-image-manipulator';
3
4
  import { Logger } from "../logger/index.js";
4
- let compressor = null;
5
- let resolved = false;
6
- const resolveCompressor = () => {
7
- if (resolved) {
8
- return compressor;
9
- }
10
- resolved = true;
11
- try {
12
- compressor = require('react-native-compressor');
13
- } catch {
14
- Logger.warn('[imageCompression] react-native-compressor is not installed; ' + 'compressImage() will return the original URI unchanged.');
15
- compressor = null;
5
+ /** expo's SaveFormat values, inlined to keep this file free of enum imports. */
6
+ const EXPO_FORMAT = {
7
+ jpg: 'jpeg',
8
+ png: 'png',
9
+ webp: 'webp'
10
+ };
11
+
12
+ /**
13
+ * expo-image-manipulator, adapted to the compressor shape this module speaks.
14
+ *
15
+ * Written against the CONTEXTUAL api rather than `manipulateAsync`, which 57
16
+ * marks deprecated. Only ONE edge is constrained: expo treats a given width and
17
+ * height as targets and would distort the image, where maxWidth/maxHeight here
18
+ * mean a box to fit inside. The smaller bound is used so the result fits
19
+ * whichever way round the image is.
20
+ */
21
+ const expoCompressorBackend = {
22
+ Image: {
23
+ compress: async (uri, options) => {
24
+ const context = ImageManipulator.manipulate(uri);
25
+ const {
26
+ maxWidth,
27
+ maxHeight
28
+ } = options;
29
+ if (typeof maxWidth === 'number' || typeof maxHeight === 'number') {
30
+ context.resize({
31
+ width: Math.min(maxWidth ?? Number.POSITIVE_INFINITY, maxHeight ?? Number.POSITIVE_INFINITY)
32
+ });
33
+ }
34
+ const image = await context.renderAsync();
35
+ const result = await image.saveAsync({
36
+ compress: options.quality,
37
+ format: EXPO_FORMAT[options.output ?? 'jpg'] ?? 'jpeg'
38
+ });
39
+ return result.uri;
40
+ }
16
41
  }
17
- return compressor;
42
+ };
43
+
44
+ /** Only what a test or the showcase injected; null means use expo-image-manipulator. */
45
+ let compressor = null;
46
+ const resolveCompressor = () => compressor ?? expoCompressorBackend;
47
+
48
+ /**
49
+ * Replace the compressor. Intended for TESTS and for the showcase; an app gets
50
+ * expo-image-manipulator without wiring anything. Pass `null` to restore it.
51
+ */
52
+ export const setImageCompressorImplementation = impl => {
53
+ compressor = impl;
18
54
  };
19
55
  const EIGHT_MB = 8 * 1024 * 1024;
20
56
  const THREE_MB = 3 * 1024 * 1024;
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
 
3
- export { compressImage } from "./imageCompression.js";
3
+ export { compressImage, setImageCompressorImplementation } from "./imageCompression.js";
4
4
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ import * as Application from 'expo-application';
3
4
  import { useEffect, useState } from 'react';
4
5
  // Type-only — erased at compile time; react-native stays lazily required below.
5
6
 
@@ -17,6 +18,26 @@ import { Logger } from "../logger/index.js";
17
18
  * wire two config keys.
18
19
  */
19
20
 
21
+ /**
22
+ * expo-application, read at call time.
23
+ *
24
+ * `applicationId` and `nativeApplicationVersion` are module CONSTANTS rather
25
+ * than functions, so they are read inside the accessors rather than captured
26
+ * here — capturing would snapshot them before the native module has
27
+ * initialised.
28
+ *
29
+ * `setConfig({ appId, appVersion })` still WINS over this: a Webority app
30
+ * already carries both in appsettings.Compiled.json, and config is checked
31
+ * first. This is the fallback for an app that would rather read its identity
32
+ * from the binary it is actually running, which is the one place the two can
33
+ * disagree.
34
+ */
35
+ const expoApplicationBackend = {
36
+ getBundleId: () => Application.applicationId ?? '',
37
+ getVersion: () => Application.nativeApplicationVersion ?? ''
38
+ };
39
+
40
+ /** Only what a test or the showcase injected; null means use expo-application. */
20
41
  let deviceInfo = null;
21
42
 
22
43
  /**
@@ -46,8 +67,9 @@ const getBundleId = () => {
46
67
  if (fromConfig) {
47
68
  return fromConfig;
48
69
  }
49
- if (deviceInfo) {
50
- return deviceInfo.getBundleId();
70
+ const fromDevice = (deviceInfo ?? expoApplicationBackend).getBundleId();
71
+ if (fromDevice) {
72
+ return fromDevice;
51
73
  }
52
74
  throw missing('store identifier', 'appId');
53
75
  };
@@ -56,8 +78,9 @@ const getVersion = () => {
56
78
  if (fromConfig) {
57
79
  return fromConfig;
58
80
  }
59
- if (deviceInfo) {
60
- return deviceInfo.getVersion();
81
+ const fromDevice = (deviceInfo ?? expoApplicationBackend).getVersion();
82
+ if (fromDevice) {
83
+ return fromDevice;
61
84
  }
62
85
  throw missing('version', 'appVersion');
63
86
  };
@@ -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';
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';
4
4
  export { formatDate, formatDateTime, formatRelativeTime, formatTime } from './date';
5
5
  export { getInitials } from './initials';
6
6
  export { formatCompactNumber, formatNumber, formatPercent } from './number';
7
- export { formatPhone, normalizePhone } from './phone';
7
+ export type { FormatPhoneOptions, PhoneCountry, PhoneNumber } from './phone';
8
+ export { detectPhoneCountry, formatPhone, isValidPhoneNumber, normalizePhone, parsePhoneNumber, parsePhoneNumberFromString } from './phone';
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