UNPKG

@tableau/taco-toolkit

Version:
607 lines 45.3 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFetchParams = exports.ingestDataRows = exports.loadParquetData = exports.loadData = exports.loadCsvData = exports.fetchArrayBuffer = exports.fetchJson = exports.FetchUtilsPlugin = void 0; var csv_stringify_1 = require("csv-stringify"); var node_fs_1 = __importDefault(require("node:fs")); var node_path_1 = __importDefault(require("node:path")); var node_stream_1 = require("node:stream"); var node_util_1 = require("node:util"); var type_utils_1 = require("../../../../shared/type-utils"); var handler_input_1 = require("../../../../shared/types/handler-input"); var permission_check_1 = require("../../../../shared/utils/permission-check"); var eps_logger_1 = require("../../../logging/eps-logger"); var parse_utils_1 = require("../../../utils/parse-utils"); var request_utils_1 = require("../../../utils/request-utils"); var sandbox_plugin_1 = require("../sandbox-plugin"); // Convert the pipeline function to a promise so we can use async/await with it var streamPipeline = (0, node_util_1.promisify)(node_stream_1.pipeline); var FetchUtilsPlugin = /** @class */ (function (_super) { __extends(FetchUtilsPlugin, _super); function FetchUtilsPlugin(config, networkAdapter, tempDataManager) { var _this = _super.call(this) || this; _this.config = config; _this.networkAdapter = networkAdapter; _this.tempDataManager = tempDataManager; _this.state = {}; _this.extractorId = config.extractorId; _this.permission = config.permission; return _this; } FetchUtilsPlugin.prototype.getState = function () { return this.state; }; FetchUtilsPlugin.prototype.registerClass = function (sandbox) { var methods = [ { isStatic: true, externalName: 'fetchJson', functionRef: this.createFetchJson(), }, { isStatic: true, externalName: 'fetchArrayBuffer', functionRef: this.createFetchArrayBuffer(), }, ]; // file ingestion functions require file name to be present var fileName = getFileName(this.config); if (fileName) { methods.push({ isStatic: true, externalName: 'loadData', functionRef: this.createLoadData(fileName), }, { isStatic: true, externalName: 'loadExcelData', functionRef: this.createLoadExcelData(fileName), }, { isStatic: true, externalName: 'loadParquetData', functionRef: this.createLoadParquetData(fileName), }, { isStatic: true, externalName: 'loadCsvData', functionRef: this.createLoadCsvData(fileName), }, { isStatic: true, externalName: 'ingestDataRows', functionRef: this.createIngestDataRows(fileName), }); } (0, sandbox_plugin_1.addClassToSandbox)(sandbox, methods, 'FetchUtils'); }; FetchUtilsPlugin.prototype.createFetchJson = function () { var _this = this; return function (url, options) { return __awaiter(_this, void 0, void 0, function () { var method; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: method = (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'GET'; checkPermission(this.permission, url, method); return [4 /*yield*/, fetchJson(this.networkAdapter, url, options)]; case 1: return [2 /*return*/, _b.sent()]; } }); }); }; }; FetchUtilsPlugin.prototype.createFetchArrayBuffer = function () { var _this = this; return function (url, options) { return __awaiter(_this, void 0, void 0, function () { var method; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: method = (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'GET'; checkPermission(this.permission, url, method); return [4 /*yield*/, fetchArrayBuffer(this.networkAdapter, url, options)]; case 1: return [2 /*return*/, _b.sent()]; } }); }); }; }; FetchUtilsPlugin.prototype.createLoadData = function (fileName) { var _this = this; return function (url, options) { return __awaiter(_this, void 0, void 0, function () { var method; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: method = (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'GET'; checkPermission(this.permission, url, method); return [4 /*yield*/, loadData(this.extractorId, fileName, this.tempDataManager, this.networkAdapter, url, options)]; case 1: return [2 /*return*/, _b.sent()]; } }); }); }; }; FetchUtilsPlugin.prototype.createLoadExcelData = function (fileName) { return this.createLoadData(fileName); }; FetchUtilsPlugin.prototype.createLoadCsvData = function (fileName) { var _this = this; return function (url, options) { return __awaiter(_this, void 0, void 0, function () { var method; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: method = (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'GET'; checkPermission(this.permission, url, method); return [4 /*yield*/, loadCsvData(this.extractorId, fileName, this.tempDataManager, this.networkAdapter, url, options)]; case 1: _b.sent(); this.setParser('taco:csv-file-parser', fileName); return [2 /*return*/]; } }); }); }; }; FetchUtilsPlugin.prototype.createLoadParquetData = function (fileName) { var _this = this; var tableFileCounter = {}; return function (url, options) { return __awaiter(_this, void 0, void 0, function () { var method; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: method = (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'GET'; checkPermission(this.permission, url, method); return [4 /*yield*/, loadParquetData(this.extractorId, fileName, this.tempDataManager, this.networkAdapter, tableFileCounter, url, options)]; case 1: _b.sent(); this.setParser('taco:parquet-file-parser', fileName); return [2 /*return*/]; } }); }); }; }; FetchUtilsPlugin.prototype.createIngestDataRows = function (fileName) { var _this = this; return function (rows) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, ingestDataRows(this.extractorId, fileName, this.tempDataManager, rows)]; case 1: _a.sent(); this.setParser('taco:data-file-parser', fileName); return [2 /*return*/]; } }); }); }; }; FetchUtilsPlugin.prototype.setParser = function (parserName, tableName) { var parser = this.state.parser; if (parser === undefined) { this.state.parser = parserName; return; } if (parser !== parserName) { throw new Error("A table can only ingest a single type of data. ".concat(parserName, " cannot be registered, because ").concat(parser, " is already set for table '").concat(tableName, "'")); } }; return FetchUtilsPlugin; }(sandbox_plugin_1.SandboxPlugin)); exports.FetchUtilsPlugin = FetchUtilsPlugin; function getFileName(config) { if ('handlerInput' in config) { var handlerInput = config.handlerInput; if ((0, handler_input_1.isFileBasedHandlerInput)(handlerInput)) { return handlerInput.name; } return; } if ('tableName' in config) { return config.tableName; } // if nothing is specified for file name, returns undefined } function checkPermission(permission, url, method) { if (!(0, permission_check_1.checkApiPermission)(permission.api, url, method)) { var error = new Error("No permission to make ".concat(method, " request URL ").concat(url)); eps_logger_1.Logger.error(error); throw error; } } function fetchJson(networkAdapter, url, options) { return __awaiter(this, void 0, void 0, function () { var fetchParams, headers, response; return __generator(this, function (_a) { switch (_a.label) { case 0: fetchParams = getFetchParams(url, options); headers = fetchParams.options.headers; if (headers === undefined) { fetchParams.options.headers = headers = {}; } headers['Accept'] = 'application/json'; return [4 /*yield*/, networkAdapter.fetch(fetchParams.urlWithQuery, fetchParams.options)]; case 1: response = _a.sent(); if (!response.ok || response.body === null) { throw new Error("fetchJson: unexpected response. [status]: ".concat(response.status, ", [statusText]: ").concat(response.statusText)); } return [4 /*yield*/, response.json()]; case 2: return [2 /*return*/, _a.sent()]; } }); }); } exports.fetchJson = fetchJson; function fetchArrayBuffer(networkAdapter, url, options) { return __awaiter(this, void 0, void 0, function () { var fetchParams, response, buffer; return __generator(this, function (_a) { switch (_a.label) { case 0: fetchParams = getFetchParams(url, options); return [4 /*yield*/, networkAdapter.fetch(fetchParams.urlWithQuery, fetchParams.options)]; case 1: response = _a.sent(); if (!response.ok || response.body === null) { throw new Error("fetchArrayBuffer: unexpected response. [status]: ".concat(response.status, ", [statusText]: ").concat(response.statusText)); } return [4 /*yield*/, response.arrayBuffer()]; case 2: buffer = _a.sent(); return [2 /*return*/, new Uint8Array(buffer)]; } }); }); } exports.fetchArrayBuffer = fetchArrayBuffer; function loadCsvData(extractorId, fileName, tempDataManager, networkAdapter, url, options) { return __awaiter(this, void 0, void 0, function () { function streamToFile(stream, filePath) { var _a; return __awaiter(this, void 0, void 0, function () { var trimColumnHeader, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: trimColumnHeader = (_a = options === null || options === void 0 ? void 0 : options.trimColumnHeader) !== null && _a !== void 0 ? _a : false; _b = !trimColumnHeader || !node_fs_1.default.existsSync(filePath); if (_b) return [3 /*break*/, 2]; return [4 /*yield*/, (0, parse_utils_1.isFileEmpty)(filePath)]; case 1: _b = (_c.sent()); _c.label = 2; case 2: if (!_b) return [3 /*break*/, 4]; return [4 /*yield*/, appendStreamToFile(stream, filePath)]; case 3: _c.sent(); return [3 /*break*/, 6]; case 4: return [4 /*yield*/, streamPipeline(stream.pipe(new FirstLineTransform()), node_fs_1.default.createWriteStream(filePath, { flags: 'a' }))]; case 5: _c.sent(); _c.label = 6; case 6: return [2 /*return*/]; } }); }); } var fetchParams, response, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: fetchParams = getFetchParams(url, options); return [4 /*yield*/, networkAdapter.fetch(fetchParams.urlWithQuery, fetchParams.options)]; case 1: response = _a.sent(); if (!response.ok || response.body === null) { throw new Error("loadCsvData: unexpected response. [status]: ".concat(response.status, ", [statusText]: ").concat(response.statusText)); } _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, ingestStreamData(extractorId, tempDataManager, response.body, streamToFile, fileName)]; case 3: _a.sent(); return [3 /*break*/, 5]; case 4: error_1 = _a.sent(); throw new Error("ingestData: Data Injestion into epsTempData directory failed.\n" + error_1); case 5: return [2 /*return*/]; } }); }); } exports.loadCsvData = loadCsvData; function loadData(extractorId, fileName, tempDataManager, networkAdapter, url, options) { return __awaiter(this, void 0, void 0, function () { var fetchParams, response, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: fetchParams = getFetchParams(url, options); return [4 /*yield*/, networkAdapter.fetch(fetchParams.urlWithQuery, fetchParams.options)]; case 1: response = _a.sent(); if (!response.ok || response.body === null) { throw new Error("loadData: unexpected response. [status]: ".concat(response.status, ", [statusText]: ").concat(response.statusText)); } _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, ingestStreamData(extractorId, tempDataManager, response.body, appendStreamToFile, fileName)]; case 3: _a.sent(); return [3 /*break*/, 5]; case 4: error_2 = _a.sent(); throw new Error("ingestData: Data Injestion into epsTempData directory failed.\n" + error_2); case 5: return [2 /*return*/]; } }); }); } exports.loadData = loadData; function loadParquetData(extractorId, fileName, tempDataManager, networkAdapter, tableFileCounter, url, options) { var _a; return __awaiter(this, void 0, void 0, function () { var fetchParams, response, dirPath, index, filePath, error_3; return __generator(this, function (_b) { switch (_b.label) { case 0: fetchParams = getFetchParams(url, options); return [4 /*yield*/, networkAdapter.fetch(fetchParams.urlWithQuery, fetchParams.options)]; case 1: response = _b.sent(); if (!response.ok || response.body === null) { throw new Error("loadParquetData: unexpected response. [status]: ".concat(response.status, ", [statusText]: ").concat(response.statusText)); } _b.label = 2; case 2: _b.trys.push([2, 4, , 5]); tempDataManager.createTempDataDirIfNotExists(extractorId); dirPath = tempDataManager.getTempDataDirPath(extractorId, fileName); // create parquet data dir if it does not exist if (!node_fs_1.default.existsSync(dirPath)) { node_fs_1.default.mkdirSync(dirPath, { recursive: true }); eps_logger_1.Logger.info("Temporary parquet data directory created: ".concat(dirPath)); } index = (_a = tableFileCounter[fileName]) !== null && _a !== void 0 ? _a : 0; tableFileCounter[fileName] = index + 1; filePath = node_path_1.default.join(dirPath, "f-".concat(index, ".parquet")); // Duckdb read APIs can only support filepaths less than 255 characters in length. if (filePath.length >= 255) { throw new Error("Temporary data filepath has to be less than 255 characters in length. tempDataFilePath: ".concat(filePath)); } return [4 /*yield*/, appendStreamToFile(response.body, filePath)]; case 3: _b.sent(); return [3 /*break*/, 5]; case 4: error_3 = _b.sent(); throw new Error("ingestData: Data Injestion into epsTempData directory failed.\n" + error_3); case 5: return [2 /*return*/]; } }); }); } exports.loadParquetData = loadParquetData; function ingestStreamData(extractorId, tempDataManager, stream, streamToFile, filename) { return __awaiter(this, void 0, void 0, function () { var filePath; return __generator(this, function (_a) { switch (_a.label) { case 0: tempDataManager.createTempDataDirIfNotExists(extractorId); filePath = tempDataManager.getTempDataFilePath(extractorId, filename); return [4 /*yield*/, streamToFile(stream, filePath)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); } /** * Using HandlerInput for now because we are getting HandlerInput via sandbox during runtime, and the type is HandlerInput. * When we export the API from SDK we should use FileBasedHandlerInput instead. */ function ingestDataRows(extractorId, fileName, tempDataManager, rows) { return __awaiter(this, void 0, void 0, function () { var filePath; return __generator(this, function (_a) { switch (_a.label) { case 0: tempDataManager.createTempDataDirIfNotExists(extractorId); filePath = tempDataManager.getTempDataFilePath(extractorId, fileName); return [4 /*yield*/, writeCSVToFile(rows, filePath)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); } exports.ingestDataRows = ingestDataRows; /** * Appends data to a CSV file identified by the file path. * The function uses the first DataRow object's keys order to determine the data column ordering. * When calling the function multiple times, the caller is responsible for maintaining the column ordering alignment. * The function uses the name property in the DataRow object to identify a table. * Rows will be appended to the same file if the file path is the same. * The header is only written on the first call. * * @param dataRows - An array of DataRow objects containing the data to be appended. * @param filePath - The file path of the CSV file to append data to. * @returns A Promise that resolves when the data has been appended to the file. */ function writeCSVToFile(dataRows, filePath) { return new Promise(function (resolve, reject) { var writeHeader = true; // Only write header once, so if the file already exists and has a header, // then we don't need to write the header again. if (node_fs_1.default.existsSync(filePath)) { writeHeader = false; } var outputStream = node_fs_1.default.createWriteStream(filePath, { flags: 'a' }); // if dataRows is empty, close the stream with an empty file if (dataRows.length === 0) { outputStream.close(); resolve(); return; } try { (0, csv_stringify_1.stringify)(dataRows, { header: writeHeader, columns: Object.keys(dataRows[0]), // using cast as csv-stringify converts boolean values to 0 or 1 cast: { boolean: function (value) { return value.toString(); }, }, }).pipe(outputStream); } catch (error) { error.message = "ingestDataRows: ingestDataRows failed.\n".concat(error.message); reject(error); } outputStream.on('error', function (err) { eps_logger_1.Logger.error('Error during ingestDataRows.' + err); reject(err); }); outputStream.on('finish', function () { eps_logger_1.Logger.info("Count of rows ingested: ".concat(dataRows.length)); resolve(); }); }); } var FirstLineTransform = /** @class */ (function (_super) { __extends(FirstLineTransform, _super); function FirstLineTransform() { var _this = _super !== null && _super.apply(this, arguments) || this; _this._firstChunk = true; return _this; } FirstLineTransform.prototype._transform = function (chunk, encoding, callback) { if (this._firstChunk) { var firstLineEnd = chunk.indexOf('\n'); if (firstLineEnd > -1) { chunk = chunk.slice(firstLineEnd + 1); this._firstChunk = false; } else { chunk = null; } } callback(null, chunk); }; return FirstLineTransform; }(node_stream_1.Transform)); /** * Append readable stream into a file */ function appendStreamToFile(stream, filePath) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, streamPipeline(stream, node_fs_1.default.createWriteStream(filePath, { flags: 'a' }))]; case 1: _a.sent(); return [2 /*return*/]; } }); }); } function getFetchParams(urlString, options) { var _a; if (options === undefined) { return { urlWithQuery: urlString, options: { method: 'GET' } }; } var method = (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'GET'; if (method !== 'GET' && method !== 'POST') { throw new Error("'method' must be 'GET' or 'POST'. Unsupported method: ".concat(method)); } var url = new URL(urlString); if (options.query) { Object.entries(options.query).forEach(function (_a) { var _b = __read(_a, 2), key = _b[0], value = _b[1]; if (value === undefined) { return; } url.searchParams.append(key, (0, type_utils_1.stringifyUnknown)(value)); }); } var headers = options.headers, body = options.body; var fetchOption = (0, request_utils_1.composeFetchOption)(method, headers, body); return { urlWithQuery: url.toString(), options: fetchOption }; } exports.getFetchParams = getFetchParams; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmV0Y2gtdXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvZXBzL3NhbmRib3gvc2RrL2ZldGNoZXItdXRpbHMvZmV0Y2gtdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLCtDQUF5QztBQUN6QyxvREFBd0I7QUFDeEIsd0RBQTRCO0FBQzVCLDJDQUFvRTtBQUNwRSx1Q0FBcUM7QUFJckMsNERBQWdFO0FBRWhFLHdFQUkrQztBQUcvQyw4RUFBOEU7QUFDOUUsMERBQW9EO0FBR3BELDBEQUF3RDtBQUN4RCw4REFBaUU7QUFFakUsb0RBQWdGO0FBRWhGLCtFQUErRTtBQUMvRSxJQUFNLGNBQWMsR0FBRyxJQUFBLHFCQUFTLEVBQUMsc0JBQVEsQ0FBQyxDQUFBO0FBc0UxQztJQUFzQyxvQ0FBYTtJQU1qRCwwQkFDVSxNQUE4QixFQUM5QixjQUE4QixFQUM5QixlQUFnQztRQUgxQyxZQUtFLGlCQUFPLFNBSVI7UUFSUyxZQUFNLEdBQU4sTUFBTSxDQUF3QjtRQUM5QixvQkFBYyxHQUFkLGNBQWMsQ0FBZ0I7UUFDOUIscUJBQWUsR0FBZixlQUFlLENBQWlCO1FBUmpDLFdBQUssR0FBMEIsRUFBRSxDQUFBO1FBWXhDLEtBQUksQ0FBQyxXQUFXLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FBQTtRQUNyQyxLQUFJLENBQUMsVUFBVSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUE7O0lBQ3JDLENBQUM7SUFFRCxtQ0FBUSxHQUFSO1FBQ0UsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFBO0lBQ25CLENBQUM7SUFFRCx3Q0FBYSxHQUFiLFVBQWMsT0FBa0I7UUFDOUIsSUFBTSxPQUFPLEdBQWlCO1lBQzVCO2dCQUNFLFFBQVEsRUFBRSxJQUFJO2dCQUNkLFlBQVksRUFBRSxXQUFXO2dC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