velocious 1.0.481 → 1.0.483

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 (38) hide show
  1. package/README.md +2 -0
  2. package/build/cli/commands/db/tenants/drop.js +34 -0
  3. package/build/cli/tenant-database-command-helper.js +8 -85
  4. package/build/database/tenants/data-copier.js +269 -0
  5. package/build/database/tenants/schema-cloner.js +390 -0
  6. package/build/database/tenants/tenant-table-plan.js +20 -0
  7. package/build/src/cli/commands/db/tenants/drop.d.ts +13 -0
  8. package/build/src/cli/commands/db/tenants/drop.d.ts.map +1 -0
  9. package/build/src/cli/commands/db/tenants/drop.js +31 -0
  10. package/build/src/cli/tenant-database-command-helper.d.ts +0 -20
  11. package/build/src/cli/tenant-database-command-helper.d.ts.map +1 -1
  12. package/build/src/cli/tenant-database-command-helper.js +8 -73
  13. package/build/src/database/tenants/data-copier.d.ts +125 -0
  14. package/build/src/database/tenants/data-copier.d.ts.map +1 -0
  15. package/build/src/database/tenants/data-copier.js +232 -0
  16. package/build/src/database/tenants/schema-cloner.d.ts +147 -0
  17. package/build/src/database/tenants/schema-cloner.d.ts.map +1 -0
  18. package/build/src/database/tenants/schema-cloner.js +326 -0
  19. package/build/src/database/tenants/tenant-table-plan.d.ts +30 -0
  20. package/build/src/database/tenants/tenant-table-plan.d.ts.map +1 -0
  21. package/build/src/database/tenants/tenant-table-plan.js +3 -0
  22. package/build/src/tenants/tenant-iterator.d.ts +52 -0
  23. package/build/src/tenants/tenant-iterator.d.ts.map +1 -0
  24. package/build/src/tenants/tenant-iterator.js +97 -0
  25. package/build/src/tenants/tenant.d.ts +51 -0
  26. package/build/src/tenants/tenant.d.ts.map +1 -0
  27. package/build/src/tenants/tenant.js +76 -0
  28. package/build/tenants/tenant-iterator.js +111 -0
  29. package/build/tenants/tenant.js +86 -0
  30. package/build/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +1 -1
  32. package/src/cli/commands/db/tenants/drop.js +34 -0
  33. package/src/cli/tenant-database-command-helper.js +8 -85
  34. package/src/database/tenants/data-copier.js +269 -0
  35. package/src/database/tenants/schema-cloner.js +390 -0
  36. package/src/database/tenants/tenant-table-plan.js +20 -0
  37. package/src/tenants/tenant-iterator.js +111 -0
  38. package/src/tenants/tenant.js +86 -0
@@ -0,0 +1,390 @@
1
+ // @ts-check
2
+
3
+ import MigrationsLedger from "../migrations-ledger.js"
4
+ import TableData from "../table-data/index.js"
5
+ import TableIndex from "../table-data/table-index.js"
6
+
7
+ const SIMPLE_DEFAULT_PATTERN = /^(?:-?\d+(?:\.\d+)?|[A-Za-z0-9 _.,:/@+-]*)$/
8
+
9
+ /** @type {Record<string, number>} */
10
+ const TEXT_TYPE_RANKS = {
11
+ tinytext: 1,
12
+ text: 2,
13
+ mediumtext: 3,
14
+ longtext: 4
15
+ }
16
+
17
+ /**
18
+ * Clones table structure (columns, text-type widening, indexes) from a source database
19
+ * into a target database for a given set of tables, then baselines the target's
20
+ * `schema_migrations` ledger to the source via {@link MigrationsLedger}. This is the
21
+ * mechanism multi-tenant apps use to provision a tenant database from a template/global
22
+ * database without re-running migrations: the structure is copied and the ledger is
23
+ * recorded as already-applied, so a later `db:tenants:migrate` does not re-run an
24
+ * `addColumn` whose column already exists.
25
+ *
26
+ * The cloner is intentionally policy-free — the caller decides which tables to sync and
27
+ * which databases are source/target. It is idempotent: missing tables are created,
28
+ * missing columns added, too-narrow text columns widened, and missing indexes created;
29
+ * an index whose definition diverges from the source is treated as drift and throws.
30
+ */
31
+ export default class SchemaCloner {
32
+ /**
33
+ * Creates a cloner that copies table structure from `sourceDb` into `targetDb`.
34
+ * @param {{sourceDb: import("../drivers/base.js").default, targetDb: import("../drivers/base.js").default}} args
35
+ */
36
+ constructor({sourceDb, targetDb}) {
37
+ this.sourceDb = sourceDb
38
+ this.targetDb = targetDb
39
+ }
40
+
41
+ /**
42
+ * Clones every given table from the source into the target, then baselines the
43
+ * target's ledger so the cloned schema is recorded as already-migrated.
44
+ * @param {string[]} tableNames
45
+ * @returns {Promise<void>}
46
+ */
47
+ async syncTables(tableNames) {
48
+ for (const tableName of tableNames) {
49
+ await this.syncTable(tableName)
50
+ }
51
+
52
+ await this.reconcileLedger()
53
+ }
54
+
55
+ /**
56
+ * Clones a single table from the source into the target, creating it or adding and
57
+ * widening columns and indexes as needed.
58
+ * @param {string} tableName
59
+ * @returns {Promise<void>}
60
+ */
61
+ async syncTable(tableName) {
62
+ const sourceTable = await this.sourceDb.getTableByName(tableName)
63
+
64
+ if (!sourceTable) {
65
+ throw new Error(`Expected source table to exist: ${tableName}`)
66
+ }
67
+
68
+ if (!await this.targetDb.tableExists(tableName)) {
69
+ await this.createTargetTable({sourceTable, tableName})
70
+ return
71
+ }
72
+
73
+ const changedColumns = await this.ensureTargetColumns({sourceTable, tableName})
74
+
75
+ if (changedColumns) {
76
+ this.targetDb.clearSchemaCache()
77
+ }
78
+
79
+ await this.ensureTargetIndexes({sourceTable, tableName})
80
+ }
81
+
82
+ /**
83
+ * Creates the table in the target from the source table's columns and its
84
+ * non-primary-key indexes.
85
+ * @param {{sourceTable: import("../drivers/base-table.js").default, tableName: string}} args
86
+ * @returns {Promise<void>}
87
+ */
88
+ async createTargetTable({sourceTable, tableName}) {
89
+ const tableData = new TableData(tableName)
90
+
91
+ for (const sourceColumn of await sourceTable.getColumns()) {
92
+ tableData.addColumn(sourceColumn.getName(), this.columnArgsFromSourceColumn(sourceColumn, {isNewColumn: false}))
93
+ }
94
+
95
+ for (const sourceIndex of await sourceTable.getIndexes()) {
96
+ if (!sourceIndex.isPrimaryKey()) {
97
+ tableData.addIndex(this.tableDataIndexFromSourceIndex(sourceIndex))
98
+ }
99
+ }
100
+
101
+ await this.targetDb.createTable(tableData)
102
+ this.targetDb.clearSchemaCache()
103
+ }
104
+
105
+ /**
106
+ * Adds columns present on the source but missing from the target, and widens
107
+ * too-narrow target text columns.
108
+ * @param {{sourceTable: import("../drivers/base-table.js").default, tableName: string}} args
109
+ * @returns {Promise<boolean>} Whether any column was added or widened.
110
+ */
111
+ async ensureTargetColumns({sourceTable, tableName}) {
112
+ const sourceColumns = await sourceTable.getColumns()
113
+ const targetTable = await this.targetDb.getTableByNameOrFail(tableName)
114
+ const targetColumnsByName = new Map()
115
+ const missingColumns = []
116
+ const columnsNeedingWidening = []
117
+
118
+ for (const targetColumn of await targetTable.getColumns()) {
119
+ targetColumnsByName.set(targetColumn.getName(), targetColumn)
120
+ }
121
+
122
+ for (const sourceColumn of sourceColumns) {
123
+ const targetColumn = targetColumnsByName.get(sourceColumn.getName())
124
+
125
+ if (!targetColumn) {
126
+ missingColumns.push(sourceColumn)
127
+ } else if (this.columnNeedsWidening(sourceColumn, targetColumn)) {
128
+ columnsNeedingWidening.push(sourceColumn)
129
+ }
130
+ }
131
+
132
+ if (missingColumns.length <= 0 && columnsNeedingWidening.length <= 0) {
133
+ return false
134
+ }
135
+
136
+ const tableData = new TableData(tableName)
137
+
138
+ for (const sourceColumn of missingColumns) {
139
+ tableData.addColumn(sourceColumn.getName(), this.columnArgsFromSourceColumn(sourceColumn, {isNewColumn: true}))
140
+ }
141
+
142
+ for (const sourceColumn of columnsNeedingWidening) {
143
+ tableData.addColumn(sourceColumn.getName(), this.columnArgsFromSourceColumn(sourceColumn, {isNewColumn: false}))
144
+ }
145
+
146
+ for (const alterSql of await this.targetDb.alterTableSQLs(tableData)) {
147
+ await this.targetDb.query(alterSql)
148
+ }
149
+
150
+ return true
151
+ }
152
+
153
+ /**
154
+ * Creates non-primary-key indexes present on the source but missing from the target,
155
+ * and throws if an existing target index diverges from the source.
156
+ * @param {{sourceTable: import("../drivers/base-table.js").default, tableName: string}} args
157
+ * @returns {Promise<void>}
158
+ */
159
+ async ensureTargetIndexes({sourceTable, tableName}) {
160
+ const targetTable = await this.targetDb.getTableByNameOrFail(tableName)
161
+ /** @type {Map<string, import("../drivers/base-columns-index.js").default>} */
162
+ const targetIndexesByName = new Map()
163
+ const targetIndexSignatures = new Set()
164
+ let createdIndex = false
165
+
166
+ for (const targetIndex of await targetTable.getIndexes()) {
167
+ targetIndexesByName.set(targetIndex.getName(), targetIndex)
168
+ targetIndexSignatures.add(this.indexSignature(targetIndex))
169
+ }
170
+
171
+ for (const sourceIndex of await sourceTable.getIndexes()) {
172
+ if (sourceIndex.isPrimaryKey()) {
173
+ continue
174
+ }
175
+
176
+ const sourceIndexSignature = this.indexSignature(sourceIndex)
177
+
178
+ // SQLite index names are unique per-database, not per-table, so match cloned
179
+ // indexes by their column/uniqueness signature rather than their name.
180
+ if (this.targetDb.getType() === "sqlite" && targetIndexSignatures.has(sourceIndexSignature)) {
181
+ continue
182
+ }
183
+
184
+ const targetIndex = this.targetDb.getType() === "sqlite" ? undefined : targetIndexesByName.get(sourceIndex.getName())
185
+
186
+ if (!targetIndex) {
187
+ const createIndexSqls = await this.targetDb.createIndexSQLs(this.createIndexArgsFromSourceIndex({sourceIndex, tableName}))
188
+
189
+ for (const createIndexSql of createIndexSqls) {
190
+ await this.targetDb.query(createIndexSql)
191
+ }
192
+
193
+ createdIndex = true
194
+ targetIndexSignatures.add(sourceIndexSignature)
195
+ continue
196
+ }
197
+
198
+ if (!this.indexesMatch(sourceIndex, targetIndex)) {
199
+ throw new Error(`Schema clone index drift for ${tableName}.${sourceIndex.getName()}`)
200
+ }
201
+ }
202
+
203
+ if (createdIndex) {
204
+ this.targetDb.clearSchemaCache()
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Baselines the target ledger so the cloned schema is recorded as already-applied.
210
+ * @returns {Promise<string[]>} The versions newly recorded on the target.
211
+ */
212
+ async reconcileLedger() {
213
+ return await MigrationsLedger.baselineFromDatabase({sourceDb: this.sourceDb, targetDb: this.targetDb})
214
+ }
215
+
216
+ /**
217
+ * Whether the target ledger is missing any version applied on the source — i.e. the
218
+ * target schema may have been advanced out of band without recording it.
219
+ * @returns {Promise<boolean>}
220
+ */
221
+ async ledgerDriftsFromSource() {
222
+ if (!await MigrationsLedger.tableExists(this.targetDb)) {
223
+ return true
224
+ }
225
+
226
+ const sourceVersions = await MigrationsLedger.appliedVersions(this.sourceDb)
227
+ const targetVersionSet = new Set(await MigrationsLedger.appliedVersions(this.targetDb))
228
+
229
+ return sourceVersions.some((version) => !targetVersionSet.has(version))
230
+ }
231
+
232
+ /**
233
+ * Maps a source index into a TableData index for table creation (SQLite omits the
234
+ * index name so the driver can generate a unique one).
235
+ * @param {import("../drivers/base-columns-index.js").default} sourceIndex
236
+ * @returns {TableIndex}
237
+ */
238
+ tableDataIndexFromSourceIndex(sourceIndex) {
239
+ /** @type {{name?: string, unique: boolean}} */
240
+ const args = {unique: sourceIndex.isUnique()}
241
+
242
+ // SQLite index names are unique per-database, not per-table, so let the driver
243
+ // generate one; other drivers preserve the source index name. Build the TableIndex
244
+ // directly (rather than via the driver's getTableDataIndex, which only MySQL and
245
+ // SQLite implement) so cloning a PostgreSQL or MS-SQL source table works too.
246
+ if (this.targetDb.getType() !== "sqlite") {
247
+ args.name = sourceIndex.getName()
248
+ }
249
+
250
+ return new TableIndex(sourceIndex.getColumnNames(), args)
251
+ }
252
+
253
+ /**
254
+ * Builds driver create-index args from a source index (the index name is omitted on
255
+ * SQLite, where index names are unique per-database rather than per-table).
256
+ * @param {{sourceIndex: import("../drivers/base-columns-index.js").default, tableName: string}} args
257
+ * @returns {{columns: string[], name?: string, tableName: string, unique: boolean}}
258
+ */
259
+ createIndexArgsFromSourceIndex({sourceIndex, tableName}) {
260
+ /** @type {{columns: string[], name?: string, tableName: string, unique: boolean}} */
261
+ const createIndexArgs = {
262
+ columns: sourceIndex.getColumnNames(),
263
+ tableName,
264
+ unique: sourceIndex.isUnique()
265
+ }
266
+
267
+ if (this.targetDb.getType() !== "sqlite") {
268
+ createIndexArgs.name = sourceIndex.getName()
269
+ }
270
+
271
+ return createIndexArgs
272
+ }
273
+
274
+ /**
275
+ * Whether two indexes have the same uniqueness and ordered column list.
276
+ * @param {import("../drivers/base-columns-index.js").default} sourceIndex
277
+ * @param {import("../drivers/base-columns-index.js").default} targetIndex
278
+ * @returns {boolean}
279
+ */
280
+ indexesMatch(sourceIndex, targetIndex) {
281
+ const sourceColumnNames = sourceIndex.getColumnNames()
282
+ const targetColumnNames = targetIndex.getColumnNames()
283
+
284
+ if (sourceIndex.isUnique() !== targetIndex.isUnique()) {
285
+ return false
286
+ }
287
+
288
+ if (sourceColumnNames.length !== targetColumnNames.length) {
289
+ return false
290
+ }
291
+
292
+ for (let columnIndex = 0; columnIndex < sourceColumnNames.length; columnIndex++) {
293
+ if (sourceColumnNames[columnIndex] !== targetColumnNames[columnIndex]) {
294
+ return false
295
+ }
296
+ }
297
+
298
+ return true
299
+ }
300
+
301
+ /**
302
+ * A stable signature for an index, used to match cloned indexes by shape.
303
+ * @param {import("../drivers/base-columns-index.js").default} index
304
+ * @returns {string}
305
+ */
306
+ indexSignature(index) {
307
+ return `${index.isUnique() ? "unique" : "index"}:${index.getColumnNames().join(",")}`
308
+ }
309
+
310
+ /**
311
+ * Normalizes a column type to its canonical lowercase form (`int` becomes `integer`).
312
+ * @param {string} columnType
313
+ * @returns {string}
314
+ */
315
+ normalizedColumnType(columnType) {
316
+ const normalizedType = columnType.toLowerCase()
317
+
318
+ if (normalizedType === "int") {
319
+ return "integer"
320
+ }
321
+
322
+ return normalizedType
323
+ }
324
+
325
+ /**
326
+ * The widening rank of a text column type (0 when not a text type).
327
+ * @param {string} columnType
328
+ * @returns {number}
329
+ */
330
+ textTypeRank(columnType) {
331
+ return TEXT_TYPE_RANKS[this.normalizedColumnType(columnType)] || 0
332
+ }
333
+
334
+ /**
335
+ * Whether the target's text column is narrower than the source's and must be widened.
336
+ * @param {import("../drivers/base-column.js").default} sourceColumn
337
+ * @param {import("../drivers/base-column.js").default} targetColumn
338
+ * @returns {boolean}
339
+ */
340
+ columnNeedsWidening(sourceColumn, targetColumn) {
341
+ const sourceRank = this.textTypeRank(sourceColumn.getType())
342
+ const targetRank = this.textTypeRank(targetColumn.getType())
343
+
344
+ return sourceRank > 0 && targetRank > 0 && sourceRank > targetRank
345
+ }
346
+
347
+ /**
348
+ * Builds TableData column args from a source column, copying type, nullability,
349
+ * length, notes, simple defaults and (for full clones) primary-key flag.
350
+ * @param {import("../drivers/base-column.js").default} sourceColumn
351
+ * @param {{isNewColumn: boolean}} args
352
+ * @returns {Record<string, unknown>}
353
+ */
354
+ columnArgsFromSourceColumn(sourceColumn, {isNewColumn}) {
355
+ /** @type {{autoIncrement?: boolean, default?: unknown, isNewColumn: boolean, maxLength?: number, notes?: string, null: boolean, primaryKey?: boolean, type: string}} */
356
+ const columnArgs = {
357
+ isNewColumn,
358
+ null: sourceColumn.getNull(),
359
+ type: this.normalizedColumnType(sourceColumn.getType())
360
+ }
361
+ const defaultValue = sourceColumn.getDefault()
362
+ const maxLength = sourceColumn.getMaxLength()
363
+ const notes = sourceColumn.getNotes()
364
+
365
+ if (!isNewColumn && sourceColumn.getPrimaryKey()) {
366
+ columnArgs.primaryKey = true
367
+ }
368
+
369
+ if (sourceColumn.getAutoIncrement()) {
370
+ columnArgs.autoIncrement = true
371
+ }
372
+
373
+ // A maxLength of -1 is the MS-SQL "max" sentinel (NVARCHAR(MAX) / VARBINARY(MAX),
374
+ // backing Velocious text/json/blob columns); the column type drives the unbounded
375
+ // SQL, so don't forward -1 as an explicit length (it would emit NVARCHAR(-1)).
376
+ if (maxLength !== undefined && maxLength >= 0) {
377
+ columnArgs.maxLength = maxLength
378
+ }
379
+
380
+ if (notes) {
381
+ columnArgs.notes = notes
382
+ }
383
+
384
+ if (defaultValue !== null && defaultValue !== undefined && SIMPLE_DEFAULT_PATTERN.test(String(defaultValue))) {
385
+ columnArgs.default = defaultValue
386
+ }
387
+
388
+ return columnArgs
389
+ }
390
+ }
@@ -0,0 +1,20 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Describes how one table's rows are partitioned between tenants so a DataCopier can select
5
+ * the subset that belongs to a given tenant key.
6
+ *
7
+ * A row belongs to the tenant when its `keyColumn` equals the tenant key value, or — for
8
+ * tables that have no direct tenant column — when its `parentColumn` references a row of
9
+ * `parentTableName` that was itself already selected as belonging to the tenant. Exactly
10
+ * one of `keyColumn` or the (`parentTableName` + `parentColumn`) pair must be set; a
11
+ * parent-scoped entry must appear after its parent in the plan so the parent ids are known
12
+ * by the time the child is loaded.
13
+ * @typedef {object} TenantTablePlanEntry
14
+ * @property {string} tableName Name of the table whose rows are partitioned by tenant.
15
+ * @property {string} [keyColumn] Column matched directly against the tenant key value.
16
+ * @property {string} [parentTableName] Earlier-in-plan table whose selected rows scope this one.
17
+ * @property {string} [parentColumn] Column on this table referencing the parent table's primary key.
18
+ */
19
+
20
+ export {}
@@ -0,0 +1,13 @@
1
+ export default class DbTenantsDrop extends BaseCommand {
2
+ /**
3
+ * Drops the tenant database/schema for every listed tenant through the provider's
4
+ * `dropDatabase` hook.
5
+ * @returns {Promise<{identifier: string, tenantCount: number} | void>} - Result in test mode.
6
+ */
7
+ execute(): Promise<{
8
+ identifier: string;
9
+ tenantCount: number;
10
+ } | void>;
11
+ }
12
+ import BaseCommand from "../../../base-command.js";
13
+ //# sourceMappingURL=drop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drop.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/commands/db/tenants/drop.js"],"names":[],"mappings":"AAKA;IACE;;;;OAIG;IACH,WAFa,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC,CAuBrE;CACF;wBA/BuB,0BAA0B"}
@@ -0,0 +1,31 @@
1
+ // @ts-check
2
+ import BaseCommand from "../../../base-command.js";
3
+ import TenantDatabaseCommandHelper from "../../../tenant-database-command-helper.js";
4
+ export default class DbTenantsDrop extends BaseCommand {
5
+ /**
6
+ * Drops the tenant database/schema for every listed tenant through the provider's
7
+ * `dropDatabase` hook.
8
+ * @returns {Promise<{identifier: string, tenantCount: number} | void>} - Result in test mode.
9
+ */
10
+ async execute() {
11
+ const helper = new TenantDatabaseCommandHelper({
12
+ command: this,
13
+ identifier: this.processArgs?.[1]
14
+ });
15
+ const provider = helper.provider;
16
+ if (typeof provider.dropDatabase !== "function") {
17
+ throw new Error(`Tenant database provider for ${helper.identifier} must define dropDatabase to use db:tenants:drop`);
18
+ }
19
+ const tenantCount = await helper.eachTenant(async ({ databaseConfiguration, tenant }) => {
20
+ await provider.dropDatabase?.({
21
+ configuration: this.getConfiguration(),
22
+ databaseConfiguration,
23
+ identifier: helper.identifier,
24
+ tenant
25
+ });
26
+ });
27
+ if (this.args.testing)
28
+ return { identifier: helper.identifier, tenantCount };
29
+ }
30
+ }
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9jbGkvY29tbWFuZHMvZGIvdGVuYW50cy9kcm9wLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLFdBQVcsTUFBTSwwQkFBMEIsQ0FBQTtBQUNsRCxPQUFPLDJCQUEyQixNQUFNLDRDQUE0QyxDQUFBO0FBRXBGLE1BQU0sQ0FBQyxPQUFPLE9BQU8sYUFBYyxTQUFRLFdBQVc7SUFDcEQ7Ozs7T0FJRztJQUNILEtBQUssQ0FBQyxPQUFPO1FBQ1gsTUFBTSxNQUFNLEdBQUcsSUFBSSwyQkFBMkIsQ0FBQztZQUM3QyxPQUFPLEVBQUUsSUFBSTtZQUNiLFVBQVUsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDO1NBQ2xDLENBQUMsQ0FBQTtRQUNGLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUE7UUFFaEMsSUFBSSxPQUFPLFFBQVEsQ0FBQyxZQUFZLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDaEQsTUFBTSxJQUFJLEtBQUssQ0FBQyxnQ0FBZ0MsTUFBTSxDQUFDLFVBQVUsa0RBQWtELENBQUMsQ0FBQTtRQUN0SCxDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsTUFBTSxNQUFNLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxFQUFDLHFCQUFxQixFQUFFLE1BQU0sRUFBQyxFQUFFLEVBQUU7WUFDcEYsTUFBTSxRQUFRLENBQUMsWUFBWSxFQUFFLENBQUM7Z0JBQzVCLGFBQWEsRUFBRSxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7Z0JBQ3RDLHFCQUFxQjtnQkFDckIsVUFBVSxFQUFFLE1BQU0sQ0FBQyxVQUFVO2dCQUM3QixNQUFNO2FBQ1AsQ0FBQyxDQUFBO1FBQ0osQ0FBQyxDQUFDLENBQUE7UUFFRixJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU8sRUFBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUMsQ0FBQTtJQUM1RSxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IEJhc2VDb21tYW5kIGZyb20gXCIuLi8uLi8uLi9iYXNlLWNvbW1hbmQuanNcIlxuaW1wb3J0IFRlbmFudERhdGFiYXNlQ29tbWFuZEhlbHBlciBmcm9tIFwiLi4vLi4vLi4vdGVuYW50LWRhdGFiYXNlLWNvbW1hbmQtaGVscGVyLmpzXCJcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRGJUZW5hbnRzRHJvcCBleHRlbmRzIEJhc2VDb21tYW5kIHtcbiAgLyoqXG4gICAqIERyb3BzIHRoZSB0ZW5hbnQgZGF0YWJhc2Uvc2NoZW1hIGZvciBldmVyeSBsaXN0ZWQgdGVuYW50IHRocm91Z2ggdGhlIHByb3ZpZGVyJ3NcbiAgICogYGRyb3BEYXRhYmFzZWAgaG9vay5cbiAgICogQHJldHVybnMge1Byb21pc2U8e2lkZW50aWZpZXI6IHN0cmluZywgdGVuYW50Q291bnQ6IG51bWJlcn0gfCB2b2lkPn0gLSBSZXN1bHQgaW4gdGVzdCBtb2RlLlxuICAgKi9cbiAgYXN5bmMgZXhlY3V0ZSgpIHtcbiAgICBjb25zdCBoZWxwZXIgPSBuZXcgVGVuYW50RGF0YWJhc2VDb21tYW5kSGVscGVyKHtcbiAgICAgIGNvbW1hbmQ6IHRoaXMsXG4gICAgICBpZGVudGlmaWVyOiB0aGlzLnByb2Nlc3NBcmdzPy5bMV1cbiAgICB9KVxuICAgIGNvbnN0IHByb3ZpZGVyID0gaGVscGVyLnByb3ZpZGVyXG5cbiAgICBpZiAodHlwZW9mIHByb3ZpZGVyLmRyb3BEYXRhYmFzZSAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbmFudCBkYXRhYmFzZSBwcm92aWRlciBmb3IgJHtoZWxwZXIuaWRlbnRpZmllcn0gbXVzdCBkZWZpbmUgZHJvcERhdGFiYXNlIHRvIHVzZSBkYjp0ZW5hbnRzOmRyb3BgKVxuICAgIH1cblxuICAgIGNvbnN0IHRlbmFudENvdW50ID0gYXdhaXQgaGVscGVyLmVhY2hUZW5hbnQoYXN5bmMgKHtkYXRhYmFzZUNvbmZpZ3VyYXRpb24sIHRlbmFudH0pID0+IHtcbiAgICAgIGF3YWl0IHByb3ZpZGVyLmRyb3BEYXRhYmFzZT8uKHtcbiAgICAgICAgY29uZmlndXJhdGlvbjogdGhpcy5nZXRDb25maWd1cmF0aW9uKCksXG4gICAgICAgIGRhdGFiYXNlQ29uZmlndXJhdGlvbixcbiAgICAgICAgaWRlbnRpZmllcjogaGVscGVyLmlkZW50aWZpZXIsXG4gICAgICAgIHRlbmFudFxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgaWYgKHRoaXMuYXJncy50ZXN0aW5nKSByZXR1cm4ge2lkZW50aWZpZXI6IGhlbHBlci5pZGVudGlmaWVyLCB0ZW5hbnRDb3VudH1cbiAgfVxufVxuIl19
@@ -41,25 +41,5 @@ export default class TenantDatabaseCommandHelper {
41
41
  * @returns {number} - Number of tenants to process concurrently.
42
42
  */
43
43
  parallelCount(): number;
44
- /**
45
- * Runs run tenant callback.
46
- * @param {object} args - Tenant callback args.
47
- * @param {function({databaseConfiguration: import("../configuration-types.js").DatabaseConfigurationType, tenant: ?}) : Promise<void>} args.callback - Callback.
48
- * @param {?} args.tenant - Tenant.
49
- * @returns {Promise<void>}
50
- */
51
- runTenantCallback({ callback, tenant }: {
52
- callback: (arg0: {
53
- databaseConfiguration: import("../configuration-types.js").DatabaseConfigurationType;
54
- tenant: unknown;
55
- }) => Promise<void>;
56
- tenant: unknown;
57
- }): Promise<void>;
58
- /**
59
- * Runs tenant label.
60
- * @param {?} tenant - Tenant.
61
- * @returns {string} - Human readable tenant label.
62
- */
63
- tenantLabel(tenant: unknown): string;
64
44
  }
65
45
  //# sourceMappingURL=tenant-database-command-helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-database-command-helper.d.ts","sourceRoot":"","sources":["../../../src/cli/tenant-database-command-helper.js"],"names":[],"mappings":"AAEA;IACE;;;;;OAKG;IACH,qCAHG;QAAkD,OAAO,EAAjD,OAAO,mBAAmB,EAAE,OAAO;QACV,UAAU,EAAnC,MAAM,GAAG,SAAS;KAC5B,EAQA;IAJC,6CAAsB;IACtB,qDAA+C;IAC/C,mBAA4B;IAC5B,yEAAwE;IAG1E;;yBAEqB;IACrB,oCADa,IAAI,CAmBhB;IAED;;;OAGG;IACH,qBAFa,OAAO,CAAC,IAAI,CAAC,CAIzB;IAED;;;OAGG;IACH,eAFa,OAAO,CAAC,KAAK,CAAC,OAAC,CAAC,CAAC,CAkB7B;IAED;;;;OAIG;IACH,qBAHW,CAAS,IAAiG,EAAjG;QAAC,qBAAqB,EAAE,OAAO,2BAA2B,EAAE,yBAAyB,CAAC;QAAC,MAAM,EAAE,OAAC,CAAA;KAAC,KAAI,OAAO,CAAC,IAAI,CAAC,GACzH,OAAO,CAAC,MAAM,CAAC,CAqD3B;IAED;;;OAGG;IACH,iBAFa,MAAM,CA0BlB;IAED;;;;;;OAMG;IACH,wCAJG;QAA0I,QAAQ,EAA1I,CAAS,IAAiG,EAAjG;YAAC,qBAAqB,EAAE,OAAO,2BAA2B,EAAE,yBAAyB,CAAC;YAAC,MAAM,EAAE,OAAC,CAAA;SAAC,KAAI,OAAO,CAAC,IAAI,CAAC;QACnH,MAAM,EAAd,OAAC;KACT,GAAU,OAAO,CAAC,IAAI,CAAC,CAazB;IAED;;;;OAIG;IACH,oBAHW,OAAC,GACC,MAAM,CAYlB;CACF"}
1
+ {"version":3,"file":"tenant-database-command-helper.d.ts","sourceRoot":"","sources":["../../../src/cli/tenant-database-command-helper.js"],"names":[],"mappings":"AAIA;IACE;;;;;OAKG;IACH,qCAHG;QAAkD,OAAO,EAAjD,OAAO,mBAAmB,EAAE,OAAO;QACV,UAAU,EAAnC,MAAM,GAAG,SAAS;KAC5B,EAQA;IAJC,6CAAsB;IACtB,qDAA+C;IAC/C,mBAA4B;IAC5B,yEAAwE;IAG1E;;yBAEqB;IACrB,oCADa,IAAI,CAmBhB;IAED;;;OAGG;IACH,qBAFa,OAAO,CAAC,IAAI,CAAC,CAIzB;IAED;;;OAGG;IACH,eAFa,OAAO,CAAC,KAAK,CAAC,OAAC,CAAC,CAAC,CAkB7B;IAED;;;;OAIG;IACH,qBAHW,CAAS,IAAiG,EAAjG;QAAC,qBAAqB,EAAE,OAAO,2BAA2B,EAAE,yBAAyB,CAAC;QAAC,MAAM,EAAE,OAAC,CAAA;KAAC,KAAI,OAAO,CAAC,IAAI,CAAC,GACzH,OAAO,CAAC,MAAM,CAAC,CAW3B;IAED;;;OAGG;IACH,iBAFa,MAAM,CA0BlB;CACF"}
@@ -1,4 +1,5 @@
1
1
  // @ts-check
2
+ import TenantIterator from "../tenants/tenant-iterator.js";
2
3
  export default class TenantDatabaseCommandHelper {
3
4
  /**
4
5
  * Runs constructor.
@@ -64,43 +65,12 @@ export default class TenantDatabaseCommandHelper {
64
65
  */
65
66
  async eachTenant(callback) {
66
67
  const tenants = await this.listTenants();
67
- const parallelCount = this.parallelCount();
68
- if (parallelCount <= 1) {
69
- for (const tenant of tenants) {
70
- await this.runTenantCallback({ callback, tenant });
71
- }
72
- return tenants.length;
73
- }
74
- /**
75
- * Failures.
76
- * @type {Array<{error: Error, tenant: ?}>} */
77
- const failures = [];
78
- const workers = [];
79
- let tenantIndex = 0;
80
- const workerCount = Math.min(parallelCount, tenants.length);
81
- for (let workerIndex = 0; workerIndex < workerCount; workerIndex++) {
82
- workers.push((async () => {
83
- while (tenantIndex < tenants.length) {
84
- const tenant = tenants[tenantIndex];
85
- tenantIndex++;
86
- try {
87
- await this.runTenantCallback({ callback, tenant });
88
- }
89
- catch (error) {
90
- failures.push({
91
- error: error instanceof Error ? error : new Error(String(error)),
92
- tenant
93
- });
94
- }
95
- }
96
- })());
97
- }
98
- await Promise.all(workers);
99
- if (failures.length > 0) {
100
- const failedTenantLabels = failures.map((failure) => this.tenantLabel(failure.tenant)).join(", ");
101
- throw new AggregateError(failures.map((failure) => failure.error), `Failed tenant database command for tenant(s): ${failedTenantLabels}`);
102
- }
103
- return tenants.length;
68
+ const iterator = new TenantIterator({
69
+ configuration: this.configuration,
70
+ identifier: this.identifier,
71
+ parallelCount: this.parallelCount()
72
+ });
73
+ return await iterator.run(tenants, callback);
104
74
  }
105
75
  /**
106
76
  * Runs parallel count.
@@ -126,40 +96,5 @@ export default class TenantDatabaseCommandHelper {
126
96
  }
127
97
  return parallelCount;
128
98
  }
129
- /**
130
- * Runs run tenant callback.
131
- * @param {object} args - Tenant callback args.
132
- * @param {function({databaseConfiguration: import("../configuration-types.js").DatabaseConfigurationType, tenant: ?}) : Promise<void>} args.callback - Callback.
133
- * @param {?} args.tenant - Tenant.
134
- * @returns {Promise<void>}
135
- */
136
- async runTenantCallback({ callback, tenant }) {
137
- await this.configuration.runWithTenant(tenant, async () => {
138
- if (!this.configuration.isDatabaseIdentifierActive(this.identifier)) {
139
- throw new Error(`Tenant database identifier ${this.identifier} is inactive for tenant: ${this.tenantLabel(tenant)}`);
140
- }
141
- await callback({
142
- databaseConfiguration: this.configuration.resolveDatabaseConfiguration(this.identifier),
143
- tenant
144
- });
145
- });
146
- }
147
- /**
148
- * Runs tenant label.
149
- * @param {?} tenant - Tenant.
150
- * @returns {string} - Human readable tenant label.
151
- */
152
- tenantLabel(tenant) {
153
- if (tenant && typeof tenant === "object") {
154
- const tenantObject = /** @type {{id?: ?, name?: ?, slug?: ?}} */ (tenant);
155
- if (tenantObject.slug)
156
- return String(tenantObject.slug);
157
- if (tenantObject.name)
158
- return String(tenantObject.name);
159
- if (tenantObject.id)
160
- return String(tenantObject.id);
161
- }
162
- return JSON.stringify(tenant);
163
- }
164
99
  }
165
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LWRhdGFiYXNlLWNvbW1hbmQtaGVscGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NsaS90ZW5hbnQtZGF0YWJhc2UtY29tbWFuZC1oZWxwZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE1BQU0sQ0FBQyxPQUFPLE9BQU8sMkJBQTJCO0lBQzlDOzs7OztPQUtHO0lBQ0gsWUFBWSxFQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUM7UUFDL0IsSUFBSSxDQUFDLFVBQVU7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUE7UUFFL0UsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUE7UUFDdEIsSUFBSSxDQUFDLGFBQWEsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQTtRQUMvQyxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQTtRQUM1QixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMseUJBQXlCLENBQUMsVUFBVSxDQUFDLENBQUE7SUFDMUUsQ0FBQztJQUVEOzt5QkFFcUI7SUFDckIsZ0NBQWdDO1FBQzlCLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyx3QkFBd0IsRUFBRSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQTtRQUU1RixJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztZQUMzQixNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQTtRQUMvRSxDQUFDO1FBRUQsSUFBSSxDQUFDLHFCQUFxQixDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3RDLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLElBQUksQ0FBQyxVQUFVLDBDQUEwQyxDQUFDLENBQUE7UUFDbkcsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyw4QkFBOEIsRUFB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Q3RILENBQUM7WUFFRCxNQUFNLFFBQVEsQ0FBQztnQkFDYixxQkFBcUIsRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLDRCQUE0QixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUM7Z0JBQ3ZGLE1BQU07YUFDUCxDQUFDLENBQUE7UUFDSixDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsV0FBVyxDQUFDLE1BQU07UUFDaEIsSUFBSSxNQUFNLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDekMsTUFBTSxZQUFZLEdBQUcsMkNBQTJDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUV6RSxJQUFJLFlBQVksQ0FBQyxJQUFJO2dCQUFFLE9BQU8sTUFBTSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQTtZQUN2RCxJQUFJLFlBQVksQ0FBQyxJQUFJO2dCQUFFLE9BQU8sTUFBTSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQTtZQUN2RCxJQUFJLFlBQVksQ0FBQyxFQUFFO2dCQUFFLE9BQU8sTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUNyRCxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQy9CLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBUZW5hbnREYXRhYmFzZUNvbW1hbmRIZWxwZXIge1xuICAvKipcbiAgICogUnVucyBjb25zdHJ1Y3Rvci5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zIG9iamVjdC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL2Jhc2UtY29tbWFuZC5qc1wiKS5kZWZhdWx0fSBhcmdzLmNvbW1hbmQgLSBDTEkgY29tbWFuZCBpbnN0YW5jZS5cbiAgICogQHBhcmFtIHtzdHJpbmcgfCB1bmRlZmluZWR9IGFyZ3MuaWRlbnRpZmllciAtIFRlbmFudCBkYXRhYmFzZSBpZGVudGlmaWVyLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe2NvbW1hbmQsIGlkZW50aWZpZXJ9KSB7XG4gICAgaWYgKCFpZGVudGlmaWVyKSB0aHJvdyBuZXcgRXJyb3IoXCJNaXNzaW5nIHRlbmFudCBkYXRhYmFzZSBpZGVudGlmaWVyIGFyZ3VtZW50XCIpXG5cbiAgICB0aGlzLmNvbW1hbmQgPSBjb21tYW5kXG4gICAgdGhpcy5jb25maWd1cmF0aW9uID0gY29tbWFuZC5nZXRDb25maWd1cmF0aW9uKClcbiAgICB0aGlzLmlkZW50aWZpZXIgPSBpZGVudGlmaWVyXG4gICAgdGhpcy5wcm92aWRlciA9IHRoaXMuY29uZmlndXJhdGlvbi5nZXRUZW5hbnREYXRhYmFzZVByb3ZpZGVyKGlkZW50aWZpZXIpXG4gIH1cblxuICAvKipcbiAgICogUnVucyB2YWxpZGF0ZSB0ZW5hbnQgZGF0YWJhc2UgaWRlbnRpZmllci5cbiAgICogQHJldHVybnMge3ZvaWR9ICovXG4gIHZhbGlkYXRlVGVuYW50RGF0YWJhc2VJZGVudGlmaWVyKCkge1xuICAgIGNvbnN0IGRhdGFiYXNlQ29uZmlndXJhdGlvbiA9IHRoaXMuY29uZmlndXJhdGlvbi5nZXREYXRhYmFzZUNvbmZpZ3VyYXRpb24oKVt0aGlzLmlkZW50aWZpZXJdXG5cbiAgICBpZiAoIWRhdGFiYXNlQ29uZmlndXJhdGlvbikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBzdWNoIGRhdGFiYXNlIGlkZW50aWZpZXIgY29uZmlndXJlZDogJHt0aGlzLmlkZW50aWZpZXJ9YClcbiAgICB9XG5cbiAgICBpZiAoIWRhdGFiYXNlQ29uZmlndXJhdGlvbi50ZW5hbnRPbmx5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYERhdGFiYXNlIGlkZW50aWZpZXIgJHt0aGlzLmlkZW50aWZpZXJ9IGlzIG5vdCBjb25maWd1cmVkIHdpdGggdGVuYW50T25seTogdHJ1ZWApXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuY29uZmlndXJhdGlvbi5nZXREaXNhYmxlZERhdGFiYXNlSWRlbnRpZmllcnMoKS5oYXModGhpcy5pZGVudGlmaWVyKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW5hbnQgZGF0YWJhc2UgaWRlbnRpZmllciAke3RoaXMuaWRlbnRpZmllcn0gaXMgZGlzYWJsZWQgYnkgVkVMT0NJT1VTX0RJU0FCTEVEX0RBVEFCQVNFX0lERU5USUZJRVJTYClcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIHRoaXMucHJvdmlkZXIubGlzdFRlbmFudHMgIT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW5hbnQgZGF0YWJhc2UgcHJvdmlkZXIgZm9yICR7dGhpcy5pZGVudGlmaWVyfSBtdXN0IGRlZmluZSBsaXN0VGVuYW50c2ApXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgaW5pdGlhbGl6ZSBydW50aW1lLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGFwcCBydW50aW1lIGlzIGluaXRpYWxpemVkLlxuICAgKi9cbiAgYXN5bmMgaW5pdGlhbGl6ZVJ1bnRpbWUoKSB7XG4gICAgYXdhaXQgdGhpcy5jb25maWd1cmF0aW9uLmluaXRpYWxpemUoe3R5cGU6IFwiZGItdGVuYW50c1wifSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGxpc3QgdGVuYW50cy5cbiAgICogQHJldHVybnMge1Byb21pc2U8QXJyYXk8Pz4+fSAtIFRlbmFudHMuXG4gICAqL1xuICBhc3luYyBsaXN0VGVuYW50cygpIHtcbiAgICB0aGlzLnZhbGlkYXRlVGVuYW50RGF0YWJhc2VJZGVudGlmaWVyKClcbiAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVSdW50aW1lKClcblxuICAgIGNvbnN0IHRlbmFudHMgPSBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uZW5zdXJlQ29ubmVjdGlvbnMoe25hbWU6IGBUZW5hbnQgZGF0YWJhc2UgbGlzdDogJHt0aGlzLmlkZW50aWZpZXJ9YH0sIGFzeW5jICgpID0+IHtcbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLnByb3ZpZGVyLmxpc3RUZW5hbnRzKHtcbiAgICAgICAgY29uZmlndXJhdGlvbjogdGhpcy5jb25maWd1cmF0aW9uLFxuICAgICAgICBpZGVudGlmaWVyOiB0aGlzLmlkZW50aWZpZXJcbiAgICAgIH0pXG4gICAgfSlcblxuICAgIGlmICghQXJyYXkuaXNBcnJheSh0ZW5hbnRzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW5hbnQgZGF0YWJhc2UgcHJvdmlkZXIgZm9yICR7dGhpcy5pZGVudGlmaWVyfSBtdXN0IHJldHVybiBhbiBhcnJheSBmcm9tIGxpc3RUZW5hbnRzYClcbiAgICB9XG5cbiAgICByZXR1cm4gdGVuYW50c1xuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgZWFjaCB0ZW5hbnQuXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb24oe2RhdGFiYXNlQ29uZmlndXJhdGlvbjogaW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5EYXRhYmFzZUNvbmZpZ3VyYXRpb25UeXBlLCB0ZW5hbnQ6ID99KSA6IFByb21pc2U8dm9pZD59IGNhbGxiYWNrIC0gQ2FsbGJhY2suXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPG51bWJlcj59IC0gTnVtYmVyIG9mIHRlbmFudHMgcHJvY2Vzc2VkLlxuICAgKi9cbiAgYXN5bmMgZWFjaFRlbmFudChjYWxsYmFjaykge1xuICAgIGNvbnN0IHRlbmFudHMgPSBhd2FpdCB0aGlzLmxpc3RUZW5hbnRzKClcbiAgICBjb25zdCBwYXJhbGxlbENvdW50ID0gdGhpcy5wYXJhbGxlbENvdW50KClcblxuICAgIGlmIChwYXJhbGxlbENvdW50IDw9IDEpIHtcbiAgICAgIGZvciAoY29uc3QgdGVuYW50IG9mIHRlbmFudHMpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5ydW5UZW5hbnRDYWxsYmFjayh7Y2FsbGJhY2ssIHRlbmFudH0pXG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0ZW5hbnRzLmxlbmd0aFxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZhaWx1cmVzLlxuICAgICAqIEB0eXBlIHtBcnJheTx7ZXJyb3I6IEVycm9yLCB0ZW5hbnQ6ID99Pn0gKi9cbiAgICBjb25zdCBmYWlsdXJlcyA9IFtdXG4gICAgY29uc3Qgd29ya2VycyA9IFtdXG4gICAgbGV0IHRlbmFudEluZGV4ID0gMFxuICAgIGNvbnN0IHdvcmtlckNvdW50ID0gTWF0aC5taW4ocGFyYWxsZWxDb3VudCwgdGVuYW50cy5sZW5ndGgpXG5cbiAgICBmb3IgKGxldCB3b3JrZXJJbmRleCA9IDA7IHdvcmtlckluZGV4IDwgd29ya2VyQ291bnQ7IHdvcmtlckluZGV4KyspIHtcbiAgICAgIHdvcmtlcnMucHVzaCgoYXN5bmMgKCkgPT4ge1xuICAgICAgICB3aGlsZSAodGVuYW50SW5kZXggPCB0ZW5hbnRzLmxlbmd0aCkge1xuICAgICAgICAgIGNvbnN0IHRlbmFudCA9IHRlbmFudHNbdGVuYW50SW5kZXhdXG5cbiAgICAgICAgICB0ZW5hbnRJbmRleCsrXG5cbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5ydW5UZW5hbnRDYWxsYmFjayh7Y2FsbGJhY2ssIHRlbmFudH0pXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGZhaWx1cmVzLnB1c2goe1xuICAgICAgICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yIDogbmV3IEVycm9yKFN0cmluZyhlcnJvcikpLFxuICAgICAgICAgICAgICB0ZW5hbnRcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KSgpKVxuICAgIH1cblxuICAgIGF3YWl0IFByb21pc2UuYWxsKHdvcmtlcnMpXG5cbiAgICBpZiAoZmFpbHVyZXMubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgZmFpbGVkVGVuYW50TGFiZWxzID0gZmFpbHVyZXMubWFwKChmYWlsdXJlKSA9PiB0aGlzLnRlbmFudExhYmVsKGZhaWx1cmUudGVuYW50KSkuam9pbihcIiwgXCIpXG5cbiAgICAgIHRocm93IG5ldyBBZ2dyZWdhdGVFcnJvcihcbiAgICAgICAgZmFpbHVyZXMubWFwKChmYWlsdXJlKSA9PiBmYWlsdXJlLmVycm9yKSxcbiAgICAgICAgYEZhaWxlZCB0ZW5hbnQgZGF0YWJhc2UgY29tbWFuZCBmb3IgdGVuYW50KHMpOiAke2ZhaWxlZFRlbmFudExhYmVsc31gXG4gICAgICApXG4gICAgfVxuXG4gICAgcmV0dXJuIHRlbmFudHMubGVuZ3RoXG4gIH1cblxuICAvKipcbiAgICogUnVucyBwYXJhbGxlbCBjb3VudC5cbiAgICogQHJldHVybnMge251bWJlcn0gLSBOdW1iZXIgb2YgdGVuYW50cyB0byBwcm9jZXNzIGNvbmN1cnJlbnRseS5cbiAgICovXG4gIHBhcmFsbGVsQ291bnQoKSB7XG4gICAgY29uc3QgcGFyc2VkUHJvY2Vzc0FyZ3MgPSB0aGlzLmNvbW1hbmQuYXJncz8ucGFyc2VkUHJvY2Vzc0FyZ3MgfHwge31cbiAgICBsZXQgcGFyYWxsZWxBcmcgPSBwYXJzZWRQcm9jZXNzQXJncy5wYXJhbGxlbFxuXG4gICAgaWYgKHBhcmFsbGVsQXJnID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IHBhcmFsbGVsQXJnSW5kZXggPSB0aGlzLmNvbW1hbmQucHJvY2Vzc0FyZ3M/LmluZGV4T2YoXCItLXBhcmFsbGVsXCIpID8/IC0xXG5cbiAgICAgIGlmIChwYXJhbGxlbEFyZ0luZGV4ID49IDApIHtcbiAgICAgICAgY29uc3QgbmV4dEFyZyA9IHRoaXMuY29tbWFuZC5wcm9jZXNzQXJncz8uW3BhcmFsbGVsQXJnSW5kZXggKyAxXVxuXG4gICAgICAgIHBhcmFsbGVsQXJnID0gbmV4dEFyZyAmJiAhbmV4dEFyZy5zdGFydHNXaXRoKFwiLVwiKSA/IG5leHRBcmcgOiB0cnVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHBhcmFsbGVsQXJnID09PSB1bmRlZmluZWQgfHwgcGFyYWxsZWxBcmcgPT09IGZhbHNlKSByZXR1cm4gMVxuICAgIGlmIChwYXJhbGxlbEFyZyA9PT0gdHJ1ZSkgcmV0dXJuIDIwXG5cbiAgICBjb25zdCBwYXJhbGxlbENvdW50ID0gTnVtYmVyKHBhcmFsbGVsQXJnKVxuXG4gICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKHBhcmFsbGVsQ291bnQpIHx8IHBhcmFsbGVsQ291bnQgPCAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYC0tcGFyYWxsZWwgbXVzdCBiZSBhIHBvc2l0aXZlIGludGVnZXIgd2hlbiBhIHZhbHVlIGlzIHByb3ZpZGVkOiAke3BhcmFsbGVsQXJnfWApXG4gICAgfVxuXG4gICAgcmV0dXJuIHBhcmFsbGVsQ291bnRcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHJ1biB0ZW5hbnQgY2FsbGJhY2suXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gVGVuYW50IGNhbGxiYWNrIGFyZ3MuXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb24oe2RhdGFiYXNlQ29uZmlndXJhdGlvbjogaW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5EYXRhYmFzZUNvbmZpZ3VyYXRpb25UeXBlLCB0ZW5hbnQ6ID99KSA6IFByb21pc2U8dm9pZD59IGFyZ3MuY2FsbGJhY2sgLSBDYWxsYmFjay5cbiAgICogQHBhcmFtIHs/fSBhcmdzLnRlbmFudCAtIFRlbmFudC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBydW5UZW5hbnRDYWxsYmFjayh7Y2FsbGJhY2ssIHRlbmFudH0pIHtcbiAgICBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24ucnVuV2l0aFRlbmFudCh0ZW5hbnQsIGFzeW5jICgpID0+IHtcbiAgICAgIGlmICghdGhpcy5jb25maWd1cmF0aW9uLmlzRGF0YWJhc2VJZGVudGlmaWVyQWN0aXZlKHRoaXMuaWRlbnRpZmllcikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW5hbnQgZGF0YWJhc2UgaWRlbnRpZmllciAke3RoaXMuaWRlbnRpZmllcn0gaXMgaW5hY3RpdmUgZm9yIHRlbmFudDogJHt0aGlzLnRlbmFudExhYmVsKHRlbmFudCl9YClcbiAgICAgIH1cblxuICAgICAgYXdhaXQgY2FsbGJhY2soe1xuICAgICAgICBkYXRhYmFzZUNvbmZpZ3VyYXRpb246IHRoaXMuY29uZmlndXJhdGlvbi5yZXNvbHZlRGF0YWJhc2VDb25maWd1cmF0aW9uKHRoaXMuaWRlbnRpZmllciksXG4gICAgICAgIHRlbmFudFxuICAgICAgfSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgdGVuYW50IGxhYmVsLlxuICAgKiBAcGFyYW0gez99IHRlbmFudCAtIFRlbmFudC5cbiAgICogQHJldHVybnMge3N0cmluZ30gLSBIdW1hbiByZWFkYWJsZSB0ZW5hbnQgbGFiZWwuXG4gICAqL1xuICB0ZW5hbnRMYWJlbCh0ZW5hbnQpIHtcbiAgICBpZiAodGVuYW50ICYmIHR5cGVvZiB0ZW5hbnQgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIGNvbnN0IHRlbmFudE9iamVjdCA9IC8qKiBAdHlwZSB7e2lkPzogPywgbmFtZT86ID8sIHNsdWc/OiA/fX0gKi8gKHRlbmFudClcblxuICAgICAgaWYgKHRlbmFudE9iamVjdC5zbHVnKSByZXR1cm4gU3RyaW5nKHRlbmFudE9iamVjdC5zbHVnKVxuICAgICAgaWYgKHRlbmFudE9iamVjdC5uYW1lKSByZXR1cm4gU3RyaW5nKHRlbmFudE9iamVjdC5uYW1lKVxuICAgICAgaWYgKHRlbmFudE9iamVjdC5pZCkgcmV0dXJuIFN0cmluZyh0ZW5hbnRPYmplY3QuaWQpXG4gICAgfVxuXG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHRlbmFudClcbiAgfVxufVxuIl19
100
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LWRhdGFiYXNlLWNvbW1hbmQtaGVscGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NsaS90ZW5hbnQtZGF0YWJhc2UtY29tbWFuZC1oZWxwZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sY0FBYyxNQUFNLCtCQUErQixDQUFBO0FBRTFELE1BQU0sQ0FBQyxPQUFPLE9BQU8sMkJBQTJCO0lBQzlDOzs7OztPQUtHO0lBQ0gsWUFBWSxFQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUM7UUFDL0IsSUFBSSxDQUFDLFVBQVU7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUE7UUFFL0UsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUE7UUFDdEIsSUFBSSxDQUFDLGFBQWEsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQTtRQUMvQyxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQTtRQUM1QixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMseUJBQXlCLENBQUMsVUFBVSxDQUFDLENBQUE7SUFDMUUsQ0FBQztJQUVEOzt5QkFFcUI7SUFDckIsZ0NBQWdDO1FBQzlCLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyx3QkFBd0IsRUFBRSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQTtRQUU1RixJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztZQUMzQixNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQTtRQUMvRSxDQUFDO1FBRUQsSUFBSSxDQUFDLHFCQUFxQixDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3RDLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLElBQUksQ0FBQyxVQUFVLDBDQUEwQyxDQUFDLENBQUE7UUFDbkcsQ0FBQztRQUVELElB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LFdBQVcsS0FBSyxTQUFTLElBQUksV0FBVyxLQUFLLEtBQUs7WUFBRSxPQUFPLENBQUMsQ0FBQTtRQUNoRSxJQUFJLFdBQVcsS0FBSyxJQUFJO1lBQUUsT0FBTyxFQUFFLENBQUE7UUFFbkMsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFBO1FBRXpDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxJQUFJLGFBQWEsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMxRCxNQUFNLElBQUksS0FBSyxDQUFDLG1FQUFtRSxXQUFXLEVBQUUsQ0FBQyxDQUFBO1FBQ25HLENBQUM7UUFFRCxPQUFPLGFBQWEsQ0FBQTtJQUN0QixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IFRlbmFudEl0ZXJhdG9yIGZyb20gXCIuLi90ZW5hbnRzL3RlbmFudC1pdGVyYXRvci5qc1wiXG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRlbmFudERhdGFiYXNlQ29tbWFuZEhlbHBlciB7XG4gIC8qKlxuICAgKiBSdW5zIGNvbnN0cnVjdG9yLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMgb2JqZWN0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vYmFzZS1jb21tYW5kLmpzXCIpLmRlZmF1bHR9IGFyZ3MuY29tbWFuZCAtIENMSSBjb21tYW5kIGluc3RhbmNlLlxuICAgKiBAcGFyYW0ge3N0cmluZyB8IHVuZGVmaW5lZH0gYXJncy5pZGVudGlmaWVyIC0gVGVuYW50IGRhdGFiYXNlIGlkZW50aWZpZXIuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7Y29tbWFuZCwgaWRlbnRpZmllcn0pIHtcbiAgICBpZiAoIWlkZW50aWZpZXIpIHRocm93IG5ldyBFcnJvcihcIk1pc3NpbmcgdGVuYW50IGRhdGFiYXNlIGlkZW50aWZpZXIgYXJndW1lbnRcIilcblxuICAgIHRoaXMuY29tbWFuZCA9IGNvbW1hbmRcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBjb21tYW5kLmdldENvbmZpZ3VyYXRpb24oKVxuICAgIHRoaXMuaWRlbnRpZmllciA9IGlkZW50aWZpZXJcbiAgICB0aGlzLnByb3ZpZGVyID0gdGhpcy5jb25maWd1cmF0aW9uLmdldFRlbmFudERhdGFiYXNlUHJvdmlkZXIoaWRlbnRpZmllcilcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHZhbGlkYXRlIHRlbmFudCBkYXRhYmFzZSBpZGVudGlmaWVyLlxuICAgKiBAcmV0dXJucyB7dm9pZH0gKi9cbiAgdmFsaWRhdGVUZW5hbnREYXRhYmFzZUlkZW50aWZpZXIoKSB7XG4gICAgY29uc3QgZGF0YWJhc2VDb25maWd1cmF0aW9uID0gdGhpcy5jb25maWd1cmF0aW9uLmdldERhdGFiYXNlQ29uZmlndXJhdGlvbigpW3RoaXMuaWRlbnRpZmllcl1cblxuICAgIGlmICghZGF0YWJhc2VDb25maWd1cmF0aW9uKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vIHN1Y2ggZGF0YWJhc2UgaWRlbnRpZmllciBjb25maWd1cmVkOiAke3RoaXMuaWRlbnRpZmllcn1gKVxuICAgIH1cblxuICAgIGlmICghZGF0YWJhc2VDb25maWd1cmF0aW9uLnRlbmFudE9ubHkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRGF0YWJhc2UgaWRlbnRpZmllciAke3RoaXMuaWRlbnRpZmllcn0gaXMgbm90IGNvbmZpZ3VyZWQgd2l0aCB0ZW5hbnRPbmx5OiB0cnVlYClcbiAgICB9XG5cbiAgICBpZiAodGhpcy5jb25maWd1cmF0aW9uLmdldERpc2FibGVkRGF0YWJhc2VJZGVudGlmaWVycygpLmhhcyh0aGlzLmlkZW50aWZpZXIpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbmFudCBkYXRhYmFzZSBpZGVudGlmaWVyICR7dGhpcy5pZGVudGlmaWVyfSBpcyBkaXNhYmxlZCBieSBWRUxPQ0lPVVNfRElTQUJMRURfREFUQUJBU0VfSURFTlRJRklFUlNgKVxuICAgIH1cblxuICAgIGlmICh0eXBlb2YgdGhpcy5wcm92aWRlci5saXN0VGVuYW50cyAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbmFudCBkYXRhYmFzZSBwcm92aWRlciBmb3IgJHt0aGlzLmlkZW50aWZpZXJ9IG11c3QgZGVmaW5lIGxpc3RUZW5hbnRzYClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBpbml0aWFsaXplIHJ1bnRpbWUuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gYXBwIHJ1bnRpbWUgaXMgaW5pdGlhbGl6ZWQuXG4gICAqL1xuICBhc3luYyBpbml0aWFsaXplUnVudGltZSgpIHtcbiAgICBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uaW5pdGlhbGl6ZSh7dHlwZTogXCJkYi10ZW5hbnRzXCJ9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgbGlzdCB0ZW5hbnRzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheTw/Pj59IC0gVGVuYW50cy5cbiAgICovXG4gIGFzeW5jIGxpc3RUZW5hbnRzKCkge1xuICAgIHRoaXMudmFsaWRhdGVUZW5hbnREYXRhYmFzZUlkZW50aWZpZXIoKVxuICAgIGF3YWl0IHRoaXMuaW5pdGlhbGl6ZVJ1bnRpbWUoKVxuXG4gICAgY29uc3QgdGVuYW50cyA9IGF3YWl0IHRoaXMuY29uZmlndXJhdGlvbi5lbnN1cmVDb25uZWN0aW9ucyh7bmFtZTogYFRlbmFudCBkYXRhYmFzZSBsaXN0OiAke3RoaXMuaWRlbnRpZmllcn1gfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucHJvdmlkZXIubGlzdFRlbmFudHMoe1xuICAgICAgICBjb25maWd1cmF0aW9uOiB0aGlzLmNvbmZpZ3VyYXRpb24sXG4gICAgICAgIGlkZW50aWZpZXI6IHRoaXMuaWRlbnRpZmllclxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgaWYgKCFBcnJheS5pc0FycmF5KHRlbmFudHMpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbmFudCBkYXRhYmFzZSBwcm92aWRlciBmb3IgJHt0aGlzLmlkZW50aWZpZXJ9IG11c3QgcmV0dXJuIGFuIGFycmF5IGZyb20gbGlzdFRlbmFudHNgKVxuICAgIH1cblxuICAgIHJldHVybiB0ZW5hbnRzXG4gIH1cblxuICAvKipcbiAgICogUnVucyBlYWNoIHRlbmFudC5cbiAgICogQHBhcmFtIHtmdW5jdGlvbih7ZGF0YWJhc2VDb25maWd1cmF0aW9uOiBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkRhdGFiYXNlQ29uZmlndXJhdGlvblR5cGUsIHRlbmFudDogP30pIDogUHJvbWlzZTx2b2lkPn0gY2FsbGJhY2sgLSBDYWxsYmFjay5cbiAgICogQHJldHVybnMge1Byb21pc2U8bnVtYmVyPn0gLSBOdW1iZXIgb2YgdGVuYW50cyBwcm9jZXNzZWQuXG4gICAqL1xuICBhc3luYyBlYWNoVGVuYW50KGNhbGxiYWNrKSB7XG4gICAgY29uc3QgdGVuYW50cyA9IGF3YWl0IHRoaXMubGlzdFRlbmFudHMoKVxuICAgIGNvbnN0IGl0ZXJhdG9yID0gbmV3IFRlbmFudEl0ZXJhdG9yKHtcbiAgICAgIGNvbmZpZ3VyYXRpb246IHRoaXMuY29uZmlndXJhdGlvbixcbiAgICAgIGlkZW50aWZpZXI6IHRoaXMuaWRlbnRpZmllcixcbiAgICAgIHBhcmFsbGVsQ291bnQ6IHRoaXMucGFyYWxsZWxDb3VudCgpXG4gICAgfSlcblxuICAgIHJldHVybiBhd2FpdCBpdGVyYXRvci5ydW4odGVuYW50cywgY2FsbGJhY2spXG4gIH1cblxuICAvKipcbiAgICogUnVucyBwYXJhbGxlbCBjb3VudC5cbiAgICogQHJldHVybnMge251bWJlcn0gLSBOdW1iZXIgb2YgdGVuYW50cyB0byBwcm9jZXNzIGNvbmN1cnJlbnRseS5cbiAgICovXG4gIHBhcmFsbGVsQ291bnQoKSB7XG4gICAgY29uc3QgcGFyc2VkUHJvY2Vzc0FyZ3MgPSB0aGlzLmNvbW1hbmQuYXJncz8ucGFyc2VkUHJvY2Vzc0FyZ3MgfHwge31cbiAgICBsZXQgcGFyYWxsZWxBcmcgPSBwYXJzZWRQcm9jZXNzQXJncy5wYXJhbGxlbFxuXG4gICAgaWYgKHBhcmFsbGVsQXJnID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IHBhcmFsbGVsQXJnSW5kZXggPSB0aGlzLmNvbW1hbmQucHJvY2Vzc0FyZ3M/LmluZGV4T2YoXCItLXBhcmFsbGVsXCIpID8/IC0xXG5cbiAgICAgIGlmIChwYXJhbGxlbEFyZ0luZGV4ID49IDApIHtcbiAgICAgICAgY29uc3QgbmV4dEFyZyA9IHRoaXMuY29tbWFuZC5wcm9jZXNzQXJncz8uW3BhcmFsbGVsQXJnSW5kZXggKyAxXVxuXG4gICAgICAgIHBhcmFsbGVsQXJnID0gbmV4dEFyZyAmJiAhbmV4dEFyZy5zdGFydHNXaXRoKFwiLVwiKSA/IG5leHRBcmcgOiB0cnVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHBhcmFsbGVsQXJnID09PSB1bmRlZmluZWQgfHwgcGFyYWxsZWxBcmcgPT09IGZhbHNlKSByZXR1cm4gMVxuICAgIGlmIChwYXJhbGxlbEFyZyA9PT0gdHJ1ZSkgcmV0dXJuIDIwXG5cbiAgICBjb25zdCBwYXJhbGxlbENvdW50ID0gTnVtYmVyKHBhcmFsbGVsQXJnKVxuXG4gICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKHBhcmFsbGVsQ291bnQpIHx8IHBhcmFsbGVsQ291bnQgPCAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYC0tcGFyYWxsZWwgbXVzdCBiZSBhIHBvc2l0aXZlIGludGVnZXIgd2hlbiBhIHZhbHVlIGlzIHByb3ZpZGVkOiAke3BhcmFsbGVsQXJnfWApXG4gICAgfVxuXG4gICAgcmV0dXJuIHBhcmFsbGVsQ291bnRcbiAgfVxufVxuIl19