@sudobility/auth_lib 0.0.71 → 0.0.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ import type { NetworkClient } from '@sudobility/types';
2
+ export interface DeleteAccountOptions {
3
+ networkClient: NetworkClient;
4
+ baseUrl: string;
5
+ userId: string;
6
+ providerTokens?: {
7
+ googleAccessToken?: string;
8
+ appleAuthorizationCode?: string;
9
+ };
10
+ }
11
+ export declare function deleteAccount(options: DeleteAccountOptions): Promise<void>;
12
+ //# sourceMappingURL=deleteAccount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteAccount.d.ts","sourceRoot":"","sources":["../../src/account/deleteAccount.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,MAAM,WAAW,oBAAoB;IAEnC,aAAa,EAAE,aAAa,CAAC;IAE7B,OAAO,EAAE,MAAM,CAAC;IAEhB,MAAM,EAAE,MAAM,CAAC;IAEf,cAAc,CAAC,EAAE;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;KACjC,CAAC;CACH;AAiBD,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
@@ -0,0 +1,23 @@
1
+ import { signOut } from 'firebase/auth';
2
+ import { getFirebaseAuth } from '../config/firebase-init';
3
+ export async function deleteAccount(options) {
4
+ const { networkClient, baseUrl, userId, providerTokens } = options;
5
+ const response = await networkClient.request(`${baseUrl}/api/v1/users/${userId}`, {
6
+ method: 'DELETE',
7
+ headers: { 'Content-Type': 'application/json' },
8
+ body: JSON.stringify(providerTokens ?? {}),
9
+ });
10
+ if (!response.ok) {
11
+ const message = response.data?.error ?? `Failed to delete account (${response.status})`;
12
+ throw new Error(message);
13
+ }
14
+ const auth = getFirebaseAuth();
15
+ if (auth) {
16
+ try {
17
+ await signOut(auth);
18
+ }
19
+ catch {
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=deleteAccount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteAccount.js","sourceRoot":"","sources":["../../src/account/deleteAccount.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAgC1D,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA6B;IAE7B,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAEnE,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,OAAO,CAC1C,GAAG,OAAO,iBAAiB,MAAM,EAAE,EACnC;QACE,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,IAAI,EAAE,CAAC;KAC3C,CACF,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,OAAO,GACX,QAAQ,CAAC,IAAI,EAAE,KAAK,IAAI,6BAA6B,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAGD,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;QAET,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { NetworkClient } from '@sudobility/types';
2
+ export interface DeleteAccountOptions {
3
+ networkClient: NetworkClient;
4
+ baseUrl: string;
5
+ userId: string;
6
+ providerTokens?: {
7
+ googleAccessToken?: string;
8
+ appleAuthorizationCode?: string;
9
+ };
10
+ }
11
+ export declare function deleteAccount(options: DeleteAccountOptions): Promise<void>;
12
+ //# sourceMappingURL=deleteAccount.rn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteAccount.rn.d.ts","sourceRoot":"","sources":["../../src/account/deleteAccount.rn.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,MAAM,WAAW,oBAAoB;IAEnC,aAAa,EAAE,aAAa,CAAC;IAE7B,OAAO,EAAE,MAAM,CAAC;IAEhB,MAAM,EAAE,MAAM,CAAC;IAEf,cAAc,CAAC,EAAE;QACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;KACjC,CAAC;CACH;AAiBD,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
@@ -0,0 +1,23 @@
1
+ import { getFirebaseAuth } from '../config/firebase-init.rn';
2
+ export async function deleteAccount(options) {
3
+ const { networkClient, baseUrl, userId, providerTokens } = options;
4
+ const response = await networkClient.request(`${baseUrl}/api/v1/users/${userId}`, {
5
+ method: 'DELETE',
6
+ headers: { 'Content-Type': 'application/json' },
7
+ body: JSON.stringify(providerTokens ?? {}),
8
+ });
9
+ if (!response.ok) {
10
+ const message = response.data?.error ?? `Failed to delete account (${response.status})`;
11
+ throw new Error(message);
12
+ }
13
+ const auth = getFirebaseAuth();
14
+ if (auth) {
15
+ try {
16
+ const { signOut } = require('@react-native-firebase/auth');
17
+ await signOut(auth);
18
+ }
19
+ catch {
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=deleteAccount.rn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteAccount.rn.js","sourceRoot":"","sources":["../../src/account/deleteAccount.rn.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAgC7D,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA6B;IAE7B,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAEnE,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,OAAO,CAC1C,GAAG,OAAO,iBAAiB,MAAM,EAAE,EACnC;QACE,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,IAAI,EAAE,CAAC;KAC3C,CACF,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,OAAO,GACX,QAAQ,CAAC,IAAI,EAAE,KAAK,IAAI,6BAA6B,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAGD,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YAEH,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAAC;YAC3D,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;QAET,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { deleteAccount, type DeleteAccountOptions } from './deleteAccount';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/account/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { deleteAccount } from './deleteAccount';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/account/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAA6B,MAAM,iBAAiB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export { useFirebaseAuthNetworkClient, createFirebaseAuthNetworkClient, } from '
4
4
  export { useSiteAdmin, siteAdminQueryKey, type UseSiteAdminOptions, type UseSiteAdminResult, type UserInfoResponse, } from './hooks';
5
5
  export { getFirebaseErrorMessage, getFirebaseErrorCode, formatFirebaseError, isFirebaseAuthError, } from './utils';
6
6
  export { FirebaseAuthNetworkService, type FirebaseAuthNetworkServiceOptions, } from './network';
7
+ export { deleteAccount, type DeleteAccountOptions } from './account';
7
8
  export { parseAdminEmails, isAdminEmail, createAdminChecker } from './admin';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,0BAA0B,EAC1B,KAAK,iCAAiC,GACvC,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,0BAA0B,EAC1B,KAAK,iCAAiC,GACvC,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGrE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -3,5 +3,6 @@ export { useFirebaseAuthNetworkClient, createFirebaseAuthNetworkClient, } from '
3
3
  export { useSiteAdmin, siteAdminQueryKey, } from './hooks';
4
4
  export { getFirebaseErrorMessage, getFirebaseErrorCode, formatFirebaseError, isFirebaseAuthError, } from './utils';
5
5
  export { FirebaseAuthNetworkService, } from './network';
6
+ export { deleteAccount } from './account';
6
7
  export { parseAdminEmails, isAdminEmail, createAdminChecker } from './admin';
7
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAQlB,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,YAAY,EACZ,iBAAiB,GAIlB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,0BAA0B,GAE3B,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAQlB,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,YAAY,EACZ,iBAAiB,GAIlB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,0BAA0B,GAE3B,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,aAAa,EAA6B,MAAM,WAAW,CAAC;AAGrE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
@@ -4,5 +4,6 @@ export { useFirebaseAuthNetworkClient, createFirebaseAuthNetworkClient, } from '
4
4
  export { useSiteAdmin, siteAdminQueryKey, type UseSiteAdminOptions, type UseSiteAdminResult, type UserInfoResponse, } from './hooks/index.js';
5
5
  export { getFirebaseErrorMessage, getFirebaseErrorCode, formatFirebaseError, isFirebaseAuthError, } from './utils/index.js';
6
6
  export { FirebaseAuthNetworkService, type FirebaseAuthNetworkServiceOptions, } from './network/FirebaseAuthNetworkService.rn.js';
7
+ export { deleteAccount, type DeleteAccountOptions, } from './account/deleteAccount.rn.js';
7
8
  export { parseAdminEmails, isAdminEmail, createAdminChecker, } from './admin/index.js';
8
9
  //# sourceMappingURL=index.rn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.rn.d.ts","sourceRoot":"","sources":["../src/index.rn.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,0BAA0B,EAC1B,KAAK,iCAAiC,GACvC,MAAM,4CAA4C,CAAC;AAGpD,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.rn.d.ts","sourceRoot":"","sources":["../src/index.rn.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,kBAAkB,EAClB,gCAAgC,GACjC,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,0BAA0B,EAC1B,KAAK,iCAAiC,GACvC,MAAM,4CAA4C,CAAC;AAGpD,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,GAC1B,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
package/dist/index.rn.js CHANGED
@@ -3,5 +3,6 @@ export { useFirebaseAuthNetworkClient, createFirebaseAuthNetworkClient, } from '
3
3
  export { useSiteAdmin, siteAdminQueryKey, } from './hooks/index.js';
4
4
  export { getFirebaseErrorMessage, getFirebaseErrorCode, formatFirebaseError, isFirebaseAuthError, } from './utils/index.js';
5
5
  export { FirebaseAuthNetworkService, } from './network/FirebaseAuthNetworkService.rn.js';
6
+ export { deleteAccount, } from './account/deleteAccount.rn.js';
6
7
  export { parseAdminEmails, isAdminEmail, createAdminChecker, } from './admin/index.js';
7
8
  //# sourceMappingURL=index.rn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.rn.js","sourceRoot":"","sources":["../src/index.rn.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AAStC,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,iBAAiB,GAIlB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,0BAA0B,GAE3B,MAAM,4CAA4C,CAAC;AAGpD,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.rn.js","sourceRoot":"","sources":["../src/index.rn.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AAStC,OAAO,EACL,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,iBAAiB,GAIlB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,0BAA0B,GAE3B,MAAM,4CAA4C,CAAC;AAGpD,OAAO,EACL,aAAa,GAEd,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,GACnB,MAAM,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/auth_lib",
3
- "version": "0.0.71",
3
+ "version": "0.0.72",
4
4
  "description": "Firebase authentication utilities with token refresh handling",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,8 +37,8 @@
37
37
  "firebase": "^12.7.0",
38
38
  "@react-native-firebase/app": ">=18.0.0",
39
39
  "@react-native-firebase/auth": ">=18.0.0",
40
- "@sudobility/di": "^1.5.53",
41
- "@sudobility/types": "^1.9.61",
40
+ "@sudobility/di": "^1.5.54",
41
+ "@sudobility/types": "^1.9.62",
42
42
  "@tanstack/react-query": "^5.0.0"
43
43
  },
44
44
  "peerDependenciesMeta": {
@@ -53,8 +53,8 @@
53
53
  }
54
54
  },
55
55
  "devDependencies": {
56
- "@sudobility/di": "^1.5.53",
57
- "@sudobility/types": "^1.9.61",
56
+ "@sudobility/di": "^1.5.54",
57
+ "@sudobility/types": "^1.9.62",
58
58
  "@tanstack/react-query": "^5.80.7",
59
59
  "@types/node": "^22.0.0",
60
60
  "vitest": "^4.0.4",