velocious 1.0.695 → 1.0.696
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.
|
@@ -4,6 +4,11 @@ import {digg} from "diggerize"
|
|
|
4
4
|
import TableData from "./table-data/index.js"
|
|
5
5
|
|
|
6
6
|
const TABLE_NAME = "schema_migrations"
|
|
7
|
+
/** SQL Server "There is already an object named ..." — raised when two sessions race to create the same table. */
|
|
8
|
+
const MSSQL_OBJECT_ALREADY_EXISTS_ERROR_NUMBER = 2714
|
|
9
|
+
/** SQL Server deadlock victim — raised when concurrent sessions deadlock on the same catalog reads. */
|
|
10
|
+
const MSSQL_DEADLOCK_VICTIM_ERROR_NUMBER = 1205
|
|
11
|
+
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* Single owner of the `schema_migrations` ledger shape and the only place that reads
|
|
@@ -35,24 +40,107 @@ export default class MigrationsLedger {
|
|
|
35
40
|
return Boolean(table)
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Whether the error means a concurrent session already created the ledger table.
|
|
45
|
+
*
|
|
46
|
+
* Several processes bootstrap the same database at the same time (for example every
|
|
47
|
+
* CI test shard), and the MSSQL `IF NOT EXISTS (...) BEGIN CREATE TABLE ... END` guard
|
|
48
|
+
* is NOT atomic: two sessions can both evaluate the check as false and then both
|
|
49
|
+
* create, so the loser is rejected. Walks the wrapped-error cause chain because
|
|
50
|
+
* `db.query` re-wraps the driver's `RequestError` in a plain `Error`, and matches the
|
|
51
|
+
* stable numeric error number instead of the localized message text.
|
|
52
|
+
* @param {unknown} error - Error thrown while creating the ledger table.
|
|
53
|
+
* @returns {boolean} - True if the table was created by a concurrent session.
|
|
54
|
+
*/
|
|
55
|
+
static isConcurrentCreationError(error) {
|
|
56
|
+
let current = error
|
|
57
|
+
|
|
58
|
+
while (current instanceof Error) {
|
|
59
|
+
const errorNumber = /** @type {{number?: unknown}} */ (current).number
|
|
60
|
+
|
|
61
|
+
if (typeof errorNumber === "number" && errorNumber === MSSQL_OBJECT_ALREADY_EXISTS_ERROR_NUMBER) return true
|
|
62
|
+
|
|
63
|
+
current = current.cause
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether the error is a transient deadlock that should be retried.
|
|
71
|
+
*
|
|
72
|
+
* Concurrent boots also deadlock on the `INFORMATION_SCHEMA` metadata scan that
|
|
73
|
+
* `getTableByName` performs on SQL Server; the victim transaction rolled back cleanly,
|
|
74
|
+
* so retrying it is safe. Matched by numeric error number, never by message text.
|
|
75
|
+
* @param {unknown} error - Error thrown while inspecting or creating the ledger table.
|
|
76
|
+
* @returns {boolean} - True if the error is a retryable deadlock.
|
|
77
|
+
*/
|
|
78
|
+
static isDeadlockError(error) {
|
|
79
|
+
let current = error
|
|
80
|
+
|
|
81
|
+
while (current instanceof Error) {
|
|
82
|
+
const errorNumber = /** @type {{number?: unknown}} */ (current).number
|
|
83
|
+
|
|
84
|
+
if (typeof errorNumber === "number" && errorNumber === MSSQL_DEADLOCK_VICTIM_ERROR_NUMBER) return true
|
|
85
|
+
|
|
86
|
+
current = current.cause
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
38
92
|
/**
|
|
39
93
|
* Creates the ledger table if it does not exist. This is the single definition of
|
|
40
94
|
* the `schema_migrations` table shape.
|
|
95
|
+
*
|
|
96
|
+
* Safe under concurrency: a session that loses the create race (or deadlocks on the
|
|
97
|
+
* catalog read) converges on the existing table instead of failing its caller. That
|
|
98
|
+
* matters because this runs on every boot of a database configured for migrations
|
|
99
|
+
* (see `Migrator#createMigrationsTableForDatabase` and
|
|
100
|
+
* `migrateRequireContextForDatabase`), so a crash here takes down a whole test shard
|
|
101
|
+
* or deployment over a table that provably already exists.
|
|
41
102
|
* @param {import("./drivers/base.js").default} db - Database that should contain the ledger table.
|
|
103
|
+
* @param {{maxAttempts?: number}} [args] - Retry budget for transient catalog deadlocks.
|
|
42
104
|
* @returns {Promise<void>}
|
|
43
105
|
*/
|
|
44
|
-
static async ensureTable(db) {
|
|
45
|
-
|
|
106
|
+
static async ensureTable(db, {maxAttempts = 3} = {}) {
|
|
107
|
+
let attempt = 0
|
|
46
108
|
|
|
47
|
-
|
|
109
|
+
while (true) {
|
|
110
|
+
attempt++
|
|
48
111
|
|
|
49
|
-
|
|
112
|
+
try {
|
|
113
|
+
if (await MigrationsLedger.tableExists(db)) return
|
|
50
114
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
115
|
+
const tableData = new TableData(TABLE_NAME, {ifNotExists: true})
|
|
116
|
+
|
|
117
|
+
tableData.string("version", {null: false, primaryKey: true})
|
|
54
118
|
|
|
55
|
-
|
|
119
|
+
for (const sql of await db.createTableSql(tableData)) {
|
|
120
|
+
await db.query(sql)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
db.clearSchemaCache()
|
|
124
|
+
|
|
125
|
+
return
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (MigrationsLedger.isConcurrentCreationError(error)) {
|
|
128
|
+
// Another session created the ledger between our check and our create. The
|
|
129
|
+
// table exists with exactly this shape, so the goal state is already reached.
|
|
130
|
+
db.clearSchemaCache()
|
|
131
|
+
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (MigrationsLedger.isDeadlockError(error) && attempt < maxAttempts) {
|
|
136
|
+
db.clearSchemaCache()
|
|
137
|
+
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
throw error
|
|
142
|
+
}
|
|
143
|
+
}
|
|
56
144
|
}
|
|
57
145
|
|
|
58
146
|
/**
|
|
@@ -20,13 +20,46 @@ export default class MigrationsLedger {
|
|
|
20
20
|
* @returns {Promise<boolean>} - Whether the ledger table exists.
|
|
21
21
|
*/
|
|
22
22
|
static tableExists(db: import("./drivers/base.js").default): Promise<boolean>;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the error means a concurrent session already created the ledger table.
|
|
25
|
+
*
|
|
26
|
+
* Several processes bootstrap the same database at the same time (for example every
|
|
27
|
+
* CI test shard), and the MSSQL `IF NOT EXISTS (...) BEGIN CREATE TABLE ... END` guard
|
|
28
|
+
* is NOT atomic: two sessions can both evaluate the check as false and then both
|
|
29
|
+
* create, so the loser is rejected. Walks the wrapped-error cause chain because
|
|
30
|
+
* `db.query` re-wraps the driver's `RequestError` in a plain `Error`, and matches the
|
|
31
|
+
* stable numeric error number instead of the localized message text.
|
|
32
|
+
* @param {unknown} error - Error thrown while creating the ledger table.
|
|
33
|
+
* @returns {boolean} - True if the table was created by a concurrent session.
|
|
34
|
+
*/
|
|
35
|
+
static isConcurrentCreationError(error: unknown): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Whether the error is a transient deadlock that should be retried.
|
|
38
|
+
*
|
|
39
|
+
* Concurrent boots also deadlock on the `INFORMATION_SCHEMA` metadata scan that
|
|
40
|
+
* `getTableByName` performs on SQL Server; the victim transaction rolled back cleanly,
|
|
41
|
+
* so retrying it is safe. Matched by numeric error number, never by message text.
|
|
42
|
+
* @param {unknown} error - Error thrown while inspecting or creating the ledger table.
|
|
43
|
+
* @returns {boolean} - True if the error is a retryable deadlock.
|
|
44
|
+
*/
|
|
45
|
+
static isDeadlockError(error: unknown): boolean;
|
|
23
46
|
/**
|
|
24
47
|
* Creates the ledger table if it does not exist. This is the single definition of
|
|
25
48
|
* the `schema_migrations` table shape.
|
|
49
|
+
*
|
|
50
|
+
* Safe under concurrency: a session that loses the create race (or deadlocks on the
|
|
51
|
+
* catalog read) converges on the existing table instead of failing its caller. That
|
|
52
|
+
* matters because this runs on every boot of a database configured for migrations
|
|
53
|
+
* (see `Migrator#createMigrationsTableForDatabase` and
|
|
54
|
+
* `migrateRequireContextForDatabase`), so a crash here takes down a whole test shard
|
|
55
|
+
* or deployment over a table that provably already exists.
|
|
26
56
|
* @param {import("./drivers/base.js").default} db - Database that should contain the ledger table.
|
|
57
|
+
* @param {{maxAttempts?: number}} [args] - Retry budget for transient catalog deadlocks.
|
|
27
58
|
* @returns {Promise<void>}
|
|
28
59
|
*/
|
|
29
|
-
static ensureTable(db: import("./drivers/base.js").default
|
|
60
|
+
static ensureTable(db: import("./drivers/base.js").default, { maxAttempts }?: {
|
|
61
|
+
maxAttempts?: number;
|
|
62
|
+
}): Promise<void>;
|
|
30
63
|
/**
|
|
31
64
|
* Every applied migration version recorded in the ledger.
|
|
32
65
|
* @param {import("./drivers/base.js").default} db - Database whose applied versions are loaded.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations-ledger.d.ts","sourceRoot":"","sources":["../../../src/database/migrations-ledger.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrations-ledger.d.ts","sourceRoot":"","sources":["../../../src/database/migrations-ledger.js"],"names":[],"mappings":"AAYA;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC;;;OAGG;IACH,MAAM,CAAC,SAAS,IAFH,MAAM,CAIlB;IAED;;;;OAIG;IACH,OAAa,WAAW,CAAC,EAAE,EAHhB,OAAO,mBAAmB,EAAE,OAGZ,GAFd,OAAO,CAAC,OAAO,CAAC,CAM5B;IAED;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,yBAAyB,CAAC,KAAK,EAH3B,OAG2B,GAFzB,OAAO,CAcnB;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,eAAe,CAAC,KAAK,EAHjB,OAGiB,GAFf,OAAO,CAcnB;IAED;;;;;;;;;;;;;OAaG;IACH,OAAa,WAAW,CAAC,EAAE,EAJhB,OAAO,mBAAmB,EAAE,OAIZ,EAAE,EAAC,WAAe,EAAC,AAH3C,CACA,EADQ;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAGmB,GAFtC,OAAO,CAAC,IAAI,CAAC,CAwCzB;IAED;;;;OAIG;IACH,OAAa,eAAe,CAAC,EAAE,EAHpB,OAAO,mBAAmB,EAAE,OAGR,GAFlB,OAAO,CAAC,MAAM,EAAE,CAAC,CAM7B;IAED;;;;;OAKG;IACH,OAAa,UAAU,CAAC,EAAE,EAJf,OAAO,mBAAmB,EAAE,OAIb,EAAE,OAAO,EAHxB,MAGwB,GAFtB,OAAO,CAAC,OAAO,CAAC,CAS5B;IAED;;;;;;OAMG;IACH,OAAa,aAAa,CAAC,EAAE,EAJlB,OAAO,mBAAmB,EAAE,OAIV,EAAE,OAAO,EAH3B,MAG2B,GAFzB,OAAO,CAAC,IAAI,CAAC,CAMzB;IAED;;;;;OAKG;IACH,OAAa,aAAa,CAAC,EAAE,EAJlB,OAAO,mBAAmB,EAAE,OAIV,EAAE,OAAO,EAH3B,MAG2B,GAFzB,OAAO,CAAC,IAAI,CAAC,CAIzB;IAED;;;;;;;OAOG;IACH,OAAa,WAAW,CAAC,EAAE,EAJhB,OAAO,mBAAmB,EAAE,OAIZ,EAAE,QAAQ,EAH1B,MAAM,EAGoB,GAFxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmB7B;IAED;;;;;;;OAOG;IACH,OAAa,oBAAoB,CAAC,EAAC,QAAQ,EAAE,QAAQ,EAAC,EAH3C;QAAC,QAAQ,EAAE,OAAO,mBAAmB,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,mBAAmB,EAAE,OAAO,CAAA;KAGlD,GAFzC,OAAO,CAAC,MAAM,EAAE,CAAC,CAM7B;CACF"}
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
import { digg } from "diggerize";
|
|
3
3
|
import TableData from "./table-data/index.js";
|
|
4
4
|
const TABLE_NAME = "schema_migrations";
|
|
5
|
+
/** SQL Server "There is already an object named ..." — raised when two sessions race to create the same table. */
|
|
6
|
+
const MSSQL_OBJECT_ALREADY_EXISTS_ERROR_NUMBER = 2714;
|
|
7
|
+
/** SQL Server deadlock victim — raised when concurrent sessions deadlock on the same catalog reads. */
|
|
8
|
+
const MSSQL_DEADLOCK_VICTIM_ERROR_NUMBER = 1205;
|
|
5
9
|
/**
|
|
6
10
|
* Single owner of the `schema_migrations` ledger shape and the only place that reads
|
|
7
11
|
* or writes applied migration versions for a database connection. The migrator uses
|
|
@@ -29,21 +33,90 @@ export default class MigrationsLedger {
|
|
|
29
33
|
const table = await db.getTableByName(TABLE_NAME, { throwError: false });
|
|
30
34
|
return Boolean(table);
|
|
31
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether the error means a concurrent session already created the ledger table.
|
|
38
|
+
*
|
|
39
|
+
* Several processes bootstrap the same database at the same time (for example every
|
|
40
|
+
* CI test shard), and the MSSQL `IF NOT EXISTS (...) BEGIN CREATE TABLE ... END` guard
|
|
41
|
+
* is NOT atomic: two sessions can both evaluate the check as false and then both
|
|
42
|
+
* create, so the loser is rejected. Walks the wrapped-error cause chain because
|
|
43
|
+
* `db.query` re-wraps the driver's `RequestError` in a plain `Error`, and matches the
|
|
44
|
+
* stable numeric error number instead of the localized message text.
|
|
45
|
+
* @param {unknown} error - Error thrown while creating the ledger table.
|
|
46
|
+
* @returns {boolean} - True if the table was created by a concurrent session.
|
|
47
|
+
*/
|
|
48
|
+
static isConcurrentCreationError(error) {
|
|
49
|
+
let current = error;
|
|
50
|
+
while (current instanceof Error) {
|
|
51
|
+
const errorNumber = /** @type {{number?: unknown}} */ (current).number;
|
|
52
|
+
if (typeof errorNumber === "number" && errorNumber === MSSQL_OBJECT_ALREADY_EXISTS_ERROR_NUMBER)
|
|
53
|
+
return true;
|
|
54
|
+
current = current.cause;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Whether the error is a transient deadlock that should be retried.
|
|
60
|
+
*
|
|
61
|
+
* Concurrent boots also deadlock on the `INFORMATION_SCHEMA` metadata scan that
|
|
62
|
+
* `getTableByName` performs on SQL Server; the victim transaction rolled back cleanly,
|
|
63
|
+
* so retrying it is safe. Matched by numeric error number, never by message text.
|
|
64
|
+
* @param {unknown} error - Error thrown while inspecting or creating the ledger table.
|
|
65
|
+
* @returns {boolean} - True if the error is a retryable deadlock.
|
|
66
|
+
*/
|
|
67
|
+
static isDeadlockError(error) {
|
|
68
|
+
let current = error;
|
|
69
|
+
while (current instanceof Error) {
|
|
70
|
+
const errorNumber = /** @type {{number?: unknown}} */ (current).number;
|
|
71
|
+
if (typeof errorNumber === "number" && errorNumber === MSSQL_DEADLOCK_VICTIM_ERROR_NUMBER)
|
|
72
|
+
return true;
|
|
73
|
+
current = current.cause;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
32
77
|
/**
|
|
33
78
|
* Creates the ledger table if it does not exist. This is the single definition of
|
|
34
79
|
* the `schema_migrations` table shape.
|
|
80
|
+
*
|
|
81
|
+
* Safe under concurrency: a session that loses the create race (or deadlocks on the
|
|
82
|
+
* catalog read) converges on the existing table instead of failing its caller. That
|
|
83
|
+
* matters because this runs on every boot of a database configured for migrations
|
|
84
|
+
* (see `Migrator#createMigrationsTableForDatabase` and
|
|
85
|
+
* `migrateRequireContextForDatabase`), so a crash here takes down a whole test shard
|
|
86
|
+
* or deployment over a table that provably already exists.
|
|
35
87
|
* @param {import("./drivers/base.js").default} db - Database that should contain the ledger table.
|
|
88
|
+
* @param {{maxAttempts?: number}} [args] - Retry budget for transient catalog deadlocks.
|
|
36
89
|
* @returns {Promise<void>}
|
|
37
90
|
*/
|
|
38
|
-
static async ensureTable(db) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
91
|
+
static async ensureTable(db, { maxAttempts = 3 } = {}) {
|
|
92
|
+
let attempt = 0;
|
|
93
|
+
while (true) {
|
|
94
|
+
attempt++;
|
|
95
|
+
try {
|
|
96
|
+
if (await MigrationsLedger.tableExists(db))
|
|
97
|
+
return;
|
|
98
|
+
const tableData = new TableData(TABLE_NAME, { ifNotExists: true });
|
|
99
|
+
tableData.string("version", { null: false, primaryKey: true });
|
|
100
|
+
for (const sql of await db.createTableSql(tableData)) {
|
|
101
|
+
await db.query(sql);
|
|
102
|
+
}
|
|
103
|
+
db.clearSchemaCache();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (MigrationsLedger.isConcurrentCreationError(error)) {
|
|
108
|
+
// Another session created the ledger between our check and our create. The
|
|
109
|
+
// table exists with exactly this shape, so the goal state is already reached.
|
|
110
|
+
db.clearSchemaCache();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (MigrationsLedger.isDeadlockError(error) && attempt < maxAttempts) {
|
|
114
|
+
db.clearSchemaCache();
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
45
119
|
}
|
|
46
|
-
db.clearSchemaCache();
|
|
47
120
|
}
|
|
48
121
|
/**
|
|
49
122
|
* Every applied migration version recorded in the ledger.
|
|
@@ -123,4 +196,4 @@ export default class MigrationsLedger {
|
|
|
123
196
|
return await MigrationsLedger.markApplied(targetDb, sourceVersions);
|
|
124
197
|
}
|
|
125
198
|
}
|
|
126
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlncmF0aW9ucy1sZWRnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZGF0YWJhc2UvbWlncmF0aW9ucy1sZWRnZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sRUFBQyxJQUFJLEVBQUMsTUFBTSxXQUFXLENBQUE7QUFDOUIsT0FBTyxTQUFTLE1BQU0sdUJBQXVCLENBQUE7QUFFN0MsTUFBTSxVQUFVLEdBQUcsbUJBQW1CLENBQUE7QUFFdEM7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxnQkFBZ0I7SUFDbkM7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLFNBQVM7UUFDZCxPQUFPLFVBQVUsQ0FBQTtJQUNuQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLEVBQUU7UUFDekIsTUFBTSxLQUFLLEdBQUcsTUFBTSxFQUFFLENBQUMsY0FBYyxDQUFDLFVBQVUsRUFBRSxFQUFDLFVBQVUsRUFBRSxLQUFLLEVBQUMsQ0FBQyxDQUFBO1FBRXRFLE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQ3ZCLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLEVBQUU7UUFDekIsSUFBSSxNQUFNLGdCQUFnQixDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFBRSxPQUFNO1FBRWxELE1BQU0sU0FBUyxHQUFHLElBQUksU0FBUyxDQUFDLFVBQVUsRUFBRSxFQUFDLFdBQVcsRUFBRSxJQUFJLEVBQUMsQ0FBQyxDQUFBO1FBRWhFLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLEVBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFDLENBQUMsQ0FBQTtRQUU1RCxLQUFLLE1BQU0sR0FBRyxJQUFJLE1BQU0sRUFBRSxDQUFDLGNBQWMsQ0FBQyxTQUFT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QUMsQ0FBQTtJQUNyRSxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IHtkaWdnfSBmcm9tIFwiZGlnZ2VyaXplXCJcbmltcG9ydCBUYWJsZURhdGEgZnJvbSBcIi4vdGFibGUtZGF0YS9pbmRleC5qc1wiXG5cbmNvbnN0IFRBQkxFX05BTUUgPSBcInNjaGVtYV9taWdyYXRpb25zXCJcblxuLyoqXG4gKiBTaW5nbGUgb3duZXIgb2YgdGhlIGBzY2hlbWFfbWlncmF0aW9uc2AgbGVkZ2VyIHNoYXBlIGFuZCB0aGUgb25seSBwbGFjZSB0aGF0IHJlYWRzXG4gKiBvciB3cml0ZXMgYXBwbGllZCBtaWdyYXRpb24gdmVyc2lvbnMgZm9yIGEgZGF0YWJhc2UgY29ubmVjdGlvbi4gVGhlIG1pZ3JhdG9yIHVzZXNcbiAqIGl0IHRvIHJlY29yZCB2ZXJzaW9ucyBhcyBpdCBydW5zIG1pZ3JhdGlvbnM7IHByb3Zpc2lvbmluZyAvIHNjaGVtYS1jbG9uZSBwYXRocyB1c2VcbiAqIGBtYXJrQXBwbGllZGAgLyBgYmFzZWxpbmVGcm9tRGF0YWJhc2VgIHRvIHJlY29yZCB2ZXJzaW9ucyBhcyBhcHBsaWVkIFdJVEhPVVRcbiAqIHJlLXJ1bm5pbmcgdGhlbSAodGhlIFJhaWxzIGBzY2hlbWE6bG9hZGAgLyBGbHl3YXkgYGJhc2VsaW5lYCBpZGVhKS4gVGhhdCBrZWVwcyB0aGVcbiAqIGxlZGdlciBob25lc3Qgd2hlbiBhIGRhdGFiYXNlJ3Mgc2NoZW1hIHdhcyBhZHZhbmNlZCBvdXQgb2YgYmFuZCDigJQgZS5nLiBieSBjbG9uaW5nXG4gKiB0YWJsZSBzdHJ1Y3R1cmUgYmV0d2VlbiBkYXRhYmFzZXMg4oCUIHNvIHRoZSBtaWdyYXRvciBkb2VzIG5vdCBsYXRlciByZS1ydW4gYVxuICogbWlncmF0aW9uIHdob3NlIHNjaGVtYSBvYmplY3QgYWxyZWFkeSBleGlzdHMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE1pZ3JhdGlvbnNMZWRnZXIge1xuICAvKipcbiAgICogVGhlIGxlZGdlciB0YWJsZSBuYW1lLlxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSAtIExlZGdlciB0YWJsZSBuYW1lLlxuICAgKi9cbiAgc3RhdGljIHRhYmxlTmFtZSgpIHtcbiAgICByZXR1cm4gVEFCTEVfTkFNRVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIGxlZGdlciB0YWJsZSBleGlzdHMgb24gdGhlIGdpdmVuIGRhdGFiYXNlLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2Ugd2hvc2UgbWlncmF0aW9uIGxlZGdlciBpcyBpbnNwZWN0ZWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgdGhlIGxlZGdlciB0YWJsZSBleGlzdHMuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgdGFibGVFeGlzdHMoZGIpIHtcbiAgICBjb25zdCB0YWJsZSA9IGF3YWl0IGRiLmdldFRhYmxlQnlOYW1lKFRBQkxFX05BTUUsIHt0aHJvd0Vycm9yOiBmYWxzZX0pXG5cbiAgICByZXR1cm4gQm9vbGVhbih0YWJsZSlcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIHRoZSBsZWRnZXIgdGFibGUgaWYgaXQgZG9lcyBub3QgZXhpc3QuIFRoaXMgaXMgdGhlIHNpbmdsZSBkZWZpbml0aW9uIG9mXG4gICAqIHRoZSBgc2NoZW1hX21pZ3JhdGlvbnNgIHRhYmxlIHNoYXBlLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2UgdGhhdCBzaG91bGQgY29udGFpbiB0aGUgbGVkZ2VyIHRhYmxlLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIHN0YXRpYyBhc3luYyBlbnN1cmVUYWJsZShkYikge1xuICAgIGlmIChhd2FpdCBNaWdyYXRpb25zTGVkZ2VyLnRhYmxlRXhpc3RzKGRiKSkgcmV0dXJuXG5cbiAgICBjb25zdCB0YWJsZURhdGEgPSBuZXcgVGFibGVEYXRhKFRBQkxFX05BTUUsIHtpZk5vdEV4aXN0czogdHJ1ZX0pXG5cbiAgICB0YWJsZURhdGEuc3RyaW5nKFwidmVyc2lvblwiLCB7bnVsbDogZmFsc2UsIHByaW1hcnlLZXk6IHRydWV9KVxuXG4gICAgZm9yIChjb25zdCBzcWwgb2YgYXdhaXQgZGIuY3JlYXRlVGFibGVTcWwodGFibGVEYXRhKSkge1xuICAgICAgYXdhaXQgZGIucXVlcnkoc3FsKVxuICAgIH1cblxuICAgIGRiLmNsZWFyU2NoZW1hQ2FjaGUoKVxuICB9XG5cbiAgLyoqXG4gICAqIEV2ZXJ5IGFwcGxpZWQgbWlncmF0aW9uIHZlcnNpb24gcmVjb3JkZWQgaW4gdGhlIGxlZGdlci5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIERhdGFiYXNlIHdob3NlIGFwcGxpZWQgdmVyc2lvbnMgYXJlIGxvYWRlZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nW10+fSAtIEFwcGxpZWQgbWlncmF0aW9uIHZlcnNpb25zLlxuICAgKi9cbiAgc3RhdGljIGFzeW5jIGFwcGxpZWRWZXJzaW9ucyhkYikge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCBkYi5zZWxlY3QoVEFCTEVfTkFNRSlcblxuICAgIHJldHVybiByb3dzLm1hcCgocm93KSA9PiBgJHtkaWdnKHJvdywgXCJ2ZXJzaW9uXCIpfWApXG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciB0aGUgZ2l2ZW4gdmVyc2lvbiBpcyByZWNvcmRlZCBhcyBhcHBsaWVkLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2Ugd2hvc2UgbGVkZ2VyIGlzIHF1ZXJpZWQuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB2ZXJzaW9uIC0gTWlncmF0aW9uIHZlcnNpb24gdG8gbG9vayB1cC5cbiAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IC0gV2hldGhlciB0aGUgbWlncmF0aW9uIHZlcnNpb24gaXMgYXBwbGllZC5cbiAgICovXG4gIHN0YXRpYyBhc3luYyBoYXNWZXJzaW9uKGRiLCB2ZXJzaW9uKSB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IGRiLm5ld1F1ZXJ5KClcbiAgICAgIC5mcm9tKFRBQkxFX05BTUUpXG4gICAgICAud2hlcmUoe3ZlcnNpb259KVxuICAgICAgLnJlc3VsdHMoKVxuXG4gICAgcmV0dXJuIHJvd3MubGVuZ3RoID4gMFxuICB9XG5cbiAgLyoqXG4gICAqIFJlY29yZHMgYSBzaW5nbGUgdmVyc2lvbiBhcyBhcHBsaWVkLiBUaGUgdGFyZ2V0ZWQgZXhpc3RlbmNlIGNoZWNrIGtlZXBzIHRoZVxuICAgKiBtaWdyYXRvcidzIHBlci1taWdyYXRpb24gaG90IHBhdGggY2hlYXAgKG5vIGZ1bGwtdGFibGUgbG9hZCBwZXIgbWlncmF0aW9uKS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIERhdGFiYXNlIHdob3NlIGxlZGdlciByZWNlaXZlcyB0aGUgdmVyc2lvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHZlcnNpb24gLSBNaWdyYXRpb24gdmVyc2lvbiB0byByZWNvcmQgYXMgYXBwbGllZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBzdGF0aWMgYXN5bmMgcmVjb3JkVmVyc2lvbihkYiwgdmVyc2lvbikge1xuICAgIGlmIChhd2FpdCBNaWdyYXRpb25zTGVkZ2VyLmhhc1ZlcnNpb24oZGIsIHZlcnNpb24pKSByZXR1cm5cblxuICAgIGF3YWl0IGRiLmluc2VydCh7dGFibGVOYW1lOiBUQUJMRV9OQU1FLCBkYXRhOiB7dmVyc2lvbn19KVxuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSB2ZXJzaW9uIGZyb20gdGhlIGxlZGdlciAodXNlZCB3aGVuIG1pZ3JhdGluZyBkb3duKS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBkYiAtIERhdGFiYXNlIHdob3NlIGxlZGdlciBsb3NlcyB0aGUgdmVyc2lvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHZlcnNpb24gLSBNaWdyYXRpb24gdmVyc2lvbiB0byBtYXJrIGFzIHVuYXBwbGllZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBzdGF0aWMgYXN5bmMgcmVtb3ZlVmVyc2lvbihkYiwgdmVyc2lvbikge1xuICAgIGF3YWl0IGRiLmRlbGV0ZSh7dGFibGVOYW1lOiBUQUJMRV9OQU1FLCBjb25kaXRpb25zOiB7dmVyc2lvbn19KVxuICB9XG5cbiAgLyoqXG4gICAqIEJhc2VsaW5lcyBhIGRhdGFiYXNlJ3MgbGVkZ2VyOiByZWNvcmRzIGVhY2ggdmVyc2lvbiBhcyBhcHBsaWVkIHdpdGhvdXQgcnVubmluZ1xuICAgKiBpdHMgbWlncmF0aW9uLiBJZGVtcG90ZW50IOKAlCBhbHJlYWR5LXJlY29yZGVkIHZlcnNpb25zIGFyZSBza2lwcGVkLiBFbnN1cmVzIHRoZVxuICAgKiBsZWRnZXIgdGFibGUgZXhpc3RzIGZpcnN0LCB0aGVuIGxvYWRzIHRoZSBleGlzdGluZyBzZXQgb25jZSBmb3IgdGhlIHdob2xlIGJhdGNoLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2Ugd2hvc2UgbGVkZ2VyIHNob3VsZCBiZSBiYXNlbGluZWQuXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IHZlcnNpb25zIC0gTWlncmF0aW9uIHZlcnNpb25zIHRvIHJlY29yZCB3aXRob3V0IHJ1bm5pbmcgdGhlbS5cbiAgICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nW10+fSBUaGUgdmVyc2lvbnMgdGhhdCB3ZXJlIG5ld2x5IHJlY29yZGVkLlxuICAgKi9cbiAgc3RhdGljIGFzeW5jIG1hcmtBcHBsaWVkKGRiLCB2ZXJzaW9ucykge1xuICAgIGF3YWl0IE1pZ3JhdGlvbnNMZWRnZXIuZW5zdXJlVGFibGUoZGIpXG5cbiAgICBjb25zdCBleGlzdGluZyA9IG5ldyBTZXQoYXdhaXQgTWlncmF0aW9uc0xlZGdlci5hcHBsaWVkVmVyc2lvbnMoZGIpKVxuICAgIGNvbnN0IHJlY29yZGVkID0gW11cblxuICAgIGZvciAoY29uc3QgdmVyc2lvbiBvZiB2ZXJzaW9ucykge1xuICAgICAgY29uc3Qgbm9ybWFsaXplZFZlcnNpb24gPSBgJHt2ZXJzaW9ufWBcblxuICAgICAgaWYgKGV4aXN0aW5nLmhhcyhub3JtYWxpemVkVmVyc2lvbikpIGNvbnRpbnVlXG5cbiAgICAgIGF3YWl0IGRiLmluc2VydCh7dGFibGVOYW1lOiBUQUJMRV9OQU1FLCBkYXRhOiB7dmVyc2lvbjogbm9ybWFsaXplZFZlcnNpb259fSlcbiAgICAgIGV4aXN0aW5nLmFkZChub3JtYWxpemVkVmVyc2lvbilcbiAgICAgIHJlY29yZGVkLnB1c2gobm9ybWFsaXplZFZlcnNpb24pXG4gICAgfVxuXG4gICAgcmV0dXJuIHJlY29yZGVkXG4gIH1cblxuICAvKipcbiAgICogQmFzZWxpbmVzIGB0YXJnZXREYmAgdG8gbWF0Y2ggdGhlIGFwcGxpZWQgdmVyc2lvbnMgb2YgYHNvdXJjZURiYC4gVXNlIHdoZW4gYVxuICAgKiBwcm92aXNpb25pbmcgcGF0aCBhZHZhbmNlZCBgdGFyZ2V0RGJgJ3Mgc2NoZW1hIHRvIG1hdGNoIGBzb3VyY2VEYmAgb3V0IG9mIGJhbmRcbiAgICogKGUuZy4gY2xvbmluZyB0YWJsZSBzdHJ1Y3R1cmUgYmV0d2VlbiBkYXRhYmFzZXMpOiB0aGUgbWlncmF0aW9ucyBhcmUsIGJ5XG4gICAqIGNvbnN0cnVjdGlvbiwgYWxyZWFkeSBhcHBsaWVkIG9uIHRoZSB0YXJnZXQsIHNvIHJlY29yZCB0aGVtIHdpdGhvdXQgcmUtcnVubmluZy5cbiAgICogQHBhcmFtIHt7c291cmNlRGI6IGltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHQsIHRhcmdldERiOiBpbXBvcnQoXCIuL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fX0gYXJncyAtIFNvdXJjZSBsZWRnZXIgYW5kIHRhcmdldCBkYXRhYmFzZSB0byBiYXNlbGluZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nW10+fSBUaGUgdmVyc2lvbnMgdGhhdCB3ZXJlIG5ld2x5IHJlY29yZGVkIG9uIHRoZSB0YXJnZXQuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgYmFzZWxpbmVGcm9tRGF0YWJhc2Uoe3NvdXJjZURiLCB0YXJnZXREYn0pIHtcbiAgICBjb25zdCBzb3VyY2VWZXJzaW9ucyA9IGF3YWl0IE1pZ3JhdGlvbnNMZWRnZXIuYXBwbGllZFZlcnNpb25zKHNvdXJjZURiKVxuXG4gICAgcmV0dXJuIGF3YWl0IE1pZ3JhdGlvbnNMZWRnZXIubWFya0FwcGxpZWQodGFyZ2V0RGIsIHNvdXJjZVZlcnNpb25zKVxuICB9XG59XG4iXX0=
|
|
199
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlncmF0aW9ucy1sZWRnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZGF0YWJhc2UvbWlncmF0aW9ucy1sZWRnZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sRUFBQyxJQUFJLEVBQUMsTUFBTSxXQUFXLENBQUE7QUFDOUIsT0FBTyxTQUFTLE1BQU0sdUJBQXVCLENBQUE7QUFFN0MsTUFBTSxVQUFVLEdBQUcsbUJBQW1CLENBQUE7QUFDdEMsa0hBQWtIO0FBQ2xILE1BQU0sd0NBQXdDLEdBQUcsSUFBSSxDQUFBO0FBQ3JELHVHQUF1RztBQUN2RyxNQUFNLGtDQUFrQyxHQUFHLElBQUksQ0FBQTtBQUcvQzs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLENBQUMsT0FBTyxPQUFPLGdCQUFnQjtJQUNuQzs7O09BR0c7SUFDSCxNQUFNLENBQUMsU0FBUztRQUNkLE9BQU8sVUFBVSxDQUFBO0lBQ25CLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsRUFBRTtRQUN6QixNQUFNLEtBQUssR0FBRyxNQUFNLEVBQUUsQ0FBQyxjQUFjLENBQUMsVUFBVSxFQUFFLEVBQUMsVUFBVSxFQUFFLEtBQUssRUFBQyxDQUFDLENBQUE7UUFFdEUsT0FBTyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDdkIsQ0FBQztJQUVEOzs7Ozs7Ozs7OztPQVdHO0lBQ0gsTUFBTSxDQUFDLHlCQUF5QixDQUFDLEtBQUs7UUFDcEMsSUFBSSxPQUFPLEdBQUcsS0FBSyxDQUFBO1FBRW5CLE9BQU8sT0FBTyxZQUFZLEtBQUssRUFBRSxDQUFDO1lBQ2hDLE1BQU0sV0FBVyxHQUFHLGlDQUFpQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxDQUFBO1lBRXRFLElBQUksT0FBTyxXQUFXLEtBQUssUUFBUSxJQUFJLFdBQVcsS0FBSyx3Q0FBd0M7Z0JBQUUsT0FBTyxJ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RUFBRSxpQkFBaUIsRUFBQyxFQUFDLENBQUMsQ0FBQTtZQUM1RSxRQUFRLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFDLENBQUE7WUFDL0IsUUFBUSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFBO1FBQ2xDLENBQUM7UUFFRCxPQUFPLFFBQVEsQ0FBQTtJQUNqQixDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsb0JBQW9CLENBQUMsRUFBQyxRQUFRLEVBQUUsUUFBUSxFQUFDO1FBQ3BELE1BQU0sY0FBYyxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsZUFBZSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBRXZFLE9BQU8sTUFBTSxnQkFBZ0IsQ0FBQyxXQUFXLENBQUMsUUFBUSxFQUFFLGNBQWMsQ0FBQyxDQUFBO0lBQ3JFLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQge2RpZ2d9IGZyb20gXCJkaWdnZXJpemVcIlxuaW1wb3J0IFRhYmxlRGF0YSBmcm9tIFwiLi90YWJsZS1kYXRhL2luZGV4LmpzXCJcblxuY29uc3QgVEFCTEVfTkFNRSA9IFwic2NoZW1hX21pZ3JhdGlvbnNcIlxuLyoqIFNRTCBTZXJ2ZXIgXCJUaGVyZSBpcyBhbHJlYWR5IGFuIG9iamVjdCBuYW1lZCAuLi5cIiDigJQgcmFpc2VkIHdoZW4gdHdvIHNlc3Npb25zIHJhY2UgdG8gY3JlYXRlIHRoZSBzYW1lIHRhYmxlLiAqL1xuY29uc3QgTVNTUUxfT0JKRUNUX0FMUkVBRFlfRVhJU1RTX0VSUk9SX05VTUJFUiA9IDI3MTRcbi8qKiBTUUwgU2VydmVyIGRlYWRsb2NrIHZpY3RpbSDigJQgcmFpc2VkIHdoZW4gY29uY3VycmVudCBzZXNzaW9ucyBkZWFkbG9jayBvbiB0aGUgc2FtZSBjYXRhbG9nIHJlYWRzLiAqL1xuY29uc3QgTVNTUUxfREVBRExPQ0tfVklDVElNX0VSUk9SX05VTUJFUiA9IDEyMDVcblxuXG4vKipcbiAqIFNpbmdsZSBvd25lciBvZiB0aGUgYHNjaGVtYV9taWdyYXRpb25zYCBsZWRnZXIgc2hhcGUgYW5kIHRoZSBvbmx5IHBsYWNlIHRoYXQgcmVhZHNcbiAqIG9yIHdyaXRlcyBhcHBsaWVkIG1pZ3JhdGlvbiB2ZXJzaW9ucyBmb3IgYSBkYXRhYmFzZSBjb25uZWN0aW9uLiBUaGUgbWlncmF0b3IgdXNlc1xuICogaXQgdG8gcmVjb3JkIHZlcnNpb25zIGFzIGl0IHJ1bnMgbWlncmF0aW9uczsgcHJvdmlzaW9uaW5nIC8gc2NoZW1hLWNsb25lIHBhdGhzIHVzZVxuICogYG1hcmtBcHBsaWVkYCAvIGBiYXNlbGluZUZyb21EYXRhYmFzZWAgdG8gcmVjb3JkIHZlcnNpb25zIGFzIGFwcGxpZWQgV0lUSE9VVFxuICogcmUtcnVubmluZyB0aGVtICh0aGUgUmFpbHMgYHNjaGVtYTpsb2FkYCAvIEZseXdheSBgYmFzZWxpbmVgIGlkZWEpLiBUaGF0IGtlZXBzIHRoZVxuICogbGVkZ2VyIGhvbmVzdCB3aGVuIGEgZGF0YWJhc2UncyBzY2hlbWEgd2FzIGFkdmFuY2VkIG91dCBvZiBiYW5kIOKAlCBlLmcuIGJ5IGNsb25pbmdcbiAqIHRhYmxlIHN0cnVjdHVyZSBiZXR3ZWVuIGRhdGFiYXNlcyDigJQgc28gdGhlIG1pZ3JhdG9yIGRvZXMgbm90IGxhdGVyIHJlLXJ1biBhXG4gKiBtaWdyYXRpb24gd2hvc2Ugc2NoZW1hIG9iamVjdCBhbHJlYWR5IGV4aXN0cy5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgTWlncmF0aW9uc0xlZGdlciB7XG4gIC8qKlxuICAgKiBUaGUgbGVkZ2VyIHRhYmxlIG5hbWUuXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IC0gTGVkZ2VyIHRhYmxlIG5hbWUuXG4gICAqL1xuICBzdGF0aWMgdGFibGVOYW1lKCkge1xuICAgIHJldHVybiBUQUJMRV9OQU1FXG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciB0aGUgbGVkZ2VyIHRhYmxlIGV4aXN0cyBvbiB0aGUgZ2l2ZW4gZGF0YWJhc2UuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSB3aG9zZSBtaWdyYXRpb24gbGVkZ2VyIGlzIGluc3BlY3RlZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IC0gV2hldGhlciB0aGUgbGVkZ2VyIHRhYmxlIGV4aXN0cy5cbiAgICovXG4gIHN0YXRpYyBhc3luYyB0YWJsZUV4aXN0cyhkYikge1xuICAgIGNvbnN0IHRhYmxlID0gYXdhaXQgZGIuZ2V0VGFibGVCeU5hbWUoVEFCTEVfTkFNRSwge3Rocm93RXJyb3I6IGZhbHNlfSlcblxuICAgIHJldHVybiBCb29sZWFuKHRhYmxlKVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIGVycm9yIG1lYW5zIGEgY29uY3VycmVudCBzZXNzaW9uIGFscmVhZHkgY3JlYXRlZCB0aGUgbGVkZ2VyIHRhYmxlLlxuICAgKlxuICAgKiBTZXZlcmFsIHByb2Nlc3NlcyBib290c3RyYXAgdGhlIHNhbWUgZGF0YWJhc2UgYXQgdGhlIHNhbWUgdGltZSAoZm9yIGV4YW1wbGUgZXZlcnlcbiAgICogQ0kgdGVzdCBzaGFyZCksIGFuZCB0aGUgTVNTUUwgYElGIE5PVCBFWElTVFMgKC4uLikgQkVHSU4gQ1JFQVRFIFRBQkxFIC4uLiBFTkRgIGd1YXJkXG4gICAqIGlzIE5PVCBhdG9taWM6IHR3byBzZXNzaW9ucyBjYW4gYm90aCBldmFsdWF0ZSB0aGUgY2hlY2sgYXMgZmFsc2UgYW5kIHRoZW4gYm90aFxuICAgKiBjcmVhdGUsIHNvIHRoZSBsb3NlciBpcyByZWplY3RlZC4gV2Fsa3MgdGhlIHdyYXBwZWQtZXJyb3IgY2F1c2UgY2hhaW4gYmVjYXVzZVxuICAgKiBgZGIucXVlcnlgIHJlLXdyYXBzIHRoZSBkcml2ZXIncyBgUmVxdWVzdEVycm9yYCBpbiBhIHBsYWluIGBFcnJvcmAsIGFuZCBtYXRjaGVzIHRoZVxuICAgKiBzdGFibGUgbnVtZXJpYyBlcnJvciBudW1iZXIgaW5zdGVhZCBvZiB0aGUgbG9jYWxpemVkIG1lc3NhZ2UgdGV4dC5cbiAgICogQHBhcmFtIHt1bmtub3dufSBlcnJvciAtIEVycm9yIHRocm93biB3aGlsZSBjcmVhdGluZyB0aGUgbGVkZ2VyIHRhYmxlLlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSB0YWJsZSB3YXMgY3JlYXRlZCBieSBhIGNvbmN1cnJlbnQgc2Vzc2lvbi5cbiAgICovXG4gIHN0YXRpYyBpc0NvbmN1cnJlbnRDcmVhdGlvbkVycm9yKGVycm9yKSB7XG4gICAgbGV0IGN1cnJlbnQgPSBlcnJvclxuXG4gICAgd2hpbGUgKGN1cnJlbnQgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgY29uc3QgZXJyb3JOdW1iZXIgPSAvKiogQHR5cGUge3tudW1iZXI/OiB1bmtub3dufX0gKi8gKGN1cnJlbnQpLm51bWJlclxuXG4gICAgICBpZiAodHlwZW9mIGVycm9yTnVtYmVyID09PSBcIm51bWJlclwiICYmIGVycm9yTnVtYmVyID09PSBNU1NRTF9PQkpFQ1RfQUxSRUFEWV9FWElTVFNfRVJST1JfTlVNQkVSKSByZXR1cm4gdHJ1ZVxuXG4gICAgICBjdXJyZW50ID0gY3VycmVudC5jYXVzZVxuICAgIH1cblxuICAgIHJldHVybiBmYWxzZVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIGVycm9yIGlzIGEgdHJhbnNpZW50IGRlYWRsb2NrIHRoYXQgc2hvdWxkIGJlIHJldHJpZWQuXG4gICAqXG4gICAqIENvbmN1cnJlbnQgYm9vdHMgYWxzbyBkZWFkbG9jayBvbiB0aGUgYElORk9STUFUSU9OX1NDSEVNQWAgbWV0YWRhdGEgc2NhbiB0aGF0XG4gICAqIGBnZXRUYWJsZUJ5TmFtZWAgcGVyZm9ybXMgb24gU1FMIFNlcnZlcjsgdGhlIHZpY3RpbSB0cmFuc2FjdGlvbiByb2xsZWQgYmFjayBjbGVhbmx5LFxuICAgKiBzbyByZXRyeWluZyBpdCBpcyBzYWZlLiBNYXRjaGVkIGJ5IG51bWVyaWMgZXJyb3IgbnVtYmVyLCBuZXZlciBieSBtZXNzYWdlIHRleHQuXG4gICAqIEBwYXJhbSB7dW5rbm93bn0gZXJyb3IgLSBFcnJvciB0aHJvd24gd2hpbGUgaW5zcGVjdGluZyBvciBjcmVhdGluZyB0aGUgbGVkZ2VyIHRhYmxlLlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBlcnJvciBpcyBhIHJldHJ5YWJsZSBkZWFkbG9jay5cbiAgICovXG4gIHN0YXRpYyBpc0RlYWRsb2NrRXJyb3IoZXJyb3IpIHtcbiAgICBsZXQgY3VycmVudCA9IGVycm9yXG5cbiAgICB3aGlsZSAoY3VycmVudCBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICBjb25zdCBlcnJvck51bWJlciA9IC8qKiBAdHlwZSB7e251bWJlcj86IHVua25vd259fSAqLyAoY3VycmVudCkubnVtYmVyXG5cbiAgICAgIGlmICh0eXBlb2YgZXJyb3JOdW1iZXIgPT09IFwibnVtYmVyXCIgJiYgZXJyb3JOdW1iZXIgPT09IE1TU1FMX0RFQURMT0NLX1ZJQ1RJTV9FUlJPUl9OVU1CRVIpIHJldHVybiB0cnVlXG5cbiAgICAgIGN1cnJlbnQgPSBjdXJyZW50LmNhdXNlXG4gICAgfVxuXG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyB0aGUgbGVkZ2VyIHRhYmxlIGlmIGl0IGRvZXMgbm90IGV4aXN0LiBUaGlzIGlzIHRoZSBzaW5nbGUgZGVmaW5pdGlvbiBvZlxuICAgKiB0aGUgYHNjaGVtYV9taWdyYXRpb25zYCB0YWJsZSBzaGFwZS5cbiAgICpcbiAgICogU2FmZSB1bmRlciBjb25jdXJyZW5jeTogYSBzZXNzaW9uIHRoYXQgbG9zZXMgdGhlIGNyZWF0ZSByYWNlIChvciBkZWFkbG9ja3Mgb24gdGhlXG4gICAqIGNhdGFsb2cgcmVhZCkgY29udmVyZ2VzIG9uIHRoZSBleGlzdGluZyB0YWJsZSBpbnN0ZWFkIG9mIGZhaWxpbmcgaXRzIGNhbGxlci4gVGhhdFxuICAgKiBtYXR0ZXJzIGJlY2F1c2UgdGhpcyBydW5zIG9uIGV2ZXJ5IGJvb3Qgb2YgYSBkYXRhYmFzZSBjb25maWd1cmVkIGZvciBtaWdyYXRpb25zXG4gICAqIChzZWUgYE1pZ3JhdG9yI2NyZWF0ZU1pZ3JhdGlvbnNUYWJsZUZvckRhdGFiYXNlYCBhbmRcbiAgICogYG1pZ3JhdGVSZXF1aXJlQ29udGV4dEZvckRhdGFiYXNlYCksIHNvIGEgY3Jhc2ggaGVyZSB0YWtlcyBkb3duIGEgd2hvbGUgdGVzdCBzaGFyZFxuICAgKiBvciBkZXBsb3ltZW50IG92ZXIgYSB0YWJsZSB0aGF0IHByb3ZhYmx5IGFscmVhZHkgZXhpc3RzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2UgdGhhdCBzaG91bGQgY29udGFpbiB0aGUgbGVkZ2VyIHRhYmxlLlxuICAgKiBAcGFyYW0ge3ttYXhBdHRlbXB0cz86IG51bWJlcn19IFthcmdzXSAtIFJldHJ5IGJ1ZGdldCBmb3IgdHJhbnNpZW50IGNhdGFsb2cgZGVhZGxvY2tzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIHN0YXRpYyBhc3luYyBlbnN1cmVUYWJsZShkYiwge21heEF0dGVtcHRzID0gM30gPSB7fSkge1xuICAgIGxldCBhdHRlbXB0ID0gMFxuXG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIGF0dGVtcHQrK1xuXG4gICAgICB0cnkge1xuICAgICAgICBpZiAoYXdhaXQgTWlncmF0aW9uc0xlZGdlci50YWJsZUV4aXN0cyhkYikpIHJldHVyblxuXG4gICAgICAgIGNvbnN0IHRhYmxlRGF0YSA9IG5ldyBUYWJsZURhdGEoVEFCTEVfTkFNRSwge2lmTm90RXhpc3RzOiB0cnVlfSlcblxuICAgICAgICB0YWJsZURhdGEuc3RyaW5nKFwidmVyc2lvblwiLCB7bnVsbDogZmFsc2UsIHByaW1hcnlLZXk6IHRydWV9KVxuXG4gICAgICAgIGZvciAoY29uc3Qgc3FsIG9mIGF3YWl0IGRiLmNyZWF0ZVRhYmxlU3FsKHRhYmxlRGF0YSkpIHtcbiAgICAgICAgICBhd2FpdCBkYi5xdWVyeShzcWwpXG4gICAgICAgIH1cblxuICAgICAgICBkYi5jbGVhclNjaGVtYUNhY2hlKClcblxuICAgICAgICByZXR1cm5cbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGlmIChNaWdyYXRpb25zTGVkZ2VyLmlzQ29uY3VycmVudENyZWF0aW9uRXJyb3IoZXJyb3IpKSB7XG4gICAgICAgICAgLy8gQW5vdGhlciBzZXNzaW9uIGNyZWF0ZWQgdGhlIGxlZGdlciBiZXR3ZWVuIG91ciBjaGVjayBhbmQgb3VyIGNyZWF0ZS4gVGhlXG4gICAgICAgICAgLy8gdGFibGUgZXhpc3RzIHdpdGggZXhhY3RseSB0aGlzIHNoYXBlLCBzbyB0aGUgZ29hbCBzdGF0ZSBpcyBhbHJlYWR5IHJlYWNoZWQuXG4gICAgICAgICAgZGIuY2xlYXJTY2hlbWFDYWNoZSgpXG5cbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChNaWdyYXRpb25zTGVkZ2VyLmlzRGVhZGxvY2tFcnJvcihlcnJvcikgJiYgYXR0ZW1wdCA8IG1heEF0dGVtcHRzKSB7XG4gICAgICAgICAgZGIuY2xlYXJTY2hlbWFDYWNoZSgpXG5cbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9XG5cbiAgICAgICAgdGhyb3cgZXJyb3JcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRXZlcnkgYXBwbGllZCBtaWdyYXRpb24gdmVyc2lvbiByZWNvcmRlZCBpbiB0aGUgbGVkZ2VyLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2Ugd2hvc2UgYXBwbGllZCB2ZXJzaW9ucyBhcmUgbG9hZGVkLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmdbXT59IC0gQXBwbGllZCBtaWdyYXRpb24gdmVyc2lvbnMuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgYXBwbGllZFZlcnNpb25zKGRiKSB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IGRiLnNlbGVjdChUQUJMRV9OQU1FKVxuXG4gICAgcmV0dXJuIHJvd3MubWFwKChyb3cpID0+IGAke2RpZ2cocm93LCBcInZlcnNpb25cIil9YClcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRoZSBnaXZlbiB2ZXJzaW9uIGlzIHJlY29yZGVkIGFzIGFwcGxpZWQuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSB3aG9zZSBsZWRnZXIgaXMgcXVlcmllZC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHZlcnNpb24gLSBNaWdyYXRpb24gdmVyc2lvbiB0byBsb29rIHVwLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gLSBXaGV0aGVyIHRoZSBtaWdyYXRpb24gdmVyc2lvbiBpcyBhcHBsaWVkLlxuICAgKi9cbiAgc3RhdGljIGFzeW5jIGhhc1ZlcnNpb24oZGIsIHZlcnNpb24pIHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgZGIubmV3UXVlcnkoKVxuICAgICAgLmZyb20oVEFCTEVfTkFNRSlcbiAgICAgIC53aGVyZSh7dmVyc2lvbn0pXG4gICAgICAucmVzdWx0cygpXG5cbiAgICByZXR1cm4gcm93cy5sZW5ndGggPiAwXG4gIH1cblxuICAvKipcbiAgICogUmVjb3JkcyBhIHNpbmdsZSB2ZXJzaW9uIGFzIGFwcGxpZWQuIFRoZSB0YXJnZXRlZCBleGlzdGVuY2UgY2hlY2sga2VlcHMgdGhlXG4gICAqIG1pZ3JhdG9yJ3MgcGVyLW1pZ3JhdGlvbiBob3QgcGF0aCBjaGVhcCAobm8gZnVsbC10YWJsZSBsb2FkIHBlciBtaWdyYXRpb24pLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2Ugd2hvc2UgbGVkZ2VyIHJlY2VpdmVzIHRoZSB2ZXJzaW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmVyc2lvbiAtIE1pZ3JhdGlvbiB2ZXJzaW9uIHRvIHJlY29yZCBhcyBhcHBsaWVkLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIHN0YXRpYyBhc3luYyByZWNvcmRWZXJzaW9uKGRiLCB2ZXJzaW9uKSB7XG4gICAgaWYgKGF3YWl0IE1pZ3JhdGlvbnNMZWRnZXIuaGFzVmVyc2lvbihkYiwgdmVyc2lvbikpIHJldHVyblxuXG4gICAgYXdhaXQgZGIuaW5zZXJ0KHt0YWJsZU5hbWU6IFRBQkxFX05BTUUsIGRhdGE6IHt2ZXJzaW9ufX0pXG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHZlcnNpb24gZnJvbSB0aGUgbGVkZ2VyICh1c2VkIHdoZW4gbWlncmF0aW5nIGRvd24pLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9IGRiIC0gRGF0YWJhc2Ugd2hvc2UgbGVkZ2VyIGxvc2VzIHRoZSB2ZXJzaW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmVyc2lvbiAtIE1pZ3JhdGlvbiB2ZXJzaW9uIHRvIG1hcmsgYXMgdW5hcHBsaWVkLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIHN0YXRpYyBhc3luYyByZW1vdmVWZXJzaW9uKGRiLCB2ZXJzaW9uKSB7XG4gICAgYXdhaXQgZGIuZGVsZXRlKHt0YWJsZU5hbWU6IFRBQkxFX05BTUUsIGNvbmRpdGlvbnM6IHt2ZXJzaW9ufX0pXG4gIH1cblxuICAvKipcbiAgICogQmFzZWxpbmVzIGEgZGF0YWJhc2UncyBsZWRnZXI6IHJlY29yZHMgZWFjaCB2ZXJzaW9uIGFzIGFwcGxpZWQgd2l0aG91dCBydW5uaW5nXG4gICAqIGl0cyBtaWdyYXRpb24uIElkZW1wb3RlbnQg4oCUIGFscmVhZHktcmVjb3JkZWQgdmVyc2lvbnMgYXJlIHNraXBwZWQuIEVuc3VyZXMgdGhlXG4gICAqIGxlZGdlciB0YWJsZSBleGlzdHMgZmlyc3QsIHRoZW4gbG9hZHMgdGhlIGV4aXN0aW5nIHNldCBvbmNlIGZvciB0aGUgd2hvbGUgYmF0Y2guXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gZGIgLSBEYXRhYmFzZSB3aG9zZSBsZWRnZXIgc2hvdWxkIGJlIGJhc2VsaW5lZC5cbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gdmVyc2lvbnMgLSBNaWdyYXRpb24gdmVyc2lvbnMgdG8gcmVjb3JkIHdpdGhvdXQgcnVubmluZyB0aGVtLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmdbXT59IFRoZSB2ZXJzaW9ucyB0aGF0IHdlcmUgbmV3bHkgcmVjb3JkZWQuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgbWFya0FwcGxpZWQoZGIsIHZlcnNpb25zKSB7XG4gICAgYXdhaXQgTWlncmF0aW9uc0xlZGdlci5lbnN1cmVUYWJsZShkYilcblxuICAgIGNvbnN0IGV4aXN0aW5nID0gbmV3IFNldChhd2FpdCBNaWdyYXRpb25zTGVkZ2VyLmFwcGxpZWRWZXJzaW9ucyhkYikpXG4gICAgY29uc3QgcmVjb3JkZWQgPSBbXVxuXG4gICAgZm9yIChjb25zdCB2ZXJzaW9uIG9mIHZlcnNpb25zKSB7XG4gICAgICBjb25zdCBub3JtYWxpemVkVmVyc2lvbiA9IGAke3ZlcnNpb259YFxuXG4gICAgICBpZiAoZXhpc3RpbmcuaGFzKG5vcm1hbGl6ZWRWZXJzaW9uKSkgY29udGludWVcblxuICAgICAgYXdhaXQgZGIuaW5zZXJ0KHt0YWJsZU5hbWU6IFRBQkxFX05BTUUsIGRhdGE6IHt2ZXJzaW9uOiBub3JtYWxpemVkVmVyc2lvbn19KVxuICAgICAgZXhpc3RpbmcuYWRkKG5vcm1hbGl6ZWRWZXJzaW9uKVxuICAgICAgcmVjb3JkZWQucHVzaChub3JtYWxpemVkVmVyc2lvbilcbiAgICB9XG5cbiAgICByZXR1cm4gcmVjb3JkZWRcbiAgfVxuXG4gIC8qKlxuICAgKiBCYXNlbGluZXMgYHRhcmdldERiYCB0byBtYXRjaCB0aGUgYXBwbGllZCB2ZXJzaW9ucyBvZiBgc291cmNlRGJgLiBVc2Ugd2hlbiBhXG4gICAqIHByb3Zpc2lvbmluZyBwYXRoIGFkdmFuY2VkIGB0YXJnZXREYmAncyBzY2hlbWEgdG8gbWF0Y2ggYHNvdXJjZURiYCBvdXQgb2YgYmFuZFxuICAgKiAoZS5nLiBjbG9uaW5nIHRhYmxlIHN0cnVjdHVyZSBiZXR3ZWVuIGRhdGFiYXNlcyk6IHRoZSBtaWdyYXRpb25zIGFyZSwgYnlcbiAgICogY29uc3RydWN0aW9uLCBhbHJlYWR5IGFwcGxpZWQgb24gdGhlIHRhcmdldCwgc28gcmVjb3JkIHRoZW0gd2l0aG91dCByZS1ydW5uaW5nLlxuICAgKiBAcGFyYW0ge3tzb3VyY2VEYjogaW1wb3J0KFwiLi9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdCwgdGFyZ2V0RGI6IGltcG9ydChcIi4vZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHR9fSBhcmdzIC0gU291cmNlIGxlZGdlciBhbmQgdGFyZ2V0IGRhdGFiYXNlIHRvIGJhc2VsaW5lLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmdbXT59IFRoZSB2ZXJzaW9ucyB0aGF0IHdlcmUgbmV3bHkgcmVjb3JkZWQgb24gdGhlIHRhcmdldC5cbiAgICovXG4gIHN0YXRpYyBhc3luYyBiYXNlbGluZUZyb21EYXRhYmFzZSh7c291cmNlRGIsIHRhcmdldERifSkge1xuICAgIGNvbnN0IHNvdXJjZVZlcnNpb25zID0gYXdhaXQgTWlncmF0aW9uc0xlZGdlci5hcHBsaWVkVmVyc2lvbnMoc291cmNlRGIpXG5cbiAgICByZXR1cm4gYXdhaXQgTWlncmF0aW9uc0xlZGdlci5tYXJrQXBwbGllZCh0YXJnZXREYiwgc291cmNlVmVyc2lvbnMpXG4gIH1cbn1cbiJdfQ==
|
package/package.json
CHANGED
|
@@ -4,6 +4,11 @@ import {digg} from "diggerize"
|
|
|
4
4
|
import TableData from "./table-data/index.js"
|
|
5
5
|
|
|
6
6
|
const TABLE_NAME = "schema_migrations"
|
|
7
|
+
/** SQL Server "There is already an object named ..." — raised when two sessions race to create the same table. */
|
|
8
|
+
const MSSQL_OBJECT_ALREADY_EXISTS_ERROR_NUMBER = 2714
|
|
9
|
+
/** SQL Server deadlock victim — raised when concurrent sessions deadlock on the same catalog reads. */
|
|
10
|
+
const MSSQL_DEADLOCK_VICTIM_ERROR_NUMBER = 1205
|
|
11
|
+
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* Single owner of the `schema_migrations` ledger shape and the only place that reads
|
|
@@ -35,24 +40,107 @@ export default class MigrationsLedger {
|
|
|
35
40
|
return Boolean(table)
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Whether the error means a concurrent session already created the ledger table.
|
|
45
|
+
*
|
|
46
|
+
* Several processes bootstrap the same database at the same time (for example every
|
|
47
|
+
* CI test shard), and the MSSQL `IF NOT EXISTS (...) BEGIN CREATE TABLE ... END` guard
|
|
48
|
+
* is NOT atomic: two sessions can both evaluate the check as false and then both
|
|
49
|
+
* create, so the loser is rejected. Walks the wrapped-error cause chain because
|
|
50
|
+
* `db.query` re-wraps the driver's `RequestError` in a plain `Error`, and matches the
|
|
51
|
+
* stable numeric error number instead of the localized message text.
|
|
52
|
+
* @param {unknown} error - Error thrown while creating the ledger table.
|
|
53
|
+
* @returns {boolean} - True if the table was created by a concurrent session.
|
|
54
|
+
*/
|
|
55
|
+
static isConcurrentCreationError(error) {
|
|
56
|
+
let current = error
|
|
57
|
+
|
|
58
|
+
while (current instanceof Error) {
|
|
59
|
+
const errorNumber = /** @type {{number?: unknown}} */ (current).number
|
|
60
|
+
|
|
61
|
+
if (typeof errorNumber === "number" && errorNumber === MSSQL_OBJECT_ALREADY_EXISTS_ERROR_NUMBER) return true
|
|
62
|
+
|
|
63
|
+
current = current.cause
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether the error is a transient deadlock that should be retried.
|
|
71
|
+
*
|
|
72
|
+
* Concurrent boots also deadlock on the `INFORMATION_SCHEMA` metadata scan that
|
|
73
|
+
* `getTableByName` performs on SQL Server; the victim transaction rolled back cleanly,
|
|
74
|
+
* so retrying it is safe. Matched by numeric error number, never by message text.
|
|
75
|
+
* @param {unknown} error - Error thrown while inspecting or creating the ledger table.
|
|
76
|
+
* @returns {boolean} - True if the error is a retryable deadlock.
|
|
77
|
+
*/
|
|
78
|
+
static isDeadlockError(error) {
|
|
79
|
+
let current = error
|
|
80
|
+
|
|
81
|
+
while (current instanceof Error) {
|
|
82
|
+
const errorNumber = /** @type {{number?: unknown}} */ (current).number
|
|
83
|
+
|
|
84
|
+
if (typeof errorNumber === "number" && errorNumber === MSSQL_DEADLOCK_VICTIM_ERROR_NUMBER) return true
|
|
85
|
+
|
|
86
|
+
current = current.cause
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
38
92
|
/**
|
|
39
93
|
* Creates the ledger table if it does not exist. This is the single definition of
|
|
40
94
|
* the `schema_migrations` table shape.
|
|
95
|
+
*
|
|
96
|
+
* Safe under concurrency: a session that loses the create race (or deadlocks on the
|
|
97
|
+
* catalog read) converges on the existing table instead of failing its caller. That
|
|
98
|
+
* matters because this runs on every boot of a database configured for migrations
|
|
99
|
+
* (see `Migrator#createMigrationsTableForDatabase` and
|
|
100
|
+
* `migrateRequireContextForDatabase`), so a crash here takes down a whole test shard
|
|
101
|
+
* or deployment over a table that provably already exists.
|
|
41
102
|
* @param {import("./drivers/base.js").default} db - Database that should contain the ledger table.
|
|
103
|
+
* @param {{maxAttempts?: number}} [args] - Retry budget for transient catalog deadlocks.
|
|
42
104
|
* @returns {Promise<void>}
|
|
43
105
|
*/
|
|
44
|
-
static async ensureTable(db) {
|
|
45
|
-
|
|
106
|
+
static async ensureTable(db, {maxAttempts = 3} = {}) {
|
|
107
|
+
let attempt = 0
|
|
46
108
|
|
|
47
|
-
|
|
109
|
+
while (true) {
|
|
110
|
+
attempt++
|
|
48
111
|
|
|
49
|
-
|
|
112
|
+
try {
|
|
113
|
+
if (await MigrationsLedger.tableExists(db)) return
|
|
50
114
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
115
|
+
const tableData = new TableData(TABLE_NAME, {ifNotExists: true})
|
|
116
|
+
|
|
117
|
+
tableData.string("version", {null: false, primaryKey: true})
|
|
54
118
|
|
|
55
|
-
|
|
119
|
+
for (const sql of await db.createTableSql(tableData)) {
|
|
120
|
+
await db.query(sql)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
db.clearSchemaCache()
|
|
124
|
+
|
|
125
|
+
return
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (MigrationsLedger.isConcurrentCreationError(error)) {
|
|
128
|
+
// Another session created the ledger between our check and our create. The
|
|
129
|
+
// table exists with exactly this shape, so the goal state is already reached.
|
|
130
|
+
db.clearSchemaCache()
|
|
131
|
+
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (MigrationsLedger.isDeadlockError(error) && attempt < maxAttempts) {
|
|
136
|
+
db.clearSchemaCache()
|
|
137
|
+
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
throw error
|
|
142
|
+
}
|
|
143
|
+
}
|
|
56
144
|
}
|
|
57
145
|
|
|
58
146
|
/**
|