@decohere/create 0.0.1-dev.48 → 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.
Files changed (124) hide show
  1. package/commonjs/src/cli.js +3 -3
  2. package/commonjs/src/resolve-launch.js +53 -0
  3. package/commonjs/src/runner.js +5 -45
  4. package/commonjs/src/version.js +1 -1
  5. package/es6/src/cli.js +3 -3
  6. package/es6/src/resolve-launch.js +46 -0
  7. package/es6/src/runner.js +8 -57
  8. package/es6/src/version.js +3 -3
  9. package/pack-builder-tgz.mjs +1 -1
  10. package/package.json +4 -11
  11. package/types/src/resolve-launch.d.ts +10 -0
  12. package/types/src/runner.d.ts +3 -13
  13. package/types/src/version.d.ts +0 -2
  14. package/commonjs/src/domain/prompt.js +0 -64
  15. package/commonjs/src/models/compose.model.js +0 -138
  16. package/commonjs/src/models/config-yaml.model.js +0 -88
  17. package/commonjs/src/models/const.js +0 -31
  18. package/commonjs/src/models/dev-container.model.js +0 -100
  19. package/commonjs/src/models/docker-image.model.js +0 -221
  20. package/commonjs/src/models/env-file.model.js +0 -47
  21. package/commonjs/src/models/gitignore.model.js +0 -31
  22. package/commonjs/src/models/launch-json.model.js +0 -74
  23. package/commonjs/src/models/package-json.model.js +0 -133
  24. package/commonjs/src/models/project.model.js +0 -134
  25. package/commonjs/src/models/source-file.js +0 -6
  26. package/commonjs/src/models/tsconfig.model.js +0 -34
  27. package/commonjs/src/models/vscode-settings.model.js +0 -31
  28. package/commonjs/src/program.js +0 -135
  29. package/commonjs/src/resolve-target-dir.js +0 -19
  30. package/commonjs/src/services/scaffold.service.js +0 -62
  31. package/commonjs/src/webapi/api-name.js +0 -95
  32. package/commonjs/src/webapi/const.js +0 -50
  33. package/commonjs/src/webapi/create-api.js +0 -285
  34. package/commonjs/src/webapi/features/auth.feature.js +0 -463
  35. package/commonjs/src/webapi/features/auth0-cli.js +0 -406
  36. package/commonjs/src/webapi/features/biome-format.js +0 -49
  37. package/commonjs/src/webapi/features/database.feature.js +0 -273
  38. package/commonjs/src/webapi/features/finalize.feature.js +0 -91
  39. package/commonjs/src/webapi/features/postman.feature.js +0 -319
  40. package/commonjs/src/webapi/models/api-package-json.model.js +0 -63
  41. package/commonjs/src/webapi/models/auth-config.model.js +0 -26
  42. package/commonjs/src/webapi/models/controller-source-file.model.js +0 -188
  43. package/commonjs/src/webapi/models/github-ci.model.js +0 -193
  44. package/commonjs/src/webapi/models/main-source-file.model.js +0 -165
  45. package/commonjs/src/webapi/models/production-dockerfile.model.js +0 -138
  46. package/commonjs/src/webapi/models/sample-files.model.js +0 -60
  47. package/commonjs/src/webapi/models/webapi-image.model.js +0 -22
  48. package/commonjs/src/webapi/postman/hello-openapi.js +0 -175
  49. package/commonjs/src/webapi/postman/local-view.js +0 -184
  50. package/commonjs/src/webapi/postman/openapi-to-postman.js +0 -202
  51. package/es6/src/domain/prompt.js +0 -43
  52. package/es6/src/models/compose.model.js +0 -132
  53. package/es6/src/models/config-yaml.model.js +0 -76
  54. package/es6/src/models/const.js +0 -21
  55. package/es6/src/models/dev-container.model.js +0 -90
  56. package/es6/src/models/docker-image.model.js +0 -185
  57. package/es6/src/models/env-file.model.js +0 -43
  58. package/es6/src/models/gitignore.model.js +0 -21
  59. package/es6/src/models/launch-json.model.js +0 -64
  60. package/es6/src/models/package-json.model.js +0 -129
  61. package/es6/src/models/project.model.js +0 -119
  62. package/es6/src/models/source-file.js +0 -3
  63. package/es6/src/models/tsconfig.model.js +0 -24
  64. package/es6/src/models/vscode-settings.model.js +0 -21
  65. package/es6/src/program.js +0 -76
  66. package/es6/src/resolve-target-dir.js +0 -9
  67. package/es6/src/services/scaffold.service.js +0 -52
  68. package/es6/src/webapi/api-name.js +0 -59
  69. package/es6/src/webapi/const.js +0 -20
  70. package/es6/src/webapi/create-api.js +0 -265
  71. package/es6/src/webapi/features/auth.feature.js +0 -412
  72. package/es6/src/webapi/features/auth0-cli.js +0 -348
  73. package/es6/src/webapi/features/biome-format.js +0 -42
  74. package/es6/src/webapi/features/database.feature.js +0 -263
  75. package/es6/src/webapi/features/finalize.feature.js +0 -81
  76. package/es6/src/webapi/features/postman.feature.js +0 -253
  77. package/es6/src/webapi/models/api-package-json.model.js +0 -58
  78. package/es6/src/webapi/models/auth-config.model.js +0 -22
  79. package/es6/src/webapi/models/controller-source-file.model.js +0 -179
  80. package/es6/src/webapi/models/github-ci.model.js +0 -186
  81. package/es6/src/webapi/models/main-source-file.model.js +0 -161
  82. package/es6/src/webapi/models/production-dockerfile.model.js +0 -126
  83. package/es6/src/webapi/models/sample-files.model.js +0 -42
  84. package/es6/src/webapi/models/webapi-image.model.js +0 -12
  85. package/es6/src/webapi/postman/hello-openapi.js +0 -168
  86. package/es6/src/webapi/postman/local-view.js +0 -169
  87. package/es6/src/webapi/postman/openapi-to-postman.js +0 -179
  88. package/types/src/domain/prompt.d.ts +0 -4
  89. package/types/src/models/compose.model.d.ts +0 -59
  90. package/types/src/models/config-yaml.model.d.ts +0 -27
  91. package/types/src/models/const.d.ts +0 -19
  92. package/types/src/models/dev-container.model.d.ts +0 -33
  93. package/types/src/models/docker-image.model.d.ts +0 -56
  94. package/types/src/models/env-file.model.d.ts +0 -15
  95. package/types/src/models/gitignore.model.d.ts +0 -6
  96. package/types/src/models/launch-json.model.d.ts +0 -7
  97. package/types/src/models/package-json.model.d.ts +0 -32
  98. package/types/src/models/project.model.d.ts +0 -60
  99. package/types/src/models/source-file.d.ts +0 -11
  100. package/types/src/models/tsconfig.model.d.ts +0 -5
  101. package/types/src/models/vscode-settings.model.d.ts +0 -7
  102. package/types/src/program.d.ts +0 -3
  103. package/types/src/resolve-target-dir.d.ts +0 -5
  104. package/types/src/services/scaffold.service.d.ts +0 -22
  105. package/types/src/webapi/api-name.d.ts +0 -14
  106. package/types/src/webapi/const.d.ts +0 -17
  107. package/types/src/webapi/create-api.d.ts +0 -15
  108. package/types/src/webapi/features/auth.feature.d.ts +0 -10
  109. package/types/src/webapi/features/auth0-cli.d.ts +0 -72
  110. package/types/src/webapi/features/biome-format.d.ts +0 -5
  111. package/types/src/webapi/features/database.feature.d.ts +0 -5
  112. package/types/src/webapi/features/finalize.feature.d.ts +0 -2
  113. package/types/src/webapi/features/postman.feature.d.ts +0 -16
  114. package/types/src/webapi/models/api-package-json.model.d.ts +0 -18
  115. package/types/src/webapi/models/auth-config.model.d.ts +0 -26
  116. package/types/src/webapi/models/controller-source-file.model.d.ts +0 -45
  117. package/types/src/webapi/models/github-ci.model.d.ts +0 -9
  118. package/types/src/webapi/models/main-source-file.model.d.ts +0 -54
  119. package/types/src/webapi/models/production-dockerfile.model.d.ts +0 -20
  120. package/types/src/webapi/models/sample-files.model.d.ts +0 -7
  121. package/types/src/webapi/models/webapi-image.model.d.ts +0 -5
  122. package/types/src/webapi/postman/hello-openapi.d.ts +0 -19
  123. package/types/src/webapi/postman/local-view.d.ts +0 -17
  124. 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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