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
- if (await MigrationsLedger.tableExists(db)) return
106
+ static async ensureTable(db, {maxAttempts = 3} = {}) {
107
+ let attempt = 0
46
108
 
47
- const tableData = new TableData(TABLE_NAME, {ifNotExists: true})
109
+ while (true) {
110
+ attempt++
48
111
 
49
- tableData.string("version", {null: false, primaryKey: true})
112
+ try {
113
+ if (await MigrationsLedger.tableExists(db)) return
50
114
 
51
- for (const sql of await db.createTableSql(tableData)) {
52
- await db.query(sql)
53
- }
115
+ const tableData = new TableData(TABLE_NAME, {ifNotExists: true})
116
+
117
+ tableData.string("version", {null: false, primaryKey: true})
54
118
 
55
- db.clearSchemaCache()
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): Promise<void>;
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":"AAOA;;;;;;;;;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;;;;;OAKG;IACH,OAAa,WAAW,CAAC,EAAE,EAHhB,OAAO,mBAAmB,EAAE,OAGZ,GAFd,OAAO,CAAC,IAAI,CAAC,CAczB;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"}
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
- if (await MigrationsLedger.tableExists(db))
40
- return;
41
- const tableData = new TableData(TABLE_NAME, { ifNotExists: true });
42
- tableData.string("version", { null: false, primaryKey: true });
43
- for (const sql of await db.createTableSql(tableData)) {
44
- await db.query(sql);
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
@@ -3,7 +3,7 @@
3
3
  "velocious": "build/bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.695",
6
+ "version": "1.0.696",
7
7
  "main": "build/index.js",
8
8
  "types": "build/index.d.ts",
9
9
  "files": [
@@ -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
- if (await MigrationsLedger.tableExists(db)) return
106
+ static async ensureTable(db, {maxAttempts = 3} = {}) {
107
+ let attempt = 0
46
108
 
47
- const tableData = new TableData(TABLE_NAME, {ifNotExists: true})
109
+ while (true) {
110
+ attempt++
48
111
 
49
- tableData.string("version", {null: false, primaryKey: true})
112
+ try {
113
+ if (await MigrationsLedger.tableExists(db)) return
50
114
 
51
- for (const sql of await db.createTableSql(tableData)) {
52
- await db.query(sql)
53
- }
115
+ const tableData = new TableData(TABLE_NAME, {ifNotExists: true})
116
+
117
+ tableData.string("version", {null: false, primaryKey: true})
54
118
 
55
- db.clearSchemaCache()
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
  /**