notification.mjs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * MinIO Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2016 MinIO, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import { EventEmitter } from 'eventemitter3';
  17. import jsonLineParser from 'stream-json/jsonl/Parser.js';
  18. import { DEFAULT_REGION } from "./helpers.mjs";
  19. import { pipesetup, uriEscape } from "./internal/helper.mjs";
  20. // TODO: type this
  21. // Base class for three supported configs.
  22. export class TargetConfig {
  23. setId(id) {
  24. this.Id = id;
  25. }
  26. addEvent(newevent) {
  27. if (!this.Event) {
  28. this.Event = [];
  29. }
  30. this.Event.push(newevent);
  31. }
  32. addFilterSuffix(suffix) {
  33. if (!this.Filter) {
  34. this.Filter = {
  35. S3Key: {
  36. FilterRule: []
  37. }
  38. };
  39. }
  40. this.Filter.S3Key.FilterRule.push({
  41. Name: 'suffix',
  42. Value: suffix
  43. });
  44. }
  45. addFilterPrefix(prefix) {
  46. if (!this.Filter) {
  47. this.Filter = {
  48. S3Key: {
  49. FilterRule: []
  50. }
  51. };
  52. }
  53. this.Filter.S3Key.FilterRule.push({
  54. Name: 'prefix',
  55. Value: prefix
  56. });
  57. }
  58. }
  59. // 1. Topic (simple notification service)
  60. export class TopicConfig extends TargetConfig {
  61. constructor(arn) {
  62. super();
  63. this.Topic = arn;
  64. }
  65. }
  66. // 2. Queue (simple queue service)
  67. export class QueueConfig extends TargetConfig {
  68. constructor(arn) {
  69. super();
  70. this.Queue = arn;
  71. }
  72. }
  73. // 3. CloudFront (lambda function)
  74. export class CloudFunctionConfig extends TargetConfig {
  75. constructor(arn) {
  76. super();
  77. this.CloudFunction = arn;
  78. }
  79. }
  80. // Notification config - array of target configs.
  81. // Target configs can be
  82. // 1. Topic (simple notification service)
  83. // 2. Queue (simple queue service)
  84. // 3. CloudFront (lambda function)
  85. export class NotificationConfig {
  86. add(target) {
  87. let instance;
  88. if (target instanceof TopicConfig) {
  89. instance = this.TopicConfiguration ?? (this.TopicConfiguration = []);
  90. }
  91. if (target instanceof QueueConfig) {
  92. instance = this.QueueConfiguration ?? (this.QueueConfiguration = []);
  93. }
  94. if (target instanceof CloudFunctionConfig) {
  95. instance = this.CloudFunctionConfiguration ?? (this.CloudFunctionConfiguration = []);
  96. }
  97. if (instance) {
  98. instance.push(target);
  99. }
  100. }
  101. }
  102. export const buildARN = (partition, service, region, accountId, resource) => {
  103. return 'arn:' + partition + ':' + service + ':' + region + ':' + accountId + ':' + resource;
  104. };
  105. export const ObjectCreatedAll = 's3:ObjectCreated:*';
  106. export const ObjectCreatedPut = 's3:ObjectCreated:Put';
  107. export const ObjectCreatedPost = 's3:ObjectCreated:Post';
  108. export const ObjectCreatedCopy = 's3:ObjectCreated:Copy';
  109. export const ObjectCreatedCompleteMultipartUpload = 's3:ObjectCreated:CompleteMultipartUpload';
  110. export const ObjectRemovedAll = 's3:ObjectRemoved:*';
  111. export const ObjectRemovedDelete = 's3:ObjectRemoved:Delete';
  112. export const ObjectRemovedDeleteMarkerCreated = 's3:ObjectRemoved:DeleteMarkerCreated';
  113. export const ObjectReducedRedundancyLostObject = 's3:ReducedRedundancyLostObject';
  114. // put string at least so auto-complete could work
  115. // TODO: type this
  116. // Poll for notifications, used in #listenBucketNotification.
  117. // Listening constitutes repeatedly requesting s3 whether or not any
  118. // changes have occurred.
  119. export class NotificationPoller extends EventEmitter {
  120. constructor(client, bucketName, prefix, suffix, events) {
  121. super();
  122. this.client = client;
  123. this.bucketName = bucketName;
  124. this.prefix = prefix;
  125. this.suffix = suffix;
  126. this.events = events;
  127. this.ending = false;
  128. }
  129. // Starts the polling.
  130. start() {
  131. this.ending = false;
  132. process.nextTick(() => {
  133. this.checkForChanges();
  134. });
  135. }
  136. // Stops the polling.
  137. stop() {
  138. this.ending = true;
  139. }
  140. checkForChanges() {
  141. // Don't continue if we're looping again but are cancelled.
  142. if (this.ending) {
  143. return;
  144. }
  145. const method = 'GET';
  146. const queries = [];
  147. if (this.prefix) {
  148. const prefix = uriEscape(this.prefix);
  149. queries.push(`prefix=${prefix}`);
  150. }
  151. if (this.suffix) {
  152. const suffix = uriEscape(this.suffix);
  153. queries.push(`suffix=${suffix}`);
  154. }
  155. if (this.events) {
  156. this.events.forEach(s3event => queries.push('events=' + uriEscape(s3event)));
  157. }
  158. queries.sort();
  159. let query = '';
  160. if (queries.length > 0) {
  161. query = `${queries.join('&')}`;
  162. }
  163. const region = this.client.region || DEFAULT_REGION;
  164. this.client.makeRequestAsync({
  165. method,
  166. bucketName: this.bucketName,
  167. query
  168. }, '', [200], region).then(response => {
  169. const asm = jsonLineParser.make();
  170. pipesetup(response, asm).on('data', data => {
  171. // Data is flushed periodically (every 5 seconds), so we should
  172. // handle it after flushing from the JSON parser.
  173. let records = data.value.Records;
  174. // If null (= no records), change to an empty array.
  175. if (!records) {
  176. records = [];
  177. }
  178. // Iterate over the notifications and emit them individually.
  179. records.forEach(record => {
  180. this.emit('notification', record);
  181. });
  182. // If we're done, stop.
  183. if (this.ending) {
  184. response === null || response === void 0 ? void 0 : response.destroy();
  185. }
  186. }).on('error', e => this.emit('error', e)).on('end', () => {
  187. // Do it again, if we haven't cancelled yet.
  188. process.nextTick(() => {
  189. this.checkForChanges();
  190. });
  191. });
  192. }, e => {
  193. return this.emit('error', e);
  194. });
  195. }
  196. }
  197. //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJFdmVudEVtaXR0ZXIiLCJqc29uTGluZVBhcnNlciIsIkRFRkFVTFRfUkVHSU9OIiwicGlwZXNldHVwIiwidXJpRXNjYXBlIiwiVGFyZ2V0Q29uZmlnIiwic2V0SWQiLCJpZCIsIklkIiwiYWRkRXZlbnQiLCJuZXdldmVudCIsIkV2ZW50IiwicHVzaCIsImFkZEZpbHRlclN1ZmZpeCIsInN1ZmZpeCIsIkZpbHRlciIsIlMzS2V5IiwiRmlsdGVyUnVsZSIsIk5hbWUiLCJWYWx1ZSIsImFkZEZpbHRlclByZWZpeCIsInByZWZpeCIsIlRvcGljQ29uZmlnIiwiY29uc3RydWN0b3IiLCJhcm4iLCJUb3BpYyIsIlF1ZXVlQ29uZmlnIiwiUXVldWUiLCJDbG91ZEZ1bmN0aW9uQ29uZmlnIiwiQ2xvdWRGdW5jdGlvbiIsIk5vdGlmaWNhdGlvbkNvbmZpZyIsImFkZCIsInRhcmdldCIsImluc3RhbmNlIiwiVG9waWNDb25maWd1cmF0aW9uIiwiUXVldWVDb25maWd1cmF0aW9uIiwiQ2xvdWRGdW5jdGlvbkNvbmZpZ3VyYXRpb24iLCJidWlsZEFSTiIsInBhcnRpdGlvbiIsInNlcnZpY2UiLCJyZWdpb24iLCJhY2NvdW50SWQiLCJyZXNvdXJjZSIsIk9iamVjdENyZWF0ZWRBbGwiLCJPYmplY3RDcmVhdGVkUHV0IiwiT2JqZWN0Q3JlYXRlZFBvc3QiLCJPYmplY3RDcmVhdGVkQ29weSIsIk9iamVjdENyZWF0ZWRDb21wbGV0ZU11bHRpcGFydFVwbG9hZCIsIk9iamVjdFJlbW92ZWRBbGwiLCJPYmplY3RSZW1vdmVkRGVsZXRlIiwiT2JqZWN0UmVtb3ZlZERlbGV0ZU1hcmtlckNyZWF0ZWQiLCJPYmplY3RSZWR1Y2VkUmVkdW5kYW5jeUxvc3RPYmplY3QiLCJOb3RpZmljYXRpb25Qb2xsZXIiLCJjbGllbnQiLCJidWNrZXROYW1lIiwiZXZlbnRzIiwiZW5kaW5nIiwic3RhcnQiLCJwcm9jZXNzIiwibmV4dFRpY2siLCJjaGVja0ZvckNoYW5nZXMiLCJzdG9wIiwibWV0aG9kIiwicXVlcmllcyIsImZvckVhY2giLCJzM2V2ZW50Iiwic29ydCIsInF1ZXJ5IiwibGVuZ3RoIiwiam9pbiIsIm1ha2VSZXF1ZXN0QXN5bmMiLCJ0aGVuIiwicmVzcG9uc2UiLCJhc20iLCJtYWtlIiwib24iLCJkYXRhIiwicmVjb3JkcyIsInZhbHVlIiwiUmVjb3JkcyIsInJlY29yZCIsImVtaXQiLCJkZXN0cm95IiwiZSJdLCJzb3VyY2VzIjpbIm5vdGlmaWNhdGlvbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogTWluSU8gSmF2YXNjcmlwdCBMaWJyYXJ5IGZvciBBbWF6b24gUzMgQ29tcGF0aWJsZSBDbG91ZCBTdG9yYWdlLCAoQykgMjAxNiBNaW5JTywgSW5jLlxuICpcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICdldmVudGVtaXR0ZXIzJ1xuaW1wb3J0IGpzb25MaW5lUGFyc2VyIGZyb20gJ3N0cmVhbS1qc29uL2pzb25sL1BhcnNlci5qcydcblxuaW1wb3J0IHsgREVGQVVMVF9SRUdJT04gfSBmcm9tICcuL2hlbHBlcnMudHMnXG5pbXBvcnQgdHlwZSB7IFR5cGVkQ2xpZW50IH0gZnJvbSAnLi9pbnRlcm5hbC9jbGllbnQudHMnXG5pbXBvcnQgeyBwaXBlc2V0dXAsIHVyaUVzY2FwZSB9IGZyb20gJy4vaW50ZXJuYWwvaGVscGVyLnRzJ1xuXG4vLyBUT0RPOiB0eXBlIHRoaXNcblxudHlwZSBFdmVudCA9IHVua25vd25cblxuLy8gQmFzZSBjbGFzcyBmb3IgdGhyZWUgc3VwcG9ydGVkIGNvbmZpZ3MuXG5leHBvcnQgY2xhc3MgVGFyZ2V0Q29uZmlnIHtcbiAgcHJpdmF0ZSBGaWx0ZXI/OiB7IFMzS2V5OiB7IEZpbHRlclJ1bGU6IHsgTmFtZTogc3RyaW5nOyBWYWx1ZTogc3RyaW5nIH1bXSB9IH1cbiAgcHJpdmF0ZSBFdmVudD86IEV2ZW50W11cbiAgcHJpdmF0ZSBJZDogdW5rbm93blxuXG4gIHNldElkKGlkOiB1bmtub3duKSB7XG4gICAgdGhpcy5JZCA9IGlkXG4gIH1cblxuICBhZGRFdmVudChuZXdldmVudDogRXZlbnQpIHtcbiAgICBpZiAoIXRoaXMuRXZlbnQpIHtcbiAgICAgIHRoaXMuRXZlbnQgPSBbXVxuICAgIH1cbiAgICB0aGlzLkV2ZW50LnB1c2gobmV3ZXZlbnQpXG4gIH1cblxuICBhZGRGaWx0ZXJTdWZmaXgoc3VmZml4OiBzdHJpbmcpIHtcbiAgICBpZiAoIXRoaXMuRmlsdGVyKSB7XG4gICAgICB0aGlzLkZpbHRlciA9IHsgUzNLZXk6IHsgRmlsdGVyUnVsZTogW10gfSB9XG4gICAgfVxuICAgIHRoaXMuRmlsdGVyLlMzS2V5LkZpbHRlclJ1bGUucHVzaCh7IE5hbWU6ICdzdWZmaXgnLCBWYWx1ZTogc3VmZml4IH0pXG4gIH1cblxuICBhZGRGaWx0ZXJQcmVmaXgocHJlZml4OiBzdHJpbmcpIHtcbiAgICBpZiAoIXRoaXMuRmlsdGVyKSB7XG4gICAgICB0aGlzLkZpbHRlciA9IHsgUzNLZXk6IHsgRmlsdGVyUnVsZTogW10gfSB9XG4gICAgfVxuICAgIHRoaXMuRmlsdGVyLlMzS2V5LkZpbHRlclJ1bGUucHVzaCh7IE5hbWU6ICdwcmVmaXgnLCBWYWx1ZTogcHJlZml4IH0pXG4gIH1cbn1cblxuLy8gMS4gVG9waWMgKHNpbXBsZSBub3RpZmljYXRpb24gc2VydmljZSlcbmV4cG9ydCBjbGFzcyBUb3BpY0NvbmZpZyBleHRlbmRzIFRhcmdldENvbmZpZyB7XG4gIHByaXZhdGUgVG9waWM6IHN0cmluZ1xuXG4gIGNvbnN0cnVjdG9yKGFybjogc3RyaW5nKSB7XG4gICAgc3VwZXIoKVxuICAgIHRoaXMuVG9waWMgPSBhcm5cbiAgfVxufVxuXG4vLyAyLiBRdWV1ZSAoc2ltcGxlIHF1ZXVlIHNlcnZpY2UpXG5leHBvcnQgY2xhc3MgUXVldWVDb25maWcgZXh0ZW5kcyBUYXJnZXRDb25maWcge1xuICBwcml2YXRlIFF1ZXVlOiBzdHJpbmdcblxuICBjb25zdHJ1Y3Rvcihhcm46IHN0cmluZykge1xuICAgIHN1cGVyKClcbiAgICB0aGlzLlF1ZXVlID0gYXJuXG4gIH1cbn1cblxuLy8gMy4gQ2xvdWRGcm9udCAobGFtYmRhIGZ1bmN0aW9uKVxuZXhwb3J0IGNsYXNzIENsb3VkRnVuY3Rpb25Db25maWcgZXh0ZW5kcyBUYXJnZXRDb25maWcge1xuICBwcml2YXRlIENsb3VkRnVuY3Rpb246IHN0cmluZ1xuXG4gIGNvbnN0cnVjdG9yKGFybjogc3RyaW5nKSB7XG4gICAgc3VwZXIoKVxuICAgIHRoaXMuQ2xvdWRGdW5jdGlvbiA9IGFyblxuICB9XG59XG5cbi8vIE5vdGlmaWNhdGlvbiBjb25maWcgLSBhcnJheSBvZiB0YXJnZXQgY29uZmlncy5cbi8vIFRhcmdldCBjb25maWdzIGNhbiBiZVxuLy8gMS4gVG9waWMgKHNpbXBsZSBub3RpZmljYXRpb24gc2VydmljZSlcbi8vIDIuIFF1ZXVlIChzaW1wbGUgcXVldWUgc2VydmljZSlcbi8vIDMuIENsb3VkRnJvbnQgKGxhbWJkYSBmdW5jdGlvbilcbmV4cG9ydCBjbGFzcyBOb3RpZmljYXRpb25Db25maWcge1xuICBwcml2YXRlIFRvcGljQ29uZmlndXJhdGlvbj86IFRhcmdldENvbmZpZ1tdXG4gIHByaXZhdGUgQ2xvdWRGdW5jdGlvbkNvbmZpZ3VyYXRpb24/OiBUYXJnZXRDb25maWdbXVxuICBwcml2YXRlIFF1ZXVlQ29uZmlndXJhdGlvbj86IFRhcmdldENvbmZpZ1tdXG5cbiAgYWRkKHRhcmdldDogVGFyZ2V0Q29uZmlnKSB7XG4gICAgbGV0IGluc3RhbmNlOiBUYXJnZXRDb25maWdbXSB8IHVuZGVmaW5lZFxuICAgIGlmICh0YXJnZXQgaW5zdGFuY2VvZiBUb3BpY0NvbmZpZykge1xuICAgICAgaW5zdGFuY2UgPSB0aGlzLlRvcGljQ29uZmlndXJhdGlvbiA/Pz0gW11cbiAgICB9XG4gICAgaWYgKHRhcmdldCBpbnN0YW5jZW9mIFF1ZXVlQ29uZmlnKSB7XG4gICAgICBpbnN0YW5jZSA9IHRoaXMuUXVldWVDb25maWd1cmF0aW9uID8/PSBbXVxuICAgIH1cbiAgICBpZiAodGFyZ2V0IGluc3RhbmNlb2YgQ2xvdWRGdW5jdGlvbkNvbmZpZykge1xuICAgICAgaW5zdGFuY2UgPSB0aGlzLkNsb3VkRnVuY3Rpb25Db25maWd1cmF0aW9uID8/PSBbXVxuICAgIH1cbiAgICBpZiAoaW5zdGFuY2UpIHtcbiAgICAgIGluc3RhbmNlLnB1c2godGFyZ2V0KVxuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgY29uc3QgYnVpbGRBUk4gPSAocGFydGl0aW9uOiBzdHJpbmcsIHNlcnZpY2U6IHN0cmluZywgcmVnaW9uOiBzdHJpbmcsIGFjY291bnRJZDogc3RyaW5nLCByZXNvdXJjZTogc3RyaW5nKSA9PiB7XG4gIHJldHVybiAnYXJuOicgKyBwYXJ0aXRpb24gKyAnOicgKyBzZXJ2aWNlICsgJzonICsgcmVnaW9uICsgJzonICsgYWNjb3VudElkICsgJzonICsgcmVzb3VyY2Vcbn1cbmV4cG9ydCBjb25zdCBPYmplY3RDcmVhdGVkQWxsID0gJ3MzOk9iamVjdENyZWF0ZWQ6KidcbmV4cG9ydCBjb25zdCBPYmplY3RDcmVhdGVkUHV0ID0gJ3MzOk9iamVjdENyZWF0ZWQ6UHV0J1xuZXhwb3J0IGNvbnN0IE9iamVjdENyZWF0ZWRQb3N0ID0gJ3MzOk9iamVjdENyZWF0ZWQ6UG9zdCdcbmV4cG9ydCBjb25zdCBPYmplY3RDcmVhdGVkQ29weSA9ICdzMzpPYmplY3RDcmVhdGVkOkNvcHknXG5leHBvcnQgY29uc3QgT2JqZWN0Q3JlYXRlZENvbXBsZXRlTXVsdGlwYXJ0VXBsb2FkID0gJ3MzOk9iamVjdENyZWF0ZWQ6Q29tcGxldGVNdWx0aXBhcnRVcGxvYWQnXG5leHBvcnQgY29uc3QgT2JqZWN0UmVtb3ZlZEFsbCA9ICdzMzpPYmplY3RSZW1vdmVkOionXG5leHBvcnQgY29uc3QgT2JqZWN0UmVtb3ZlZERlbGV0ZSA9ICdzMzpPYmplY3RSZW1vdmVkOkRlbGV0ZSdcbmV4cG9ydCBjb25zdCBPYmplY3RSZW1vdmVkRGVsZXRlTWFya2VyQ3JlYXRlZCA9ICdzMzpPYmplY3RSZW1vdmVkOkRlbGV0ZU1hcmtlckNyZWF0ZWQnXG5leHBvcnQgY29uc3QgT2JqZWN0UmVkdWNlZFJlZHVuZGFuY3lMb3N0T2JqZWN0ID0gJ3MzOlJlZHVjZWRSZWR1bmRhbmN5TG9zdE9iamVjdCdcbmV4cG9ydCB0eXBlIE5vdGlmaWNhdGlvbkV2ZW50ID1cbiAgfCAnczM6T2JqZWN0Q3JlYXRlZDoqJ1xuICB8ICdzMzpPYmplY3RDcmVhdGVkOlB1dCdcbiAgfCAnczM6T2JqZWN0Q3JlYXRlZDpQb3N0J1xuICB8ICdzMzpPYmplY3RDcmVhdGVkOkNvcHknXG4gIHwgJ3MzOk9iamVjdENyZWF0ZWQ6Q29tcGxldGVNdWx0aXBhcnRVcGxvYWQnXG4gIHwgJ3MzOk9iamVjdFJlbW92ZWQ6KidcbiAgfCAnczM6T2JqZWN0UmVtb3ZlZDpEZWxldGUnXG4gIHwgJ3MzOk9iamVjdFJlbW92ZWQ6RGVsZXRlTWFya2VyQ3JlYXRlZCdcbiAgfCAnczM6UmVkdWNlZFJlZHVuZGFuY3lMb3N0T2JqZWN0J1xuICB8ICdzMzpUZXN0RXZlbnQnXG4gIHwgJ3MzOk9iamVjdFJlc3RvcmU6UG9zdCdcbiAgfCAnczM6T2JqZWN0UmVzdG9yZTpDb21wbGV0ZWQnXG4gIHwgJ3MzOlJlcGxpY2F0aW9uOk9wZXJhdGlvbkZhaWxlZFJlcGxpY2F0aW9uJ1xuICB8ICdzMzpSZXBsaWNhdGlvbjpPcGVyYXRpb25NaXNzZWRUaHJlc2hvbGQnXG4gIHwgJ3MzOlJlcGxpY2F0aW9uOk9wZXJhdGlvblJlcGxpY2F0ZWRBZnRlclRocmVzaG9sZCdcbiAgfCAnczM6UmVwbGljYXRpb246T3BlcmF0aW9uTm90VHJhY2tlZCdcbiAgfCBzdHJpbmcgLy8gcHV0IHN0cmluZyBhdCBsZWFzdCBzbyBhdXRvLWNvbXBsZXRlIGNvdWxkIHdvcmtcblxuLy8gVE9ETzogdHlwZSB0aGlzXG5leHBvcnQgdHlwZSBOb3RpZmljYXRpb25SZWNvcmQgPSB1bmtub3duXG4vLyBQb2xsIGZvciBub3RpZmljYXRpb25zLCB1c2VkIGluICNsaXN0ZW5CdWNrZXROb3RpZmljYXRpb24uXG4vLyBMaXN0ZW5pbmcgY29uc3RpdHV0ZXMgcmVwZWF0ZWRseSByZXF1ZXN0aW5nIHMzIHdoZXRoZXIgb3Igbm90IGFueVxuLy8gY2hhbmdlcyBoYXZlIG9jY3VycmVkLlxuZXhwb3J0IGNsYXNzIE5vdGlmaWNhdGlvblBvbGxlciBleHRlbmRzIEV2ZW50RW1pdHRlcjx7XG4gIG5vdGlmaWNhdGlvbjogKGV2ZW50OiBOb3RpZmljYXRpb25SZWNvcmQpID0+IHZvaWRcbiAgZXJyb3I6IChlcnJvcjogdW5rbm93bikgPT4gdm9pZFxufT4ge1xuICBwcml2YXRlIGNsaWVudDogVHlwZWRDbGllbnRcbiAgcHJpdmF0ZSBidWNrZXROYW1lOiBzdHJpbmdcbiAgcHJpdmF0ZSBwcmVmaXg6IHN0cmluZ1xuICBwcml2YXRlIHN1ZmZpeDogc3RyaW5nXG4gIHByaXZhdGUgZXZlbnRzOiBOb3RpZmljYXRpb25FdmVudFtdXG4gIHByaXZhdGUgZW5kaW5nOiBib29sZWFuXG5cbiAgY29uc3RydWN0b3IoY2xpZW50OiBUeXBlZENsaWVudCwgYnVja2V0TmFtZTogc3RyaW5nLCBwcmVmaXg6IHN0cmluZywgc3VmZml4OiBzdHJpbmcsIGV2ZW50czogTm90aWZpY2F0aW9uRXZlbnRbXSkge1xuICAgIHN1cGVyKClcblxuICAgIHRoaXMuY2xpZW50ID0gY2xpZW50XG4gICAgdGhpcy5idWNrZXROYW1lID0gYnVja2V0TmFtZVxuICAgIHRoaXMucHJlZml4ID0gcHJlZml4XG4gICAgdGhpcy5zdWZmaXggPSBzdWZmaXhcbiAgICB0aGlzLmV2ZW50cyA9IGV2ZW50c1xuXG4gICAgdGhpcy5lbmRpbmcgPSBmYWxzZVxuICB9XG5cbiAgLy8gU3RhcnRzIHRoZSBwb2xsaW5nLlxuICBzdGFydCgpIHtcbiAgICB0aGlzLmVuZGluZyA9IGZhbHNlXG5cbiAgICBwcm9jZXNzLm5leHRUaWNrKCgpID0+IHtcbiAgICAgIHRoaXMuY2hlY2tGb3JDaGFuZ2VzKClcbiAgICB9KVxuICB9XG5cbiAgLy8gU3RvcHMgdGhlIHBvbGxpbmcuXG4gIHN0b3AoKSB7XG4gICAgdGhpcy5lbmRpbmcgPSB0cnVlXG4gIH1cblxuICBjaGVja0ZvckNoYW5nZXMoKSB7XG4gICAgLy8gRG9uJ3QgY29udGludWUgaWYgd2UncmUgbG9vcGluZyBhZ2FpbiBidXQgYXJlIGNhbmNlbGxlZC5cbiAgICBpZiAodGhpcy5lbmRpbmcpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IG1ldGhvZCA9ICdHRVQnXG4gICAgY29uc3QgcXVlcmllcyA9IFtdXG4gICAgaWYgKHRoaXMucHJlZml4KSB7XG4gICAgICBjb25zdCBwcmVmaXggPSB1cmlFc2NhcGUodGhpcy5wcmVmaXgpXG4gICAgICBxdWVyaWVzLnB1c2goYHByZWZpeD0ke3ByZWZpeH1gKVxuICAgIH1cbiAgICBpZiAodGhpcy5zdWZmaXgpIHtcbiAgICAgIGNvbnN0IHN1ZmZpeCA9IHVyaUVzY2FwZSh0aGlzLnN1ZmZpeClcbiAgICAgIHF1ZXJpZXMucHVzaChgc3VmZml4PSR7c3VmZml4fWApXG4gICAgfVxuICAgIGlmICh0aGlzLmV2ZW50cykge1xuICAgICAgdGhpcy5ldmVudHMuZm9yRWFjaCgoczNldmVudCkgPT4gcXVlcmllcy5wdXNoKCdldmVudHM9JyArIHVyaUVzY2FwZShzM2V2ZW50KSkpXG4gICAgfVxuICAgIHF1ZXJpZXMuc29ydCgpXG5cbiAgICBsZXQgcXVlcnkgPSAnJ1xuICAgIGlmIChxdWVyaWVzLmxlbmd0aCA+IDApIHtcbiAgICAgIHF1ZXJ5ID0gYCR7cXVlcmllcy5qb2luKCcmJyl9YFxuICAgIH1cbiAgICBjb25zdCByZWdpb24gPSB0aGlzLmNsaWVudC5yZWdpb24gfHwgREVGQVVMVF9SRUdJT05cblxuICAgIHRoaXMuY2xpZW50Lm1ha2VSZXF1ZXN0QXN5bmMoeyBtZXRob2QsIGJ1Y2tldE5hbWU6IHRoaXMuYnVja2V0TmFtZSwgcXVlcnkgfSwgJycsIFsyMDBdLCByZWdpb24pLnRoZW4oXG4gICAgICAocmVzcG9uc2UpID0+IHtcbiAgICAgICAgY29uc3QgYXNtID0ganNvbkxpbmVQYXJzZXIubWFrZSgpXG5cbiAgICAgICAgcGlwZXNldHVwKHJlc3BvbnNlLCBhc20pXG4gICAgICAgICAgLm9uKCdkYXRhJywgKGRhdGEpID0+IHtcbiAgICAgICAgICAgIC8vIERhdGEgaXMgZmx1c2hlZCBwZXJpb2RpY2FsbHkgKGV2ZXJ5IDUgc2Vjb25kcyksIHNvIHdlIHNob3VsZFxuICAgICAgICAgICAgLy8gaGFuZGxlIGl0IGFmdGVyIGZsdXNoaW5nIGZyb20gdGhlIEpTT04gcGFyc2VyLlxuICAgICAgICAgICAgbGV0IHJlY29yZHMgPSBkYXRhLnZhbHVlLlJlY29yZHNcbiAgICAgICAgICAgIC8vIElmIG51bGwgKD0gbm8gcmVjb3JkcyksIGNoYW5nZSB0byBhbiBlbXB0eSBhcnJheS5cbiAgICAgICAgICAgIGlmICghcmVjb3Jkcykge1xuICAgICAgICAgICAgICByZWNvcmRzID0gW11cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gSXRlcmF0ZSBvdmVyIHRoZSBub3RpZmljYXRpb25zIGFuZCBlbWl0IHRoZW0gaW5kaXZpZHVhbGx5LlxuICAgICAgICAgICAgcmVjb3Jkcy5mb3JFYWNoKChyZWNvcmQ6IE5vdGlmaWNhdGlvblJlY29yZCkgPT4ge1xuICAgICAgICAgICAgICB0aGlzLmVtaXQoJ25vdGlmaWNhdGlvbicsIHJlY29yZClcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIC8vIElmIHdlJ3JlIGRvbmUsIHN0b3AuXG4gICAgICAgICAgICBpZiAodGhpcy5lbmRpbmcpIHtcbiAgICAgICAgICAgICAgcmVzcG9uc2U/LmRlc3Ryb3koKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pXG4gICAgICAgICAgLm9uKCdlcnJvcicsIChlKSA9PiB0aGlzLmVtaXQoJ2Vycm9yJywgZSkpXG4gICAgICAgICAgLm9uKCdlbmQnLCAoKSA9PiB7XG4gICAgICAgICAgICAvLyBEbyBpdCBhZ2FpbiwgaWYgd2UgaGF2ZW4ndCBjYW5jZWxsZWQgeWV0LlxuICAgICAgICAgICAgcHJvY2Vzcy5uZXh0VGljaygoKSA9PiB7XG4gICAgICAgICAgICAgIHRoaXMuY2hlY2tGb3JDaGFuZ2VzKClcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgfSlcbiAgICAgIH0sXG4gICAgICAoZSkgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5lbWl0KCdlcnJvcicsIGUpXG4gICAgICB9LFxuICAgIClcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsU0FBU0EsWUFBWSxRQUFRLGVBQWU7QUFDNUMsT0FBT0MsY0FBYyxNQUFNLDZC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