@umituz/react-native-firebase 1.13.48 → 1.13.50
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/dist/scripts/cli-handlers.d.ts +27 -0
- package/dist/scripts/cli-handlers.d.ts.map +1 -0
- package/dist/scripts/cli-handlers.js +125 -0
- package/dist/scripts/cli-handlers.js.map +1 -0
- package/dist/scripts/cli-parser.d.ts +25 -0
- package/dist/scripts/cli-parser.d.ts.map +1 -0
- package/dist/scripts/cli-parser.js +101 -0
- package/dist/scripts/cli-parser.js.map +1 -0
- package/dist/scripts/cli.js +20 -155
- package/dist/scripts/cli.js.map +1 -1
- package/dist/scripts/firestore-operations.d.ts +18 -0
- package/dist/scripts/firestore-operations.d.ts.map +1 -0
- package/dist/scripts/firestore-operations.js +88 -0
- package/dist/scripts/firestore-operations.js.map +1 -0
- package/dist/scripts/firestore-queries.d.ts +27 -0
- package/dist/scripts/firestore-queries.d.ts.map +1 -0
- package/dist/scripts/firestore-queries.js +77 -0
- package/dist/scripts/firestore-queries.js.map +1 -0
- package/dist/scripts/firestore-seeding.d.ts +21 -0
- package/dist/scripts/firestore-seeding.d.ts.map +1 -0
- package/dist/scripts/firestore-seeding.js +67 -0
- package/dist/scripts/firestore-seeding.js.map +1 -0
- package/dist/scripts/firestore.d.ts +3 -48
- package/dist/scripts/firestore.d.ts.map +1 -1
- package/dist/scripts/firestore.js +16 -210
- package/dist/scripts/firestore.js.map +1 -1
- package/dist/scripts/user-commands.d.ts +33 -0
- package/dist/scripts/user-commands.d.ts.map +1 -0
- package/dist/scripts/user-commands.js +113 -0
- package/dist/scripts/user-commands.js.map +1 -0
- package/dist/scripts/user-formatters.d.ts +10 -0
- package/dist/scripts/user-formatters.d.ts.map +1 -0
- package/dist/scripts/user-formatters.js +55 -0
- package/dist/scripts/user-formatters.js.map +1 -0
- package/dist/scripts/user-queries.d.ts +42 -0
- package/dist/scripts/user-queries.d.ts.map +1 -0
- package/dist/scripts/user-queries.js +125 -0
- package/dist/scripts/user-queries.js.map +1 -0
- package/dist/scripts/user.d.ts +3 -67
- package/dist/scripts/user.d.ts.map +1 -1
- package/dist/scripts/user.js +15 -272
- package/dist/scripts/user.js.map +1 -1
- package/package.json +1 -1
- package/scripts/cli-handlers.ts +170 -0
- package/scripts/cli-parser.ts +82 -0
- package/scripts/cli.ts +27 -193
- package/scripts/firestore-operations.ts +111 -0
- package/scripts/firestore-queries.ts +97 -0
- package/scripts/firestore-seeding.ts +87 -0
- package/scripts/firestore.ts +20 -275
- package/scripts/user-commands.ts +104 -0
- package/scripts/user-formatters.ts +55 -0
- package/scripts/user-queries.ts +185 -0
- package/scripts/user.ts +19 -326
- package/src/auth/infrastructure/config/FirebaseAuthClient.ts +16 -171
- package/src/auth/infrastructure/services/account-deletion.service.ts +41 -351
- package/src/auth/infrastructure/services/reauthentication.service.ts +47 -207
- package/src/auth/infrastructure/services/reauthentication.types.ts +39 -0
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Account Deletion Service
|
|
3
|
-
* Handles Firebase Auth account deletion operations with automatic reauthentication
|
|
4
|
-
*
|
|
5
|
-
* SOLID: Single Responsibility - Only handles account deletion
|
|
6
3
|
*/
|
|
7
4
|
|
|
8
5
|
import { deleteUser, type User } from "firebase/auth";
|
|
@@ -11,381 +8,74 @@ import {
|
|
|
11
8
|
getUserAuthProvider,
|
|
12
9
|
reauthenticateWithApple,
|
|
13
10
|
reauthenticateWithPassword,
|
|
11
|
+
reauthenticateWithGoogle,
|
|
14
12
|
} from "./reauthentication.service";
|
|
13
|
+
import type { AccountDeletionResult, AccountDeletionOptions } from "./reauthentication.types";
|
|
15
14
|
|
|
16
|
-
export
|
|
17
|
-
success: boolean;
|
|
18
|
-
error?: {
|
|
19
|
-
code: string;
|
|
20
|
-
message: string;
|
|
21
|
-
requiresReauth: boolean;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
15
|
+
export type { AccountDeletionResult, AccountDeletionOptions } from "./reauthentication.types";
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Google ID token for reauthentication (required if user signed in with Google)
|
|
28
|
-
* This must be provided by the calling code after prompting user for Google sign-in
|
|
29
|
-
*/
|
|
30
|
-
googleIdToken?: string;
|
|
31
|
-
/**
|
|
32
|
-
* Password for reauthentication (required if user signed in with email/password)
|
|
33
|
-
* This must be provided by the calling code after prompting user for password
|
|
34
|
-
*/
|
|
35
|
-
password?: string;
|
|
36
|
-
/**
|
|
37
|
-
* If true, will attempt to reauthenticate with Apple automatically
|
|
38
|
-
* (shows Apple sign-in prompt to user)
|
|
39
|
-
*/
|
|
40
|
-
autoReauthenticate?: boolean;
|
|
41
|
-
}
|
|
17
|
+
declare const __DEV__: boolean;
|
|
42
18
|
|
|
43
|
-
/**
|
|
44
|
-
* Delete the current user's Firebase Auth account
|
|
45
|
-
* Now with automatic reauthentication support for Apple Sign-In
|
|
46
|
-
* Note: This is irreversible and also signs out the user
|
|
47
|
-
*/
|
|
48
19
|
export async function deleteCurrentUser(
|
|
49
20
|
options: AccountDeletionOptions = { autoReauthenticate: true }
|
|
50
21
|
): Promise<AccountDeletionResult> {
|
|
51
|
-
if (__DEV__) {
|
|
52
|
-
console.log("[deleteCurrentUser] Starting with options:", options);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
22
|
const auth = getFirebaseAuth();
|
|
23
|
+
const user = auth?.currentUser;
|
|
56
24
|
|
|
57
|
-
if (!auth) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
message: "Firebase Auth is not initialized",
|
|
66
|
-
requiresReauth: false,
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const user = auth.currentUser;
|
|
72
|
-
|
|
73
|
-
if (!user) {
|
|
74
|
-
if (__DEV__) {
|
|
75
|
-
console.log("[deleteCurrentUser] ❌ No user signed in");
|
|
76
|
-
}
|
|
77
|
-
return {
|
|
78
|
-
success: false,
|
|
79
|
-
error: {
|
|
80
|
-
code: "auth/no-user",
|
|
81
|
-
message: "No user is currently signed in",
|
|
82
|
-
requiresReauth: false,
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (user.isAnonymous) {
|
|
88
|
-
if (__DEV__) {
|
|
89
|
-
console.log("[deleteCurrentUser] ❌ Cannot delete anonymous user");
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
success: false,
|
|
93
|
-
error: {
|
|
94
|
-
code: "auth/anonymous-user",
|
|
95
|
-
message: "Cannot delete anonymous account",
|
|
96
|
-
requiresReauth: false,
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (__DEV__) {
|
|
102
|
-
const provider = getUserAuthProvider(user);
|
|
103
|
-
console.log("[deleteCurrentUser] User info:", {
|
|
104
|
-
uid: user.uid,
|
|
105
|
-
provider,
|
|
106
|
-
providerData: user.providerData?.map(p => p.providerId),
|
|
107
|
-
});
|
|
108
|
-
}
|
|
25
|
+
if (!auth || !user) return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: { code: "auth/not-ready", message: "Auth not ready", requiresReauth: false }
|
|
28
|
+
};
|
|
29
|
+
if (user.isAnonymous) return {
|
|
30
|
+
success: false,
|
|
31
|
+
error: { code: "auth/anonymous", message: "Cannot delete anonymous", requiresReauth: false }
|
|
32
|
+
};
|
|
109
33
|
|
|
110
|
-
// First attempt to delete
|
|
111
34
|
try {
|
|
112
|
-
if (__DEV__) {
|
|
113
|
-
console.log("[deleteCurrentUser] Attempting to delete user...");
|
|
114
|
-
}
|
|
115
35
|
await deleteUser(user);
|
|
116
|
-
if (__DEV__) {
|
|
117
|
-
console.log("[deleteCurrentUser] ✅ User deleted successfully");
|
|
118
|
-
}
|
|
119
36
|
return { success: true };
|
|
120
|
-
} catch (error) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (__DEV__) {
|
|
125
|
-
console.log("[deleteCurrentUser] First delete attempt failed:", {
|
|
126
|
-
code: firebaseError.code,
|
|
127
|
-
message: firebaseError.message,
|
|
128
|
-
requiresReauth,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// If reauthentication is required and autoReauthenticate is enabled OR password is provided
|
|
133
|
-
if (requiresReauth && (options.autoReauthenticate || options.password)) {
|
|
134
|
-
if (__DEV__) {
|
|
135
|
-
console.log("[deleteCurrentUser] Attempting auto-reauthentication...");
|
|
136
|
-
}
|
|
137
|
-
const reauthResult = await attemptReauthenticationAndDelete(user, options);
|
|
138
|
-
if (reauthResult) {
|
|
139
|
-
if (__DEV__) {
|
|
140
|
-
console.log("[deleteCurrentUser] Auto-reauth result:", reauthResult);
|
|
141
|
-
}
|
|
142
|
-
return reauthResult;
|
|
143
|
-
}
|
|
37
|
+
} catch (error: any) {
|
|
38
|
+
if (error.code === "auth/requires-recent-login" && (options.autoReauthenticate || options.password || options.googleIdToken)) {
|
|
39
|
+
const reauth = await attemptReauth(user, options);
|
|
40
|
+
if (reauth) return reauth;
|
|
144
41
|
}
|
|
145
|
-
|
|
146
42
|
return {
|
|
147
43
|
success: false,
|
|
148
|
-
error: {
|
|
149
|
-
code: firebaseError.code || "auth/unknown",
|
|
150
|
-
message: requiresReauth
|
|
151
|
-
? "Please sign in again before deleting your account"
|
|
152
|
-
: firebaseError.message || "Failed to delete account",
|
|
153
|
-
requiresReauth,
|
|
154
|
-
},
|
|
44
|
+
error: { code: error.code || "auth/failed", message: error.message, requiresReauth: error.code === "auth/requires-recent-login" }
|
|
155
45
|
};
|
|
156
46
|
}
|
|
157
47
|
}
|
|
158
48
|
|
|
159
|
-
|
|
160
|
-
* Attempt to reauthenticate based on provider and then delete the account
|
|
161
|
-
*/
|
|
162
|
-
async function attemptReauthenticationAndDelete(
|
|
163
|
-
user: User,
|
|
164
|
-
options: AccountDeletionOptions
|
|
165
|
-
): Promise<AccountDeletionResult | null> {
|
|
49
|
+
async function attemptReauth(user: User, options: AccountDeletionOptions): Promise<AccountDeletionResult | null> {
|
|
166
50
|
const provider = getUserAuthProvider(user);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (provider === "
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (reauthResult.success) {
|
|
185
|
-
// Retry deletion after successful reauthentication
|
|
186
|
-
try {
|
|
187
|
-
if (__DEV__) {
|
|
188
|
-
console.log("[attemptReauthenticationAndDelete] Deleting user after Apple reauth...");
|
|
189
|
-
}
|
|
190
|
-
await deleteUser(user);
|
|
191
|
-
if (__DEV__) {
|
|
192
|
-
console.log("[attemptReauthenticationAndDelete] ✅ User deleted after Apple reauth");
|
|
193
|
-
}
|
|
194
|
-
return { success: true };
|
|
195
|
-
} catch (deleteError) {
|
|
196
|
-
const firebaseError = deleteError as { code?: string; message?: string };
|
|
197
|
-
if (__DEV__) {
|
|
198
|
-
console.log("[attemptReauthenticationAndDelete] ❌ Delete failed after Apple reauth:", firebaseError);
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
success: false,
|
|
202
|
-
error: {
|
|
203
|
-
code: firebaseError.code || "auth/deletion-failed-after-reauth",
|
|
204
|
-
message: firebaseError.message || "Failed to delete account after reauthentication",
|
|
205
|
-
requiresReauth: false,
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
} else {
|
|
210
|
-
// Reauthentication failed
|
|
211
|
-
if (__DEV__) {
|
|
212
|
-
console.log("[attemptReauthenticationAndDelete] ❌ Apple reauth failed");
|
|
213
|
-
}
|
|
214
|
-
return {
|
|
215
|
-
success: false,
|
|
216
|
-
error: {
|
|
217
|
-
code: reauthResult.error?.code || "auth/reauthentication-failed",
|
|
218
|
-
message: reauthResult.error?.message || "Reauthentication failed",
|
|
219
|
-
requiresReauth: true,
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Handle Google reauthentication (requires ID token from caller)
|
|
226
|
-
if (provider === "google.com") {
|
|
227
|
-
// For Google, we need the caller to provide the ID token
|
|
228
|
-
// This is because we need to trigger Google Sign-In UI which must be done at the presentation layer
|
|
229
|
-
if (!options.googleIdToken) {
|
|
230
|
-
return {
|
|
231
|
-
success: false,
|
|
232
|
-
error: {
|
|
233
|
-
code: "auth/google-reauth-required",
|
|
234
|
-
message: "Please sign in with Google again to delete your account",
|
|
235
|
-
requiresReauth: true,
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// If we have a Google ID token, reauthenticate with it
|
|
241
|
-
const { reauthenticateWithGoogle } = await import("./reauthentication.service");
|
|
242
|
-
const reauthResult = await reauthenticateWithGoogle(user, options.googleIdToken);
|
|
243
|
-
|
|
244
|
-
if (reauthResult.success) {
|
|
245
|
-
try {
|
|
246
|
-
await deleteUser(user);
|
|
247
|
-
return { success: true };
|
|
248
|
-
} catch (deleteError) {
|
|
249
|
-
const firebaseError = deleteError as { code?: string; message?: string };
|
|
250
|
-
return {
|
|
251
|
-
success: false,
|
|
252
|
-
error: {
|
|
253
|
-
code: firebaseError.code || "auth/deletion-failed-after-reauth",
|
|
254
|
-
message: firebaseError.message || "Failed to delete account after reauthentication",
|
|
255
|
-
requiresReauth: false,
|
|
256
|
-
},
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
} else {
|
|
260
|
-
return {
|
|
261
|
-
success: false,
|
|
262
|
-
error: {
|
|
263
|
-
code: reauthResult.error?.code || "auth/reauthentication-failed",
|
|
264
|
-
message: reauthResult.error?.message || "Google reauthentication failed",
|
|
265
|
-
requiresReauth: true,
|
|
266
|
-
},
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Handle Password reauthentication (requires password from caller)
|
|
272
|
-
if (provider === "password") {
|
|
273
|
-
if (__DEV__) {
|
|
274
|
-
console.log("[attemptReauthenticationAndDelete] Password provider detected");
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// For password, we need the caller to provide the password
|
|
278
|
-
if (!options.password) {
|
|
279
|
-
if (__DEV__) {
|
|
280
|
-
console.log("[attemptReauthenticationAndDelete] No password provided, requesting reauth");
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
success: false,
|
|
284
|
-
error: {
|
|
285
|
-
code: "auth/password-reauth-required",
|
|
286
|
-
message: "Please enter your password to delete your account",
|
|
287
|
-
requiresReauth: true,
|
|
288
|
-
},
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (__DEV__) {
|
|
293
|
-
console.log("[attemptReauthenticationAndDelete] Attempting password reauthentication...");
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const reauthResult = await reauthenticateWithPassword(user, options.password);
|
|
297
|
-
|
|
298
|
-
if (__DEV__) {
|
|
299
|
-
console.log("[attemptReauthenticationAndDelete] Password reauth result:", reauthResult);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
if (reauthResult.success) {
|
|
303
|
-
try {
|
|
304
|
-
if (__DEV__) {
|
|
305
|
-
console.log("[attemptReauthenticationAndDelete] Deleting user after password reauth...");
|
|
306
|
-
}
|
|
307
|
-
await deleteUser(user);
|
|
308
|
-
if (__DEV__) {
|
|
309
|
-
console.log("[attemptReauthenticationAndDelete] ✅ User deleted after password reauth");
|
|
310
|
-
}
|
|
311
|
-
return { success: true };
|
|
312
|
-
} catch (deleteError) {
|
|
313
|
-
const firebaseError = deleteError as { code?: string; message?: string };
|
|
314
|
-
if (__DEV__) {
|
|
315
|
-
console.log("[attemptReauthenticationAndDelete] ❌ Delete failed after password reauth:", firebaseError);
|
|
316
|
-
}
|
|
317
|
-
return {
|
|
318
|
-
success: false,
|
|
319
|
-
error: {
|
|
320
|
-
code: firebaseError.code || "auth/deletion-failed-after-reauth",
|
|
321
|
-
message: firebaseError.message || "Failed to delete account after reauthentication",
|
|
322
|
-
requiresReauth: false,
|
|
323
|
-
},
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
} else {
|
|
327
|
-
if (__DEV__) {
|
|
328
|
-
console.log("[attemptReauthenticationAndDelete] ❌ Password reauth failed");
|
|
329
|
-
}
|
|
330
|
-
return {
|
|
331
|
-
success: false,
|
|
332
|
-
error: {
|
|
333
|
-
code: reauthResult.error?.code || "auth/reauthentication-failed",
|
|
334
|
-
message: reauthResult.error?.message || "Password reauthentication failed",
|
|
335
|
-
requiresReauth: true,
|
|
336
|
-
},
|
|
337
|
-
};
|
|
51
|
+
let res: { success: boolean, error?: any };
|
|
52
|
+
|
|
53
|
+
if (provider === "apple.com") res = await reauthenticateWithApple(user);
|
|
54
|
+
else if (provider === "google.com") {
|
|
55
|
+
if (!options.googleIdToken) return { success: false, error: { code: "auth/google-reauth", message: "Google reauth required", requiresReauth: true } };
|
|
56
|
+
res = await reauthenticateWithGoogle(user, options.googleIdToken);
|
|
57
|
+
} else if (provider === "password") {
|
|
58
|
+
if (!options.password) return { success: false, error: { code: "auth/password-reauth", message: "Password required", requiresReauth: true } };
|
|
59
|
+
res = await reauthenticateWithPassword(user, options.password);
|
|
60
|
+
} else return null;
|
|
61
|
+
|
|
62
|
+
if (res.success) {
|
|
63
|
+
try {
|
|
64
|
+
await deleteUser(user);
|
|
65
|
+
return { success: true };
|
|
66
|
+
} catch (err: any) {
|
|
67
|
+
return { success: false, error: { code: err.code || "auth/post-reauth-failed", message: err.message, requiresReauth: false } };
|
|
338
68
|
}
|
|
339
69
|
}
|
|
340
|
-
|
|
341
|
-
// For other providers, return null to use the default error handling
|
|
342
|
-
return null;
|
|
70
|
+
return { success: false, error: { code: res.error?.code || "auth/reauth-failed", message: res.error?.message, requiresReauth: true } };
|
|
343
71
|
}
|
|
344
72
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
*/
|
|
348
|
-
export async function deleteUserAccount(
|
|
349
|
-
user: User
|
|
350
|
-
): Promise<AccountDeletionResult> {
|
|
351
|
-
if (!user) {
|
|
352
|
-
return {
|
|
353
|
-
success: false,
|
|
354
|
-
error: {
|
|
355
|
-
code: "auth/no-user",
|
|
356
|
-
message: "No user provided",
|
|
357
|
-
requiresReauth: false,
|
|
358
|
-
},
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (user.isAnonymous) {
|
|
363
|
-
return {
|
|
364
|
-
success: false,
|
|
365
|
-
error: {
|
|
366
|
-
code: "auth/anonymous-user",
|
|
367
|
-
message: "Cannot delete anonymous account",
|
|
368
|
-
requiresReauth: false,
|
|
369
|
-
},
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
|
|
73
|
+
export async function deleteUserAccount(user: User): Promise<AccountDeletionResult> {
|
|
74
|
+
if (!user || user.isAnonymous) return { success: false, error: { code: "auth/invalid", message: "Invalid user", requiresReauth: false } };
|
|
373
75
|
try {
|
|
374
76
|
await deleteUser(user);
|
|
375
77
|
return { success: true };
|
|
376
|
-
} catch (error) {
|
|
377
|
-
|
|
378
|
-
const requiresReauth = firebaseError.code === "auth/requires-recent-login";
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
success: false,
|
|
382
|
-
error: {
|
|
383
|
-
code: firebaseError.code || "auth/unknown",
|
|
384
|
-
message: requiresReauth
|
|
385
|
-
? "Please sign in again before deleting your account"
|
|
386
|
-
: firebaseError.message || "Failed to delete account",
|
|
387
|
-
requiresReauth,
|
|
388
|
-
},
|
|
389
|
-
};
|
|
78
|
+
} catch (error: any) {
|
|
79
|
+
return { success: false, error: { code: error.code || "auth/failed", message: error.message, requiresReauth: error.code === "auth/requires-recent-login" } };
|
|
390
80
|
}
|
|
391
81
|
}
|