valtech-components 2.0.452 → 2.0.453
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/esm2022/lib/services/auth/auth-state.service.mjs +173 -0
- package/esm2022/lib/services/auth/auth.service.mjs +454 -0
- package/esm2022/lib/services/auth/config.mjs +76 -0
- package/esm2022/lib/services/auth/guards.mjs +194 -0
- package/esm2022/lib/services/auth/index.mjs +70 -0
- package/esm2022/lib/services/auth/interceptor.mjs +98 -0
- package/esm2022/lib/services/auth/storage.service.mjs +141 -0
- package/esm2022/lib/services/auth/sync.service.mjs +149 -0
- package/esm2022/lib/services/auth/token.service.mjs +113 -0
- package/esm2022/lib/services/auth/types.mjs +29 -0
- package/esm2022/lib/services/firebase/config.mjs +108 -0
- package/esm2022/lib/services/firebase/firebase.service.mjs +288 -0
- package/esm2022/lib/services/firebase/firestore-collection.mjs +254 -0
- package/esm2022/lib/services/firebase/firestore.service.mjs +509 -0
- package/esm2022/lib/services/firebase/index.mjs +49 -0
- package/esm2022/lib/services/firebase/messaging.service.mjs +512 -0
- package/esm2022/lib/services/firebase/shared-config.mjs +138 -0
- package/esm2022/lib/services/firebase/storage.service.mjs +422 -0
- package/esm2022/lib/services/firebase/types.mjs +8 -0
- package/esm2022/lib/services/firebase/utils/path-builder.mjs +195 -0
- package/esm2022/lib/services/firebase/utils/query-builder.mjs +302 -0
- package/esm2022/public-api.mjs +3 -5
- package/fesm2022/valtech-components.mjs +4195 -4
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/services/auth/auth-state.service.d.ts +85 -0
- package/lib/services/auth/auth.service.d.ts +146 -0
- package/lib/services/auth/config.d.ts +38 -0
- package/lib/services/auth/guards.d.ts +123 -0
- package/lib/services/auth/index.d.ts +63 -0
- package/lib/services/auth/interceptor.d.ts +22 -0
- package/lib/services/auth/storage.service.d.ts +48 -0
- package/lib/services/auth/sync.service.d.ts +49 -0
- package/lib/services/auth/token.service.d.ts +51 -0
- package/lib/services/auth/types.d.ts +315 -0
- package/lib/services/firebase/config.d.ts +49 -0
- package/lib/services/firebase/firebase.service.d.ts +140 -0
- package/lib/services/firebase/firestore-collection.d.ts +175 -0
- package/lib/services/firebase/firestore.service.d.ts +304 -0
- package/lib/services/firebase/index.d.ts +39 -0
- package/lib/services/firebase/messaging.service.d.ts +263 -0
- package/lib/services/firebase/shared-config.d.ts +126 -0
- package/lib/services/firebase/storage.service.d.ts +206 -0
- package/lib/services/firebase/types.d.ts +281 -0
- package/lib/services/firebase/utils/path-builder.d.ts +132 -0
- package/lib/services/firebase/utils/query-builder.d.ts +210 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Shared Configuration
|
|
3
|
+
*
|
|
4
|
+
* Configuración base de Firebase compartida entre todas las apps del monorepo.
|
|
5
|
+
* Los secrets (apiKey, appId) se inyectan en build time via environment.
|
|
6
|
+
*/
|
|
7
|
+
export const APP_IDS = {
|
|
8
|
+
DEMO: 'demo',
|
|
9
|
+
SHOWCASE: 'showcase',
|
|
10
|
+
ADMIN_PORTAL: 'admin-portal',
|
|
11
|
+
APP: 'app',
|
|
12
|
+
};
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// FIREBASE PROJECT IDS
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* IDs de los proyectos Firebase por ambiente.
|
|
18
|
+
* Deben coincidir con los proyectos creados en Firebase Console.
|
|
19
|
+
*/
|
|
20
|
+
export const FIREBASE_PROJECTS = {
|
|
21
|
+
development: 'myvaltech-dev',
|
|
22
|
+
production: 'myvaltech-prod',
|
|
23
|
+
};
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// EMULATOR CONFIGURATION (shared across all apps)
|
|
26
|
+
// ============================================================================
|
|
27
|
+
/**
|
|
28
|
+
* Configuración de emuladores compartida.
|
|
29
|
+
* Todos los puertos deben coincidir con frontend/firebase/firebase.json
|
|
30
|
+
*/
|
|
31
|
+
export const SHARED_EMULATOR_CONFIG = {
|
|
32
|
+
firestore: { host: 'localhost', port: 9080 },
|
|
33
|
+
storage: { host: 'localhost', port: 9199 },
|
|
34
|
+
auth: { host: 'localhost', port: 9099 },
|
|
35
|
+
};
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// STORAGE PATH BUILDERS
|
|
38
|
+
// ============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Genera paths de Storage con namespace por app.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Path específico de la app
|
|
44
|
+
* storagePaths.forApp('showcase', 'uploads', 'image.jpg')
|
|
45
|
+
* // => 'showcase/uploads/image.jpg'
|
|
46
|
+
*
|
|
47
|
+
* // Path compartido
|
|
48
|
+
* storagePaths.shared.profilePhoto('user123', 'avatar.jpg')
|
|
49
|
+
* // => 'profile-photos/user123/avatar.jpg'
|
|
50
|
+
*/
|
|
51
|
+
export const storagePaths = {
|
|
52
|
+
/** Carpeta específica de la app: {appId}/{...paths} */
|
|
53
|
+
forApp: (appId, ...paths) => [appId, ...paths].join('/'),
|
|
54
|
+
/** Carpetas compartidas (sin namespace) */
|
|
55
|
+
shared: {
|
|
56
|
+
/** Foto de perfil de usuario */
|
|
57
|
+
profilePhoto: (userId, fileName) => `profile-photos/${userId}/${fileName}`,
|
|
58
|
+
/** Archivos públicos accesibles sin autenticación */
|
|
59
|
+
public: (...paths) => `public/${paths.join('/')}`,
|
|
60
|
+
},
|
|
61
|
+
/** Carpetas de desarrollo (acceso libre en emuladores) */
|
|
62
|
+
demo: (...paths) => `demo/${paths.join('/')}`,
|
|
63
|
+
};
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// FIRESTORE COLLECTION BUILDERS
|
|
66
|
+
// ============================================================================
|
|
67
|
+
/**
|
|
68
|
+
* Genera paths de colecciones con namespace por app.
|
|
69
|
+
*
|
|
70
|
+
* IMPORTANTE: La estructura es /apps/{appId}/{collection}/{docId}
|
|
71
|
+
* Firestore requiere número impar de segmentos para paths de colección.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // Colección específica de la app
|
|
75
|
+
* collections.forApp('showcase', 'items')
|
|
76
|
+
* // => 'apps/showcase/items'
|
|
77
|
+
*
|
|
78
|
+
* // Colección de desarrollo
|
|
79
|
+
* collections.forApp('demo', 'items')
|
|
80
|
+
* // => 'apps/demo/items'
|
|
81
|
+
*
|
|
82
|
+
* // Colección compartida
|
|
83
|
+
* collections.shared.users
|
|
84
|
+
* // => 'users'
|
|
85
|
+
*/
|
|
86
|
+
export const collections = {
|
|
87
|
+
/** Colección específica de la app: apps/{appId}/{collection} */
|
|
88
|
+
forApp: (appId, collectionName) => `apps/${appId}/${collectionName}`,
|
|
89
|
+
/** Colecciones compartidas (sin namespace, nivel raíz) */
|
|
90
|
+
shared: {
|
|
91
|
+
/** Usuarios del sistema */
|
|
92
|
+
users: 'users',
|
|
93
|
+
/** Perfiles públicos */
|
|
94
|
+
profiles: 'profiles',
|
|
95
|
+
/** Notificaciones de usuarios */
|
|
96
|
+
notifications: 'notifications',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Crea la configuración completa de Firebase desde variables de entorno.
|
|
101
|
+
* Usa esto en el environment.ts de cada app.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // environment.ts
|
|
105
|
+
* export const environment = {
|
|
106
|
+
* firebase: createFirebaseConfig(
|
|
107
|
+
* {
|
|
108
|
+
* apiKey: 'AIza...',
|
|
109
|
+
* authDomain: 'myvaltech-dev.firebaseapp.com',
|
|
110
|
+
* projectId: 'myvaltech-dev',
|
|
111
|
+
* storageBucket: 'myvaltech-dev.appspot.com',
|
|
112
|
+
* messagingSenderId: '123456789',
|
|
113
|
+
* appId: '1:123456789:web:abc123',
|
|
114
|
+
* },
|
|
115
|
+
* { useEmulators: true, persistence: true }
|
|
116
|
+
* ),
|
|
117
|
+
* };
|
|
118
|
+
*/
|
|
119
|
+
export function createFirebaseConfig(envConfig, options = {}) {
|
|
120
|
+
const { useEmulators = false, persistence = true, enableMessaging = false, messagingVapidKey } = options;
|
|
121
|
+
return {
|
|
122
|
+
firebase: envConfig,
|
|
123
|
+
persistence,
|
|
124
|
+
enableMessaging,
|
|
125
|
+
messagingVapidKey,
|
|
126
|
+
emulator: useEmulators ? SHARED_EMULATOR_CONFIG : undefined,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// UTILITY: Check if running in emulator mode
|
|
131
|
+
// ============================================================================
|
|
132
|
+
/**
|
|
133
|
+
* Verifica si la configuración tiene emuladores habilitados
|
|
134
|
+
*/
|
|
135
|
+
export function isEmulatorMode(config) {
|
|
136
|
+
return config.emulator !== undefined;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hhcmVkLWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc2VydmljZXMvZmlyZWJhc2Uvc2hhcmVkLWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7R0FLRztBQWNILE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRztJQUNyQixJQUFJLEVBQUUsTUFBZTtJQUNyQixRQUFRLEVBQUUsVUFBbUI7SUFDN0IsWUFBWSxFQUFFLGNBQXVCO0lBQ3JDLEdBQUcsRUFBRSxLQUFjO0NBQ1gsQ0FBQztBQUVYLCtFQUErRTtBQUMvRSx1QkFBdUI7QUFDdkIsK0VBQStFO0FBRS9FOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHO0lBQy9CLFdBQVcsRUFBRSxlQUFlO0lBQzVCLFVBQVUsRUFBRSxnQkFBZ0I7Q0FDcEIsQ0FBQztBQUVYLCtFQUErRTtBQUMvRSxrREFBa0Q7QUFDbEQsK0VBQStFO0FBRS9FOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFtQjtJQUNwRCxTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUU7SUFDNUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFO0lBQzFDLElBQUksRUFBRSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRTtDQUN4QyxDQUFDO0FBRUYsK0VBQStFO0FBQy9FLHdCQUF3QjtBQUN4QiwrRUFBK0U7QUFFL0U7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUc7SUFDMUIsdURBQXVEO0lBQ3ZELE1BQU0sRUFBRSxDQUFDLEtBQVksRUFBRSxHQUFHLEtBQWUsRUFBVSxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsR0FBRyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDO0lBRWpGLDJDQUEyQztJQUMzQyxNQUFNLEVBQUU7UUFDTixnQ0FBZ0M7UUFDaEMsWUFBWSxFQUFFLENBQUMsTUFBYyxFQUFFLFFBQWdCLEVBQVUsRUFBRSxDQUN6RCxrQkFBa0IsTUFBTSxJQUFJLFFBQVEsRUFBRTtRQUV4QyxxREFBcUQ7UUFDckQsTUFBTSxFQUFFLENBQUMsR0FBRyxLQUFlLEVBQVUsRUFBRSxDQUFDLFVBQVUsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRTtLQUNwRTtJQUVELDBEQUEwRDtJQUMxRCxJQUFJLEVBQUUsQ0FBQyxHQUFHLEtBQWUsRUFBVSxFQUFFLENBQUMsUUFBUSxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFO0NBQ2hFLENBQUM7QUFFRiwrRUFBK0U7QUFDL0UsZ0NBQWdDO0FBQ2hDLCtFQUErRTtBQUUvRTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHO0lBQ3pCLGdFQUFnRTtJQUNoRSxNQUFNLEVBQUUsQ0FBQyxLQUFZLEVBQUUsY0FBc0IsRUFBVSxFQUFFLENBQUMsUUFBUSxLQUFLLElBQUksY0FBYyxFQUFFO0lBRTNGLDBEQUEwRDtJQUMxRCxNQUFNLEVBQUU7UUFDTiwyQkFBMkI7UUFDM0IsS0FBSyxFQUFFLE9BQU87UUFDZCx3QkFBd0I7UUFDeEIsUUFBUSxFQUFFLFVBQVU7UUFDcEIsaUNBQWlDO1FBQ2pDLGFBQWEsRUFBRSxlQUFlO0tBQy9CO0NBQ0YsQ0FBQztBQW9CRjs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNILE1BQU0sVUFBVSxvQkFBb0IsQ0FDbEMsU0FBeUIsRUFDekIsVUFBdUMsRUFBRTtJQUV6QyxNQUFNLEVBQUUsWUFBWSxHQUFHLEtBQUssRUFBRSxXQUFXLEdBQUcsSUFBSSxFQUFFLGVBQWUsR0FBRyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsR0FDNUYsT0FBTyxDQUFDO0lBRVYsT0FBTztRQUNMLFFBQVEsRUFBRSxTQUFTO1FBQ25CLFdBQVc7UUFDWCxlQUFlO1FBQ2YsaUJBQWlCO1FBQ2pCLFFBQVEsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQUFDLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxTQUFTO0tBQzVELENBQUM7QUFDSixDQUFDO0FBRUQsK0VBQStFO0FBQy9FLDZDQUE2QztBQUM3QywrRUFBK0U7QUFFL0U7O0dBRUc7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUFDLE1BQTZCO0lBQzFELE9BQU8sTUFBTSxDQUFDLFFBQVEsS0FBSyxTQUFTLENBQUM7QUFDdkMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRmlyZWJhc2UgU2hhcmVkIENvbmZpZ3VyYXRpb25cbiAqXG4gKiBDb25maWd1cmFjacOzbiBiYXNlIGRlIEZpcmViYXNlIGNvbXBhcnRpZGEgZW50cmUgdG9kYXMgbGFzIGFwcHMgZGVsIG1vbm9yZXBvLlxuICogTG9zIHNlY3JldHMgKGFwaUtleSwgYXBwSWQpIHNlIGlueWVjdGFuIGVuIGJ1aWxkIHRpbWUgdmlhIGVudmlyb25tZW50LlxuICovXG5cbmltcG9ydCB7IEVtdWxhdG9yQ29uZmlnLCBGaXJlYmFzZUNvbmZpZywgVmFsdGVjaEZpcmViYXNlQ29uZmlnIH0gZnJvbSAnLi90eXBlcyc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEFQUCBJRFMgLSBJZGVudGlmaWNhZG9yZXMgw7puaWNvcyBwb3IgYXBsaWNhY2nDs25cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBJZGVudGlmaWNhZG9yZXMgZGUgbGFzIGFwcHMgZGVsIG1vbm9yZXBvLlxuICogVXNhZG9zIHBhcmEgbmFtZXNwYWNpbmcgZGUgY29sZWNjaW9uZXMgRmlyZXN0b3JlIHkgcGF0aHMgZGUgU3RvcmFnZS5cbiAqL1xuZXhwb3J0IHR5cGUgQXBwSWQgPSAnZGVtbycgfCAnc2hvd2Nhc2UnIHwgJ2FkbWluLXBvcnRhbCcgfCAnYXBwJztcblxuZXhwb3J0IGNvbnN0IEFQUF9JRFMgPSB7XG4gIERFTU86ICdkZW1vJyBhcyBBcHBJZCxcbiAgU0hPV0NBU0U6ICdzaG93Y2FzZScgYXMgQXBwSWQsXG4gIEFETUlOX1BPUlRBTDogJ2FkbWluLXBvcnRhbCcgYXMgQXBwSWQsXG4gIEFQUDogJ2FwcCcgYXMgQXBwSWQsXG59IGFzIGNvbnN0O1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBGSVJFQkFTRSBQUk9KRUNUIElEU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIElEcyBkZSBsb3MgcHJveWVjdG9zIEZpcmViYXNlIHBvciBhbWJpZW50ZS5cbiAqIERlYmVuIGNvaW5jaWRpciBjb24gbG9zIHByb3llY3RvcyBjcmVhZG9zIGVuIEZpcmViYXNlIENvbnNvbGUuXG4gKi9cbmV4cG9ydCBjb25zdCBGSVJFQkFTRV9QUk9KRUNUUyA9IHtcbiAgZGV2ZWxvcG1lbnQ6ICdteXZhbHRlY2gtZGV2JyxcbiAgcHJvZHVjdGlvbjogJ215dmFsdGVjaC1wcm9kJyxcbn0gYXMgY29uc3Q7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEVNVUxBVE9SIENPTkZJR1VSQVRJT04gKHNoYXJlZCBhY3Jvc3MgYWxsIGFwcHMpXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogQ29uZmlndXJhY2nDs24gZGUgZW11bGFkb3JlcyBjb21wYXJ0aWRhLlxuICogVG9kb3MgbG9zIHB1ZXJ0b3MgZGViZW4gY29pbmNpZGlyIGNvbiBmcm9udGVuZC9maXJlYmFzZS9maXJlYmFzZS5qc29uXG4gKi9cbmV4cG9ydCBjb25zdCBTSEFSRURfRU1VTEFUT1JfQ09ORklHOiBFbXVsYXRvckNvbmZpZyA9IHtcbiAgZmlyZXN0b3JlOiB7IGhvc3Q6ICdsb2NhbGhvc3QnLCBwb3J0OiA5MDgwIH0sXG4gIHN0b3JhZ2U6IHsgaG9zdDogJ2xvY2FsaG9zdCcsIHBvcnQ6IDkxOTkgfSxcbiAgYXV0aDogeyBob3N0OiAnbG9jYWxob3N0JywgcG9ydDogOTA5OSB9LFxufTtcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gU1RPUkFHRSBQQVRIIEJVSUxERVJTXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogR2VuZXJhIHBhdGhzIGRlIFN0b3JhZ2UgY29uIG5hbWVzcGFjZSBwb3IgYXBwLlxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBQYXRoIGVzcGVjw61maWNvIGRlIGxhIGFwcFxuICogc3RvcmFnZVBhdGhzLmZvckFwcCgnc2hvd2Nhc2UnLCAndXBsb2FkcycsICdpbWFnZS5qcGcnKVxuICogLy8gPT4gJ3Nob3djYXNlL3VwbG9hZHMvaW1hZ2UuanBnJ1xuICpcbiAqIC8vIFBhdGggY29tcGFydGlkb1xuICogc3RvcmFnZVBhdGhzLnNoYXJlZC5wcm9maWxlUGhvdG8oJ3VzZXIxMjMnLCAnYXZhdGFyLmpwZycpXG4gKiAvLyA9PiAncHJvZmlsZS1waG90b3MvdXNlcjEyMy9hdmF0YXIuanBnJ1xuICovXG5leHBvcnQgY29uc3Qgc3RvcmFnZVBhdGhzID0ge1xuICAvKiogQ2FycGV0YSBlc3BlY8OtZmljYSBkZSBsYSBhcHA6IHthcHBJZH0vey4uLnBhdGhzfSAqL1xuICBmb3JBcHA6IChhcHBJZDogQXBwSWQsIC4uLnBhdGhzOiBzdHJpbmdbXSk6IHN0cmluZyA9PiBbYXBwSWQsIC4uLnBhdGhzXS5qb2luKCcvJyksXG5cbiAgLyoqIENhcnBldGFzIGNvbXBhcnRpZGFzIChzaW4gbmFtZXNwYWNlKSAqL1xuICBzaGFyZWQ6IHtcbiAgICAvKiogRm90byBkZSBwZXJmaWwgZGUgdXN1YXJpbyAqL1xuICAgIHByb2ZpbGVQaG90bzogKHVzZXJJZDogc3RyaW5nLCBmaWxlTmFtZTogc3RyaW5nKTogc3RyaW5nID0+XG4gICAgICBgcHJvZmlsZS1waG90b3MvJHt1c2VySWR9LyR7ZmlsZU5hbWV9YCxcblxuICAgIC8qKiBBcmNoaXZvcyBww7pibGljb3MgYWNjZXNpYmxlcyBzaW4gYXV0ZW50aWNhY2nDs24gKi9cbiAgICBwdWJsaWM6ICguLi5wYXRoczogc3RyaW5nW10pOiBzdHJpbmcgPT4gYHB1YmxpYy8ke3BhdGhzLmpvaW4oJy8nKX1gLFxuICB9LFxuXG4gIC8qKiBDYXJwZXRhcyBkZSBkZXNhcnJvbGxvIChhY2Nlc28gbGlicmUgZW4gZW11bGFkb3JlcykgKi9cbiAgZGVtbzogKC4uLnBhdGhzOiBzdHJpbmdbXSk6IHN0cmluZyA9PiBgZGVtby8ke3BhdGhzLmpvaW4oJy8nKX1gLFxufTtcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gRklSRVNUT1JFIENPTExFQ1RJT04gQlVJTERFUlNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBHZW5lcmEgcGF0aHMgZGUgY29sZWNjaW9uZXMgY29uIG5hbWVzcGFjZSBwb3IgYXBwLlxuICpcbiAqIElNUE9SVEFOVEU6IExhIGVzdHJ1Y3R1cmEgZXMgL2FwcHMve2FwcElkfS97Y29sbGVjdGlvbn0ve2RvY0lkfVxuICogRmlyZXN0b3JlIHJlcXVpZXJlIG7Dum1lcm8gaW1wYXIgZGUgc2VnbWVudG9zIHBhcmEgcGF0aHMgZGUgY29sZWNjacOzbi5cbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gQ29sZWNjacOzbiBlc3BlY8OtZmljYSBkZSBsYSBhcHBcbiAqIGNvbGxlY3Rpb25zLmZvckFwcCgnc2hvd2Nhc2UnLCAnaXRlbXMnKVxuICogLy8gPT4gJ2FwcHMvc2hvd2Nhc2UvaXRlbXMnXG4gKlxuICogLy8gQ29sZWNjacOzbiBkZSBkZXNhcnJvbGxvXG4gKiBjb2xsZWN0aW9ucy5mb3JBcHAoJ2RlbW8nLCAnaXRlbXMnKVxuICogLy8gPT4gJ2FwcHMvZGVtby9pdGVtcydcbiAqXG4gKiAvLyBDb2xlY2Npw7NuIGNvbXBhcnRpZGFcbiAqIGNvbGxlY3Rpb25zLnNoYXJlZC51c2Vyc1xuICogLy8gPT4gJ3VzZXJzJ1xuICovXG5leHBvcnQgY29uc3QgY29sbGVjdGlvbnMgPSB7XG4gIC8qKiBDb2xlY2Npw7NuIGVzcGVjw61maWNhIGRlIGxhIGFwcDogYXBwcy97YXBwSWR9L3tjb2xsZWN0aW9ufSAqL1xuICBmb3JBcHA6IChhcHBJZDogQXBwSWQsIGNvbGxlY3Rpb25OYW1lOiBzdHJpbmcpOiBzdHJpbmcgPT4gYGFwcHMvJHthcHBJZH0vJHtjb2xsZWN0aW9uTmFtZX1gLFxuXG4gIC8qKiBDb2xlY2Npb25lcyBjb21wYXJ0aWRhcyAoc2luIG5hbWVzcGFjZSwgbml2ZWwgcmHDrXopICovXG4gIHNoYXJlZDoge1xuICAgIC8qKiBVc3VhcmlvcyBkZWwgc2lzdGVtYSAqL1xuICAgIHVzZXJzOiAndXNlcnMnLFxuICAgIC8qKiBQZXJmaWxlcyBww7pibGljb3MgKi9cbiAgICBwcm9maWxlczogJ3Byb2ZpbGVzJyxcbiAgICAvKiogTm90aWZpY2FjaW9uZXMgZGUgdXN1YXJpb3MgKi9cbiAgICBub3RpZmljYXRpb25zOiAnbm90aWZpY2F0aW9ucycsXG4gIH0sXG59O1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBIRUxQRVI6IENyZWF0ZSBGaXJlYmFzZSBDb25maWcgZnJvbSBFbnZpcm9ubWVudFxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIE9wY2lvbmVzIHBhcmEgY3JlYXIgbGEgY29uZmlndXJhY2nDs24gZGUgRmlyZWJhc2VcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDcmVhdGVGaXJlYmFzZUNvbmZpZ09wdGlvbnMge1xuICAvKiogVXNhciBlbXVsYWRvcmVzIGxvY2FsZXMgKHBhcmEgZGVzYXJyb2xsbykgKi9cbiAgdXNlRW11bGF0b3JzPzogYm9vbGVhbjtcbiAgLyoqIEhhYmlsaXRhciBwZXJzaXN0ZW5jaWEgb2ZmbGluZSBkZSBGaXJlc3RvcmUgKi9cbiAgcGVyc2lzdGVuY2U/OiBib29sZWFuO1xuICAvKiogSGFiaWxpdGFyIEZpcmViYXNlIENsb3VkIE1lc3NhZ2luZyAqL1xuICBlbmFibGVNZXNzYWdpbmc/OiBib29sZWFuO1xuICAvKiogVkFQSUQga2V5IHBhcmEgRkNNIChyZXF1ZXJpZG8gc2kgZW5hYmxlTWVzc2FnaW5nIGVzIHRydWUpICovXG4gIG1lc3NhZ2luZ1ZhcGlkS2V5Pzogc3RyaW5nO1xufVxuXG4vKipcbiAqIENyZWEgbGEgY29uZmlndXJhY2nDs24gY29tcGxldGEgZGUgRmlyZWJhc2UgZGVzZGUgdmFyaWFibGVzIGRlIGVudG9ybm8uXG4gKiBVc2EgZXN0byBlbiBlbCBlbnZpcm9ubWVudC50cyBkZSBjYWRhIGFwcC5cbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gZW52aXJvbm1lbnQudHNcbiAqIGV4cG9ydCBjb25zdCBlbnZpcm9ubWVudCA9IHtcbiAqICAgZmlyZWJhc2U6IGNyZWF0ZUZpcmViYXNlQ29uZmlnKFxuICogICAgIHtcbiAqICAgICAgIGFwaUtleTogJ0FJemEuLi4nLFxuICogICAgICAgYXV0aERvbWFpbjogJ215dmFsdGVjaC1kZXYuZmlyZWJhc2VhcHAuY29tJyxcbiAqICAgICAgIHByb2plY3RJZDogJ215dmFsdGVjaC1kZXYnLFxuICogICAgICAgc3RvcmFnZUJ1Y2tldDogJ215dmFsdGVjaC1kZXYuYXBwc3BvdC5jb20nLFxuICogICAgICAgbWVzc2FnaW5nU2VuZGVySWQ6ICcxMjM0NTY3ODknLFxuICogICAgICAgYXBwSWQ6ICcxOjEyMzQ1Njc4OTp3ZWI6YWJjMTIzJyxcbiAqICAgICB9LFxuICogICAgIHsgdXNlRW11bGF0b3JzOiB0cnVlLCBwZXJzaXN0ZW5jZTogdHJ1ZSB9XG4gKiAgICksXG4gKiB9O1xuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRmlyZWJhc2VDb25maWcoXG4gIGVudkNvbmZpZzogRmlyZWJhc2VDb25maWcsXG4gIG9wdGlvbnM6IENyZWF0ZUZpcmViYXNlQ29uZmlnT3B0aW9ucyA9IHt9XG4pOiBWYWx0ZWNoRmlyZWJhc2VDb25maWcge1xuICBjb25zdCB7IHVzZUVtdWxhdG9ycyA9IGZhbHNlLCBwZXJzaXN0ZW5jZSA9IHRydWUsIGVuYWJsZU1lc3NhZ2luZyA9IGZhbHNlLCBtZXNzYWdpbmdWYXBpZEtleSB9ID1cbiAgICBvcHRpb25zO1xuXG4gIHJldHVybiB7XG4gICAgZmlyZWJhc2U6IGVudkNvbmZpZyxcbiAgICBwZXJzaXN0ZW5jZSxcbiAgICBlbmFibGVNZXNzYWdpbmcsXG4gICAgbWVzc2FnaW5nVmFwaWRLZXksXG4gICAgZW11bGF0b3I6IHVzZUVtdWxhdG9ycyA/IFNIQVJFRF9FTVVMQVRPUl9DT05GSUcgOiB1bmRlZmluZWQsXG4gIH07XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFVUSUxJVFk6IENoZWNrIGlmIHJ1bm5pbmcgaW4gZW11bGF0b3IgbW9kZVxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIFZlcmlmaWNhIHNpIGxhIGNvbmZpZ3VyYWNpw7NuIHRpZW5lIGVtdWxhZG9yZXMgaGFiaWxpdGFkb3NcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzRW11bGF0b3JNb2RlKGNvbmZpZzogVmFsdGVjaEZpcmViYXNlQ29uZmlnKTogYm9vbGVhbiB7XG4gIHJldHVybiBjb25maWcuZW11bGF0b3IgIT09IHVuZGVmaW5lZDtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Service
|
|
3
|
+
*
|
|
4
|
+
* Servicio para operaciones de Firebase Storage.
|
|
5
|
+
* Soporta upload con tracking de progreso, download y gestión de archivos.
|
|
6
|
+
*/
|
|
7
|
+
import { Injectable } from '@angular/core';
|
|
8
|
+
import { deleteObject, getDownloadURL, getMetadata, listAll, ref, uploadBytesResumable, } from '@angular/fire/storage';
|
|
9
|
+
import { BehaviorSubject } from 'rxjs';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
import * as i1 from "@angular/fire/storage";
|
|
12
|
+
/**
|
|
13
|
+
* Servicio para Firebase Storage.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* @Component({...})
|
|
18
|
+
* export class FileUploadComponent {
|
|
19
|
+
* private storage = inject(StorageService);
|
|
20
|
+
*
|
|
21
|
+
* uploadProgress = signal<number>(0);
|
|
22
|
+
* downloadUrl = signal<string | null>(null);
|
|
23
|
+
*
|
|
24
|
+
* async onFileSelected(event: Event) {
|
|
25
|
+
* const file = (event.target as HTMLInputElement).files?.[0];
|
|
26
|
+
* if (!file) return;
|
|
27
|
+
*
|
|
28
|
+
* // Upload con progreso
|
|
29
|
+
* this.storage.upload(`uploads/${file.name}`, file).subscribe({
|
|
30
|
+
* next: (progress) => this.uploadProgress.set(progress.percentage),
|
|
31
|
+
* complete: async () => {
|
|
32
|
+
* const url = await this.storage.getDownloadUrl(`uploads/${file.name}`);
|
|
33
|
+
* this.downloadUrl.set(url);
|
|
34
|
+
* }
|
|
35
|
+
* });
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export class StorageService {
|
|
41
|
+
constructor(storage) {
|
|
42
|
+
this.storage = storage;
|
|
43
|
+
}
|
|
44
|
+
// ===========================================================================
|
|
45
|
+
// UPLOAD
|
|
46
|
+
// ===========================================================================
|
|
47
|
+
/**
|
|
48
|
+
* Sube un archivo con tracking de progreso.
|
|
49
|
+
*
|
|
50
|
+
* @param path - Ruta en Storage donde guardar el archivo
|
|
51
|
+
* @param file - Archivo a subir (File o Blob)
|
|
52
|
+
* @param metadata - Metadata opcional (contentType, customMetadata)
|
|
53
|
+
* @returns Observable que emite el progreso y completa cuando termina
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* // Upload básico
|
|
58
|
+
* storage.upload('images/photo.jpg', file).subscribe({
|
|
59
|
+
* next: (progress) => console.log(`${progress.percentage}%`),
|
|
60
|
+
* complete: () => console.log('Upload completado')
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* // Con metadata
|
|
64
|
+
* storage.upload('docs/report.pdf', file, {
|
|
65
|
+
* contentType: 'application/pdf',
|
|
66
|
+
* customMetadata: { uploadedBy: 'user123' }
|
|
67
|
+
* }).subscribe(...);
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
upload(path, file, metadata) {
|
|
71
|
+
const storageRef = ref(this.storage, path);
|
|
72
|
+
const uploadMetadata = {
|
|
73
|
+
contentType: metadata?.contentType || (file instanceof File ? file.type : undefined),
|
|
74
|
+
customMetadata: metadata?.customMetadata,
|
|
75
|
+
cacheControl: metadata?.cacheControl,
|
|
76
|
+
};
|
|
77
|
+
const task = uploadBytesResumable(storageRef, file, uploadMetadata);
|
|
78
|
+
const progress$ = new BehaviorSubject({
|
|
79
|
+
bytesTransferred: 0,
|
|
80
|
+
totalBytes: file.size,
|
|
81
|
+
percentage: 0,
|
|
82
|
+
state: 'running',
|
|
83
|
+
});
|
|
84
|
+
task.on('state_changed', (snapshot) => {
|
|
85
|
+
progress$.next({
|
|
86
|
+
bytesTransferred: snapshot.bytesTransferred,
|
|
87
|
+
totalBytes: snapshot.totalBytes,
|
|
88
|
+
percentage: Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100),
|
|
89
|
+
state: this.mapTaskState(snapshot.state),
|
|
90
|
+
});
|
|
91
|
+
}, (error) => {
|
|
92
|
+
progress$.next({
|
|
93
|
+
bytesTransferred: 0,
|
|
94
|
+
totalBytes: file.size,
|
|
95
|
+
percentage: 0,
|
|
96
|
+
state: 'error',
|
|
97
|
+
});
|
|
98
|
+
progress$.error(this.getErrorMessage(error));
|
|
99
|
+
}, () => {
|
|
100
|
+
progress$.next({
|
|
101
|
+
bytesTransferred: file.size,
|
|
102
|
+
totalBytes: file.size,
|
|
103
|
+
percentage: 100,
|
|
104
|
+
state: 'success',
|
|
105
|
+
});
|
|
106
|
+
progress$.complete();
|
|
107
|
+
});
|
|
108
|
+
return progress$.asObservable();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Sube un archivo y retorna la URL de descarga al completar.
|
|
112
|
+
*
|
|
113
|
+
* @param path - Ruta en Storage
|
|
114
|
+
* @param file - Archivo a subir
|
|
115
|
+
* @param metadata - Metadata opcional
|
|
116
|
+
* @returns Resultado del upload con URL de descarga
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const result = await storage.uploadAndGetUrl('avatars/user123.jpg', file);
|
|
121
|
+
* console.log('URL:', result.downloadUrl);
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
async uploadAndGetUrl(path, file, metadata) {
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
this.upload(path, file, metadata).subscribe({
|
|
127
|
+
complete: async () => {
|
|
128
|
+
try {
|
|
129
|
+
const storageRef = ref(this.storage, path);
|
|
130
|
+
const downloadUrl = await getDownloadURL(storageRef);
|
|
131
|
+
const storedMetadata = await getMetadata(storageRef);
|
|
132
|
+
resolve({
|
|
133
|
+
downloadUrl,
|
|
134
|
+
fullPath: storedMetadata.fullPath,
|
|
135
|
+
name: storedMetadata.name,
|
|
136
|
+
size: storedMetadata.size,
|
|
137
|
+
contentType: storedMetadata.contentType || 'application/octet-stream',
|
|
138
|
+
metadata: storedMetadata.customMetadata || {},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
reject(this.getErrorMessage(error));
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
error: (error) => reject(error),
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Sube un archivo desde una Data URL (base64).
|
|
151
|
+
*
|
|
152
|
+
* @param path - Ruta en Storage
|
|
153
|
+
* @param dataUrl - Data URL (ej: 'data:image/png;base64,...')
|
|
154
|
+
* @param metadata - Metadata opcional
|
|
155
|
+
* @returns Resultado del upload
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* // Desde canvas
|
|
160
|
+
* const dataUrl = canvas.toDataURL('image/png');
|
|
161
|
+
* const result = await storage.uploadFromDataUrl('images/drawing.png', dataUrl);
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
async uploadFromDataUrl(path, dataUrl, metadata) {
|
|
165
|
+
// Extraer content type y datos base64
|
|
166
|
+
const matches = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
|
|
167
|
+
if (!matches) {
|
|
168
|
+
throw new Error('Data URL inválida');
|
|
169
|
+
}
|
|
170
|
+
const contentType = matches[1];
|
|
171
|
+
const base64Data = matches[2];
|
|
172
|
+
// Convertir base64 a Blob
|
|
173
|
+
const byteCharacters = atob(base64Data);
|
|
174
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
175
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
176
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
177
|
+
}
|
|
178
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
179
|
+
const blob = new Blob([byteArray], { type: contentType });
|
|
180
|
+
return this.uploadAndGetUrl(path, blob, {
|
|
181
|
+
contentType,
|
|
182
|
+
...metadata,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// ===========================================================================
|
|
186
|
+
// DOWNLOAD
|
|
187
|
+
// ===========================================================================
|
|
188
|
+
/**
|
|
189
|
+
* Obtiene la URL de descarga de un archivo.
|
|
190
|
+
*
|
|
191
|
+
* @param path - Ruta del archivo en Storage
|
|
192
|
+
* @returns URL de descarga
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```typescript
|
|
196
|
+
* const url = await storage.getDownloadUrl('images/photo.jpg');
|
|
197
|
+
* // Usar en <img [src]="url">
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
async getDownloadUrl(path) {
|
|
201
|
+
try {
|
|
202
|
+
const storageRef = ref(this.storage, path);
|
|
203
|
+
return await getDownloadURL(storageRef);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
throw new Error(this.getErrorMessage(error));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Obtiene la metadata de un archivo.
|
|
211
|
+
*
|
|
212
|
+
* @param path - Ruta del archivo
|
|
213
|
+
* @returns Metadata del archivo
|
|
214
|
+
*/
|
|
215
|
+
async getMetadata(path) {
|
|
216
|
+
try {
|
|
217
|
+
const storageRef = ref(this.storage, path);
|
|
218
|
+
const metadata = await getMetadata(storageRef);
|
|
219
|
+
return {
|
|
220
|
+
contentType: metadata.contentType,
|
|
221
|
+
customMetadata: metadata.customMetadata,
|
|
222
|
+
cacheControl: metadata.cacheControl,
|
|
223
|
+
size: metadata.size,
|
|
224
|
+
name: metadata.name,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
throw new Error(this.getErrorMessage(error));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ===========================================================================
|
|
232
|
+
// DELETE
|
|
233
|
+
// ===========================================================================
|
|
234
|
+
/**
|
|
235
|
+
* Elimina un archivo.
|
|
236
|
+
*
|
|
237
|
+
* @param path - Ruta del archivo a eliminar
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* await storage.delete('images/old-photo.jpg');
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
async delete(path) {
|
|
245
|
+
try {
|
|
246
|
+
const storageRef = ref(this.storage, path);
|
|
247
|
+
await deleteObject(storageRef);
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
throw new Error(this.getErrorMessage(error));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Elimina múltiples archivos.
|
|
255
|
+
*
|
|
256
|
+
* @param paths - Array de rutas a eliminar
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* await storage.deleteMultiple([
|
|
261
|
+
* 'images/photo1.jpg',
|
|
262
|
+
* 'images/photo2.jpg'
|
|
263
|
+
* ]);
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
async deleteMultiple(paths) {
|
|
267
|
+
await Promise.all(paths.map((path) => this.delete(path)));
|
|
268
|
+
}
|
|
269
|
+
// ===========================================================================
|
|
270
|
+
// LIST
|
|
271
|
+
// ===========================================================================
|
|
272
|
+
/**
|
|
273
|
+
* Lista archivos en un directorio.
|
|
274
|
+
*
|
|
275
|
+
* @param path - Ruta del directorio
|
|
276
|
+
* @returns Lista de rutas de archivos
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const result = await storage.list('images/');
|
|
281
|
+
* console.log(result.items); // ['images/photo1.jpg', 'images/photo2.jpg']
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
async list(path) {
|
|
285
|
+
try {
|
|
286
|
+
const storageRef = ref(this.storage, path);
|
|
287
|
+
const result = await listAll(storageRef);
|
|
288
|
+
return {
|
|
289
|
+
items: result.items.map((item) => item.fullPath),
|
|
290
|
+
nextPageToken: undefined, // listAll no soporta paginación
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
throw new Error(this.getErrorMessage(error));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// ===========================================================================
|
|
298
|
+
// UTILIDADES
|
|
299
|
+
// ===========================================================================
|
|
300
|
+
/**
|
|
301
|
+
* Genera un nombre de archivo único con timestamp.
|
|
302
|
+
*
|
|
303
|
+
* @param originalName - Nombre original del archivo
|
|
304
|
+
* @param prefix - Prefijo opcional
|
|
305
|
+
* @returns Nombre único
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* const uniqueName = storage.generateFileName('photo.jpg', 'user123');
|
|
310
|
+
* // => 'user123_1703091234567_photo.jpg'
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
generateFileName(originalName, prefix) {
|
|
314
|
+
const timestamp = Date.now();
|
|
315
|
+
const sanitizedName = originalName.replace(/[^a-zA-Z0-9.-]/g, '_');
|
|
316
|
+
if (prefix) {
|
|
317
|
+
return `${prefix}_${timestamp}_${sanitizedName}`;
|
|
318
|
+
}
|
|
319
|
+
return `${timestamp}_${sanitizedName}`;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Genera una ruta única para un archivo.
|
|
323
|
+
*
|
|
324
|
+
* @param directory - Directorio base
|
|
325
|
+
* @param originalName - Nombre original
|
|
326
|
+
* @param prefix - Prefijo opcional
|
|
327
|
+
* @returns Ruta completa única
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```typescript
|
|
331
|
+
* const path = storage.generatePath('uploads', 'photo.jpg', 'user123');
|
|
332
|
+
* // => 'uploads/user123_1703091234567_photo.jpg'
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
generatePath(directory, originalName, prefix) {
|
|
336
|
+
const fileName = this.generateFileName(originalName, prefix);
|
|
337
|
+
const cleanDir = directory.replace(/\/+$/, ''); // Remover / final
|
|
338
|
+
return `${cleanDir}/${fileName}`;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Obtiene la extensión de un archivo.
|
|
342
|
+
*
|
|
343
|
+
* @param filename - Nombre del archivo
|
|
344
|
+
* @returns Extensión (sin el punto)
|
|
345
|
+
*/
|
|
346
|
+
getExtension(filename) {
|
|
347
|
+
const parts = filename.split('.');
|
|
348
|
+
return parts.length > 1 ? parts.pop().toLowerCase() : '';
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Verifica si un archivo es una imagen basándose en su extensión.
|
|
352
|
+
*/
|
|
353
|
+
isImage(filename) {
|
|
354
|
+
const ext = this.getExtension(filename);
|
|
355
|
+
return ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'].includes(ext);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Verifica si un archivo es un documento.
|
|
359
|
+
*/
|
|
360
|
+
isDocument(filename) {
|
|
361
|
+
const ext = this.getExtension(filename);
|
|
362
|
+
return ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'].includes(ext);
|
|
363
|
+
}
|
|
364
|
+
// ===========================================================================
|
|
365
|
+
// MÉTODOS PRIVADOS
|
|
366
|
+
// ===========================================================================
|
|
367
|
+
/**
|
|
368
|
+
* Mapea el estado de la tarea de upload
|
|
369
|
+
*/
|
|
370
|
+
mapTaskState(state) {
|
|
371
|
+
switch (state) {
|
|
372
|
+
case 'running':
|
|
373
|
+
return 'running';
|
|
374
|
+
case 'paused':
|
|
375
|
+
return 'paused';
|
|
376
|
+
case 'success':
|
|
377
|
+
return 'success';
|
|
378
|
+
case 'canceled':
|
|
379
|
+
return 'canceled';
|
|
380
|
+
case 'error':
|
|
381
|
+
return 'error';
|
|
382
|
+
default:
|
|
383
|
+
return 'running';
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Convierte errores de Storage a mensajes en español
|
|
388
|
+
*/
|
|
389
|
+
getErrorMessage(error) {
|
|
390
|
+
if (error instanceof Error) {
|
|
391
|
+
const code = error.code;
|
|
392
|
+
switch (code) {
|
|
393
|
+
case 'storage/object-not-found':
|
|
394
|
+
return 'El archivo no existe';
|
|
395
|
+
case 'storage/unauthorized':
|
|
396
|
+
return 'No tienes permiso para acceder a este archivo';
|
|
397
|
+
case 'storage/canceled':
|
|
398
|
+
return 'La operación fue cancelada';
|
|
399
|
+
case 'storage/quota-exceeded':
|
|
400
|
+
return 'Se ha excedido la cuota de almacenamiento';
|
|
401
|
+
case 'storage/invalid-checksum':
|
|
402
|
+
return 'El archivo está corrupto';
|
|
403
|
+
case 'storage/retry-limit-exceeded':
|
|
404
|
+
return 'Error de conexión. Intenta de nuevo';
|
|
405
|
+
case 'storage/invalid-url':
|
|
406
|
+
return 'URL de archivo inválida';
|
|
407
|
+
case 'storage/invalid-argument':
|
|
408
|
+
return 'Argumento inválido';
|
|
409
|
+
default:
|
|
410
|
+
return error.message || 'Error de almacenamiento desconocido';
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return 'Error de almacenamiento desconocido';
|
|
414
|
+
}
|
|
415
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StorageService, deps: [{ token: i1.Storage }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
416
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StorageService, providedIn: 'root' }); }
|
|
417
|
+
}
|
|
418
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StorageService, decorators: [{
|
|
419
|
+
type: Injectable,
|
|
420
|
+
args: [{ providedIn: 'root' }]
|
|
421
|
+
}], ctorParameters: () => [{ type: i1.Storage }] });
|
|
422
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZS5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9zZXJ2aWNlcy9maXJlYmFzZS9zdG9yYWdlLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFFSCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFDTCxZQUFZLEVBQ1osY0FBYyxFQUNkLFdBQVcsRUFDWCxPQUFPLEVBQ1AsR0FBRyxFQUVILG9CQUFvQixHQUdyQixNQUFNLHVCQUF1QixDQUFDO0FBQy9CLE9BQU8sRUFBRSxlQUFlLEVBQWMsTUFBTSxNQUFNLENBQUM7OztBQUluRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMkJHO0FBRUgsTUFBTSxPQUFPLGNBQWM7SUFDekIsWUFBb0IsT0FBZ0I7UUFBaEIsWUFBTyxHQUFQLE9BQU8sQ0FBUztJQUFHLENBQUM7SUFFeEMsOEVBQThFO0lBQzlFLFNBQVM7SUFDVCw4RUFBOEU7SUFFOUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FzQkc7SUFDSCxNQUFNLENBQUMsSUFBWSxFQUFFLElBQWlCLEVBQUUsUUFBMEI7UUFDaEUsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDM0MsTUFBTSxjQUFjLEdBQW1CO1lBQ3JDLFdBQVcsRUFBRSxRQUFRLEVBQUUsV0FBVyxJQUFJLENBQUMsSUFBSSxZQUFZLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO1lBQ3BGLGNBQWMsRUFBRSxRQUFRLEVBQUUsY0FBYztZQUN4QyxZQUFZLEVBQUUsUUFBUSxFQUFFLFlBQVk7U0FDckMsQ0FBQztRQUVGLE1BQU0sSUFBSSxHQUFHLG9CQUFvQixDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsY0FB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QUFPLCtDQUErQyxDQUFDO2dCQUN6RCxLQUFLLGtCQUFrQjtvQkFDckIsT0FBTyw0QkFBNEIsQ0FBQztnQkFDdEMsS0FBSyx3QkFBd0I7b0JBQzNCLE9BQU8sMkNBQTJDLENBQUM7Z0JBQ3JELEtBQUssMEJBQTBCO29CQUM3QixPQUFPLDBCQUEwQixDQUFDO2dCQUNwQyxLQUFLLDhCQUE4QjtvQkFDakMsT0FBTyxxQ0FBcUMsQ0FBQztnQkFDL0MsS0FBSyxxQkFBcUI7b0JBQ3hCLE9BQU8seUJBQXlCLENBQUM7Z0JBQ25DLEtBQUssMEJBQTBCO29CQUM3QixPQUFPLG9CQUFvQixDQUFDO2dCQUM5QjtvQkFDRSxPQUFPLEtBQUssQ0FBQyxPQUFPLElBQUkscUNBQXFDLENBQUM7WUFDbEUsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLHFDQUFxQyxDQUFDO0lBQy9DLENBQUM7K0dBNVpVLGNBQWM7bUhBQWQsY0FBYyxjQURELE1BQU07OzRGQUNuQixjQUFjO2tCQUQxQixVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU3RvcmFnZSBTZXJ2aWNlXG4gKlxuICogU2VydmljaW8gcGFyYSBvcGVyYWNpb25lcyBkZSBGaXJlYmFzZSBTdG9yYWdlLlxuICogU29wb3J0YSB1cGxvYWQgY29uIHRyYWNraW5nIGRlIHByb2dyZXNvLCBkb3dubG9hZCB5IGdlc3Rpw7NuIGRlIGFyY2hpdm9zLlxuICovXG5cbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIGRlbGV0ZU9iamVjdCxcbiAgZ2V0RG93bmxvYWRVUkwsXG4gIGdldE1ldGFkYXRhLFxuICBsaXN0QWxsLFxuICByZWYsXG4gIFN0b3JhZ2UsXG4gIHVwbG9hZEJ5dGVzUmVzdW1hYmxlLFxuICBVcGxvYWRNZXRhZGF0YSxcbiAgVXBsb2FkVGFza1NuYXBzaG90LFxufSBmcm9tICdAYW5ndWxhci9maXJlL3N0b3JhZ2UnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IFN0b3JhZ2VMaXN0UmVzdWx0LCBTdG9yYWdlTWV0YWRhdGEsIFVwbG9hZFByb2dyZXNzLCBVcGxvYWRSZXN1bHQsIFVwbG9hZFN0YXRlIH0gZnJvbSAnLi90eXBlcyc7XG5cbi8qKlxuICogU2VydmljaW8gcGFyYSBGaXJlYmFzZSBTdG9yYWdlLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBAQ29tcG9uZW50KHsuLi59KVxuICogZXhwb3J0IGNsYXNzIEZpbGVVcGxvYWRDb21wb25lbnQge1xuICogICBwcml2YXRlIHN0b3JhZ2UgPSBpbmplY3QoU3RvcmFnZVNlcnZpY2UpO1xuICpcbiAqICAgdXBsb2FkUHJvZ3Jlc3MgPSBzaWduYWw8bnVtYmVyPigwKTtcbiAqICAgZG93bmxvYWRVcmwgPSBzaWduYWw8c3RyaW5nIHwgbnVsbD4obnVsbCk7XG4gKlxuICogICBhc3luYyBvbkZpbGVTZWxlY3RlZChldmVudDogRXZlbnQpIHtcbiAqICAgICBjb25zdCBmaWxlID0gKGV2ZW50LnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50KS5maWxlcz8uWzBdO1xuICogICAgIGlmICghZmlsZSkgcmV0dXJuO1xuICpcbiAqICAgICAvLyBVcGxvYWQgY29uIHByb2dyZXNvXG4gKiAgICAgdGhpcy5zdG9yYWdlLnVwbG9hZChgdXBsb2Fkcy8ke2ZpbGUubmFtZX1gLCBmaWxlKS5zdWJzY3JpYmUoe1xuICogICAgICAgbmV4dDogKHByb2dyZXNzKSA9PiB0aGlzLnVwbG9hZFByb2dyZXNzLnNldChwcm9ncmVzcy5wZXJjZW50YWdlKSxcbiAqICAgICAgIGNvbXBsZXRlOiBhc3luYyAoKSA9PiB7XG4gKiAgICAgICAgIGNvbnN0IHVybCA9IGF3YWl0IHRoaXMuc3RvcmFnZS5nZXREb3dubG9hZFVybChgdXBsb2Fkcy8ke2ZpbGUubmFtZX1gKTtcbiAqICAgICAgICAgdGhpcy5kb3dubG9hZFVybC5zZXQodXJsKTtcbiAqICAgICAgIH1cbiAqICAgICB9KTtcbiAqICAgfVxuICogfVxuICogYGBgXG4gKi9cbkBJbmplY3RhYmxlKHsgcHJvdmlkZWRJbjogJ3Jvb3QnIH0pXG5leHBvcnQgY2xhc3MgU3RvcmFnZVNlcnZpY2Uge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHN0b3JhZ2U6IFN0b3JhZ2UpIHt9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIFVQTE9BRFxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKipcbiAgICogU3ViZSB1biBhcmNoaXZvIGNvbiB0cmFja2luZyBkZSBwcm9ncmVzby5cbiAgICpcbiAgICogQHBhcmFtIHBhdGggLSBSdXRhIGVuIFN0b3JhZ2UgZG9uZGUgZ3VhcmRhciBlbCBhcmNoaXZvXG4gICAqIEBwYXJhbSBmaWxlIC0gQXJjaGl2byBhIHN1YmlyIChGaWxlIG8gQmxvYilcbiAgICogQHBhcmFtIG1ldGFkYXRhIC0gTWV0YWRhdGEgb3BjaW9uYWwgKGNvbnRlbnRUeXBlLCBjdXN0b21NZXRhZGF0YSlcbiAgICogQHJldHVybnMgT2JzZXJ2YWJsZSBxdWUgZW1pdGUgZWwgcHJvZ3Jlc28geSBjb21wbGV0YSBjdWFuZG8gdGVybWluYVxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIC8vIFVwbG9hZCBiw6FzaWNvXG4gICAqIHN0b3JhZ2UudXBsb2FkKCdpbWFnZXMvcGhvdG8uanBnJywgZmlsZSkuc3Vic2NyaWJlKHtcbiAgICogICBuZXh0OiAocHJvZ3Jlc3MpID0+IGNvbnNvbGUubG9nKGAke3Byb2dyZXNzLnBlcmNlbnRhZ2V9JWApLFxuICAgKiAgIGNvbXBsZXRlOiAoKSA9PiBjb25zb2xlLmxvZygnVXBsb2FkIGNvbXBsZXRhZG8nKVxuICAgKiB9KTtcbiAgICpcbiAgICogLy8gQ29uIG1ldGFkYXRhXG4gICAqIHN0b3JhZ2UudXBsb2FkKCdkb2NzL3JlcG9ydC5wZGYnLCBmaWxlLCB7XG4gICAqICAgY29udGVudFR5cGU6ICdhcHBsaWNhdGlvbi9wZGYnLFxuICAgKiAgIGN1c3RvbU1ldGFkYXRhOiB7IHVwbG9hZGVkQnk6ICd1c2VyMTIzJyB9XG4gICAqIH0pLnN1YnNjcmliZSguLi4pO1xuICAgKiBgYGBcbiAgICovXG4gIHVwbG9hZChwYXRoOiBzdHJpbmcsIGZpbGU6IEZpbGUgfCBCbG9iLCBtZXRhZGF0YT86IFN0b3JhZ2VNZXRhZGF0YSk6IE9ic2VydmFibGU8VXBsb2FkUHJvZ3Jlc3M+IHtcbiAgICBjb25zdCBzdG9yYWdlUmVmID0gcmVmKHRoaXMuc3RvcmFnZSwgcGF0aCk7XG4gICAgY29uc3QgdXBsb2FkTWV0YWRhdGE6IFVwbG9hZE1ldGFkYXRhID0ge1xuICAgICAgY29udGVudFR5cGU6IG1ldGFkYXRhPy5jb250ZW50VHlwZSB8fCAoZmlsZSBpbnN0YW5jZW9mIEZpbGUgPyBmaWxlLnR5cGUgOiB1bmRlZmluZWQpLFxuICAgICAgY3VzdG9tTWV0YWRhdGE6IG1ldGFkYXRhPy5jdXN0b21NZXRhZGF0YSxcbiAgICAgIGNhY2hlQ29udHJvbDogbWV0YWRhdGE/LmNhY2hlQ29udHJvbCxcbiAgICB9O1xuXG4gICAgY29uc3QgdGFzayA9IHVwbG9hZEJ5dGVzUmVzdW1hYmxlKHN0b3JhZ2VSZWYsIGZpbGUsIHVwbG9hZE1ldGFkYXRhKTtcbiAgICBjb25zdCBwcm9ncmVzcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFVwbG9hZFByb2dyZXNzPih7XG4gICAgICBieXRlc1RyYW5zZmVycmVkOiAwLFxuICAgICAgdG90YWxCeXRlczogZmlsZS5zaXplLFxuICAgICAgcGVyY2VudGFnZTogMCxcbiAgICAgIHN0YXRlOiAncnVubmluZycsXG4gICAgfSk7XG5cbiAgICB0YXNrLm9uKFxuICAgICAgJ3N0YXRlX2NoYW5nZWQnLFxuICAgICAgKHNuYXBzaG90OiBVcGxvYWRUYXNrU25hcHNob3QpID0+IHtcbiAgICAgICAgcHJvZ3Jlc3MkLm5leHQoe1xuICAgICAgICAgIGJ5dGVzVHJhbnNmZXJyZWQ6IHNuYXBzaG90LmJ5dGVzVHJhbnNmZXJyZWQsXG4gICAgICAgICAgdG90YWxCeXRlczogc25hcHNob3QudG90YWxCeXRlcyxcbiAgICAgICAgICBwZXJjZW50YWdlOiBNYXRoLnJvdW5kKChzbmFwc2hvdC5ieXRlc1RyYW5zZmVycmVkIC8gc25hcHNob3QudG90YWxCeXRlcykgKiAxMDApLFxuICAgICAgICAgIHN0YXRlOiB0aGlzLm1hcFRhc2tTdGF0ZShzbmFwc2hvdC5zdGF0ZSksXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICAgIChlcnJvcikgPT4ge1xuICAgICAgICBwcm9ncmVzcyQubmV4dCh7XG4gICAgICAgICAgYnl0ZXNUcmFuc2ZlcnJlZDogMCxcbiAgICAgICAgICB0b3RhbEJ5dGVzOiBmaWxlLnNpemUsXG4gICAgICAgICAgcGVyY2VudGFnZTogMCxcbiAgICAgICAgICBzdGF0ZTogJ2Vycm9yJyxcbiAgICAgICAgfSk7XG4gICAgICAgIHByb2dyZXNzJC5lcnJvcih0aGlzLmdldEVycm9yTWVzc2FnZShlcnJvcikpO1xuICAgICAgfSxcbiAgICAgICgpID0+IHtcbiAgICAgICAgcHJvZ3Jlc3MkLm5leHQoe1xuICAgICAgICAgIGJ5dGVzVHJhbnNmZXJyZWQ6IGZpbGUuc2l6ZSxcbiAgICAgICAgICB0b3RhbEJ5dGVzOiBmaWxlLnNpemUsXG4gICAgICAgICAgcGVyY2VudGFnZTogMTAwLFxuICAgICAgICAgIHN0YXRlOiAnc3VjY2VzcycsXG4gICAgICAgIH0pO1xuICAgICAgICBwcm9ncmVzcyQuY29tcGxldGUoKTtcbiAgICAgIH1cbiAgICApO1xuXG4gICAgcmV0dXJuIHByb2dyZXNzJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJlIHVuIGFyY2hpdm8geSByZXRvcm5hIGxhIFVSTCBkZSBkZXNjYXJnYSBhbCBjb21wbGV0YXIuXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoIC0gUnV0YSBlbiBTdG9yYWdlXG4gICAqIEBwYXJhbSBmaWxlIC0gQXJjaGl2byBhIHN1YmlyXG4gICAqIEBwYXJhbSBtZXRhZGF0YSAtIE1ldGFkYXRhIG9wY2lvbmFsXG4gICAqIEByZXR1cm5zIFJlc3VsdGFkbyBkZWwgdXBsb2FkIGNvbiBVUkwgZGUgZGVzY2FyZ2FcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCByZXN1bHQgPSBhd2FpdCBzdG9yYWdlLnVwbG9hZEFuZEdldFVybCgnYXZhdGFycy91c2VyMTIzLmpwZycsIGZpbGUpO1xuICAgKiBjb25zb2xlLmxvZygnVVJMOicsIHJlc3VsdC5kb3dubG9hZFVybCk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgdXBsb2FkQW5kR2V0VXJsKFxuICAgIHBhdGg6IHN0cmluZyxcbiAgICBmaWxlOiBGaWxlIHwgQmxvYixcbiAgICBtZXRhZGF0YT86IFN0b3JhZ2VNZXRhZGF0YVxuICApOiBQcm9taXNlPFVwbG9hZFJlc3VsdD4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICB0aGlzLnVwbG9hZChwYXRoLCBmaWxlLCBtZXRhZGF0YSkuc3Vic2NyaWJlKHtcbiAgICAgICAgY29tcGxldGU6IGFzeW5jICgpID0+IHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3Qgc3RvcmFnZVJlZiA9IHJlZih0aGlzLnN0b3JhZ2UsIHBhdGgpO1xuICAgICAgICAgICAgY29uc3QgZG93bmxvYWRVcmwgPSBhd2FpdCBnZXREb3dubG9hZFVSTChzdG9yYWdlUmVmKTtcbiAgICAgICAgICAgIGNvbnN0IHN0b3JlZE1ldGFkYXRhID0gYXdhaXQgZ2V0TWV0YWRhdGEoc3RvcmFnZVJlZik7XG5cbiAgICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgICBkb3dubG9hZFVybCxcbiAgICAgICAgICAgICAgZnVsbFBhdGg6IHN0b3JlZE1ldGFkYXRhLmZ1bGxQYXRoLFxuICAgICAgICAgICAgICBuYW1lOiBzdG9yZWRNZXRhZGF0YS5uYW1lLFxuICAgICAgICAgICAgICBzaXplOiBzdG9yZWRNZXRhZGF0YS5zaXplLFxuICAgICAgICAgICAgICBjb250ZW50VHlwZTogc3RvcmVkTWV0YWRhdGEuY29udGVudFR5cGUgfHwgJ2FwcGxpY2F0aW9uL29jdGV0LXN0cmVhbScsXG4gICAgICAgICAgICAgIG1ldGFkYXRhOiBzdG9yZWRNZXRhZGF0YS5jdXN0b21NZXRhZGF0YSB8fCB7fSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICByZWplY3QodGhpcy5nZXRFcnJvck1lc3NhZ2UoZXJyb3IpKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yOiAoZXJyb3IpID0+IHJlamVjdChlcnJvciksXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJlIHVuIGFyY2hpdm8gZGVzZGUgdW5hIERhdGEgVVJMIChiYXNlNjQpLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFJ1dGEgZW4gU3RvcmFnZVxuICAgKiBAcGFyYW0gZGF0YVVybCAtIERhdGEgVVJMIChlajogJ2RhdGE6aW1hZ2UvcG5nO2Jhc2U2NCwuLi4nKVxuICAgKiBAcGFyYW0gbWV0YWRhdGEgLSBNZXRhZGF0YSBvcGNpb25hbFxuICAgKiBAcmV0dXJucyBSZXN1bHRhZG8gZGVsIHVwbG9hZFxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIC8vIERlc2RlIGNhbnZhc1xuICAgKiBjb25zdCBkYXRhVXJsID0gY2FudmFzLnRvRGF0YVVSTCgnaW1hZ2UvcG5nJyk7XG4gICAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHN0b3JhZ2UudXBsb2FkRnJvbURhdGFVcmwoJ2ltYWdlcy9kcmF3aW5nLnBuZycsIGRhdGFVcmwpO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIHVwbG9hZEZyb21EYXRhVXJsKFxuICAgIHBhdGg6IHN0cmluZyxcbiAgICBkYXRhVXJsOiBzdHJpbmcsXG4gICAgbWV0YWRhdGE/OiBTdG9yYWdlTWV0YWRhdGFcbiAgKTogUHJvbWlzZTxVcGxvYWRSZXN1bHQ+IHtcbiAgICAvLyBFeHRyYWVyIGNvbnRlbnQgdHlwZSB5IGRhdG9zIGJhc2U2NFxuICAgIGNvbnN0IG1hdGNoZXMgPSBkYXRhVXJsLm1hdGNoKC9eZGF0YTooW147XSspO2Jhc2U2NCwoLispJC8pO1xuICAgIGlmICghbWF0Y2hlcykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhIFVSTCBpbnbDoWxpZGEnKTtcbiAgICB9XG5cbiAgICBjb25zdCBjb250ZW50VHlwZSA9IG1hdGNoZXNbMV07XG4gICAgY29uc3QgYmFzZTY0RGF0YSA9IG1hdGNoZXNbMl07XG5cbiAgICAvLyBDb252ZXJ0aXIgYmFzZTY0IGEgQmxvYlxuICAgIGNvbnN0IGJ5dGVDaGFyYWN0ZXJzID0gYXRvYihiYXNlNjREYXRhKTtcbiAgICBjb25zdCBieXRlTnVtYmVycyA9IG5ldyBBcnJheShieXRlQ2hhcmFjdGVycy5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYnl0ZUNoYXJhY3RlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGJ5dGVOdW1iZXJzW2ldID0gYnl0ZUNoYXJhY3RlcnMuY2hhckNvZGVBdChpKTtcbiAgICB9XG4gICAgY29uc3QgYnl0ZUFycmF5ID0gbmV3IFVpbnQ4QXJyYXkoYnl0ZU51bWJlcnMpO1xuICAgIGNvbnN0IGJsb2IgPSBuZXcgQmxvYihbYnl0ZUFycmF5XSwgeyB0eXBlOiBjb250ZW50VHlwZSB9KTtcblxuICAgIHJldHVybiB0aGlzLnVwbG9hZEFuZEdldFVybChwYXRoLCBibG9iLCB7XG4gICAgICBjb250ZW50VHlwZSxcbiAgICAgIC4uLm1ldGFkYXRhLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIERPV05MT0FEXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBPYnRpZW5lIGxhIFVSTCBkZSBkZXNjYXJnYSBkZSB1biBhcmNoaXZvLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFJ1dGEgZGVsIGFyY2hpdm8gZW4gU3RvcmFnZVxuICAgKiBAcmV0dXJucyBVUkwgZGUgZGVzY2FyZ2FcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCB1cmwgPSBhd2FpdCBzdG9yYWdlLmdldERvd25sb2FkVXJsKCdpbWFnZXMvcGhvdG8uanBnJyk7XG4gICAqIC8vIFVzYXIgZW4gPGltZyBbc3JjXT1cInVybFwiPlxuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGdldERvd25sb2FkVXJsKHBhdGg6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHN0b3JhZ2VSZWYgPSByZWYodGhpcy5zdG9yYWdlLCBwYXRoKTtcbiAgICAgIHJldHVybiBhd2FpdCBnZXREb3dubG9hZFVSTChzdG9yYWdlUmVmKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKHRoaXMuZ2V0RXJyb3JNZXNzYWdlKGVycm9yKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIE9idGllbmUgbGEgbWV0YWRhdGEgZGUgdW4gYXJjaGl2by5cbiAgICpcbiAgICogQHBhcmFtIHBhdGggLSBSdXRhIGRlbCBhcmNoaXZvXG4gICAqIEByZXR1cm5zIE1ldGFkYXRhIGRlbCBhcmNoaXZvXG4gICAqL1xuICBhc3luYyBnZXRNZXRhZGF0YShwYXRoOiBzdHJpbmcpOiBQcm9taXNlPFN0b3JhZ2VNZXRhZGF0YSAmIHsgc2l6ZTogbnVtYmVyOyBuYW1lOiBzdHJpbmcgfT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzdG9yYWdlUmVmID0gcmVmKHRoaXMuc3RvcmFnZSwgcGF0aCk7XG4gICAgICBjb25zdCBtZXRhZGF0YSA9IGF3YWl0IGdldE1ldGFkYXRhKHN0b3JhZ2VSZWYpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgY29udGVudFR5cGU6IG1ldGFkYXRhLmNvbnRlbnRUeXBlLFxuICAgICAgICBjdXN0b21NZXRhZGF0YTogbWV0YWRhdGEuY3VzdG9tTWV0YWRhdGEsXG4gICAgICAgIGNhY2hlQ29udHJvbDogbWV0YWRhdGEuY2FjaGVDb250cm9sLFxuICAgICAgICBzaXplOiBtZXRhZGF0YS5zaXplLFxuICAgICAgICBuYW1lOiBtZXRhZGF0YS5uYW1lLFxuICAgICAgfTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKHRoaXMuZ2V0RXJyb3JNZXNzYWdlKGVycm9yKSk7XG4gICAgfVxuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gIC8vIERFTEVURVxuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKipcbiAgICogRWxpbWluYSB1biBhcmNoaXZvLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFJ1dGEgZGVsIGFyY2hpdm8gYSBlbGltaW5hclxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGF3YWl0IHN0b3JhZ2UuZGVsZXRlKCdpbWFnZXMvb2xkLXBob3RvLmpwZycpO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGRlbGV0ZShwYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3Qgc3RvcmFnZVJlZiA9IHJlZih0aGlzLnN0b3JhZ2UsIHBhdGgpO1xuICAgICAgYXdhaXQgZGVsZXRlT2JqZWN0KHN0b3JhZ2VSZWYpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IodGhpcy5nZXRFcnJvck1lc3NhZ2UoZXJyb3IpKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRWxpbWluYSBtw7psdGlwbGVzIGFyY2hpdm9zLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aHMgLSBBcnJheSBkZSBydXRhcyBhIGVsaW1pbmFyXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogYXdhaXQgc3RvcmFnZS5kZWxldGVNdWx0aXBsZShbXG4gICAqICAgJ2ltYWdlcy9waG90bzEuanBnJyxcbiAgICogICAnaW1hZ2VzL3Bob3RvMi5qcGcnXG4gICAqIF0pO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGRlbGV0ZU11bHRpcGxlKHBhdGhzOiBzdHJpbmdbXSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IFByb21pc2UuYWxsKHBhdGhzLm1hcCgocGF0aCkgPT4gdGhpcy5kZWxldGUocGF0aCkpKTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBMSVNUXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBMaXN0YSBhcmNoaXZvcyBlbiB1biBkaXJlY3RvcmlvLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFJ1dGEgZGVsIGRpcmVjdG9yaW9cbiAgICogQHJldHVybnMgTGlzdGEgZGUgcnV0YXMgZGUgYXJjaGl2b3NcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCByZXN1bHQgPSBhd2FpdCBzdG9yYWdlLmxpc3QoJ2ltYWdlcy8nKTtcbiAgICogY29uc29sZS5sb2cocmVzdWx0Lml0ZW1zKTsgLy8gWydpbWFnZXMvcGhvdG8xLmpwZycsICdpbWFnZXMvcGhvdG8yLmpwZyddXG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgbGlzdChwYXRoOiBzdHJpbmcpOiBQcm9taXNlPFN0b3JhZ2VMaXN0UmVzdWx0PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHN0b3JhZ2VSZWYgPSByZWYodGhpcy5zdG9yYWdlLCBwYXRoKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGxpc3RBbGwoc3RvcmFnZVJlZik7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGl0ZW1zOiByZXN1bHQuaXRlbXMubWFwKChpdGVtKSA9PiBpdGVtLmZ1bGxQYXRoKSxcbiAgICAgICAgbmV4dFBhZ2VUb2tlbjogdW5kZWZpbmVkLCAvLyBsaXN0QWxsIG5vIHNvcG9ydGEgcGFnaW5hY2nDs25cbiAgICAgIH07XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcih0aGlzLmdldEVycm9yTWVzc2FnZShlcnJvcikpO1xuICAgIH1cbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBVVElMSURBREVTXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBHZW5lcmEgdW4gbm9tYnJlIGRlIGFyY2hpdm8gw7puaWNvIGNvbiB0aW1lc3RhbXAuXG4gICAqXG4gICAqIEBwYXJhbSBvcmlnaW5hbE5hbWUgLSBOb21icmUgb3JpZ2luYWwgZGVsIGFyY2hpdm9cbiAgICogQHBhcmFtIHByZWZpeCAtIFByZWZpam8gb3BjaW9uYWxcbiAgICogQHJldHVybnMgTm9tYnJlIMO6bmljb1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IHVuaXF1ZU5hbWUgPSBzdG9yYWdlLmdlbmVyYXRlRmlsZU5hbWUoJ3Bob3RvLmpwZycsICd1c2VyMTIzJyk7XG4gICAqIC8vID0+ICd1c2VyMTIzXzE3MDMwOTEyMzQ1NjdfcGhvdG8uanBnJ1xuICAgKiBgYGBcbiAgICovXG4gIGdlbmVyYXRlRmlsZU5hbWUob3JpZ2luYWxOYW1lOiBzdHJpbmcsIHByZWZpeD86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgdGltZXN0YW1wID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCBzYW5pdGl6ZWROYW1lID0gb3JpZ2luYWxOYW1lLnJlcGxhY2UoL1teYS16QS1aMC05Li1dL2csICdfJyk7XG5cbiAgICBpZiAocHJlZml4KSB7XG4gICAgICByZXR1cm4gYCR7cHJlZml4fV8ke3RpbWVzdGFtcH1fJHtzYW5pdGl6ZWROYW1lfWA7XG4gICAgfVxuICAgIHJldHVybiBgJHt0aW1lc3RhbXB9XyR7c2FuaXRpemVkTmFtZX1gO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYSB1bmEgcnV0YSDDum5pY2EgcGFyYSB1biBhcmNoaXZvLlxuICAgKlxuICAgKiBAcGFyYW0gZGlyZWN0b3J5IC0gRGlyZWN0b3JpbyBiYXNlXG4gICAqIEBwYXJhbSBvcmlnaW5hbE5hbWUgLSBOb21icmUgb3JpZ2luYWxcbiAgICogQHBhcmFtIHByZWZpeCAtIFByZWZpam8gb3BjaW9uYWxcbiAgICogQHJldHVybnMgUnV0YSBjb21wbGV0YSDDum5pY2FcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBwYXRoID0gc3RvcmFnZS5nZW5lcmF0ZVBhdGgoJ3VwbG9hZHMnLCAncGhvdG8uanBnJywgJ3VzZXIxMjMnKTtcbiAgICogLy8gPT4gJ3VwbG9hZHMvdXNlcjEyM18xNzAzMDkxMjM0NTY3X3Bob3RvLmpwZydcbiAgICogYGBgXG4gICAqL1xuICBnZW5lcmF0ZVBhdGgoZGlyZWN0b3J5OiBzdHJpbmcsIG9yaWdpbmFsTmFtZTogc3RyaW5nLCBwcmVmaXg/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IGZpbGVOYW1lID0gdGhpcy5nZW5lcmF0ZUZpbGVOYW1lKG9yaWdpbmFsTmFtZSwgcHJlZml4KTtcbiAgICBjb25zdCBjbGVhbkRpciA9IGRpcmVjdG9yeS5yZXBsYWNlKC9cXC8rJC8sICcnKTsgLy8gUmVtb3ZlciAvIGZpbmFsXG4gICAgcmV0dXJuIGAke2NsZWFuRGlyfS8ke2ZpbGVOYW1lfWA7XG4gIH1cblxuICAvKipcbiAgICogT2J0aWVuZSBsYSBleHRlbnNpw7NuIGRlIHVuIGFyY2hpdm8uXG4gICAqXG4gICAqIEBwYXJhbSBmaWxlbmFtZSAtIE5vbWJyZSBkZWwgYXJjaGl2b1xuICAgKiBAcmV0dXJucyBFeHRlbnNpw7NuIChzaW4gZWwgcHVudG8pXG4gICAqL1xuICBnZXRFeHRlbnNpb24oZmlsZW5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgcGFydHMgPSBmaWxlbmFtZS5zcGxpdCgnLicpO1xuICAgIHJldHVybiBwYXJ0cy5sZW5ndGggPiAxID8gcGFydHMucG9wKCkhLnRvTG93ZXJDYXNlKCkgOiAnJztcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZmljYSBzaSB1biBhcmNoaXZvIGVzIHVuYSBpbWFnZW4gYmFzw6FuZG9zZSBlbiBzdSBleHRlbnNpw7NuLlxuICAgKi9cbiAgaXNJbWFnZShmaWxlbmFtZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgY29uc3QgZXh0ID0gdGhpcy5nZXRFeHRlbnNpb24oZmlsZW5hbWUpO1xuICAgIHJldHVybiBbJ2pwZycsICdqcGVnJywgJ3BuZycsICdnaWYnLCAnd2VicCcsICdzdmcnLCAnYm1wJ10uaW5jbHVkZXMoZXh0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZmljYSBzaSB1biBhcmNoaXZvIGVzIHVuIGRvY3VtZW50by5cbiAgICovXG4gIGlzRG9jdW1lbnQoZmlsZW5hbWU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGV4dCA9IHRoaXMuZ2V0RXh0ZW5zaW9uKGZpbGVuYW1lKTtcbiAgICByZXR1cm4gWydwZGYnLCAnZG9jJywgJ2RvY3gnLCAneGxzJywgJ3hsc3gnLCAncHB0JywgJ3BwdHgnLCAndHh0J10uaW5jbHVkZXMoZXh0KTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAvLyBNw4lUT0RPUyBQUklWQURPU1xuICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAvKipcbiAgICogTWFwZWEgZWwgZXN0YWRvIGRlIGxhIHRhcmVhIGRlIHVwbG9hZFxuICAgKi9cbiAgcHJpdmF0ZSBtYXBUYXNrU3RhdGUoc3RhdGU6IHN0cmluZyk6IFVwbG9hZFN0YXRlIHtcbiAgICBzd2l0Y2ggKHN0YXRlKSB7XG4gICAgICBjYXNlICdydW5uaW5nJzpcbiAgICAgICAgcmV0dXJuICdydW5uaW5nJztcbiAgICAgIGNhc2UgJ3BhdXNlZCc6XG4gICAgICAgIHJldHVybiAncGF1c2VkJztcbiAgICAgIGNhc2UgJ3N1Y2Nlc3MnOlxuICAgICAgICByZXR1cm4gJ3N1Y2Nlc3MnO1xuICAgICAgY2FzZSAnY2FuY2VsZWQnOlxuICAgICAgICByZXR1cm4gJ2NhbmNlbGVkJztcbiAgICAgIGNhc2UgJ2Vycm9yJzpcbiAgICAgICAgcmV0dXJuICdlcnJvcic7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gJ3J1bm5pbmcnO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDb252aWVydGUgZXJyb3JlcyBkZSBTdG9yYWdlIGEgbWVuc2FqZXMgZW4gZXNwYcOxb2xcbiAgICovXG4gIHByaXZhdGUgZ2V0RXJyb3JNZXNzYWdlKGVycm9yOiB1bmtub3duKTogc3RyaW5nIHtcbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgY29uc3QgY29kZSA9IChlcnJvciBhcyB7IGNvZGU/OiBzdHJpbmcgfSkuY29kZTtcblxuICAgICAgc3dpdGNoIChjb2RlKSB7XG4gICAgICAgIGNhc2UgJ3N0b3JhZ2Uvb2JqZWN0LW5vdC1mb3VuZCc6XG4gICAgICAgICAgcmV0dXJuICdFbCBhcmNoaXZvIG5vIGV4aXN0ZSc7XG4gICAgICAgIGNhc2UgJ3N0b3JhZ2UvdW5hdXRob3JpemVkJzpcbiAgICAgICAgICByZXR1cm4gJ05vIHRpZW5lcyBwZXJtaXNvIHBhcmEgYWNjZWRlciBhIGVzdGUgYXJjaGl2byc7XG4gICAgICAgIGNhc2UgJ3N0b3JhZ2UvY2FuY2VsZWQnOlxuICAgICAgICAgIHJldHVybiAnTGEgb3BlcmFjacOzbiBmdWUgY2FuY2VsYWRhJztcbiAgICAgICAgY2FzZSAnc3RvcmFnZS9xdW90YS1leGNlZWRlZCc6XG4gICAgICAgICAgcmV0dXJuICdTZSBoYSBleGNlZGlkbyBsYSBjdW90YSBkZSBhbG1hY2VuYW1pZW50byc7XG4gICAgICAgIGNhc2UgJ3N0b3JhZ2UvaW52YWxpZC1jaGVja3N1bSc6XG4gICAgICAgICAgcmV0dXJuICdFbCBhcmNoaXZvIGVzdMOhIGNvcnJ1cHRvJztcbiAgICAgICAgY2FzZSAnc3RvcmFnZS9yZXRyeS1saW1pdC1leGNlZWRlZCc6XG4gICAgICAgICAgcmV0dXJuICdFcnJvciBkZSBjb25leGnDs24uIEludGVudGEgZGUgbnVldm8nO1xuICAgICAgICBjYXNlICdzdG9yYWdlL2ludmFsaWQtdXJsJzpcbiAgICAgICAgICByZXR1cm4gJ1VSTCBkZSBhcmNoaXZvIGludsOhbGlkYSc7XG4gICAgICAgIGNhc2UgJ3N0b3JhZ2UvaW52YWxpZC1hcmd1bWVudCc6XG4gICAgICAgICAgcmV0dXJuICdBcmd1bWVudG8gaW52w6FsaWRvJztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICByZXR1cm4gZXJyb3IubWVzc2FnZSB8fCAnRXJyb3IgZGUgYWxtYWNlbmFtaWVudG8gZGVzY29ub2NpZG8nO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiAnRXJyb3IgZGUgYWxtYWNlbmFtaWVudG8gZGVzY29ub2NpZG8nO1xuICB9XG59XG4iXX0=
|