@aws-cdk-testing/cli-integ 2.61.0

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.
Files changed (165) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +16 -0
  3. package/README.md +151 -0
  4. package/bin/apply-patches +19 -0
  5. package/bin/download-and-run-old-tests +52 -0
  6. package/bin/query-github +2 -0
  7. package/bin/query-github.d.ts +1 -0
  8. package/bin/query-github.js +55 -0
  9. package/bin/run-suite +2 -0
  10. package/bin/run-suite.d.ts +1 -0
  11. package/bin/run-suite.js +126 -0
  12. package/bin/stage-distribution +2 -0
  13. package/bin/stage-distribution.d.ts +1 -0
  14. package/bin/stage-distribution.js +209 -0
  15. package/bin/test-root +2 -0
  16. package/bin/test-root.d.ts +1 -0
  17. package/bin/test-root.js +6 -0
  18. package/entrypoints/test-cli-regression-against-current-code.sh +11 -0
  19. package/entrypoints/test-cli-regression-against-latest-release.sh +11 -0
  20. package/entrypoints/test-cli-regression.bash +83 -0
  21. package/entrypoints/test.sh +12 -0
  22. package/lib/aws.d.ts +55 -0
  23. package/lib/aws.js +243 -0
  24. package/lib/corking.d.ts +13 -0
  25. package/lib/corking.js +34 -0
  26. package/lib/files.d.ts +15 -0
  27. package/lib/files.js +80 -0
  28. package/lib/github.d.ts +4 -0
  29. package/lib/github.js +43 -0
  30. package/lib/index.d.ts +12 -0
  31. package/lib/index.js +25 -0
  32. package/lib/integ-test.d.ts +11 -0
  33. package/lib/integ-test.js +55 -0
  34. package/lib/lists.d.ts +1 -0
  35. package/lib/lists.js +12 -0
  36. package/lib/memoize.d.ts +6 -0
  37. package/lib/memoize.js +19 -0
  38. package/lib/npm.d.ts +4 -0
  39. package/lib/npm.js +15 -0
  40. package/lib/package-sources/release-source.d.ts +22 -0
  41. package/lib/package-sources/release-source.js +67 -0
  42. package/lib/package-sources/repo-source.d.ts +23 -0
  43. package/lib/package-sources/repo-source.js +92 -0
  44. package/lib/package-sources/repo-tools/npm +2 -0
  45. package/lib/package-sources/repo-tools/npm.d.ts +1 -0
  46. package/lib/package-sources/repo-tools/npm.js +42 -0
  47. package/lib/package-sources/source.d.ts +24 -0
  48. package/lib/package-sources/source.js +3 -0
  49. package/lib/package-sources/subprocess.d.ts +3 -0
  50. package/lib/package-sources/subprocess.js +18 -0
  51. package/lib/resource-pool.d.ts +54 -0
  52. package/lib/resource-pool.js +120 -0
  53. package/lib/resources.d.ts +1 -0
  54. package/lib/resources.js +6 -0
  55. package/lib/shell.d.ts +59 -0
  56. package/lib/shell.js +118 -0
  57. package/lib/staging/codeartifact.d.ts +44 -0
  58. package/lib/staging/codeartifact.js +258 -0
  59. package/lib/staging/maven.d.ts +5 -0
  60. package/lib/staging/maven.js +83 -0
  61. package/lib/staging/npm.d.ts +4 -0
  62. package/lib/staging/npm.js +56 -0
  63. package/lib/staging/nuget.d.ts +4 -0
  64. package/lib/staging/nuget.js +71 -0
  65. package/lib/staging/parallel-shell.d.ts +6 -0
  66. package/lib/staging/parallel-shell.js +46 -0
  67. package/lib/staging/pypi.d.ts +4 -0
  68. package/lib/staging/pypi.js +49 -0
  69. package/lib/staging/usage-dir.d.ts +31 -0
  70. package/lib/staging/usage-dir.js +87 -0
  71. package/lib/with-aws.d.ts +13 -0
  72. package/lib/with-aws.js +60 -0
  73. package/lib/with-cdk-app.d.ts +146 -0
  74. package/lib/with-cdk-app.js +398 -0
  75. package/lib/with-packages.d.ts +5 -0
  76. package/lib/with-packages.js +14 -0
  77. package/lib/with-sam.d.ts +33 -0
  78. package/lib/with-sam.js +240 -0
  79. package/lib/with-temporary-directory.d.ts +5 -0
  80. package/lib/with-temporary-directory.js +32 -0
  81. package/lib/xpmutex.d.ts +43 -0
  82. package/lib/xpmutex.js +207 -0
  83. package/package.json +73 -0
  84. package/resources/cdk-apps/app/app.js +463 -0
  85. package/resources/cdk-apps/app/cdk.json +7 -0
  86. package/resources/cdk-apps/app/docker/Dockerfile +2 -0
  87. package/resources/cdk-apps/app/docker/Dockerfile.Custom +2 -0
  88. package/resources/cdk-apps/app/lambda/index.js +4 -0
  89. package/resources/cdk-apps/app/lambda/response.json +3 -0
  90. package/resources/cdk-apps/app/nested-stack.js +49 -0
  91. package/resources/cdk-apps/cfn-include-app/.gitignore +1 -0
  92. package/resources/cdk-apps/cfn-include-app/cdk.json +4 -0
  93. package/resources/cdk-apps/cfn-include-app/cfn-include-app.js +21 -0
  94. package/resources/cdk-apps/cfn-include-app/example-template.json +13 -0
  95. package/resources/cdk-apps/sam_cdk_integ_app/bin/test-app.js +11 -0
  96. package/resources/cdk-apps/sam_cdk_integ_app/cdk.json +6 -0
  97. package/resources/cdk-apps/sam_cdk_integ_app/lib/nested-stack.js +19 -0
  98. package/resources/cdk-apps/sam_cdk_integ_app/lib/test-stack.js +134 -0
  99. package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/.no-packagejson-validator +0 -0
  100. package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/Dockerfile +9 -0
  101. package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/app.js +22 -0
  102. package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/package.json +18 -0
  103. package/resources/cdk-apps/sam_cdk_integ_app/src/go/GoFunctionConstruct/go.mod +5 -0
  104. package/resources/cdk-apps/sam_cdk_integ_app/src/go/GoFunctionConstruct/go.sum +17 -0
  105. package/resources/cdk-apps/sam_cdk_integ_app/src/go/GoFunctionConstruct/main.go +17 -0
  106. package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/.no-packagejson-validator +0 -0
  107. package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/app.ts +16 -0
  108. package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/package-lock.json +12 -0
  109. package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/package.json +5 -0
  110. package/resources/cdk-apps/sam_cdk_integ_app/src/python/Function/app.py +15 -0
  111. package/resources/cdk-apps/sam_cdk_integ_app/src/python/Function/requirements.txt +1 -0
  112. package/resources/cdk-apps/sam_cdk_integ_app/src/python/Layer/layer_version_dependency.py +5 -0
  113. package/resources/cdk-apps/sam_cdk_integ_app/src/python/Layer/requirements.txt +1 -0
  114. package/resources/cdk-apps/sam_cdk_integ_app/src/rest-api-definition.yaml +12 -0
  115. package/resources/cli-regression-patches/v1.119.0/NOTES.md +5 -0
  116. package/resources/cli-regression-patches/v1.119.0/cli.integtest.js +659 -0
  117. package/resources/cli-regression-patches/v1.130.0/NOTES.md +12 -0
  118. package/resources/cli-regression-patches/v1.130.0/app/app.js +378 -0
  119. package/resources/cli-regression-patches/v1.130.0/bootstrapping.integtest.js +220 -0
  120. package/resources/cli-regression-patches/v1.44.0/NOTES.md +18 -0
  121. package/resources/cli-regression-patches/v1.44.0/bootstrapping.integtest.js +126 -0
  122. package/resources/cli-regression-patches/v1.44.0/test.sh +26 -0
  123. package/resources/cli-regression-patches/v1.61.1/NOTES.md +2 -0
  124. package/resources/cli-regression-patches/v1.61.1/skip-tests.txt +16 -0
  125. package/resources/cli-regression-patches/v1.62.0/NOTES.md +2 -0
  126. package/resources/cli-regression-patches/v1.62.0/aws-helpers.js +245 -0
  127. package/resources/cli-regression-patches/v1.63.0/NOTES.md +1 -0
  128. package/resources/cli-regression-patches/v1.63.0/skip-tests.txt +7 -0
  129. package/resources/cli-regression-patches/v1.64.0/NOTES.md +3 -0
  130. package/resources/cli-regression-patches/v1.64.0/cdk-helpers.js +325 -0
  131. package/resources/cli-regression-patches/v1.64.0/cli.integtest.js +599 -0
  132. package/resources/cli-regression-patches/v1.64.1/NOTES.md +3 -0
  133. package/resources/cli-regression-patches/v1.64.1/cdk-helpers.js +324 -0
  134. package/resources/cli-regression-patches/v1.64.1/cli.integtest.js +599 -0
  135. package/resources/cli-regression-patches/v1.67.0/NOTES.md +2 -0
  136. package/resources/cli-regression-patches/v1.67.0/cdk-helpers.js +331 -0
  137. package/resources/cloud-assemblies/0.36.0/InitStack.template.json +1 -0
  138. package/resources/cloud-assemblies/0.36.0/manifest.json +19 -0
  139. package/resources/cloud-assemblies/1.10.0-lookup-default-vpc/InitStack.template.json +2 -0
  140. package/resources/cloud-assemblies/1.10.0-lookup-default-vpc/manifest.json.js +37 -0
  141. package/resources/cloud-assemblies/1.10.0-request-azs/InitStack.template.json +2 -0
  142. package/resources/cloud-assemblies/1.10.0-request-azs/manifest.json.js +34 -0
  143. package/resources/integ.jest.config.js +25 -0
  144. package/skip-tests.txt +8 -0
  145. package/tests/cli-integ-tests/README.md +47 -0
  146. package/tests/cli-integ-tests/bootstrapping.integtest.d.ts +1 -0
  147. package/tests/cli-integ-tests/bootstrapping.integtest.js +271 -0
  148. package/tests/cli-integ-tests/cli.integtest.d.ts +1 -0
  149. package/tests/cli-integ-tests/cli.integtest.js +1029 -0
  150. package/tests/init-csharp/init-csharp.integtest.d.ts +1 -0
  151. package/tests/init-csharp/init-csharp.integtest.js +14 -0
  152. package/tests/init-fsharp/init-fsharp.integtest.d.ts +1 -0
  153. package/tests/init-fsharp/init-fsharp.integtest.js +14 -0
  154. package/tests/init-java/init-java.integtest.d.ts +1 -0
  155. package/tests/init-java/init-java.integtest.js +14 -0
  156. package/tests/init-javascript/init-javascript.integtest.d.ts +1 -0
  157. package/tests/init-javascript/init-javascript.integtest.js +15 -0
  158. package/tests/init-python/init-python.integtest.d.ts +1 -0
  159. package/tests/init-python/init-python.integtest.js +19 -0
  160. package/tests/init-typescript-app/init-typescript-app.integtest.d.ts +1 -0
  161. package/tests/init-typescript-app/init-typescript-app.integtest.js +49 -0
  162. package/tests/init-typescript-lib/init-typescript-lib.integtest.d.ts +1 -0
  163. package/tests/init-typescript-lib/init-typescript-lib.integtest.js +13 -0
  164. package/tests/uberpackage/uberpackage.integtest.d.ts +1 -0
  165. package/tests/uberpackage/uberpackage.integtest.js +11 -0
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shellWithAction = exports.randomInteger = exports.SamIntegrationTestFixture = exports.withSamIntegrationFixture = exports.withSamIntegrationCdkApp = void 0;
4
+ const child_process = require("child_process");
5
+ const fs = require("fs");
6
+ const os = require("os");
7
+ const path = require("path");
8
+ const axios_1 = require("axios");
9
+ const resources_1 = require("./resources");
10
+ const shell_1 = require("./shell");
11
+ const with_aws_1 = require("./with-aws");
12
+ const with_cdk_app_1 = require("./with-cdk-app");
13
+ /**
14
+ * Higher order function to execute a block with a SAM Integration CDK app fixture
15
+ */
16
+ function withSamIntegrationCdkApp(block) {
17
+ return async (context) => {
18
+ const randy = context.randomString;
19
+ const stackNamePrefix = `cdktest-${randy}`;
20
+ const integTestDir = path.join(os.tmpdir(), `cdk-integ-${randy}`);
21
+ context.log(` Stack prefix: ${stackNamePrefix}\n`);
22
+ context.log(` Test directory: ${integTestDir}\n`);
23
+ context.log(` Region: ${context.aws.region}\n`);
24
+ await with_cdk_app_1.cloneDirectory(path.join(resources_1.RESOURCES_DIR, 'cdk-apps', 'sam_cdk_integ_app'), integTestDir, context.output);
25
+ const fixture = new SamIntegrationTestFixture(integTestDir, stackNamePrefix, context.output, context.aws, context.randomString);
26
+ let success = true;
27
+ try {
28
+ const installationVersion = fixture.packages.requestedFrameworkVersion();
29
+ if (fixture.packages.majorVersion() === '1') {
30
+ await with_cdk_app_1.installNpmPackages(fixture, {
31
+ '@aws-cdk/aws-iam': installationVersion,
32
+ '@aws-cdk/aws-apigateway': installationVersion,
33
+ '@aws-cdk/aws-lambda': installationVersion,
34
+ '@aws-cdk/aws-lambda-go': installationVersion,
35
+ '@aws-cdk/aws-lambda-nodejs': installationVersion,
36
+ '@aws-cdk/aws-lambda-python': installationVersion,
37
+ '@aws-cdk/aws-logs': installationVersion,
38
+ '@aws-cdk/core': installationVersion,
39
+ 'constructs': '^3',
40
+ });
41
+ }
42
+ else {
43
+ const alphaInstallationVersion = fixture.packages.requestedAlphaVersion();
44
+ await with_cdk_app_1.installNpmPackages(fixture, {
45
+ 'aws-cdk-lib': installationVersion,
46
+ '@aws-cdk/aws-lambda-go-alpha': alphaInstallationVersion,
47
+ '@aws-cdk/aws-lambda-python-alpha': alphaInstallationVersion,
48
+ 'constructs': '^10',
49
+ });
50
+ }
51
+ await block(fixture);
52
+ }
53
+ catch (e) {
54
+ // We survive certain cases involving gopkg.in
55
+ if (errorCausedByGoPkg(e.message)) {
56
+ return;
57
+ }
58
+ success = false;
59
+ throw e;
60
+ }
61
+ finally {
62
+ if (process.env.INTEG_NO_CLEAN) {
63
+ context.log(`Left test directory in '${integTestDir}' ($INTEG_NO_CLEAN)\n`);
64
+ }
65
+ else {
66
+ await fixture.dispose(success);
67
+ }
68
+ }
69
+ };
70
+ }
71
+ exports.withSamIntegrationCdkApp = withSamIntegrationCdkApp;
72
+ /**
73
+ * Return whether or not the error is being caused by gopkg.in being down
74
+ *
75
+ * Our Go build depends on https://gopkg.in/, which has errors pretty often
76
+ * (every couple of days). It is run by a single volunteer.
77
+ */
78
+ function errorCausedByGoPkg(error) {
79
+ // The error is different depending on what request fails. Messages recognized:
80
+ ////////////////////////////////////////////////////////////////////
81
+ // go: github.com/aws/aws-lambda-go@v1.28.0 requires
82
+ // gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git ls-remote -q origin in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128:
83
+ // remote: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
84
+ // fatal: unable to access 'https://gopkg.in/yaml.v3/': The requested URL returned error: 502
85
+ ////////////////////////////////////////////////////////////////////
86
+ // go: downloading github.com/aws/aws-lambda-go v1.28.0
87
+ // go: github.com/aws/aws-lambda-go@v1.28.0 requires
88
+ // gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: unrecognized import path "gopkg.in/yaml.v3": reading https://gopkg.in/yaml.v3?go-get=1: 502 Bad Gateway
89
+ // server response: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
90
+ ////////////////////////////////////////////////////////////////////
91
+ // go: github.com/aws/aws-lambda-go@v1.28.0 requires
92
+ // gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128:
93
+ // error: RPC failed; HTTP 502 curl 22 The requested URL returned error: 502
94
+ // fatal: the remote end hung up unexpectedly
95
+ ////////////////////////////////////////////////////////////////////
96
+ return (error.includes('gopkg\.in.*invalid version.*exit status 128')
97
+ || error.match(/unrecognized import path[^\n]gopkg\.in/));
98
+ }
99
+ /**
100
+ * SAM Integration test fixture for CDK - SAM integration test cases
101
+ */
102
+ function withSamIntegrationFixture(block) {
103
+ return with_aws_1.withAws(withSamIntegrationCdkApp(block));
104
+ }
105
+ exports.withSamIntegrationFixture = withSamIntegrationFixture;
106
+ class SamIntegrationTestFixture extends with_cdk_app_1.TestFixture {
107
+ async samShell(command, filter, action, options = {}) {
108
+ return shellWithAction(command, filter, action, {
109
+ output: this.output,
110
+ cwd: path.join(this.integTestDir, 'cdk.out').toString(),
111
+ ...options,
112
+ });
113
+ }
114
+ async samBuild(stackName) {
115
+ const fullStackName = this.fullStackName(stackName);
116
+ const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`);
117
+ const args = ['--template', templatePath.toString()];
118
+ return this.samShell(['sam', 'build', ...args]);
119
+ }
120
+ async samLocalStartApi(stackName, isBuilt, port, apiPath) {
121
+ const fullStackName = this.fullStackName(stackName);
122
+ const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`);
123
+ const args = isBuilt ? [] : ['--template', templatePath.toString()];
124
+ args.push('--port');
125
+ args.push(port.toString());
126
+ return this.samShell(['sam', 'local', 'start-api', ...args], '(Press CTRL+C to quit)', () => {
127
+ return new Promise((resolve, reject) => {
128
+ axios_1.default.get(`http://127.0.0.1:${port}${apiPath}`).then(resp => {
129
+ resolve(resp.data);
130
+ }).catch(error => {
131
+ reject(new Error(`Failed to invoke api path ${apiPath} on port ${port} with error ${error}`));
132
+ });
133
+ });
134
+ });
135
+ }
136
+ /**
137
+ * Cleanup leftover stacks and buckets
138
+ */
139
+ async dispose(success) {
140
+ // If the tests completed successfully, happily delete the fixture
141
+ // (otherwise leave it for humans to inspect)
142
+ if (success) {
143
+ shell_1.rimraf(this.integTestDir);
144
+ }
145
+ }
146
+ }
147
+ exports.SamIntegrationTestFixture = SamIntegrationTestFixture;
148
+ function randomInteger(min, max) {
149
+ return Math.floor(Math.random() * (max - min) + min);
150
+ }
151
+ exports.randomInteger = randomInteger;
152
+ /**
153
+ * A shell command that does what you want
154
+ *
155
+ * Is platform-aware, handles errors nicely.
156
+ */
157
+ async function shellWithAction(command, filter, action, options = {}) {
158
+ if (options.modEnv && options.env) {
159
+ throw new Error('Use either env or modEnv but not both');
160
+ }
161
+ options.output?.write(`💻 ${command.join(' ')}\n`);
162
+ const env = options.env ?? (options.modEnv ? { ...process.env, ...options.modEnv } : undefined);
163
+ const child = child_process.spawn(command[0], command.slice(1), {
164
+ ...options,
165
+ env,
166
+ // Need this for Windows where we want .cmd and .bat to be found as well.
167
+ shell: true,
168
+ stdio: ['ignore', 'pipe', 'pipe'],
169
+ });
170
+ return new Promise((resolve, reject) => {
171
+ const out = new Array();
172
+ const stdout = new Array();
173
+ const stderr = new Array();
174
+ let actionSucceeded = false;
175
+ let actionOutput;
176
+ let actionExecuted = false;
177
+ function executeAction(chunk) {
178
+ out.push(chunk);
179
+ if (!actionExecuted && typeof filter === 'string' && out.toString().includes(filter) && typeof action === 'function') {
180
+ actionExecuted = true;
181
+ options.output?.write('before executing action');
182
+ action().then((output) => {
183
+ options.output?.write(`action output is ${output}`);
184
+ actionOutput = output;
185
+ actionSucceeded = true;
186
+ }).catch((error) => {
187
+ options.output?.write(`action error is ${error}`);
188
+ actionSucceeded = false;
189
+ actionOutput = error;
190
+ }).finally(() => {
191
+ options.output?.write('terminate sam sub process');
192
+ killSubProcess(child, command.join(' '));
193
+ });
194
+ }
195
+ }
196
+ child.stdout.on('data', chunk => {
197
+ options.output?.write(chunk);
198
+ stdout.push(chunk);
199
+ executeAction(chunk);
200
+ });
201
+ child.stderr.on('data', chunk => {
202
+ options.output?.write(chunk);
203
+ if (options.captureStderr ?? true) {
204
+ stderr.push(chunk);
205
+ }
206
+ executeAction(chunk);
207
+ });
208
+ child.once('error', reject);
209
+ child.once('close', code => {
210
+ const output = (Buffer.concat(stdout).toString('utf-8') + Buffer.concat(stderr).toString('utf-8')).trim();
211
+ if (code == null || code === 0 || options.allowErrExit) {
212
+ let result = new Array();
213
+ result.push(actionOutput);
214
+ result.push(output);
215
+ resolve({
216
+ actionSucceeded: actionSucceeded,
217
+ actionOutput: actionOutput,
218
+ shellOutput: output,
219
+ });
220
+ }
221
+ else {
222
+ reject(new Error(`'${command.join(' ')}' exited with error code ${code}. Output: \n${output}`));
223
+ }
224
+ });
225
+ });
226
+ }
227
+ exports.shellWithAction = shellWithAction;
228
+ function killSubProcess(child, command) {
229
+ /**
230
+ * Check if the sub process is running in container, so child_process.spawn will
231
+ * create multiple processes, and to kill all of them we need to run different logic
232
+ */
233
+ if (fs.existsSync('/.dockerenv')) {
234
+ child_process.exec(`for pid in $(ps -ef | grep "${command}" | awk '{print $2}'); do kill -2 $pid; done`);
235
+ }
236
+ else {
237
+ child.kill('SIGINT');
238
+ }
239
+ }
240
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC1zYW0uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ3aXRoLXNhbS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQ0FBK0M7QUFDL0MseUJBQXlCO0FBQ3pCLHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFDN0IsaUNBQTBCO0FBRTFCLDJDQUE0QztBQUM1QyxtQ0FBK0M7QUFDL0MseUNBQWlEO0FBQ2pELGlEQUFpRjtBQVNqRjs7R0FFRztBQUNILFNBQWdCLHdCQUF3QixDQUFxQyxLQUE0RDtJQUN2SSxPQUFPLEtBQUssRUFBRSxPQUFVLEVBQUUsRUFBRTtRQUMxQixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDO1FBQ25DLE1BQU0sZUFBZSxHQUFHLFdBQVcsS0FBSyxFQUFFLENBQUM7UUFDM0MsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsYUFBYSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBRWxFLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLGVBQWUsSUFBSSxDQUFDLENBQUM7UUFDckQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsWUFBWSxJQUFJLENBQUMsQ0FBQztRQUNsRCxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixPQUFPLENBQUMsR0FBRyxDQUFDLE1BQU0sSUFBSSxDQUFDLENBQUM7UUFFeEQsTUFBTSw2QkFBYyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMseUJBQWEsRUFBRSxVQUFVLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzlHLE1BQU0sT0FBTyxHQUFHLElBQUkseUJBQXlCLENBQzNDLFlBQVksRUFDWixlQUFlLEVBQ2YsT0FBTyxDQUFDLE1BQU0sRUFDZCxPQUFPLENBQUMsR0FBRyxFQUNYLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUV4QixJQUFJLE9B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QUMsQ0FBQztJQUVMLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQS9FRCwwQ0ErRUM7QUFFRCxTQUFTLGNBQWMsQ0FBQyxLQUFpQyxFQUFFLE9BQWU7SUFDeEU7OztPQUdHO0lBQ0gsSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxFQUFFO1FBQ2hDLGFBQWEsQ0FBQyxJQUFJLENBQUMsK0JBQStCLE9BQU8sOENBQThDLENBQUMsQ0FBQztLQUMxRztTQUFNO1FBQ0wsS0FBSyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztLQUN0QjtBQUVILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjaGlsZF9wcm9jZXNzIGZyb20gJ2NoaWxkX3Byb2Nlc3MnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCBheGlvcyBmcm9tICdheGlvcyc7XG5pbXBvcnQgeyBUZXN0Q29udGV4dCB9IGZyb20gJy4vaW50ZWctdGVzdCc7XG5pbXBvcnQgeyBSRVNPVVJDRVNfRElSIH0gZnJvbSAnLi9yZXNvdXJjZXMnO1xuaW1wb3J0IHsgU2hlbGxPcHRpb25zLCByaW1yYWYgfSBmcm9tICcuL3NoZWxsJztcbmltcG9ydCB7IEF3c0NvbnRleHQsIHdpdGhBd3MgfSBmcm9tICcuL3dpdGgtYXdzJztcbmltcG9ydCB7IGNsb25lRGlyZWN0b3J5LCBpbnN0YWxsTnBtUGFja2FnZXMsIFRlc3RGaXh0dXJlIH0gZnJvbSAnLi93aXRoLWNkay1hcHAnO1xuXG5cbmV4cG9ydCBpbnRlcmZhY2UgQWN0aW9uT3V0cHV0IHtcbiAgYWN0aW9uU3VjY2VlZGVkPzogYm9vbGVhbjtcbiAgYWN0aW9uT3V0cHV0PzogYW55O1xuICBzaGVsbE91dHB1dD86IHN0cmluZztcbn1cblxuLyoqXG4gKiBIaWdoZXIgb3JkZXIgZnVuY3Rpb24gdG8gZXhlY3V0ZSBhIGJsb2NrIHdpdGggYSBTQU0gSW50ZWdyYXRpb24gQ0RLIGFwcCBmaXh0dXJlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRoU2FtSW50ZWdyYXRpb25DZGtBcHA8QSBleHRlbmRzIFRlc3RDb250ZXh0ICYgQXdzQ29udGV4dD4oYmxvY2s6IChjb250ZXh0OiBTYW1JbnRlZ3JhdGlvblRlc3RGaXh0dXJlKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiBhc3luYyAoY29udGV4dDogQSkgPT4ge1xuICAgIGNvbnN0IHJhbmR5ID0gY29udGV4dC5yYW5kb21TdHJpbmc7XG4gICAgY29uc3Qgc3RhY2tOYW1lUHJlZml4ID0gYGNka3Rlc3QtJHtyYW5keX1gO1xuICAgIGNvbnN0IGludGVnVGVzdERpciA9IHBhdGguam9pbihvcy50bXBkaXIoKSwgYGNkay1pbnRlZy0ke3JhbmR5fWApO1xuXG4gICAgY29udGV4dC5sb2coYCBTdGFjayBwcmVmaXg6ICAgJHtzdGFja05hbWVQcmVmaXh9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBUZXN0IGRpcmVjdG9yeTogJHtpbnRlZ1Rlc3REaXJ9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBSZWdpb246ICAgICAgICAgJHtjb250ZXh0LmF3cy5yZWdpb259XFxuYCk7XG5cbiAgICBhd2FpdCBjbG9uZURpcmVjdG9yeShwYXRoLmpvaW4oUkVTT1VSQ0VTX0RJUiwgJ2Nkay1hcHBzJywgJ3NhbV9jZGtfaW50ZWdfYXBwJyksIGludGVnVGVzdERpciwgY29udGV4dC5vdXRwdXQpO1xuICAgIGNvbnN0IGZpeHR1cmUgPSBuZXcgU2FtSW50ZWdyYXRpb25UZXN0Rml4dHVyZShcbiAgICAgIGludGVnVGVzdERpcixcbiAgICAgIHN0YWNrTmFtZVByZWZpeCxcbiAgICAgIGNvbnRleHQub3V0cHV0LFxuICAgICAgY29udGV4dC5hd3MsXG4gICAgICBjb250ZXh0LnJhbmRvbVN0cmluZyk7XG5cbiAgICBsZXQgc3VjY2VzcyA9IHRydWU7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGluc3RhbGxhdGlvblZlcnNpb24gPSBmaXh0dXJlLnBhY2thZ2VzLnJlcXVlc3RlZEZyYW1ld29ya1ZlcnNpb24oKTtcblxuICAgICAgaWYgKGZpeHR1cmUucGFja2FnZXMubWFqb3JWZXJzaW9uKCkgPT09ICcxJykge1xuICAgICAgICBhd2FpdCBpbnN0YWxsTnBtUGFja2FnZXMoZml4dHVyZSwge1xuICAgICAgICAgICdAYXdzLWNkay9hd3MtaWFtJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgICAnQGF3cy1jZGsvYXdzLWFwaWdhdGV3YXknOiBpbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdAYXdzLWNkay9hd3MtbGFtYmRhJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgICAnQGF3cy1jZGsvYXdzLWxhbWJkYS1nbyc6IGluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtbm9kZWpzJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgICAnQGF3cy1jZGsvYXdzLWxhbWJkYS1weXRob24nOiBpbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdAYXdzLWNkay9hd3MtbG9ncyc6IGluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2NvcmUnOiBpbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdjb25zdHJ1Y3RzJzogJ14zJyxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBhbHBoYUluc3RhbGxhdGlvblZlcnNpb24gPSBmaXh0dXJlLnBhY2thZ2VzLnJlcXVlc3RlZEFscGhhVmVyc2lvbigpO1xuICAgICAgICBhd2FpdCBpbnN0YWxsTnBtUGFja2FnZXMoZml4dHVyZSwge1xuICAgICAgICAgICdhd3MtY2RrLWxpYic6IGluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtZ28tYWxwaGEnOiBhbHBoYUluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtcHl0aG9uLWFscGhhJzogYWxwaGFJbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdjb25zdHJ1Y3RzJzogJ14xMCcsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgYXdhaXQgYmxvY2soZml4dHVyZSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gV2Ugc3Vydml2ZSBjZXJ0YWluIGNhc2VzIGludm9sdmluZyBnb3BrZy5pblxuICAgICAgaWYgKGVycm9yQ2F1c2VkQnlHb1BrZyhlLm1lc3NhZ2UpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHN1Y2Nlc3MgPSBmYWxzZTtcbiAgICAgIHRocm93IGU7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGlmIChwcm9jZXNzLmVudi5JTlRFR19OT19DTEVBTikge1xuICAgICAgICBjb250ZXh0LmxvZyhgTGVmdCB0ZXN0IGRpcmVjdG9yeSBpbiAnJHtpbnRlZ1Rlc3REaXJ9JyAoJElOVEVHX05PX0NMRUFOKVxcbmApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYXdhaXQgZml4dHVyZS5kaXNwb3NlKHN1Y2Nlc3MpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbn1cblxuLyoqXG4gKiBSZXR1cm4gd2hldGhlciBvciBub3QgdGhlIGVycm9yIGlzIGJlaW5nIGNhdXNlZCBieSBnb3BrZy5pbiBiZWluZyBkb3duXG4gKlxuICogT3VyIEdvIGJ1aWxkIGRlcGVuZHMgb24gaHR0cHM6Ly9nb3BrZy5pbi8sIHdoaWNoIGhhcyBlcnJvcnMgcHJldHR5IG9mdGVuXG4gKiAoZXZlcnkgY291cGxlIG9mIGRheXMpLiBJdCBpcyBydW4gYnkgYSBzaW5nbGUgdm9sdW50ZWVyLlxuICovXG5mdW5jdGlvbiBlcnJvckNhdXNlZEJ5R29Qa2coZXJyb3I6IHN0cmluZykge1xuICAvLyBUaGUgZXJyb3IgaXMgZGlmZmVyZW50IGRlcGVuZGluZyBvbiB3aGF0IHJlcXVlc3QgZmFpbHMuIE1lc3NhZ2VzIHJlY29nbml6ZWQ6XG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vICAgIGdvOiBnaXRodWIuY29tL2F3cy9hd3MtbGFtYmRhLWdvQHYxLjI4LjAgcmVxdWlyZXNcbiAgLy8gICAgICAgIGdvcGtnLmluL3lhbWwudjNAdjMuMC4wLTIwMjAwNjE1MTEzNDEzLWVlZWNhNDhmZTc3NjogaW52YWxpZCB2ZXJzaW9uOiBnaXQgbHMtcmVtb3RlIC1xIG9yaWdpbiBpbiAvZ28vcGtnL21vZC9jYWNoZS92Y3MvMDkwMWRjMWVmNjdmY2NlMWM5YjNhZTUxMDc4NzQwZGU0YTBlMmRjNjczZTcyMDU4NGFjMzAyOTczYWY4MmYzNjogZXhpdCBzdGF0dXMgMTI4OlxuICAvLyAgICAgICAgcmVtb3RlOiBDYW5ub3Qgb2J0YWluIHJlZnMgZnJvbSBHaXRIdWI6IGNhbm5vdCB0YWxrIHRvIEdpdEh1YjogR2V0IGh0dHBzOi8vZ2l0aHViLmNvbS9nby15YW1sL3lhbWwuZ2l0L2luZm8vcmVmcz9zZXJ2aWNlPWdpdC11cGxvYWQtcGFjazogbmV0L2h0dHA6IHJlcXVlc3QgY2FuY2VsZWQgKENsaWVudC5UaW1lb3V0IGV4Y2VlZGVkIHdoaWxlIGF3YWl0aW5nIGhlYWRlcnMpXG4gIC8vICAgICAgICBmYXRhbDogdW5hYmxlIHRvIGFjY2VzcyAnaHR0cHM6Ly9nb3BrZy5pbi95YW1sLnYzLyc6IFRoZSByZXF1ZXN0ZWQgVVJMIHJldHVybmVkIGVycm9yOiA1MDJcbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gICAgZ286IGRvd25sb2FkaW5nIGdpdGh1Yi5jb20vYXdzL2F3cy1sYW1iZGEtZ28gdjEuMjguMFxuICAvLyAgICBnbzogZ2l0aHViLmNvbS9hd3MvYXdzLWxhbWJkYS1nb0B2MS4yOC4wIHJlcXVpcmVzXG4gIC8vICAgICAgICBnb3BrZy5pbi95YW1sLnYzQHYzLjAuMC0yMDIwMDYxNTExMzQxMy1lZWVjYTQ4ZmU3NzY6IHVucmVjb2duaXplZCBpbXBvcnQgcGF0aCBcImdvcGtnLmluL3lhbWwudjNcIjogcmVhZGluZyBodHRwczovL2dvcGtnLmluL3lhbWwudjM/Z28tZ2V0PTE6IDUwMiBCYWQgR2F0ZXdheVxuICAvLyAgICAgICAgc2VydmVyIHJlc3BvbnNlOiBDYW5ub3Qgb2J0YWluIHJlZnMgZnJvbSBHaXRIdWI6IGNhbm5vdCB0YWxrIHRvIEdpdEh1YjogR2V0IGh0dHBzOi8vZ2l0aHViLmNvbS9nby15YW1sL3lhbWwuZ2l0L2luZm8vcmVmcz9zZXJ2aWNlPWdpdC11cGxvYWQtcGFjazogbmV0L2h0dHA6IHJlcXVlc3QgY2FuY2VsZWQgKENsaWVudC5UaW1lb3V0IGV4Y2VlZGVkIHdoaWxlIGF3YWl0aW5nIGhlYWRlcnMpXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vICAgIGdvOiBnaXRodWIuY29tL2F3cy9hd3MtbGFtYmRhLWdvQHYxLjI4LjAgcmVxdWlyZXNcbiAgLy8gICAgICAgIGdvcGtnLmluL3lhbWwudjNAdjMuMC4wLTIwMjAwNjE1MTEzNDEzLWVlZWNhNDhmZTc3NjogaW52YWxpZCB2ZXJzaW9uOiBnaXQgZmV0Y2ggLWYgb3JpZ2luIHJlZnMvaGVhZHMvKjpyZWZzL2hlYWRzLyogcmVmcy90YWdzLyo6cmVmcy90YWdzLyogaW4gL2dvL3BrZy9tb2QvY2FjaGUvdmNzLzA5MDFkYzFlZjY3ZmNjZTFjOWIzYWU1MTA3ODc0MGRlNGEwZTJkYzY3M2U3MjA1ODRhYzMwMjk3M2FmODJmMzY6IGV4aXQgc3RhdHVzIDEyODpcbiAgLy8gICAgICAgIGVycm9yOiBSUEMgZmFpbGVkOyBIVFRQIDUwMiBjdXJsIDIyIFRoZSByZXF1ZXN0ZWQgVVJMIHJldHVybmVkIGVycm9yOiA1MDJcbiAgLy8gICAgICAgIGZhdGFsOiB0aGUgcmVtb3RlIGVuZCBodW5nIHVwIHVuZXhwZWN0ZWRseVxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gIHJldHVybiAoZXJyb3IuaW5jbHVkZXMoJ2dvcGtnXFwuaW4uKmludmFsaWQgdmVyc2lvbi4qZXhpdCBzdGF0dXMgMTI4JylcbiAgICB8fCBlcnJvci5tYXRjaCgvdW5yZWNvZ25pemVkIGltcG9ydCBwYXRoW15cXG5dZ29wa2dcXC5pbi8pKTtcbn1cblxuLyoqXG4gKiBTQU0gSW50ZWdyYXRpb24gdGVzdCBmaXh0dXJlIGZvciBDREsgLSBTQU0gaW50ZWdyYXRpb24gdGVzdCBjYXNlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aFNhbUludGVncmF0aW9uRml4dHVyZShibG9jazogKGNvbnRleHQ6IFNhbUludGVncmF0aW9uVGVzdEZpeHR1cmUpID0+IFByb21pc2U8dm9pZD4pIHtcbiAgcmV0dXJuIHdpdGhBd3M8VGVzdENvbnRleHQ+KHdpdGhTYW1JbnRlZ3JhdGlvbkNka0FwcChibG9jaykpO1xufVxuXG5leHBvcnQgY2xhc3MgU2FtSW50ZWdyYXRpb25UZXN0Rml4dHVyZSBleHRlbmRzIFRlc3RGaXh0dXJlIHtcbiAgcHVibGljIGFzeW5jIHNhbVNoZWxsKGNvbW1hbmQ6IHN0cmluZ1tdLCBmaWx0ZXI/OiBzdHJpbmcsIGFjdGlvbj86ICgpID0+IGFueSwgb3B0aW9uczogT21pdDxTaGVsbE9wdGlvbnMsICdjd2QnIHwgJ291dHB1dCc+ID0ge30pOiBQcm9taXNlPEFjdGlvbk91dHB1dD4ge1xuICAgIHJldHVybiBzaGVsbFdpdGhBY3Rpb24oY29tbWFuZCwgZmlsdGVyLCBhY3Rpb24sIHtcbiAgICAgIG91dHB1dDogdGhpcy5vdXRwdXQsXG4gICAgICBjd2Q6IHBhdGguam9pbih0aGlzLmludGVnVGVzdERpciwgJ2Nkay5vdXQnKS50b1N0cmluZygpLFxuICAgICAgLi4ub3B0aW9ucyxcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBzYW1CdWlsZChzdGFja05hbWU6IHN0cmluZykge1xuICAgIGNvbnN0IGZ1bGxTdGFja05hbWUgPSB0aGlzLmZ1bGxTdGFja05hbWUoc3RhY2tOYW1lKTtcbiAgICBjb25zdCB0ZW1wbGF0ZVBhdGggPSBwYXRoLmpvaW4odGhpcy5pbnRlZ1Rlc3REaXIsICdjZGsub3V0JywgYCR7ZnVsbFN0YWNrTmFtZX0udGVtcGxhdGUuanNvbmApO1xuICAgIGNvbnN0IGFyZ3MgPSBbJy0tdGVtcGxhdGUnLCB0ZW1wbGF0ZVBhdGgudG9TdHJpbmcoKV07XG4gICAgcmV0dXJuIHRoaXMuc2FtU2hlbGwoWydzYW0nLCAnYnVpbGQnLCAuLi5hcmdzXSk7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgc2FtTG9jYWxTdGFydEFwaShzdGFja05hbWU6IHN0cmluZywgaXNCdWlsdDogYm9vbGVhbiwgcG9ydDogbnVtYmVyLCBhcGlQYXRoOiBzdHJpbmcpOiBQcm9taXNlPEFjdGlvbk91dHB1dD4ge1xuICAgIGNvbnN0IGZ1bGxTdGFja05hbWUgPSB0aGlzLmZ1bGxTdGFja05hbWUoc3RhY2tOYW1lKTtcbiAgICBjb25zdCB0ZW1wbGF0ZVBhdGggPSBwYXRoLmpvaW4odGhpcy5pbnRlZ1Rlc3REaXIsICdjZGsub3V0JywgYCR7ZnVsbFN0YWNrTmFtZX0udGVtcGxhdGUuanNvbmApO1xuICAgIGNvbnN0IGFyZ3MgPSBpc0J1aWx0PyBbXSA6IFsnLS10ZW1wbGF0ZScsIHRlbXBsYXRlUGF0aC50b1N0cmluZygpXTtcbiAgICBhcmdzLnB1c2goJy0tcG9ydCcpO1xuICAgIGFyZ3MucHVzaChwb3J0LnRvU3RyaW5nKCkpO1xuXG4gICAgcmV0dXJuIHRoaXMuc2FtU2hlbGwoWydzYW0nLCAnbG9jYWwnLCAnc3RhcnQtYXBpJywgLi4uYXJnc10sICcoUHJlc3MgQ1RSTCtDIHRvIHF1aXQpJywgKCk9PntcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxBY3Rpb25PdXRwdXQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgYXhpb3MuZ2V0KGBodHRwOi8vMTI3LjAuMC4xOiR7cG9ydH0ke2FwaVBhdGh9YCkudGhlbiggcmVzcCA9PiB7XG4gICAgICAgICAgcmVzb2x2ZShyZXNwLmRhdGEpO1xuICAgICAgICB9KS5jYXRjaCggZXJyb3IgPT4ge1xuICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBpbnZva2UgYXBpIHBhdGggJHthcGlQYXRofSBvbiBwb3J0ICR7cG9ydH0gd2l0aCBlcnJvciAke2Vycm9yfWApKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhbnVwIGxlZnRvdmVyIHN0YWNrcyBhbmQgYnVja2V0c1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGRpc3Bvc2Uoc3VjY2VzczogYm9vbGVhbikge1xuICAgIC8vIElmIHRoZSB0ZXN0cyBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5LCBoYXBwaWx5IGRlbGV0ZSB0aGUgZml4dHVyZVxuICAgIC8vIChvdGhlcndpc2UgbGVhdmUgaXQgZm9yIGh1bWFucyB0byBpbnNwZWN0KVxuICAgIGlmIChzdWNjZXNzKSB7XG4gICAgICByaW1yYWYodGhpcy5pbnRlZ1Rlc3REaXIpO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmFuZG9tSW50ZWdlcihtaW46IG51bWJlciwgbWF4OiBudW1iZXIpIHtcbiAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4pICsgbWluKTtcbn1cblxuLyoqXG4gKiBBIHNoZWxsIGNvbW1hbmQgdGhhdCBkb2VzIHdoYXQgeW91IHdhbnRcbiAqXG4gKiBJcyBwbGF0Zm9ybS1hd2FyZSwgaGFuZGxlcyBlcnJvcnMgbmljZWx5LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2hlbGxXaXRoQWN0aW9uKFxuICBjb21tYW5kOiBzdHJpbmdbXSwgZmlsdGVyPzogc3RyaW5nLCBhY3Rpb24/OiAoKSA9PiBQcm9taXNlPGFueT4sIG9wdGlvbnM6IFNoZWxsT3B0aW9ucyA9IHt9KTogUHJvbWlzZTxBY3Rpb25PdXRwdXQ+IHtcbiAgaWYgKG9wdGlvbnMubW9kRW52ICYmIG9wdGlvbnMuZW52KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdVc2UgZWl0aGVyIGVudiBvciBtb2RFbnYgYnV0IG5vdCBib3RoJyk7XG4gIH1cblxuICBvcHRpb25zLm91dHB1dD8ud3JpdGUoYPCfkrsgJHtjb21tYW5kLmpvaW4oJyAnKX1cXG5gKTtcblxuICBjb25zdCBlbnYgPSBvcHRpb25zLmVudiA/PyAob3B0aW9ucy5tb2RFbnYgPyB7IC4uLnByb2Nlc3MuZW52LCAuLi5vcHRpb25zLm1vZEVudiB9IDogdW5kZWZpbmVkKTtcblxuICBjb25zdCBjaGlsZCA9IGNoaWxkX3Byb2Nlc3Muc3Bhd24oY29tbWFuZFswXSwgY29tbWFuZC5zbGljZSgxKSwge1xuICAgIC4uLm9wdGlvbnMsXG4gICAgZW52LFxuICAgIC8vIE5lZWQgdGhpcyBmb3IgV2luZG93cyB3aGVyZSB3ZSB3YW50IC5jbWQgYW5kIC5iYXQgdG8gYmUgZm91bmQgYXMgd2VsbC5cbiAgICBzaGVsbDogdHJ1ZSxcbiAgICBzdGRpbzogWydpZ25vcmUnLCAncGlwZScsICdwaXBlJ10sXG4gIH0pO1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZTxBY3Rpb25PdXRwdXQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjb25zdCBvdXQgPSBuZXcgQXJyYXk8QnVmZmVyPigpO1xuICAgIGNvbnN0IHN0ZG91dCA9IG5ldyBBcnJheTxCdWZmZXI+KCk7XG4gICAgY29uc3Qgc3RkZXJyID0gbmV3IEFycmF5PEJ1ZmZlcj4oKTtcbiAgICBsZXQgYWN0aW9uU3VjY2VlZGVkID0gZmFsc2U7XG4gICAgbGV0IGFjdGlvbk91dHB1dDogYW55O1xuICAgIGxldCBhY3Rpb25FeGVjdXRlZCA9IGZhbHNlO1xuXG4gICAgZnVuY3Rpb24gZXhlY3V0ZUFjdGlvbihjaHVuazogYW55KSB7XG4gICAgICBvdXQucHVzaChjaHVuayk7XG4gICAgICBpZiAoIWFjdGlvbkV4ZWN1dGVkICYmIHR5cGVvZiBmaWx0ZXIgPT09ICdzdHJpbmcnICYmIG91dC50b1N0cmluZygpLmluY2x1ZGVzKGZpbHRlcikgJiYgdHlwZW9mIGFjdGlvbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBhY3Rpb25FeGVjdXRlZCA9IHRydWU7XG4gICAgICAgIG9wdGlvbnMub3V0cHV0Py53cml0ZSgnYmVmb3JlIGV4ZWN1dGluZyBhY3Rpb24nKTtcbiAgICAgICAgYWN0aW9uKCkudGhlbigob3V0cHV0KSA9PiB7XG4gICAgICAgICAgb3B0aW9ucy5vdXRwdXQ/LndyaXRlKGBhY3Rpb24gb3V0cHV0IGlzICR7b3V0cHV0fWApO1xuICAgICAgICAgIGFjdGlvbk91dHB1dCA9IG91dHB1dDtcbiAgICAgICAgICBhY3Rpb25TdWNjZWVkZWQgPSB0cnVlO1xuICAgICAgICB9KS5jYXRjaCgoZXJyb3IpID0+IHtcbiAgICAgICAgICBvcHRpb25zLm91dHB1dD8ud3JpdGUoYGFjdGlvbiBlcnJvciBpcyAke2Vycm9yfWApO1xuICAgICAgICAgIGFjdGlvblN1Y2NlZWRlZCA9IGZhbHNlO1xuICAgICAgICAgIGFjdGlvbk91dHB1dCA9IGVycm9yO1xuICAgICAgICB9KS5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgICBvcHRpb25zLm91dHB1dD8ud3JpdGUoJ3Rlcm1pbmF0ZSBzYW0gc3ViIHByb2Nlc3MnKTtcbiAgICAgICAgICBraWxsU3ViUHJvY2VzcyhjaGlsZCwgY29tbWFuZC5qb2luKCcgJykpO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjaGlsZC5zdGRvdXQhLm9uKCdkYXRhJywgY2h1bmsgPT4ge1xuICAgICAgb3B0aW9ucy5vdXRwdXQ/LndyaXRlKGNodW5rKTtcbiAgICAgIHN0ZG91dC5wdXNoKGNodW5rKTtcbiAgICAgIGV4ZWN1dGVBY3Rpb24oY2h1bmspO1xuICAgIH0pO1xuXG4gICAgY2hpbGQuc3RkZXJyIS5vbignZGF0YScsIGNodW5rID0+IHtcbiAgICAgIG9wdGlvbnMub3V0cHV0Py53cml0ZShjaHVuayk7XG4gICAgICBpZiAob3B0aW9ucy5jYXB0dXJlU3RkZXJyID8/IHRydWUpIHtcbiAgICAgICAgc3RkZXJyLnB1c2goY2h1bmspO1xuICAgICAgfVxuICAgICAgZXhlY3V0ZUFjdGlvbihjaHVuayk7XG4gICAgfSk7XG5cbiAgICBjaGlsZC5vbmNlKCdlcnJvcicsIHJlamVjdCk7XG5cbiAgICBjaGlsZC5vbmNlKCdjbG9zZScsIGNvZGUgPT4ge1xuICAgICAgY29uc3Qgb3V0cHV0ID0gKEJ1ZmZlci5jb25jYXQoc3Rkb3V0KS50b1N0cmluZygndXRmLTgnKSArIEJ1ZmZlci5jb25jYXQoc3RkZXJyKS50b1N0cmluZygndXRmLTgnKSkudHJpbSgpO1xuICAgICAgaWYgKGNvZGUgPT0gbnVsbCB8fCBjb2RlID09PSAwIHx8IG9wdGlvbnMuYWxsb3dFcnJFeGl0KSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBuZXcgQXJyYXk8c3RyaW5nPigpO1xuICAgICAgICByZXN1bHQucHVzaChhY3Rpb25PdXRwdXQpO1xuICAgICAgICByZXN1bHQucHVzaChvdXRwdXQpO1xuICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICBhY3Rpb25TdWNjZWVkZWQ6IGFjdGlvblN1Y2NlZWRlZCxcbiAgICAgICAgICBhY3Rpb25PdXRwdXQ6IGFjdGlvbk91dHB1dCxcbiAgICAgICAgICBzaGVsbE91dHB1dDogb3V0cHV0LFxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlamVjdChuZXcgRXJyb3IoYCcke2NvbW1hbmQuam9pbignICcpfScgZXhpdGVkIHdpdGggZXJyb3IgY29kZSAke2NvZGV9LiBPdXRwdXQ6IFxcbiR7b3V0cHV0fWApKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICB9KTtcbn1cblxuZnVuY3Rpb24ga2lsbFN1YlByb2Nlc3MoY2hpbGQ6IGNoaWxkX3Byb2Nlc3MuQ2hpbGRQcm9jZXNzLCBjb21tYW5kOiBzdHJpbmcpIHtcbiAgLyoqXG4gICAqIENoZWNrIGlmIHRoZSBzdWIgcHJvY2VzcyBpcyBydW5uaW5nIGluIGNvbnRhaW5lciwgc28gY2hpbGRfcHJvY2Vzcy5zcGF3biB3aWxsXG4gICAqIGNyZWF0ZSBtdWx0aXBsZSBwcm9jZXNzZXMsIGFuZCB0byBraWxsIGFsbCBvZiB0aGVtIHdlIG5lZWQgdG8gcnVuIGRpZmZlcmVudCBsb2dpY1xuICAgKi9cbiAgaWYgKGZzLmV4aXN0c1N5bmMoJy8uZG9ja2VyZW52JykpIHtcbiAgICBjaGlsZF9wcm9jZXNzLmV4ZWMoYGZvciBwaWQgaW4gJChwcyAtZWYgfCBncmVwIFwiJHtjb21tYW5kfVwiIHwgYXdrICd7cHJpbnQgJDJ9Jyk7IGRvIGtpbGwgLTIgJHBpZDsgZG9uZWApO1xuICB9IGVsc2Uge1xuICAgIGNoaWxkLmtpbGwoJ1NJR0lOVCcpO1xuICB9XG5cbn0iXX0=
@@ -0,0 +1,5 @@
1
+ import { TestContext } from './integ-test';
2
+ export interface TemporaryDirectoryContext {
3
+ readonly integTestDir: string;
4
+ }
5
+ export declare function withTemporaryDirectory<A extends TestContext>(block: (context: A & TemporaryDirectoryContext) => Promise<void>): (context: A) => Promise<void>;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withTemporaryDirectory = void 0;
4
+ const fs = require("fs");
5
+ const os = require("os");
6
+ const path = require("path");
7
+ const shell_1 = require("./shell");
8
+ function withTemporaryDirectory(block) {
9
+ return async (context) => {
10
+ const integTestDir = path.join(os.tmpdir(), `cdk-integ-${context.randomString}`);
11
+ fs.mkdirSync(integTestDir, { recursive: true });
12
+ try {
13
+ await block({
14
+ ...context,
15
+ integTestDir,
16
+ });
17
+ // Clean up in case of success
18
+ if (process.env.SKIP_CLEANUP) {
19
+ context.log(`Left test directory in '${integTestDir}' ($SKIP_CLEANUP)\n`);
20
+ }
21
+ else {
22
+ shell_1.rimraf(integTestDir);
23
+ }
24
+ }
25
+ catch (e) {
26
+ context.log(`Left test directory in '${integTestDir}'\n`);
27
+ throw e;
28
+ }
29
+ };
30
+ }
31
+ exports.withTemporaryDirectory = withTemporaryDirectory;
32
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC10ZW1wb3JhcnktZGlyZWN0b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2l0aC10ZW1wb3JhcnktZGlyZWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlCQUF5QjtBQUN6Qix5QkFBeUI7QUFDekIsNkJBQTZCO0FBRTdCLG1DQUFpQztBQU1qQyxTQUFnQixzQkFBc0IsQ0FBd0IsS0FBZ0U7SUFDNUgsT0FBTyxLQUFLLEVBQUUsT0FBVSxFQUFFLEVBQUU7UUFDMUIsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsYUFBYSxPQUFPLENBQUMsWUFBWSxFQUFFLENBQUMsQ0FBQztRQUVqRixFQUFFLENBQUMsU0FBUyxDQUFDLFlBQVksRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBRWhELElBQUk7WUFDRixNQUFNLEtBQUssQ0FBQztnQkFDVixHQUFHLE9BQU87Z0JBQ1YsWUFBWTthQUNiLENBQUMsQ0FBQztZQUVILDhCQUE4QjtZQUM5QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFO2dCQUM1QixPQUFPLENBQUMsR0FBRyxDQUFDLDJCQUEyQixZQUFZLHFCQUFxQixDQUFDLENBQUM7YUFDM0U7aUJBQU07Z0JBQ0wsY0FBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO2FBQ3RCO1NBQ0Y7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLFlBQVksS0FBSyxDQUFDLENBQUM7WUFDMUQsTUFBTSxDQUFDLENBQUM7U0FDVDtJQUNILENBQUMsQ0FBQztBQUNKLENBQUM7QUF2QkQsd0RBdUJDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IFRlc3RDb250ZXh0IH0gZnJvbSAnLi9pbnRlZy10ZXN0JztcbmltcG9ydCB7IHJpbXJhZiB9IGZyb20gJy4vc2hlbGwnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFRlbXBvcmFyeURpcmVjdG9yeUNvbnRleHQge1xuICByZWFkb25seSBpbnRlZ1Rlc3REaXI6IHN0cmluZztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHdpdGhUZW1wb3JhcnlEaXJlY3Rvcnk8QSBleHRlbmRzIFRlc3RDb250ZXh0PihibG9jazogKGNvbnRleHQ6IEEgJiBUZW1wb3JhcnlEaXJlY3RvcnlDb250ZXh0KSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiBhc3luYyAoY29udGV4dDogQSkgPT4ge1xuICAgIGNvbnN0IGludGVnVGVzdERpciA9IHBhdGguam9pbihvcy50bXBkaXIoKSwgYGNkay1pbnRlZy0ke2NvbnRleHQucmFuZG9tU3RyaW5nfWApO1xuXG4gICAgZnMubWtkaXJTeW5jKGludGVnVGVzdERpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgYmxvY2soe1xuICAgICAgICAuLi5jb250ZXh0LFxuICAgICAgICBpbnRlZ1Rlc3REaXIsXG4gICAgICB9KTtcblxuICAgICAgLy8gQ2xlYW4gdXAgaW4gY2FzZSBvZiBzdWNjZXNzXG4gICAgICBpZiAocHJvY2Vzcy5lbnYuU0tJUF9DTEVBTlVQKSB7XG4gICAgICAgIGNvbnRleHQubG9nKGBMZWZ0IHRlc3QgZGlyZWN0b3J5IGluICcke2ludGVnVGVzdERpcn0nICgkU0tJUF9DTEVBTlVQKVxcbmApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmltcmFmKGludGVnVGVzdERpcik7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29udGV4dC5sb2coYExlZnQgdGVzdCBkaXJlY3RvcnkgaW4gJyR7aW50ZWdUZXN0RGlyfSdcXG5gKTtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICB9O1xufVxuXG4iXX0=
@@ -0,0 +1,43 @@
1
+ export declare class XpMutexPool {
2
+ readonly directory: string;
3
+ static fromDirectory(directory: string): XpMutexPool;
4
+ static fromName(name: string): XpMutexPool;
5
+ private readonly waitingResolvers;
6
+ private watcher;
7
+ private constructor();
8
+ mutex(name: string): XpMutex;
9
+ /**
10
+ * Await an unlock event
11
+ *
12
+ * (An unlock event is when a file in the directory gets deleted, with a tiny
13
+ * random sleep attached to it).
14
+ */
15
+ awaitUnlock(maxWaitMs?: number): Promise<void>;
16
+ private startWatch;
17
+ private notifyWaiters;
18
+ }
19
+ /**
20
+ * Cross-process mutex
21
+ *
22
+ * Uses the presence of a file on disk and `fs.watch` to represent the mutex
23
+ * and discover unlocks.
24
+ */
25
+ export declare class XpMutex {
26
+ private readonly pool;
27
+ readonly mutexName: string;
28
+ private readonly fileName;
29
+ constructor(pool: XpMutexPool, mutexName: string);
30
+ /**
31
+ * Try to acquire the lock (may fail)
32
+ */
33
+ tryAcquire(): Promise<ILock | undefined>;
34
+ /**
35
+ * Acquire the lock, waiting until we can
36
+ */
37
+ acquire(): Promise<ILock>;
38
+ private readPidFile;
39
+ private writePidFile;
40
+ }
41
+ export interface ILock {
42
+ release(): Promise<void>;
43
+ }
package/lib/xpmutex.js ADDED
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.XpMutex = exports.XpMutexPool = void 0;
4
+ const fs_1 = require("fs");
5
+ const os = require("os");
6
+ const path = require("path");
7
+ class XpMutexPool {
8
+ constructor(directory) {
9
+ this.directory = directory;
10
+ this.waitingResolvers = new Set();
11
+ this.startWatch();
12
+ }
13
+ static fromDirectory(directory) {
14
+ fs_1.mkdirSync(directory, { recursive: true });
15
+ return new XpMutexPool(directory);
16
+ }
17
+ static fromName(name) {
18
+ return XpMutexPool.fromDirectory(path.join(os.tmpdir(), name));
19
+ }
20
+ mutex(name) {
21
+ return new XpMutex(this, name);
22
+ }
23
+ /**
24
+ * Await an unlock event
25
+ *
26
+ * (An unlock event is when a file in the directory gets deleted, with a tiny
27
+ * random sleep attached to it).
28
+ */
29
+ awaitUnlock(maxWaitMs) {
30
+ const wait = new Promise(ok => {
31
+ this.waitingResolvers.add(async () => {
32
+ await randomSleep(10);
33
+ ok();
34
+ });
35
+ });
36
+ if (maxWaitMs) {
37
+ return Promise.race([wait, sleep(maxWaitMs)]);
38
+ }
39
+ else {
40
+ return wait;
41
+ }
42
+ }
43
+ startWatch() {
44
+ this.watcher = fs_1.watch(this.directory);
45
+ this.watcher.unref(); // @types doesn't know about this but it exists
46
+ this.watcher.on('change', async (eventType, fname) => {
47
+ // Only trigger on 'deletes'.
48
+ // After receiving the event, we check if the file exists.
49
+ // - If no: the file was deleted! Huzzah, this counts as a wakeup.
50
+ // - If yes: either the file was just created (in which case we don't need to wakeup)
51
+ // or the event was due to a delete but someone raced us to it and claimed the
52
+ // file already (in which case we also don't need to wake up).
53
+ if (eventType === 'rename' && !await fileExists(path.join(this.directory, fname.toString()))) {
54
+ this.notifyWaiters();
55
+ }
56
+ });
57
+ this.watcher.on('error', async (e) => {
58
+ // eslint-disable-next-line no-console
59
+ console.error(e);
60
+ await randomSleep(100);
61
+ this.startWatch();
62
+ });
63
+ }
64
+ notifyWaiters() {
65
+ for (const promise of this.waitingResolvers) {
66
+ promise();
67
+ }
68
+ this.waitingResolvers.clear();
69
+ }
70
+ }
71
+ exports.XpMutexPool = XpMutexPool;
72
+ /**
73
+ * Cross-process mutex
74
+ *
75
+ * Uses the presence of a file on disk and `fs.watch` to represent the mutex
76
+ * and discover unlocks.
77
+ */
78
+ class XpMutex {
79
+ constructor(pool, mutexName) {
80
+ this.pool = pool;
81
+ this.mutexName = mutexName;
82
+ this.fileName = path.join(pool.directory, `${mutexName}.mutex`);
83
+ }
84
+ /**
85
+ * Try to acquire the lock (may fail)
86
+ */
87
+ async tryAcquire() {
88
+ while (true) {
89
+ // Acquire lock by being the one to create the file
90
+ try {
91
+ return await this.writePidFile('wx'); // Fails if the file already exists
92
+ }
93
+ catch (e) {
94
+ if (e.code !== 'EEXIST') {
95
+ throw e;
96
+ }
97
+ }
98
+ // File already exists. Read the contents, see if it's an existent PID (if so, the lock is taken)
99
+ const ownerPid = await this.readPidFile();
100
+ if (ownerPid === undefined) {
101
+ // File got deleted just now, maybe we can acquire it again
102
+ continue;
103
+ }
104
+ if (processExists(ownerPid)) {
105
+ return undefined;
106
+ }
107
+ // If not, the lock is stale and will never be released anymore. We may
108
+ // delete it and acquire it anyway, but we may be racing someone else trying
109
+ // to do the same. Solve this as follows:
110
+ // - Try to acquire a lock that gives us permissions to declare the existing lock stale.
111
+ // - Sleep a small random period to reduce contention on this operation
112
+ await randomSleep(10);
113
+ const innerMux = new XpMutex(this.pool, `${this.mutexName}.${ownerPid}`);
114
+ const innerLock = await innerMux.tryAcquire();
115
+ if (!innerLock) {
116
+ return undefined;
117
+ }
118
+ // We may not release the 'inner lock' we used to acquire the rights to declare the other
119
+ // lock stale until we release the actual lock itself. If we did, other contenders might
120
+ // see it released while they're still in this fallback block and accidentally steal
121
+ // from a new legitimate owner.
122
+ return this.writePidFile('w', innerLock); // Force write lock file, attach inner lock as well
123
+ }
124
+ }
125
+ /**
126
+ * Acquire the lock, waiting until we can
127
+ */
128
+ async acquire() {
129
+ while (true) {
130
+ // Start the wait here, so we don't miss the signal if it comes after
131
+ // we try but before we sleep.
132
+ //
133
+ // We also periodically retry anyway since we may have missed the delete
134
+ // signal due to unfortunate timing.
135
+ const wait = this.pool.awaitUnlock(5000);
136
+ const lock = await this.acquire();
137
+ if (lock) {
138
+ // Ignore the wait (count as handled)
139
+ wait.then(() => { }, () => { });
140
+ return lock;
141
+ }
142
+ await wait;
143
+ await randomSleep(100);
144
+ }
145
+ }
146
+ async readPidFile() {
147
+ const deadLine = Date.now() + 1000;
148
+ while (Date.now() < deadLine) {
149
+ let contents;
150
+ try {
151
+ contents = await fs_1.promises.readFile(this.fileName, { encoding: 'utf-8' });
152
+ }
153
+ catch (e) {
154
+ if (e.code === 'ENOENT') {
155
+ return undefined;
156
+ }
157
+ throw e;
158
+ }
159
+ // Retry until we've seen the full contents
160
+ if (contents.endsWith('.')) {
161
+ return parseInt(contents.substring(0, contents.length - 1), 10);
162
+ }
163
+ await sleep(10);
164
+ }
165
+ throw new Error(`${this.fileName} was never completely written`);
166
+ }
167
+ async writePidFile(mode, additionalLock) {
168
+ const fd = await fs_1.promises.open(this.fileName, mode); // May fail if the file already exists
169
+ await fd.write(`${process.pid}.`); // Period guards against partial reads
170
+ await fd.close();
171
+ return {
172
+ release: async () => {
173
+ await fs_1.promises.unlink(this.fileName);
174
+ await additionalLock?.release();
175
+ },
176
+ };
177
+ }
178
+ }
179
+ exports.XpMutex = XpMutex;
180
+ async function fileExists(fileName) {
181
+ try {
182
+ await fs_1.promises.stat(fileName);
183
+ return true;
184
+ }
185
+ catch (e) {
186
+ if (e.code === 'ENOENT') {
187
+ return false;
188
+ }
189
+ throw e;
190
+ }
191
+ }
192
+ function processExists(pid) {
193
+ try {
194
+ process.kill(pid, 0);
195
+ return true;
196
+ }
197
+ catch (e) {
198
+ return false;
199
+ }
200
+ }
201
+ function sleep(ms) {
202
+ return new Promise(ok => setTimeout(ok, ms).unref());
203
+ }
204
+ function randomSleep(ms) {
205
+ return sleep(Math.floor(Math.random() * ms));
206
+ }
207
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieHBtdXRleC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInhwbXV0ZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkJBQXNEO0FBQ3RELHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFFN0IsTUFBYSxXQUFXO0lBYXRCLFlBQW9DLFNBQWlCO1FBQWpCLGNBQVMsR0FBVCxTQUFTLENBQVE7UUFIcEMscUJBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQWMsQ0FBQztRQUl4RCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQWRNLE1BQU0sQ0FBQyxhQUFhLENBQUMsU0FBaUI7UUFDM0MsY0FBUyxDQUFDLFNBQVMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzFDLE9BQU8sSUFBSSxXQUFXLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUVNLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBWTtRQUNqQyxPQUFPLFdBQVcsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBU00sS0FBSyxDQUFDLElBQVk7UUFDdkIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDakMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksV0FBVyxDQUFDLFNBQWtCO1FBQ25DLE1BQU0sSUFBSSxHQUFHLElBQUksT0FBTyxDQUFPLEVBQUUsQ0FBQyxFQUFFO1lBQ2xDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxJQUFJLEVBQUU7Z0JBQ25DLE1BQU0sV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUN0QixFQUFFLEVBQUUsQ0FBQztZQUNQLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7UUFFSCxJQUFJLFNBQVMsRUFBRTtZ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QWdCO0lBQ3hDLElBQUk7UUFDRixNQUFNLGFBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDeEIsT0FBTyxJQUFJLENBQUM7S0FDYjtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsSUFBSSxDQUFDLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFBRTtZQUFFLE9BQU8sS0FBSyxDQUFDO1NBQUU7UUFDMUMsTUFBTSxDQUFDLENBQUM7S0FDVDtBQUNILENBQUM7QUFFRCxTQUFTLGFBQWEsQ0FBQyxHQUFXO0lBQ2hDLElBQUk7UUFDRixPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNyQixPQUFPLElBQUksQ0FBQztLQUNiO0lBQUMsT0FBTyxDQUFDLEVBQUU7UUFDVixPQUFPLEtBQUssQ0FBQztLQUNkO0FBQ0gsQ0FBQztBQUVELFNBQVMsS0FBSyxDQUFDLEVBQVU7SUFDdkIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFFLFVBQVUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFTLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztBQUNoRSxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsRUFBVTtJQUM3QixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQy9DLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB3YXRjaCwgcHJvbWlzZXMgYXMgZnMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5cbmV4cG9ydCBjbGFzcyBYcE11dGV4UG9vbCB7XG4gIHB1YmxpYyBzdGF0aWMgZnJvbURpcmVjdG9yeShkaXJlY3Rvcnk6IHN0cmluZykge1xuICAgIG1rZGlyU3luYyhkaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIHJldHVybiBuZXcgWHBNdXRleFBvb2woZGlyZWN0b3J5KTtcbiAgfVxuXG4gIHB1YmxpYyBzdGF0aWMgZnJvbU5hbWUobmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIFhwTXV0ZXhQb29sLmZyb21EaXJlY3RvcnkocGF0aC5qb2luKG9zLnRtcGRpcigpLCBuYW1lKSk7XG4gIH1cblxuICBwcml2YXRlIHJlYWRvbmx5IHdhaXRpbmdSZXNvbHZlcnMgPSBuZXcgU2V0PCgpID0+IHZvaWQ+KCk7XG4gIHByaXZhdGUgd2F0Y2hlcjogUmV0dXJuVHlwZTx0eXBlb2Ygd2F0Y2g+IHwgdW5kZWZpbmVkO1xuXG4gIHByaXZhdGUgY29uc3RydWN0b3IocHVibGljIHJlYWRvbmx5IGRpcmVjdG9yeTogc3RyaW5nKSB7XG4gICAgdGhpcy5zdGFydFdhdGNoKCk7XG4gIH1cblxuICBwdWJsaWMgbXV0ZXgobmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIG5ldyBYcE11dGV4KHRoaXMsIG5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIEF3YWl0IGFuIHVubG9jayBldmVudFxuICAgKlxuICAgKiAoQW4gdW5sb2NrIGV2ZW50IGlzIHdoZW4gYSBmaWxlIGluIHRoZSBkaXJlY3RvcnkgZ2V0cyBkZWxldGVkLCB3aXRoIGEgdGlueVxuICAgKiByYW5kb20gc2xlZXAgYXR0YWNoZWQgdG8gaXQpLlxuICAgKi9cbiAgcHVibGljIGF3YWl0VW5sb2NrKG1heFdhaXRNcz86IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHdhaXQgPSBuZXcgUHJvbWlzZTx2b2lkPihvayA9PiB7XG4gICAgICB0aGlzLndhaXRpbmdSZXNvbHZlcnMuYWRkKGFzeW5jICgpID0+IHtcbiAgICAgICAgYXdhaXQgcmFuZG9tU2xlZXAoMTApO1xuICAgICAgICBvaygpO1xuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICBpZiAobWF4V2FpdE1zKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yYWNlKFt3YWl0LCBzbGVlcChtYXhXYWl0TXMpXSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB3YWl0O1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc3RhcnRXYXRjaCgpIHtcbiAgICB0aGlzLndhdGNoZXIgPSB3YXRjaCh0aGlzLmRpcmVjdG9yeSk7XG4gICAgKHRoaXMud2F0Y2hlciBhcyBhbnkpLnVucmVmKCk7IC8vIEB0eXBlcyBkb2Vzbid0IGtub3cgYWJvdXQgdGhpcyBidXQgaXQgZXhpc3RzXG4gICAgdGhpcy53YXRjaGVyLm9uKCdjaGFuZ2UnLCBhc3luYyAoZXZlbnRUeXBlLCBmbmFtZSkgPT4ge1xuICAgICAgLy8gT25seSB0cmlnZ2VyIG9uICdkZWxldGVzJy5cbiAgICAgIC8vIEFmdGVyIHJlY2VpdmluZyB0aGUgZXZlbnQsIHdlIGNoZWNrIGlmIHRoZSBmaWxlIGV4aXN0cy5cbiAgICAgIC8vIC0gSWYgbm86IHRoZSBmaWxlIHdhcyBkZWxldGVkISBIdXp6YWgsIHRoaXMgY291bnRzIGFzIGEgd2FrZXVwLlxuICAgICAgLy8gLSBJZiB5ZXM6IGVpdGhlciB0aGUgZmlsZSB3YXMganVzdCBjcmVhdGVkIChpbiB3aGljaCBjYXNlIHdlIGRvbid0IG5lZWQgdG8gd2FrZXVwKVxuICAgICAgLy8gICBvciB0aGUgZXZlbnQgd2FzIGR1ZSB0byBhIGRlbGV0ZSBidXQgc29tZW9uZSByYWNlZCB1cyB0byBpdCBhbmQgY2xhaW1lZCB0aGVcbiAgICAgIC8vICAgZmlsZSBhbHJlYWR5IChpbiB3aGljaCBjYXNlIHdlIGFsc28gZG9uJ3QgbmVlZCB0byB3YWtlIHVwKS5cbiAgICAgIGlmIChldmVudFR5cGUgPT09ICdyZW5hbWUnICYmICFhd2FpdCBmaWxlRXhpc3RzKHBhdGguam9pbih0aGlzLmRpcmVjdG9yeSwgZm5hbWUudG9TdHJpbmcoKSkpKSB7XG4gICAgICAgIHRoaXMubm90aWZ5V2FpdGVycygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMud2F0Y2hlci5vbignZXJyb3InLCBhc3luYyAoZSkgPT4ge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICBhd2FpdCByYW5kb21TbGVlcCgxMDApO1xuICAgICAgdGhpcy5zdGFydFdhdGNoKCk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIG5vdGlmeVdhaXRlcnMoKSB7XG4gICAgZm9yIChjb25zdCBwcm9taXNlIG9mIHRoaXMud2FpdGluZ1Jlc29sdmVycykge1xuICAgICAgcHJvbWlzZSgpO1xuICAgIH1cbiAgICB0aGlzLndhaXRpbmdSZXNvbHZlcnMuY2xlYXIoKTtcbiAgfVxufVxuXG4vKipcbiAqIENyb3NzLXByb2Nlc3MgbXV0ZXhcbiAqXG4gKiBVc2VzIHRoZSBwcmVzZW5jZSBvZiBhIGZpbGUgb24gZGlzayBhbmQgYGZzLndhdGNoYCB0byByZXByZXNlbnQgdGhlIG11dGV4XG4gKiBhbmQgZGlzY292ZXIgdW5sb2Nrcy5cbiAqL1xuZXhwb3J0IGNsYXNzIFhwTXV0ZXgge1xuICBwcml2YXRlIHJlYWRvbmx5IGZpbGVOYW1lOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBwb29sOiBYcE11dGV4UG9vbCwgcHVibGljIHJlYWRvbmx5IG11dGV4TmFtZTogc3RyaW5nKSB7XG4gICAgdGhpcy5maWxlTmFtZSA9IHBhdGguam9pbihwb29sLmRpcmVjdG9yeSwgYCR7bXV0ZXhOYW1lfS5tdXRleGApO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyeSB0byBhY3F1aXJlIHRoZSBsb2NrIChtYXkgZmFpbClcbiAgICovXG4gIHB1YmxpYyBhc3luYyB0cnlBY3F1aXJlKCk6IFByb21pc2U8SUxvY2sgfCB1bmRlZmluZWQ+IHtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgLy8gQWNxdWlyZSBsb2NrIGJ5IGJlaW5nIHRoZSBvbmUgdG8gY3JlYXRlIHRoZSBmaWxlXG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy53cml0ZVBpZEZpbGUoJ3d4Jyk7IC8vIEZhaWxzIGlmIHRoZSBmaWxlIGFscmVhZHkgZXhpc3RzXG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChlLmNvZGUgIT09ICdFRVhJU1QnKSB7IHRocm93IGU7IH1cbiAgICAgIH1cblxuICAgICAgLy8gRmlsZSBhbHJlYWR5IGV4aXN0cy4gUmVhZCB0aGUgY29udGVudHMsIHNlZSBpZiBpdCdzIGFuIGV4aXN0ZW50IFBJRCAoaWYgc28sIHRoZSBsb2NrIGlzIHRha2VuKVxuICAgICAgY29uc3Qgb3duZXJQaWQgPSBhd2FpdCB0aGlzLnJlYWRQaWRGaWxlKCk7XG4gICAgICBpZiAob3duZXJQaWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAvLyBGaWxlIGdvdCBkZWxldGVkIGp1c3Qgbm93LCBtYXliZSB3ZSBjYW4gYWNxdWlyZSBpdCBhZ2FpblxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmIChwcm9jZXNzRXhpc3RzKG93bmVyUGlkKSkge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiBub3QsIHRoZSBsb2NrIGlzIHN0YWxlIGFuZCB3aWxsIG5ldmVyIGJlIHJlbGVhc2VkIGFueW1vcmUuIFdlIG1heVxuICAgICAgLy8gZGVsZXRlIGl0IGFuZCBhY3F1aXJlIGl0IGFueXdheSwgYnV0IHdlIG1heSBiZSByYWNpbmcgc29tZW9uZSBlbHNlIHRyeWluZ1xuICAgICAgLy8gdG8gZG8gdGhlIHNhbWUuIFNvbHZlIHRoaXMgYXMgZm9sbG93czpcbiAgICAgIC8vIC0gVHJ5IHRvIGFjcXVpcmUgYSBsb2NrIHRoYXQgZ2l2ZXMgdXMgcGVybWlzc2lvbnMgdG8gZGVjbGFyZSB0aGUgZXhpc3RpbmcgbG9jayBzdGFsZS5cbiAgICAgIC8vIC0gU2xlZXAgYSBzbWFsbCByYW5kb20gcGVyaW9kIHRvIHJlZHVjZSBjb250ZW50aW9uIG9uIHRoaXMgb3BlcmF0aW9uXG4gICAgICBhd2FpdCByYW5kb21TbGVlcCgxMCk7XG4gICAgICBjb25zdCBpbm5lck11eCA9IG5ldyBYcE11dGV4KHRoaXMucG9vbCwgYCR7dGhpcy5tdXRleE5hbWV9LiR7b3duZXJQaWR9YCk7XG4gICAgICBjb25zdCBpbm5lckxvY2sgPSBhd2FpdCBpbm5lck11eC50cnlBY3F1aXJlKCk7XG4gICAgICBpZiAoIWlubmVyTG9jaykge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgfVxuXG4gICAgICAvLyBXZSBtYXkgbm90IHJlbGVhc2UgdGhlICdpbm5lciBsb2NrJyB3ZSB1c2VkIHRvIGFjcXVpcmUgdGhlIHJpZ2h0cyB0byBkZWNsYXJlIHRoZSBvdGhlclxuICAgICAgLy8gbG9jayBzdGFsZSB1bnRpbCB3ZSByZWxlYXNlIHRoZSBhY3R1YWwgbG9jayBpdHNlbGYuIElmIHdlIGRpZCwgb3RoZXIgY29udGVuZGVycyBtaWdodFxuICAgICAgLy8gc2VlIGl0IHJlbGVhc2VkIHdoaWxlIHRoZXkncmUgc3RpbGwgaW4gdGhpcyBmYWxsYmFjayBibG9jayBhbmQgYWNjaWRlbnRhbGx5IHN0ZWFsXG4gICAgICAvLyBmcm9tIGEgbmV3IGxlZ2l0aW1hdGUgb3duZXIuXG4gICAgICByZXR1cm4gdGhpcy53cml0ZVBpZEZpbGUoJ3cnLCBpbm5lckxvY2spOyAvLyBGb3JjZSB3cml0ZSBsb2NrIGZpbGUsIGF0dGFjaCBpbm5lciBsb2NrIGFzIHdlbGxcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWNxdWlyZSB0aGUgbG9jaywgd2FpdGluZyB1bnRpbCB3ZSBjYW5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBhY3F1aXJlKCk6IFByb21pc2U8SUxvY2s+IHtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgLy8gU3RhcnQgdGhlIHdhaXQgaGVyZSwgc28gd2UgZG9uJ3QgbWlzcyB0aGUgc2lnbmFsIGlmIGl0IGNvbWVzIGFmdGVyXG4gICAgICAvLyB3ZSB0cnkgYnV0IGJlZm9yZSB3ZSBzbGVlcC5cbiAgICAgIC8vXG4gICAgICAvLyBXZSBhbHNvIHBlcmlvZGljYWxseSByZXRyeSBhbnl3YXkgc2luY2Ugd2UgbWF5IGhhdmUgbWlzc2VkIHRoZSBkZWxldGVcbiAgICAgIC8vIHNpZ25hbCBkdWUgdG8gdW5mb3J0dW5hdGUgdGltaW5nLlxuICAgICAgY29uc3Qgd2FpdCA9IHRoaXMucG9vbC5hd2FpdFVubG9jayg1MDAwKTtcblxuICAgICAgY29uc3QgbG9jayA9IGF3YWl0IHRoaXMuYWNxdWlyZSgpO1xuICAgICAgaWYgKGxvY2spIHtcbiAgICAgICAgLy8gSWdub3JlIHRoZSB3YWl0IChjb3VudCBhcyBoYW5kbGVkKVxuICAgICAgICB3YWl0LnRoZW4oKCkgPT4ge30sICgpID0+IHt9KTtcbiAgICAgICAgcmV0dXJuIGxvY2s7XG4gICAgICB9XG5cbiAgICAgIGF3YWl0IHdhaXQ7XG4gICAgICBhd2FpdCByYW5kb21TbGVlcCgxMDApO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVhZFBpZEZpbGUoKTogUHJvbWlzZTxudW1iZXIgfCB1bmRlZmluZWQ+IHtcbiAgICBjb25zdCBkZWFkTGluZSA9IERhdGUubm93KCkgKyAxMDAwO1xuICAgIHdoaWxlIChEYXRlLm5vdygpIDwgZGVhZExpbmUpIHtcbiAgICAgIGxldCBjb250ZW50cztcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnRlbnRzID0gYXdhaXQgZnMucmVhZEZpbGUodGhpcy5maWxlTmFtZSwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgaWYgKGUuY29kZSA9PT0gJ0VOT0VOVCcpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXRyeSB1bnRpbCB3ZSd2ZSBzZWVuIHRoZSBmdWxsIGNvbnRlbnRzXG4gICAgICBpZiAoY29udGVudHMuZW5kc1dpdGgoJy4nKSkgeyByZXR1cm4gcGFyc2VJbnQoY29udGVudHMuc3Vic3RyaW5nKDAsIGNvbnRlbnRzLmxlbmd0aCAtIDEpLCAxMCk7IH1cbiAgICAgIGF3YWl0IHNsZWVwKDEwKTtcbiAgICB9XG5cbiAgICB0aHJvdyBuZXcgRXJyb3IoYCR7dGhpcy5maWxlTmFtZX0gd2FzIG5ldmVyIGNvbXBsZXRlbHkgd3JpdHRlbmApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB3cml0ZVBpZEZpbGUobW9kZTogc3RyaW5nLCBhZGRpdGlvbmFsTG9jaz86IElMb2NrKTogUHJvbWlzZTxJTG9jaz4ge1xuICAgIGNvbnN0IGZkID0gYXdhaXQgZnMub3Blbih0aGlzLmZpbGVOYW1lLCBtb2RlKTsgLy8gTWF5IGZhaWwgaWYgdGhlIGZpbGUgYWxyZWFkeSBleGlzdHNcbiAgICBhd2FpdCBmZC53cml0ZShgJHtwcm9jZXNzLnBpZH0uYCk7IC8vIFBlcmlvZCBndWFyZHMgYWdhaW5zdCBwYXJ0aWFsIHJlYWRzXG4gICAgYXdhaXQgZmQuY2xvc2UoKTtcblxuICAgIHJldHVybiB7XG4gICAgICByZWxlYXNlOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmZpbGVOYW1lKTtcbiAgICAgICAgYXdhaXQgYWRkaXRpb25hbExvY2s/LnJlbGVhc2UoKTtcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIElMb2NrIHtcbiAgcmVsZWFzZSgpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5hc3luYyBmdW5jdGlvbiBmaWxlRXhpc3RzKGZpbGVOYW1lOiBzdHJpbmcpIHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy5zdGF0KGZpbGVOYW1lKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGlmIChlLmNvZGUgPT09ICdFTk9FTlQnKSB7IHJldHVybiBmYWxzZTsgfVxuICAgIHRocm93IGU7XG4gIH1cbn1cblxuZnVuY3Rpb24gcHJvY2Vzc0V4aXN0cyhwaWQ6IG51bWJlcikge1xuICB0cnkge1xuICAgIHByb2Nlc3Mua2lsbChwaWQsIDApO1xuICAgIHJldHVybiB0cnVlO1xuICB9IGNhdGNoIChlKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmZ1bmN0aW9uIHNsZWVwKG1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKG9rID0+IChzZXRUaW1lb3V0KG9rLCBtcykgYXMgYW55KS51bnJlZigpKTtcbn1cblxuZnVuY3Rpb24gcmFuZG9tU2xlZXAobXM6IG51bWJlcikge1xuICByZXR1cm4gc2xlZXAoTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogbXMpKTtcbn1cbiJdfQ==