@decohere/create 0.0.1-dev.47 → 0.0.1-dev.49
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/commonjs/src/cli.js +3 -3
- package/commonjs/src/resolve-launch.js +53 -0
- package/commonjs/src/runner.js +5 -45
- package/commonjs/src/version.js +1 -1
- package/es6/src/cli.js +3 -3
- package/es6/src/resolve-launch.js +46 -0
- package/es6/src/runner.js +8 -57
- package/es6/src/version.js +3 -3
- package/pack-builder-tgz.mjs +19 -35
- package/package.json +4 -11
- package/types/src/resolve-launch.d.ts +10 -0
- package/types/src/runner.d.ts +3 -13
- package/types/src/version.d.ts +0 -2
- package/commonjs/src/domain/prompt.js +0 -64
- package/commonjs/src/models/compose.model.js +0 -138
- package/commonjs/src/models/config-yaml.model.js +0 -88
- package/commonjs/src/models/const.js +0 -31
- package/commonjs/src/models/dev-container.model.js +0 -100
- package/commonjs/src/models/docker-image.model.js +0 -221
- package/commonjs/src/models/env-file.model.js +0 -47
- package/commonjs/src/models/gitignore.model.js +0 -31
- package/commonjs/src/models/launch-json.model.js +0 -74
- package/commonjs/src/models/package-json.model.js +0 -133
- package/commonjs/src/models/project.model.js +0 -134
- package/commonjs/src/models/source-file.js +0 -6
- package/commonjs/src/models/tsconfig.model.js +0 -34
- package/commonjs/src/models/vscode-settings.model.js +0 -31
- package/commonjs/src/program.js +0 -135
- package/commonjs/src/resolve-target-dir.js +0 -19
- package/commonjs/src/services/scaffold.service.js +0 -62
- package/commonjs/src/webapi/api-name.js +0 -95
- package/commonjs/src/webapi/const.js +0 -50
- package/commonjs/src/webapi/create-api.js +0 -285
- package/commonjs/src/webapi/features/auth.feature.js +0 -463
- package/commonjs/src/webapi/features/auth0-cli.js +0 -406
- package/commonjs/src/webapi/features/biome-format.js +0 -49
- package/commonjs/src/webapi/features/database.feature.js +0 -273
- package/commonjs/src/webapi/features/finalize.feature.js +0 -91
- package/commonjs/src/webapi/features/postman.feature.js +0 -319
- package/commonjs/src/webapi/models/api-package-json.model.js +0 -63
- package/commonjs/src/webapi/models/auth-config.model.js +0 -26
- package/commonjs/src/webapi/models/controller-source-file.model.js +0 -188
- package/commonjs/src/webapi/models/github-ci.model.js +0 -193
- package/commonjs/src/webapi/models/main-source-file.model.js +0 -165
- package/commonjs/src/webapi/models/production-dockerfile.model.js +0 -138
- package/commonjs/src/webapi/models/sample-files.model.js +0 -60
- package/commonjs/src/webapi/models/webapi-image.model.js +0 -22
- package/commonjs/src/webapi/postman/hello-openapi.js +0 -175
- package/commonjs/src/webapi/postman/local-view.js +0 -184
- package/commonjs/src/webapi/postman/openapi-to-postman.js +0 -202
- package/es6/src/domain/prompt.js +0 -43
- package/es6/src/models/compose.model.js +0 -132
- package/es6/src/models/config-yaml.model.js +0 -76
- package/es6/src/models/const.js +0 -21
- package/es6/src/models/dev-container.model.js +0 -90
- package/es6/src/models/docker-image.model.js +0 -185
- package/es6/src/models/env-file.model.js +0 -43
- package/es6/src/models/gitignore.model.js +0 -21
- package/es6/src/models/launch-json.model.js +0 -64
- package/es6/src/models/package-json.model.js +0 -129
- package/es6/src/models/project.model.js +0 -119
- package/es6/src/models/source-file.js +0 -3
- package/es6/src/models/tsconfig.model.js +0 -24
- package/es6/src/models/vscode-settings.model.js +0 -21
- package/es6/src/program.js +0 -76
- package/es6/src/resolve-target-dir.js +0 -9
- package/es6/src/services/scaffold.service.js +0 -52
- package/es6/src/webapi/api-name.js +0 -59
- package/es6/src/webapi/const.js +0 -20
- package/es6/src/webapi/create-api.js +0 -265
- package/es6/src/webapi/features/auth.feature.js +0 -412
- package/es6/src/webapi/features/auth0-cli.js +0 -348
- package/es6/src/webapi/features/biome-format.js +0 -42
- package/es6/src/webapi/features/database.feature.js +0 -263
- package/es6/src/webapi/features/finalize.feature.js +0 -81
- package/es6/src/webapi/features/postman.feature.js +0 -253
- package/es6/src/webapi/models/api-package-json.model.js +0 -58
- package/es6/src/webapi/models/auth-config.model.js +0 -22
- package/es6/src/webapi/models/controller-source-file.model.js +0 -179
- package/es6/src/webapi/models/github-ci.model.js +0 -186
- package/es6/src/webapi/models/main-source-file.model.js +0 -161
- package/es6/src/webapi/models/production-dockerfile.model.js +0 -126
- package/es6/src/webapi/models/sample-files.model.js +0 -42
- package/es6/src/webapi/models/webapi-image.model.js +0 -12
- package/es6/src/webapi/postman/hello-openapi.js +0 -168
- package/es6/src/webapi/postman/local-view.js +0 -169
- package/es6/src/webapi/postman/openapi-to-postman.js +0 -179
- package/types/src/domain/prompt.d.ts +0 -4
- package/types/src/models/compose.model.d.ts +0 -59
- package/types/src/models/config-yaml.model.d.ts +0 -27
- package/types/src/models/const.d.ts +0 -19
- package/types/src/models/dev-container.model.d.ts +0 -33
- package/types/src/models/docker-image.model.d.ts +0 -56
- package/types/src/models/env-file.model.d.ts +0 -15
- package/types/src/models/gitignore.model.d.ts +0 -6
- package/types/src/models/launch-json.model.d.ts +0 -7
- package/types/src/models/package-json.model.d.ts +0 -32
- package/types/src/models/project.model.d.ts +0 -60
- package/types/src/models/source-file.d.ts +0 -11
- package/types/src/models/tsconfig.model.d.ts +0 -5
- package/types/src/models/vscode-settings.model.d.ts +0 -7
- package/types/src/program.d.ts +0 -3
- package/types/src/resolve-target-dir.d.ts +0 -5
- package/types/src/services/scaffold.service.d.ts +0 -22
- package/types/src/webapi/api-name.d.ts +0 -14
- package/types/src/webapi/const.d.ts +0 -17
- package/types/src/webapi/create-api.d.ts +0 -15
- package/types/src/webapi/features/auth.feature.d.ts +0 -10
- package/types/src/webapi/features/auth0-cli.d.ts +0 -72
- package/types/src/webapi/features/biome-format.d.ts +0 -5
- package/types/src/webapi/features/database.feature.d.ts +0 -5
- package/types/src/webapi/features/finalize.feature.d.ts +0 -2
- package/types/src/webapi/features/postman.feature.d.ts +0 -16
- package/types/src/webapi/models/api-package-json.model.d.ts +0 -18
- package/types/src/webapi/models/auth-config.model.d.ts +0 -26
- package/types/src/webapi/models/controller-source-file.model.d.ts +0 -45
- package/types/src/webapi/models/github-ci.model.d.ts +0 -9
- package/types/src/webapi/models/main-source-file.model.d.ts +0 -54
- package/types/src/webapi/models/production-dockerfile.model.d.ts +0 -20
- package/types/src/webapi/models/sample-files.model.d.ts +0 -7
- package/types/src/webapi/models/webapi-image.model.d.ts +0 -5
- package/types/src/webapi/postman/hello-openapi.d.ts +0 -19
- package/types/src/webapi/postman/local-view.d.ts +0 -17
- package/types/src/webapi/postman/openapi-to-postman.d.ts +0 -62
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
import { ask, pick } from "../../domain/prompt.js";
|
|
2
|
-
import { WELL_KNOWN_FILES } from "../../models/const.js";
|
|
3
|
-
import { AuthConfigModel } from "../models/auth-config.model.js";
|
|
4
|
-
import { ControllerSourceFile } from "../models/controller-source-file.model.js";
|
|
5
|
-
import * as auth0Cli from "./auth0-cli.js";
|
|
6
|
-
const ADMIN_ROLE = "admin";
|
|
7
|
-
const USER_ENTITY_TS = `import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
|
8
|
-
import type { Entity as IEntity } from "@decohere/codex";
|
|
9
|
-
|
|
10
|
-
@Entity()
|
|
11
|
-
export class UserEntity implements IEntity {
|
|
12
|
-
\t@PrimaryGeneratedColumn()
|
|
13
|
-
\tpublic id!: number;
|
|
14
|
-
|
|
15
|
-
\t@Column({ unique: true })
|
|
16
|
-
\tpublic sub!: string;
|
|
17
|
-
|
|
18
|
-
\t@Column({ type: "varchar", nullable: true })
|
|
19
|
-
\tpublic email!: string | null;
|
|
20
|
-
|
|
21
|
-
\t@Column()
|
|
22
|
-
\tpublic displayName!: string;
|
|
23
|
-
|
|
24
|
-
\t/** App-owned roles. The \`admin\` role grants CASL \`manage\`/\`all\` (wildcard). */
|
|
25
|
-
\t@Column({ type: "simple-json", default: "[]" })
|
|
26
|
-
\tpublic roles!: string[];
|
|
27
|
-
}
|
|
28
|
-
`;
|
|
29
|
-
const USER_REPOSITORY_TS = `import { injectable } from "@decohere/crucible/decorators";
|
|
30
|
-
import { injectUnitOfWork } from "@decohere/codex";
|
|
31
|
-
import { TypeOrmRepository, TypeOrmUnitOfWork } from "@decohere/codex-typeorm";
|
|
32
|
-
import { UserEntity } from "../entities/user.entity.js";
|
|
33
|
-
|
|
34
|
-
@injectable()
|
|
35
|
-
export class UserEntityRepository extends TypeOrmRepository<UserEntity> {
|
|
36
|
-
\tpublic constructor(@injectUnitOfWork() unitOfWork: TypeOrmUnitOfWork) {
|
|
37
|
-
\t\tsuper(unitOfWork, UserEntity);
|
|
38
|
-
\t}
|
|
39
|
-
|
|
40
|
-
\tpublic findBySub(sub: string): Promise<UserEntity | null> {
|
|
41
|
-
\t\treturn this.firstOrDefault({ where: { sub } });
|
|
42
|
-
\t}
|
|
43
|
-
}
|
|
44
|
-
`;
|
|
45
|
-
const USER_SERVICE_TS = `import { injectable } from "@decohere/crucible/decorators";
|
|
46
|
-
import type { Principal } from "@decohere/cortex";
|
|
47
|
-
import { UserEntity } from "../entities/user.entity.js";
|
|
48
|
-
import { UserEntityRepository } from "../repositories/user.repository.js";
|
|
49
|
-
|
|
50
|
-
function claimString(principal: Principal | undefined, key: string): string | undefined {
|
|
51
|
-
\tconst value = principal?.[key];
|
|
52
|
-
\treturn typeof value === "string" && value.length > 0 ? value : undefined;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
@injectable()
|
|
56
|
-
export class UserService {
|
|
57
|
-
\tpublic constructor(private readonly users: UserEntityRepository) {}
|
|
58
|
-
|
|
59
|
-
\t/**
|
|
60
|
-
\t * Upsert a local profile keyed by JWT \`sub\`.
|
|
61
|
-
\t * Profile fields from the token fill gaps; seeded rows keep displayName/email/roles.
|
|
62
|
-
\t */
|
|
63
|
-
\tpublic async ensureFromPrincipal(principal: Principal | undefined): Promise<UserEntity> {
|
|
64
|
-
\t\tconst sub = principal?.sub;
|
|
65
|
-
\t\tif (!sub) {
|
|
66
|
-
\t\t\tthrow new Error("Authenticated request is missing principal.sub");
|
|
67
|
-
\t\t}
|
|
68
|
-
|
|
69
|
-
\t\tconst email =
|
|
70
|
-
\t\t\tclaimString(principal, "email") ?? claimString(principal, "preferred_username");
|
|
71
|
-
\t\tconst displayFromToken =
|
|
72
|
-
\t\t\tclaimString(principal, "name") ??
|
|
73
|
-
\t\t\tclaimString(principal, "nickname") ??
|
|
74
|
-
\t\t\temail;
|
|
75
|
-
|
|
76
|
-
\t\tconst existing = await this.users.findBySub(sub);
|
|
77
|
-
\t\tif (existing) {
|
|
78
|
-
\t\t\tlet dirty = false;
|
|
79
|
-
\t\t\tif (!existing.email && email) {
|
|
80
|
-
\t\t\t\texisting.email = email;
|
|
81
|
-
\t\t\t\tdirty = true;
|
|
82
|
-
\t\t\t}
|
|
83
|
-
\t\t\tif (existing.displayName === existing.sub && displayFromToken) {
|
|
84
|
-
\t\t\t\texisting.displayName = displayFromToken;
|
|
85
|
-
\t\t\t\tdirty = true;
|
|
86
|
-
\t\t\t}
|
|
87
|
-
\t\t\treturn dirty ? this.users.save(existing) : existing;
|
|
88
|
-
\t\t}
|
|
89
|
-
|
|
90
|
-
\t\tconst created = new UserEntity();
|
|
91
|
-
\t\tcreated.sub = sub;
|
|
92
|
-
\t\tcreated.email = email ?? null;
|
|
93
|
-
\t\tcreated.displayName = displayFromToken ?? sub;
|
|
94
|
-
\t\tcreated.roles = Array.isArray(principal.roles)
|
|
95
|
-
\t\t\t? principal.roles.filter((r): r is string => typeof r === "string")
|
|
96
|
-
\t\t\t: [];
|
|
97
|
-
\t\treturn this.users.save(created);
|
|
98
|
-
\t}
|
|
99
|
-
}
|
|
100
|
-
`;
|
|
101
|
-
const ABILITY_FACTORY_TS = `import { AbilityBuilder, createMongoAbility } from "@casl/ability";
|
|
102
|
-
import type { Principal } from "@decohere/cortex";
|
|
103
|
-
|
|
104
|
-
/** Root role — expands to CASL \`manage\`/\`all\` (covers every future ability). */
|
|
105
|
-
export const ADMIN_ROLE = "admin";
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Roles are collections of abilities. Admin is a wildcard, not an enumerated list.
|
|
109
|
-
* Non-admin principals keep Auth0 scope / permission → ability mapping.
|
|
110
|
-
*/
|
|
111
|
-
export function createAbilityFactory(principal: Principal) {
|
|
112
|
-
\tconst { can, build } = new AbilityBuilder(createMongoAbility);
|
|
113
|
-
\tif (principal.roles?.includes(ADMIN_ROLE)) {
|
|
114
|
-
\t\tcan("manage", "all");
|
|
115
|
-
\t\treturn build();
|
|
116
|
-
\t}
|
|
117
|
-
\tfor (const scope of principal.scopes ?? []) {
|
|
118
|
-
\t\tconst [action, subject] = scope.split(":");
|
|
119
|
-
\t\tif (action && subject) can(action, subject);
|
|
120
|
-
\t}
|
|
121
|
-
\treturn build();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Map JWT payload → Principal.
|
|
126
|
-
*
|
|
127
|
-
* - \`roles\` come from the IdP (Auth0 RBAC / Action from app_metadata). Admins are
|
|
128
|
-
* assigned in Auth0 — no server env change required for additional admins.
|
|
129
|
-
* - \`AUTH0_ADMIN_USER_SUB\` is an optional bootstrap so the seeded admin works
|
|
130
|
-
* before/without roles on the access token.
|
|
131
|
-
* - API permissions are taken from \`scope\` and from Auth0's \`permissions\` claim.
|
|
132
|
-
*/
|
|
133
|
-
export function mapPrincipal(payload: Record<string, unknown>): Principal {
|
|
134
|
-
\tconst sub = typeof payload.sub === "string" ? payload.sub : "";
|
|
135
|
-
\tconst fromScopeClaim =
|
|
136
|
-
\t\ttypeof payload.scope === "string"
|
|
137
|
-
\t\t\t? payload.scope.split(" ").filter(Boolean)
|
|
138
|
-
\t\t\t: Array.isArray(payload.scopes)
|
|
139
|
-
\t\t\t\t? (payload.scopes as unknown[]).filter((s): s is string => typeof s === "string")
|
|
140
|
-
\t\t\t\t: [];
|
|
141
|
-
\tconst fromPermissions = Array.isArray(payload.permissions)
|
|
142
|
-
\t\t? (payload.permissions as unknown[]).filter((s): s is string => typeof s === "string")
|
|
143
|
-
\t\t: [];
|
|
144
|
-
\tconst merged = [...new Set([...fromScopeClaim, ...fromPermissions])];
|
|
145
|
-
\tconst scopes = merged.length > 0 ? merged : undefined;
|
|
146
|
-
\tconst fromToken = Array.isArray(payload.roles)
|
|
147
|
-
\t\t? (payload.roles as unknown[]).filter((r): r is string => typeof r === "string")
|
|
148
|
-
\t\t: [];
|
|
149
|
-
\tconst roles =
|
|
150
|
-
\t\tsub && sub === process.env.AUTH0_ADMIN_USER_SUB
|
|
151
|
-
\t\t\t? [...new Set([ADMIN_ROLE, ...fromToken])]
|
|
152
|
-
\t\t\t: fromToken.length > 0
|
|
153
|
-
\t\t\t\t? fromToken
|
|
154
|
-
\t\t\t\t: undefined;
|
|
155
|
-
\treturn { ...payload, sub, roles, scopes };
|
|
156
|
-
}
|
|
157
|
-
`;
|
|
158
|
-
async function getOptions(workspace, cliOptions) {
|
|
159
|
-
if (cliOptions.authProvider === "auth0" && !cliOptions.issuerBaseUrl) {
|
|
160
|
-
return getAuth0Options(workspace, cliOptions);
|
|
161
|
-
}
|
|
162
|
-
if (cliOptions.authProvider != null) return {};
|
|
163
|
-
const choices = [
|
|
164
|
-
{
|
|
165
|
-
label: "none"
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
label: "auth0"
|
|
169
|
-
}
|
|
170
|
-
];
|
|
171
|
-
const selected = await pick("\n Auth provider: ", choices, (c)=>c.label);
|
|
172
|
-
const provider = selected?.label ?? "none";
|
|
173
|
-
if (provider !== "auth0") return {
|
|
174
|
-
authProvider: "none"
|
|
175
|
-
};
|
|
176
|
-
return getAuth0Options(workspace, cliOptions);
|
|
177
|
-
}
|
|
178
|
-
async function getAuth0Options(workspace, cliOptions) {
|
|
179
|
-
if (!auth0Cli.isInstalled()) {
|
|
180
|
-
console.error("\n The Auth0 CLI is required for automated setup.\n" + " Install: brew install auth0/auth0-cli/auth0\n" + " Or visit: https://github.com/auth0/auth0-cli\n");
|
|
181
|
-
process.exit(1);
|
|
182
|
-
}
|
|
183
|
-
process.stdout.write("\n Checking Auth0 credentials...");
|
|
184
|
-
let tenants = auth0Cli.listTenants();
|
|
185
|
-
if (tenants.length === 0) {
|
|
186
|
-
process.stdout.write(" not logged in.\n");
|
|
187
|
-
console.log(" Logging in to Auth0 (your browser will open)...");
|
|
188
|
-
if (!auth0Cli.login()) {
|
|
189
|
-
console.error("\n Auth0 login failed.");
|
|
190
|
-
process.exit(1);
|
|
191
|
-
}
|
|
192
|
-
tenants = auth0Cli.listTenants();
|
|
193
|
-
}
|
|
194
|
-
try {
|
|
195
|
-
auth0Cli.listApis();
|
|
196
|
-
process.stdout.write(" ok.\n");
|
|
197
|
-
} catch (err) {
|
|
198
|
-
if (err instanceof auth0Cli.Auth0AuthError) {
|
|
199
|
-
process.stdout.write(" token expired.\n");
|
|
200
|
-
console.log(" Re-authenticating (your browser will open)...");
|
|
201
|
-
if (!auth0Cli.login()) {
|
|
202
|
-
console.error("\n Auth0 login failed.");
|
|
203
|
-
process.exit(1);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
const active = tenants.find((t)=>t.active) ?? tenants[0];
|
|
208
|
-
const issuerBaseUrl = `https://${active.name}`;
|
|
209
|
-
console.log(`\n ✓ Auth0 tenant: ${issuerBaseUrl}`);
|
|
210
|
-
process.stdout.write("\n Fetching Auth0 APIs...");
|
|
211
|
-
const userApis = auth0Cli.listApis().filter((a)=>!a.name.includes("Management API"));
|
|
212
|
-
process.stdout.write(` found ${userApis.length}.\n`);
|
|
213
|
-
const projectName = workspace.name;
|
|
214
|
-
const apiChoices = [
|
|
215
|
-
{
|
|
216
|
-
label: `<new> https://api.${projectName}`,
|
|
217
|
-
existing: null
|
|
218
|
-
},
|
|
219
|
-
...userApis.map((a)=>({
|
|
220
|
-
label: `${a.name} — ${a.identifier}`,
|
|
221
|
-
existing: a.identifier
|
|
222
|
-
}))
|
|
223
|
-
];
|
|
224
|
-
const apiSelected = await pick("\n Select Auth0 API: ", apiChoices, (c)=>c.label);
|
|
225
|
-
let audience;
|
|
226
|
-
if (!apiSelected || apiSelected.existing === null) {
|
|
227
|
-
const defaultId = `https://api.${projectName}`;
|
|
228
|
-
audience = await ask(` API Identifier [${defaultId}]: `, defaultId);
|
|
229
|
-
console.log("\n Creating Auth0 API...");
|
|
230
|
-
audience = auth0Cli.createApi(`${projectName} API`, audience).identifier;
|
|
231
|
-
console.log(` \u2713 API created: ${audience}`);
|
|
232
|
-
} else {
|
|
233
|
-
audience = apiSelected.existing;
|
|
234
|
-
console.log(` \u2713 Selected: ${audience}`);
|
|
235
|
-
try {
|
|
236
|
-
auth0Cli.updateApiScopes(audience);
|
|
237
|
-
console.log(` \u2713 API scopes ensured (${auth0Cli.HELLO_API_SCOPES.join(", ")})`);
|
|
238
|
-
} catch {
|
|
239
|
-
console.error(" ✗ Failed to update API scopes — add Hello scopes in the Auth0 dashboard.");
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
const withLocalUsers = cliOptions.databaseProvider === "postgres/typeorm";
|
|
243
|
-
let adminUsername;
|
|
244
|
-
let adminPassword;
|
|
245
|
-
if (withLocalUsers) {
|
|
246
|
-
adminUsername = cliOptions.adminUsername ?? await ask("\n Admin username [admin]: ", "admin");
|
|
247
|
-
adminPassword = cliOptions.adminPassword;
|
|
248
|
-
if (adminPassword == null) {
|
|
249
|
-
adminPassword = await ask(" Admin password (leave blank to generate): ");
|
|
250
|
-
if (!adminPassword) {
|
|
251
|
-
adminPassword = auth0Cli.generateAdminPassword();
|
|
252
|
-
console.log(` ✓ Generated admin password (written to .env): ${adminPassword}`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
authProvider: "auth0",
|
|
258
|
-
issuerBaseUrl,
|
|
259
|
-
audience,
|
|
260
|
-
...withLocalUsers ? {
|
|
261
|
-
adminUsername,
|
|
262
|
-
adminPassword
|
|
263
|
-
} : {}
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
function adminEmail(workspaceName, username) {
|
|
267
|
-
if (username.includes("@")) return username;
|
|
268
|
-
const slug = workspaceName.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
269
|
-
return `${username}@${slug}.example.com`;
|
|
270
|
-
}
|
|
271
|
-
function createAuth0AdminUser(workspace, options, authConfig) {
|
|
272
|
-
const username = options.adminUsername ?? "admin";
|
|
273
|
-
const password = options.adminPassword;
|
|
274
|
-
if (!password) return;
|
|
275
|
-
const email = adminEmail(workspace.name, username);
|
|
276
|
-
try {
|
|
277
|
-
const admin = auth0Cli.createAdminDatabaseUser({
|
|
278
|
-
email,
|
|
279
|
-
password,
|
|
280
|
-
username
|
|
281
|
-
});
|
|
282
|
-
authConfig.setAdminUser({
|
|
283
|
-
sub: admin.user_id,
|
|
284
|
-
email: admin.email,
|
|
285
|
-
password: admin.password,
|
|
286
|
-
username: admin.username
|
|
287
|
-
});
|
|
288
|
-
workspace.getModel(WELL_KNOWN_FILES.ENV_FILE)?.addEntry("AUTH0_ADMIN_USER_SUB", admin.user_id).addEntry("AUTH0_ADMIN_USER_EMAIL", admin.email).addEntry("AUTH0_ADMIN_USER_PASSWORD", admin.password).addEntry("AUTH0_ADMIN_USERNAME", admin.username);
|
|
289
|
-
console.log(` ✓ Auth0 admin user ready: ${admin.email} (role: ${ADMIN_ROLE})`);
|
|
290
|
-
console.log(` Login with EMAIL (not username): ${admin.email}`);
|
|
291
|
-
console.log(` Password: see AUTH0_ADMIN_USER_PASSWORD in .env`);
|
|
292
|
-
} catch (err) {
|
|
293
|
-
console.error(` ✗ Failed to ensure Auth0 admin user (${email}): ${err instanceof Error ? err.message : err}`);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
function scaffoldLocalUserProfile(workspace) {
|
|
297
|
-
workspace.getModel(WELL_KNOWN_FILES.PACKAGE_JSON)?.addDependency("@casl/ability", "^6.8.1");
|
|
298
|
-
for (const project of workspace.getProjects()){
|
|
299
|
-
project.getModel(WELL_KNOWN_FILES.API_PACKAGE_JSON)?.addDependency("@casl/ability", "^6.8.1");
|
|
300
|
-
project.getModel(WELL_KNOWN_FILES.MAIN_TS)?.addEntity({
|
|
301
|
-
className: "UserEntity",
|
|
302
|
-
importPath: "./entities/user.entity.js"
|
|
303
|
-
}).addRepository({
|
|
304
|
-
className: "UserEntityRepository",
|
|
305
|
-
importPath: "./repositories/user.repository.js"
|
|
306
|
-
}).addService({
|
|
307
|
-
className: "UserService",
|
|
308
|
-
importPath: "./services/user.service.js",
|
|
309
|
-
scope: "request"
|
|
310
|
-
}).addImport("createAbilityFactory", "./auth/ability.factory.js").addImport("mapPrincipal", "./auth/ability.factory.js").setTypeOrmSeed([
|
|
311
|
-
"const sub = process.env.AUTH0_ADMIN_USER_SUB;",
|
|
312
|
-
"if (!sub) return;",
|
|
313
|
-
"const repo = dataSource.getRepository(UserEntity);",
|
|
314
|
-
"const existing = await repo.findOne({ where: { sub } });",
|
|
315
|
-
"if (existing) return;",
|
|
316
|
-
"await repo.save(repo.create({",
|
|
317
|
-
" sub,",
|
|
318
|
-
" email: process.env.AUTH0_ADMIN_USER_EMAIL ?? null,",
|
|
319
|
-
' displayName: process.env.AUTH0_ADMIN_USERNAME ?? "admin",',
|
|
320
|
-
' roles: ["admin"],',
|
|
321
|
-
"}));"
|
|
322
|
-
].join(" "));
|
|
323
|
-
project.addFile({
|
|
324
|
-
files: ()=>[
|
|
325
|
-
{
|
|
326
|
-
filePath: "src/entities/user.entity.ts",
|
|
327
|
-
content: USER_ENTITY_TS
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
filePath: "src/repositories/user.repository.ts",
|
|
331
|
-
content: USER_REPOSITORY_TS
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
filePath: "src/services/user.service.ts",
|
|
335
|
-
content: USER_SERVICE_TS
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
filePath: "src/auth/ability.factory.ts",
|
|
339
|
-
content: ABILITY_FACTORY_TS
|
|
340
|
-
}
|
|
341
|
-
]
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
for (const controller of workspace.getModels(ControllerSourceFile)){
|
|
345
|
-
controller.addImport("UserService", "../services/user.service.js").addConstructorParam("private readonly users: UserService");
|
|
346
|
-
controller.getMethod("getOne").setAsync(true).setBody([
|
|
347
|
-
"const user = await this.users.ensureFromPrincipal(this.principalProvider.currentPrincipal);",
|
|
348
|
-
// biome-ignore lint/suspicious/noTemplateCurlyInString: template literal in generated code
|
|
349
|
-
"return { id, message: `Hello ${user.displayName}!`, username: user.displayName };"
|
|
350
|
-
]);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
function scaffold(workspace, options) {
|
|
354
|
-
if (options.authProvider !== "auth0") return;
|
|
355
|
-
const resource = "Hello";
|
|
356
|
-
const withLocalUsers = options.databaseProvider === "postgres/typeorm";
|
|
357
|
-
for (const controller of workspace.getModels(ControllerSourceFile)){
|
|
358
|
-
controller.addDecorator(`@Authorize("read", "${resource}")`).addCortexImport("AllowAnonymous").addCortexImport("Authorize").addCortexImport("IPrincipalProviderToken").addCortexImport("IPrincipalProvider", true).addImport("inject", "@decohere/crucible/decorators").addConstructorParam(`@inject(IPrincipalProviderToken) private readonly principalProvider: IPrincipalProvider`);
|
|
359
|
-
controller.getMethod("getAll").addDecorator("@AllowAnonymous()");
|
|
360
|
-
if (!withLocalUsers) {
|
|
361
|
-
controller.getMethod("getOne").setBody([
|
|
362
|
-
"const principal = this.principalProvider.currentPrincipal;",
|
|
363
|
-
"const username =",
|
|
364
|
-
'\t(typeof principal?.name === "string" && principal.name) ||',
|
|
365
|
-
'\t(typeof principal?.nickname === "string" && principal.nickname) ||',
|
|
366
|
-
'\t(typeof principal?.email === "string" && principal.email) ||',
|
|
367
|
-
'\t(typeof principal?.preferred_username === "string" && principal.preferred_username) ||',
|
|
368
|
-
"\tprincipal?.sub ||",
|
|
369
|
-
'\t"unknown";',
|
|
370
|
-
// biome-ignore lint/suspicious/noTemplateCurlyInString: template literal in generated code
|
|
371
|
-
"return { id, message: `Hello ${username}!`, username };"
|
|
372
|
-
]);
|
|
373
|
-
}
|
|
374
|
-
controller.getMethod("create").addDecorator(`@Authorize("create", "${resource}")`);
|
|
375
|
-
controller.getMethod("update").addDecorator(`@Authorize("update", "${resource}")`);
|
|
376
|
-
controller.getMethod("remove").addDecorator(`@Authorize("delete", "${resource}")`);
|
|
377
|
-
}
|
|
378
|
-
for (const project of workspace.getProjects()){
|
|
379
|
-
project.getModel(WELL_KNOWN_FILES.MAIN_TS)?.addSideEffectImport("@decohere/cortex/auth");
|
|
380
|
-
}
|
|
381
|
-
if (withLocalUsers) {
|
|
382
|
-
scaffoldLocalUserProfile(workspace);
|
|
383
|
-
for (const project of workspace.getProjects()){
|
|
384
|
-
project.getModel(WELL_KNOWN_FILES.MAIN_TS)?.addBuilderCall(".withAuth(createAbilityFactory, { principalMapper: mapPrincipal })");
|
|
385
|
-
}
|
|
386
|
-
} else {
|
|
387
|
-
for (const project of workspace.getProjects()){
|
|
388
|
-
project.getModel(WELL_KNOWN_FILES.MAIN_TS)?.addBuilderCall(".withAuth()");
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
// biome-ignore lint/suspicious/noTemplateCurlyInString: intentional YAML env-var placeholders
|
|
392
|
-
const issuerPlaceholder = "${AUTH0_ISSUER_BASE_URL}";
|
|
393
|
-
// biome-ignore lint/suspicious/noTemplateCurlyInString: intentional YAML env-var placeholders
|
|
394
|
-
const audPlaceholder = "${AUTH0_AUDIENCE}";
|
|
395
|
-
const configSection = `auth:\n discoveryUrl: ${issuerPlaceholder}\n audience: ${audPlaceholder}\n`;
|
|
396
|
-
workspace.getModel(WELL_KNOWN_FILES.CONFIG_YAML)?.addSection(configSection);
|
|
397
|
-
workspace.getModel(WELL_KNOWN_FILES.ENV_FILE)?.addEntry("AUTH0_ISSUER_BASE_URL", options.issuerBaseUrl ?? "").addEntry("AUTH0_AUDIENCE", options.audience ?? "");
|
|
398
|
-
const authConfig = new AuthConfigModel({
|
|
399
|
-
issuerBaseUrl: options.issuerBaseUrl ?? "",
|
|
400
|
-
audience: options.audience ?? ""
|
|
401
|
-
});
|
|
402
|
-
workspace.addModel(authConfig);
|
|
403
|
-
if (withLocalUsers && options.adminPassword) {
|
|
404
|
-
createAuth0AdminUser(workspace, options, authConfig);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
export const AuthFeature = {
|
|
408
|
-
getOptions,
|
|
409
|
-
scaffold
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL2NyZWF0ZS9zcmMvd2ViYXBpL2ZlYXR1cmVzL2F1dGguZmVhdHVyZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc2ssIHBpY2sgfSBmcm9tIFwiLi4vLi4vZG9tYWluL3Byb21wdC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBDb25maWdZYW1sIH0gZnJvbSBcIi4uLy4uL21vZGVscy9jb25maWcteWFtbC5tb2RlbC5qc1wiO1xuaW1wb3J0IHsgV0VMTF9LTk9XTl9GSUxFUyB9IGZyb20gXCIuLi8uLi9tb2RlbHMvY29uc3QuanNcIjtcbmltcG9ydCB0eXBlIHsgRW52RmlsZSB9IGZyb20gXCIuLi8uLi9tb2RlbHMvZW52LWZpbGUubW9kZWwuanNcIjtcbmltcG9ydCB0eXBlIHsgUGFja2FnZUpzb24gfSBmcm9tIFwiLi4vLi4vbW9kZWxzL3BhY2thZ2UtanNvbi5tb2RlbC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBXb3Jrc3BhY2VTY2FmZm9sZCB9IGZyb20gXCIuLi8uLi9tb2RlbHMvcHJvamVjdC5tb2RlbC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBGZWF0dXJlIH0gZnJvbSBcIi4uLy4uL3NlcnZpY2VzL3NjYWZmb2xkLnNlcnZpY2UuanNcIjtcbmltcG9ydCB0eXBlIHsgQXBpUGFja2FnZUpzb24gfSBmcm9tIFwiLi4vbW9kZWxzL2FwaS1wYWNrYWdlLWpzb24ubW9kZWwuanNcIjtcbmltcG9ydCB7IEF1dGhDb25maWdNb2RlbCB9IGZyb20gXCIuLi9tb2RlbHMvYXV0aC1jb25maWcubW9kZWwuanNcIjtcbmltcG9ydCB7IENvbnRyb2xsZXJTb3VyY2VGaWxlIH0gZnJvbSBcIi4uL21vZGVscy9jb250cm9sbGVyLXNvdXJjZS1maWxlLm1vZGVsLmpzXCI7XG5pbXBvcnQgdHlwZSB7IE1haW5Tb3VyY2VGaWxlIH0gZnJvbSBcIi4uL21vZGVscy9tYWluLXNvdXJjZS1maWxlLm1vZGVsLmpzXCI7XG5pbXBvcnQgKiBhcyBhdXRoMENsaSBmcm9tIFwiLi9hdXRoMC1jbGkuanNcIjtcblxuZXhwb3J0IHR5cGUgQXV0aE9wdGlvbnMgPSB7XG5cdGF1dGhQcm92aWRlcj86IFwiYXV0aDBcIiB8IFwibm9uZVwiO1xuXHRpc3N1ZXJCYXNlVXJsPzogc3RyaW5nO1xuXHRhdWRpZW5jZT86IHN0cmluZztcblx0ZGF0YWJhc2VQcm92aWRlcj86IFwibm9uZVwiIHwgXCJwb3N0Z3Jlcy90eXBlb3JtXCI7XG5cdGFkbWluVXNlcm5hbWU/OiBzdHJpbmc7XG5cdGFkbWluUGFzc3dvcmQ/OiBzdHJpbmc7XG59O1xuXG5jb25zdCBBRE1JTl9ST0xFID0gXCJhZG1pblwiO1xuXG5jb25zdCBVU0VSX0VOVElUWV9UUyA9IGBpbXBvcnQgeyBDb2x1bW4sIEVudGl0eSwgUHJpbWFyeUdlbmVyYXRlZENvbHVtbiB9IGZyb20gXCJ0eXBlb3JtXCI7XG5pbXBvcnQgdHlwZSB7IEVudGl0eSBhcyBJRW50aXR5IH0gZnJvbSBcIkBkZWNvaGVyZS9jb2RleFwiO1xuXG5ARW50aXR5KClcbmV4cG9ydCBjbGFzcyBVc2VyRW50aXR5IGltcGxlbWVudHMgSUVudGl0eSB7XG5cXHRAUHJpbWFyeUdlbmVyYXRlZENvbHVtbigpXG5cXHRwdWJsaWMgaWQhOiBudW1iZXI7XG5cblxcdEBDb2x1bW4oeyB1bmlxdWU6IHRydWUgfSlcblxcdHB1YmxpYyBzdWIhOiBzdHJpbmc7XG5cblxcdEBDb2x1bW4oeyB0eXBlOiBcInZhcmNoYXJcIiwgbnVsbGFibGU6IHRydWUgfSlcblxcdHB1YmxpYyBlbWFpbCE6IHN0cmluZyB8IG51bGw7XG5cblxcdEBDb2x1bW4oKVxuXFx0cHVibGljIGRpc3BsYXlOYW1lITogc3RyaW5nO1xuXG5cXHQvKiogQXBwLW93bmVkIHJvbGVzLiBUaGUgXFxgYWRtaW5cXGAgcm9sZSBncmFudHMgQ0FTTCBcXGBtYW5hZ2VcXGAvXFxgYWxsXFxgICh3aWxkY2FyZCkuICovXG5cXHRAQ29sdW1uKHsgdHlwZTogXCJzaW1wbGUtanNvblwiLCBkZWZhdWx0OiBcIltdXCIgfSlcblxcdHB1YmxpYyByb2xlcyE6IHN0cmluZ1tdO1xufVxuYDtcblxuY29uc3QgVVNFUl9SRVBPU0lUT1JZX1RTID0gYGltcG9ydCB7IGluamVjdGFibGUgfSBmcm9tIFwiQGRlY29oZXJlL2NydWNpYmxlL2RlY29yYXRvcnNcIjtcbmltcG9ydCB7IGluamVjdFVuaXRPZldvcmsgfSBmcm9tIFwiQGRlY29oZXJlL2NvZGV4XCI7XG5pbXBvcnQgeyBUeXBlT3JtUmVwb3NpdG9yeSwgVHlwZU9ybVVuaXRPZldvcmsgfSBmcm9tIFwiQGRlY29oZXJlL2NvZGV4LXR5cGVvcm1cIjtcbmltcG9ydCB7IFVzZXJFbnRpdHkgfSBmcm9tIFwiLi4vZW50aXRpZXMvdXNlci5lbnRpdHkuanNcIjtcblxuQGluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFVzZXJFbnRpdHlSZXBvc2l0b3J5IGV4dGVuZHMgVHlwZU9ybVJlcG9zaXRvcnk8VXNlckVudGl0eT4ge1xuXFx0cHVibGljIGNvbnN0cnVjdG9yKEBpbmplY3RVbml0T2ZXb3JrKCkgdW5pdE9mV29yazogVHlwZU9ybVVuaXRPZldvcmspIHtcblxcdFxcdHN1cGVyKHVuaXRPZldvcmssIFVzZXJFbnRpdHkpO1xuXFx0fVxuXG5cXHRwdWJsaWMgZmluZEJ5U3ViKHN1Yjogc3RyaW5nKTogUHJvbWlzZTxVc2VyRW50aXR5IHwgbnVsbD4ge1xuXFx0XFx0cmV0dXJuIHRoaXMuZmlyc3RPckRlZmF1bHQoeyB3aGVyZTogeyBzdWIgfSB9KTtcblxcdH1cbn1cbmA7XG5cbmNvbnN0IFVTRVJfU0VSVklDRV9UUyA9IGBpbXBvcnQgeyBpbmplY3RhYmxlIH0gZnJvbSBcIkBkZWNvaGVyZS9jcnVjaWJsZS9kZWNvcmF0b3JzXCI7XG5pbXBvcnQgdHlwZSB7IFByaW5jaXBhbCB9IGZyb20gXCJAZGVjb2hlcmUvY29ydGV4XCI7XG5pbXBvcnQgeyBVc2VyRW50aXR5IH0gZnJvbSBcIi4uL2VudGl0aWVzL3VzZXIuZW50aXR5LmpzXCI7XG5pbXBvcnQgeyBVc2VyRW50aXR5UmVwb3NpdG9yeSB9IGZyb20gXCIuLi9yZXBvc2l0b3JpZXMvdXNlci5yZXBvc2l0b3J5LmpzXCI7XG5cbmZ1bmN0aW9uIGNsYWltU3RyaW5nKHByaW5jaXBhbDogUHJpbmNpcGFsIHwgdW5kZWZpbmVkLCBrZXk6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG5cXHRjb25zdCB2YWx1ZSA9IHByaW5jaXBhbD8uW2tleV07XG5cXHRyZXR1cm4gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiICYmIHZhbHVlLmxlbmd0aCA+IDAgPyB2YWx1ZSA6IHVuZGVmaW5lZDtcbn1cblxuQGluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFVzZXJTZXJ2aWNlIHtcblxcdHB1YmxpYyBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHVzZXJzOiBVc2VyRW50aXR5UmVwb3NpdG9yeSkge31cblxuXFx0LyoqXG5cXHQgKiBVcHNlcnQgYSBsb2NhbCBwcm9maWxlIGtleWVkIGJ5IEpXVCBcXGBzdWJcXGAuXG5cXHQgKiBQcm9maWxlIGZpZWxkcyBmcm9tIHRoZSB0b2tlbiBmaWxsIGdhcHM7IHNlZWRlZCByb3dzIGtlZXAgZGlzcGxheU5hbWUvZW1haWwvcm9sZXMuXG5cXHQgKi9cblxcdHB1YmxpYyBhc3luYyBlbnN1cmVGcm9tUHJpbmNpcGFsKHByaW5jaXBhbDogUHJpbmNpcGFsIHwgdW5kZWZpbmVkKTogUHJvbWlzZTxVc2VyRW50aXR5PiB7XG5cXHRcXHRjb25zdCBzdWIgPSBwcmluY2lwYWw/LnN1YjtcblxcdFxcdGlmICghc3ViKSB7XG5cXHRcXHRcXHR0aHJvdyBuZXcgRXJyb3IoXCJBdXRoZW50aWNhdGVkIHJlcXVlc3QgaXMgbWlzc2luZyBwcmluY2lwYWwuc3ViXCIpO1xuXFx0XFx0fVxuXG5cXHRcXHRjb25zdCBlbWFpbCA9XG5cXHRcXHRcXHRjbGFpbVN0cmluZyhwcmluY2lwYWwsIFwiZW1haWxcIikgPz8gY2xhaW1TdHJpbmcocHJpbmNpcGFsLCBcInByZWZlcnJlZF91c2VybmFtZVwiKTtcblxcdFxcdGNvbnN0IGRpc3BsYXlGcm9tVG9rZW4gPVxuXFx0XFx0XFx0Y2xhaW1TdHJpbmcocHJpbmNpcGFsLCBcIm5hbWVcIikgPz9cblxcdFxcdFxcdGNsYWltU3RyaW5nKHByaW5jaXBhbCwgXCJuaWNrbmFtZVwiKSA/P1xuXFx0XFx0XFx0ZW1haWw7XG5cblxcdFxcdGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgdGhpcy51c2Vycy5maW5kQnlTdWIoc3ViKTtcblxcdFxcdGlmIChleGlzdGluZykge1xuXFx0XFx0XFx0bGV0IGRpcnR5ID0gZmFsc2U7XG5cXHRcXHRcXHRpZiAoIWV4aXN0aW5nLmVtYWlsICYmIGVtYWlsKSB7XG5cXHRcXHRcXHRcXHRleGlzdGluZy5lbWFpbCA9IGVtYWlsO1xuXFx0XFx0XFx0XFx0ZGlydHkgPSB0cnVlO1xuXFx0XFx0XFx0fVxuXFx0XFx0XFx0aWYgKGV4aXN0aW5nLmRpc3BsYXlOYW1lID09PSBleGlzdGluZy5zdWIgJiYgZGlzcGxheUZyb21Ub2tlbikge1xuXFx0XFx0XFx0XFx0ZXhpc3RpbmcuZGlzcGxheU5hbWUgPSBkaXNwbGF5RnJvbVRva2VuO1xuXFx0XFx0XFx0XFx0ZGlydHkgPSB0cnVlO1xuXFx0XFx0XFx0fVxuXFx0XFx0XFx0cmV0dXJuIGRpcnR5ID8gdGhpcy51c2Vycy5zYXZlKGV4aXN0aW5nKSA6IGV4aXN0aW5nO1xuXFx0XFx0fVxuXG5cXHRcXHRjb25zdCBjcmVhdGVkID0gbmV3IFVzZXJFbnRpdHkoKTtcblxcdFxcdGNyZWF0ZWQuc3ViID0gc3ViO1xuXFx0XFx0Y3JlYXRlZC5lbWFpbCA9IGVtYWlsID8/IG51bGw7XG5cXHRcXHRjcmVhdGVkLmRpc3BsYXlOYW1lID0gZGlzcGxheUZyb21Ub2tlbiA/PyBzdWI7XG5cXHRcXHRjcmVhdGVkLnJvbGVzID0gQXJyYXkuaXNBcnJheShwcmluY2lwYWwucm9sZXMpXG5cXHRcXHRcXHQ/IHByaW5jaXBhbC5yb2xlcy5maWx0ZXIoKHIpOiByIGlzIHN0cmluZyA9PiB0eXBlb2YgciA9PT0gXCJzdHJpbmdcIilcblxcdFxcdFxcdDogW107XG5cXHRcXHRyZXR1cm4gdGhpcy51c2Vycy5zYXZlKGNyZWF0ZWQpO1xuXFx0fVxufVxuYDtcblxuY29uc3QgQUJJTElUWV9GQUNUT1JZX1RTID0gYGltcG9ydCB7IEFiaWxpdHlCdWlsZGVyLCBjcmVhdGVNb25nb0FiaWxpdHkgfSBmcm9tIFwiQGNhc2wvYWJpbGl0eVwiO1xuaW1wb3J0IHR5cGUgeyBQcmluY2lwYWwgfSBmcm9tIFwiQGRlY29oZXJlL2NvcnRleFwiO1xuXG4vKiogUm9vdCByb2xlIOKAlCBleHBhbmRzIHRvIENBU0wgXFxgbWFuYWdlXFxgL1xcYGFsbFxcYCAoY292ZXJzIGV2ZXJ5IGZ1dHVyZSBhYmlsaXR5KS4gKi9cbmV4cG9ydCBjb25zdCBBRE1JTl9ST0xFID0gXCJhZG1pblwiO1xuXG4vKipcbiAqIFJvbGVzIGFyZSBjb2xsZWN0aW9ucyBvZiBhYmlsaXRpZXMuIEFkbWluIGlzIGEgd2lsZGNhcmQsIG5vdCBhbiBlbnVtZXJhdGVkIGxpc3QuXG4gKiBOb24tYWRtaW4gcHJpbmNpcGFscyBrZWVwIEF1dGgwIHNjb3BlIC8gcGVybWlzc2lvbiDihpIgYWJpbGl0eSBtYXBwaW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlQWJpbGl0eUZhY3RvcnkocHJpbmNpcGFsOiBQcmluY2lwYWwpIHtcblxcdGNvbnN0IHsgY2FuLCBidWlsZCB9ID0gbmV3IEFiaWxpdHlCdWlsZGVyKGNyZWF0ZU1vbmdvQWJpbGl0eSk7XG5cXHRpZiAocHJpbmNpcGFsLnJvbGVzPy5pbmNsdWRlcyhBRE1JTl9ST0xFKSkge1xuXFx0XFx0Y2FuKFwibWFuYWdlXCIsIFwiYWxsXCIpO1xuXFx0XFx0cmV0dXJuIGJ1aWxkKCk7XG5cXHR9XG5cXHRmb3IgKGNvbnN0IHNjb3BlIG9mIHByaW5jaXBhbC5zY29wZXMgPz8gW10pIHtcblxcdFxcdGNvbnN0IFthY3Rpb24sIHN1YmplY3RdID0gc2NvcGUuc3BsaXQoXCI6XCIpO1xuXFx0XFx0aWYgKGFjdGlvbiAmJiBzdWJqZWN0KSBjYW4oYWN0aW9uLCBzdWJqZWN0KTtcblxcdH1cblxcdHJldHVybiBidWlsZCgpO1xufVxuXG4vKipcbiAqIE1hcCBKV1QgcGF5bG9hZCDihpIgUHJpbmNpcGFsLlxuICpcbiAqIC0gXFxgcm9sZXNcXGAgY29tZSBmcm9tIHRoZSBJZFAgKEF1dGgwIFJCQUMgLyBBY3Rpb24gZnJvbSBhcHBfbWV0YWRhdGEpLiBBZG1pbnMgYXJlXG4gKiAgIGFzc2lnbmVkIGluIEF1dGgwIOKAlCBubyBzZXJ2ZXIgZW52IGNoYW5nZSByZXF1aXJlZCBmb3IgYWRkaXRpb25hbCBhZG1pbnMuXG4gKiAtIFxcYEFVVEgwX0FETUlOX1VTRVJfU1VCXFxgIGlzIGFuIG9wdGlvbmFsIGJvb3RzdHJhcCBzbyB0aGUgc2VlZGVkIGFkbWluIHdvcmtzXG4gKiAgIGJlZm9yZS93aXRob3V0IHJvbGVzIG9uIHRoZSBhY2Nlc3MgdG9rZW4uXG4gKiAtIEFQSSBwZXJtaXNzaW9ucyBhcmUgdGFrZW4gZnJvbSBcXGBzY29wZVxcYCBhbmQgZnJvbSBBdXRoMCdzIFxcYHBlcm1pc3Npb25zXFxgIGNsYWltLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbWFwUHJpbmNpcGFsKHBheWxvYWQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogUHJpbmNpcGFsIHtcblxcdGNvbnN0IHN1YiA9IHR5cGVvZiBwYXlsb2FkLnN1YiA9PT0gXCJzdHJpbmdcIiA/IHBheWxvYWQuc3ViIDogXCJcIjtcblxcdGNvbnN0IGZyb21TY29wZUNsYWltID1cblxcdFxcdHR5cGVvZiBwYXlsb2FkLnNjb3BlID09PSBcInN0cmluZ1wiXG5cXHRcXHRcXHQ/IHBheWxvYWQuc2NvcGUuc3BsaXQoXCIgXCIpLmZpbHRlcihCb29sZWFuKVxuXFx0XFx0XFx0OiBBcnJheS5pc0FycmF5KHBheWxvYWQuc2NvcGVzKVxuXFx0XFx0XFx0XFx0PyAocGF5bG9hZC5zY29wZXMgYXMgdW5rbm93bltdKS5maWx0ZXIoKHMpOiBzIGlzIHN0cmluZyA9PiB0eXBlb2YgcyA9PT0gXCJzdHJpbmdcIilcblxcdFxcdFxcdFxcdDogW107XG5cXHRjb25zdCBmcm9tUGVybWlzc2lvbnMgPSBBcnJheS5pc0FycmF5KHBheWxvYWQucGVybWlzc2lvbnMpXG5cXHRcXHQ/IChwYXlsb2FkLnBlcm1pc3Npb25zIGFzIHVua25vd25bXSkuZmlsdGVyKChzKTogcyBpcyBzdHJpbmcgPT4gdHlwZW9mIHMgPT09IFwic3RyaW5nXCIpXG5cXHRcXHQ6IFtdO1xuXFx0Y29uc3QgbWVyZ2VkID0gWy4uLm5ldyBTZXQoWy4uLmZyb21TY29wZUNsYWltLCAuLi5mcm9tUGVybWlzc2lvbnNdKV07XG5cXHRjb25zdCBzY29wZXMgPSBtZXJnZWQubGVuZ3RoID4gMCA/IG1lcmdlZCA6IHVuZGVmaW5lZDtcblxcdGNvbnN0IGZyb21Ub2tlbiA9IEFycmF5LmlzQXJyYXkocGF5bG9hZC5yb2xlcylcblxcdFxcdD8gKHBheWxvYWQucm9sZXMgYXMgdW5rbm93bltdKS5maWx0ZXIoKHIpOiByIGlzIHN0cmluZyA9PiB0eXBlb2YgciA9PT0gXCJzdHJpbmdcIilcblxcdFxcdDogW107XG5cXHRjb25zdCByb2xlcyA9XG5cXHRcXHRzdWIgJiYgc3ViID09PSBwcm9jZXNzLmVudi5BVVRIMF9BRE1JTl9VU0VSX1NVQlxuXFx0XFx0XFx0PyBbLi4ubmV3IFNldChbQURNSU5fUk9MRSwgLi4uZnJvbVRva2VuXSldXG5cXHRcXHRcXHQ6IGZyb21Ub2tlbi5sZW5ndGggPiAwXG5cXHRcXHRcXHRcXHQ/IGZyb21Ub2tlblxuXFx0XFx0XFx0XFx0OiB1bmRlZmluZWQ7XG5cXHRyZXR1cm4geyAuLi5wYXlsb2FkLCBzdWIsIHJvbGVzLCBzY29wZXMgfTtcbn1cbmA7XG5cbmFzeW5jIGZ1bmN0aW9uIGdldE9wdGlvbnMoXG5cdHdvcmtzcGFjZTogV29ya3NwYWNlU2NhZmZvbGQsXG5cdGNsaU9wdGlvbnM6IFBhcnRpYWw8QXV0aE9wdGlvbnM+LFxuKTogUHJvbWlzZTxQYXJ0aWFsPEF1dGhPcHRpb25zPj4ge1xuXHRpZiAoY2xpT3B0aW9ucy5hdXRoUHJvdmlkZXIgPT09IFwiYXV0aDBcIiAmJiAhY2xpT3B0aW9ucy5pc3N1ZXJCYXNlVXJsKSB7XG5cdFx0cmV0dXJuIGdldEF1dGgwT3B0aW9ucyh3b3Jrc3BhY2UsIGNsaU9wdGlvbnMpO1xuXHR9XG5cdGlmIChjbGlPcHRpb25zLmF1dGhQcm92aWRlciAhPSBudWxsKSByZXR1cm4ge307XG5cblx0Y29uc3QgY2hvaWNlcyA9IFt7IGxhYmVsOiBcIm5vbmVcIiBhcyBjb25zdCB9LCB7IGxhYmVsOiBcImF1dGgwXCIgYXMgY29uc3QgfV07XG5cdGNvbnN0IHNlbGVjdGVkID0gYXdhaXQgcGljayhcIlxcbiAgQXV0aCBwcm92aWRlcjogXCIsIGNob2ljZXMsIChjKSA9PiBjLmxhYmVsKTtcblx0Y29uc3QgcHJvdmlkZXIgPSBzZWxlY3RlZD8ubGFiZWwgPz8gXCJub25lXCI7XG5cblx0aWYgKHByb3ZpZGVyICE9PSBcImF1dGgwXCIpIHJldHVybiB7IGF1dGhQcm92aWRlcjogXCJub25lXCIgfTtcblxuXHRyZXR1cm4gZ2V0QXV0aDBPcHRpb25zKHdvcmtzcGFjZSwgY2xpT3B0aW9ucyk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldEF1dGgwT3B0aW9ucyhcblx0d29ya3NwYWNlOiBXb3Jrc3BhY2VTY2FmZm9sZCxcblx0Y2xpT3B0aW9uczogUGFydGlhbDxBdXRoT3B0aW9ucz4sXG4pOiBQcm9taXNlPFBhcnRpYWw8QXV0aE9wdGlvbnM+PiB7XG5cdGlmICghYXV0aDBDbGkuaXNJbnN0YWxsZWQoKSkge1xuXHRcdGNvbnNvbGUuZXJyb3IoXG5cdFx0XHRcIlxcbiAgVGhlIEF1dGgwIENMSSBpcyByZXF1aXJlZCBmb3IgYXV0b21hdGVkIHNldHVwLlxcblwiICtcblx0XHRcdFx0XCIgIEluc3RhbGw6IGJyZXcgaW5zdGFsbCBhdXRoMC9hdXRoMC1jbGkvYXV0aDBcXG5cIiArXG5cdFx0XHRcdFwiICBPciB2aXNpdDogaHR0cHM6Ly9naXRodWIuY29tL2F1dGgwL2F1dGgwLWNsaVxcblwiLFxuXHRcdCk7XG5cdFx0cHJvY2Vzcy5leGl0KDEpO1xuXHR9XG5cblx0cHJvY2Vzcy5zdGRvdXQud3JpdGUoXCJcXG4gIENoZWNraW5nIEF1dGgwIGNyZWRlbnRpYWxzLi4uXCIpO1xuXHRsZXQgdGVuYW50cyA9IGF1dGgwQ2xpLmxpc3RUZW5hbnRzKCk7XG5cdGlmICh0ZW5hbnRzLmxlbmd0aCA9PT0gMCkge1xuXHRcdHByb2Nlc3Muc3Rkb3V0LndyaXRlKFwiIG5vdCBsb2dnZWQgaW4uXFxuXCIpO1xuXHRcdGNvbnNvbGUubG9nKFwiICBMb2dnaW5nIGluIHRvIEF1dGgwICh5b3VyIGJyb3dzZXIgd2lsbCBvcGVuKS4uLlwiKTtcblx0XHRpZiAoIWF1dGgwQ2xpLmxvZ2luKCkpIHtcblx0XHRcdGNvbnNvbGUuZXJyb3IoXCJcXG4gIEF1dGgwIGxvZ2luIGZhaWxlZC5cIik7XG5cdFx0XHRwcm9jZXNzLmV4aXQoMSk7XG5cdFx0fVxuXHRcdHRlbmFudHMgPSBhdXRoMENsaS5saXN0VGVuYW50cygpO1xuXHR9XG5cblx0dHJ5IHtcblx0XHRhdXRoMENsaS5saXN0QXBpcygpO1xuXHRcdHByb2Nlc3Muc3Rkb3V0LndyaXRlKFwiIG9rLlxcblwiKTtcblx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0aWYgKGVyciBpbnN0YW5jZW9mIGF1dGgwQ2xpLkF1dGgwQXV0aEVycm9yKSB7XG5cdFx0XHRwcm9jZXNzLnN0ZG91dC53cml0ZShcIiB0b2tlbiBleHBpcmVkLlxcblwiKTtcblx0XHRcdGNvbnNvbGUubG9nKFwiICBSZS1hdXRoZW50aWNhdGluZyAoeW91ciBicm93c2VyIHdpbGwgb3BlbikuLi5cIik7XG5cdFx0XHRpZiAoIWF1dGgwQ2xpLmxvZ2luKCkpIHtcblx0XHRcdFx0Y29uc29sZS5lcnJvcihcIlxcbiAgQXV0aDAgbG9naW4gZmFpbGVkLlwiKTtcblx0XHRcdFx0cHJvY2Vzcy5leGl0KDEpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdGNvbnN0IGFjdGl2ZSA9IHRlbmFudHMuZmluZCgodCkgPT4gdC5hY3RpdmUpID8/IHRlbmFudHNbMF07XG5cdGNvbnN0IGlzc3VlckJhc2VVcmwgPSBgaHR0cHM6Ly8ke2FjdGl2ZS5uYW1lfWA7XG5cdGNvbnNvbGUubG9nKGBcXG4gIOKckyBBdXRoMCB0ZW5hbnQ6ICR7aXNzdWVyQmFzZVVybH1gKTtcblxuXHRwcm9jZXNzLnN0ZG91dC53cml0ZShcIlxcbiAgRmV0Y2hpbmcgQXV0aDAgQVBJcy4uLlwiKTtcblx0Y29uc3QgdXNlckFwaXMgPSBhdXRoMENsaS5saXN0QXBpcygpLmZpbHRlcigoYSkgPT4gIWEubmFtZS5pbmNsdWRlcyhcIk1hbmFnZW1lbnQgQVBJXCIpKTtcblx0cHJvY2Vzcy5zdGRvdXQud3JpdGUoYCBmb3VuZCAke3VzZXJBcGlzLmxlbmd0aH0uXFxuYCk7XG5cblx0dHlwZSBBcGlDaG9pY2UgPSB7IGxhYmVsOiBzdHJpbmc7IGV4aXN0aW5nOiBzdHJpbmcgfCBudWxsIH07XG5cdGNvbnN0IHByb2plY3ROYW1lID0gd29ya3NwYWNlLm5hbWU7XG5cdGNvbnN0IGFwaUNob2ljZXM6IEFwaUNob2ljZVtdID0gW1xuXHRcdHsgbGFiZWw6IGA8bmV3PiBodHRwczovL2FwaS4ke3Byb2plY3ROYW1lfWAsIGV4aXN0aW5nOiBudWxsIH0sXG5cdFx0Li4udXNlckFwaXMubWFwKChhKSA9PiAoeyBsYWJlbDogYCR7YS5uYW1lfSDigJQgJHthLmlkZW50aWZpZXJ9YCwgZXhpc3Rpbmc6IGEuaWRlbnRpZmllciB9KSksXG5cdF07XG5cblx0Y29uc3QgYXBpU2VsZWN0ZWQgPSBhd2FpdCBwaWNrKFwiXFxuICBTZWxlY3QgQXV0aDAgQVBJOiBcIiwgYXBpQ2hvaWNlcywgKGMpID0+IGMubGFiZWwpO1xuXG5cdGxldCBhdWRpZW5jZTogc3RyaW5nO1xuXHRpZiAoIWFwaVNlbGVjdGVkIHx8IGFwaVNlbGVjdGVkLmV4aXN0aW5nID09PSBudWxsKSB7XG5cdFx0Y29uc3QgZGVmYXVsdElkID0gYGh0dHBzOi8vYXBpLiR7cHJvamVjdE5hbWV9YDtcblx0XHRhdWRpZW5jZSA9IGF3YWl0IGFzayhgICBBUEkgSWRlbnRpZmllciBbJHtkZWZhdWx0SWR9XTogYCwgZGVmYXVsdElkKTtcblx0XHRjb25zb2xlLmxvZyhcIlxcbiAgQ3JlYXRpbmcgQXV0aDAgQVBJLi4uXCIpO1xuXHRcdGF1ZGllbmNlID0gYXV0aDBDbGkuY3JlYXRlQXBpKGAke3Byb2plY3ROYW1lfSBBUElgLCBhdWRpZW5jZSkuaWRlbnRpZmllcjtcblx0XHRjb25zb2xlLmxvZyhgICBcXHUyNzEzIEFQSSBjcmVhdGVkOiAke2F1ZGllbmNlfWApO1xuXHR9IGVsc2Uge1xuXHRcdGF1ZGllbmNlID0gYXBpU2VsZWN0ZWQuZXhpc3Rpbmc7XG5cdFx0Y29uc29sZS5sb2coYCAgXFx1MjcxMyBTZWxlY3RlZDogJHthdWRpZW5jZX1gKTtcblx0XHR0cnkge1xuXHRcdFx0YXV0aDBDbGkudXBkYXRlQXBpU2NvcGVzKGF1ZGllbmNlKTtcblx0XHRcdGNvbnNvbGUubG9nKGAgIFxcdTI3MTMgQVBJIHNjb3BlcyBlbnN1cmVkICgke2F1dGgwQ2xpLkhFTExPX0FQSV9TQ09QRVMuam9pbihcIiwgXCIpfSlgKTtcblx0XHR9IGNhdGNoIHtcblx0XHRcdGNvbnNvbGUuZXJyb3IoXG5cdFx0XHRcdFwiICDinJcgRmFpbGVkIHRvIHVwZGF0ZSBBUEkgc2NvcGVzIOKAlCBhZGQgSGVsbG8gc2NvcGVzIGluIHRoZSBBdXRoMCBkYXNoYm9hcmQuXCIsXG5cdFx0XHQpO1xuXHRcdH1cblx0fVxuXG5cdGNvbnN0IHdpdGhMb2NhbFVzZXJzID0gY2xpT3B0aW9ucy5kYXRhYmFzZVByb3ZpZGVyID09PSBcInBvc3RncmVzL3R5cGVvcm1cIjtcblx0bGV0IGFkbWluVXNlcm5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcblx0bGV0IGFkbWluUGFzc3dvcmQ6IHN0cmluZyB8IHVuZGVmaW5lZDtcblx0aWYgKHdpdGhMb2NhbFVzZXJzKSB7XG5cdFx0YWRtaW5Vc2VybmFtZSA9XG5cdFx0XHRjbGlPcHRpb25zLmFkbWluVXNlcm5hbWUgPz8gKGF3YWl0IGFzayhcIlxcbiAgQWRtaW4gdXNlcm5hbWUgW2FkbWluXTogXCIsIFwiYWRtaW5cIikpO1xuXHRcdGFkbWluUGFzc3dvcmQgPSBjbGlPcHRpb25zLmFkbWluUGFzc3dvcmQ7XG5cdFx0aWYgKGFkbWluUGFzc3dvcmQgPT0gbnVsbCkge1xuXHRcdFx0YWRtaW5QYXNzd29yZCA9IGF3YWl0IGFzayhcIiAgQWRtaW4gcGFzc3dvcmQgKGxlYXZlIGJsYW5rIHRvIGdlbmVyYXRlKTogXCIpO1xuXHRcdFx0aWYgKCFhZG1pblBhc3N3b3JkKSB7XG5cdFx0XHRcdGFkbWluUGFzc3dvcmQgPSBhdXRoMENsaS5nZW5lcmF0ZUFkbWluUGFzc3dvcmQoKTtcblx0XHRcdFx0Y29uc29sZS5sb2coYCAg4pyTIEdlbmVyYXRlZCBhZG1pbiBwYXNzd29yZCAod3JpdHRlbiB0byAuZW52KTogJHthZG1pblBhc3N3b3JkfWApO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHJldHVybiB7XG5cdFx0YXV0aFByb3ZpZGVyOiBcImF1dGgwXCIsXG5cdFx0aXNzdWVyQmFzZVVybCxcblx0XHRhdWRpZW5jZSxcblx0XHQuLi4od2l0aExvY2FsVXNlcnMgPyB7IGFkbWluVXNlcm5hbWUsIGFkbWluUGFzc3dvcmQgfSA6IHt9KSxcblx0fTtcbn1cblxuZnVuY3Rpb24gYWRtaW5FbWFpbCh3b3Jrc3BhY2VOYW1lOiBzdHJpbmcsIHVzZXJuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuXHRpZiAodXNlcm5hbWUuaW5jbHVkZXMoXCJAXCIpKSByZXR1cm4gdXNlcm5hbWU7XG5cdGNvbnN0IHNsdWcgPSB3b3Jrc3BhY2VOYW1lLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgvW15hLXowLTktXS9nLCBcIi1cIik7XG5cdHJldHVybiBgJHt1c2VybmFtZX1AJHtzbHVnfS5leGFtcGxlLmNvbWA7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUF1dGgwQWRtaW5Vc2VyKFxuXHR3b3Jrc3BhY2U6IFdvcmtzcGFjZVNjYWZmb2xkLFxuXHRvcHRpb25zOiBBdXRoT3B0aW9ucyxcblx0YXV0aENvbmZpZzogQXV0aENvbmZpZ01vZGVsLFxuKTogdm9pZCB7XG5cdGNvbnN0IHVzZXJuYW1lID0gb3B0aW9ucy5hZG1pblVzZXJuYW1lID8/IFwiYWRtaW5cIjtcblx0Y29uc3QgcGFzc3dvcmQgPSBvcHRpb25zLmFkbWluUGFzc3dvcmQ7XG5cdGlmICghcGFzc3dvcmQpIHJldHVybjtcblxuXHRjb25zdCBlbWFpbCA9IGFkbWluRW1haWwod29ya3NwYWNlLm5hbWUsIHVzZXJuYW1lKTtcblx0dHJ5IHtcblx0XHRjb25zdCBhZG1pbiA9IGF1dGgwQ2xpLmNyZWF0ZUFkbWluRGF0YWJhc2VVc2VyKHsgZW1haWwsIHBhc3N3b3JkLCB1c2VybmFtZSB9KTtcblx0XHRhdXRoQ29uZmlnLnNldEFkbWluVXNlcih7XG5cdFx0XHRzdWI6IGFkbWluLnVzZXJfaWQsXG5cdFx0XHRlbWFpbDogYWRtaW4uZW1haWwsXG5cdFx0XHRwYXNzd29yZDogYWRtaW4ucGFzc3dvcmQsXG5cdFx0XHR1c2VybmFtZTogYWRtaW4udXNlcm5hbWUsXG5cdFx0fSk7XG5cdFx0d29ya3NwYWNlXG5cdFx0XHQuZ2V0TW9kZWw8RW52RmlsZT4oV0VMTF9LTk9XTl9GSUxFUy5FTlZfRklMRSlcblx0XHRcdD8uYWRkRW50cnkoXCJBVVRIMF9BRE1JTl9VU0VSX1NVQlwiLCBhZG1pbi51c2VyX2lkKVxuXHRcdFx0LmFkZEVudHJ5KFwiQVVUSDBfQURNSU5fVVNFUl9FTUFJTFwiLCBhZG1pbi5lbWFpbClcblx0XHRcdC5hZGRFbnRyeShcIkFVVEgwX0FETUlOX1VTRVJfUEFTU1dPUkRcIiwgYWRtaW4ucGFzc3dvcmQpXG5cdFx0XHQuYWRkRW50cnkoXCJBVVRIMF9BRE1JTl9VU0VSTkFNRVwiLCBhZG1pbi51c2VybmFtZSk7XG5cdFx0Y29uc29sZS5sb2coYCAg4pyTIEF1dGgwIGFkbWluIHVzZXIgcmVhZHk6ICR7YWRtaW4uZW1haWx9IChyb2xlOiAke0FETUlOX1JPTEV9KWApO1xuXHRcdGNvbnNvbGUubG9nKGAgICAgTG9naW4gd2l0aCBFTUFJTCAobm90IHVzZXJuYW1lKTogJHthZG1pbi5lbWFpbH1gKTtcblx0XHRjb25zb2xlLmxvZyhgICAgIFBhc3N3b3JkOiBzZWUgQVVUSDBfQURNSU5fVVNFUl9QQVNTV09SRCBpbiAuZW52YCk7XG5cdH0gY2F0Y2ggKGVycikge1xuXHRcdGNvbnNvbGUuZXJyb3IoXG5cdFx0XHRgICDinJcgRmFpbGVkIHRvIGVuc3VyZSBBdXRoMCBhZG1pbiB1c2VyICgke2VtYWlsfSk6ICR7ZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IGVycn1gLFxuXHRcdCk7XG5cdH1cbn1cblxuZnVuY3Rpb24gc2NhZmZvbGRMb2NhbFVzZXJQcm9maWxlKHdvcmtzcGFjZTogV29ya3NwYWNlU2NhZmZvbGQpOiB2b2lkIHtcblx0d29ya3NwYWNlXG5cdFx0LmdldE1vZGVsPFBhY2thZ2VKc29uPihXRUxMX0tOT1dOX0ZJTEVTLlBBQ0tBR0VfSlNPTilcblx0XHQ/LmFkZERlcGVuZGVuY3koXCJAY2FzbC9hYmlsaXR5XCIsIFwiXjYuOC4xXCIpO1xuXG5cdGZvciAoY29uc3QgcHJvamVjdCBvZiB3b3Jrc3BhY2UuZ2V0UHJvamVjdHMoKSkge1xuXHRcdHByb2plY3Rcblx0XHRcdC5nZXRNb2RlbDxBcGlQYWNrYWdlSnNvbj4oV0VMTF9LTk9XTl9GSUxFUy5BUElfUEFDS0FHRV9KU09OKVxuXHRcdFx0Py5hZGREZXBlbmRlbmN5KFwiQGNhc2wvYWJpbGl0eVwiLCBcIl42LjguMVwiKTtcblxuXHRcdHByb2plY3Rcblx0XHRcdC5nZXRNb2RlbDxNYWluU291cmNlRmlsZT4oV0VMTF9LTk9XTl9GSUxFUy5NQUlOX1RTKVxuXHRcdFx0Py5hZGRFbnRpdHkoeyBjbGFzc05hbWU6IFwiVXNlckVudGl0eVwiLCBpbXBvcnRQYXRoOiBcIi4vZW50aXRpZXMvdXNlci5lbnRpdHkuanNcIiB9KVxuXHRcdFx0LmFkZFJlcG9zaXRvcnkoe1xuXHRcdFx0XHRjbGFzc05hbWU6IFwiVXNlckVudGl0eVJlcG9zaXRvcnlcIixcblx0XHRcdFx0aW1wb3J0UGF0aDogXCIuL3JlcG9zaXRvcmllcy91c2VyLnJlcG9zaXRvcnkuanNcIixcblx0XHRcdH0pXG5cdFx0XHQuYWRkU2VydmljZSh7XG5cdFx0XHRcdGNsYXNzTmFtZTogXCJVc2VyU2VydmljZVwiLFxuXHRcdFx0XHRpbXBvcnRQYXRoOiBcIi4vc2VydmljZXMvdXNlci5zZXJ2aWNlLmpzXCIsXG5cdFx0XHRcdHNjb3BlOiBcInJlcXVlc3RcIixcblx0XHRcdH0pXG5cdFx0XHQuYWRkSW1wb3J0KFwiY3JlYXRlQWJpbGl0eUZhY3RvcnlcIiwgXCIuL2F1dGgvYWJpbGl0eS5mYWN0b3J5LmpzXCIpXG5cdFx0XHQuYWRkSW1wb3J0KFwibWFwUHJpbmNpcGFsXCIsIFwiLi9hdXRoL2FiaWxpdHkuZmFjdG9yeS5qc1wiKVxuXHRcdFx0LnNldFR5cGVPcm1TZWVkKFxuXHRcdFx0XHRbXG5cdFx0XHRcdFx0XCJjb25zdCBzdWIgPSBwcm9jZXNzLmVudi5BVVRIMF9BRE1JTl9VU0VSX1NVQjtcIixcblx0XHRcdFx0XHRcImlmICghc3ViKSByZXR1cm47XCIsXG5cdFx0XHRcdFx0XCJjb25zdCByZXBvID0gZGF0YVNvdXJjZS5nZXRSZXBvc2l0b3J5KFVzZXJFbnRpdHkpO1wiLFxuXHRcdFx0XHRcdFwiY29uc3QgZXhpc3RpbmcgPSBhd2FpdCByZXBvLmZpbmRPbmUoeyB3aGVyZTogeyBzdWIgfSB9KTtcIixcblx0XHRcdFx0XHRcImlmIChleGlzdGluZykgcmV0dXJuO1wiLFxuXHRcdFx0XHRcdFwiYXdhaXQgcmVwby5zYXZlKHJlcG8uY3JlYXRlKHtcIixcblx0XHRcdFx0XHRcIiAgc3ViLFwiLFxuXHRcdFx0XHRcdFwiICBlbWFpbDogcHJvY2Vzcy5lbnYuQVVUSDBfQURNSU5fVVNFUl9FTUFJTCA/PyBudWxsLFwiLFxuXHRcdFx0XHRcdCcgIGRpc3BsYXlOYW1lOiBwcm9jZXNzLmVudi5BVVRIMF9BRE1JTl9VU0VSTkFNRSA/PyBcImFkbWluXCIsJyxcblx0XHRcdFx0XHQnICByb2xlczogW1wiYWRtaW5cIl0sJyxcblx0XHRcdFx0XHRcIn0pKTtcIixcblx0XHRcdFx0XS5qb2luKFwiIFwiKSxcblx0XHRcdCk7XG5cblx0XHRwcm9qZWN0LmFkZEZpbGUoe1xuXHRcdFx0ZmlsZXM6ICgpID0+IFtcblx0XHRcdFx0eyBmaWxlUGF0aDogXCJzcmMvZW50aXRpZXMvdXNlci5lbnRpdHkudHNcIiwgY29udGVudDogVVNFUl9FTlRJVFlfVFMgfSxcblx0XHRcdFx0eyBmaWxlUGF0aDogXCJzcmMvcmVwb3NpdG9yaWVzL3VzZXIucmVwb3NpdG9yeS50c1wiLCBjb250ZW50OiBVU0VSX1JFUE9TSVRPUllfVFMgfSxcblx0XHRcdFx0eyBmaWxlUGF0aDogXCJzcmMvc2VydmljZXMvdXNlci5zZXJ2aWNlLnRzXCIsIGNvbnRlbnQ6IFVTRVJfU0VSVklDRV9UUyB9LFxuXHRcdFx0XHR7IGZpbGVQYXRoOiBcInNyYy9hdXRoL2FiaWxpdHkuZmFjdG9yeS50c1wiLCBjb250ZW50OiBBQklMSVRZX0ZBQ1RPUllfVFMgfSxcblx0XHRcdF0sXG5cdFx0fSk7XG5cdH1cblxuXHRmb3IgKGNvbnN0IGNvbnRyb2xsZXIgb2Ygd29ya3NwYWNlLmdldE1vZGVscyhDb250cm9sbGVyU291cmNlRmlsZSkpIHtcblx0XHRjb250cm9sbGVyXG5cdFx0XHQuYWRkSW1wb3J0KFwiVXNlclNlcnZpY2VcIiwgXCIuLi9zZXJ2aWNlcy91c2VyLnNlcnZpY2UuanNcIilcblx0XHRcdC5hZGRDb25zdHJ1Y3RvclBhcmFtKFwicHJpdmF0ZSByZWFkb25seSB1c2VyczogVXNlclNlcnZpY2VcIik7XG5cdFx0Y29udHJvbGxlci5nZXRNZXRob2QoXCJnZXRPbmVcIikuc2V0QXN5bmModHJ1ZSkuc2V0Qm9keShbXG5cdFx0XHRcImNvbnN0IHVzZXIgPSBhd2FpdCB0aGlzLnVzZXJzLmVuc3VyZUZyb21QcmluY2lwYWwodGhpcy5wcmluY2lwYWxQcm92aWRlci5jdXJyZW50UHJpbmNpcGFsKTtcIixcblx0XHRcdC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9UZW1wbGF0ZUN1cmx5SW5TdHJpbmc6IHRlbXBsYXRlIGxpdGVyYWwgaW4gZ2VuZXJhdGVkIGNvZGVcblx0XHRcdFwicmV0dXJuIHsgaWQsIG1lc3NhZ2U6IGBIZWxsbyAke3VzZXIuZGlzcGxheU5hbWV9IWAsIHVzZXJuYW1lOiB1c2VyLmRpc3BsYXlOYW1lIH07XCIsXG5cdFx0XSk7XG5cdH1cbn1cblxuZnVuY3Rpb24gc2NhZmZvbGQod29ya3NwYWNlOiBXb3Jrc3BhY2VTY2FmZm9sZCwgb3B0aW9uczogQXV0aE9wdGlvbnMpOiB2b2lkIHtcblx0aWYgKG9wdGlvbnMuYXV0aFByb3ZpZGVyICE9PSBcImF1dGgwXCIpIHJldHVybjtcblxuXHRjb25zdCByZXNvdXJjZSA9IFwiSGVsbG9cIjtcblx0Y29uc3Qgd2l0aExvY2FsVXNlcnMgPSBvcHRpb25zLmRhdGFiYXNlUHJvdmlkZXIgPT09IFwicG9zdGdyZXMvdHlwZW9ybVwiO1xuXG5cdGZvciAoY29uc3QgY29udHJvbGxlciBvZiB3b3Jrc3BhY2UuZ2V0TW9kZWxzKENvbnRyb2xsZXJTb3VyY2VGaWxlKSkge1xuXHRcdGNvbnRyb2xsZXJcblx0XHRcdC5hZGREZWNvcmF0b3IoYEBBdXRob3JpemUoXCJyZWFkXCIsIFwiJHtyZXNvdXJjZX1cIilgKVxuXHRcdFx0LmFkZENvcnRleEltcG9ydChcIkFsbG93QW5vbnltb3VzXCIpXG5cdFx0XHQuYWRkQ29ydGV4SW1wb3J0KFwiQXV0aG9yaXplXCIpXG5cdFx0XHQuYWRkQ29ydGV4SW1wb3J0KFwiSVByaW5jaXBhbFByb3ZpZGVyVG9rZW5cIilcblx0XHRcdC5hZGRDb3J0ZXhJbXBvcnQoXCJJUHJpbmNpcGFsUHJvdmlkZXJcIiwgdHJ1ZSlcblx0XHRcdC5hZGRJbXBvcnQoXCJpbmplY3RcIiwgXCJAZGVjb2hlcmUvY3J1Y2libGUvZGVjb3JhdG9yc1wiKVxuXHRcdFx0LmFkZENvbnN0cnVjdG9yUGFyYW0oXG5cdFx0XHRcdGBAaW5qZWN0KElQcmluY2lwYWxQcm92aWRlclRva2VuKSBwcml2YXRlIHJlYWRvbmx5IHByaW5jaXBhbFByb3ZpZGVyOiBJUHJpbmNpcGFsUHJvdmlkZXJgLFxuXHRcdFx0KTtcblx0XHRjb250cm9sbGVyLmdldE1ldGhvZChcImdldEFsbFwiKS5hZGREZWNvcmF0b3IoXCJAQWxsb3dBbm9ueW1vdXMoKVwiKTtcblx0XHRpZiAoIXdpdGhMb2NhbFVzZXJzKSB7XG5cdFx0XHRjb250cm9sbGVyLmdldE1ldGhvZChcImdldE9uZVwiKS5zZXRCb2R5KFtcblx0XHRcdFx0XCJjb25zdCBwcmluY2lwYWwgPSB0aGlzLnByaW5jaXBhbFByb3ZpZGVyLmN1cnJlbnRQcmluY2lwYWw7XCIsXG5cdFx0XHRcdFwiY29uc3QgdXNlcm5hbWUgPVwiLFxuXHRcdFx0XHQnXFx0KHR5cGVvZiBwcmluY2lwYWw/Lm5hbWUgPT09IFwic3RyaW5nXCIgJiYgcHJpbmNpcGFsLm5hbWUpIHx8Jyxcblx0XHRcdFx0J1xcdCh0eXBlb2YgcHJpbmNpcGFsPy5uaWNrbmFtZSA9PT0gXCJzdHJpbmdcIiAmJiBwcmluY2lwYWwubmlja25hbWUpIHx8Jyxcblx0XHRcdFx0J1xcdCh0eXBlb2YgcHJpbmNpcGFsPy5lbWFpbCA9PT0gXCJzdHJpbmdcIiAmJiBwcmluY2lwYWwuZW1haWwpIHx8Jyxcblx0XHRcdFx0J1xcdCh0eXBlb2YgcHJpbmNpcGFsPy5wcmVmZXJyZWRfdXNlcm5hbWUgPT09IFwic3RyaW5nXCIgJiYgcHJpbmNpcGFsLnByZWZlcnJlZF91c2VybmFtZSkgfHwnLFxuXHRcdFx0XHRcIlxcdHByaW5jaXBhbD8uc3ViIHx8XCIsXG5cdFx0XHRcdCdcXHRcInVua25vd25cIjsnLFxuXHRcdFx0XHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vVGVtcGxhdGVDdXJseUluU3RyaW5nOiB0ZW1wbGF0ZSBsaXRlcmFsIGluIGdlbmVyYXRlZCBjb2RlXG5cdFx0XHRcdFwicmV0dXJuIHsgaWQsIG1lc3NhZ2U6IGBIZWxsbyAke3VzZXJuYW1lfSFgLCB1c2VybmFtZSB9O1wiLFxuXHRcdFx0XSk7XG5cdFx0fVxuXHRcdGNvbnRyb2xsZXIuZ2V0TWV0aG9kKFwiY3JlYXRlXCIpLmFkZERlY29yYXRvcihgQEF1dGhvcml6ZShcImNyZWF0ZVwiLCBcIiR7cmVzb3VyY2V9XCIpYCk7XG5cdFx0Y29udHJvbGxlci5nZXRNZXRob2QoXCJ1cGRhdGVcIikuYWRkRGVjb3JhdG9yKGBAQXV0aG9yaXplKFwidXBkYXRlXCIsIFwiJHtyZXNvdXJjZX1cIilgKTtcblx0XHRjb250cm9sbGVyLmdldE1ldGhvZChcInJlbW92ZVwiKS5hZGREZWNvcmF0b3IoYEBBdXRob3JpemUoXCJkZWxldGVcIiwgXCIke3Jlc291cmNlfVwiKWApO1xuXHR9XG5cblx0Zm9yIChjb25zdCBwcm9qZWN0IG9mIHdvcmtzcGFjZS5nZXRQcm9qZWN0cygpKSB7XG5cdFx0cHJvamVjdFxuXHRcdFx0LmdldE1vZGVsPE1haW5Tb3VyY2VGaWxlPihXRUxMX0tOT1dOX0ZJTEVTLk1BSU5fVFMpXG5cdFx0XHQ/LmFkZFNpZGVFZmZlY3RJbXBvcnQoXCJAZGVjb2hlcmUvY29ydGV4L2F1dGhcIik7XG5cdH1cblxuXHRpZiAod2l0aExvY2FsVXNlcnMpIHtcblx0XHRzY2FmZm9sZExvY2FsVXNlclByb2ZpbGUod29ya3NwYWNlKTtcblx0XHRmb3IgKGNvbnN0IHByb2plY3Qgb2Ygd29ya3NwYWNlLmdldFByb2plY3RzKCkpIHtcblx0XHRcdHByb2plY3Rcblx0XHRcdFx0LmdldE1vZGVsPE1haW5Tb3VyY2VGaWxlPihXRUxMX0tOT1dOX0ZJTEVTLk1BSU5fVFMpXG5cdFx0XHRcdD8uYWRkQnVpbGRlckNhbGwoXCIud2l0aEF1dGgoY3JlYXRlQWJpbGl0eUZhY3RvcnksIHsgcHJpbmNpcGFsTWFwcGVyOiBtYXBQcmluY2lwYWwgfSlcIik7XG5cdFx0fVxuXHR9IGVsc2Uge1xuXHRcdGZvciAoY29uc3QgcHJvamVjdCBvZiB3b3Jrc3BhY2UuZ2V0UHJvamVjdHMoKSkge1xuXHRcdFx0cHJvamVjdFxuXHRcdFx0XHQuZ2V0TW9kZWw8TWFpblNvdXJjZUZpbGU+KFdFTExfS05PV05fRklMRVMuTUFJTl9UUylcblx0XHRcdFx0Py5hZGRCdWlsZGVyQ2FsbChcIi53aXRoQXV0aCgpXCIpO1xuXHRcdH1cblx0fVxuXG5cdC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9UZW1wbGF0ZUN1cmx5SW5TdHJpbmc6IGludGVudGlvbmFsIFlBTUwgZW52LXZhciBwbGFjZWhvbGRlcnNcblx0Y29uc3QgaXNzdWVyUGxhY2Vob2xkZXIgPSBcIiR7QVVUSDBfSVNTVUVSX0JBU0VfVVJMfVwiO1xuXHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vVGVtcGxhdGVDdXJseUluU3RyaW5nOiBpbnRlbnRpb25hbCBZQU1MIGVudi12YXIgcGxhY2Vob2xkZXJzXG5cdGNvbnN0IGF1ZFBsYWNlaG9sZGVyID0gXCIke0FVVEgwX0FVRElFTkNFfVwiO1xuXG5cdGNvbnN0IGNvbmZpZ1NlY3Rpb24gPSBgYXV0aDpcXG4gIGRpc2NvdmVyeVVybDogJHtpc3N1ZXJQbGFjZWhvbGRlcn1cXG4gIGF1ZGllbmNlOiAke2F1ZFBsYWNlaG9sZGVyfVxcbmA7XG5cblx0d29ya3NwYWNlLmdldE1vZGVsPENvbmZpZ1lhbWw+KFdFTExfS05PV05fRklMRVMuQ09ORklHX1lBTUwpPy5hZGRTZWN0aW9uKGNvbmZpZ1NlY3Rpb24pO1xuXG5cdHdvcmtzcGFjZVxuXHRcdC5nZXRNb2RlbDxFbnZGaWxlPihXRUxMX0tOT1dOX0ZJTEVTLkVOVl9GSUxFKVxuXHRcdD8uYWRkRW50cnkoXCJBVVRIMF9JU1NVRVJfQkFTRV9VUkxcIiwgb3B0aW9ucy5pc3N1ZXJCYXNlVXJsID8/IFwiXCIpXG5cdFx0LmFkZEVudHJ5KFwiQVVUSDBfQVVESUVOQ0VcIiwgb3B0aW9ucy5hdWRpZW5jZSA/PyBcIlwiKTtcblxuXHRjb25zdCBhdXRoQ29uZmlnID0gbmV3IEF1dGhDb25maWdNb2RlbCh7XG5cdFx0aXNzdWVyQmFzZVVybDogb3B0aW9ucy5pc3N1ZXJCYXNlVXJsID8/IFwiXCIsXG5cdFx0YXVkaWVuY2U6IG9wdGlvbnMuYXVkaWVuY2UgPz8gXCJcIixcblx0fSk7XG5cdHdvcmtzcGFjZS5hZGRNb2RlbChhdXRoQ29uZmlnKTtcblxuXHRpZiAod2l0aExvY2FsVXNlcnMgJiYgb3B0aW9ucy5hZG1pblBhc3N3b3JkKSB7XG5cdFx0Y3JlYXRlQXV0aDBBZG1pblVzZXIod29ya3NwYWNlLCBvcHRpb25zLCBhdXRoQ29uZmlnKTtcblx0fVxufVxuXG5leHBvcnQgY29uc3QgQXV0aEZlYXR1cmU6IEZlYXR1cmU8QXV0aE9wdGlvbnM+ID0geyBnZXRPcHRpb25zLCBzY2FmZm9sZCB9O1xuIl0sIm5hbWVzIjpbImFzayIsInBpY2siLCJXRUxMX0tOT1dOX0ZJTEVTIiwiQXV0aENvbmZpZ01vZGVsIiwiQ29udHJvbGxlclNvdXJjZUZpbGUiLCJhdXRoMENsaSIsIkFETUlOX1JPTEUiLCJVU0VSX0VOVElUWV9UUyIsIlVTRVJfUkVQT1NJVE9SWV9UUyIsIlVTRVJfU0VSVklDRV9UUyIsIkFCSUxJVFlfRkFDVE9SWV9UUyIsImdldE9wdGlvbnMiLCJ3b3Jrc3BhY2UiLCJjbGlPcHRpb25zIiwiYXV0aFByb3ZpZGVyIiwiaXNzdWVyQmFzZVVybCIsImdldEF1dGgwT3B0aW9ucyIsImNob2ljZXMiLCJsYWJlbCIsInNlbGVjdGVkIiwiYyIsInByb3ZpZGVyIiwiaXNJbnN0YWxsZWQiLCJjb25zb2xlIiwiZXJyb3IiLCJwcm9jZXNzIiwiZXhpdCIsInN0ZG91dCIsIndyaXRlIiwidGVuYW50cyIsImxpc3RUZW5hbnRzIiwibGVuZ3RoIiwibG9nIiwibG9naW4iLCJsaXN0QXBpcyIsImVyciIsIkF1dGgwQXV0aEVycm9yIiwiYWN0aXZlIiwiZmluZCIsInQiLCJuYW1lIiwidXNlckFwaXMiLCJmaWx0ZXIiLCJhIiwiaW5jbHVkZXMiLCJwcm9qZWN0TmFtZSIsImFwaUNob2ljZXMiLCJleGlzdGluZyIsIm1hcCIsImlkZW50aWZpZXIiLCJhcGlTZWxlY3RlZCIsImF1ZGllbmNlIiwiZGVmYXVsdElkIiwiY3JlYXRlQXBpIiwidXBkYXRlQXBpU2NvcGVzIiwiSEVMTE9fQVBJX1NDT1BFUyIsImpvaW4iLCJ3aXRoTG9jYWxVc2VycyIsImRhdGFiYXNlUHJvdmlkZXIiLCJhZG1pblVzZXJuYW1lIiwiYWRtaW5QYXNzd29yZCIsImdlbmVyYXRlQWRtaW5QYXNzd29yZCIsImFkbWluRW1haWwiLCJ3b3Jrc3BhY2VOYW1lIiwidXNlcm5hbWUiLCJzbHVnIiwidG9Mb3dlckNhc2UiLCJyZXBsYWNlIiwiY3JlYXRlQXV0aDBBZG1pblVzZXIiLCJvcHRpb25zIiwiYXV0aENvbmZpZyIsInBhc3N3b3JkIiwiZW1haWwiLCJhZG1pbiIsImNyZWF0ZUFkbWluRGF0YWJhc2VVc2VyIiwic2V0QWRtaW5Vc2VyIiwic3ViIiwidXNlcl9pZCIsImdldE1vZGVsIiwiRU5WX0ZJTEUiLCJhZGRFbnRyeSIsIkVycm9yIiwibWVzc2FnZSIsInNjYWZmb2xkTG9jYWxVc2VyUHJvZmlsZSIsIlBBQ0tBR0VfSlNPTiIsImFkZERlcGVuZGVuY3kiLCJwcm9qZWN0IiwiZ2V0UHJvamVjdHMiLCJBUElfUEFDS0FHRV9KU09OIiwiTUFJTl9UUyIsImFkZEVudGl0eSIsImNsYXNzTmFtZSIsImltcG9ydFBhdGgiLCJhZGRSZXBvc2l0b3J5IiwiYWRkU2VydmljZSIsInNjb3BlIiwiYWRkSW1wb3J0Iiwic2V0VHlwZU9ybVNlZWQiLCJhZGRGaWxlIiwiZmlsZXMiLCJmaWxlUGF0aCIsImNvbnRlbnQiLCJjb250cm9sbGVyIiwiZ2V0TW9kZWxzIiwiYWRkQ29uc3RydWN0b3JQYXJhbSIsImdldE1ldGhvZCIsInNldEFzeW5jIiwic2V0Qm9keSIsInNjYWZmb2xkIiwicmVzb3VyY2UiLCJhZGREZWNvcmF0b3IiLCJhZGRDb3J0ZXhJbXBvcnQiLCJhZGRTaWRlRWZmZWN0SW1wb3J0IiwiYWRkQnVpbGRlckNhbGwiLCJpc3N1ZXJQbGFjZWhvbGRlciIsImF1ZFBsYWNlaG9sZGVyIiwiY29uZmlnU2VjdGlvbiIsIkNPTkZJR19ZQU1MIiwiYWRkU2VjdGlvbiIsImFkZE1vZGVsIiwiQXV0aEZlYXR1cmUiXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLEdBQUcsRUFBRUMsSUFBSSxRQUFRLHlCQUF5QjtBQUVuRCxTQUFTQyxnQkFBZ0IsUUFBUSx3QkFBd0I7QUFNekQsU0FBU0MsZUFBZSxRQUFRLGlDQUFpQztBQUNqRSxTQUFTQyxvQkFBb0IsUUFBUSw0Q0FBNEM7QUFFakYsWUFBWUMsY0FBYyxpQkFBaUI7QUFXM0MsTUFBTUMsYUFBYTtBQUVuQixNQUFNQyxpQkFBaUIsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBcUJ4QixDQUFDO0FBRUQsTUFBTUMscUJBQXFCLENBQUM7Ozs7Ozs7Ozs7Ozs7OztBQWU1QixDQUFDO0FBRUQsTUFBTUMsa0JBQWtCLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF1RHpCLENBQUM7QUFFRCxNQUFNQyxxQkFBcUIsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF3RDVCLENBQUM7QUFFRCxlQUFlQyxXQUNkQyxTQUE0QixFQUM1QkMsVUFBZ0M7SUFFaEMsSUFBSUEsV0FBV0MsWUFBWSxLQUFLLFdBQVcsQ0FBQ0QsV0FBV0UsYUFBYSxFQUFFO1FBQ3JFLE9BQU9DLGdCQUFnQkosV0FBV0M7SUFDbkM7SUFDQSxJQUFJQSxXQUFXQyxZQUFZLElBQUksTUFBTSxPQUFPLENBQUM7SUFFN0MsTUFBTUcsVUFBVTtRQUFDO1lBQUVDLE9BQU87UUFBZ0I7UUFBRztZQUFFQSxPQUFPO1FBQWlCO0tBQUU7SUFDekUsTUFBTUMsV0FBVyxNQUFNbEIsS0FBSyx1QkFBdUJnQixTQUFTLENBQUNHLElBQU1BLEVB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
|