516 lines
52 KiB
JavaScript
516 lines
52 KiB
JavaScript
|
|
"use strict";
|
||
|
|
|
||
|
|
Object.defineProperty(exports, "__esModule", {
|
||
|
|
value: true
|
||
|
|
});
|
||
|
|
exports.default = void 0;
|
||
|
|
var _events = require("events");
|
||
|
|
var _writableTrackingBuffer = _interopRequireDefault(require("./tracking-buffer/writable-tracking-buffer"));
|
||
|
|
var _stream = require("stream");
|
||
|
|
var _token = require("./token/token");
|
||
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
const FLAGS = {
|
||
|
|
nullable: 1 << 0,
|
||
|
|
caseSen: 1 << 1,
|
||
|
|
updateableReadWrite: 1 << 2,
|
||
|
|
updateableUnknown: 1 << 3,
|
||
|
|
identity: 1 << 4,
|
||
|
|
computed: 1 << 5,
|
||
|
|
// introduced in TDS 7.2
|
||
|
|
fixedLenCLRType: 1 << 8,
|
||
|
|
// introduced in TDS 7.2
|
||
|
|
sparseColumnSet: 1 << 10,
|
||
|
|
// introduced in TDS 7.3.B
|
||
|
|
hidden: 1 << 13,
|
||
|
|
// introduced in TDS 7.2
|
||
|
|
key: 1 << 14,
|
||
|
|
// introduced in TDS 7.2
|
||
|
|
nullableUnknown: 1 << 15 // introduced in TDS 7.2
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
const DONE_STATUS = {
|
||
|
|
FINAL: 0x00,
|
||
|
|
MORE: 0x1,
|
||
|
|
ERROR: 0x2,
|
||
|
|
INXACT: 0x4,
|
||
|
|
COUNT: 0x10,
|
||
|
|
ATTN: 0x20,
|
||
|
|
SRVERROR: 0x100
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
const rowTokenBuffer = Buffer.from([_token.TYPE.ROW]);
|
||
|
|
const textPointerAndTimestampBuffer = Buffer.from([
|
||
|
|
// TextPointer length
|
||
|
|
0x10,
|
||
|
|
// TextPointer
|
||
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
|
// Timestamp
|
||
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||
|
|
const textPointerNullBuffer = Buffer.from([0x00]);
|
||
|
|
|
||
|
|
// A transform that converts rows to packets.
|
||
|
|
class RowTransform extends _stream.Transform {
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
constructor(bulkLoad) {
|
||
|
|
super({
|
||
|
|
writableObjectMode: true
|
||
|
|
});
|
||
|
|
this.bulkLoad = bulkLoad;
|
||
|
|
this.mainOptions = bulkLoad.options;
|
||
|
|
this.columns = bulkLoad.columns;
|
||
|
|
this.columnMetadataWritten = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
_transform(row, _encoding, callback) {
|
||
|
|
if (!this.columnMetadataWritten) {
|
||
|
|
this.push(this.bulkLoad.getColMetaData());
|
||
|
|
this.columnMetadataWritten = true;
|
||
|
|
}
|
||
|
|
this.push(rowTokenBuffer);
|
||
|
|
for (let i = 0; i < this.columns.length; i++) {
|
||
|
|
const c = this.columns[i];
|
||
|
|
let value = Array.isArray(row) ? row[i] : row[c.objName];
|
||
|
|
if (!this.bulkLoad.firstRowWritten) {
|
||
|
|
try {
|
||
|
|
value = c.type.validate(value, c.collation);
|
||
|
|
} catch (error) {
|
||
|
|
return callback(error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const parameter = {
|
||
|
|
length: c.length,
|
||
|
|
scale: c.scale,
|
||
|
|
precision: c.precision,
|
||
|
|
value: value
|
||
|
|
};
|
||
|
|
if (c.type.name === 'Text' || c.type.name === 'Image' || c.type.name === 'NText') {
|
||
|
|
if (value == null) {
|
||
|
|
this.push(textPointerNullBuffer);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
this.push(textPointerAndTimestampBuffer);
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
this.push(c.type.generateParameterLength(parameter, this.mainOptions));
|
||
|
|
for (const chunk of c.type.generateParameterData(parameter, this.mainOptions)) {
|
||
|
|
this.push(chunk);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
return callback(error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
process.nextTick(callback);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
_flush(callback) {
|
||
|
|
this.push(this.bulkLoad.createDoneToken());
|
||
|
|
process.nextTick(callback);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A BulkLoad instance is used to perform a bulk insert.
|
||
|
|
*
|
||
|
|
* Use [[Connection.newBulkLoad]] to create a new instance, and [[Connection.execBulkLoad]] to execute it.
|
||
|
|
*
|
||
|
|
* Example of BulkLoad Usages:
|
||
|
|
*
|
||
|
|
* ```js
|
||
|
|
* // optional BulkLoad options
|
||
|
|
* const options = { keepNulls: true };
|
||
|
|
*
|
||
|
|
* // instantiate - provide the table where you'll be inserting to, options and a callback
|
||
|
|
* const bulkLoad = connection.newBulkLoad('MyTable', options, (error, rowCount) => {
|
||
|
|
* console.log('inserted %d rows', rowCount);
|
||
|
|
* });
|
||
|
|
*
|
||
|
|
* // setup your columns - always indicate whether the column is nullable
|
||
|
|
* bulkLoad.addColumn('myInt', TYPES.Int, { nullable: false });
|
||
|
|
* bulkLoad.addColumn('myString', TYPES.NVarChar, { length: 50, nullable: true });
|
||
|
|
*
|
||
|
|
* // execute
|
||
|
|
* connection.execBulkLoad(bulkLoad, [
|
||
|
|
* { myInt: 7, myString: 'hello' },
|
||
|
|
* { myInt: 23, myString: 'world' }
|
||
|
|
* ]);
|
||
|
|
* ```
|
||
|
|
*/
|
||
|
|
class BulkLoad extends _events.EventEmitter {
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
constructor(table, collation, connectionOptions, {
|
||
|
|
checkConstraints = false,
|
||
|
|
fireTriggers = false,
|
||
|
|
keepNulls = false,
|
||
|
|
lockTable = false,
|
||
|
|
order = {}
|
||
|
|
}, callback) {
|
||
|
|
if (typeof checkConstraints !== 'boolean') {
|
||
|
|
throw new TypeError('The "options.checkConstraints" property must be of type boolean.');
|
||
|
|
}
|
||
|
|
if (typeof fireTriggers !== 'boolean') {
|
||
|
|
throw new TypeError('The "options.fireTriggers" property must be of type boolean.');
|
||
|
|
}
|
||
|
|
if (typeof keepNulls !== 'boolean') {
|
||
|
|
throw new TypeError('The "options.keepNulls" property must be of type boolean.');
|
||
|
|
}
|
||
|
|
if (typeof lockTable !== 'boolean') {
|
||
|
|
throw new TypeError('The "options.lockTable" property must be of type boolean.');
|
||
|
|
}
|
||
|
|
if (typeof order !== 'object' || order === null) {
|
||
|
|
throw new TypeError('The "options.order" property must be of type object.');
|
||
|
|
}
|
||
|
|
for (const [column, direction] of Object.entries(order)) {
|
||
|
|
if (direction !== 'ASC' && direction !== 'DESC') {
|
||
|
|
throw new TypeError('The value of the "' + column + '" key in the "options.order" object must be either "ASC" or "DESC".');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
super();
|
||
|
|
this.error = undefined;
|
||
|
|
this.canceled = false;
|
||
|
|
this.executionStarted = false;
|
||
|
|
this.collation = collation;
|
||
|
|
this.table = table;
|
||
|
|
this.options = connectionOptions;
|
||
|
|
this.callback = callback;
|
||
|
|
this.columns = [];
|
||
|
|
this.columnsByName = {};
|
||
|
|
this.firstRowWritten = false;
|
||
|
|
this.streamingMode = false;
|
||
|
|
this.rowToPacketTransform = new RowTransform(this); // eslint-disable-line no-use-before-define
|
||
|
|
|
||
|
|
this.bulkOptions = {
|
||
|
|
checkConstraints,
|
||
|
|
fireTriggers,
|
||
|
|
keepNulls,
|
||
|
|
lockTable,
|
||
|
|
order
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Adds a column to the bulk load.
|
||
|
|
*
|
||
|
|
* The column definitions should match the table you are trying to insert into.
|
||
|
|
* Attempting to call addColumn after the first row has been added will throw an exception.
|
||
|
|
*
|
||
|
|
* ```js
|
||
|
|
* bulkLoad.addColumn('MyIntColumn', TYPES.Int, { nullable: false });
|
||
|
|
* ```
|
||
|
|
*
|
||
|
|
* @param name The name of the column.
|
||
|
|
* @param type One of the supported `data types`.
|
||
|
|
* @param __namedParameters Additional column type information. At a minimum, `nullable` must be set to true or false.
|
||
|
|
* @param length For VarChar, NVarChar, VarBinary. Use length as `Infinity` for VarChar(max), NVarChar(max) and VarBinary(max).
|
||
|
|
* @param nullable Indicates whether the column accepts NULL values.
|
||
|
|
* @param objName If the name of the column is different from the name of the property found on `rowObj` arguments passed to [[addRow]] or [[Connection.execBulkLoad]], then you can use this option to specify the property name.
|
||
|
|
* @param precision For Numeric, Decimal.
|
||
|
|
* @param scale For Numeric, Decimal, Time, DateTime2, DateTimeOffset.
|
||
|
|
*/
|
||
|
|
addColumn(name, type, {
|
||
|
|
output = false,
|
||
|
|
length,
|
||
|
|
precision,
|
||
|
|
scale,
|
||
|
|
objName = name,
|
||
|
|
nullable = true
|
||
|
|
}) {
|
||
|
|
if (this.firstRowWritten) {
|
||
|
|
throw new Error('Columns cannot be added to bulk insert after the first row has been written.');
|
||
|
|
}
|
||
|
|
if (this.executionStarted) {
|
||
|
|
throw new Error('Columns cannot be added to bulk insert after execution has started.');
|
||
|
|
}
|
||
|
|
const column = {
|
||
|
|
type: type,
|
||
|
|
name: name,
|
||
|
|
value: null,
|
||
|
|
output: output,
|
||
|
|
length: length,
|
||
|
|
precision: precision,
|
||
|
|
scale: scale,
|
||
|
|
objName: objName,
|
||
|
|
nullable: nullable,
|
||
|
|
collation: this.collation
|
||
|
|
};
|
||
|
|
if ((type.id & 0x30) === 0x20) {
|
||
|
|
if (column.length == null && type.resolveLength) {
|
||
|
|
column.length = type.resolveLength(column);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (type.resolvePrecision && column.precision == null) {
|
||
|
|
column.precision = type.resolvePrecision(column);
|
||
|
|
}
|
||
|
|
if (type.resolveScale && column.scale == null) {
|
||
|
|
column.scale = type.resolveScale(column);
|
||
|
|
}
|
||
|
|
this.columns.push(column);
|
||
|
|
this.columnsByName[name] = column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
getOptionsSql() {
|
||
|
|
const addOptions = [];
|
||
|
|
if (this.bulkOptions.checkConstraints) {
|
||
|
|
addOptions.push('CHECK_CONSTRAINTS');
|
||
|
|
}
|
||
|
|
if (this.bulkOptions.fireTriggers) {
|
||
|
|
addOptions.push('FIRE_TRIGGERS');
|
||
|
|
}
|
||
|
|
if (this.bulkOptions.keepNulls) {
|
||
|
|
addOptions.push('KEEP_NULLS');
|
||
|
|
}
|
||
|
|
if (this.bulkOptions.lockTable) {
|
||
|
|
addOptions.push('TABLOCK');
|
||
|
|
}
|
||
|
|
if (this.bulkOptions.order) {
|
||
|
|
const orderColumns = [];
|
||
|
|
for (const [column, direction] of Object.entries(this.bulkOptions.order)) {
|
||
|
|
orderColumns.push(`${column} ${direction}`);
|
||
|
|
}
|
||
|
|
if (orderColumns.length) {
|
||
|
|
addOptions.push(`ORDER (${orderColumns.join(', ')})`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (addOptions.length > 0) {
|
||
|
|
return ` WITH (${addOptions.join(',')})`;
|
||
|
|
} else {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
getBulkInsertSql() {
|
||
|
|
let sql = 'insert bulk ' + this.table + '(';
|
||
|
|
for (let i = 0, len = this.columns.length; i < len; i++) {
|
||
|
|
const c = this.columns[i];
|
||
|
|
if (i !== 0) {
|
||
|
|
sql += ', ';
|
||
|
|
}
|
||
|
|
sql += '[' + c.name + '] ' + c.type.declaration(c);
|
||
|
|
}
|
||
|
|
sql += ')';
|
||
|
|
sql += this.getOptionsSql();
|
||
|
|
return sql;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This is simply a helper utility function which returns a `CREATE TABLE SQL` statement based on the columns added to the bulkLoad object.
|
||
|
|
* This may be particularly handy when you want to insert into a temporary table (a table which starts with `#`).
|
||
|
|
*
|
||
|
|
* ```js
|
||
|
|
* var sql = bulkLoad.getTableCreationSql();
|
||
|
|
* ```
|
||
|
|
*
|
||
|
|
* A side note on bulk inserting into temporary tables: if you want to access a local temporary table after executing the bulk load,
|
||
|
|
* you'll need to use the same connection and execute your requests using [[Connection.execSqlBatch]] instead of [[Connection.execSql]]
|
||
|
|
*/
|
||
|
|
getTableCreationSql() {
|
||
|
|
let sql = 'CREATE TABLE ' + this.table + '(\n';
|
||
|
|
for (let i = 0, len = this.columns.length; i < len; i++) {
|
||
|
|
const c = this.columns[i];
|
||
|
|
if (i !== 0) {
|
||
|
|
sql += ',\n';
|
||
|
|
}
|
||
|
|
sql += '[' + c.name + '] ' + c.type.declaration(c);
|
||
|
|
if (c.nullable !== undefined) {
|
||
|
|
sql += ' ' + (c.nullable ? 'NULL' : 'NOT NULL');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
sql += '\n)';
|
||
|
|
return sql;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
getColMetaData() {
|
||
|
|
const tBuf = new _writableTrackingBuffer.default(100, null, true);
|
||
|
|
// TokenType
|
||
|
|
tBuf.writeUInt8(_token.TYPE.COLMETADATA);
|
||
|
|
// Count
|
||
|
|
tBuf.writeUInt16LE(this.columns.length);
|
||
|
|
for (let j = 0, len = this.columns.length; j < len; j++) {
|
||
|
|
const c = this.columns[j];
|
||
|
|
// UserType
|
||
|
|
if (this.options.tdsVersion < '7_2') {
|
||
|
|
tBuf.writeUInt16LE(0);
|
||
|
|
} else {
|
||
|
|
tBuf.writeUInt32LE(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Flags
|
||
|
|
let flags = FLAGS.updateableReadWrite;
|
||
|
|
if (c.nullable) {
|
||
|
|
flags |= FLAGS.nullable;
|
||
|
|
} else if (c.nullable === undefined && this.options.tdsVersion >= '7_2') {
|
||
|
|
flags |= FLAGS.nullableUnknown;
|
||
|
|
}
|
||
|
|
tBuf.writeUInt16LE(flags);
|
||
|
|
|
||
|
|
// TYPE_INFO
|
||
|
|
tBuf.writeBuffer(c.type.generateTypeInfo(c, this.options));
|
||
|
|
|
||
|
|
// TableName
|
||
|
|
if (c.type.hasTableName) {
|
||
|
|
tBuf.writeUsVarchar(this.table, 'ucs2');
|
||
|
|
}
|
||
|
|
|
||
|
|
// ColName
|
||
|
|
tBuf.writeBVarchar(c.name, 'ucs2');
|
||
|
|
}
|
||
|
|
return tBuf.data;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Sets a timeout for this bulk load.
|
||
|
|
*
|
||
|
|
* ```js
|
||
|
|
* bulkLoad.setTimeout(timeout);
|
||
|
|
* ```
|
||
|
|
*
|
||
|
|
* @param timeout The number of milliseconds before the bulk load is considered failed, or 0 for no timeout.
|
||
|
|
* When no timeout is set for the bulk load, the [[ConnectionOptions.requestTimeout]] of the Connection is used.
|
||
|
|
*/
|
||
|
|
setTimeout(timeout) {
|
||
|
|
this.timeout = timeout;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
createDoneToken() {
|
||
|
|
// It might be nice to make DoneToken a class if anything needs to create them, but for now, just do it here
|
||
|
|
const tBuf = new _writableTrackingBuffer.default(this.options.tdsVersion < '7_2' ? 9 : 13);
|
||
|
|
tBuf.writeUInt8(_token.TYPE.DONE);
|
||
|
|
const status = DONE_STATUS.FINAL;
|
||
|
|
tBuf.writeUInt16LE(status);
|
||
|
|
tBuf.writeUInt16LE(0); // CurCmd (TDS ignores this)
|
||
|
|
tBuf.writeUInt32LE(0); // row count - doesn't really matter
|
||
|
|
if (this.options.tdsVersion >= '7_2') {
|
||
|
|
tBuf.writeUInt32LE(0); // row count is 64 bits in >= TDS 7.2
|
||
|
|
}
|
||
|
|
return tBuf.data;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @private
|
||
|
|
*/
|
||
|
|
cancel() {
|
||
|
|
if (this.canceled) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
this.canceled = true;
|
||
|
|
this.emit('cancel');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
var _default = exports.default = BulkLoad;
|
||
|
|
module.exports = BulkLoad;
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZXZlbnRzIiwicmVxdWlyZSIsIl93cml0YWJsZVRyYWNraW5nQnVmZmVyIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9zdHJlYW0iLCJfdG9rZW4iLCJvYmoiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsIkZMQUdTIiwibnVsbGFibGUiLCJjYXNlU2VuIiwidXBkYXRlYWJsZVJlYWRXcml0ZSIsInVwZGF0ZWFibGVVbmtub3duIiwiaWRlbnRpdHkiLCJjb21wdXRlZCIsImZpeGVkTGVuQ0xSVHlwZSIsInNwYXJzZUNvbHVtblNldCIsImhpZGRlbiIsImtleSIsIm51bGxhYmxlVW5rbm93biIsIkRPTkVfU1RBVFVTIiwiRklOQUwiLCJNT1JFIiwiRVJST1IiLCJJTlhBQ1QiLCJDT1VOVCIsIkFUVE4iLCJTUlZFUlJPUiIsInJvd1Rva2VuQnVmZmVyIiwiQnVmZmVyIiwiZnJvbSIsIlRPS0VOX1RZUEUiLCJST1ciLCJ0ZXh0UG9pbnRlckFuZFRpbWVzdGFtcEJ1ZmZlciIsInRleHRQb2ludGVyTnVsbEJ1ZmZlciIsIlJvd1RyYW5zZm9ybSIsIlRyYW5zZm9ybSIsImNvbnN0cnVjdG9yIiwiYnVsa0xvYWQiLCJ3cml0YWJsZU9iamVjdE1vZGUiLCJtYWluT3B0aW9ucyIsIm9wdGlvbnMiLCJjb2x1bW5zIiwiY29sdW1uTWV0YWRhdGFXcml0dGVuIiwiX3RyYW5zZm9ybSIsInJvdyIsIl9lbmNvZGluZyIsImNhbGxiYWNrIiwicHVzaCIsImdldENvbE1ldGFEYXRhIiwiaSIsImxlbmd0aCIsImMiLCJ2YWx1ZSIsIkFycmF5IiwiaXNBcnJheSIsIm9iak5hbWUiLCJmaXJzdFJvd1dyaXR0ZW4iLCJ0eXBlIiwidmFsaWRhdGUiLCJjb2xsYXRpb24iLCJlcnJvciIsInBhcmFtZXRlciIsInNjYWxlIiwicHJlY2lzaW9uIiwibmFtZSIsImdlbmVyYXRlUGFyYW1ldGVyTGVuZ3RoIiwiY2h1bmsiLCJnZW5lcmF0ZVBhcmFtZXRlckRhdGEiLCJwcm9jZXNzIiwibmV4dFRpY2siLCJfZmx1c2giLCJjcmVhdGVEb25lVG9rZW4iLCJCdWxrTG9hZCIsIkV2ZW50RW1pdHRlciIsInRhYmxlIiwiY29ubmVjdGlvbk9wdGlvbnMiLCJjaGVja0NvbnN0cmFpbnRzIiwiZmlyZVRyaWdnZXJzIiwia2VlcE51bGxzIiwibG9ja1RhYmxlIiwib3JkZXIiLCJUeXBlRXJyb3IiLCJjb2x1bW4iLCJkaXJlY3Rpb24iLCJPYmplY3QiLCJlbnRyaWVzIiwidW5kZWZpbmVkIiwiY2FuY2VsZWQiLCJleGVjdXRpb25TdGFydGVkIiwiY29sdW1uc0J5TmFtZSIsInN0cmVhbWluZ01vZGUiLCJyb3dUb1BhY2tldFRyYW5zZm9ybSIsImJ1bGtPcHRpb25zIiwiYWRkQ29sdW1uIiwib3V0cHV0IiwiRXJyb3IiLCJpZCIsInJlc29sdmVMZW5ndGgiLCJyZXNvbHZlUHJlY2lzaW9uIiwicmVzb2x2ZVNjYWxlIiwiZ2V0T3B0aW9uc1NxbCIsImFkZE9wdGlvbnMiLCJvcmRlckNvbHVtbnMiLCJqb2luIiwiZ2V0QnVsa0luc2VydFNxbCIsInNxbCIsImxlbiIsImRlY2xhcmF0aW9uIiwiZ2V0VGFibGVDcmVhdGlvblNxbCIsInRCdWYiLCJXcml0YWJsZVRyYWNraW5nQnVmZmVyIiwid3JpdGVVSW50OCIsIkNPTE1FVEFEQVRBIiwid3JpdGVVSW50MTZMRSIsImoiLCJ0ZHNWZXJzaW9uIiwid3JpdGVVSW50MzJMRSIsImZsYWdzIiwid3JpdGVCdWZmZXIiLCJnZW5lcmF0ZVR5cGVJbmZvIiwiaGFzVGFibGVOYW1lIiwid3JpdGVVc1ZhcmNoYXIiLCJ3cml0ZUJWYXJjaGFyIiwiZGF0YSIsInNldFRpbWVvdXQiLCJ0aW1lb3V0IiwiRE9ORSIsInN0YXR1cyIsImNhbmNlbCIsImVtaXQiLCJfZGVmYXVsdCIsImV4cG9ydHMiLCJtb2R1bGUiXSwic291cmNlcyI6WyIuLi9zcmMvYnVsay1sb2FkLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEV2ZW50RW1pdHRlciB9IGZyb20gJ2V2ZW50cyc7XG5pbXBvcnQgV3JpdGFibGVUcmFja2luZ0J1ZmZlciBmcm9tICcuL3RyYWNraW5nLWJ1ZmZlci93cml0YWJsZS10cmFja2luZy1idWZmZXInO1xuaW1wb3J0IENvbm5lY3Rpb24sIHsgdHlwZSBJbnRlcm5hbENvbm5lY3Rpb25PcHRpb25zIH0gZnJvbSAnLi9jb25uZWN0aW9uJztcblxuaW1wb3J0IHsgVHJhbnNmb3JtIH0gZnJvbSAnc3RyZWFtJztcbmltcG9ydCB7IFRZUEUgYXMgVE9LRU5fVFlQRSB9IGZyb20gJy4vdG9rZW4vdG9rZW4nO1xuXG5pbXBvcnQgeyB0eXBlIERhdGFUeXBlLCB0eXBlIFBhcmFtZXRlciB9IGZyb20gJy4vZGF0YS10eXBlJztcbmltcG9ydCB7IENvbGxhdGlvbiB9IGZyb20gJy4vY29sbGF0aW9uJztcblxuLyoqXG4gKiBAcHJpdmF0ZVxuICovXG5jb25zdCBGTEFHUyA9IHtcbiAgbnVsbGFibGU6IDEgPDwgMCxcbiAgY2FzZVNlbjogMSA8PCAxLFxuICB1cGRhdGVhYmxlUmVhZFdyaXRlOiAxIDw8IDIsXG4gIHVwZGF0ZWFibGVVbmtub3duOiAxIDw8IDMsXG4gIGlkZW50aXR5OiAxIDw8IDQsXG4gIGNvbXB1dGVkOiAxIDw8IDUsIC8vIGludHJvZHVjZWQgaW4gVERTIDcuMlxuICBmaXhlZExlbkNMUlR5cGU6IDEgPDwgOCwgLy8gaW50cm9kdWNlZCBpbiBURFMgNy4yXG4gIHNwYXJzZUNvbHVtblNldDogMSA8PCAxMCwgLy8gaW50cm9kdWNlZCBpbiBURFMgNy4zLkJcbiAgaGlkZGVuOiAxIDw8IDEzLCAvLyBpbnRyb2R1Y2VkIGluIFREUyA3LjJcbiAga2V5OiAxIDw8IDE0LCAvLyBpbnRyb2R1Y2VkIGluIFREUyA3LjJcbiAgbnVsbGFibGVVbmtub3duOiAxIDw8IDE1IC8vIGludHJvZHVjZWQgaW4gVERTIDcuMlxufTtcblxuLyoqXG4gKiBAcHJpdmF0ZVxuICovXG5jb25zdCBET05FX1NUQVRVUyA9IHtcbiAgRklOQUw6IDB4MDAsXG4gIE1PUkU6IDB4MSxcbiAgRVJST1I6IDB4MixcbiAgSU5YQUNUOiAweDQsXG4gIENPVU5UOiAweDEwLFxuICBBVFROOiAweDIwLFxuICBTUlZFUlJPUjogMHgxMDBcbn07XG5cbi8qKlxuICogQHByaXZhdGVcbiAqL1xuaW50ZXJmYWNlIEludGVybmFsT3B0aW9ucyB7XG4gIGNoZWNrQ29uc3RyYWludHM6IGJvb2xlYW47XG4gIGZpcmVUcmlnZ2VyczogYm9vbGVhbjtcbiAga2VlcE51bGxzOiBib29sZWFuO1xuICBsb2NrVGFibGU6IGJvb2xlYW47XG4gIG9yZGVyOiB7IFtjb2x1bW5OYW1lOiBzdHJpbmddOiAnQVNDJyB8ICdERVNDJyB9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE9wdGlvbnM
|