@webority/mobile-core 0.111.0 → 0.112.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.
package/README.md CHANGED
@@ -29,6 +29,8 @@ import { Geolocation } from '@webority/mobile-core/geolocation';
29
29
  import { openDatabase } from '@webority/mobile-core/sqlite';
30
30
  import { downloadFile } from '@webority/mobile-core/download';
31
31
  import { shareFile } from '@webority/mobile-core/share';
32
+ import { fetchAttachment, fetchPreviewAttachment, previewKind } from '@webority/mobile-core/attachments';
33
+ import { getOrCreateInstallationId, clearTokensOnFirstLaunch } from '@webority/mobile-core/installation';
32
34
  import { requestReviewAfterSuccess } from '@webority/mobile-core/review';
33
35
  import { initPush, enablePush, usePush } from '@webority/mobile-core/push';
34
36
  import { definePushActionTask } from '@webority/mobile-core/push/task';
@@ -229,7 +231,7 @@ await userClient.get('/status', { skipErrorLog: true });
229
231
  JWT-shaped strings, and the values of keys named `token`, `accessToken`, `refreshToken`,
230
232
  `password` and `authorization` (any case) in objects and JSON strings.
231
233
 
232
- `configureLogger({ redactKeys })` adds app keys to the built-in masked list and never removes one. `Logger.getBreadcrumbs()` returns the last 50 breadcrumbs, oldest first, already redacted; `configureLogger({ breadcrumbLimit })` changes the cap. `initWebority` takes a required `remoteLogger`: the sink, or `null` for a build without crash reporting.
234
+ `configureLogger({ redactKeys })` adds app keys to the built-in masked list and never removes one. `Logger.getBreadcrumbs()` returns the last 50 breadcrumbs, oldest first, already redacted; `configureLogger({ breadcrumbLimit })` changes the cap. `initWebority` takes a required `remoteLogger`: the sink, or `null` for a build without crash reporting. It also takes a required `appId`, the compiled settings' app id, and refuses to start when it differs from the installed app's id: a bundle prebuilt for another environment stops at start-up with the prebuild step to run.
233
235
 
234
236
  ## Metro bridge
235
237
 
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.previewKind=exports.mimeTypeFromFileName=exports.fetchPreviewAttachment=exports.fetchAttachment=void 0;var _expoFileSystem=require("expo-file-system"),_userClient=_interopRequireDefault(require("../api/userClient.js")),_index=require("../download/index.js"),_index2=require("../logger/index.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const EXTENSION_TYPES={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp",gif:"image/gif"},mimeTypeFromFileName=e=>{const t=(e??"").split(".").pop()?.toLowerCase()??"";return Object.hasOwn(EXTENSION_TYPES,t)?EXTENSION_TYPES[t]:void 0};exports.mimeTypeFromFileName=mimeTypeFromFileName;const fetchAttachment=({url:e,fileName:t,mimeType:i})=>(0,_index.downloadFile)({client:_userClient.default,url:e,fileName:t,mimeType:i??mimeTypeFromFileName(t),destination:"cache",overwrite:!0});exports.fetchAttachment=fetchAttachment;const PREVIEW_LIMIT=20,previewCache=new Map,previewFolderName=e=>e.replace(/[^A-Za-z0-9_-]/g,"_"),forgetPreview=e=>{try{new _expoFileSystem.Directory(e.folder).delete()}catch(e){_index2.Logger.warn("Removing an old preview failed:",e)}},fetchPreviewAttachment=async({cacheId:e,version:t,url:i,fileName:r,mimeType:p})=>{if(!t)return fetchAttachment({url:i,fileName:r,mimeType:p});const n=`${e}@${t}`,o=previewCache.get(n);if(o&&new _expoFileSystem.File(o.path).exists)return previewCache.delete(n),previewCache.set(n,o),o;previewCache.delete(n);const a=await fetchAttachment({url:i,fileName:r,mimeType:p}),m=new _expoFileSystem.File(a.path),c=new _expoFileSystem.Directory(_expoFileSystem.Paths.cache,"previews",previewFolderName(n));c.exists&&c.delete(),c.create({intermediates:!0,idempotent:!0}),await m.move(new _expoFileSystem.File(c,m.name));const l={path:new _expoFileSystem.File(c,m.name).uri,mimeType:a.mimeType,folder:c.uri};if(previewCache.set(n,l),previewCache.size>20){const e=previewCache.entries().next().value;e&&(previewCache.delete(e[0]),forgetPreview(e[1]))}return l};exports.fetchPreviewAttachment=fetchPreviewAttachment;const previewKind=e=>{const{contentType:t,name:i}=e,r=(i??"").split(".").pop()?.toLowerCase()??"",p=(t??r).toLowerCase();return p.includes("pdf")?"pdf":["png","jpeg","jpg","webp","gif"].some(e=>p.includes(e))?"image":null};exports.previewKind=previewKind;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.initWebority=void 0;var _authStore=require("./auth/authStore.js"),_index=require("./config/index.js"),_index2=require("./logger/index.js"),_networkStatus=require("./network/networkStatus.js"),_index3=require("./storage/index.js");const initWebority=e=>{if(!e.baseUrl)throw new Error("baseUrl is required for Webority packages initialization");if(void 0===e.remoteLogger)throw new Error("remoteLogger is required for Webority packages initialization: pass a RemoteLogger, or null for a build without crash reporting");(0,_index2.setRemoteLogger)(e.remoteLogger),(0,_index.setConfig)({baseUrl:e.baseUrl,loggingEnabled:e.loggingEnabled??!0,logLevel:e.logLevel||_index.DEFAULT_LOG_LEVEL,environment:e.environment||"Development",country:(e.country||"in").toLowerCase(),appId:e.appId??null,appVersion:e.appVersion??null,commit:e.commit??null,appBuild:e.appBuild??null,appStoreId:e.appStoreId??null,logCurl:e.logCurl??!1,timeoutMs:e.timeoutMs??_index.DEFAULT_TIMEOUT_MS,retries:e.retries??_index.DEFAULT_RETRIES});const t=e.backends;t?.storage&&(0,_index3.setStorageImplementation)(t.storage),t?.keychain&&(0,_authStore.setKeychainImplementation)(t.keychain),t?.network&&(0,_networkStatus.setNetworkStatusImplementation)(t.network),_index2.Logger.info("[@webority/mobile-core] initialized")};exports.initWebority=initWebority;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.initWebority=void 0;var cache,Application=_interopRequireWildcard(require("expo-application")),_authStore=require("./auth/authStore.js"),_index=require("./config/index.js"),_index2=require("./logger/index.js"),_networkStatus=require("./network/networkStatus.js"),_index3=require("./storage/index.js");function _interopRequireWildcard(e,r){if(!r&&e&&e.__esModule)return e;var t,i,o=Object.defineProperty,n={__proto__:null,default:e};if(Object(e)!==e)return n;if(cache||"function"!=typeof WeakMap||(cache=new WeakMap),cache){if(cache.has(e))return cache.get(e);cache.set(e,n)}for(i in e)"default"!==i&&{}.hasOwnProperty.call(e,i)&&((t=o&&Object.getOwnPropertyDescriptor(e,i))&&(t.get||t.set)?o(n,i,t):n[i]=e[i]);return n}const initWebority=e=>{if(!e.baseUrl)throw new Error("baseUrl is required for Webority packages initialisation");if(void 0===e.remoteLogger)throw new Error("remoteLogger is required for Webority packages initialisation: pass a RemoteLogger, or null for a build without crash reporting");if(!e.appId)throw new Error("appId is required for Webority packages initialisation");const r=Application.applicationId;if(null!==r&&e.appId!==r)throw new Error(`This build's settings are for ${e.appId} but the installed app is ${r}. Run mobile-tools prebuild for the right environment, then rebuild the bundle.`);(0,_index2.setRemoteLogger)(e.remoteLogger),(0,_index.setConfig)({baseUrl:e.baseUrl,loggingEnabled:e.loggingEnabled??!0,logLevel:e.logLevel||_index.DEFAULT_LOG_LEVEL,environment:e.environment||"Development",country:(e.country||"in").toLowerCase(),appId:e.appId,appVersion:e.appVersion??null,commit:e.commit??null,appBuild:e.appBuild??null,appStoreId:e.appStoreId??null,logCurl:e.logCurl??!1,timeoutMs:e.timeoutMs??_index.DEFAULT_TIMEOUT_MS,retries:e.retries??_index.DEFAULT_RETRIES});const t=e.backends;t?.storage&&(0,_index3.setStorageImplementation)(t.storage),t?.keychain&&(0,_authStore.setKeychainImplementation)(t.keychain),t?.network&&(0,_networkStatus.setNetworkStatusImplementation)(t.network),_index2.Logger.info("[@webority/mobile-core] initialized")};exports.initWebority=initWebority;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOrCreateInstallationId=exports.clearTokensOnFirstLaunch=exports.INSTALLATION_STORAGE_KEYS=void 0;var _expoCrypto=require("expo-crypto"),_reactNative=require("react-native"),_authStore=require("../auth/authStore.js"),_index=require("../logger/index.js"),_index2=require("../storage/index.js");const INSTALLATION_STORAGE_KEYS=exports.INSTALLATION_STORAGE_KEYS={HAS_LAUNCHED_BEFORE:"hasLaunchedBefore",INSTALLATION_ID:"installationId"},getOrCreateInstallationId=async()=>{const e=await _index2.Storage.getItem(INSTALLATION_STORAGE_KEYS.INSTALLATION_ID);if(e)return e;const t=(0,_expoCrypto.randomUUID)();if(!await _index2.Storage.setItem(INSTALLATION_STORAGE_KEYS.INSTALLATION_ID,t))throw new Error("Could not persist the installation id");return t};exports.getOrCreateInstallationId=getOrCreateInstallationId;const clearTokensOnFirstLaunch=async()=>{if("ios"!==_reactNative.Platform.OS)return;null===await _index2.Storage.getItem(INSTALLATION_STORAGE_KEYS.HAS_LAUNCHED_BEFORE)&&(_index.Logger.info("First launch after install: clearing stored tokens"),await(0,_authStore.removeToken)(_authStore.ACCESS_TOKEN_KEY),await(0,_authStore.removeToken)(_authStore.REFRESH_TOKEN_KEY),await _index2.Storage.setItem(INSTALLATION_STORAGE_KEYS.HAS_LAUNCHED_BEFORE,"true"))};exports.clearTokensOnFirstLaunch=clearTokensOnFirstLaunch;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"SERIALIZABLE_IGNORED_ACTION_PATHS",{enumerable:!0,get:function(){return _serializableCheck.SERIALIZABLE_IGNORED_ACTION_PATHS}}),Object.defineProperty(exports,"createRootReducer",{enumerable:!0,get:function(){return _rootReducer.createRootReducer}}),Object.defineProperty(exports,"errorToastMiddleware",{enumerable:!0,get:function(){return _errorToastMiddleware.errorToastMiddleware}}),Object.defineProperty(exports,"isSilentRejection",{enumerable:!0,get:function(){return _errorToastMiddleware.isSilentRejection}}),Object.defineProperty(exports,"resetState",{enumerable:!0,get:function(){return _rootReducer.resetState}});var _errorToastMiddleware=require("./errorToastMiddleware.js"),_rootReducer=require("./rootReducer.js"),_serializableCheck=require("./serializableCheck.js");
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"SERIALIZABLE_IGNORED_ACTION_PATHS",{enumerable:!0,get:function(){return _serializableCheck.SERIALIZABLE_IGNORED_ACTION_PATHS}}),Object.defineProperty(exports,"createRootReducer",{enumerable:!0,get:function(){return _rootReducer.createRootReducer}}),Object.defineProperty(exports,"createSessionMiddleware",{enumerable:!0,get:function(){return _sessionMiddleware.createSessionMiddleware}}),Object.defineProperty(exports,"errorToastMiddleware",{enumerable:!0,get:function(){return _errorToastMiddleware.errorToastMiddleware}}),Object.defineProperty(exports,"isSilentRejection",{enumerable:!0,get:function(){return _errorToastMiddleware.isSilentRejection}}),Object.defineProperty(exports,"resetState",{enumerable:!0,get:function(){return _rootReducer.resetState}});var _errorToastMiddleware=require("./errorToastMiddleware.js"),_rootReducer=require("./rootReducer.js"),_serializableCheck=require("./serializableCheck.js"),_sessionMiddleware=require("./sessionMiddleware.js");
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.createSessionMiddleware=void 0;const isAction=e=>"object"==typeof e&&null!==e&&"type"in e,thunkMeta=e=>isAction(e)&&"object"==typeof e.meta&&null!==e.meta?e.meta:{},createSessionMiddleware=e=>{const t=new Set,n=new Set;return()=>r=>s=>{const{requestId:i,requestStatus:o}=thunkMeta(s);if(!i||!o)return r(s);if("pending"===o){if(isAction(s)&&e.isSignOut(s)){for(const e of t)n.add(e);t.clear()}else t.add(i);return r(s)}return t.delete(i),n.delete(i)?s:r(s)}};exports.createSessionMiddleware=createSessionMiddleware;
@@ -0,0 +1 @@
1
+ "use strict";import{Directory,File,Paths}from"expo-file-system";import userClient from"../api/userClient.js";import{downloadFile}from"../download/index.js";import{Logger}from"../logger/index.js";const EXTENSION_TYPES={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp",gif:"image/gif"};export const mimeTypeFromFileName=e=>{const t=(e??"").split(".").pop()?.toLowerCase()??"";return Object.hasOwn(EXTENSION_TYPES,t)?EXTENSION_TYPES[t]:void 0};export const fetchAttachment=({url:e,fileName:t,mimeType:i})=>downloadFile({client:userClient,url:e,fileName:t,mimeType:i??mimeTypeFromFileName(t),destination:"cache",overwrite:!0});const PREVIEW_LIMIT=20,previewCache=new Map,previewFolderName=e=>e.replace(/[^A-Za-z0-9_-]/g,"_"),forgetPreview=e=>{try{new Directory(e.folder).delete()}catch(e){Logger.warn("Removing an old preview failed:",e)}};export const fetchPreviewAttachment=async({cacheId:e,version:t,url:i,fileName:o,mimeType:p})=>{if(!t)return fetchAttachment({url:i,fileName:o,mimeType:p});const r=`${e}@${t}`,n=previewCache.get(r);if(n&&new File(n.path).exists)return previewCache.delete(r),previewCache.set(r,n),n;previewCache.delete(r);const a=await fetchAttachment({url:i,fileName:o,mimeType:p}),m=new File(a.path),c=new Directory(Paths.cache,"previews",previewFolderName(r));c.exists&&c.delete(),c.create({intermediates:!0,idempotent:!0}),await m.move(new File(c,m.name));const l={path:new File(c,m.name).uri,mimeType:a.mimeType,folder:c.uri};if(previewCache.set(r,l),previewCache.size>20){const e=previewCache.entries().next().value;e&&(previewCache.delete(e[0]),forgetPreview(e[1]))}return l};export const previewKind=e=>{const{contentType:t,name:i}=e,o=(i??"").split(".").pop()?.toLowerCase()??"",p=(t??o).toLowerCase();return p.includes("pdf")?"pdf":["png","jpeg","jpg","webp","gif"].some(e=>p.includes(e))?"image":null};
@@ -1 +1 @@
1
- "use strict";import{setKeychainImplementation}from"./auth/authStore.js";import{DEFAULT_LOG_LEVEL,DEFAULT_RETRIES,DEFAULT_TIMEOUT_MS,setConfig}from"./config/index.js";import{Logger,setRemoteLogger}from"./logger/index.js";import{setNetworkStatusImplementation}from"./network/networkStatus.js";import{setStorageImplementation}from"./storage/index.js";export const initWebority=e=>{if(!e.baseUrl)throw new Error("baseUrl is required for Webority packages initialization");if(void 0===e.remoteLogger)throw new Error("remoteLogger is required for Webority packages initialization: pass a RemoteLogger, or null for a build without crash reporting");setRemoteLogger(e.remoteLogger),setConfig({baseUrl:e.baseUrl,loggingEnabled:e.loggingEnabled??!0,logLevel:e.logLevel||DEFAULT_LOG_LEVEL,environment:e.environment||"Development",country:(e.country||"in").toLowerCase(),appId:e.appId??null,appVersion:e.appVersion??null,commit:e.commit??null,appBuild:e.appBuild??null,appStoreId:e.appStoreId??null,logCurl:e.logCurl??!1,timeoutMs:e.timeoutMs??DEFAULT_TIMEOUT_MS,retries:e.retries??DEFAULT_RETRIES});const o=e.backends;o?.storage&&setStorageImplementation(o.storage),o?.keychain&&setKeychainImplementation(o.keychain),o?.network&&setNetworkStatusImplementation(o.network),Logger.info("[@webority/mobile-core] initialized")};
1
+ "use strict";import*as Application from"expo-application";import{setKeychainImplementation}from"./auth/authStore.js";import{DEFAULT_LOG_LEVEL,DEFAULT_RETRIES,DEFAULT_TIMEOUT_MS,setConfig}from"./config/index.js";import{Logger,setRemoteLogger}from"./logger/index.js";import{setNetworkStatusImplementation}from"./network/networkStatus.js";import{setStorageImplementation}from"./storage/index.js";export const initWebority=e=>{if(!e.baseUrl)throw new Error("baseUrl is required for Webority packages initialisation");if(void 0===e.remoteLogger)throw new Error("remoteLogger is required for Webority packages initialisation: pass a RemoteLogger, or null for a build without crash reporting");if(!e.appId)throw new Error("appId is required for Webority packages initialisation");const o=Application.applicationId;if(null!==o&&e.appId!==o)throw new Error(`This build's settings are for ${e.appId} but the installed app is ${o}. Run mobile-tools prebuild for the right environment, then rebuild the bundle.`);setRemoteLogger(e.remoteLogger),setConfig({baseUrl:e.baseUrl,loggingEnabled:e.loggingEnabled??!0,logLevel:e.logLevel||DEFAULT_LOG_LEVEL,environment:e.environment||"Development",country:(e.country||"in").toLowerCase(),appId:e.appId,appVersion:e.appVersion??null,commit:e.commit??null,appBuild:e.appBuild??null,appStoreId:e.appStoreId??null,logCurl:e.logCurl??!1,timeoutMs:e.timeoutMs??DEFAULT_TIMEOUT_MS,retries:e.retries??DEFAULT_RETRIES});const t=e.backends;t?.storage&&setStorageImplementation(t.storage),t?.keychain&&setKeychainImplementation(t.keychain),t?.network&&setNetworkStatusImplementation(t.network),Logger.info("[@webority/mobile-core] initialized")};
@@ -0,0 +1 @@
1
+ "use strict";import{randomUUID}from"expo-crypto";import{Platform}from"react-native";import{ACCESS_TOKEN_KEY,REFRESH_TOKEN_KEY,removeToken}from"../auth/authStore.js";import{Logger}from"../logger/index.js";import{Storage}from"../storage/index.js";export const INSTALLATION_STORAGE_KEYS={HAS_LAUNCHED_BEFORE:"hasLaunchedBefore",INSTALLATION_ID:"installationId"};export const getOrCreateInstallationId=async()=>{const t=await Storage.getItem(INSTALLATION_STORAGE_KEYS.INSTALLATION_ID);if(t)return t;const e=randomUUID();if(!await Storage.setItem(INSTALLATION_STORAGE_KEYS.INSTALLATION_ID,e))throw new Error("Could not persist the installation id");return e};export const clearTokensOnFirstLaunch=async()=>{if("ios"!==Platform.OS)return;null===await Storage.getItem(INSTALLATION_STORAGE_KEYS.HAS_LAUNCHED_BEFORE)&&(Logger.info("First launch after install: clearing stored tokens"),await removeToken(ACCESS_TOKEN_KEY),await removeToken(REFRESH_TOKEN_KEY),await Storage.setItem(INSTALLATION_STORAGE_KEYS.HAS_LAUNCHED_BEFORE,"true"))};
@@ -1 +1 @@
1
- "use strict";export{errorToastMiddleware,isSilentRejection}from"./errorToastMiddleware.js";export{createRootReducer,resetState}from"./rootReducer.js";export{SERIALIZABLE_IGNORED_ACTION_PATHS}from"./serializableCheck.js";
1
+ "use strict";export{errorToastMiddleware,isSilentRejection}from"./errorToastMiddleware.js";export{createRootReducer,resetState}from"./rootReducer.js";export{SERIALIZABLE_IGNORED_ACTION_PATHS}from"./serializableCheck.js";export{createSessionMiddleware}from"./sessionMiddleware.js";
@@ -0,0 +1 @@
1
+ "use strict";const isAction=e=>"object"==typeof e&&null!==e&&"type"in e,thunkMeta=e=>isAction(e)&&"object"==typeof e.meta&&null!==e.meta?e.meta:{};export const createSessionMiddleware=e=>{const t=new Set,n=new Set;return()=>s=>i=>{const{requestId:o,requestStatus:r}=thunkMeta(i);if(!o||!r)return s(i);if("pending"===r){if(isAction(i)&&e.isSignOut(i)){for(const e of t)n.add(e);t.clear()}else t.add(o);return s(i)}return t.delete(o),n.delete(o)?i:s(i)}};
@@ -0,0 +1,37 @@
1
+ import { type DownloadResult } from "../download";
2
+ /**
3
+ * The content type a file name's extension stands for. Files that arrive with a name only need a type to be saved
4
+ * properly and to offer the right apps for them.
5
+ */
6
+ export declare const mimeTypeFromFileName: (fileName: string | null | undefined) => string | undefined;
7
+ export interface AttachmentRequest {
8
+ url: string;
9
+ fileName: string;
10
+ mimeType?: string | null;
11
+ }
12
+ /**
13
+ * Fetches a file through the signed-in client into the app's cache, where a viewer or a save reads it. Answers with
14
+ * its local `path` and `mimeType`; a file with no content type of its own takes the one its name stands for.
15
+ */
16
+ export declare const fetchAttachment: ({ url, fileName, mimeType }: AttachmentRequest) => Promise<DownloadResult>;
17
+ export interface PreviewAttachment {
18
+ path: string;
19
+ mimeType?: string;
20
+ folder: string;
21
+ }
22
+ /**
23
+ * fetchAttachment for a preview: a document already downloaded this session opens from the phone with no second
24
+ * download. The document's `version` (its updated time) is part of the key, so a replaced document is fetched again;
25
+ * without a version the file is not kept at all.
26
+ */
27
+ export declare const fetchPreviewAttachment: ({ cacheId, version, url, fileName, mimeType, }: AttachmentRequest & {
28
+ cacheId: string;
29
+ version?: string | null;
30
+ }) => Promise<PreviewAttachment | DownloadResult>;
31
+ export type PreviewKind = "image" | "pdf";
32
+ /** What the app can show itself, from a content type or a file name: "image", "pdf", or null for anything else. */
33
+ export declare const previewKind: (file: {
34
+ contentType?: string | null;
35
+ name?: string | null;
36
+ }) => PreviewKind | null;
37
+ //# sourceMappingURL=index.d.ts.map
@@ -10,6 +10,8 @@
10
10
  * @webority/mobile-core/geolocation a geolocation provider
11
11
  * @webority/mobile-core/sqlite expo-sqlite
12
12
  * @webority/mobile-core/download a filesystem module
13
+ * @webority/mobile-core/installation installation id (expo-crypto)
14
+ * @webority/mobile-core/attachments fetch, preview cache and preview kind (expo-file-system)
13
15
  * @webority/mobile-core/share expo-sharing
14
16
  * @webority/mobile-core/review expo-store-review
15
17
  * @webority/mobile-core/secureScreen expo-screen-capture
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Simple initialization API for Webority packages
2
+ * Simple initialisation API for Webority packages
3
3
  * One function to configure everything needed
4
4
  */
5
5
  import { type KeychainLike } from "./auth/authStore";
@@ -27,13 +27,17 @@ export interface InitConfig {
27
27
  */
28
28
  country?: string;
29
29
  /**
30
- * The app's store identifier (Android package name / iOS bundle id), its
31
- * user-visible version and its internal build number (`VERSION_INTERNAL`).
30
+ * The app's store identifier (Android package name / iOS bundle id), from `APP_ID` in
31
+ * `appsettings.Compiled.json`. Required: it must equal the bundle id the installed app runs,
32
+ * so a bundle compiled for another environment stops at start-up.
33
+ */
34
+ appId: string;
35
+ /**
36
+ * The app's user-visible version and its internal build number (`VERSION_INTERNAL`).
32
37
  * Every Webority app already has them in `appsettings.Compiled.json`, which
33
38
  * is why they come from config rather than across the native bridge. The
34
39
  * version check compares `appBuild` with the API's `Min-App-Version`.
35
40
  */
36
- appId?: string;
37
41
  appVersion?: string;
38
42
  appBuild?: number;
39
43
  /** The numeric Apple App Store id; the iOS update button needs it. */
@@ -73,6 +77,7 @@ export interface InitConfig {
73
77
  * // Basic setup
74
78
  * initWebority({
75
79
  * baseUrl: 'https://api.myapp.com',
80
+ * appId: 'com.myapp',
76
81
  * remoteLogger: null
77
82
  * });
78
83
  *
@@ -80,6 +85,7 @@ export interface InitConfig {
80
85
  * // Full configuration
81
86
  * initWebority({
82
87
  * baseUrl: 'https://api.myapp.com',
88
+ * appId: 'com.myapp',
83
89
  * remoteLogger: crashlyticsRemoteLogger(crashlytics),
84
90
  * loggingEnabled: __DEV__,
85
91
  * logLevel: 'warn',
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The stored names the existing apps already use. Keeping them means an
3
+ * installation that already has an id keeps it after the app adopts this module.
4
+ */
5
+ export declare const INSTALLATION_STORAGE_KEYS: {
6
+ readonly HAS_LAUNCHED_BEFORE: "hasLaunchedBefore";
7
+ readonly INSTALLATION_ID: "installationId";
8
+ };
9
+ /**
10
+ * Returns this installation's id, creating and persisting one on first use. A
11
+ * stored id is never replaced, whatever its format. Throws when the new id cannot
12
+ * be persisted: every account call is keyed on the id, and a fresh id per call
13
+ * would register a new installation on each sign-in.
14
+ */
15
+ export declare const getOrCreateInstallationId: () => Promise<string>;
16
+ /**
17
+ * The iOS keychain keeps tokens across an uninstall, so a reinstalled app would
18
+ * start signed in as the previous person. This clears them on the first launch
19
+ * after install. It never calls clearSession: no session-ended listener may fire
20
+ * before the app has started.
21
+ */
22
+ export declare const clearTokensOnFirstLaunch: () => Promise<void>;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -18,7 +18,7 @@ export interface CrashlyticsModule<Instance = unknown> {
18
18
  * import * as crashlytics from '@react-native-firebase/crashlytics';
19
19
  * import { crashlyticsRemoteLogger, initWebority } from '@webority/mobile-core';
20
20
  *
21
- * initWebority({ baseUrl, remoteLogger: crashlyticsRemoteLogger(crashlytics) });
21
+ * initWebority({ baseUrl, appId, remoteLogger: crashlyticsRemoteLogger(crashlytics) });
22
22
  * ```
23
23
  *
24
24
  * Breadcrumbs and log lines become Crashlytics log lines, errors become
@@ -3,4 +3,6 @@ export { errorToastMiddleware, isSilentRejection } from "./errorToastMiddleware"
3
3
  export type { RootReducerOptions } from "./rootReducer";
4
4
  export { createRootReducer, resetState } from "./rootReducer";
5
5
  export { SERIALIZABLE_IGNORED_ACTION_PATHS } from "./serializableCheck";
6
+ export type { SessionMiddlewareOptions } from "./sessionMiddleware";
7
+ export { createSessionMiddleware } from "./sessionMiddleware";
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { Middleware, UnknownAction } from "@reduxjs/toolkit";
2
+ export interface SessionMiddlewareOptions {
3
+ /** Answers true for the pending action of the app's own sign-out thunk, for example `logout.pending.match`. */
4
+ isSignOut: (action: UnknownAction) => boolean;
5
+ }
6
+ /**
7
+ * Drops the outcome of every call still out when the person signs out, so an answer for the ended
8
+ * session never refills the store the sign-out just cleared, or shows to the next person on this
9
+ * phone. Each async thunk's actions carry a `requestId`; the ones in flight at the sign-out's
10
+ * pending action are stale. The thunk's own promise still settles, so a caller awaiting it is not
11
+ * left hanging.
12
+ */
13
+ export declare const createSessionMiddleware: (options: SessionMiddlewareOptions) => Middleware;
14
+ //# sourceMappingURL=sessionMiddleware.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { type DownloadResult } from "../download/index.js";
2
+ /**
3
+ * The content type a file name's extension stands for. Files that arrive with a name only need a type to be saved
4
+ * properly and to offer the right apps for them.
5
+ */
6
+ export declare const mimeTypeFromFileName: (fileName: string | null | undefined) => string | undefined;
7
+ export interface AttachmentRequest {
8
+ url: string;
9
+ fileName: string;
10
+ mimeType?: string | null;
11
+ }
12
+ /**
13
+ * Fetches a file through the signed-in client into the app's cache, where a viewer or a save reads it. Answers with
14
+ * its local `path` and `mimeType`; a file with no content type of its own takes the one its name stands for.
15
+ */
16
+ export declare const fetchAttachment: ({ url, fileName, mimeType }: AttachmentRequest) => Promise<DownloadResult>;
17
+ export interface PreviewAttachment {
18
+ path: string;
19
+ mimeType?: string;
20
+ folder: string;
21
+ }
22
+ /**
23
+ * fetchAttachment for a preview: a document already downloaded this session opens from the phone with no second
24
+ * download. The document's `version` (its updated time) is part of the key, so a replaced document is fetched again;
25
+ * without a version the file is not kept at all.
26
+ */
27
+ export declare const fetchPreviewAttachment: ({ cacheId, version, url, fileName, mimeType, }: AttachmentRequest & {
28
+ cacheId: string;
29
+ version?: string | null;
30
+ }) => Promise<PreviewAttachment | DownloadResult>;
31
+ export type PreviewKind = "image" | "pdf";
32
+ /** What the app can show itself, from a content type or a file name: "image", "pdf", or null for anything else. */
33
+ export declare const previewKind: (file: {
34
+ contentType?: string | null;
35
+ name?: string | null;
36
+ }) => PreviewKind | null;
37
+ //# sourceMappingURL=index.d.ts.map
@@ -10,6 +10,8 @@
10
10
  * @webority/mobile-core/geolocation a geolocation provider
11
11
  * @webority/mobile-core/sqlite expo-sqlite
12
12
  * @webority/mobile-core/download a filesystem module
13
+ * @webority/mobile-core/installation installation id (expo-crypto)
14
+ * @webority/mobile-core/attachments fetch, preview cache and preview kind (expo-file-system)
13
15
  * @webority/mobile-core/share expo-sharing
14
16
  * @webority/mobile-core/review expo-store-review
15
17
  * @webority/mobile-core/secureScreen expo-screen-capture
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Simple initialization API for Webority packages
2
+ * Simple initialisation API for Webority packages
3
3
  * One function to configure everything needed
4
4
  */
5
5
  import { type KeychainLike } from "./auth/authStore.js";
@@ -27,13 +27,17 @@ export interface InitConfig {
27
27
  */
28
28
  country?: string;
29
29
  /**
30
- * The app's store identifier (Android package name / iOS bundle id), its
31
- * user-visible version and its internal build number (`VERSION_INTERNAL`).
30
+ * The app's store identifier (Android package name / iOS bundle id), from `APP_ID` in
31
+ * `appsettings.Compiled.json`. Required: it must equal the bundle id the installed app runs,
32
+ * so a bundle compiled for another environment stops at start-up.
33
+ */
34
+ appId: string;
35
+ /**
36
+ * The app's user-visible version and its internal build number (`VERSION_INTERNAL`).
32
37
  * Every Webority app already has them in `appsettings.Compiled.json`, which
33
38
  * is why they come from config rather than across the native bridge. The
34
39
  * version check compares `appBuild` with the API's `Min-App-Version`.
35
40
  */
36
- appId?: string;
37
41
  appVersion?: string;
38
42
  appBuild?: number;
39
43
  /** The numeric Apple App Store id; the iOS update button needs it. */
@@ -73,6 +77,7 @@ export interface InitConfig {
73
77
  * // Basic setup
74
78
  * initWebority({
75
79
  * baseUrl: 'https://api.myapp.com',
80
+ * appId: 'com.myapp',
76
81
  * remoteLogger: null
77
82
  * });
78
83
  *
@@ -80,6 +85,7 @@ export interface InitConfig {
80
85
  * // Full configuration
81
86
  * initWebority({
82
87
  * baseUrl: 'https://api.myapp.com',
88
+ * appId: 'com.myapp',
83
89
  * remoteLogger: crashlyticsRemoteLogger(crashlytics),
84
90
  * loggingEnabled: __DEV__,
85
91
  * logLevel: 'warn',
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The stored names the existing apps already use. Keeping them means an
3
+ * installation that already has an id keeps it after the app adopts this module.
4
+ */
5
+ export declare const INSTALLATION_STORAGE_KEYS: {
6
+ readonly HAS_LAUNCHED_BEFORE: "hasLaunchedBefore";
7
+ readonly INSTALLATION_ID: "installationId";
8
+ };
9
+ /**
10
+ * Returns this installation's id, creating and persisting one on first use. A
11
+ * stored id is never replaced, whatever its format. Throws when the new id cannot
12
+ * be persisted: every account call is keyed on the id, and a fresh id per call
13
+ * would register a new installation on each sign-in.
14
+ */
15
+ export declare const getOrCreateInstallationId: () => Promise<string>;
16
+ /**
17
+ * The iOS keychain keeps tokens across an uninstall, so a reinstalled app would
18
+ * start signed in as the previous person. This clears them on the first launch
19
+ * after install. It never calls clearSession: no session-ended listener may fire
20
+ * before the app has started.
21
+ */
22
+ export declare const clearTokensOnFirstLaunch: () => Promise<void>;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -18,7 +18,7 @@ export interface CrashlyticsModule<Instance = unknown> {
18
18
  * import * as crashlytics from '@react-native-firebase/crashlytics';
19
19
  * import { crashlyticsRemoteLogger, initWebority } from '@webority/mobile-core';
20
20
  *
21
- * initWebority({ baseUrl, remoteLogger: crashlyticsRemoteLogger(crashlytics) });
21
+ * initWebority({ baseUrl, appId, remoteLogger: crashlyticsRemoteLogger(crashlytics) });
22
22
  * ```
23
23
  *
24
24
  * Breadcrumbs and log lines become Crashlytics log lines, errors become
@@ -3,4 +3,6 @@ export { errorToastMiddleware, isSilentRejection } from "./errorToastMiddleware.
3
3
  export type { RootReducerOptions } from "./rootReducer.js";
4
4
  export { createRootReducer, resetState } from "./rootReducer.js";
5
5
  export { SERIALIZABLE_IGNORED_ACTION_PATHS } from "./serializableCheck.js";
6
+ export type { SessionMiddlewareOptions } from "./sessionMiddleware.js";
7
+ export { createSessionMiddleware } from "./sessionMiddleware.js";
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { Middleware, UnknownAction } from "@reduxjs/toolkit";
2
+ export interface SessionMiddlewareOptions {
3
+ /** Answers true for the pending action of the app's own sign-out thunk, for example `logout.pending.match`. */
4
+ isSignOut: (action: UnknownAction) => boolean;
5
+ }
6
+ /**
7
+ * Drops the outcome of every call still out when the person signs out, so an answer for the ended
8
+ * session never refills the store the sign-out just cleared, or shows to the next person on this
9
+ * phone. Each async thunk's actions carry a `requestId`; the ones in flight at the sign-out's
10
+ * pending action are stale. The thunk's own promise still settles, so a caller awaiting it is not
11
+ * left hanging.
12
+ */
13
+ export declare const createSessionMiddleware: (options: SessionMiddlewareOptions) => Middleware;
14
+ //# sourceMappingURL=sessionMiddleware.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/mobile-core",
3
- "version": "0.111.0",
3
+ "version": "0.112.3",
4
4
  "description": "Platform layer for Webority React Native apps: HTTP clients, auth/token storage, config, logging, formatters, validators, network status, permissions, storage, version checks and over-the-air updates. No UI.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -152,6 +152,26 @@
152
152
  "default": "./lib/commonjs/download/index.js"
153
153
  }
154
154
  },
155
+ "./installation": {
156
+ "import": {
157
+ "types": "./lib/typescript/module/installation/index.d.ts",
158
+ "default": "./lib/module/installation/index.js"
159
+ },
160
+ "require": {
161
+ "types": "./lib/typescript/commonjs/installation/index.d.ts",
162
+ "default": "./lib/commonjs/installation/index.js"
163
+ }
164
+ },
165
+ "./attachments": {
166
+ "import": {
167
+ "types": "./lib/typescript/module/attachments/index.d.ts",
168
+ "default": "./lib/module/attachments/index.js"
169
+ },
170
+ "require": {
171
+ "types": "./lib/typescript/commonjs/attachments/index.d.ts",
172
+ "default": "./lib/commonjs/attachments/index.js"
173
+ }
174
+ },
155
175
  "./share": {
156
176
  "import": {
157
177
  "types": "./lib/typescript/module/share/index.d.ts",
@@ -329,6 +349,7 @@
329
349
  "expo-calendar": ">=57.0.0",
330
350
  "expo-camera": ">=57.0.0",
331
351
  "expo-contacts": ">=57.0.0",
352
+ "expo-crypto": ">=57.0.0",
332
353
  "expo-document-picker": ">=57.0.0",
333
354
  "expo-file-system": ">=57.0.0",
334
355
  "expo-image-manipulator": ">=57.0.0",
@@ -362,6 +383,9 @@
362
383
  "expo-contacts": {
363
384
  "optional": true
364
385
  },
386
+ "expo-crypto": {
387
+ "optional": true
388
+ },
365
389
  "expo-document-picker": {
366
390
  "optional": true
367
391
  },
@@ -443,6 +467,7 @@
443
467
  "@types/react": "19.2.18",
444
468
  "expo": "57.0.17",
445
469
  "expo-application": "57.0.2",
470
+ "expo-crypto": "57.0.4",
446
471
  "expo-document-picker": "57.0.2",
447
472
  "expo-file-system": "57.0.6",
448
473
  "expo-image-manipulator": "57.0.14",