@sentry/node 8.0.0-alpha.1 → 8.0.0-alpha.3
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.
- package/README.md +14 -93
- package/cjs/index.js +3665 -77
- package/cjs/index.js.map +1 -1
- package/esm/index.js +3614 -31
- package/esm/index.js.map +1 -1
- package/esm/integrations/anr/worker-script.js +2 -2
- package/esm/package.json +1 -0
- package/package.json +21 -6
- package/types/index.d.ts +6 -5
- package/types/index.d.ts.map +1 -1
- package/types/integrations/anr/index.d.ts.map +1 -1
- package/types/integrations/anr/worker-script.d.ts +1 -1
- package/types/integrations/anr/worker-script.d.ts.map +1 -1
- package/types/integrations/http.d.ts +4 -0
- package/types/integrations/http.d.ts.map +1 -1
- package/types/integrations/local-variables/common.d.ts +1 -0
- package/types/integrations/local-variables/common.d.ts.map +1 -1
- package/types/integrations/local-variables/local-variables-sync.d.ts +1 -0
- package/types/integrations/local-variables/local-variables-sync.d.ts.map +1 -1
- package/types/integrations/tracing/fastify.d.ts +9 -0
- package/types/integrations/tracing/fastify.d.ts.map +1 -1
- package/types/integrations/tracing/hapi/index.d.ts.map +1 -1
- package/types/integrations/tracing/prisma.d.ts.map +1 -1
- package/types/proxy/helpers.d.ts +2 -2
- package/types/proxy/helpers.d.ts.map +1 -1
- package/types/proxy/index.d.ts.map +1 -1
- package/types/sdk/hub.d.ts.map +1 -1
- package/types/sdk/init.d.ts.map +1 -1
- package/types/transports/http-module.d.ts +2 -2
- package/types/transports/http-module.d.ts.map +1 -1
- package/types/types.d.ts +12 -4
- package/types/types.d.ts.map +1 -1
- package/types-ts3.8/index.d.ts +6 -5
- package/types-ts3.8/integrations/anr/worker-script.d.ts +1 -1
- package/types-ts3.8/integrations/http.d.ts +4 -0
- package/types-ts3.8/integrations/local-variables/common.d.ts +1 -0
- package/types-ts3.8/integrations/local-variables/local-variables-sync.d.ts +1 -0
- package/types-ts3.8/integrations/tracing/fastify.d.ts +9 -0
- package/types-ts3.8/proxy/helpers.d.ts +2 -2
- package/types-ts3.8/transports/http-module.d.ts +2 -2
- package/types-ts3.8/types.d.ts +12 -4
- package/cjs/cron/common.js +0 -56
- package/cjs/cron/common.js.map +0 -1
- package/cjs/cron/cron.js +0 -102
- package/cjs/cron/cron.js.map +0 -1
- package/cjs/cron/index.js +0 -15
- package/cjs/cron/index.js.map +0 -1
- package/cjs/cron/node-cron.js +0 -61
- package/cjs/cron/node-cron.js.map +0 -1
- package/cjs/cron/node-schedule.js +0 -57
- package/cjs/cron/node-schedule.js.map +0 -1
- package/cjs/debug-build.js +0 -11
- package/cjs/debug-build.js.map +0 -1
- package/cjs/integrations/anr/index.js +0 -145
- package/cjs/integrations/anr/index.js.map +0 -1
- package/cjs/integrations/anr/worker-script.js +0 -2
- package/cjs/integrations/anr/worker-script.js.map +0 -1
- package/cjs/integrations/console.js +0 -40
- package/cjs/integrations/console.js.map +0 -1
- package/cjs/integrations/context.js +0 -435
- package/cjs/integrations/context.js.map +0 -1
- package/cjs/integrations/contextlines.js +0 -137
- package/cjs/integrations/contextlines.js.map +0 -1
- package/cjs/integrations/http.js +0 -137
- package/cjs/integrations/http.js.map +0 -1
- package/cjs/integrations/local-variables/common.js +0 -87
- package/cjs/integrations/local-variables/common.js.map +0 -1
- package/cjs/integrations/local-variables/index.js +0 -8
- package/cjs/integrations/local-variables/index.js.map +0 -1
- package/cjs/integrations/local-variables/local-variables-sync.js +0 -400
- package/cjs/integrations/local-variables/local-variables-sync.js.map +0 -1
- package/cjs/integrations/modules.js +0 -99
- package/cjs/integrations/modules.js.map +0 -1
- package/cjs/integrations/node-fetch.js +0 -82
- package/cjs/integrations/node-fetch.js.map +0 -1
- package/cjs/integrations/onuncaughtexception.js +0 -137
- package/cjs/integrations/onuncaughtexception.js.map +0 -1
- package/cjs/integrations/onunhandledrejection.js +0 -90
- package/cjs/integrations/onunhandledrejection.js.map +0 -1
- package/cjs/integrations/spotlight.js +0 -109
- package/cjs/integrations/spotlight.js.map +0 -1
- package/cjs/integrations/tracing/express.js +0 -103
- package/cjs/integrations/tracing/express.js.map +0 -1
- package/cjs/integrations/tracing/fastify.js +0 -33
- package/cjs/integrations/tracing/fastify.js.map +0 -1
- package/cjs/integrations/tracing/graphql.js +0 -34
- package/cjs/integrations/tracing/graphql.js.map +0 -1
- package/cjs/integrations/tracing/hapi/index.js +0 -81
- package/cjs/integrations/tracing/hapi/index.js.map +0 -1
- package/cjs/integrations/tracing/index.js +0 -37
- package/cjs/integrations/tracing/index.js.map +0 -1
- package/cjs/integrations/tracing/koa.js +0 -21
- package/cjs/integrations/tracing/koa.js.map +0 -1
- package/cjs/integrations/tracing/mongo.js +0 -33
- package/cjs/integrations/tracing/mongo.js.map +0 -1
- package/cjs/integrations/tracing/mongoose.js +0 -33
- package/cjs/integrations/tracing/mongoose.js.map +0 -1
- package/cjs/integrations/tracing/mysql.js +0 -26
- package/cjs/integrations/tracing/mysql.js.map +0 -1
- package/cjs/integrations/tracing/mysql2.js +0 -33
- package/cjs/integrations/tracing/mysql2.js.map +0 -1
- package/cjs/integrations/tracing/nest.js +0 -26
- package/cjs/integrations/tracing/nest.js.map +0 -1
- package/cjs/integrations/tracing/postgres.js +0 -34
- package/cjs/integrations/tracing/postgres.js.map +0 -1
- package/cjs/integrations/tracing/prisma.js +0 -33
- package/cjs/integrations/tracing/prisma.js.map +0 -1
- package/cjs/nodeVersion.js +0 -12
- package/cjs/nodeVersion.js.map +0 -1
- package/cjs/otel/contextManager.js +0 -16
- package/cjs/otel/contextManager.js.map +0 -1
- package/cjs/proxy/base.js +0 -126
- package/cjs/proxy/base.js.map +0 -1
- package/cjs/proxy/index.js +0 -209
- package/cjs/proxy/index.js.map +0 -1
- package/cjs/proxy/parse-proxy-response.js +0 -104
- package/cjs/proxy/parse-proxy-response.js.map +0 -1
- package/cjs/sdk/api.js +0 -74
- package/cjs/sdk/api.js.map +0 -1
- package/cjs/sdk/client.js +0 -57
- package/cjs/sdk/client.js.map +0 -1
- package/cjs/sdk/hub.js +0 -104
- package/cjs/sdk/hub.js.map +0 -1
- package/cjs/sdk/init.js +0 -224
- package/cjs/sdk/init.js.map +0 -1
- package/cjs/sdk/initOtel.js +0 -70
- package/cjs/sdk/initOtel.js.map +0 -1
- package/cjs/sdk/scope.js +0 -18
- package/cjs/sdk/scope.js.map +0 -1
- package/cjs/transports/http.js +0 -160
- package/cjs/transports/http.js.map +0 -1
- package/cjs/utils/addOriginToSpan.js +0 -11
- package/cjs/utils/addOriginToSpan.js.map +0 -1
- package/cjs/utils/errorhandling.js +0 -44
- package/cjs/utils/errorhandling.js.map +0 -1
- package/cjs/utils/getRequestUrl.js +0 -18
- package/cjs/utils/getRequestUrl.js.map +0 -1
- package/cjs/utils/module.js +0 -62
- package/cjs/utils/module.js.map +0 -1
- package/esm/cron/common.js +0 -54
- package/esm/cron/common.js.map +0 -1
- package/esm/cron/cron.js +0 -100
- package/esm/cron/cron.js.map +0 -1
- package/esm/cron/index.js +0 -13
- package/esm/cron/index.js.map +0 -1
- package/esm/cron/node-cron.js +0 -56
- package/esm/cron/node-cron.js.map +0 -1
- package/esm/cron/node-schedule.js +0 -55
- package/esm/cron/node-schedule.js.map +0 -1
- package/esm/debug-build.js +0 -9
- package/esm/debug-build.js.map +0 -1
- package/esm/integrations/anr/index.js +0 -139
- package/esm/integrations/anr/index.js.map +0 -1
- package/esm/integrations/anr/worker-script.js.map +0 -1
- package/esm/integrations/console.js +0 -38
- package/esm/integrations/console.js.map +0 -1
- package/esm/integrations/context.js +0 -427
- package/esm/integrations/context.js.map +0 -1
- package/esm/integrations/contextlines.js +0 -131
- package/esm/integrations/contextlines.js.map +0 -1
- package/esm/integrations/http.js +0 -132
- package/esm/integrations/http.js.map +0 -1
- package/esm/integrations/local-variables/common.js +0 -81
- package/esm/integrations/local-variables/common.js.map +0 -1
- package/esm/integrations/local-variables/index.js +0 -6
- package/esm/integrations/local-variables/index.js.map +0 -1
- package/esm/integrations/local-variables/local-variables-sync.js +0 -394
- package/esm/integrations/local-variables/local-variables-sync.js.map +0 -1
- package/esm/integrations/modules.js +0 -97
- package/esm/integrations/modules.js.map +0 -1
- package/esm/integrations/node-fetch.js +0 -80
- package/esm/integrations/node-fetch.js.map +0 -1
- package/esm/integrations/onuncaughtexception.js +0 -134
- package/esm/integrations/onuncaughtexception.js.map +0 -1
- package/esm/integrations/onunhandledrejection.js +0 -87
- package/esm/integrations/onunhandledrejection.js.map +0 -1
- package/esm/integrations/spotlight.js +0 -106
- package/esm/integrations/spotlight.js.map +0 -1
- package/esm/integrations/tracing/express.js +0 -96
- package/esm/integrations/tracing/express.js.map +0 -1
- package/esm/integrations/tracing/fastify.js +0 -31
- package/esm/integrations/tracing/fastify.js.map +0 -1
- package/esm/integrations/tracing/graphql.js +0 -32
- package/esm/integrations/tracing/graphql.js.map +0 -1
- package/esm/integrations/tracing/hapi/index.js +0 -77
- package/esm/integrations/tracing/hapi/index.js.map +0 -1
- package/esm/integrations/tracing/index.js +0 -35
- package/esm/integrations/tracing/index.js.map +0 -1
- package/esm/integrations/tracing/koa.js +0 -19
- package/esm/integrations/tracing/koa.js.map +0 -1
- package/esm/integrations/tracing/mongo.js +0 -31
- package/esm/integrations/tracing/mongo.js.map +0 -1
- package/esm/integrations/tracing/mongoose.js +0 -31
- package/esm/integrations/tracing/mongoose.js.map +0 -1
- package/esm/integrations/tracing/mysql.js +0 -24
- package/esm/integrations/tracing/mysql.js.map +0 -1
- package/esm/integrations/tracing/mysql2.js +0 -31
- package/esm/integrations/tracing/mysql2.js.map +0 -1
- package/esm/integrations/tracing/nest.js +0 -24
- package/esm/integrations/tracing/nest.js.map +0 -1
- package/esm/integrations/tracing/postgres.js +0 -32
- package/esm/integrations/tracing/postgres.js.map +0 -1
- package/esm/integrations/tracing/prisma.js +0 -31
- package/esm/integrations/tracing/prisma.js.map +0 -1
- package/esm/nodeVersion.js +0 -9
- package/esm/nodeVersion.js.map +0 -1
- package/esm/otel/contextManager.js +0 -14
- package/esm/otel/contextManager.js.map +0 -1
- package/esm/proxy/base.js +0 -121
- package/esm/proxy/base.js.map +0 -1
- package/esm/proxy/index.js +0 -203
- package/esm/proxy/index.js.map +0 -1
- package/esm/proxy/parse-proxy-response.js +0 -102
- package/esm/proxy/parse-proxy-response.js.map +0 -1
- package/esm/sdk/api.js +0 -69
- package/esm/sdk/api.js.map +0 -1
- package/esm/sdk/client.js +0 -52
- package/esm/sdk/client.js.map +0 -1
- package/esm/sdk/hub.js +0 -102
- package/esm/sdk/hub.js.map +0 -1
- package/esm/sdk/init.js +0 -221
- package/esm/sdk/init.js.map +0 -1
- package/esm/sdk/initOtel.js +0 -67
- package/esm/sdk/initOtel.js.map +0 -1
- package/esm/sdk/scope.js +0 -16
- package/esm/sdk/scope.js.map +0 -1
- package/esm/transports/http.js +0 -155
- package/esm/transports/http.js.map +0 -1
- package/esm/utils/addOriginToSpan.js +0 -9
- package/esm/utils/addOriginToSpan.js.map +0 -1
- package/esm/utils/errorhandling.js +0 -42
- package/esm/utils/errorhandling.js.map +0 -1
- package/esm/utils/getRequestUrl.js +0 -16
- package/esm/utils/getRequestUrl.js.map +0 -1
- package/esm/utils/module.js +0 -60
- package/esm/utils/module.js.map +0 -1
- package/types/integrations/local-variables/local-variables-async.d.ts +0 -14
- package/types/integrations/local-variables/local-variables-async.d.ts.map +0 -1
- package/types-ts3.8/integrations/local-variables/local-variables-async.d.ts +0 -14
package/esm/index.js
CHANGED
|
@@ -1,33 +1,3616 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
|
|
10
|
-
export { anrIntegration } from './integrations/anr/index.js';
|
|
11
|
-
export { expressErrorHandler, expressIntegration, setupExpressErrorHandler } from './integrations/tracing/express.js';
|
|
12
|
-
export { fastifyIntegration } from './integrations/tracing/fastify.js';
|
|
13
|
-
export { graphqlIntegration } from './integrations/tracing/graphql.js';
|
|
14
|
-
export { mongoIntegration } from './integrations/tracing/mongo.js';
|
|
15
|
-
export { mongooseIntegration } from './integrations/tracing/mongoose.js';
|
|
16
|
-
export { mysqlIntegration } from './integrations/tracing/mysql.js';
|
|
17
|
-
export { mysql2Integration } from './integrations/tracing/mysql2.js';
|
|
18
|
-
export { nestIntegration } from './integrations/tracing/nest.js';
|
|
19
|
-
export { postgresIntegration } from './integrations/tracing/postgres.js';
|
|
20
|
-
export { prismaIntegration } from './integrations/tracing/prisma.js';
|
|
21
|
-
export { hapiIntegration, setupHapiErrorHandler } from './integrations/tracing/hapi/index.js';
|
|
22
|
-
export { getDefaultIntegrations, init } from './sdk/init.js';
|
|
23
|
-
export { getAutoPerformanceIntegrations } from './integrations/tracing/index.js';
|
|
24
|
-
export { defaultStackParser, getClient, getSentryRelease, makeMain } from './sdk/api.js';
|
|
25
|
-
export { createGetModuleFromFilename } from './utils/module.js';
|
|
26
|
-
export { makeNodeTransport } from './transports/http.js';
|
|
27
|
-
export { NodeClient } from './sdk/client.js';
|
|
28
|
-
export { getCurrentHub } from './sdk/hub.js';
|
|
29
|
-
export { cron } from './cron/index.js';
|
|
30
|
-
export { getActiveSpan, startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from '@sentry/opentelemetry';
|
|
1
|
+
import { _optionalChainDelete, _nullishCoalesce, _optionalChain } from '@sentry/utils';
|
|
2
|
+
import { context, SpanKind, trace, diag, DiagLogLevel } from '@opentelemetry/api';
|
|
3
|
+
import { registerInstrumentations } from '@opentelemetry/instrumentation';
|
|
4
|
+
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
|
|
5
|
+
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, isSentryRequestUrl, getIsolationScope, addBreadcrumb, getClient as getClient$2, convertIntegrationFnToClass, captureException, getCurrentScope, SDK_VERSION, getActiveSpan, getRootSpan, SPAN_STATUS_ERROR, createTransport, ServerRuntimeClient, applySdkMetadata, inboundFiltersIntegration, functionToStringIntegration, linkedErrorsIntegration, requestDataIntegration, hasTracingEnabled, getIntegrationsToSetup, startSession, endSession, withScope, captureEvent, setUser, setTags, setTag, setExtra, setExtras, setContext, withMonitor } from '@sentry/core';
|
|
6
|
+
export { Hub, SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, Scope, addBreadcrumb, addEventProcessor, addIntegration, captureCheckIn, captureConsoleIntegration, captureEvent, captureException, captureMessage, captureSession, close, createTransport, debugIntegration, dedupeIntegration, endSession, extraErrorDataIntegration, flush, functionToStringIntegration, getActiveSpan, getCurrentScope, getGlobalScope, getIsolationScope, getRootSpan, getSpanDescendants, getSpanStatusFromHttpCode, inboundFiltersIntegration, isInitialized, linkedErrorsIntegration, metricsDefault as metrics, parameterize, requestDataIntegration, rewriteFramesIntegration, sessionTimingIntegration, setContext, setCurrentClient, setExtra, setExtras, setHttpStatus, setMeasurement, setTag, setTags, setUser, startInactiveSpan, startSession, startSpan, startSpanManual, withActiveSpan, withIsolationScope, withMonitor, withScope } from '@sentry/core';
|
|
7
|
+
import { getScopesFromContext, getClient as getClient$1, getSpanKind, _INTERNAL, wrapContextManagerClass, setupEventContextTrace, SentrySampler, SentrySpanProcessor, SentryPropagator, setOpenTelemetryContextAsyncContextStrategy, openTelemetrySetupCheck } from '@sentry/opentelemetry';
|
|
8
|
+
export { continueTrace } from '@sentry/opentelemetry';
|
|
9
|
+
import { parseSemver, addConsoleInstrumentationHandler, severityLevelFromString, LRUMap, addContextToFrame, logger, consoleSandbox, serializeEnvelope, dirname as dirname$1, createStackParser, nodeStackLineParser, GLOBAL_OBJ, dropUndefinedKeys, stackParserFromStackParserOptions, propagationContextFromHeaders } from '@sentry/utils';
|
|
31
10
|
export { DEFAULT_USER_INCLUDES, addRequestDataToEvent, extractRequestData } from '@sentry/utils';
|
|
32
|
-
|
|
11
|
+
import * as util from 'util';
|
|
12
|
+
import { promisify } from 'util';
|
|
13
|
+
import { execFile } from 'child_process';
|
|
14
|
+
import { readFile, readdir, promises, existsSync, readFileSync } from 'fs';
|
|
15
|
+
import * as os from 'os';
|
|
16
|
+
import { join, dirname, posix, sep } from 'path';
|
|
17
|
+
import * as inspector from 'inspector';
|
|
18
|
+
import { Session } from 'inspector';
|
|
19
|
+
import { Worker } from 'worker_threads';
|
|
20
|
+
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
|
|
21
|
+
import { FastifyInstrumentation } from '@opentelemetry/instrumentation-fastify';
|
|
22
|
+
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
|
|
23
|
+
import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb';
|
|
24
|
+
import { MongooseInstrumentation } from '@opentelemetry/instrumentation-mongoose';
|
|
25
|
+
import { MySQLInstrumentation } from '@opentelemetry/instrumentation-mysql';
|
|
26
|
+
import { MySQL2Instrumentation } from '@opentelemetry/instrumentation-mysql2';
|
|
27
|
+
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
|
|
28
|
+
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
|
|
29
|
+
import * as prismaInstrumentation from '@prisma/instrumentation';
|
|
30
|
+
import { HapiInstrumentation } from '@opentelemetry/instrumentation-hapi';
|
|
31
|
+
import * as http from 'http';
|
|
32
|
+
import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa';
|
|
33
|
+
import * as http$1 from 'node:http';
|
|
34
|
+
import * as https from 'node:https';
|
|
35
|
+
import { Readable } from 'stream';
|
|
36
|
+
import { createGzip } from 'zlib';
|
|
37
|
+
import * as net from 'net';
|
|
38
|
+
import * as tls from 'tls';
|
|
39
|
+
import { Resource } from '@opentelemetry/resources';
|
|
40
|
+
import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
|
|
41
|
+
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
|
42
|
+
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Update the active isolation scope.
|
|
46
|
+
* Should be used with caution!
|
|
47
|
+
*/
|
|
48
|
+
function setIsolationScope(isolationScope) {
|
|
49
|
+
const scopes = getScopesFromContext(context.active());
|
|
50
|
+
if (scopes) {
|
|
51
|
+
scopes.isolationScope = isolationScope;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Adds an origin to an OTEL Span. */
|
|
56
|
+
function addOriginToSpan(span, origin) {
|
|
57
|
+
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, origin);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Build a full URL from request options. */
|
|
61
|
+
function getRequestUrl(requestOptions) {
|
|
62
|
+
const protocol = requestOptions.protocol || '';
|
|
63
|
+
const hostname = requestOptions.hostname || requestOptions.host || '';
|
|
64
|
+
// Don't log standard :80 (http) and :443 (https) ports to reduce the noise
|
|
65
|
+
// Also don't add port if the hostname already includes a port
|
|
66
|
+
const port =
|
|
67
|
+
!requestOptions.port || requestOptions.port === 80 || requestOptions.port === 443 || /^(.*):(\d+)$/.test(hostname)
|
|
68
|
+
? ''
|
|
69
|
+
: `:${requestOptions.port}`;
|
|
70
|
+
const path = requestOptions.path ? requestOptions.path : '/';
|
|
71
|
+
return `${protocol}//${hostname}${port}${path}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _optionalChain$8(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
75
|
+
|
|
76
|
+
const _httpIntegration = ((options = {}) => {
|
|
77
|
+
const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
|
|
78
|
+
const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;
|
|
79
|
+
const _ignoreIncomingRequests = options.ignoreIncomingRequests;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
name: 'Http',
|
|
83
|
+
setupOnce() {
|
|
84
|
+
const instrumentations = [
|
|
85
|
+
new HttpInstrumentation({
|
|
86
|
+
ignoreOutgoingRequestHook: request => {
|
|
87
|
+
const url = getRequestUrl(request);
|
|
88
|
+
|
|
89
|
+
if (!url) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (isSentryRequestUrl(url, getClient$1())) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (_ignoreOutgoingRequests && _ignoreOutgoingRequests(url)) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return false;
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
ignoreIncomingRequestHook: request => {
|
|
105
|
+
const url = getRequestUrl(request);
|
|
106
|
+
|
|
107
|
+
const method = _optionalChain$8([request, 'access', _ => _.method, 'optionalAccess', _2 => _2.toUpperCase, 'call', _3 => _3()]);
|
|
108
|
+
// We do not capture OPTIONS/HEAD requests as transactions
|
|
109
|
+
if (method === 'OPTIONS' || method === 'HEAD') {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (_ignoreIncomingRequests && _ignoreIncomingRequests(url)) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return false;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
requireParentforOutgoingSpans: true,
|
|
121
|
+
requireParentforIncomingSpans: false,
|
|
122
|
+
requestHook: (span, req) => {
|
|
123
|
+
_updateSpan$1(span);
|
|
124
|
+
|
|
125
|
+
// Update the isolation scope, isolate this request
|
|
126
|
+
if (getSpanKind(span) === SpanKind.SERVER) {
|
|
127
|
+
const isolationScope = getIsolationScope().clone();
|
|
128
|
+
isolationScope.setSDKProcessingMetadata({ request: req });
|
|
129
|
+
|
|
130
|
+
const client = getClient$1();
|
|
131
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
132
|
+
isolationScope.setRequestSession({ status: 'ok' });
|
|
133
|
+
}
|
|
134
|
+
setIsolationScope(isolationScope);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
responseHook: (span, res) => {
|
|
138
|
+
if (_breadcrumbs) {
|
|
139
|
+
_addRequestBreadcrumb$1(span, res);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const client = getClient$1();
|
|
143
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
144
|
+
setImmediate(() => {
|
|
145
|
+
client['_captureRequestSession']();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
}),
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
registerInstrumentations({
|
|
153
|
+
instrumentations,
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}) ;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The http integration instruments Node's internal http and https modules.
|
|
161
|
+
* It creates breadcrumbs and spans for outgoing HTTP requests which will be attached to the currently active span.
|
|
162
|
+
*/
|
|
163
|
+
const httpIntegration = defineIntegration(_httpIntegration);
|
|
164
|
+
|
|
165
|
+
/** Update the span with data we need. */
|
|
166
|
+
function _updateSpan$1(span) {
|
|
167
|
+
addOriginToSpan(span, 'auto.http.otel.http');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Add a breadcrumb for outgoing requests. */
|
|
171
|
+
function _addRequestBreadcrumb$1(span, response) {
|
|
172
|
+
if (getSpanKind(span) !== SpanKind.CLIENT) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const data = _INTERNAL.getRequestSpanData(span);
|
|
177
|
+
addBreadcrumb(
|
|
178
|
+
{
|
|
179
|
+
category: 'http',
|
|
180
|
+
data: {
|
|
181
|
+
status_code: response.statusCode,
|
|
182
|
+
...data,
|
|
183
|
+
},
|
|
184
|
+
type: 'http',
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
event: 'response',
|
|
188
|
+
// TODO FN: Do we need access to `request` here?
|
|
189
|
+
// If we do, we'll have to use the `applyCustomAttributesOnSpan` hook instead,
|
|
190
|
+
// but this has worse context semantics than request/responseHook.
|
|
191
|
+
response,
|
|
192
|
+
},
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const NODE_VERSION = parseSemver(process.versions.node)
|
|
197
|
+
|
|
198
|
+
;
|
|
199
|
+
const NODE_MAJOR = NODE_VERSION.major || 0;
|
|
200
|
+
|
|
201
|
+
const _nativeNodeFetchIntegration = ((options = {}) => {
|
|
202
|
+
const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
|
|
203
|
+
const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;
|
|
204
|
+
|
|
205
|
+
async function getInstrumentation() {
|
|
206
|
+
// Only add NodeFetch if Node >= 16, as previous versions do not support it
|
|
207
|
+
if (NODE_MAJOR < 16) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const pkg = await import('opentelemetry-instrumentation-fetch-node');
|
|
213
|
+
return [
|
|
214
|
+
new pkg.FetchInstrumentation({
|
|
215
|
+
ignoreRequestHook: (request) => {
|
|
216
|
+
const url = request.origin;
|
|
217
|
+
return _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);
|
|
218
|
+
},
|
|
219
|
+
onRequest: ({ span }) => {
|
|
220
|
+
_updateSpan(span);
|
|
221
|
+
|
|
222
|
+
if (_breadcrumbs) {
|
|
223
|
+
_addRequestBreadcrumb(span);
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
227
|
+
} ),
|
|
228
|
+
];
|
|
229
|
+
} catch (error) {
|
|
230
|
+
// Could not load instrumentation
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
name: 'NodeFetch',
|
|
236
|
+
setupOnce() {
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
238
|
+
getInstrumentation().then(instrumentations => {
|
|
239
|
+
if (instrumentations) {
|
|
240
|
+
registerInstrumentations({
|
|
241
|
+
instrumentations,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}) ;
|
|
248
|
+
|
|
249
|
+
const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);
|
|
250
|
+
|
|
251
|
+
/** Update the span with data we need. */
|
|
252
|
+
function _updateSpan(span) {
|
|
253
|
+
addOriginToSpan(span, 'auto.http.otel.node_fetch');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Add a breadcrumb for outgoing requests. */
|
|
257
|
+
function _addRequestBreadcrumb(span) {
|
|
258
|
+
if (getSpanKind(span) !== SpanKind.CLIENT) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const data = _INTERNAL.getRequestSpanData(span);
|
|
263
|
+
addBreadcrumb({
|
|
264
|
+
category: 'http',
|
|
265
|
+
data: {
|
|
266
|
+
...data,
|
|
267
|
+
},
|
|
268
|
+
type: 'http',
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const INTEGRATION_NAME$8 = 'Console';
|
|
273
|
+
|
|
274
|
+
const _consoleIntegration = (() => {
|
|
275
|
+
return {
|
|
276
|
+
name: INTEGRATION_NAME$8,
|
|
277
|
+
setup(client) {
|
|
278
|
+
addConsoleInstrumentationHandler(({ args, level }) => {
|
|
279
|
+
if (getClient$2() !== client) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
addBreadcrumb(
|
|
284
|
+
{
|
|
285
|
+
category: 'console',
|
|
286
|
+
level: severityLevelFromString(level),
|
|
287
|
+
message: util.format.apply(undefined, args),
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
input: [...args],
|
|
291
|
+
level,
|
|
292
|
+
},
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}) ;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Capture console logs as breadcrumbs.
|
|
301
|
+
*/
|
|
302
|
+
const consoleIntegration = defineIntegration(_consoleIntegration);
|
|
303
|
+
|
|
304
|
+
function _optionalChain$7(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
305
|
+
const readFileAsync$1 = promisify(readFile);
|
|
306
|
+
const readDirAsync = promisify(readdir);
|
|
307
|
+
|
|
308
|
+
const INTEGRATION_NAME$7 = 'Context';
|
|
309
|
+
|
|
310
|
+
const _nodeContextIntegration = ((options = {}) => {
|
|
311
|
+
let cachedContext;
|
|
312
|
+
|
|
313
|
+
const _options = {
|
|
314
|
+
app: true,
|
|
315
|
+
os: true,
|
|
316
|
+
device: true,
|
|
317
|
+
culture: true,
|
|
318
|
+
cloudResource: true,
|
|
319
|
+
...options,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/** Add contexts to the event. Caches the context so we only look it up once. */
|
|
323
|
+
async function addContext(event) {
|
|
324
|
+
if (cachedContext === undefined) {
|
|
325
|
+
cachedContext = _getContexts();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const updatedContext = _updateContext(await cachedContext);
|
|
329
|
+
|
|
330
|
+
event.contexts = {
|
|
331
|
+
...event.contexts,
|
|
332
|
+
app: { ...updatedContext.app, ..._optionalChain$7([event, 'access', _ => _.contexts, 'optionalAccess', _2 => _2.app]) },
|
|
333
|
+
os: { ...updatedContext.os, ..._optionalChain$7([event, 'access', _3 => _3.contexts, 'optionalAccess', _4 => _4.os]) },
|
|
334
|
+
device: { ...updatedContext.device, ..._optionalChain$7([event, 'access', _5 => _5.contexts, 'optionalAccess', _6 => _6.device]) },
|
|
335
|
+
culture: { ...updatedContext.culture, ..._optionalChain$7([event, 'access', _7 => _7.contexts, 'optionalAccess', _8 => _8.culture]) },
|
|
336
|
+
cloud_resource: { ...updatedContext.cloud_resource, ..._optionalChain$7([event, 'access', _9 => _9.contexts, 'optionalAccess', _10 => _10.cloud_resource]) },
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
return event;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Get the contexts from node. */
|
|
343
|
+
async function _getContexts() {
|
|
344
|
+
const contexts = {};
|
|
345
|
+
|
|
346
|
+
if (_options.os) {
|
|
347
|
+
contexts.os = await getOsContext();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (_options.app) {
|
|
351
|
+
contexts.app = getAppContext();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (_options.device) {
|
|
355
|
+
contexts.device = getDeviceContext(_options.device);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (_options.culture) {
|
|
359
|
+
const culture = getCultureContext();
|
|
360
|
+
|
|
361
|
+
if (culture) {
|
|
362
|
+
contexts.culture = culture;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (_options.cloudResource) {
|
|
367
|
+
contexts.cloud_resource = getCloudResourceContext();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return contexts;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
name: INTEGRATION_NAME$7,
|
|
375
|
+
processEvent(event) {
|
|
376
|
+
return addContext(event);
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}) ;
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Capture context about the environment and the device that the client is running on, to events.
|
|
383
|
+
*/
|
|
384
|
+
const nodeContextIntegration = defineIntegration(_nodeContextIntegration);
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Updates the context with dynamic values that can change
|
|
388
|
+
*/
|
|
389
|
+
function _updateContext(contexts) {
|
|
390
|
+
// Only update properties if they exist
|
|
391
|
+
if (_optionalChain$7([contexts, 'optionalAccess', _11 => _11.app, 'optionalAccess', _12 => _12.app_memory])) {
|
|
392
|
+
contexts.app.app_memory = process.memoryUsage().rss;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (_optionalChain$7([contexts, 'optionalAccess', _13 => _13.device, 'optionalAccess', _14 => _14.free_memory])) {
|
|
396
|
+
contexts.device.free_memory = os.freemem();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return contexts;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Returns the operating system context.
|
|
404
|
+
*
|
|
405
|
+
* Based on the current platform, this uses a different strategy to provide the
|
|
406
|
+
* most accurate OS information. Since this might involve spawning subprocesses
|
|
407
|
+
* or accessing the file system, this should only be executed lazily and cached.
|
|
408
|
+
*
|
|
409
|
+
* - On macOS (Darwin), this will execute the `sw_vers` utility. The context
|
|
410
|
+
* has a `name`, `version`, `build` and `kernel_version` set.
|
|
411
|
+
* - On Linux, this will try to load a distribution release from `/etc` and set
|
|
412
|
+
* the `name`, `version` and `kernel_version` fields.
|
|
413
|
+
* - On all other platforms, only a `name` and `version` will be returned. Note
|
|
414
|
+
* that `version` might actually be the kernel version.
|
|
415
|
+
*/
|
|
416
|
+
async function getOsContext() {
|
|
417
|
+
const platformId = os.platform();
|
|
418
|
+
switch (platformId) {
|
|
419
|
+
case 'darwin':
|
|
420
|
+
return getDarwinInfo();
|
|
421
|
+
case 'linux':
|
|
422
|
+
return getLinuxInfo();
|
|
423
|
+
default:
|
|
424
|
+
return {
|
|
425
|
+
name: PLATFORM_NAMES[platformId] || platformId,
|
|
426
|
+
version: os.release(),
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function getCultureContext() {
|
|
432
|
+
try {
|
|
433
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
434
|
+
if (typeof (process.versions ).icu !== 'string') {
|
|
435
|
+
// Node was built without ICU support
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Check that node was built with full Intl support. Its possible it was built without support for non-English
|
|
440
|
+
// locales which will make resolvedOptions inaccurate
|
|
441
|
+
//
|
|
442
|
+
// https://nodejs.org/api/intl.html#detecting-internationalization-support
|
|
443
|
+
const january = new Date(9e8);
|
|
444
|
+
const spanish = new Intl.DateTimeFormat('es', { month: 'long' });
|
|
445
|
+
if (spanish.format(january) === 'enero') {
|
|
446
|
+
const options = Intl.DateTimeFormat().resolvedOptions();
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
locale: options.locale,
|
|
450
|
+
timezone: options.timeZone,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
} catch (err) {
|
|
454
|
+
//
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function getAppContext() {
|
|
461
|
+
const app_memory = process.memoryUsage().rss;
|
|
462
|
+
const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString();
|
|
463
|
+
|
|
464
|
+
return { app_start_time, app_memory };
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Gets device information from os
|
|
469
|
+
*/
|
|
470
|
+
function getDeviceContext(deviceOpt) {
|
|
471
|
+
const device = {};
|
|
472
|
+
|
|
473
|
+
// Sometimes os.uptime() throws due to lacking permissions: https://github.com/getsentry/sentry-javascript/issues/8202
|
|
474
|
+
let uptime;
|
|
475
|
+
try {
|
|
476
|
+
uptime = os.uptime && os.uptime();
|
|
477
|
+
} catch (e) {
|
|
478
|
+
// noop
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// os.uptime or its return value seem to be undefined in certain environments (e.g. Azure functions).
|
|
482
|
+
// Hence, we only set boot time, if we get a valid uptime value.
|
|
483
|
+
// @see https://github.com/getsentry/sentry-javascript/issues/5856
|
|
484
|
+
if (typeof uptime === 'number') {
|
|
485
|
+
device.boot_time = new Date(Date.now() - uptime * 1000).toISOString();
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
device.arch = os.arch();
|
|
489
|
+
|
|
490
|
+
if (deviceOpt === true || deviceOpt.memory) {
|
|
491
|
+
device.memory_size = os.totalmem();
|
|
492
|
+
device.free_memory = os.freemem();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (deviceOpt === true || deviceOpt.cpu) {
|
|
496
|
+
const cpuInfo = os.cpus();
|
|
497
|
+
if (cpuInfo && cpuInfo.length) {
|
|
498
|
+
const firstCpu = cpuInfo[0];
|
|
499
|
+
|
|
500
|
+
device.processor_count = cpuInfo.length;
|
|
501
|
+
device.cpu_description = firstCpu.model;
|
|
502
|
+
device.processor_frequency = firstCpu.speed;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return device;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/** Mapping of Node's platform names to actual OS names. */
|
|
510
|
+
const PLATFORM_NAMES = {
|
|
511
|
+
aix: 'IBM AIX',
|
|
512
|
+
freebsd: 'FreeBSD',
|
|
513
|
+
openbsd: 'OpenBSD',
|
|
514
|
+
sunos: 'SunOS',
|
|
515
|
+
win32: 'Windows',
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
/** Linux version file to check for a distribution. */
|
|
519
|
+
|
|
520
|
+
/** Mapping of linux release files located in /etc to distributions. */
|
|
521
|
+
const LINUX_DISTROS = [
|
|
522
|
+
{ name: 'fedora-release', distros: ['Fedora'] },
|
|
523
|
+
{ name: 'redhat-release', distros: ['Red Hat Linux', 'Centos'] },
|
|
524
|
+
{ name: 'redhat_version', distros: ['Red Hat Linux'] },
|
|
525
|
+
{ name: 'SuSE-release', distros: ['SUSE Linux'] },
|
|
526
|
+
{ name: 'lsb-release', distros: ['Ubuntu Linux', 'Arch Linux'] },
|
|
527
|
+
{ name: 'debian_version', distros: ['Debian'] },
|
|
528
|
+
{ name: 'debian_release', distros: ['Debian'] },
|
|
529
|
+
{ name: 'arch-release', distros: ['Arch Linux'] },
|
|
530
|
+
{ name: 'gentoo-release', distros: ['Gentoo Linux'] },
|
|
531
|
+
{ name: 'novell-release', distros: ['SUSE Linux'] },
|
|
532
|
+
{ name: 'alpine-release', distros: ['Alpine Linux'] },
|
|
533
|
+
];
|
|
534
|
+
|
|
535
|
+
/** Functions to extract the OS version from Linux release files. */
|
|
536
|
+
const LINUX_VERSIONS
|
|
537
|
+
|
|
538
|
+
= {
|
|
539
|
+
alpine: content => content,
|
|
540
|
+
arch: content => matchFirst(/distrib_release=(.*)/, content),
|
|
541
|
+
centos: content => matchFirst(/release ([^ ]+)/, content),
|
|
542
|
+
debian: content => content,
|
|
543
|
+
fedora: content => matchFirst(/release (..)/, content),
|
|
544
|
+
mint: content => matchFirst(/distrib_release=(.*)/, content),
|
|
545
|
+
red: content => matchFirst(/release ([^ ]+)/, content),
|
|
546
|
+
suse: content => matchFirst(/VERSION = (.*)\n/, content),
|
|
547
|
+
ubuntu: content => matchFirst(/distrib_release=(.*)/, content),
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Executes a regular expression with one capture group.
|
|
552
|
+
*
|
|
553
|
+
* @param regex A regular expression to execute.
|
|
554
|
+
* @param text Content to execute the RegEx on.
|
|
555
|
+
* @returns The captured string if matched; otherwise undefined.
|
|
556
|
+
*/
|
|
557
|
+
function matchFirst(regex, text) {
|
|
558
|
+
const match = regex.exec(text);
|
|
559
|
+
return match ? match[1] : undefined;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/** Loads the macOS operating system context. */
|
|
563
|
+
async function getDarwinInfo() {
|
|
564
|
+
// Default values that will be used in case no operating system information
|
|
565
|
+
// can be loaded. The default version is computed via heuristics from the
|
|
566
|
+
// kernel version, but the build ID is missing.
|
|
567
|
+
const darwinInfo = {
|
|
568
|
+
kernel_version: os.release(),
|
|
569
|
+
name: 'Mac OS X',
|
|
570
|
+
version: `10.${Number(os.release().split('.')[0]) - 4}`,
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
try {
|
|
574
|
+
// We try to load the actual macOS version by executing the `sw_vers` tool.
|
|
575
|
+
// This tool should be available on every standard macOS installation. In
|
|
576
|
+
// case this fails, we stick with the values computed above.
|
|
577
|
+
|
|
578
|
+
const output = await new Promise((resolve, reject) => {
|
|
579
|
+
execFile('/usr/bin/sw_vers', (error, stdout) => {
|
|
580
|
+
if (error) {
|
|
581
|
+
reject(error);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
resolve(stdout);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
darwinInfo.name = matchFirst(/^ProductName:\s+(.*)$/m, output);
|
|
589
|
+
darwinInfo.version = matchFirst(/^ProductVersion:\s+(.*)$/m, output);
|
|
590
|
+
darwinInfo.build = matchFirst(/^BuildVersion:\s+(.*)$/m, output);
|
|
591
|
+
} catch (e) {
|
|
592
|
+
// ignore
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return darwinInfo;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** Returns a distribution identifier to look up version callbacks. */
|
|
599
|
+
function getLinuxDistroId(name) {
|
|
600
|
+
return name.split(' ')[0].toLowerCase();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Loads the Linux operating system context. */
|
|
604
|
+
async function getLinuxInfo() {
|
|
605
|
+
// By default, we cannot assume anything about the distribution or Linux
|
|
606
|
+
// version. `os.release()` returns the kernel version and we assume a generic
|
|
607
|
+
// "Linux" name, which will be replaced down below.
|
|
608
|
+
const linuxInfo = {
|
|
609
|
+
kernel_version: os.release(),
|
|
610
|
+
name: 'Linux',
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
try {
|
|
614
|
+
// We start guessing the distribution by listing files in the /etc
|
|
615
|
+
// directory. This is were most Linux distributions (except Knoppix) store
|
|
616
|
+
// release files with certain distribution-dependent meta data. We search
|
|
617
|
+
// for exactly one known file defined in `LINUX_DISTROS` and exit if none
|
|
618
|
+
// are found. In case there are more than one file, we just stick with the
|
|
619
|
+
// first one.
|
|
620
|
+
const etcFiles = await readDirAsync('/etc');
|
|
621
|
+
const distroFile = LINUX_DISTROS.find(file => etcFiles.includes(file.name));
|
|
622
|
+
if (!distroFile) {
|
|
623
|
+
return linuxInfo;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Once that file is known, load its contents. To make searching in those
|
|
627
|
+
// files easier, we lowercase the file contents. Since these files are
|
|
628
|
+
// usually quite small, this should not allocate too much memory and we only
|
|
629
|
+
// hold on to it for a very short amount of time.
|
|
630
|
+
const distroPath = join('/etc', distroFile.name);
|
|
631
|
+
const contents = ((await readFileAsync$1(distroPath, { encoding: 'utf-8' })) ).toLowerCase();
|
|
632
|
+
|
|
633
|
+
// Some Linux distributions store their release information in the same file
|
|
634
|
+
// (e.g. RHEL and Centos). In those cases, we scan the file for an
|
|
635
|
+
// identifier, that basically consists of the first word of the linux
|
|
636
|
+
// distribution name (e.g. "red" for Red Hat). In case there is no match, we
|
|
637
|
+
// just assume the first distribution in our list.
|
|
638
|
+
const { distros } = distroFile;
|
|
639
|
+
linuxInfo.name = distros.find(d => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];
|
|
640
|
+
|
|
641
|
+
// Based on the found distribution, we can now compute the actual version
|
|
642
|
+
// number. This is different for every distribution, so several strategies
|
|
643
|
+
// are computed in `LINUX_VERSIONS`.
|
|
644
|
+
const id = getLinuxDistroId(linuxInfo.name);
|
|
645
|
+
linuxInfo.version = LINUX_VERSIONS[id](contents);
|
|
646
|
+
} catch (e) {
|
|
647
|
+
// ignore
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return linuxInfo;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Grabs some information about hosting provider based on best effort.
|
|
655
|
+
*/
|
|
656
|
+
function getCloudResourceContext() {
|
|
657
|
+
if (process.env.VERCEL) {
|
|
658
|
+
// https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables
|
|
659
|
+
return {
|
|
660
|
+
'cloud.provider': 'vercel',
|
|
661
|
+
'cloud.region': process.env.VERCEL_REGION,
|
|
662
|
+
};
|
|
663
|
+
} else if (process.env.AWS_REGION) {
|
|
664
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
|
|
665
|
+
return {
|
|
666
|
+
'cloud.provider': 'aws',
|
|
667
|
+
'cloud.region': process.env.AWS_REGION,
|
|
668
|
+
'cloud.platform': process.env.AWS_EXECUTION_ENV,
|
|
669
|
+
};
|
|
670
|
+
} else if (process.env.GCP_PROJECT) {
|
|
671
|
+
// https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables
|
|
672
|
+
return {
|
|
673
|
+
'cloud.provider': 'gcp',
|
|
674
|
+
};
|
|
675
|
+
} else if (process.env.ALIYUN_REGION_ID) {
|
|
676
|
+
// TODO: find where I found these environment variables - at least gc.github.com returns something
|
|
677
|
+
return {
|
|
678
|
+
'cloud.provider': 'alibaba_cloud',
|
|
679
|
+
'cloud.region': process.env.ALIYUN_REGION_ID,
|
|
680
|
+
};
|
|
681
|
+
} else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {
|
|
682
|
+
// https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment
|
|
683
|
+
return {
|
|
684
|
+
'cloud.provider': 'azure',
|
|
685
|
+
'cloud.region': process.env.REGION_NAME,
|
|
686
|
+
};
|
|
687
|
+
} else if (process.env.IBM_CLOUD_REGION) {
|
|
688
|
+
// TODO: find where I found these environment variables - at least gc.github.com returns something
|
|
689
|
+
return {
|
|
690
|
+
'cloud.provider': 'ibm_cloud',
|
|
691
|
+
'cloud.region': process.env.IBM_CLOUD_REGION,
|
|
692
|
+
};
|
|
693
|
+
} else if (process.env.TENCENTCLOUD_REGION) {
|
|
694
|
+
// https://www.tencentcloud.com/document/product/583/32748
|
|
695
|
+
return {
|
|
696
|
+
'cloud.provider': 'tencent_cloud',
|
|
697
|
+
'cloud.region': process.env.TENCENTCLOUD_REGION,
|
|
698
|
+
'cloud.account.id': process.env.TENCENTCLOUD_APPID,
|
|
699
|
+
'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE,
|
|
700
|
+
};
|
|
701
|
+
} else if (process.env.NETLIFY) {
|
|
702
|
+
// https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables
|
|
703
|
+
return {
|
|
704
|
+
'cloud.provider': 'netlify',
|
|
705
|
+
};
|
|
706
|
+
} else if (process.env.FLY_REGION) {
|
|
707
|
+
// https://fly.io/docs/reference/runtime-environment/
|
|
708
|
+
return {
|
|
709
|
+
'cloud.provider': 'fly.io',
|
|
710
|
+
'cloud.region': process.env.FLY_REGION,
|
|
711
|
+
};
|
|
712
|
+
} else if (process.env.DYNO) {
|
|
713
|
+
// https://devcenter.heroku.com/articles/dynos#local-environment-variables
|
|
714
|
+
return {
|
|
715
|
+
'cloud.provider': 'heroku',
|
|
716
|
+
};
|
|
717
|
+
} else {
|
|
718
|
+
return undefined;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function _optionalChain$6(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
723
|
+
const FILE_CONTENT_CACHE = new LRUMap(100);
|
|
724
|
+
const DEFAULT_LINES_OF_CONTEXT = 7;
|
|
725
|
+
const INTEGRATION_NAME$6 = 'ContextLines';
|
|
726
|
+
|
|
727
|
+
const readFileAsync = promises.readFile;
|
|
728
|
+
|
|
729
|
+
/** Exported only for tests, as a type-safe variant. */
|
|
730
|
+
const _contextLinesIntegration = ((options = {}) => {
|
|
731
|
+
const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT;
|
|
732
|
+
|
|
733
|
+
return {
|
|
734
|
+
name: INTEGRATION_NAME$6,
|
|
735
|
+
processEvent(event) {
|
|
736
|
+
return addSourceContext(event, contextLines);
|
|
737
|
+
},
|
|
738
|
+
};
|
|
739
|
+
}) ;
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Capture the lines before and after the frame's context.
|
|
743
|
+
*/
|
|
744
|
+
const contextLinesIntegration = defineIntegration(_contextLinesIntegration);
|
|
745
|
+
|
|
746
|
+
async function addSourceContext(event, contextLines) {
|
|
747
|
+
// keep a lookup map of which files we've already enqueued to read,
|
|
748
|
+
// so we don't enqueue the same file multiple times which would cause multiple i/o reads
|
|
749
|
+
const enqueuedReadSourceFileTasks = {};
|
|
750
|
+
const readSourceFileTasks = [];
|
|
751
|
+
|
|
752
|
+
if (contextLines > 0 && _optionalChain$6([event, 'access', _2 => _2.exception, 'optionalAccess', _3 => _3.values])) {
|
|
753
|
+
for (const exception of event.exception.values) {
|
|
754
|
+
if (!_optionalChain$6([exception, 'access', _4 => _4.stacktrace, 'optionalAccess', _5 => _5.frames])) {
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// We want to iterate in reverse order as calling cache.get will bump the file in our LRU cache.
|
|
759
|
+
// This ends up prioritizes source context for frames at the top of the stack instead of the bottom.
|
|
760
|
+
for (let i = exception.stacktrace.frames.length - 1; i >= 0; i--) {
|
|
761
|
+
const frame = exception.stacktrace.frames[i];
|
|
762
|
+
// Call cache.get to bump the file to the top of the cache and ensure we have not already
|
|
763
|
+
// enqueued a read operation for this filename
|
|
764
|
+
if (frame.filename && !enqueuedReadSourceFileTasks[frame.filename] && !FILE_CONTENT_CACHE.get(frame.filename)) {
|
|
765
|
+
readSourceFileTasks.push(_readSourceFile(frame.filename));
|
|
766
|
+
enqueuedReadSourceFileTasks[frame.filename] = 1;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// check if files to read > 0, if so, await all of them to be read before adding source contexts.
|
|
773
|
+
// Normally, Promise.all here could be short circuited if one of the promises rejects, but we
|
|
774
|
+
// are guarding from that by wrapping the i/o read operation in a try/catch.
|
|
775
|
+
if (readSourceFileTasks.length > 0) {
|
|
776
|
+
await Promise.all(readSourceFileTasks);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// Perform the same loop as above, but this time we can assume all files are in the cache
|
|
780
|
+
// and attempt to add source context to frames.
|
|
781
|
+
if (contextLines > 0 && _optionalChain$6([event, 'access', _6 => _6.exception, 'optionalAccess', _7 => _7.values])) {
|
|
782
|
+
for (const exception of event.exception.values) {
|
|
783
|
+
if (exception.stacktrace && exception.stacktrace.frames) {
|
|
784
|
+
await addSourceContextToFrames(exception.stacktrace.frames, contextLines);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return event;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/** Adds context lines to frames */
|
|
793
|
+
function addSourceContextToFrames(frames, contextLines) {
|
|
794
|
+
for (const frame of frames) {
|
|
795
|
+
// Only add context if we have a filename and it hasn't already been added
|
|
796
|
+
if (frame.filename && frame.context_line === undefined) {
|
|
797
|
+
const sourceFileLines = FILE_CONTENT_CACHE.get(frame.filename);
|
|
798
|
+
|
|
799
|
+
if (sourceFileLines) {
|
|
800
|
+
try {
|
|
801
|
+
addContextToFrame(sourceFileLines, frame, contextLines);
|
|
802
|
+
} catch (e) {
|
|
803
|
+
// anomaly, being defensive in case
|
|
804
|
+
// unlikely to ever happen in practice but can definitely happen in theory
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Reads file contents and caches them in a global LRU cache.
|
|
813
|
+
* If reading fails, mark the file as null in the cache so we don't try again.
|
|
814
|
+
*
|
|
815
|
+
* @param filename filepath to read content from.
|
|
816
|
+
*/
|
|
817
|
+
async function _readSourceFile(filename) {
|
|
818
|
+
const cachedFile = FILE_CONTENT_CACHE.get(filename);
|
|
819
|
+
|
|
820
|
+
// We have already attempted to read this file and failed, do not try again
|
|
821
|
+
if (cachedFile === null) {
|
|
822
|
+
return null;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// We have a cache hit, return it
|
|
826
|
+
if (cachedFile !== undefined) {
|
|
827
|
+
return cachedFile;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Guard from throwing if readFile fails, this enables us to use Promise.all and
|
|
831
|
+
// not have it short circuiting if one of the promises rejects + since context lines are added
|
|
832
|
+
// on a best effort basis, we want to throw here anyways.
|
|
833
|
+
|
|
834
|
+
// If we made it to here, it means that our file is not cache nor marked as failed, so attempt to read it
|
|
835
|
+
let content = null;
|
|
836
|
+
try {
|
|
837
|
+
const rawFileContents = await readFileAsync(filename, 'utf-8');
|
|
838
|
+
content = rawFileContents.split('\n');
|
|
839
|
+
} catch (_) {
|
|
840
|
+
// if we fail, we will mark the file as null in the cache and short circuit next time we try to read it
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
FILE_CONTENT_CACHE.set(filename, content);
|
|
844
|
+
return content;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Creates a rate limiter that will call the disable callback when the rate limit is reached and the enable callback
|
|
849
|
+
* when a timeout has occurred.
|
|
850
|
+
* @param maxPerSecond Maximum number of calls per second
|
|
851
|
+
* @param enable Callback to enable capture
|
|
852
|
+
* @param disable Callback to disable capture
|
|
853
|
+
* @returns A function to call to increment the rate limiter count
|
|
854
|
+
*/
|
|
855
|
+
function createRateLimiter(
|
|
856
|
+
maxPerSecond,
|
|
857
|
+
enable,
|
|
858
|
+
disable,
|
|
859
|
+
) {
|
|
860
|
+
let count = 0;
|
|
861
|
+
let retrySeconds = 5;
|
|
862
|
+
let disabledTimeout = 0;
|
|
863
|
+
|
|
864
|
+
setInterval(() => {
|
|
865
|
+
if (disabledTimeout === 0) {
|
|
866
|
+
if (count > maxPerSecond) {
|
|
867
|
+
retrySeconds *= 2;
|
|
868
|
+
disable(retrySeconds);
|
|
869
|
+
|
|
870
|
+
// Cap at one day
|
|
871
|
+
if (retrySeconds > 86400) {
|
|
872
|
+
retrySeconds = 86400;
|
|
873
|
+
}
|
|
874
|
+
disabledTimeout = retrySeconds;
|
|
875
|
+
}
|
|
876
|
+
} else {
|
|
877
|
+
disabledTimeout -= 1;
|
|
878
|
+
|
|
879
|
+
if (disabledTimeout === 0) {
|
|
880
|
+
enable();
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
count = 0;
|
|
885
|
+
}, 1000).unref();
|
|
886
|
+
|
|
887
|
+
return () => {
|
|
888
|
+
count += 1;
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// Add types for the exception event data
|
|
893
|
+
|
|
894
|
+
/** Could this be an anonymous function? */
|
|
895
|
+
function isAnonymous(name) {
|
|
896
|
+
return name !== undefined && (name.length === 0 || name === '?' || name === '<anonymous>');
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/** Do the function names appear to match? */
|
|
900
|
+
function functionNamesMatch(a, b) {
|
|
901
|
+
return a === b || (isAnonymous(a) && isAnonymous(b));
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/** Creates a unique hash from stack frames */
|
|
905
|
+
function hashFrames(frames) {
|
|
906
|
+
if (frames === undefined) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Only hash the 10 most recent frames (ie. the last 10)
|
|
911
|
+
return frames.slice(-10).reduce((acc, frame) => `${acc},${frame.function},${frame.lineno},${frame.colno}`, '');
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* We use the stack parser to create a unique hash from the exception stack trace
|
|
916
|
+
* This is used to lookup vars when the exception passes through the event processor
|
|
917
|
+
*/
|
|
918
|
+
function hashFromStack(stackParser, stack) {
|
|
919
|
+
if (stack === undefined) {
|
|
920
|
+
return undefined;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
return hashFrames(stackParser(stack, 1));
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function _optionalChain$5(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
927
|
+
/** Creates a container for callbacks to be called sequentially */
|
|
928
|
+
function createCallbackList(complete) {
|
|
929
|
+
// A collection of callbacks to be executed last to first
|
|
930
|
+
let callbacks = [];
|
|
931
|
+
|
|
932
|
+
let completedCalled = false;
|
|
933
|
+
function checkedComplete(result) {
|
|
934
|
+
callbacks = [];
|
|
935
|
+
if (completedCalled) {
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
completedCalled = true;
|
|
939
|
+
complete(result);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// complete should be called last
|
|
943
|
+
callbacks.push(checkedComplete);
|
|
944
|
+
|
|
945
|
+
function add(fn) {
|
|
946
|
+
callbacks.push(fn);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
function next(result) {
|
|
950
|
+
const popped = callbacks.pop() || checkedComplete;
|
|
951
|
+
|
|
952
|
+
try {
|
|
953
|
+
popped(result);
|
|
954
|
+
} catch (_) {
|
|
955
|
+
// If there is an error, we still want to call the complete callback
|
|
956
|
+
checkedComplete(result);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
return { add, next };
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Promise API is available as `Experimental` and in Node 19 only.
|
|
965
|
+
*
|
|
966
|
+
* Callback-based API is `Stable` since v14 and `Experimental` since v8.
|
|
967
|
+
* Because of that, we are creating our own `AsyncSession` class.
|
|
968
|
+
*
|
|
969
|
+
* https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api
|
|
970
|
+
* https://nodejs.org/docs/latest-v14.x/api/inspector.html
|
|
971
|
+
*/
|
|
972
|
+
class AsyncSession {
|
|
973
|
+
|
|
974
|
+
/** Throws if inspector API is not available */
|
|
975
|
+
constructor() {
|
|
976
|
+
this._session = new Session();
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/** @inheritdoc */
|
|
980
|
+
configureAndConnect(onPause, captureAll) {
|
|
981
|
+
this._session.connect();
|
|
982
|
+
|
|
983
|
+
this._session.on('Debugger.paused', event => {
|
|
984
|
+
onPause(event, () => {
|
|
985
|
+
// After the pause work is complete, resume execution or the exception context memory is leaked
|
|
986
|
+
this._session.post('Debugger.resume');
|
|
987
|
+
});
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
this._session.post('Debugger.enable');
|
|
991
|
+
this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
setPauseOnExceptions(captureAll) {
|
|
995
|
+
this._session.post('Debugger.setPauseOnExceptions', { state: captureAll ? 'all' : 'uncaught' });
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/** @inheritdoc */
|
|
999
|
+
getLocalVariables(objectId, complete) {
|
|
1000
|
+
this._getProperties(objectId, props => {
|
|
1001
|
+
const { add, next } = createCallbackList(complete);
|
|
1002
|
+
|
|
1003
|
+
for (const prop of props) {
|
|
1004
|
+
if (_optionalChain$5([prop, 'optionalAccess', _2 => _2.value, 'optionalAccess', _3 => _3.objectId]) && _optionalChain$5([prop, 'optionalAccess', _4 => _4.value, 'access', _5 => _5.className]) === 'Array') {
|
|
1005
|
+
const id = prop.value.objectId;
|
|
1006
|
+
add(vars => this._unrollArray(id, prop.name, vars, next));
|
|
1007
|
+
} else if (_optionalChain$5([prop, 'optionalAccess', _6 => _6.value, 'optionalAccess', _7 => _7.objectId]) && _optionalChain$5([prop, 'optionalAccess', _8 => _8.value, 'optionalAccess', _9 => _9.className]) === 'Object') {
|
|
1008
|
+
const id = prop.value.objectId;
|
|
1009
|
+
add(vars => this._unrollObject(id, prop.name, vars, next));
|
|
1010
|
+
} else if (_optionalChain$5([prop, 'optionalAccess', _10 => _10.value])) {
|
|
1011
|
+
add(vars => this._unrollOther(prop, vars, next));
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
next({});
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Gets all the PropertyDescriptors of an object
|
|
1021
|
+
*/
|
|
1022
|
+
_getProperties(objectId, next) {
|
|
1023
|
+
this._session.post(
|
|
1024
|
+
'Runtime.getProperties',
|
|
1025
|
+
{
|
|
1026
|
+
objectId,
|
|
1027
|
+
ownProperties: true,
|
|
1028
|
+
},
|
|
1029
|
+
(err, params) => {
|
|
1030
|
+
if (err) {
|
|
1031
|
+
next([]);
|
|
1032
|
+
} else {
|
|
1033
|
+
next(params.result);
|
|
1034
|
+
}
|
|
1035
|
+
},
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Unrolls an array property
|
|
1041
|
+
*/
|
|
1042
|
+
_unrollArray(objectId, name, vars, next) {
|
|
1043
|
+
this._getProperties(objectId, props => {
|
|
1044
|
+
vars[name] = props
|
|
1045
|
+
.filter(v => v.name !== 'length' && !isNaN(parseInt(v.name, 10)))
|
|
1046
|
+
.sort((a, b) => parseInt(a.name, 10) - parseInt(b.name, 10))
|
|
1047
|
+
.map(v => _optionalChain$5([v, 'optionalAccess', _11 => _11.value, 'optionalAccess', _12 => _12.value]));
|
|
1048
|
+
|
|
1049
|
+
next(vars);
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Unrolls an object property
|
|
1055
|
+
*/
|
|
1056
|
+
_unrollObject(objectId, name, vars, next) {
|
|
1057
|
+
this._getProperties(objectId, props => {
|
|
1058
|
+
vars[name] = props
|
|
1059
|
+
.map(v => [v.name, _optionalChain$5([v, 'optionalAccess', _13 => _13.value, 'optionalAccess', _14 => _14.value])])
|
|
1060
|
+
.reduce((obj, [key, val]) => {
|
|
1061
|
+
obj[key] = val;
|
|
1062
|
+
return obj;
|
|
1063
|
+
}, {} );
|
|
1064
|
+
|
|
1065
|
+
next(vars);
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* Unrolls other properties
|
|
1071
|
+
*/
|
|
1072
|
+
_unrollOther(prop, vars, next) {
|
|
1073
|
+
if (prop.value) {
|
|
1074
|
+
if ('value' in prop.value) {
|
|
1075
|
+
if (prop.value.value === undefined || prop.value.value === null) {
|
|
1076
|
+
vars[prop.name] = `<${prop.value.value}>`;
|
|
1077
|
+
} else {
|
|
1078
|
+
vars[prop.name] = prop.value.value;
|
|
1079
|
+
}
|
|
1080
|
+
} else if ('description' in prop.value && prop.value.type !== 'function') {
|
|
1081
|
+
vars[prop.name] = `<${prop.value.description}>`;
|
|
1082
|
+
} else if (prop.value.type === 'undefined') {
|
|
1083
|
+
vars[prop.name] = '<undefined>';
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
next(vars);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* When using Vercel pkg, the inspector module is not available.
|
|
1093
|
+
* https://github.com/getsentry/sentry-javascript/issues/6769
|
|
1094
|
+
*/
|
|
1095
|
+
function tryNewAsyncSession() {
|
|
1096
|
+
try {
|
|
1097
|
+
return new AsyncSession();
|
|
1098
|
+
} catch (e) {
|
|
1099
|
+
return undefined;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const INTEGRATION_NAME$5 = 'LocalVariables';
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Adds local variables to exception frames
|
|
1107
|
+
*/
|
|
1108
|
+
const _localVariablesSyncIntegration = ((
|
|
1109
|
+
options = {},
|
|
1110
|
+
session = tryNewAsyncSession(),
|
|
1111
|
+
) => {
|
|
1112
|
+
const cachedFrames = new LRUMap(20);
|
|
1113
|
+
let rateLimiter;
|
|
1114
|
+
let shouldProcessEvent = false;
|
|
1115
|
+
|
|
1116
|
+
function handlePaused(
|
|
1117
|
+
stackParser,
|
|
1118
|
+
{ params: { reason, data, callFrames } },
|
|
1119
|
+
complete,
|
|
1120
|
+
) {
|
|
1121
|
+
if (reason !== 'exception' && reason !== 'promiseRejection') {
|
|
1122
|
+
complete();
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
_optionalChain$5([rateLimiter, 'optionalCall', _15 => _15()]);
|
|
1127
|
+
|
|
1128
|
+
// data.description contains the original error.stack
|
|
1129
|
+
const exceptionHash = hashFromStack(stackParser, _optionalChain$5([data, 'optionalAccess', _16 => _16.description]));
|
|
1130
|
+
|
|
1131
|
+
if (exceptionHash == undefined) {
|
|
1132
|
+
complete();
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
const { add, next } = createCallbackList(frames => {
|
|
1137
|
+
cachedFrames.set(exceptionHash, frames);
|
|
1138
|
+
complete();
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
// Because we're queuing up and making all these calls synchronously, we can potentially overflow the stack
|
|
1142
|
+
// For this reason we only attempt to get local variables for the first 5 frames
|
|
1143
|
+
for (let i = 0; i < Math.min(callFrames.length, 5); i++) {
|
|
1144
|
+
const { scopeChain, functionName, this: obj } = callFrames[i];
|
|
1145
|
+
|
|
1146
|
+
const localScope = scopeChain.find(scope => scope.type === 'local');
|
|
1147
|
+
|
|
1148
|
+
// obj.className is undefined in ESM modules
|
|
1149
|
+
const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;
|
|
1150
|
+
|
|
1151
|
+
if (_optionalChain$5([localScope, 'optionalAccess', _17 => _17.object, 'access', _18 => _18.objectId]) === undefined) {
|
|
1152
|
+
add(frames => {
|
|
1153
|
+
frames[i] = { function: fn };
|
|
1154
|
+
next(frames);
|
|
1155
|
+
});
|
|
1156
|
+
} else {
|
|
1157
|
+
const id = localScope.object.objectId;
|
|
1158
|
+
add(frames =>
|
|
1159
|
+
_optionalChain$5([session, 'optionalAccess', _19 => _19.getLocalVariables, 'call', _20 => _20(id, vars => {
|
|
1160
|
+
frames[i] = { function: fn, vars };
|
|
1161
|
+
next(frames);
|
|
1162
|
+
})]),
|
|
1163
|
+
);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
next([]);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
function addLocalVariablesToException(exception) {
|
|
1171
|
+
const hash = hashFrames(_optionalChain$5([exception, 'optionalAccess', _21 => _21.stacktrace, 'optionalAccess', _22 => _22.frames]));
|
|
1172
|
+
|
|
1173
|
+
if (hash === undefined) {
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// Check if we have local variables for an exception that matches the hash
|
|
1178
|
+
// remove is identical to get but also removes the entry from the cache
|
|
1179
|
+
const cachedFrame = cachedFrames.remove(hash);
|
|
1180
|
+
|
|
1181
|
+
if (cachedFrame === undefined) {
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const frameCount = _optionalChain$5([exception, 'access', _23 => _23.stacktrace, 'optionalAccess', _24 => _24.frames, 'optionalAccess', _25 => _25.length]) || 0;
|
|
1186
|
+
|
|
1187
|
+
for (let i = 0; i < frameCount; i++) {
|
|
1188
|
+
// Sentry frames are in reverse order
|
|
1189
|
+
const frameIndex = frameCount - i - 1;
|
|
1190
|
+
|
|
1191
|
+
// Drop out if we run out of frames to match up
|
|
1192
|
+
if (!_optionalChain$5([exception, 'optionalAccess', _26 => _26.stacktrace, 'optionalAccess', _27 => _27.frames, 'optionalAccess', _28 => _28[frameIndex]]) || !cachedFrame[i]) {
|
|
1193
|
+
break;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
if (
|
|
1197
|
+
// We need to have vars to add
|
|
1198
|
+
cachedFrame[i].vars === undefined ||
|
|
1199
|
+
// We're not interested in frames that are not in_app because the vars are not relevant
|
|
1200
|
+
exception.stacktrace.frames[frameIndex].in_app === false ||
|
|
1201
|
+
// The function names need to match
|
|
1202
|
+
!functionNamesMatch(exception.stacktrace.frames[frameIndex].function, cachedFrame[i].function)
|
|
1203
|
+
) {
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
exception.stacktrace.frames[frameIndex].vars = cachedFrame[i].vars;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function addLocalVariablesToEvent(event) {
|
|
1212
|
+
for (const exception of _optionalChain$5([event, 'optionalAccess', _29 => _29.exception, 'optionalAccess', _30 => _30.values]) || []) {
|
|
1213
|
+
addLocalVariablesToException(exception);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
return event;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
return {
|
|
1220
|
+
name: INTEGRATION_NAME$5,
|
|
1221
|
+
setupOnce() {
|
|
1222
|
+
const client = getClient$2();
|
|
1223
|
+
const clientOptions = _optionalChain$5([client, 'optionalAccess', _31 => _31.getOptions, 'call', _32 => _32()]);
|
|
1224
|
+
|
|
1225
|
+
if (session && _optionalChain$5([clientOptions, 'optionalAccess', _33 => _33.includeLocalVariables])) {
|
|
1226
|
+
// Only setup this integration if the Node version is >= v18
|
|
1227
|
+
// https://github.com/getsentry/sentry-javascript/issues/7697
|
|
1228
|
+
const unsupportedNodeVersion = NODE_MAJOR < 18;
|
|
1229
|
+
|
|
1230
|
+
if (unsupportedNodeVersion) {
|
|
1231
|
+
logger.log('The `LocalVariables` integration is only supported on Node >= v18.');
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
const captureAll = options.captureAllExceptions !== false;
|
|
1236
|
+
|
|
1237
|
+
session.configureAndConnect(
|
|
1238
|
+
(ev, complete) =>
|
|
1239
|
+
handlePaused(clientOptions.stackParser, ev , complete),
|
|
1240
|
+
captureAll,
|
|
1241
|
+
);
|
|
1242
|
+
|
|
1243
|
+
if (captureAll) {
|
|
1244
|
+
const max = options.maxExceptionsPerSecond || 50;
|
|
1245
|
+
|
|
1246
|
+
rateLimiter = createRateLimiter(
|
|
1247
|
+
max,
|
|
1248
|
+
() => {
|
|
1249
|
+
logger.log('Local variables rate-limit lifted.');
|
|
1250
|
+
_optionalChain$5([session, 'optionalAccess', _34 => _34.setPauseOnExceptions, 'call', _35 => _35(true)]);
|
|
1251
|
+
},
|
|
1252
|
+
seconds => {
|
|
1253
|
+
logger.log(
|
|
1254
|
+
`Local variables rate-limit exceeded. Disabling capturing of caught exceptions for ${seconds} seconds.`,
|
|
1255
|
+
);
|
|
1256
|
+
_optionalChain$5([session, 'optionalAccess', _36 => _36.setPauseOnExceptions, 'call', _37 => _37(false)]);
|
|
1257
|
+
},
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
shouldProcessEvent = true;
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
processEvent(event) {
|
|
1265
|
+
if (shouldProcessEvent) {
|
|
1266
|
+
return addLocalVariablesToEvent(event);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
return event;
|
|
1270
|
+
},
|
|
1271
|
+
// These are entirely for testing
|
|
1272
|
+
_getCachedFramesCount() {
|
|
1273
|
+
return cachedFrames.size;
|
|
1274
|
+
},
|
|
1275
|
+
_getFirstCachedFrame() {
|
|
1276
|
+
return cachedFrames.values()[0];
|
|
1277
|
+
},
|
|
1278
|
+
};
|
|
1279
|
+
}) ;
|
|
1280
|
+
|
|
1281
|
+
const localVariablesSyncIntegration = defineIntegration(_localVariablesSyncIntegration);
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Adds local variables to exception frames.
|
|
1285
|
+
* @deprecated Use `localVariablesSyncIntegration()` instead.
|
|
1286
|
+
*/
|
|
1287
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
1288
|
+
convertIntegrationFnToClass(
|
|
1289
|
+
INTEGRATION_NAME$5,
|
|
1290
|
+
localVariablesSyncIntegration,
|
|
1291
|
+
)
|
|
1292
|
+
|
|
1293
|
+
;
|
|
1294
|
+
|
|
1295
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
1296
|
+
|
|
1297
|
+
const localVariablesIntegration = localVariablesSyncIntegration;
|
|
1298
|
+
|
|
1299
|
+
let moduleCache;
|
|
1300
|
+
|
|
1301
|
+
const INTEGRATION_NAME$4 = 'Modules';
|
|
1302
|
+
|
|
1303
|
+
const _modulesIntegration = (() => {
|
|
1304
|
+
return {
|
|
1305
|
+
name: INTEGRATION_NAME$4,
|
|
1306
|
+
processEvent(event) {
|
|
1307
|
+
event.modules = {
|
|
1308
|
+
...event.modules,
|
|
1309
|
+
..._getModules(),
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
return event;
|
|
1313
|
+
},
|
|
1314
|
+
};
|
|
1315
|
+
}) ;
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Add node modules / packages to the event.
|
|
1319
|
+
*/
|
|
1320
|
+
const modulesIntegration = defineIntegration(_modulesIntegration);
|
|
1321
|
+
|
|
1322
|
+
/** Extract information about paths */
|
|
1323
|
+
function getPaths() {
|
|
1324
|
+
try {
|
|
1325
|
+
return require.cache ? Object.keys(require.cache ) : [];
|
|
1326
|
+
} catch (e) {
|
|
1327
|
+
return [];
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/** Extract information about package.json modules */
|
|
1332
|
+
function collectModules()
|
|
1333
|
+
|
|
1334
|
+
{
|
|
1335
|
+
const mainPaths = (require.main && require.main.paths) || [];
|
|
1336
|
+
const paths = getPaths();
|
|
1337
|
+
const infos
|
|
1338
|
+
|
|
1339
|
+
= {};
|
|
1340
|
+
const seen
|
|
1341
|
+
|
|
1342
|
+
= {};
|
|
1343
|
+
|
|
1344
|
+
paths.forEach(path => {
|
|
1345
|
+
let dir = path;
|
|
1346
|
+
|
|
1347
|
+
/** Traverse directories upward in the search of package.json file */
|
|
1348
|
+
const updir = () => {
|
|
1349
|
+
const orig = dir;
|
|
1350
|
+
dir = dirname(orig);
|
|
1351
|
+
|
|
1352
|
+
if (!dir || orig === dir || seen[orig]) {
|
|
1353
|
+
return undefined;
|
|
1354
|
+
}
|
|
1355
|
+
if (mainPaths.indexOf(dir) < 0) {
|
|
1356
|
+
return updir();
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
const pkgfile = join(orig, 'package.json');
|
|
1360
|
+
seen[orig] = true;
|
|
1361
|
+
|
|
1362
|
+
if (!existsSync(pkgfile)) {
|
|
1363
|
+
return updir();
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
try {
|
|
1367
|
+
const info = JSON.parse(readFileSync(pkgfile, 'utf8'))
|
|
1368
|
+
|
|
1369
|
+
;
|
|
1370
|
+
infos[info.name] = info.version;
|
|
1371
|
+
} catch (_oO) {
|
|
1372
|
+
// no-empty
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
updir();
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
return infos;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */
|
|
1383
|
+
function _getModules() {
|
|
1384
|
+
if (!moduleCache) {
|
|
1385
|
+
moduleCache = collectModules();
|
|
1386
|
+
}
|
|
1387
|
+
return moduleCache;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code.
|
|
1392
|
+
*
|
|
1393
|
+
* ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking.
|
|
1394
|
+
*/
|
|
1395
|
+
const DEBUG_BUILD = (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__);
|
|
1396
|
+
|
|
1397
|
+
const DEFAULT_SHUTDOWN_TIMEOUT = 2000;
|
|
1398
|
+
|
|
1399
|
+
/**
|
|
1400
|
+
* @hidden
|
|
1401
|
+
*/
|
|
1402
|
+
function logAndExitProcess(error) {
|
|
1403
|
+
consoleSandbox(() => {
|
|
1404
|
+
// eslint-disable-next-line no-console
|
|
1405
|
+
console.error(error);
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
const client = getClient$2();
|
|
1409
|
+
|
|
1410
|
+
if (client === undefined) {
|
|
1411
|
+
DEBUG_BUILD && logger.warn('No NodeClient was defined, we are exiting the process now.');
|
|
1412
|
+
global.process.exit(1);
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
const options = client.getOptions();
|
|
1417
|
+
const timeout =
|
|
1418
|
+
(options && options.shutdownTimeout && options.shutdownTimeout > 0 && options.shutdownTimeout) ||
|
|
1419
|
+
DEFAULT_SHUTDOWN_TIMEOUT;
|
|
1420
|
+
client.close(timeout).then(
|
|
1421
|
+
(result) => {
|
|
1422
|
+
if (!result) {
|
|
1423
|
+
DEBUG_BUILD && logger.warn('We reached the timeout for emptying the request buffer, still exiting now!');
|
|
1424
|
+
}
|
|
1425
|
+
global.process.exit(1);
|
|
1426
|
+
},
|
|
1427
|
+
error => {
|
|
1428
|
+
DEBUG_BUILD && logger.error(error);
|
|
1429
|
+
},
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
const INTEGRATION_NAME$3 = 'OnUncaughtException';
|
|
1434
|
+
|
|
1435
|
+
const _onUncaughtExceptionIntegration = ((options = {}) => {
|
|
1436
|
+
const _options = {
|
|
1437
|
+
exitEvenIfOtherHandlersAreRegistered: true,
|
|
1438
|
+
...options,
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
return {
|
|
1442
|
+
name: INTEGRATION_NAME$3,
|
|
1443
|
+
setup(client) {
|
|
1444
|
+
global.process.on('uncaughtException', makeErrorHandler(client, _options));
|
|
1445
|
+
},
|
|
1446
|
+
};
|
|
1447
|
+
}) ;
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Add a global exception handler.
|
|
1451
|
+
*/
|
|
1452
|
+
const onUncaughtExceptionIntegration = defineIntegration(_onUncaughtExceptionIntegration);
|
|
1453
|
+
|
|
1454
|
+
/** Exported only for tests */
|
|
1455
|
+
function makeErrorHandler(client, options) {
|
|
1456
|
+
const timeout = 2000;
|
|
1457
|
+
let caughtFirstError = false;
|
|
1458
|
+
let caughtSecondError = false;
|
|
1459
|
+
let calledFatalError = false;
|
|
1460
|
+
let firstError;
|
|
1461
|
+
|
|
1462
|
+
const clientOptions = client.getOptions();
|
|
1463
|
+
|
|
1464
|
+
return Object.assign(
|
|
1465
|
+
(error) => {
|
|
1466
|
+
let onFatalError = logAndExitProcess;
|
|
1467
|
+
|
|
1468
|
+
if (options.onFatalError) {
|
|
1469
|
+
onFatalError = options.onFatalError;
|
|
1470
|
+
} else if (clientOptions.onFatalError) {
|
|
1471
|
+
onFatalError = clientOptions.onFatalError ;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not
|
|
1475
|
+
// want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust
|
|
1476
|
+
// exit behaviour of the SDK accordingly:
|
|
1477
|
+
// - If other listeners are attached, do not exit.
|
|
1478
|
+
// - If the only listener attached is ours, exit.
|
|
1479
|
+
const userProvidedListenersCount = (global.process.listeners('uncaughtException') ).filter(
|
|
1480
|
+
listener => {
|
|
1481
|
+
// There are 3 listeners we ignore:
|
|
1482
|
+
return (
|
|
1483
|
+
// as soon as we're using domains this listener is attached by node itself
|
|
1484
|
+
listener.name !== 'domainUncaughtExceptionClear' &&
|
|
1485
|
+
// the handler we register for tracing
|
|
1486
|
+
listener.tag !== 'sentry_tracingErrorCallback' &&
|
|
1487
|
+
// the handler we register in this integration
|
|
1488
|
+
(listener )._errorHandler !== true
|
|
1489
|
+
);
|
|
1490
|
+
},
|
|
1491
|
+
).length;
|
|
1492
|
+
|
|
1493
|
+
const processWouldExit = userProvidedListenersCount === 0;
|
|
1494
|
+
const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;
|
|
1495
|
+
|
|
1496
|
+
if (!caughtFirstError) {
|
|
1497
|
+
// this is the first uncaught error and the ultimate reason for shutting down
|
|
1498
|
+
// we want to do absolutely everything possible to ensure it gets captured
|
|
1499
|
+
// also we want to make sure we don't go recursion crazy if more errors happen after this one
|
|
1500
|
+
firstError = error;
|
|
1501
|
+
caughtFirstError = true;
|
|
1502
|
+
|
|
1503
|
+
if (getClient$2() === client) {
|
|
1504
|
+
captureException(error, {
|
|
1505
|
+
originalException: error,
|
|
1506
|
+
captureContext: {
|
|
1507
|
+
level: 'fatal',
|
|
1508
|
+
},
|
|
1509
|
+
mechanism: {
|
|
1510
|
+
handled: false,
|
|
1511
|
+
type: 'onuncaughtexception',
|
|
1512
|
+
},
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
if (!calledFatalError && shouldApplyFatalHandlingLogic) {
|
|
1517
|
+
calledFatalError = true;
|
|
1518
|
+
onFatalError(error);
|
|
1519
|
+
}
|
|
1520
|
+
} else {
|
|
1521
|
+
if (shouldApplyFatalHandlingLogic) {
|
|
1522
|
+
if (calledFatalError) {
|
|
1523
|
+
// we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down
|
|
1524
|
+
DEBUG_BUILD &&
|
|
1525
|
+
logger.warn(
|
|
1526
|
+
'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',
|
|
1527
|
+
);
|
|
1528
|
+
logAndExitProcess(error);
|
|
1529
|
+
} else if (!caughtSecondError) {
|
|
1530
|
+
// two cases for how we can hit this branch:
|
|
1531
|
+
// - capturing of first error blew up and we just caught the exception from that
|
|
1532
|
+
// - quit trying to capture, proceed with shutdown
|
|
1533
|
+
// - a second independent error happened while waiting for first error to capture
|
|
1534
|
+
// - want to avoid causing premature shutdown before first error capture finishes
|
|
1535
|
+
// it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff
|
|
1536
|
+
// so let's instead just delay a bit before we proceed with our action here
|
|
1537
|
+
// in case 1, we just wait a bit unnecessarily but ultimately do the same thing
|
|
1538
|
+
// in case 2, the delay hopefully made us wait long enough for the capture to finish
|
|
1539
|
+
// two potential nonideal outcomes:
|
|
1540
|
+
// nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError
|
|
1541
|
+
// nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error
|
|
1542
|
+
// note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)
|
|
1543
|
+
// we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish
|
|
1544
|
+
caughtSecondError = true;
|
|
1545
|
+
setTimeout(() => {
|
|
1546
|
+
if (!calledFatalError) {
|
|
1547
|
+
// it was probably case 1, let's treat err as the sendErr and call onFatalError
|
|
1548
|
+
calledFatalError = true;
|
|
1549
|
+
onFatalError(firstError, error);
|
|
1550
|
+
}
|
|
1551
|
+
}, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
},
|
|
1556
|
+
{ _errorHandler: true },
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
const INTEGRATION_NAME$2 = 'OnUnhandledRejection';
|
|
1561
|
+
|
|
1562
|
+
const _onUnhandledRejectionIntegration = ((options = {}) => {
|
|
1563
|
+
const mode = options.mode || 'warn';
|
|
1564
|
+
|
|
1565
|
+
return {
|
|
1566
|
+
name: INTEGRATION_NAME$2,
|
|
1567
|
+
setup(client) {
|
|
1568
|
+
global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, { mode }));
|
|
1569
|
+
},
|
|
1570
|
+
};
|
|
1571
|
+
}) ;
|
|
1572
|
+
|
|
1573
|
+
/**
|
|
1574
|
+
* Add a global promise rejection handler.
|
|
1575
|
+
*/
|
|
1576
|
+
const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration);
|
|
1577
|
+
|
|
1578
|
+
/**
|
|
1579
|
+
* Send an exception with reason
|
|
1580
|
+
* @param reason string
|
|
1581
|
+
* @param promise promise
|
|
1582
|
+
*
|
|
1583
|
+
* Exported only for tests.
|
|
1584
|
+
*/
|
|
1585
|
+
function makeUnhandledPromiseHandler(
|
|
1586
|
+
client,
|
|
1587
|
+
options,
|
|
1588
|
+
) {
|
|
1589
|
+
return function sendUnhandledPromise(reason, promise) {
|
|
1590
|
+
if (getClient$2() !== client) {
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
captureException(reason, {
|
|
1595
|
+
originalException: promise,
|
|
1596
|
+
captureContext: {
|
|
1597
|
+
extra: { unhandledPromiseRejection: true },
|
|
1598
|
+
},
|
|
1599
|
+
mechanism: {
|
|
1600
|
+
handled: false,
|
|
1601
|
+
type: 'onunhandledrejection',
|
|
1602
|
+
},
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
handleRejection(reason, options);
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/**
|
|
1610
|
+
* Handler for `mode` option
|
|
1611
|
+
|
|
1612
|
+
*/
|
|
1613
|
+
function handleRejection(
|
|
1614
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1615
|
+
reason,
|
|
1616
|
+
options,
|
|
1617
|
+
) {
|
|
1618
|
+
// https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240
|
|
1619
|
+
const rejectionWarning =
|
|
1620
|
+
'This error originated either by ' +
|
|
1621
|
+
'throwing inside of an async function without a catch block, ' +
|
|
1622
|
+
'or by rejecting a promise which was not handled with .catch().' +
|
|
1623
|
+
' The promise rejected with the reason:';
|
|
1624
|
+
|
|
1625
|
+
/* eslint-disable no-console */
|
|
1626
|
+
if (options.mode === 'warn') {
|
|
1627
|
+
consoleSandbox(() => {
|
|
1628
|
+
console.warn(rejectionWarning);
|
|
1629
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1630
|
+
console.error(reason && reason.stack ? reason.stack : reason);
|
|
1631
|
+
});
|
|
1632
|
+
} else if (options.mode === 'strict') {
|
|
1633
|
+
consoleSandbox(() => {
|
|
1634
|
+
console.warn(rejectionWarning);
|
|
1635
|
+
});
|
|
1636
|
+
logAndExitProcess(reason);
|
|
1637
|
+
}
|
|
1638
|
+
/* eslint-enable no-console */
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// This string is a placeholder that gets overwritten with the worker code.
|
|
1642
|
+
const base64WorkerScript = 'aW1wb3J0IHsgU2Vzc2lvbiB9IGZyb20gJ2luc3BlY3Rvcic7CmltcG9ydCB7IHdvcmtlckRhdGEsIHBhcmVudFBvcnQgfSBmcm9tICd3b3JrZXJfdGhyZWFkcyc7CmltcG9ydCB7IHBvc2l4LCBzZXAgfSBmcm9tICdwYXRoJzsKaW1wb3J0ICogYXMgaHR0cCQxIGZyb20gJ25vZGU6aHR0cCc7CmltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ25vZGU6aHR0cHMnOwppbXBvcnQgeyBSZWFkYWJsZSB9IGZyb20gJ3N0cmVhbSc7CmltcG9ydCB7IGNyZWF0ZUd6aXAgfSBmcm9tICd6bGliJzsKaW1wb3J0ICogYXMgbmV0IGZyb20gJ25ldCc7CmltcG9ydCAqIGFzIHRscyBmcm9tICd0bHMnOwppbXBvcnQgKiBhcyBodHRwIGZyb20gJ2h0dHAnOwoKLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC91bmJvdW5kLW1ldGhvZApjb25zdCBvYmplY3RUb1N0cmluZyA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUncyB0eXBlIGlzIG9uZSBvZiBhIGZldyBFcnJvciBvciBFcnJvci1saWtlCiAqIHtAbGluayBpc0Vycm9yfS4KICoKICogQHBhcmFtIHdhdCBBIHZhbHVlIHRvIGJlIGNoZWNrZWQuCiAqIEByZXR1cm5zIEEgYm9vbGVhbiByZXByZXNlbnRpbmcgdGhlIHJlc3VsdC4KICovCmZ1bmN0aW9uIGlzRXJyb3Iod2F0KSB7CiAgc3dpdGNoIChvYmplY3RUb1N0cmluZy5jYWxsKHdhdCkpIHsKICAgIGNhc2UgJ1tvYmplY3QgRXJyb3JdJzoKICAgIGNhc2UgJ1tvYmplY3QgRXhjZXB0aW9uXSc6CiAgICBjYXNlICdbb2JqZWN0IERPTUV4Y2VwdGlvbl0nOgogICAgICByZXR1cm4gdHJ1ZTsKICAgIGRlZmF1bHQ6CiAgICAgIHJldHVybiBpc0luc3RhbmNlT2Yod2F0LCBFcnJvcik7CiAgfQp9Ci8qKgogKiBDaGVja3Mgd2hldGhlciBnaXZlbiB2YWx1ZSBpcyBhbiBpbnN0YW5jZSBvZiB0aGUgZ2l2ZW4gYnVpbHQtaW4gY2xhc3MuCiAqCiAqIEBwYXJhbSB3YXQgVGhlIHZhbHVlIHRvIGJlIGNoZWNrZWQKICogQHBhcmFtIGNsYXNzTmFtZQogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc0J1aWx0aW4od2F0LCBjbGFzc05hbWUpIHsKICByZXR1cm4gb2JqZWN0VG9TdHJpbmcuY2FsbCh3YXQpID09PSBgW29iamVjdCAke2NsYXNzTmFtZX1dYDsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhIHN0cmluZwogKiB7QGxpbmsgaXNTdHJpbmd9LgogKgogKiBAcGFyYW0gd2F0IEEgdmFsdWUgdG8gYmUgY2hlY2tlZC4KICogQHJldHVybnMgQSBib29sZWFuIHJlcHJlc2VudGluZyB0aGUgcmVzdWx0LgogKi8KZnVuY3Rpb24gaXNTdHJpbmcod2F0KSB7CiAgcmV0dXJuIGlzQnVpbHRpbih3YXQsICdTdHJpbmcnKTsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhbiBvYmplY3QgbGl0ZXJhbCwgb3IgYSBjbGFzcyBpbnN0YW5jZS4KICoge0BsaW5rIGlzUGxhaW5PYmplY3R9LgogKgogKiBAcGFyYW0gd2F0IEEgdmFsdWUgdG8gYmUgY2hlY2tlZC4KICogQHJldHVybnMgQSBib29sZWFuIHJlcHJlc2VudGluZyB0aGUgcmVzdWx0LgogKi8KZnVuY3Rpb24gaXNQbGFpbk9iamVjdCh3YXQpIHsKICByZXR1cm4gaXNCdWlsdGluKHdhdCwgJ09iamVjdCcpOwp9CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUncyB0eXBlIGlzIGFuIEV2ZW50IGluc3RhbmNlCiAqIHtAbGluayBpc0V2ZW50fS4KICoKICogQHBhcmFtIHdhdCBBIHZhbHVlIHRvIGJlIGNoZWNrZWQuCiAqIEByZXR1cm5zIEEgYm9vbGVhbiByZXByZXNlbnRpbmcgdGhlIHJlc3VsdC4KICovCmZ1bmN0aW9uIGlzRXZlbnQod2F0KSB7CiAgcmV0dXJuIHR5cGVvZiBFdmVudCAhPT0gJ3VuZGVmaW5lZCcgJiYgaXNJbnN0YW5jZU9mKHdhdCwgRXZlbnQpOwp9CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUncyB0eXBlIGlzIGFuIEVsZW1lbnQgaW5zdGFuY2UKICoge0BsaW5rIGlzRWxlbWVudH0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc0VsZW1lbnQod2F0KSB7CiAgcmV0dXJuIHR5cGVvZiBFbGVtZW50ICE9PSAndW5kZWZpbmVkJyAmJiBpc0luc3RhbmNlT2Yod2F0LCBFbGVtZW50KTsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlIGhhcyBhIHRoZW4gZnVuY3Rpb24uCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKi8KZnVuY3Rpb24gaXNUaGVuYWJsZSh3YXQpIHsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVuc2FmZS1tZW1iZXItYWNjZXNzCiAgcmV0dXJuIEJvb2xlYW4od2F0ICYmIHdhdC50aGVuICYmIHR5cGVvZiB3YXQudGhlbiA9PT0gJ2Z1bmN0aW9uJyk7Cn0KCi8qKgogKiBDaGVja3Mgd2hldGhlciBnaXZlbiB2YWx1ZSdzIHR5cGUgaXMgYSBTeW50aGV0aWNFdmVudAogKiB7QGxpbmsgaXNTeW50aGV0aWNFdmVudH0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc1N5bnRoZXRpY0V2ZW50KHdhdCkgewogIHJldHVybiBpc1BsYWluT2JqZWN0KHdhdCkgJiYgJ25hdGl2ZUV2ZW50JyBpbiB3YXQgJiYgJ3ByZXZlbnREZWZhdWx0JyBpbiB3YXQgJiYgJ3N0b3BQcm9wYWdhdGlvbicgaW4gd2F0Owp9CgovKioKICogQ2hlY2tzIHdoZXRoZXIgZ2l2ZW4gdmFsdWUgaXMgTmFOCiAqIHtAbGluayBpc05hTn0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc05hTiQxKHdhdCkgewogIHJldHVybiB0eXBlb2Ygd2F0ID09PSAnbnVtYmVyJyAmJiB3YXQgIT09IHdhdDsKfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhbiBpbnN0YW5jZSBvZiBwcm92aWRlZCBjb25zdHJ1Y3Rvci4KICoge0BsaW5rIGlzSW5zdGFuY2VPZn0uCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcGFyYW0gYmFzZSBBIGNvbnN0cnVjdG9yIHRvIGJlIHVzZWQgaW4gYSBjaGVjay4KICogQHJldHVybnMgQSBib29sZWFuIHJlcHJlc2VudGluZyB0aGUgcmVzdWx0LgogKi8KZnVuY3Rpb24gaXNJbnN0YW5jZU9mKHdhdCwgYmFzZSkgewogIHRyeSB7CiAgICByZXR1cm4gd2F0IGluc3RhbmNlb2YgYmFzZTsKICB9IGNhdGNoIChfZSkgewogICAgcmV0dXJuIGZhbHNlOwogIH0KfQoKLyoqCiAqIENoZWNrcyB3aGV0aGVyIGdpdmVuIHZhbHVlJ3MgdHlwZSBpcyBhIFZ1ZSBWaWV3TW9kZWwuCiAqCiAqIEBwYXJhbSB3YXQgQSB2YWx1ZSB0byBiZSBjaGVja2VkLgogKiBAcmV0dXJucyBBIGJvb2xlYW4gcmVwcmVzZW50aW5nIHRoZSByZXN1bHQuCiAqLwpmdW5jdGlvbiBpc1Z1ZVZpZXdNb2RlbCh3YXQpIHsKICAvLyBOb3QgdXNpbmcgT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZyBiZWNhdXNlIGluIFZ1ZSAzIGl0IHdvdWxkIHJlYWQgdGhlIGluc3RhbmNlJ3MgU3ltYm9sKFN5bWJvbC50b1N0cmluZ1RhZykgcHJvcGVydHkuCiAgcmV0dXJuICEhKHR5cGVvZiB3YXQgPT09ICdvYmplY3QnICYmIHdhdCAhPT0gbnVsbCAmJiAoKHdhdCApLl9faXNWdWUgfHwgKHdhdCApLl9pc1Z1ZSkpOwp9CgovKiogSW50ZXJuYWwgZ2xvYmFsIHdpdGggY29tbW9uIHByb3BlcnRpZXMgYW5kIFNlbnRyeSBleHRlbnNpb25zICAqLwoKLy8gVGhlIGNvZGUgYmVsb3cgZm9yICdpc0dsb2JhbE9iaicgYW5kICdHTE9CQUxfT0JKJyB3YXMgY29waWVkIGZyb20gY29yZS1qcyBiZWZvcmUgbW9kaWZpY2F0aW9uCi8vIGh0dHBzOi8vZ2l0aHViLmNvbS96bG9pcm9jay9jb3JlLWpzL2Jsb2IvMWI5NDRkZjU1MjgyY2RjOTljOTBkYjVmNDllYjBiNmVkYTJjYzBhMy9wYWNrYWdlcy9jb3JlLWpzL2ludGVybmFscy9nbG9iYWwuanMKLy8gY29yZS1qcyBoYXMgdGhlIGZvbGxvd2luZyBsaWNlbmNlOgovLwovLyBDb3B5cmlnaHQgKGMpIDIwMTQtMjAyMiBEZW5pcyBQdXNoa2FyZXYKLy8KLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGEgY29weQovLyBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSAiU29mdHdhcmUiKSwgdG8gZGVhbAovLyBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzCi8vIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwKLy8gY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzCi8vIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6Ci8vCi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluCi8vIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLgovLwovLyBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgovLyBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKLy8gRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCi8vIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKLy8gTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKLy8gT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTgovLyBUSEUgU09GVFdBUkUuCgovKiogUmV0dXJucyAnb2JqJyBpZiBpdCdzIHRoZSBnbG9iYWwgb2JqZWN0LCBvdGhlcndpc2UgcmV0dXJucyB1bmRlZmluZWQgKi8KZnVuY3Rpb24gaXNHbG9iYWxPYmoob2JqKSB7CiAgcmV0dXJuIG9iaiAmJiBvYmouTWF0aCA9PSBNYXRoID8gb2JqIDogdW5kZWZpbmVkOwp9CgovKiogR2V0J3MgdGhlIGdsb2JhbCBvYmplY3QgZm9yIHRoZSBjdXJyZW50IEphdmFTY3JpcHQgcnVudGltZSAqLwpjb25zdCBHTE9CQUxfT0JKID0KICAodHlwZW9mIGdsb2JhbFRoaXMgPT0gJ29iamVjdCcgJiYgaXNHbG9iYWxPYmooZ2xvYmFsVGhpcykpIHx8CiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXJlc3RyaWN0ZWQtZ2xvYmFscwogICh0eXBlb2Ygd2luZG93ID09ICdvYmplY3QnICYmIGlzR2xvYmFsT2JqKHdpbmRvdykpIHx8CiAgKHR5cGVvZiBzZWxmID09ICdvYmplY3QnICYmIGlzR2xvYmFsT2JqKHNlbGYpKSB8fAogICh0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGlzR2xvYmFsT2JqKGdsb2JhbCkpIHx8CiAgKGZ1bmN0aW9uICgpIHsKICAgIHJldHVybiB0aGlzOwogIH0pKCkgfHwKICB7fTsKCi8qKgogKiBAZGVwcmVjYXRlZCBVc2UgR0xPQkFMX09CSiBpbnN0ZWFkIG9yIFdJTkRPVyBmcm9tIEBzZW50cnkvYnJvd3Nlci4gVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjgKICovCmZ1bmN0aW9uIGdldEdsb2JhbE9iamVjdCgpIHsKICByZXR1cm4gR0xPQkFMX09CSiA7Cn0KCi8qKgogKiBSZXR1cm5zIGEgZ2xvYmFsIHNpbmdsZXRvbiBjb250YWluZWQgaW4gdGhlIGdsb2JhbCBgX19TRU5UUllfX2Agb2JqZWN0LgogKgogKiBJZiB0aGUgc2luZ2xldG9uIGRvZXNuJ3QgYWxyZWFkeSBleGlzdCBpbiBgX19TRU5UUllfX2AsIGl0IHdpbGwgYmUgY3JlYXRlZCB1c2luZyB0aGUgZ2l2ZW4gZmFjdG9yeQogKiBmdW5jdGlvbiBhbmQgYWRkZWQgdG8gdGhlIGBfX1NFTlRSWV9fYCBvYmplY3QuCiAqCiAqIEBwYXJhbSBuYW1lIG5hbWUgb2YgdGhlIGdsb2JhbCBzaW5nbGV0b24gb24gX19TRU5UUllfXwogKiBAcGFyYW0gY3JlYXRvciBjcmVhdG9yIEZhY3RvcnkgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSBzaW5nbGV0b24gaWYgaXQgZG9lc24ndCBhbHJlYWR5IGV4aXN0IG9uIGBfX1NFTlRSWV9fYAogKiBAcGFyYW0gb2JqIChPcHRpb25hbCkgVGhlIGdsb2JhbCBvYmplY3Qgb24gd2hpY2ggdG8gbG9vayBmb3IgYF9fU0VOVFJZX19gLCBpZiBub3QgYEdMT0JBTF9PQkpgJ3MgcmV0dXJuIHZhbHVlCiAqIEByZXR1cm5zIHRoZSBzaW5nbGV0b24KICovCmZ1bmN0aW9uIGdldEdsb2JhbFNpbmdsZXRvbihuYW1lLCBjcmVhdG9yLCBvYmopIHsKICBjb25zdCBnYmwgPSAob2JqIHx8IEdMT0JBTF9PQkopIDsKICBjb25zdCBfX1NFTlRSWV9fID0gKGdibC5fX1NFTlRSWV9fID0gZ2JsLl9fU0VOVFJZX18gfHwge30pOwogIGNvbnN0IHNpbmdsZXRvbiA9IF9fU0VOVFJZX19bbmFtZV0gfHwgKF9fU0VOVFJZX19bbmFtZV0gPSBjcmVhdG9yKCkpOwogIHJldHVybiBzaW5nbGV0b247Cn0KCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgpjb25zdCBXSU5ET1ckNSA9IGdldEdsb2JhbE9iamVjdCgpOwoKY29uc3QgREVGQVVMVF9NQVhfU1RSSU5HX0xFTkdUSCA9IDgwOwoKLyoqCiAqIEdpdmVuIGEgY2hpbGQgRE9NIGVsZW1lbnQsIHJldHVybnMgYSBxdWVyeS1zZWxlY3RvciBzdGF0ZW1lbnQgZGVzY3JpYmluZyB0aGF0CiAqIGFuZCBpdHMgYW5jZXN0b3JzCiAqIGUuZy4gW0hUTUxFbGVtZW50XSA9PiBib2R5ID4gZGl2ID4gaW5wdXQjZm9vLmJ0bltuYW1lPWJhel0KICogQHJldHVybnMgZ2VuZXJhdGVkIERPTSBwYXRoCiAqLwpmdW5jdGlvbiBodG1sVHJlZUFzU3RyaW5nKAogIGVsZW0sCiAgb3B0aW9ucyA9IHt9LAopIHsKICBpZiAoIWVsZW0pIHsKICAgIHJldHVybiAnPHVua25vd24+JzsKICB9CgogIC8vIHRyeS9jYXRjaCBib3RoOgogIC8vIC0gYWNjZXNzaW5nIGV2ZW50LnRhcmdldCAoc2VlIGdldHNlbnRyeS9yYXZlbi1qcyM4MzgsICM3NjgpCiAgLy8gLSBgaHRtbFRyZWVBc1N0cmluZ2AgYmVjYXVzZSBpdCdzIGNvbXBsZXgsIGFuZCBqdXN0IGFjY2Vzc2luZyB0aGUgRE9NIGluY29ycmVjdGx5CiAgLy8gLSBjYW4gdGhyb3cgYW4gZXhjZXB0aW9uIGluIHNvbWUgY2lyY3Vtc3RhbmNlcy4KICB0cnkgewogICAgbGV0IGN1cnJlbnRFbGVtID0gZWxlbSA7CiAgICBjb25zdCBNQVhfVFJBVkVSU0VfSEVJR0hUID0gNTsKICAgIGNvbnN0IG91dCA9IFtdOwogICAgbGV0IGhlaWdodCA9IDA7CiAgICBsZXQgbGVuID0gMDsKICAgIGNvbnN0IHNlcGFyYXRvciA9ICcgPiAnOwogICAgY29uc3Qgc2VwTGVuZ3RoID0gc2VwYXJhdG9yLmxlbmd0aDsKICAgIGxldCBuZXh0U3RyOwogICAgY29uc3Qga2V5QXR0cnMgPSBBcnJheS5pc0FycmF5KG9wdGlvbnMpID8gb3B0aW9ucyA6IG9wdGlvbnMua2V5QXR0cnM7CiAgICBjb25zdCBtYXhTdHJpbmdMZW5ndGggPSAoIUFycmF5LmlzQXJyYXkob3B0aW9ucykgJiYgb3B0aW9ucy5tYXhTdHJpbmdMZW5ndGgpIHx8IERFRkFVTFRfTUFYX1NUUklOR19MRU5HVEg7CgogICAgd2hpbGUgKGN1cnJlbnRFbGVtICYmIGhlaWdodCsrIDwgTUFYX1RSQVZFUlNFX0hFSUdIVCkgewogICAgICBuZXh0U3RyID0gX2h0bWxFbGVtZW50QXNTdHJpbmcoY3VycmVudEVsZW0sIGtleUF0dHJzKTsKICAgICAgLy8gYmFpbCBvdXQgaWYKICAgICAgLy8gLSBuZXh0U3RyIGlzIHRoZSAnaHRtbCcgZWxlbWVudAogICAgICAvLyAtIHRoZSBsZW5ndGggb2YgdGhlIHN0cmluZyB0aGF0IHdvdWxkIGJlIGNyZWF0ZWQgZXhjZWVkcyBtYXhTdHJpbmdMZW5ndGgKICAgICAgLy8gICAoaWdub3JlIHRoaXMgbGltaXQgaWYgd2UgYXJlIG9uIHRoZSBmaXJzdCBpdGVyYXRpb24pCiAgICAgIGlmIChuZXh0U3RyID09PSAnaHRtbCcgfHwgKGhlaWdodCA+IDEgJiYgbGVuICsgb3V0Lmxlbmd0aCAqIHNlcExlbmd0aCArIG5leHRTdHIubGVuZ3RoID49IG1heFN0cmluZ0xlbmd0aCkpIHsKICAgICAgICBicmVhazsKICAgICAgfQoKICAgICAgb3V0LnB1c2gobmV4dFN0cik7CgogICAgICBsZW4gKz0gbmV4dFN0ci5sZW5ndGg7CiAgICAgIGN1cnJlbnRFbGVtID0gY3VycmVudEVsZW0ucGFyZW50Tm9kZTsKICAgIH0KCiAgICByZXR1cm4gb3V0LnJldmVyc2UoKS5qb2luKHNlcGFyYXRvcik7CiAgfSBjYXRjaCAoX29PKSB7CiAgICByZXR1cm4gJzx1bmtub3duPic7CiAgfQp9CgovKioKICogUmV0dXJucyBhIHNpbXBsZSwgcXVlcnktc2VsZWN0b3IgcmVwcmVzZW50YXRpb24gb2YgYSBET00gZWxlbWVudAogKiBlLmcuIFtIVE1MRWxlbWVudF0gPT4gaW5wdXQjZm9vLmJ0bltuYW1lPWJhel0KICogQHJldHVybnMgZ2VuZXJhdGVkIERPTSBwYXRoCiAqLwpmdW5jdGlvbiBfaHRtbEVsZW1lbnRBc1N0cmluZyhlbCwga2V5QXR0cnMpIHsKICBjb25zdCBlbGVtID0gZWwKCjsKCiAgY29uc3Qgb3V0ID0gW107CiAgbGV0IGNsYXNzTmFtZTsKICBsZXQgY2xhc3NlczsKICBsZXQga2V5OwogIGxldCBhdHRyOwogIGxldCBpOwoKICBpZiAoIWVsZW0gfHwgIWVsZW0udGFnTmFtZSkgewogICAgcmV0dXJuICcnOwogIH0KCiAgLy8gQHRzLWV4cGVjdC1lcnJvciBXSU5ET1cgaGFzIEhUTUxFbGVtZW50CiAgaWYgKFdJTkRPVyQ1LkhUTUxFbGVtZW50KSB7CiAgICAvLyBJZiB1c2luZyB0aGUgY29tcG9uZW50IG5hbWUgYW5ub3RhdGlvbiBwbHVnaW4sIHRoaXMgdmFsdWUgbWF5IGJlIGF2YWlsYWJsZSBvbiB0aGUgRE9NIG5vZGUKICAgIGlmIChlbGVtIGluc3RhbmNlb2YgSFRNTEVsZW1lbnQgJiYgZWxlbS5kYXRhc2V0ICYmIGVsZW0uZGF0YXNldFsnc2VudHJ5Q29tcG9uZW50J10pIHsKICAgICAgcmV0dXJuIGVsZW0uZGF0YXNldFsnc2VudHJ5Q29tcG9uZW50J107CiAgICB9CiAgfQoKICBvdXQucHVzaChlbGVtLnRhZ05hbWUudG9Mb3dlckNhc2UoKSk7CgogIC8vIFBhaXJzIG9mIGF0dHJpYnV0ZSBrZXlzIGRlZmluZWQgaW4gYHNlcmlhbGl6ZUF0dHJpYnV0ZWAgYW5kIHRoZWlyIHZhbHVlcyBvbiBlbGVtZW50LgogIGNvbnN0IGtleUF0dHJQYWlycyA9CiAgICBrZXlBdHRycyAmJiBrZXlBdHRycy5sZW5ndGgKICAgICAgPyBrZXlBdHRycy5maWx0ZXIoa2V5QXR0ciA9PiBlbGVtLmdldEF0dHJpYnV0ZShrZXlBdHRyKSkubWFwKGtleUF0dHIgPT4gW2tleUF0dHIsIGVsZW0uZ2V0QXR0cmlidXRlKGtleUF0dHIpXSkKICAgICAgOiBudWxsOwoKICBpZiAoa2V5QXR0clBhaXJzICYmIGtleUF0dHJQYWlycy5sZW5ndGgpIHsKICAgIGtleUF0dHJQYWlycy5mb3JFYWNoKGtleUF0dHJQYWlyID0+IHsKICAgICAgb3V0LnB1c2goYFske2tleUF0dHJQYWlyWzBdfT0iJHtrZXlBdHRyUGFpclsxXX0iXWApOwogICAgfSk7CiAgfSBlbHNlIHsKICAgIGlmIChlbGVtLmlkKSB7CiAgICAgIG91dC5wdXNoKGAjJHtlbGVtLmlkfWApOwogICAgfQoKICAgIGNsYXNzTmFtZSA9IGVsZW0uY2xhc3NOYW1lOwogICAgaWYgKGNsYXNzTmFtZSAmJiBpc1N0cmluZyhjbGFzc05hbWUpKSB7CiAgICAgIGNsYXNzZXMgPSBjbGFzc05hbWUuc3BsaXQoL1xzKy8pOwogICAgICBmb3IgKGkgPSAwOyBpIDwgY2xhc3Nlcy5sZW5ndGg7IGkrKykgewogICAgICAgIG91dC5wdXNoKGAuJHtjbGFzc2VzW2ldfWApOwogICAgICB9CiAgICB9CiAgfQogIGNvbnN0IGFsbG93ZWRBdHRycyA9IFsnYXJpYS1sYWJlbCcsICd0eXBlJywgJ25hbWUnLCAndGl0bGUnLCAnYWx0J107CiAgZm9yIChpID0gMDsgaSA8IGFsbG93ZWRBdHRycy5sZW5ndGg7IGkrKykgewogICAga2V5ID0gYWxsb3dlZEF0dHJzW2ldOwogICAgYXR0ciA9IGVsZW0uZ2V0QXR0cmlidXRlKGtleSk7CiAgICBpZiAoYXR0cikgewogICAgICBvdXQucHVzaChgWyR7a2V5fT0iJHthdHRyfSJdYCk7CiAgICB9CiAgfQogIHJldHVybiBvdXQuam9pbignJyk7Cn0KCi8qKgogKiBUaGlzIHNlcnZlcyBhcyBhIGJ1aWxkIHRpbWUgZmxhZyB0aGF0IHdpbGwgYmUgdHJ1ZSBieSBkZWZhdWx0LCBidXQgZmFsc2UgaW4gbm9uLWRlYnVnIGJ1aWxkcyBvciBpZiB1c2VycyByZXBsYWNlIGBfX1NFTlRSWV9ERUJVR19fYCBpbiB0aGVpciBnZW5lcmF0ZWQgY29kZS4KICoKICogQVRURU5USU9OOiBUaGlzIGNvbnN0YW50IG11c3QgbmV2ZXIgY3Jvc3MgcGFja2FnZSBib3VuZGFyaWVzIChpLmUuIGJlIGV4cG9ydGVkKSB0byBndWFyYW50ZWUgdGhhdCBpdCBjYW4gYmUgdXNlZCBmb3IgdHJlZSBzaGFraW5nLgogKi8KY29uc3QgREVCVUdfQlVJTEQkMSA9ICh0eXBlb2YgX19TRU5UUllfREVCVUdfXyA9PT0gJ3VuZGVmaW5lZCcgfHwgX19TRU5UUllfREVCVUdfXyk7CgovKiogUHJlZml4IGZvciBsb2dnaW5nIHN0cmluZ3MgKi8KY29uc3QgUFJFRklYID0gJ1NlbnRyeSBMb2dnZXIgJzsKCmNvbnN0IENPTlNPTEVfTEVWRUxTID0gWwogICdkZWJ1ZycsCiAgJ2luZm8nLAogICd3YXJuJywKICAnZXJyb3InLAogICdsb2cnLAogICdhc3NlcnQnLAogICd0cmFjZScsCl0gOwoKLyoqIFRoaXMgbWF5IGJlIG11dGF0ZWQgYnkgdGhlIGNvbnNvbGUgaW5zdHJ1bWVudGF0aW9uLiAqLwpjb25zdCBvcmlnaW5hbENvbnNvbGVNZXRob2RzCgogPSB7fTsKCi8qKiBKU0RvYyAqLwoKLyoqCiAqIFRlbXBvcmFyaWx5IGRpc2FibGUgc2VudHJ5IGNvbnNvbGUgaW5zdHJ1bWVudGF0aW9ucy4KICoKICogQHBhcmFtIGNhbGxiYWNrIFRoZSBmdW5jdGlvbiB0byBydW4gYWdhaW5zdCB0aGUgb3JpZ2luYWwgYGNvbnNvbGVgIG1lc3NhZ2VzCiAqIEByZXR1cm5zIFRoZSByZXN1bHRzIG9mIHRoZSBjYWxsYmFjawogKi8KZnVuY3Rpb24gY29uc29sZVNhbmRib3goY2FsbGJhY2spIHsKICBpZiAoISgnY29uc29sZScgaW4gR0xPQkFMX09CSikpIHsKICAgIHJldHVybiBjYWxsYmFjaygpOwogIH0KCiAgY29uc3QgY29uc29sZSA9IEdMT0JBTF9PQkouY29uc29sZSA7CiAgY29uc3Qgd3JhcHBlZEZ1bmNzID0ge307CgogIGNvbnN0IHdyYXBwZWRMZXZlbHMgPSBPYmplY3Qua2V5cyhvcmlnaW5hbENvbnNvbGVNZXRob2RzKSA7CgogIC8vIFJlc3RvcmUgYWxsIHdyYXBwZWQgY29uc29sZSBtZXRob2RzCiAgd3JhcHBlZExldmVscy5mb3JFYWNoKGxldmVsID0+IHsKICAgIGNvbnN0IG9yaWdpbmFsQ29uc29sZU1ldGhvZCA9IG9yaWdpbmFsQ29uc29sZU1ldGhvZHNbbGV2ZWxdIDsKICAgIHdyYXBwZWRGdW5jc1tsZXZlbF0gPSBjb25zb2xlW2xldmVsXSA7CiAgICBjb25zb2xlW2xldmVsXSA9IG9yaWdpbmFsQ29uc29sZU1ldGhvZDsKICB9KTsKCiAgdHJ5IHsKICAgIHJldHVybiBjYWxsYmFjaygpOwogIH0gZmluYWxseSB7CiAgICAvLyBSZXZlcnQgcmVzdG9yYXRpb24gdG8gd3JhcHBlZCBzdGF0ZQogICAgd3JhcHBlZExldmVscy5mb3JFYWNoKGxldmVsID0+IHsKICAgICAgY29uc29sZVtsZXZlbF0gPSB3cmFwcGVkRnVuY3NbbGV2ZWxdIDsKICAgIH0pOwogIH0KfQoKZnVuY3Rpb24gbWFrZUxvZ2dlcigpIHsKICBsZXQgZW5hYmxlZCA9IGZhbHNlOwogIGNvbnN0IGxvZ2dlciA9IHsKICAgIGVuYWJsZTogKCkgPT4gewogICAgICBlbmFibGVkID0gdHJ1ZTsKICAgIH0sCiAgICBkaXNhYmxlOiAoKSA9PiB7CiAgICAgIGVuYWJsZWQgPSBmYWxzZTsKICAgIH0sCiAgICBpc0VuYWJsZWQ6ICgpID0+IGVuYWJsZWQsCiAgfTsKCiAgaWYgKERFQlVHX0JVSUxEJDEpIHsKICAgIENPTlNPTEVfTEVWRUxTLmZvckVhY2gobmFtZSA9PiB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55CiAgICAgIGxvZ2dlcltuYW1lXSA9ICguLi5hcmdzKSA9PiB7CiAgICAgICAgaWYgKGVuYWJsZWQpIHsKICAgICAgICAgIGNvbnNvbGVTYW5kYm94KCgpID0+IHsKICAgICAgICAgICAgR0xPQkFMX09CSi5jb25zb2xlW25hbWVdKGAke1BSRUZJWH1bJHtuYW1lfV06YCwgLi4uYXJncyk7CiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgIH07CiAgICB9KTsKICB9IGVsc2UgewogICAgQ09OU09MRV9MRVZFTFMuZm9yRWFjaChuYW1lID0+IHsKICAgICAgbG9nZ2VyW25hbWVdID0gKCkgPT4gdW5kZWZpbmVkOwogICAgfSk7CiAgfQoKICByZXR1cm4gbG9nZ2VyIDsKfQoKY29uc3QgbG9nZ2VyID0gbWFrZUxvZ2dlcigpOwoKLyoqCiAqIFJlbmRlcnMgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIERzbi4KICoKICogQnkgZGVmYXVsdCwgdGhpcyB3aWxsIHJlbmRlciB0aGUgcHVibGljIHJlcHJlc2VudGF0aW9uIHdpdGhvdXQgdGhlIHBhc3N3b3JkCiAqIGNvbXBvbmVudC4gVG8gZ2V0IHRoZSBkZXByZWNhdGVkIHByaXZhdGUgcmVwcmVzZW50YXRpb24sIHNldCBgd2l0aFBhc3N3b3JkYAogKiB0byB0cnVlLgogKgogKiBAcGFyYW0gd2l0aFBhc3N3b3JkIFdoZW4gc2V0IHRvIHRydWUsIHRoZSBwYXNzd29yZCB3aWxsIGJlIGluY2x1ZGVkLgogKi8KZnVuY3Rpb24gZHNuVG9TdHJpbmcoZHNuLCB3aXRoUGFzc3dvcmQgPSBmYWxzZSkgewogIGNvbnN0IHsgaG9zdCwgcGF0aCwgcGFzcywgcG9ydCwgcHJvamVjdElkLCBwcm90b2NvbCwgcHVibGljS2V5IH0gPSBkc247CiAgcmV0dXJuICgKICAgIGAke3Byb3RvY29sfTovLyR7cHVibGljS2V5fSR7d2l0aFBhc3N3b3JkICYmIHBhc3MgPyBgOiR7cGFzc31gIDogJyd9YCArCiAgICBgQCR7aG9zdH0ke3BvcnQgPyBgOiR7cG9ydH1gIDogJyd9LyR7cGF0aCA/IGAke3BhdGh9L2AgOiBwYXRofSR7cHJvamVjdElkfWAKICApOwp9CgovKiogQW4gZXJyb3IgZW1pdHRlZCBieSBTZW50cnkgU0RLcyBhbmQgcmVsYXRlZCB1dGlsaXRpZXMuICovCmNsYXNzIFNlbnRyeUVycm9yIGV4dGVuZHMgRXJyb3IgewogIC8qKiBEaXNwbGF5IG5hbWUgb2YgdGhpcyBlcnJvciBpbnN0YW5jZS4gKi8KCiAgIGNvbnN0cnVjdG9yKCBtZXNzYWdlLCBsb2dMZXZlbCA9ICd3YXJuJykgewogICAgc3VwZXIobWVzc2FnZSk7dGhpcy5tZXNzYWdlID0gbWVzc2FnZTsKICAgIHRoaXMubmFtZSA9IG5ldy50YXJnZXQucHJvdG90eXBlLmNvbnN0cnVjdG9yLm5hbWU7CiAgICAvLyBUaGlzIHNldHMgdGhlIHByb3RvdHlwZSB0byBiZSBgRXJyb3JgLCBub3QgYFNlbnRyeUVycm9yYC4gSXQncyB1bmNsZWFyIHdoeSB3ZSBkbyB0aGlzLCBidXQgY29tbWVudGluZyB0aGlzIGxpbmUKICAgIC8vIG91dCBjYXVzZXMgdmFyaW91cyAoc2VlbWluZ2x5IHRvdGFsbHkgdW5yZWxhdGVkKSBwbGF5d3JpZ2h0IHRlc3RzIGNvbnNpc3RlbnRseSB0aW1lIG91dC4gRllJLCB0aGlzIG1ha2VzCiAgICAvLyBpbnN0YW5jZXMgb2YgYFNlbnRyeUVycm9yYCBmYWlsIGBvYmogaW5zdGFuY2VvZiBTZW50cnlFcnJvcmAgY2hlY2tzLgogICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIG5ldy50YXJnZXQucHJvdG90eXBlKTsKICAgIHRoaXMubG9nTGV2ZWwgPSBsb2dMZXZlbDsKICB9Cn0KCmNvbnN0IFNUQUNLVFJBQ0VfRlJBTUVfTElNSVQgPSA1MDsKY29uc3QgVU5LTk9XTl9GVU5DVElPTiA9ICc/JzsKY29uc3QgU1RSSVBfRlJBTUVfUkVHRVhQID0gL2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vOwoKLyoqCiAqIFJlbW92ZXMgU2VudHJ5IGZyYW1lcyBmcm9tIHRoZSB0b3AgYW5kIGJvdHRvbSBvZiB0aGUgc3RhY2sgaWYgcHJlc2VudCBhbmQgZW5mb3JjZXMgYSBsaW1pdCBvZiBtYXggbnVtYmVyIG9mIGZyYW1lcy4KICogQXNzdW1lcyBzdGFjayBpbnB1dCBpcyBvcmRlcmVkIGZyb20gdG9wIHRvIGJvdHRvbSBhbmQgcmV0dXJucyB0aGUgcmV2ZXJzZSByZXByZXNlbnRhdGlvbiBzbyBjYWxsIHNpdGUgb2YgdGhlCiAqIGZ1bmN0aW9uIHRoYXQgY2F1c2VkIHRoZSBjcmFzaCBpcyB0aGUgbGFzdCBmcmFtZSBpbiB0aGUgYXJyYXkuCiAqIEBoaWRkZW4KICovCmZ1bmN0aW9uIHN0cmlwU2VudHJ5RnJhbWVzQW5kUmV2ZXJzZShzdGFjaykgewogIGlmICghc3RhY2subGVuZ3RoKSB7CiAgICByZXR1cm4gW107CiAgfQoKICBjb25zdCBsb2NhbFN0YWNrID0gQXJyYXkuZnJvbShzdGFjayk7CgogIC8vIElmIHN0YWNrIHN0YXJ0cyB3aXRoIG9uZSBvZiBvdXIgQVBJIGNhbGxzLCByZW1vdmUgaXQgKHN0YXJ0cywgbWVhbmluZyBpdCdzIHRoZSB0b3Agb2YgdGhlIHN0YWNrIC0gYWthIGxhc3QgY2FsbCkKICBpZiAoL3NlbnRyeVdyYXBwZWQvLnRlc3QobG9jYWxTdGFja1tsb2NhbFN0YWNrLmxlbmd0aCAtIDFdLmZ1bmN0aW9uIHx8ICcnKSkgewogICAgbG9jYWxTdGFjay5wb3AoKTsKICB9CgogIC8vIFJldmVyc2luZyBpbiB0aGUgbWlkZGxlIG9mIHRoZSBwcm9jZWR1cmUgYWxsb3dzIHVzIHRvIGp1c3QgcG9wIHRoZSB2YWx1ZXMgb2ZmIHRoZSBzdGFjawogIGxvY2FsU3RhY2sucmV2ZXJzZSgpOwoKICAvLyBJZiBzdGFjayBlbmRzIHdpdGggb25lIG9mIG91ciBpbnRlcm5hbCBBUEkgY2FsbHMsIHJlbW92ZSBpdCAoZW5kcywgbWVhbmluZyBpdCdzIHRoZSBib3R0b20gb2YgdGhlIHN0YWNrIC0gYWthIHRvcC1tb3N0IGNhbGwpCiAgaWYgKFNUUklQX0ZSQU1FX1JFR0VYUC50ZXN0KGxvY2FsU3RhY2tbbG9jYWxTdGFjay5sZW5ndGggLSAxXS5mdW5jdGlvbiB8fCAnJykpIHsKICAgIGxvY2FsU3RhY2sucG9wKCk7CgogICAgLy8gV2hlbiB1c2luZyBzeW50aGV0aWMgZXZlbnRzLCB3ZSB3aWxsIGhhdmUgYSAyIGxldmVscyBkZWVwIHN0YWNrLCBhcyBgbmV3IEVycm9yKCdTZW50cnkgc3ludGhldGljRXhjZXB0aW9uJylgCiAgICAvLyBpcyBwcm9kdWNlZCB3aXRoaW4gdGhlIGh1YiBpdHNlbGYsIG1ha2luZyBpdDoKICAgIC8vCiAgICAvLyAgIFNlbnRyeS5jYXB0dXJlRXhjZXB0aW9uKCkKICAgIC8vICAgZ2V0Q3VycmVudEh1YigpLmNhcHR1cmVFeGNlcHRpb24oKQogICAgLy8KICAgIC8vIGluc3RlYWQgb2YganVzdCB0aGUgdG9wIGBTZW50cnlgIGNhbGwgaXRzZWxmLgogICAgLy8gVGhpcyBmb3JjZXMgdXMgdG8gcG9zc2libHkgc3RyaXAgYW4gYWRkaXRpb25hbCBmcmFtZSBpbiB0aGUgZXhhY3Qgc2FtZSB3YXMgYXMgYWJvdmUuCiAgICBpZiAoU1RSSVBfRlJBTUVfUkVHRVhQLnRlc3QobG9jYWxTdGFja1tsb2NhbFN0YWNrLmxlbmd0aCAtIDFdLmZ1bmN0aW9uIHx8ICcnKSkgewogICAgICBsb2NhbFN0YWNrLnBvcCgpOwogICAgfQogIH0KCiAgcmV0dXJuIGxvY2FsU3RhY2suc2xpY2UoMCwgU1RBQ0tUUkFDRV9GUkFNRV9MSU1JVCkubWFwKGZyYW1lID0+ICh7CiAgICAuLi5mcmFtZSwKICAgIGZpbGVuYW1lOiBmcmFtZS5maWxlbmFtZSB8fCBsb2NhbFN0YWNrW2xvY2FsU3RhY2subGVuZ3RoIC0gMV0uZmlsZW5hbWUsCiAgICBmdW5jdGlvbjogZnJhbWUuZnVuY3Rpb24gfHwgVU5LTk9XTl9GVU5DVElPTiwKICB9KSk7Cn0KCmNvbnN0IGRlZmF1bHRGdW5jdGlvbk5hbWUgPSAnPGFub255bW91cz4nOwoKLyoqCiAqIFNhZmVseSBleHRyYWN0IGZ1bmN0aW9uIG5hbWUgZnJvbSBpdHNlbGYKICovCmZ1bmN0aW9uIGdldEZ1bmN0aW9uTmFtZShmbikgewogIHRyeSB7CiAgICBpZiAoIWZuIHx8IHR5cGVvZiBmbiAhPT0gJ2Z1bmN0aW9uJykgewogICAgICByZXR1cm4gZGVmYXVsdEZ1bmN0aW9uTmFtZTsKICAgIH0KICAgIHJldHVybiBmbi5uYW1lIHx8IGRlZmF1bHRGdW5jdGlvbk5hbWU7CiAgfSBjYXRjaCAoZSkgewogICAgLy8gSnVzdCBhY2Nlc3NpbmcgY3VzdG9tIHByb3BzIGluIHNvbWUgU2VsZW5pdW0gZW52aXJvbm1lbnRzCiAgICAvLyBjYW4gY2F1c2UgYSAiUGVybWlzc2lvbiBkZW5pZWQiIGV4Y2VwdGlvbiAoc2VlIHJhdmVuLWpzIzQ5NSkuCiAgICByZXR1cm4gZGVmYXVsdEZ1bmN0aW9uTmFtZTsKICB9Cn0KCi8qKgogKiBUaGlzIGV4dHJhY3RzIHRoZSBvcmlnaW5hbCBmdW5jdGlvbiBpZiBhdmFpbGFibGUuICBTZWUKICogYG1hcmtGdW5jdGlvbldyYXBwZWRgIGZvciBtb3JlIGluZm9ybWF0aW9uLgogKgogKiBAcGFyYW0gZnVuYyB0aGUgZnVuY3Rpb24gdG8gdW53cmFwCiAqIEByZXR1cm5zIHRoZSB1bndyYXBwZWQgdmVyc2lvbiBvZiB0aGUgZnVuY3Rpb24gaWYgYXZhaWxhYmxlLgogKi8KZnVuY3Rpb24gZ2V0T3JpZ2luYWxGdW5jdGlvbihmdW5jKSB7CiAgcmV0dXJuIGZ1bmMuX19zZW50cnlfb3JpZ2luYWxfXzsKfQoKLyoqCiAqIEVuY29kZXMgZ2l2ZW4gb2JqZWN0IGludG8gdXJsLWZyaWVuZGx5IGZvcm1hdAogKgogKiBAcGFyYW0gb2JqZWN0IEFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIHNlcmlhbGl6YWJsZSB2YWx1ZXMKICogQHJldHVybnMgc3RyaW5nIEVuY29kZWQKICovCmZ1bmN0aW9uIHVybEVuY29kZShvYmplY3QpIHsKICByZXR1cm4gT2JqZWN0LmtleXMob2JqZWN0KQogICAgLm1hcChrZXkgPT4gYCR7ZW5jb2RlVVJJQ29tcG9uZW50KGtleSl9PSR7ZW5jb2RlVVJJQ29tcG9uZW50KG9iamVjdFtrZXldKX1gKQogICAgLmpvaW4oJyYnKTsKfQoKLyoqCiAqIFRyYW5zZm9ybXMgYW55IGBFcnJvcmAgb3IgYEV2ZW50YCBpbnRvIGEgcGxhaW4gb2JqZWN0IHdpdGggYWxsIG9mIHRoZWlyIGVudW1lcmFibGUgcHJvcGVydGllcywgYW5kIHNvbWUgb2YgdGhlaXIKICogbm9uLWVudW1lcmFibGUgcHJvcGVydGllcyBhdHRhY2hlZC4KICoKICogQHBhcmFtIHZhbHVlIEluaXRpYWwgc291cmNlIHRoYXQgd2UgaGF2ZSB0byB0cmFuc2Zvcm0gaW4gb3JkZXIgZm9yIGl0IHRvIGJlIHVzYWJsZSBieSB0aGUgc2VyaWFsaXplcgogKiBAcmV0dXJucyBBbiBFdmVudCBvciBFcnJvciB0dXJuZWQgaW50byBhbiBvYmplY3QgLSBvciB0aGUgdmFsdWUgYXJndXJtZW50IGl0c2VsZiwgd2hlbiB2YWx1ZSBpcyBuZWl0aGVyIGFuIEV2ZW50IG5vcgogKiAgYW4gRXJyb3IuCiAqLwpmdW5jdGlvbiBjb252ZXJ0VG9QbGFpbk9iamVjdCgKICB2YWx1ZSwKKQoKIHsKICBpZiAoaXNFcnJvcih2YWx1ZSkpIHsKICAgIHJldHVybiB7CiAgICAgIG1lc3NhZ2U6IHZhbHVlLm1lc3NhZ2UsCiAgICAgIG5hbWU6IHZhbHVlLm5hbWUsCiAgICAgIHN0YWNrOiB2YWx1ZS5zdGFjaywKICAgICAgLi4uZ2V0T3duUHJvcGVydGllcyh2YWx1ZSksCiAgICB9OwogIH0gZWxzZSBpZiAoaXNFdmVudCh2YWx1ZSkpIHsKICAgIGNvbnN0IG5ld09iagoKID0gewogICAgICB0eXBlOiB2YWx1ZS50eXBlLAogICAgICB0YXJnZXQ6IHNlcmlhbGl6ZUV2ZW50VGFyZ2V0KHZhbHVlLnRhcmdldCksCiAgICAgIGN1cnJlbnRUYXJnZXQ6IHNlcmlhbGl6ZUV2ZW50VGFyZ2V0KHZhbHVlLmN1cnJlbnRUYXJnZXQpLAogICAgICAuLi5nZXRPd25Qcm9wZXJ0aWVzKHZhbHVlKSwKICAgIH07CgogICAgaWYgKHR5cGVvZiBDdXN0b21FdmVudCAhPT0gJ3VuZGVmaW5lZCcgJiYgaXNJbnN0YW5jZU9mKHZhbHVlLCBDdXN0b21FdmVudCkpIHsKICAgICAgbmV3T2JqLmRldGFpbCA9IHZhbHVlLmRldGFpbDsKICAgIH0KCiAgICByZXR1cm4gbmV3T2JqOwogIH0gZWxzZSB7CiAgICByZXR1cm4gdmFsdWU7CiAgfQp9CgovKiogQ3JlYXRlcyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgdGFyZ2V0IG9mIGFuIGBFdmVudGAgb2JqZWN0ICovCmZ1bmN0aW9uIHNlcmlhbGl6ZUV2ZW50VGFyZ2V0KHRhcmdldCkgewogIHRyeSB7CiAgICByZXR1cm4gaXNFbGVtZW50KHRhcmdldCkgPyBodG1sVHJlZUFzU3RyaW5nKHRhcmdldCkgOiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodGFyZ2V0KTsKICB9IGNhdGNoIChfb08pIHsKICAgIHJldHVybiAnPHVua25vd24+JzsKICB9Cn0KCi8qKiBGaWx0ZXJzIG91dCBhbGwgYnV0IGFuIG9iamVjdCdzIG93biBwcm9wZXJ0aWVzICovCmZ1bmN0aW9uIGdldE93blByb3BlcnRpZXMob2JqKSB7CiAgaWYgKHR5cGVvZiBvYmogPT09ICdvYmplY3QnICYmIG9iaiAhPT0gbnVsbCkgewogICAgY29uc3QgZXh0cmFjdGVkUHJvcHMgPSB7fTsKICAgIGZvciAoY29uc3QgcHJvcGVydHkgaW4gb2JqKSB7CiAgICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wZXJ0eSkpIHsKICAgICAgICBleHRyYWN0ZWRQcm9wc1twcm9wZXJ0eV0gPSAob2JqIClbcHJvcGVydHldOwogICAgICB9CiAgICB9CiAgICByZXR1cm4gZXh0cmFjdGVkUHJvcHM7CiAgfSBlbHNlIHsKICAgIHJldHVybiB7fTsKICB9Cn0KCi8qKgogKiBHaXZlbiBhbnkgb2JqZWN0LCByZXR1cm4gYSBuZXcgb2JqZWN0IGhhdmluZyByZW1vdmVkIGFsbCBmaWVsZHMgd2hvc2UgdmFsdWUgd2FzIGB1bmRlZmluZWRgLgogKiBXb3JrcyByZWN1cnNpdmVseSBvbiBvYmplY3RzIGFuZCBhcnJheXMuCiAqCiAqIEF0dGVudGlvbjogVGhpcyBmdW5jdGlvbiBrZWVwcyBjaXJjdWxhciByZWZlcmVuY2VzIGluIHRoZSByZXR1cm5lZCBvYmplY3QuCiAqLwpmdW5jdGlvbiBkcm9wVW5kZWZpbmVkS2V5cyhpbnB1dFZhbHVlKSB7CiAgLy8gVGhpcyBtYXAga2VlcHMgdHJhY2sgb2Ygd2hhdCBhbHJlYWR5IHZpc2l0ZWQgbm9kZXMgbWFwIHRvLgogIC8vIE91ciBTZXQgLSBiYXNlZCBtZW1vQnVpbGRlciBkb2Vzbid0IHdvcmsgaGVyZSBiZWNhdXNlIHdlIHdhbnQgdG8gdGhlIG91dHB1dCBvYmplY3QgdG8gaGF2ZSB0aGUgc2FtZSBjaXJjdWxhcgogIC8vIHJlZmVyZW5jZXMgYXMgdGhlIGlucHV0IG9iamVjdC4KICBjb25zdCBtZW1vaXphdGlvbk1hcCA9IG5ldyBNYXAoKTsKCiAgLy8gVGhpcyBmdW5jdGlvbiBqdXN0IHByb3hpZXMgYF9kcm9wVW5kZWZpbmVkS2V5c2AgdG8ga2VlcCB0aGUgYG1lbW9CdWlsZGVyYCBvdXQgb2YgdGhpcyBmdW5jdGlvbidzIEFQSQogIHJldHVybiBfZHJvcFVuZGVmaW5lZEtleXMoaW5wdXRWYWx1ZSwgbWVtb2l6YXRpb25NYXApOwp9CgpmdW5jdGlvbiBfZHJvcFVuZGVmaW5lZEtleXMoaW5wdXRWYWx1ZSwgbWVtb2l6YXRpb25NYXApIHsKICBpZiAoaXNQb2pvKGlucHV0VmFsdWUpKSB7CiAgICAvLyBJZiB0aGlzIG5vZGUgaGFzIGFscmVhZHkgYmVlbiB2aXNpdGVkIGR1ZSB0byBhIGNpcmN1bGFyIHJlZmVyZW5jZSwgcmV0dXJuIHRoZSBvYmplY3QgaXQgd2FzIG1hcHBlZCB0byBpbiB0aGUgbmV3IG9iamVjdAogICAgY29uc3QgbWVtb1ZhbCA9IG1lbW9pemF0aW9uTWFwLmdldChpbnB1dFZhbHVlKTsKICAgIGlmIChtZW1vVmFsICE9PSB1bmRlZmluZWQpIHsKICAgICAgcmV0dXJuIG1lbW9WYWwgOwogICAgfQoKICAgIGNvbnN0IHJldHVyblZhbHVlID0ge307CiAgICAvLyBTdG9yZSB0aGUgbWFwcGluZyBvZiB0aGlzIHZhbHVlIGluIGNhc2Ugd2UgdmlzaXQgaXQgYWdhaW4sIGluIGNhc2Ugb2YgY2lyY3VsYXIgZGF0YQogICAgbWVtb2l6YXRpb25NYXAuc2V0KGlucHV0VmFsdWUsIHJldHVyblZhbHVlKTsKCiAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyhpbnB1dFZhbHVlKSkgewogICAgICBpZiAodHlwZW9mIGlucHV0VmFsdWVba2V5XSAhPT0gJ3VuZGVmaW5lZCcpIHsKICAgICAgICByZXR1cm5WYWx1ZVtrZXldID0gX2Ryb3BVbmRlZmluZWRLZXlzKGlucHV0VmFsdWVba2V5XSwgbWVtb2l6YXRpb25NYXApOwogICAgICB9CiAgICB9CgogICAgcmV0dXJuIHJldHVyblZhbHVlIDsKICB9CgogIGlmIChBcnJheS5pc0FycmF5KGlucHV0VmFsdWUpKSB7CiAgICAvLyBJZiB0aGlzIG5vZGUgaGFzIGFscmVhZHkgYmVlbiB2aXNpdGVkIGR1ZSB0byBhIGNpcmN1bGFyIHJlZmVyZW5jZSwgcmV0dXJuIHRoZSBhcnJheSBpdCB3YXMgbWFwcGVkIHRvIGluIHRoZSBuZXcgb2JqZWN0CiAgICBjb25zdCBtZW1vVmFsID0gbWVtb2l6YXRpb25NYXAuZ2V0KGlucHV0VmFsdWUpOwogICAgaWYgKG1lbW9WYWwgIT09IHVuZGVmaW5lZCkgewogICAgICByZXR1cm4gbWVtb1ZhbCA7CiAgICB9CgogICAgY29uc3QgcmV0dXJuVmFsdWUgPSBbXTsKICAgIC8vIFN0b3JlIHRoZSBtYXBwaW5nIG9mIHRoaXMgdmFsdWUgaW4gY2FzZSB3ZSB2aXNpdCBpdCBhZ2FpbiwgaW4gY2FzZSBvZiBjaXJjdWxhciBkYXRhCiAgICBtZW1vaXphdGlvbk1hcC5zZXQoaW5wdXRWYWx1ZSwgcmV0dXJuVmFsdWUpOwoKICAgIGlucHV0VmFsdWUuZm9yRWFjaCgoaXRlbSkgPT4gewogICAgICByZXR1cm5WYWx1ZS5wdXNoKF9kcm9wVW5kZWZpbmVkS2V5cyhpdGVtLCBtZW1vaXphdGlvbk1hcCkpOwogICAgfSk7CgogICAgcmV0dXJuIHJldHVyblZhbHVlIDsKICB9CgogIHJldHVybiBpbnB1dFZhbHVlOwp9CgpmdW5jdGlvbiBpc1Bvam8oaW5wdXQpIHsKICBpZiAoIWlzUGxhaW5PYmplY3QoaW5wdXQpKSB7CiAgICByZXR1cm4gZmFsc2U7CiAgfQoKICB0cnkgewogICAgY29uc3QgbmFtZSA9IChPYmplY3QuZ2V0UHJvdG90eXBlT2YoaW5wdXQpICkuY29uc3RydWN0b3IubmFtZTsKICAgIHJldHVybiAhbmFtZSB8fCBuYW1lID09PSAnT2JqZWN0JzsKICB9IGNhdGNoIChlKSB7CiAgICByZXR1cm4gdHJ1ZTsKICB9Cn0KCi8qKgogKiBVVUlENCBnZW5lcmF0b3IKICoKICogQHJldHVybnMgc3RyaW5nIEdlbmVyYXRlZCBVVUlENC4KICovCmZ1bmN0aW9uIHV1aWQ0KCkgewogIGNvbnN0IGdibCA9IEdMT0JBTF9PQkogOwogIGNvbnN0IGNyeXB0byA9IGdibC5jcnlwdG8gfHwgZ2JsLm1zQ3J5cHRvOwoKICBsZXQgZ2V0UmFuZG9tQnl0ZSA9ICgpID0+IE1hdGgucmFuZG9tKCkgKiAxNjsKICB0cnkgewogICAgaWYgKGNyeXB0byAmJiBjcnlwdG8ucmFuZG9tVVVJRCkgewogICAgICByZXR1cm4gY3J5cHRvLnJhbmRvbVVVSUQoKS5yZXBsYWNlKC8tL2csICcnKTsKICAgIH0KICAgIGlmIChjcnlwdG8gJiYgY3J5cHRvLmdldFJhbmRvbVZhbHVlcykgewogICAgICBnZXRSYW5kb21CeXRlID0gKCkgPT4gewogICAgICAgIC8vIGNyeXB0by5nZXRSYW5kb21WYWx1ZXMgbWlnaHQgcmV0dXJuIHVuZGVmaW5lZCBpbnN0ZWFkIG9mIHRoZSB0eXBlZCBhcnJheQogICAgICAgIC8vIGluIG9sZCBDaHJvbWl1bSB2ZXJzaW9ucyAoZS5nLiAyMy4wLjEyMzUuMCAoMTUxNDIyKSkKICAgICAgICAvLyBIb3dldmVyLCBgdHlwZWRBcnJheWAgaXMgc3RpbGwgZmlsbGVkIGluLXBsYWNlLgogICAgICAgIC8vIEBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0NyeXB0by9nZXRSYW5kb21WYWx1ZXMjdHlwZWRhcnJheQogICAgICAgIGNvbnN0IHR5cGVkQXJyYXkgPSBuZXcgVWludDhBcnJheSgxKTsKICAgICAgICBjcnlwdG8uZ2V0UmFuZG9tVmFsdWVzKHR5cGVkQXJyYXkpOwogICAgICAgIHJldHVybiB0eXBlZEFycmF5WzBdOwogICAgICB9OwogICAgfQogIH0gY2F0Y2ggKF8pIHsKICAgIC8vIHNvbWUgcnVudGltZXMgY2FuIGNyYXNoIGludm9raW5nIGNyeXB0bwogICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdC9pc3N1ZXMvODkzNQogIH0KCiAgLy8gaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMDUwMzQvaG93LXRvLWNyZWF0ZS1hLWd1aWQtdXVpZC1pbi1qYXZhc2NyaXB0LzIxMTc1MjMjMjExNzUyMwogIC8vIENvbmNhdGVuYXRpbmcgdGhlIGZvbGxvd2luZyBudW1iZXJzIGFzIHN0cmluZ3MgcmVzdWx0cyBpbiAnMTAwMDAwMDAxMDAwNDAwMDgwMDAxMDAwMDAwMDAwMDAnCiAgcmV0dXJuICgoWzFlN10gKSArIDFlMyArIDRlMyArIDhlMyArIDFlMTEpLnJlcGxhY2UoL1swMThdL2csIGMgPT4KICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1iaXR3aXNlCiAgICAoKGMgKSBeICgoZ2V0UmFuZG9tQnl0ZSgpICYgMTUpID4+ICgoYyApIC8gNCkpKS50b1N0cmluZygxNiksCiAgKTsKfQoKLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVuc2FmZS1tZW1iZXItYWNjZXNzICovCi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkgKi8KCi8qKgogKiBIZWxwZXIgdG8gZGVjeWNsZSBqc29uIG9iamVjdHMKICovCmZ1bmN0aW9uIG1lbW9CdWlsZGVyKCkgewogIGNvbnN0IGhhc1dlYWtTZXQgPSB0eXBlb2YgV2Vha1NldCA9PT0gJ2Z1bmN0aW9uJzsKICBjb25zdCBpbm5lciA9IGhhc1dlYWtTZXQgPyBuZXcgV2Vha1NldCgpIDogW107CiAgZnVuY3Rpb24gbWVtb2l6ZShvYmopIHsKICAgIGlmIChoYXNXZWFrU2V0KSB7CiAgICAgIGlmIChpbm5lci5oYXMob2JqKSkgewogICAgICAgIHJldHVybiB0cnVlOwogICAgICB9CiAgICAgIGlubmVyLmFkZChvYmopOwogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L3ByZWZlci1mb3Itb2YKICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5uZXIubGVuZ3RoOyBpKyspIHsKICAgICAgY29uc3QgdmFsdWUgPSBpbm5lcltpXTsKICAgICAgaWYgKHZhbHVlID09PSBvYmopIHsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfQogICAgfQogICAgaW5uZXIucHVzaChvYmopOwogICAgcmV0dXJuIGZhbHNlOwogIH0KCiAgZnVuY3Rpb24gdW5tZW1vaXplKG9iaikgewogICAgaWYgKGhhc1dlYWtTZXQpIHsKICAgICAgaW5uZXIuZGVsZXRlKG9iaik7CiAgICB9IGVsc2UgewogICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlubmVyLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgaWYgKGlubmVyW2ldID09PSBvYmopIHsKICAgICAgICAgIGlubmVyLnNwbGljZShpLCAxKTsKICAgICAgICAgIGJyZWFrOwogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KICByZXR1cm4gW21lbW9pemUsIHVubWVtb2l6ZV07Cn0KCi8qKgogKiBSZWN1cnNpdmVseSBub3JtYWxpemVzIHRoZSBnaXZlbiBvYmplY3QuCiAqCiAqIC0gQ3JlYXRlcyBhIGNvcHkgdG8gcHJldmVudCBvcmlnaW5hbCBpbnB1dCBtdXRhdGlvbgogKiAtIFNraXBzIG5vbi1lbnVtZXJhYmxlIHByb3BlcnRpZXMKICogLSBXaGVuIHN0cmluZ2lmeWluZywgY2FsbHMgYHRvSlNPTmAgaWYgaW1wbGVtZW50ZWQKICogLSBSZW1vdmVzIGNpcmN1bGFyIHJlZmVyZW5jZXMKICogLSBUcmFuc2xhdGVzIG5vbi1zZXJpYWxpemFibGUgdmFsdWVzIChgdW5kZWZpbmVkYC9gTmFOYC9mdW5jdGlvbnMpIHRvIHNlcmlhbGl6YWJsZSBmb3JtYXQKICogLSBUcmFuc2xhdGVzIGtub3duIGdsb2JhbCBvYmplY3RzL2NsYXNzZXMgdG8gYSBzdHJpbmcgcmVwcmVzZW50YXRpb25zCiAqIC0gVGFrZXMgY2FyZSBvZiBgRXJyb3JgIG9iamVjdCBzZXJpYWxpemF0aW9uCiAqIC0gT3B0aW9uYWxseSBsaW1pdHMgZGVwdGggb2YgZmluYWwgb3V0cHV0CiAqIC0gT3B0aW9uYWxseSBsaW1pdHMgbnVtYmVyIG9mIHByb3BlcnRpZXMvZWxlbWVudHMgaW5jbHVkZWQgaW4gYW55IHNpbmdsZSBvYmplY3QvYXJyYXkKICoKICogQHBhcmFtIGlucHV0IFRoZSBvYmplY3QgdG8gYmUgbm9ybWFsaXplZC4KICogQHBhcmFtIGRlcHRoIFRoZSBtYXggZGVwdGggdG8gd2hpY2ggdG8gbm9ybWFsaXplIHRoZSBvYmplY3QuIChBbnl0aGluZyBkZWVwZXIgc3RyaW5naWZpZWQgd2hvbGUuKQogKiBAcGFyYW0gbWF4UHJvcGVydGllcyBUaGUgbWF4IG51bWJlciBvZiBlbGVtZW50cyBvciBwcm9wZXJ0aWVzIHRvIGJlIGluY2x1ZGVkIGluIGFueSBzaW5nbGUgYXJyYXkgb3IKICogb2JqZWN0IGluIHRoZSBub3JtYWxsaXplZCBvdXRwdXQuCiAqIEByZXR1cm5zIEEgbm9ybWFsaXplZCB2ZXJzaW9uIG9mIHRoZSBvYmplY3QsIG9yIGAiKipub24tc2VyaWFsaXphYmxlKioiYCBpZiBhbnkgZXJyb3JzIGFyZSB0aHJvd24gZHVyaW5nIG5vcm1hbGl6YXRpb24uCiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQpmdW5jdGlvbiBub3JtYWxpemUoaW5wdXQsIGRlcHRoID0gMTAwLCBtYXhQcm9wZXJ0aWVzID0gK0luZmluaXR5KSB7CiAgdHJ5IHsKICAgIC8vIHNpbmNlIHdlJ3JlIGF0IHRoZSBvdXRlcm1vc3QgbGV2ZWwsIHdlIGRvbid0IHByb3ZpZGUgYSBrZXkKICAgIHJldHVybiB2aXNpdCgnJywgaW5wdXQsIGRlcHRoLCBtYXhQcm9wZXJ0aWVzKTsKICB9IGNhdGNoIChlcnIpIHsKICAgIHJldHVybiB7IEVSUk9SOiBgKipub24tc2VyaWFsaXphYmxlKiogKCR7ZXJyfSlgIH07CiAgfQp9CgovKioKICogVmlzaXRzIGEgbm9kZSB0byBwZXJmb3JtIG5vcm1hbGl6YXRpb24gb24gaXQKICoKICogQHBhcmFtIGtleSBUaGUga2V5IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGdpdmVuIG5vZGUKICogQHBhcmFtIHZhbHVlIFRoZSBub2RlIHRvIGJlIHZpc2l0ZWQKICogQHBhcmFtIGRlcHRoIE9wdGlvbmFsIG51bWJlciBpbmRpY2F0aW5nIHRoZSBtYXhpbXVtIHJlY3Vyc2lvbiBkZXB0aAogKiBAcGFyYW0gbWF4UHJvcGVydGllcyBPcHRpb25hbCBtYXhpbXVtIG51bWJlciBvZiBwcm9wZXJ0aWVzL2VsZW1lbnRzIGluY2x1ZGVkIGluIGFueSBzaW5nbGUgb2JqZWN0L2FycmF5CiAqIEBwYXJhbSBtZW1vIE9wdGlvbmFsIE1lbW8gY2xhc3MgaGFuZGxpbmcgZGVjeWNsaW5nCiAqLwpmdW5jdGlvbiB2aXNpdCgKICBrZXksCiAgdmFsdWUsCiAgZGVwdGggPSArSW5maW5pdHksCiAgbWF4UHJvcGVydGllcyA9ICtJbmZpbml0eSwKICBtZW1vID0gbWVtb0J1aWxkZXIoKSwKKSB7CiAgY29uc3QgW21lbW9pemUsIHVubWVtb2l6ZV0gPSBtZW1vOwoKICAvLyBHZXQgdGhlIHNpbXBsZSBjYXNlcyBvdXQgb2YgdGhlIHdheSBmaXJzdAogIGlmICgKICAgIHZhbHVlID09IG51bGwgfHwgLy8gdGhpcyBtYXRjaGVzIG51bGwgYW5kIHVuZGVmaW5lZCAtPiBlcWVxIG5vdCBlcWVxZXEKICAgIChbJ251bWJlcicsICdib29sZWFuJywgJ3N0cmluZyddLmluY2x1ZGVzKHR5cGVvZiB2YWx1ZSkgJiYgIWlzTmFOJDEodmFsdWUpKQogICkgewogICAgcmV0dXJuIHZhbHVlIDsKICB9CgogIGNvbnN0IHN0cmluZ2lmaWVkID0gc3RyaW5naWZ5VmFsdWUoa2V5LCB2YWx1ZSk7CgogIC8vIEFueXRoaW5nIHdlIGNvdWxkIHBvdGVudGlhbGx5IGRpZyBpbnRvIG1vcmUgKG9iamVjdHMgb3IgYXJyYXlzKSB3aWxsIGhhdmUgY29tZSBiYWNrIGFzIGAiW29iamVjdCBYWFhYXSJgLgogIC8vIEV2ZXJ5dGhpbmcgZWxzZSB3aWxsIGhhdmUgYWxyZWFkeSBiZWVuIHNlcmlhbGl6ZWQsIHNvIGlmIHdlIGRvbid0IHNlZSB0aGF0IHBhdHRlcm4sIHdlJ3JlIGRvbmUuCiAgaWYgKCFzdHJpbmdpZmllZC5zdGFydHNXaXRoKCdbb2JqZWN0ICcpKSB7CiAgICByZXR1cm4gc3RyaW5naWZpZWQ7CiAgfQoKICAvLyBGcm9tIGhlcmUgb24sIHdlIGNhbiBhc3NlcnQgdGhhdCBgdmFsdWVgIGlzIGVpdGhlciBhbiBvYmplY3Qgb3IgYW4gYXJyYXkuCgogIC8vIERvIG5vdCBub3JtYWxpemUgb2JqZWN0cyB0aGF0IHdlIGtub3cgaGF2ZSBhbHJlYWR5IGJlZW4gbm9ybWFsaXplZC4gQXMgYSBnZW5lcmFsIHJ1bGUsIHRoZQogIC8vICJfX3NlbnRyeV9za2lwX25vcm1hbGl6YXRpb25fXyIgcHJvcGVydHkgc2hvdWxkIG9ubHkgYmUgdXNlZCBzcGFyaW5nbHkgYW5kIG9ubHkgc2hvdWxkIG9ubHkgYmUgc2V0IG9uIG9iamVjdHMgdGhhdAogIC8vIGhhdmUgYWxyZWFkeSBiZWVuIG5vcm1hbGl6ZWQuCiAgaWYgKCh2YWx1ZSApWydfX3NlbnRyeV9za2lwX25vcm1hbGl6YXRpb25fXyddKSB7CiAgICByZXR1cm4gdmFsdWUgOwogIH0KCiAgLy8gV2UgY2FuIHNldCBgX19zZW50cnlfb3ZlcnJpZGVfbm9ybWFsaXphdGlvbl9kZXB0aF9fYCBvbiBhbiBvYmplY3QgdG8gZW5zdXJlIHRoYXQgZnJvbSB0aGVyZQogIC8vIFdlIGtlZXAgYSBjZXJ0YWluIGFtb3VudCBvZiBkZXB0aC4KICAvLyBUaGlzIHNob3VsZCBiZSB1c2VkIHNwYXJpbmdseSwgZS5nLiB3ZSB1c2UgaXQgZm9yIHRoZSByZWR1eCBpbnRlZ3JhdGlvbiB0byBlbnN1cmUgd2UgZ2V0IGEgY2VydGFpbiBhbW91bnQgb2Ygc3RhdGUuCiAgY29uc3QgcmVtYWluaW5nRGVwdGggPQogICAgdHlwZW9mICh2YWx1ZSApWydfX3NlbnRyeV9vdmVycmlkZV9ub3JtYWxpemF0aW9uX2RlcHRoX18nXSA9PT0gJ251bWJlcicKICAgICAgPyAoKHZhbHVlIClbJ19fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXyddICkKICAgICAgOiBkZXB0aDsKCiAgLy8gV2UncmUgYWxzbyBkb25lIGlmIHdlJ3ZlIHJlYWNoZWQgdGhlIG1heCBkZXB0aAogIGlmIChyZW1haW5pbmdEZXB0aCA9PT0gMCkgewogICAgLy8gQXQgdGhpcyBwb2ludCB3ZSBrbm93IGBzZXJpYWxpemVkYCBpcyBhIHN0cmluZyBvZiB0aGUgZm9ybSBgIltvYmplY3QgWFhYWF0iYC4gQ2xlYW4gaXQgdXAgc28gaXQncyBqdXN0IGAiW1hYWFhdImAuCiAgICByZXR1cm4gc3RyaW5naWZpZWQucmVwbGFjZSgnb2JqZWN0ICcsICcnKTsKICB9CgogIC8vIElmIHdlJ3ZlIGFscmVhZHkgdmlzaXRlZCB0aGlzIGJyYW5jaCwgYmFpbCBvdXQsIGFzIGl0J3MgY2lyY3VsYXIgcmVmZXJlbmNlLiBJZiBub3QsIG5vdGUgdGhhdCB3ZSdyZSBzZWVpbmcgaXQgbm93LgogIGlmIChtZW1vaXplKHZhbHVlKSkgewogICAgcmV0dXJuICdbQ2lyY3VsYXIgfl0nOwogIH0KCiAgLy8gSWYgdGhlIHZhbHVlIGhhcyBhIGB0b0pTT05gIG1ldGhvZCwgd2UgY2FsbCBpdCB0byBleHRyYWN0IG1vcmUgaW5mb3JtYXRpb24KICBjb25zdCB2YWx1ZVdpdGhUb0pTT04gPSB2YWx1ZSA7CiAgaWYgKHZhbHVlV2l0aFRvSlNPTiAmJiB0eXBlb2YgdmFsdWVXaXRoVG9KU09OLnRvSlNPTiA9PT0gJ2Z1bmN0aW9uJykgewogICAgdHJ5IHsKICAgICAgY29uc3QganNvblZhbHVlID0gdmFsdWVXaXRoVG9KU09OLnRvSlNPTigpOwogICAgICAvLyBXZSBuZWVkIHRvIG5vcm1hbGl6ZSB0aGUgcmV0dXJuIHZhbHVlIG9mIGAudG9KU09OKClgIGluIGNhc2UgaXQgaGFzIGNpcmN1bGFyIHJlZmVyZW5jZXMKICAgICAgcmV0dXJuIHZpc2l0KCcnLCBqc29uVmFsdWUsIHJlbWFpbmluZ0RlcHRoIC0gMSwgbWF4UHJvcGVydGllcywgbWVtbyk7CiAgICB9IGNhdGNoIChlcnIpIHsKICAgICAgLy8gcGFzcyAoVGhlIGJ1aWx0LWluIGB0b0pTT05gIGZhaWxlZCwgYnV0IHdlIGNhbiBzdGlsbCB0cnkgdG8gZG8gaXQgb3Vyc2VsdmVzKQogICAgfQogIH0KCiAgLy8gQXQgdGhpcyBwb2ludCB3ZSBrbm93IHdlIGVpdGhlciBoYXZlIGFuIG9iamVjdCBvciBhbiBhcnJheSwgd2UgaGF2ZW4ndCBzZWVuIGl0IGJlZm9yZSwgYW5kIHdlJ3JlIGdvaW5nIHRvIHJlY3Vyc2UKICAvLyBiZWNhdXNlIHdlIGhhdmVuJ3QgeWV0IHJlYWNoZWQgdGhlIG1heCBkZXB0aC4gQ3JlYXRlIGFuIGFjY3VtdWxhdG9yIHRvIGhvbGQgdGhlIHJlc3VsdHMgb2YgdmlzaXRpbmcgZWFjaAogIC8vIHByb3BlcnR5L2VudHJ5LCBhbmQga2VlcCB0cmFjayBvZiB0aGUgbnVtYmVyIG9mIGl0ZW1zIHdlIGFkZCB0byBpdC4KICBjb25zdCBub3JtYWxpemVkID0gKEFycmF5LmlzQXJyYXkodmFsdWUpID8gW10gOiB7fSkgOwogIGxldCBudW1BZGRlZCA9IDA7CgogIC8vIEJlZm9yZSB3ZSBiZWdpbiwgY29udmVydGBFcnJvcmAgYW5kYEV2ZW50YCBpbnN0YW5jZXMgaW50byBwbGFpbiBvYmplY3RzLCBzaW5jZSBzb21lIG9mIGVhY2ggb2YgdGhlaXIgcmVsZXZhbnQKICAvLyBwcm9wZXJ0aWVzIGFyZSBub24tZW51bWVyYWJsZSBhbmQgb3RoZXJ3aXNlIHdvdWxkIGdldCBtaXNzZWQuCiAgY29uc3QgdmlzaXRhYmxlID0gY29udmVydFRvUGxhaW5PYmplY3QodmFsdWUgKTsKCiAgZm9yIChjb25zdCB2aXNpdEtleSBpbiB2aXNpdGFibGUpIHsKICAgIC8vIEF2b2lkIGl0ZXJhdGluZyBvdmVyIGZpZWxkcyBpbiB0aGUgcHJvdG90eXBlIGlmIHRoZXkndmUgc29tZWhvdyBiZWVuIGV4cG9zZWQgdG8gZW51bWVyYXRpb24uCiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh2aXNpdGFibGUsIHZpc2l0S2V5KSkgewogICAgICBjb250aW51ZTsKICAgIH0KCiAgICBpZiAobnVtQWRkZWQgPj0gbWF4UHJvcGVydGllcykgewogICAgICBub3JtYWxpemVkW3Zpc2l0S2V5XSA9ICdbTWF4UHJvcGVydGllcyB+XSc7CiAgICAgIGJyZWFrOwogICAgfQoKICAgIC8vIFJlY3Vyc2l2ZWx5IHZpc2l0IGFsbCB0aGUgY2hpbGQgbm9kZXMKICAgIGNvbnN0IHZpc2l0VmFsdWUgPSB2aXNpdGFibGVbdmlzaXRLZXldOwogICAgbm9ybWFsaXplZFt2aXNpdEtleV0gPSB2aXNpdCh2aXNpdEtleSwgdmlzaXRWYWx1ZSwgcmVtYWluaW5nRGVwdGggLSAxLCBtYXhQcm9wZXJ0aWVzLCBtZW1vKTsKCiAgICBudW1BZGRlZCsrOwogIH0KCiAgLy8gT25jZSB3ZSd2ZSB2aXNpdGVkIGFsbCB0aGUgYnJhbmNoZXMsIHJlbW92ZSB0aGUgcGFyZW50IGZyb20gbWVtbyBzdG9yYWdlCiAgdW5tZW1vaXplKHZhbHVlKTsKCiAgLy8gUmV0dXJuIGFjY3VtdWxhdGVkIHZhbHVlcwogIHJldHVybiBub3JtYWxpemVkOwp9CgovKiBlc2xpbnQtZGlzYWJsZSBjb21wbGV4aXR5ICovCi8qKgogKiBTdHJpbmdpZnkgdGhlIGdpdmVuIHZhbHVlLiBIYW5kbGVzIHZhcmlvdXMga25vd24gc3BlY2lhbCB2YWx1ZXMgYW5kIHR5cGVzLgogKgogKiBOb3QgbWVhbnQgdG8gYmUgdXNlZCBvbiBzaW1wbGUgcHJpbWl0aXZlcyB3aGljaCBhbHJlYWR5IGhhdmUgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24sIGFzIGl0IHdpbGwsIGZvciBleGFtcGxlLCB0dXJuCiAqIHRoZSBudW1iZXIgMTIzMSBpbnRvICJbT2JqZWN0IE51bWJlcl0iLCBub3Igb24gYG51bGxgLCBhcyBpdCB3aWxsIHRocm93LgogKgogKiBAcGFyYW0gdmFsdWUgVGhlIHZhbHVlIHRvIHN0cmluZ2lmeQogKiBAcmV0dXJucyBBIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBnaXZlbiB2YWx1ZQogKi8KZnVuY3Rpb24gc3RyaW5naWZ5VmFsdWUoCiAga2V5LAogIC8vIHRoaXMgdHlwZSBpcyBhIHRpbnkgYml0IG9mIGEgY2hlYXQsIHNpbmNlIHRoaXMgZnVuY3Rpb24gZG9lcyBoYW5kbGUgTmFOICh3aGljaCBpcyB0ZWNobmljYWxseSBhIG51bWJlciksIGJ1dCBmb3IKICAvLyBvdXIgaW50ZXJuYWwgdXNlLCBpdCdsbCBkbwogIHZhbHVlLAopIHsKICB0cnkgewogICAgaWYgKGtleSA9PT0gJ2RvbWFpbicgJiYgdmFsdWUgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiAodmFsdWUgKS5fZXZlbnRzKSB7CiAgICAgIHJldHVybiAnW0RvbWFpbl0nOwogICAgfQoKICAgIGlmIChrZXkgPT09ICdkb21haW5FbWl0dGVyJykgewogICAgICByZXR1cm4gJ1tEb21haW5FbWl0dGVyXSc7CiAgICB9CgogICAgLy8gSXQncyBzYWZlIHRvIHVzZSBgZ2xvYmFsYCwgYHdpbmRvd2AsIGFuZCBgZG9jdW1lbnRgIGhlcmUgaW4gdGhpcyBtYW5uZXIsIGFzIHdlIGFyZSBhc3NlcnRpbmcgdXNpbmcgYHR5cGVvZmAgZmlyc3QKICAgIC8vIHdoaWNoIHdvbid0IHRocm93IGlmIHRoZXkgYXJlIG5vdCBwcmVzZW50LgoKICAgIGlmICh0eXBlb2YgZ2xvYmFsICE9PSAndW5kZWZpbmVkJyAmJiB2YWx1ZSA9PT0gZ2xvYmFsKSB7CiAgICAgIHJldHVybiAnW0dsb2JhbF0nOwogICAgfQoKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLWdsb2JhbHMKICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB2YWx1ZSA9PT0gd2luZG93KSB7CiAgICAgIHJldHVybiAnW1dpbmRvd10nOwogICAgfQoKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLWdsb2JhbHMKICAgIGlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnICYmIHZhbHVlID09PSBkb2N1bWVudCkgewogICAgICByZXR1cm4gJ1tEb2N1bWVudF0nOwogICAgfQoKICAgIGlmIChpc1Z1ZVZpZXdNb2RlbCh2YWx1ZSkpIHsKICAgICAgcmV0dXJuICdbVnVlVmlld01vZGVsXSc7CiAgICB9CgogICAgLy8gUmVhY3QncyBTeW50aGV0aWNFdmVudCB0aGluZ3kKICAgIGlmIChpc1N5bnRoZXRpY0V2ZW50KHZhbHVlKSkgewogICAgICByZXR1cm4gJ1tTeW50aGV0aWNFdmVudF0nOwogICAgfQoKICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInICYmIHZhbHVlICE9PSB2YWx1ZSkgewogICAgICByZXR1cm4gJ1tOYU5dJzsKICAgIH0KCiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7CiAgICAgIHJldHVybiBgW0Z1bmN0aW9uOiAke2dldEZ1bmN0aW9uTmFtZSh2YWx1ZSl9XWA7CiAgICB9CgogICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N5bWJvbCcpIHsKICAgICAgcmV0dXJuIGBbJHtTdHJpbmcodmFsdWUpfV1gOwogICAgfQoKICAgIC8vIHN0cmluZ2lmaWVkIEJpZ0ludHMgYXJlIGluZGlzdGluZ3Vpc2hhYmxlIGZyb20gcmVndWxhciBudW1iZXJzLCBzbyB3ZSBuZWVkIHRvIGxhYmVsIHRoZW0gdG8gYXZvaWQgY29uZnVzaW9uCiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnYmlnaW50JykgewogICAgICByZXR1cm4gYFtCaWdJbnQ6ICR7U3RyaW5nKHZhbHVlKX1dYDsKICAgIH0KCiAgICAvLyBOb3cgdGhhdCB3ZSd2ZSBrbm9ja2VkIG91dCBhbGwgdGhlIHNwZWNpYWwgY2FzZXMgYW5kIHRoZSBwcmltaXRpdmVzLCBhbGwgd2UgaGF2ZSBsZWZ0IGFyZSBvYmplY3RzLiBTaW1wbHkgY2FzdGluZwogICAgLy8gdGhlbSB0byBzdHJpbmdzIG1lYW5zIHRoYXQgaW5zdGFuY2VzIG9mIGNsYXNzZXMgd2hpY2ggaGF2ZW4ndCBkZWZpbmVkIHRoZWlyIGB0b1N0cmluZ1RhZ2Agd2lsbCBqdXN0IGNvbWUgb3V0IGFzCiAgICAvLyBgIltvYmplY3QgT2JqZWN0XSJgLiBJZiB3ZSBpbnN0ZWFkIGxvb2sgYXQgdGhlIGNvbnN0cnVjdG9yJ3MgbmFtZSAod2hpY2ggaXMgdGhlIHNhbWUgYXMgdGhlIG5hbWUgb2YgdGhlIGNsYXNzKSwKICAgIC8vIHdlIGNhbiBtYWtlIHN1cmUgdGhhdCBvbmx5IHBsYWluIG9iamVjdHMgY29tZSBvdXQgdGhhdCB3YXkuCiAgICBjb25zdCBvYmpOYW1lID0gZ2V0Q29uc3RydWN0b3JOYW1lKHZhbHVlKTsKCiAgICAvLyBIYW5kbGUgSFRNTCBFbGVtZW50cwogICAgaWYgKC9eSFRNTChcdyopRWxlbWVudCQvLnRlc3Qob2JqTmFtZSkpIHsKICAgICAgcmV0dXJuIGBbSFRNTEVsZW1lbnQ6ICR7b2JqTmFtZX1dYDsKICAgIH0KCiAgICByZXR1cm4gYFtvYmplY3QgJHtvYmpOYW1lfV1gOwogIH0gY2F0Y2ggKGVycikgewogICAgcmV0dXJuIGAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHtlcnJ9KWA7CiAgfQp9Ci8qIGVzbGludC1lbmFibGUgY29tcGxleGl0eSAqLwoKZnVuY3Rpb24gZ2V0Q29uc3RydWN0b3JOYW1lKHZhbHVlKSB7CiAgY29uc3QgcHJvdG90eXBlID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKHZhbHVlKTsKCiAgcmV0dXJuIHByb3RvdHlwZSA/IHByb3RvdHlwZS5jb25zdHJ1Y3Rvci5uYW1lIDogJ251bGwgcHJvdG90eXBlJzsKfQoKLyoqCiAqIE5vcm1hbGl6ZXMgVVJMcyBpbiBleGNlcHRpb25zIGFuZCBzdGFja3RyYWNlcyB0byBhIGJhc2UgcGF0aCBzbyBTZW50cnkgY2FuIGZpbmdlcnByaW50CiAqIGFjcm9zcyBwbGF0Zm9ybXMgYW5kIHdvcmtpbmcgZGlyZWN0b3J5LgogKgogKiBAcGFyYW0gdXJsIFRoZSBVUkwgdG8gYmUgbm9ybWFsaXplZC4KICogQHBhcmFtIGJhc2VQYXRoIFRoZSBhcHBsaWNhdGlvbiBiYXNlIHBhdGguCiAqIEByZXR1cm5zIFRoZSBub3JtYWxpemVkIFVSTC4KICovCmZ1bmN0aW9uIG5vcm1hbGl6ZVVybFRvQmFzZSh1cmwsIGJhc2VQYXRoKSB7CiAgY29uc3QgZXNjYXBlZEJhc2UgPSBiYXNlUGF0aAogICAgLy8gQmFja3NsYXNoIHRvIGZvcndhcmQKICAgIC5yZXBsYWNlKC9cXC9nLCAnLycpCiAgICAvLyBFc2NhcGUgUmVnRXhwIHNwZWNpYWwgY2hhcmFjdGVycwogICAgLnJlcGxhY2UoL1t8XFx7fSgpW1xdXiQrKj8uXS9nLCAnXFwkJicpOwoKICBsZXQgbmV3VXJsID0gdXJsOwogIHRyeSB7CiAgICBuZXdVcmwgPSBkZWNvZGVVUkkodXJsKTsKICB9IGNhdGNoIChfT28pIHsKICAgIC8vIFNvbWV0aW1lIHRoaXMgYnJlYWtzCiAgfQogIHJldHVybiAoCiAgICBuZXdVcmwKICAgICAgLnJlcGxhY2UoL1xcL2csICcvJykKICAgICAgLnJlcGxhY2UoL3dlYnBhY2s6XC8/L2csICcnKSAvLyBSZW1vdmUgaW50ZXJtZWRpYXRlIGJhc2UgcGF0aAogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHNlbnRyeS1pbnRlcm5hbC9zZGsvbm8tcmVnZXhwLWNvbnN0cnVjdG9yCiAgICAgIC5yZXBsYWNlKG5ldyBSZWdFeHAoYChmaWxlOi8vKT8vKiR7ZXNjYXBlZEJhc2V9LypgLCAnaWcnKSwgJ2FwcDovLy8nKQogICk7Cn0KCi8vIFNwbGl0IGEgZmlsZW5hbWUgaW50byBbcm9vdCwgZGlyLCBiYXNlbmFtZSwgZXh0XSwgdW5peCB2ZXJzaW9uCi8vICdyb290JyBpcyBqdXN0IGEgc2xhc2gsIG9yIG5vdGhpbmcuCmNvbnN0IHNwbGl0UGF0aFJlID0gL14oXFMrOlxcfFwvPykoW1xzXFNdKj8pKCg/OlwuezEsMn18W14vXFxdKz98KShcLlteLi9cXF0qfCkpKD86Wy9cXF0qKSQvOwovKiogSlNEb2MgKi8KZnVuY3Rpb24gc3BsaXRQYXRoKGZpbGVuYW1lKSB7CiAgLy8gVHJ1bmNhdGUgZmlsZXMgbmFtZXMgZ3JlYXRlciB0aGFuIDEwMjQgY2hhcmFjdGVycyB0byBhdm9pZCByZWdleCBkb3MKICAvLyBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0L3B1bGwvODczNyNkaXNjdXNzaW9uX3IxMjg1NzE5MTcyCiAgY29uc3QgdHJ1bmNhdGVkID0gZmlsZW5hbWUubGVuZ3RoID4gMTAyNCA/IGA8dHJ1bmNhdGVkPiR7ZmlsZW5hbWUuc2xpY2UoLTEwMjQpfWAgOiBmaWxlbmFtZTsKICBjb25zdCBwYXJ0cyA9IHNwbGl0UGF0aFJlLmV4ZWModHJ1bmNhdGVkKTsKICByZXR1cm4gcGFydHMgPyBwYXJ0cy5zbGljZSgxKSA6IFtdOwp9CgovKiogSlNEb2MgKi8KZnVuY3Rpb24gZGlybmFtZShwYXRoKSB7CiAgY29uc3QgcmVzdWx0ID0gc3BsaXRQYXRoKHBhdGgpOwogIGNvbnN0IHJvb3QgPSByZXN1bHRbMF07CiAgbGV0IGRpciA9IHJlc3VsdFsxXTsKCiAgaWYgKCFyb290ICYmICFkaXIpIHsKICAgIC8vIE5vIGRpcm5hbWUgd2hhdHNvZXZlcgogICAgcmV0dXJuICcuJzsKICB9CgogIGlmIChkaXIpIHsKICAgIC8vIEl0IGhhcyBhIGRpcm5hbWUsIHN0cmlwIHRyYWlsaW5nIHNsYXNoCiAgICBkaXIgPSBkaXIuc2xpY2UoMCwgZGlyLmxlbmd0aCAtIDEpOwogIH0KCiAgcmV0dXJuIHJvb3QgKyBkaXI7Cn0KCi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9leHBsaWNpdC1mdW5jdGlvbi1yZXR1cm4tdHlwZSAqLwovKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55ICovCgovKiogU3luY1Byb21pc2UgaW50ZXJuYWwgc3RhdGVzICovCnZhciBTdGF0ZXM7IChmdW5jdGlvbiAoU3RhdGVzKSB7CiAgLyoqIFBlbmRpbmcgKi8KICBjb25zdCBQRU5ESU5HID0gMDsgU3RhdGVzW1N0YXRlc1siUEVORElORyJdID0gUEVORElOR10gPSAiUEVORElORyI7CiAgLyoqIFJlc29sdmVkIC8gT0sgKi8KICBjb25zdCBSRVNPTFZFRCA9IDE7IFN0YXRlc1tTdGF0ZXNbIlJFU09MVkVEIl0gPSBSRVNPTFZFRF0gPSAiUkVTT0xWRUQiOwogIC8qKiBSZWplY3RlZCAvIEVycm9yICovCiAgY29uc3QgUkVKRUNURUQgPSAyOyBTdGF0ZXNbU3RhdGVzWyJSRUpFQ1RFRCJdID0gUkVKRUNURURdID0gIlJFSkVDVEVEIjsKfSkoU3RhdGVzIHx8IChTdGF0ZXMgPSB7fSkpOwoKLy8gT3ZlcmxvYWRzIHNvIHdlIGNhbiBjYWxsIHJlc29sdmVkU3luY1Byb21pc2Ugd2l0aG91dCBhcmd1bWVudHMgYW5kIGdlbmVyaWMgYXJndW1lbnQKCi8qKgogKiBDcmVhdGVzIGEgcmVzb2x2ZWQgc3luYyBwcm9taXNlLgogKgogKiBAcGFyYW0gdmFsdWUgdGhlIHZhbHVlIHRvIHJlc29sdmUgdGhlIHByb21pc2Ugd2l0aAogKiBAcmV0dXJucyB0aGUgcmVzb2x2ZWQgc3luYyBwcm9taXNlCiAqLwpmdW5jdGlvbiByZXNvbHZlZFN5bmNQcm9taXNlKHZhbHVlKSB7CiAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZShyZXNvbHZlID0+IHsKICAgIHJlc29sdmUodmFsdWUpOwogIH0pOwp9CgovKioKICogQ3JlYXRlcyBhIHJlamVjdGVkIHN5bmMgcHJvbWlzZS4KICoKICogQHBhcmFtIHZhbHVlIHRoZSB2YWx1ZSB0byByZWplY3QgdGhlIHByb21pc2Ugd2l0aAogKiBAcmV0dXJucyB0aGUgcmVqZWN0ZWQgc3luYyBwcm9taXNlCiAqLwpmdW5jdGlvbiByZWplY3RlZFN5bmNQcm9taXNlKHJlYXNvbikgewogIHJldHVybiBuZXcgU3luY1Byb21pc2UoKF8sIHJlamVjdCkgPT4gewogICAgcmVqZWN0KHJlYXNvbik7CiAgfSk7Cn0KCi8qKgogKiBUaGVuYWJsZSBjbGFzcyB0aGF0IGJlaGF2ZXMgbGlrZSBhIFByb21pc2UgYW5kIGZvbGxvd3MgaXQncyBpbnRlcmZhY2UKICogYnV0IGlzIG5vdCBhc3luYyBpbnRlcm5hbGx5CiAqLwpjbGFzcyBTeW5jUHJvbWlzZSB7CgogICBjb25zdHJ1Y3RvcigKICAgIGV4ZWN1dG9yLAogICkge1N5bmNQcm9taXNlLnByb3RvdHlwZS5fX2luaXQuY2FsbCh0aGlzKTtTeW5jUHJvbWlzZS5wcm90b3R5cGUuX19pbml0Mi5jYWxsKHRoaXMpO1N5bmNQcm9taXNlLnByb3RvdHlwZS5fX2luaXQzLmNhbGwodGhpcyk7U3luY1Byb21pc2UucHJvdG90eXBlLl9faW5pdDQuY2FsbCh0aGlzKTsKICAgIHRoaXMuX3N0YXRlID0gU3RhdGVzLlBFTkRJTkc7CiAgICB0aGlzLl9oYW5kbGVycyA9IFtdOwoKICAgIHRyeSB7CiAgICAgIGV4ZWN1dG9yKHRoaXMuX3Jlc29sdmUsIHRoaXMuX3JlamVjdCk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHRoaXMuX3JlamVjdChlKTsKICAgIH0KICB9CgogIC8qKiBKU0RvYyAqLwogICB0aGVuKAogICAgb25mdWxmaWxsZWQsCiAgICBvbnJlamVjdGVkLAogICkgewogICAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIHRoaXMuX2hhbmRsZXJzLnB1c2goWwogICAgICAgIGZhbHNlLAogICAgICAgIHJlc3VsdCA9PiB7CiAgICAgICAgICBpZiAoIW9uZnVsZmlsbGVkKSB7CiAgICAgICAgICAgIC8vIFRPRE86IMKvXF8o44OEKV8vwq8KICAgICAgICAgICAgLy8gVE9ETzogRklYTUUKICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQgKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgcmVzb2x2ZShvbmZ1bGZpbGxlZChyZXN1bHQpKTsKICAgICAgICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgICAgICAgIHJlamVjdChlKTsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgcmVhc29uID0+IHsKICAgICAgICAgIGlmICghb25yZWplY3RlZCkgewogICAgICAgICAgICByZWplY3QocmVhc29uKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHRyeSB7CiAgICAgICAgICAgICAgcmVzb2x2ZShvbnJlamVjdGVkKHJlYXNvbikpOwogICAgICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICAgICAgcmVqZWN0KGUpOwogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgXSk7CiAgICAgIHRoaXMuX2V4ZWN1dGVIYW5kbGVycygpOwogICAgfSk7CiAgfQoKICAvKiogSlNEb2MgKi8KICAgY2F0Y2goCiAgICBvbnJlamVjdGVkLAogICkgewogICAgcmV0dXJuIHRoaXMudGhlbih2YWwgPT4gdmFsLCBvbnJlamVjdGVkKTsKICB9CgogIC8qKiBKU0RvYyAqLwogICBmaW5hbGx5KG9uZmluYWxseSkgewogICAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIGxldCB2YWw7CiAgICAgIGxldCBpc1JlamVjdGVkOwoKICAgICAgcmV0dXJuIHRoaXMudGhlbigKICAgICAgICB2YWx1ZSA9PiB7CiAgICAgICAgICBpc1JlamVjdGVkID0gZmFsc2U7CiAgICAgICAgICB2YWwgPSB2YWx1ZTsKICAgICAgICAgIGlmIChvbmZpbmFsbHkpIHsKICAgICAgICAgICAgb25maW5hbGx5KCk7CiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICByZWFzb24gPT4gewogICAgICAgICAgaXNSZWplY3RlZCA9IHRydWU7CiAgICAgICAgICB2YWwgPSByZWFzb247CiAgICAgICAgICBpZiAob25maW5hbGx5KSB7CiAgICAgICAgICAgIG9uZmluYWxseSgpOwogICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICkudGhlbigoKSA9PiB7CiAgICAgICAgaWYgKGlzUmVqZWN0ZWQpIHsKICAgICAgICAgIHJlamVjdCh2YWwpOwogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KCiAgICAgICAgcmVzb2x2ZSh2YWwgKTsKICAgICAgfSk7CiAgICB9KTsKICB9CgogIC8qKiBKU0RvYyAqLwogICAgX19pbml0KCkge3RoaXMuX3Jlc29sdmUgPSAodmFsdWUpID0+IHsKICAgIHRoaXMuX3NldFJlc3VsdChTdGF0ZXMuUkVTT0xWRUQsIHZhbHVlKTsKICB9O30KCiAgLyoqIEpTRG9jICovCiAgICBfX2luaXQyKCkge3RoaXMuX3JlamVjdCA9IChyZWFzb24pID0+IHsKICAgIHRoaXMuX3NldFJlc3VsdChTdGF0ZXMuUkVKRUNURUQsIHJlYXNvbik7CiAgfTt9CgogIC8qKiBKU0RvYyAqLwogICAgX19pbml0MygpIHt0aGlzLl9zZXRSZXN1bHQgPSAoc3RhdGUsIHZhbHVlKSA9PiB7CiAgICBpZiAodGhpcy5fc3RhdGUgIT09IFN0YXRlcy5QRU5ESU5HKSB7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBpZiAoaXNUaGVuYWJsZSh2YWx1ZSkpIHsKICAgICAgdm9pZCAodmFsdWUgKS50aGVuKHRoaXMuX3Jlc29sdmUsIHRoaXMuX3JlamVjdCk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICB0aGlzLl9zdGF0ZSA9IHN0YXRlOwogICAgdGhpcy5fdmFsdWUgPSB2YWx1ZTsKCiAgICB0aGlzLl9leGVjdXRlSGFuZGxlcnMoKTsKICB9O30KCiAgLyoqIEpTRG9jICovCiAgICBfX2luaXQ0KCkge3RoaXMuX2V4ZWN1dGVIYW5kbGVycyA9ICgpID0+IHsKICAgIGlmICh0aGlzLl9zdGF0ZSA9PT0gU3RhdGVzLlBFTkRJTkcpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIGNvbnN0IGNhY2hlZEhhbmRsZXJzID0gdGhpcy5faGFuZGxlcnMuc2xpY2UoKTsKICAgIHRoaXMuX2hhbmRsZXJzID0gW107CgogICAgY2FjaGVkSGFuZGxlcnMuZm9yRWFjaChoYW5kbGVyID0+IHsKICAgICAgaWYgKGhhbmRsZXJbMF0pIHsKICAgICAgICByZXR1cm47CiAgICAgIH0KCiAgICAgIGlmICh0aGlzLl9zdGF0ZSA9PT0gU3RhdGVzLlJFU09MVkVEKSB7CiAgICAgICAgaGFuZGxlclsxXSh0aGlzLl92YWx1ZSApOwogICAgICB9CgogICAgICBpZiAodGhpcy5fc3RhdGUgPT09IFN0YXRlcy5SRUpFQ1RFRCkgewogICAgICAgIGhhbmRsZXJbMl0odGhpcy5fdmFsdWUpOwogICAgICB9CgogICAgICBoYW5kbGVyWzBdID0gdHJ1ZTsKICAgIH0pOwogIH07fQp9CgovKioKICogQ3JlYXRlcyBhbiBuZXcgUHJvbWlzZUJ1ZmZlciBvYmplY3Qgd2l0aCB0aGUgc3BlY2lmaWVkIGxpbWl0CiAqIEBwYXJhbSBsaW1pdCBtYXggbnVtYmVyIG9mIHByb21pc2VzIHRoYXQgY2FuIGJlIHN0b3JlZCBpbiB0aGUgYnVmZmVyCiAqLwpmdW5jdGlvbiBtYWtlUHJvbWlzZUJ1ZmZlcihsaW1pdCkgewogIGNvbnN0IGJ1ZmZlciA9IFtdOwoKICBmdW5jdGlvbiBpc1JlYWR5KCkgewogICAgcmV0dXJuIGxpbWl0ID09PSB1bmRlZmluZWQgfHwgYnVmZmVyLmxlbmd0aCA8IGxpbWl0OwogIH0KCiAgLyoqCiAgICogUmVtb3ZlIGEgcHJvbWlzZSBmcm9tIHRoZSBxdWV1ZS4KICAgKgogICAqIEBwYXJhbSB0YXNrIENhbiBiZSBhbnkgUHJvbWlzZUxpa2U8VD4KICAgKiBAcmV0dXJucyBSZW1vdmVkIHByb21pc2UuCiAgICovCiAgZnVuY3Rpb24gcmVtb3ZlKHRhc2spIHsKICAgIHJldHVybiBidWZmZXIuc3BsaWNlKGJ1ZmZlci5pbmRleE9mKHRhc2spLCAxKVswXTsKICB9CgogIC8qKgogICAqIEFkZCBhIHByb21pc2UgKHJlcHJlc2VudGluZyBhbiBpbi1mbGlnaHQgYWN0aW9uKSB0byB0aGUgcXVldWUsIGFuZCBzZXQgaXQgdG8gcmVtb3ZlIGl0c2VsZiBvbiBmdWxmaWxsbWVudC4KICAgKgogICAqIEBwYXJhbSB0YXNrUHJvZHVjZXIgQSBmdW5jdGlvbiBwcm9kdWNpbmcgYW55IFByb21pc2VMaWtlPFQ+OyBJbiBwcmV2aW91cyB2ZXJzaW9ucyB0aGlzIHVzZWQgdG8gYmUgYHRhc2s6CiAgICogICAgICAgIFByb21pc2VMaWtlPFQ+YCwgYnV0IHVuZGVyIHRoYXQgbW9kZWwsIFByb21pc2VzIHdlcmUgaW5zdGFudGx5IGNyZWF0ZWQgb24gdGhlIGNhbGwtc2l0ZSBhbmQgdGhlaXIgZXhlY3V0b3IKICAgKiAgICAgICAgZnVuY3Rpb25zIHRoZXJlZm9yZSByYW4gaW1tZWRpYXRlbHkuIFRodXMsIGV2ZW4gaWYgdGhlIGJ1ZmZlciB3YXMgZnVsbCwgdGhlIGFjdGlvbiBzdGlsbCBoYXBwZW5lZC4gQnkKICAgKiAgICAgICAgcmVxdWlyaW5nIHRoZSBwcm9taXNlIHRvIGJlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgd2UgY2FuIGRlZmVyIHByb21pc2UgY3JlYXRpb24gdW50aWwgYWZ0ZXIgdGhlIGJ1ZmZlcgogICAqICAgICAgICBsaW1pdCBjaGVjay4KICAgKiBAcmV0dXJucyBUaGUgb3JpZ2luYWwgcHJvbWlzZS4KICAgKi8KICBmdW5jdGlvbiBhZGQodGFza1Byb2R1Y2VyKSB7CiAgICBpZiAoIWlzUmVhZHkoKSkgewogICAgICByZXR1cm4gcmVqZWN0ZWRTeW5jUHJvbWlzZShuZXcgU2VudHJ5RXJyb3IoJ05vdCBhZGRpbmcgUHJvbWlzZSBiZWNhdXNlIGJ1ZmZlciBsaW1pdCB3YXMgcmVhY2hlZC4nKSk7CiAgICB9CgogICAgLy8gc3RhcnQgdGhlIHRhc2sgYW5kIGFkZCBpdHMgcHJvbWlzZSB0byB0aGUgcXVldWUKICAgIGNvbnN0IHRhc2sgPSB0YXNrUHJvZHVjZXIoKTsKICAgIGlmIChidWZmZXIuaW5kZXhPZih0YXNrKSA9PT0gLTEpIHsKICAgICAgYnVmZmVyLnB1c2godGFzayk7CiAgICB9CiAgICB2b2lkIHRhc2sKICAgICAgLnRoZW4oKCkgPT4gcmVtb3ZlKHRhc2spKQogICAgICAvLyBVc2UgYHRoZW4obnVsbCwgcmVqZWN0aW9uSGFuZGxlcilgIHJhdGhlciB0aGFuIGBjYXRjaChyZWplY3Rpb25IYW5kbGVyKWAgc28gdGhhdCB3ZSBjYW4gdXNlIGBQcm9taXNlTGlrZWAKICAgICAgLy8gcmF0aGVyIHRoYW4gYFByb21pc2VgLiBgUHJvbWlzZUxpa2VgIGRvZXNuJ3QgaGF2ZSBhIGAuY2F0Y2hgIG1ldGhvZCwgbWFraW5nIGl0cyBwb2x5ZmlsbCBzbWFsbGVyLiAoRVM1IGRpZG4ndAogICAgICAvLyBoYXZlIHByb21pc2VzLCBzbyBUUyBoYXMgdG8gcG9seWZpbGwgd2hlbiBkb3duLWNvbXBpbGluZy4pCiAgICAgIC50aGVuKG51bGwsICgpID0+CiAgICAgICAgcmVtb3ZlKHRhc2spLnRoZW4obnVsbCwgKCkgPT4gewogICAgICAgICAgLy8gV2UgaGF2ZSB0byBhZGQgYW5vdGhlciBjYXRjaCBoZXJlIGJlY2F1c2UgYHJlbW92ZSgpYCBzdGFydHMgYSBuZXcgcHJvbWlzZSBjaGFpbi4KICAgICAgICB9KSwKICAgICAgKTsKICAgIHJldHVybiB0YXNrOwogIH0KCiAgLyoqCiAgICogV2FpdCBmb3IgYWxsIHByb21pc2VzIGluIHRoZSBxdWV1ZSB0byByZXNvbHZlIG9yIGZvciB0aW1lb3V0IHRvIGV4cGlyZSwgd2hpY2hldmVyIGNvbWVzIGZpcnN0LgogICAqCiAgICogQHBhcmFtIHRpbWVvdXQgVGhlIHRpbWUsIGluIG1zLCBhZnRlciB3aGljaCB0byByZXNvbHZlIHRvIGBmYWxzZWAgaWYgdGhlIHF1ZXVlIGlzIHN0aWxsIG5vbi1lbXB0eS4gUGFzc2luZyBgMGAgKG9yCiAgICogbm90IHBhc3NpbmcgYW55dGhpbmcpIHdpbGwgbWFrZSB0aGUgcHJvbWlzZSB3YWl0IGFzIGxvbmcgYXMgaXQgdGFrZXMgZm9yIHRoZSBxdWV1ZSB0byBkcmFpbiBiZWZvcmUgcmVzb2x2aW5nIHRvCiAgICogYHRydWVgLgogICAqIEByZXR1cm5zIEEgcHJvbWlzZSB3aGljaCB3aWxsIHJlc29sdmUgdG8gYHRydWVgIGlmIHRoZSBxdWV1ZSBpcyBhbHJlYWR5IGVtcHR5IG9yIGRyYWlucyBiZWZvcmUgdGhlIHRpbWVvdXQsIGFuZAogICAqIGBmYWxzZWAgb3RoZXJ3aXNlCiAgICovCiAgZnVuY3Rpb24gZHJhaW4odGltZW91dCkgewogICAgcmV0dXJuIG5ldyBTeW5jUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAgIGxldCBjb3VudGVyID0gYnVmZmVyLmxlbmd0aDsKCiAgICAgIGlmICghY291bnRlcikgewogICAgICAgIHJldHVybiByZXNvbHZlKHRydWUpOwogICAgICB9CgogICAgICAvLyB3YWl0IGZvciBgdGltZW91dGAgbXMgYW5kIHRoZW4gcmVzb2x2ZSB0byBgZmFsc2VgIChpZiBub3QgY2FuY2VsbGVkIGZpcnN0KQogICAgICBjb25zdCBjYXB0dXJlZFNldFRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHsKICAgICAgICBpZiAodGltZW91dCAmJiB0aW1lb3V0ID4gMCkgewogICAgICAgICAgcmVzb2x2ZShmYWxzZSk7CiAgICAgICAgfQogICAgICB9LCB0aW1lb3V0KTsKCiAgICAgIC8vIGlmIGFsbCBwcm9taXNlcyByZXNvbHZlIGluIHRpbWUsIGNhbmNlbCB0aGUgdGltZXIgYW5kIHJlc29sdmUgdG8gYHRydWVgCiAgICAgIGJ1ZmZlci5mb3JFYWNoKGl0ZW0gPT4gewogICAgICAgIHZvaWQgcmVzb2x2ZWRTeW5jUHJvbWlzZShpdGVtKS50aGVuKCgpID0+IHsKICAgICAgICAgIGlmICghLS1jb3VudGVyKSB7CiAgICAgICAgICAgIGNsZWFyVGltZW91dChjYXB0dXJlZFNldFRpbWVvdXQpOwogICAgICAgICAgICByZXNvbHZlKHRydWUpOwogICAgICAgICAgfQogICAgICAgIH0sIHJlamVjdCk7CiAgICAgIH0pOwogICAgfSk7CiAgfQoKICByZXR1cm4gewogICAgJDogYnVmZmVyLAogICAgYWRkLAogICAgZHJhaW4sCiAgfTsKfQoKLyoqCiAqIFRoaXMgY29kZSB3YXMgb3JpZ2luYWxseSBjb3BpZWQgZnJvbSB0aGUgJ2Nvb2tpZWAgbW9kdWxlIGF0IHYwLjUuMCBhbmQgd2FzIHNpbXBsaWZpZWQgZm9yIG91ciB1c2UgY2FzZS4KICogaHR0cHM6Ly9naXRodWIuY29tL2pzaHR0cC9jb29raWUvYmxvYi9hMGM4NDE0N2FhYjYyNjZiZGIzOTk2Y2Y0MDYyZTkzOTA3YzBiMGZjL2luZGV4LmpzCiAqIEl0IGhhZCB0aGUgZm9sbG93aW5nIGxpY2Vuc2U6CiAqCiAqIChUaGUgTUlUIExpY2Vuc2UpCiAqCiAqIENvcHlyaWdodCAoYykgMjAxMi0yMDE0IFJvbWFuIFNodHlsbWFuIDxzaHR5bG1hbkBnbWFpbC5jb20+CiAqIENvcHlyaWdodCAoYykgMjAxNSBEb3VnbGFzIENocmlzdG9waGVyIFdpbHNvbiA8ZG91Z0Bzb21ldGhpbmdkb3VnLmNvbT4KICoKICogUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nCiAqIGEgY29weSBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZQogKiAnU29mdHdhcmUnKSwgdG8gZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nCiAqIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwKICogZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvCiAqIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0bwogKiB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6CiAqCiAqIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlCiAqIGluY2x1ZGVkIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLgogKgogKiBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgJ0FTIElTJywgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwKICogRVhQUkVTUyBPUiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GCiAqIE1FUkNIQU5UQUJJTElUWSwgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4KICogSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkKICogQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwKICogVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUKICogU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUgU09GVFdBUkUuCiAqLwoKLyoqCiAqIFBhcnNlcyBhIGNvb2tpZSBzdHJpbmcKICovCmZ1bmN0aW9uIHBhcnNlQ29va2llKHN0cikgewogIGNvbnN0IG9iaiA9IHt9OwogIGxldCBpbmRleCA9IDA7CgogIHdoaWxlIChpbmRleCA8IHN0ci5sZW5ndGgpIHsKICAgIGNvbnN0IGVxSWR4ID0gc3RyLmluZGV4T2YoJz0nLCBpbmRleCk7CgogICAgLy8gbm8gbW9yZSBjb29raWUgcGFpcnMKICAgIGlmIChlcUlkeCA9PT0gLTEpIHsKICAgICAgYnJlYWs7CiAgICB9CgogICAgbGV0IGVuZElkeCA9IHN0ci5pbmRleE9mKCc7JywgaW5kZXgpOwoKICAgIGlmIChlbmRJZHggPT09IC0xKSB7CiAgICAgIGVuZElkeCA9IHN0ci5sZW5ndGg7CiAgICB9IGVsc2UgaWYgKGVuZElkeCA8IGVxSWR4KSB7CiAgICAgIC8vIGJhY2t0cmFjayBvbiBwcmlvciBzZW1pY29sb24KICAgICAgaW5kZXggPSBzdHIubGFzdEluZGV4T2YoJzsnLCBlcUlkeCAtIDEpICsgMTsKICAgICAgY29udGludWU7CiAgICB9CgogICAgY29uc3Qga2V5ID0gc3RyLnNsaWNlKGluZGV4LCBlcUlkeCkudHJpbSgpOwoKICAgIC8vIG9ubHkgYXNzaWduIG9uY2UKICAgIGlmICh1bmRlZmluZWQgPT09IG9ialtrZXldKSB7CiAgICAgIGxldCB2YWwgPSBzdHIuc2xpY2UoZXFJZHggKyAxLCBlbmRJZHgpLnRyaW0oKTsKCiAgICAgIC8vIHF1b3RlZCB2YWx1ZXMKICAgICAgaWYgKHZhbC5jaGFyQ29kZUF0KDApID09PSAweDIyKSB7CiAgICAgICAgdmFsID0gdmFsLnNsaWNlKDEsIC0xKTsKICAgICAgfQoKICAgICAgdHJ5IHsKICAgICAgICBvYmpba2V5XSA9IHZhbC5pbmRleE9mKCclJykgIT09IC0xID8gZGVjb2RlVVJJQ29tcG9uZW50KHZhbCkgOiB2YWw7CiAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICBvYmpba2V5XSA9IHZhbDsKICAgICAgfQogICAgfQoKICAgIGluZGV4ID0gZW5kSWR4ICsgMTsKICB9CgogIHJldHVybiBvYmo7Cn0KCi8qKgogKiBTdHJpcCB0aGUgcXVlcnkgc3RyaW5nIGFuZCBmcmFnbWVudCBvZmYgb2YgYSBnaXZlbiBVUkwgb3IgcGF0aCAoaWYgcHJlc2VudCkKICoKICogQHBhcmFtIHVybFBhdGggRnVsbCBVUkwgb3IgcGF0aCwgaW5jbHVkaW5nIHBvc3NpYmxlIHF1ZXJ5IHN0cmluZyBhbmQvb3IgZnJhZ21lbnQKICogQHJldHVybnMgVVJMIG9yIHBhdGggd2l0aG91dCBxdWVyeSBzdHJpbmcgb3IgZnJhZ21lbnQKICovCmZ1bmN0aW9uIHN0cmlwVXJsUXVlcnlBbmRGcmFnbWVudCh1cmxQYXRoKSB7CiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVzZWxlc3MtZXNjYXBlCiAgcmV0dXJuIHVybFBhdGguc3BsaXQoL1tcPyNdLywgMSlbMF07Cn0KCmNvbnN0IERFRkFVTFRfSU5DTFVERVMgPSB7CiAgaXA6IGZhbHNlLAogIHJlcXVlc3Q6IHRydWUsCiAgdHJhbnNhY3Rpb246IHRydWUsCiAgdXNlcjogdHJ1ZSwKfTsKY29uc3QgREVGQVVMVF9SRVFVRVNUX0lOQ0xVREVTID0gWydjb29raWVzJywgJ2RhdGEnLCAnaGVhZGVycycsICdtZXRob2QnLCAncXVlcnlfc3RyaW5nJywgJ3VybCddOwpjb25zdCBERUZBVUxUX1VTRVJfSU5DTFVERVMgPSBbJ2lkJywgJ3VzZXJuYW1lJywgJ2VtYWlsJ107CgovKioKICogRXh0cmFjdHMgYSBjb21wbGV0ZSBhbmQgcGFyYW1ldGVyaXplZCBwYXRoIGZyb20gdGhlIHJlcXVlc3Qgb2JqZWN0IGFuZCB1c2VzIGl0IHRvIGNvbnN0cnVjdCB0cmFuc2FjdGlvbiBuYW1lLgogKiBJZiB0aGUgcGFyYW1ldGVyaXplZCB0cmFuc2FjdGlvbiBuYW1lIGNhbm5vdCBiZSBleHRyYWN0ZWQsIHdlIGZhbGwgYmFjayB0byB0aGUgcmF3IFVSTC4KICoKICogQWRkaXRpb25hbGx5LCB0aGlzIGZ1bmN0aW9uIGRldGVybWluZXMgYW5kIHJldHVybnMgdGhlIHRyYW5zYWN0aW9uIG5hbWUgc291cmNlCiAqCiAqIGVnLiBHRVQgL21vdW50cG9pbnQvdXNlci86aWQKICoKICogQHBhcmFtIHJlcSBBIHJlcXVlc3Qgb2JqZWN0CiAqIEBwYXJhbSBvcHRpb25zIFdoYXQgdG8gaW5jbHVkZSBpbiB0aGUgdHJhbnNhY3Rpb24gbmFtZSAobWV0aG9kLCBwYXRoLCBvciBhIGN1c3RvbSByb3V0ZSBuYW1lIHRvIGJlCiAqICAgICAgICAgICAgICAgIHVzZWQgaW5zdGVhZCBvZiB0aGUgcmVxdWVzdCdzIHJvdXRlKQogKgogKiBAcmV0dXJucyBBIHR1cGxlIG9mIHRoZSBmdWxseSBjb25zdHJ1Y3RlZCB0cmFuc2FjdGlvbiBuYW1lIFswXSBhbmQgaXRzIHNvdXJjZSBbMV0gKGNhbiBiZSBlaXRoZXIgJ3JvdXRlJyBvciAndXJsJykKICovCmZ1bmN0aW9uIGV4dHJhY3RQYXRoRm9yVHJhbnNhY3Rpb24oCiAgcmVxLAogIG9wdGlvbnMgPSB7fSwKKSB7CiAgY29uc3QgbWV0aG9kID0gcmVxLm1ldGhvZCAmJiByZXEubWV0aG9kLnRvVXBwZXJDYXNlKCk7CgogIGxldCBwYXRoID0gJyc7CiAgbGV0IHNvdXJjZSA9ICd1cmwnOwoKICAvLyBDaGVjayB0byBzZWUgaWYgdGhlcmUncyBhIHBhcmFtZXRlcml6ZWQgcm91dGUgd2UgY2FuIHVzZSAoYXMgdGhlcmUgaXMgaW4gRXhwcmVzcykKICBpZiAob3B0aW9ucy5jdXN0b21Sb3V0ZSB8fCByZXEucm91dGUpIHsKICAgIHBhdGggPSBvcHRpb25zLmN1c3RvbVJvdXRlIHx8IGAke3JlcS5iYXNlVXJsIHx8ICcnfSR7cmVxLnJvdXRlICYmIHJlcS5yb3V0ZS5wYXRofWA7CiAgICBzb3VyY2UgPSAncm91dGUnOwogIH0KCiAgLy8gT3RoZXJ3aXNlLCBqdXN0IHRha2UgdGhlIG9yaWdpbmFsIFVSTAogIGVsc2UgaWYgKHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsKSB7CiAgICBwYXRoID0gc3RyaXBVcmxRdWVyeUFuZEZyYWdtZW50KHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsIHx8ICcnKTsKICB9CgogIGxldCBuYW1lID0gJyc7CiAgaWYgKG9wdGlvbnMubWV0aG9kICYmIG1ldGhvZCkgewogICAgbmFtZSArPSBtZXRob2Q7CiAgfQogIGlmIChvcHRpb25zLm1ldGhvZCAmJiBvcHRpb25zLnBhdGgpIHsKICAgIG5hbWUgKz0gJyAnOwogIH0KICBpZiAob3B0aW9ucy5wYXRoICYmIHBhdGgpIHsKICAgIG5hbWUgKz0gcGF0aDsKICB9CgogIHJldHVybiBbbmFtZSwgc291cmNlXTsKfQoKLyoqIEpTRG9jICovCmZ1bmN0aW9uIGV4dHJhY3RUcmFuc2FjdGlvbihyZXEsIHR5cGUpIHsKICBzd2l0Y2ggKHR5cGUpIHsKICAgIGNhc2UgJ3BhdGgnOiB7CiAgICAgIHJldHVybiBleHRyYWN0UGF0aEZvclRyYW5zYWN0aW9uKHJlcSwgeyBwYXRoOiB0cnVlIH0pWzBdOwogICAgfQogICAgY2FzZSAnaGFuZGxlcic6IHsKICAgICAgcmV0dXJuIChyZXEucm91dGUgJiYgcmVxLnJvdXRlLnN0YWNrICYmIHJlcS5yb3V0ZS5zdGFja1swXSAmJiByZXEucm91dGUuc3RhY2tbMF0ubmFtZSkgfHwgJzxhbm9ueW1vdXM+JzsKICAgIH0KICAgIGNhc2UgJ21ldGhvZFBhdGgnOgogICAgZGVmYXVsdDogewogICAgICAvLyBpZiBleGlzdCBfcmVjb25zdHJ1Y3RlZFJvdXRlIHJldHVybiB0aGF0IHBhdGggaW5zdGVhZCBvZiByb3V0ZS5wYXRoCiAgICAgIGNvbnN0IGN1c3RvbVJvdXRlID0gcmVxLl9yZWNvbnN0cnVjdGVkUm91dGUgPyByZXEuX3JlY29uc3RydWN0ZWRSb3V0ZSA6IHVuZGVmaW5lZDsKICAgICAgcmV0dXJuIGV4dHJhY3RQYXRoRm9yVHJhbnNhY3Rpb24ocmVxLCB7IHBhdGg6IHRydWUsIG1ldGhvZDogdHJ1ZSwgY3VzdG9tUm91dGUgfSlbMF07CiAgICB9CiAgfQp9CgovKiogSlNEb2MgKi8KZnVuY3Rpb24gZXh0cmFjdFVzZXJEYXRhKAogIHVzZXIKCiwKICBrZXlzLAopIHsKICBjb25zdCBleHRyYWN0ZWRVc2VyID0ge307CiAgY29uc3QgYXR0cmlidXRlcyA9IEFycmF5LmlzQXJyYXkoa2V5cykgPyBrZXlzIDogREVGQVVMVF9VU0VSX0lOQ0xVREVTOwoKICBhdHRyaWJ1dGVzLmZvckVhY2goa2V5ID0+IHsKICAgIGlmICh1c2VyICYmIGtleSBpbiB1c2VyKSB7CiAgICAgIGV4dHJhY3RlZFVzZXJba2V5XSA9IHVzZXJba2V5XTsKICAgIH0KICB9KTsKCiAgcmV0dXJuIGV4dHJhY3RlZFVzZXI7Cn0KCi8qKgogKiBOb3JtYWxpemUgZGF0YSBmcm9tIHRoZSByZXF1ZXN0IG9iamVjdCwgYWNjb3VudGluZyBmb3IgZnJhbWV3b3JrIGRpZmZlcmVuY2VzLgogKgogKiBAcGFyYW0gcmVxIFRoZSByZXF1ZXN0IG9iamVjdCBmcm9tIHdoaWNoIHRvIGV4dHJhY3QgZGF0YQogKiBAcGFyYW0gb3B0aW9ucy5pbmNsdWRlIEFuIG9wdGlvbmFsIGFycmF5IG9mIGtleXMgdG8gaW5jbHVkZSBpbiB0aGUgbm9ybWFsaXplZCBkYXRhLiBEZWZhdWx0cyB0bwogKiBERUZBVUxUX1JFUVVFU1RfSU5DTFVERVMgaWYgbm90IHByb3ZpZGVkLgogKiBAcGFyYW0gb3B0aW9ucy5kZXBzIEluamVjdGVkLCBwbGF0Zm9ybS1zcGVjaWZpYyBkZXBlbmRlbmNpZXMKICogQHJldHVybnMgQW4gb2JqZWN0IGNvbnRhaW5pbmcgbm9ybWFsaXplZCByZXF1ZXN0IGRhdGEKICovCmZ1bmN0aW9uIGV4dHJhY3RSZXF1ZXN0RGF0YSgKICByZXEsCiAgb3B0aW9ucwoKLAopIHsKICBjb25zdCB7IGluY2x1ZGUgPSBERUZBVUxUX1JFUVVFU1RfSU5DTFVERVMgfSA9IG9wdGlvbnMgfHwge307CiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkKICBjb25zdCByZXF1ZXN0RGF0YSA9IHt9OwoKICAvLyBoZWFkZXJzOgogIC8vICAgbm9kZSwgZXhwcmVzcywga29hLCBuZXh0anM6IHJlcS5oZWFkZXJzCiAgY29uc3QgaGVhZGVycyA9IChyZXEuaGVhZGVycyB8fCB7fSkKCjsKICAvLyBtZXRob2Q6CiAgLy8gICBub2RlLCBleHByZXNzLCBrb2EsIG5leHRqczogcmVxLm1ldGhvZAogIGNvbnN0IG1ldGhvZCA9IHJlcS5tZXRob2Q7CiAgLy8gaG9zdDoKICAvLyAgIGV4cHJlc3M6IHJlcS5ob3N0bmFtZSBpbiA+IDQgYW5kIHJlcS5ob3N0IGluIDwgNAogIC8vICAga29hOiByZXEuaG9zdAogIC8vICAgbm9kZSwgbmV4dGpzOiByZXEuaGVhZGVycy5ob3N0CiAgLy8gRXhwcmVzcyA0IG1pc3Rha2VubHkgc3RyaXBzIG9mZiBwb3J0IG51bWJlciBmcm9tIHJlcS5ob3N0IC8gcmVxLmhvc3RuYW1lIHNvIHdlIGNhbid0IHJlbHkgb24gdGhlbQogIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2V4cHJlc3Nqcy9leHByZXNzL2lzc3Vlcy8zMDQ3I2lzc3VlY29tbWVudC0yMzY2NTMyMjMKICAvLyBBbHNvOiBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0L2lzc3Vlcy8xOTE3CiAgY29uc3QgaG9zdCA9IGhlYWRlcnMuaG9zdCB8fCByZXEuaG9zdG5hbWUgfHwgcmVxLmhvc3QgfHwgJzxubyBob3N0Pic7CiAgLy8gcHJvdG9jb2w6CiAgLy8gICBub2RlLCBuZXh0anM6IDxuL2E+CiAgLy8gICBleHByZXNzLCBrb2E6IHJlcS5wcm90b2NvbAogIGNvbnN0IHByb3RvY29sID0gcmVxLnByb3RvY29sID09PSAnaHR0cHMnIHx8IChyZXEuc29ja2V0ICYmIHJlcS5zb2NrZXQuZW5jcnlwdGVkKSA/ICdodHRwcycgOiAnaHR0cCc7CiAgLy8gdXJsIChpbmNsdWRpbmcgcGF0aCBhbmQgcXVlcnkgc3RyaW5nKToKICAvLyAgIG5vZGUsIGV4cHJlc3M6IHJlcS5vcmlnaW5hbFVybAogIC8vICAga29hLCBuZXh0anM6IHJlcS51cmwKICBjb25zdCBvcmlnaW5hbFVybCA9IHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsIHx8ICcnOwogIC8vIGFic29sdXRlIHVybAogIGNvbnN0IGFic29sdXRlVXJsID0gb3JpZ2luYWxVcmwuc3RhcnRzV2l0aChwcm90b2NvbCkgPyBvcmlnaW5hbFVybCA6IGAke3Byb3RvY29sfTovLyR7aG9zdH0ke29yaWdpbmFsVXJsfWA7CiAgaW5jbHVkZS5mb3JFYWNoKGtleSA9PiB7CiAgICBzd2l0Y2ggKGtleSkgewogICAgICBjYXNlICdoZWFkZXJzJzogewogICAgICAgIHJlcXVlc3REYXRhLmhlYWRlcnMgPSBoZWFkZXJzOwoKICAgICAgICAvLyBSZW1vdmUgdGhlIENvb2tpZSBoZWFkZXIgaW4gY2FzZSBjb29raWUgZGF0YSBzaG91bGQgbm90IGJlIGluY2x1ZGVkIGluIHRoZSBldmVudAogICAgICAgIGlmICghaW5jbHVkZS5pbmNsdWRlcygnY29va2llcycpKSB7CiAgICAgICAgICBkZWxldGUgKHJlcXVlc3REYXRhLmhlYWRlcnMgKS5jb29raWU7CiAgICAgICAgfQoKICAgICAgICBicmVhazsKICAgICAgfQogICAgICBjYXNlICdtZXRob2QnOiB7CiAgICAgICAgcmVxdWVzdERhdGEubWV0aG9kID0gbWV0aG9kOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICAgIGNhc2UgJ3VybCc6IHsKICAgICAgICByZXF1ZXN0RGF0YS51cmwgPSBhYnNvbHV0ZVVybDsKICAgICAgICBicmVhazsKICAgICAgfQogICAgICBjYXNlICdjb29raWVzJzogewogICAgICAgIC8vIGNvb2tpZXM6CiAgICAgICAgLy8gICBub2RlLCBleHByZXNzLCBrb2E6IHJlcS5oZWFkZXJzLmNvb2tpZQogICAgICAgIC8vICAgdmVyY2VsLCBzYWlscy5qcywgZXhwcmVzcyAody8gY29va2llIG1pZGRsZXdhcmUpLCBuZXh0anM6IHJlcS5jb29raWVzCiAgICAgICAgcmVxdWVzdERhdGEuY29va2llcyA9CiAgICAgICAgICAvLyBUT0RPICh2OCAvICM1MjU3KTogV2UncmUgb25seSBzZW5kaW5nIHRoZSBlbXB0eSBvYmplY3QgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5LCBzbyB0aGUgbGFzdCBiaXQgY2FuCiAgICAgICAgICAvLyBjb21lIG9mZiBpbiB2OAogICAgICAgICAgcmVxLmNvb2tpZXMgfHwgKGhlYWRlcnMuY29va2llICYmIHBhcnNlQ29va2llKGhlYWRlcnMuY29va2llKSkgfHwge307CiAgICAgICAgYnJlYWs7CiAgICAgIH0KICAgICAgY2FzZSAncXVlcnlfc3RyaW5nJzogewogICAgICAgIC8vIHF1ZXJ5IHN0cmluZzoKICAgICAgICAvLyAgIG5vZGU6IHJlcS51cmwgKHJhdykKICAgICAgICAvLyAgIGV4cHJlc3MsIGtvYSwgbmV4dGpzOiByZXEucXVlcnkKICAgICAgICByZXF1ZXN0RGF0YS5xdWVyeV9zdHJpbmcgPSBleHRyYWN0UXVlcnlQYXJhbXMocmVxKTsKICAgICAgICBicmVhazsKICAgICAgfQogICAgICBjYXNlICdkYXRhJzogewogICAgICAgIGlmIChtZXRob2QgPT09ICdHRVQnIHx8IG1ldGhvZCA9PT0gJ0hFQUQnKSB7CiAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICAgICAgLy8gYm9keSBkYXRhOgogICAgICAgIC8vICAgZXhwcmVzcywga29hLCBuZXh0anM6IHJlcS5ib2R5CiAgICAgICAgLy8KICAgICAgICAvLyAgIHdoZW4gdXNpbmcgbm9kZSBieSBpdHNlbGYsIHlvdSBoYXZlIHRvIHJlYWQgdGhlIGluY29taW5nIHN0cmVhbShzZWUKICAgICAgICAvLyAgIGh0dHBzOi8vbm9kZWpzLmRldi9sZWFybi9nZXQtaHR0cC1yZXF1ZXN0LWJvZHktZGF0YS11c2luZy1ub2RlanMpOyBpZiBhIHVzZXIgaXMgZG9pbmcgdGhhdCwgd2UgY2FuJ3Qga25vdwogICAgICAgIC8vICAgd2hlcmUgdGhleSdyZSBnb2luZyB0byBzdG9yZSB0aGUgZmluYWwgcmVzdWx0LCBzbyB0aGV5J2xsIGhhdmUgdG8gY2FwdHVyZSB0aGlzIGRhdGEgdGhlbXNlbHZlcwogICAgICAgIGlmIChyZXEuYm9keSAhPT0gdW5kZWZpbmVkKSB7CiAgICAgICAgICByZXF1ZXN0RGF0YS5kYXRhID0gaXNTdHJpbmcocmVxLmJvZHkpID8gcmVxLmJvZHkgOiBKU09OLnN0cmluZ2lmeShub3JtYWxpemUocmVxLmJvZHkpKTsKICAgICAgICB9CiAgICAgICAgYnJlYWs7CiAgICAgIH0KICAgICAgZGVmYXVsdDogewogICAgICAgIGlmICh7fS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHJlcSwga2V5KSkgewogICAgICAgICAgcmVxdWVzdERhdGFba2V5XSA9IChyZXEgKVtrZXldOwogICAgICAgIH0KICAgICAgfQogICAgfQogIH0pOwoKICByZXR1cm4gcmVxdWVzdERhdGE7Cn0KCi8qKgogKiBBZGQgZGF0YSBmcm9tIHRoZSBnaXZlbiByZXF1ZXN0IHRvIHRoZSBnaXZlbiBldmVudAogKgogKiBAcGFyYW0gZXZlbnQgVGhlIGV2ZW50IHRvIHdoaWNoIHRoZSByZXF1ZXN0IGRhdGEgd2lsbCBiZSBhZGRlZAogKiBAcGFyYW0gcmVxIFJlcXVlc3Qgb2JqZWN0CiAqIEBwYXJhbSBvcHRpb25zLmluY2x1ZGUgRmxhZ3MgdG8gY29udHJvbCB3aGF0IGRhdGEgaXMgaW5jbHVkZWQKICogQHBhcmFtIG9wdGlvbnMuZGVwcyBJbmplY3RlZCBwbGF0Zm9ybS1zcGVjaWZpYyBkZXBlbmRlbmNpZXMKICogQHJldHVybnMgVGhlIG11dGF0ZWQgYEV2ZW50YCBvYmplY3QKICovCmZ1bmN0aW9uIGFkZFJlcXVlc3REYXRhVG9FdmVudCgKICBldmVudCwKICByZXEsCiAgb3B0aW9ucywKKSB7CiAgY29uc3QgaW5jbHVkZSA9IHsKICAgIC4uLkRFRkFVTFRfSU5DTFVERVMsCiAgICAuLi4ob3B0aW9ucyAmJiBvcHRpb25zLmluY2x1ZGUpLAogIH07CgogIGlmIChpbmNsdWRlLnJlcXVlc3QpIHsKICAgIGNvbnN0IGV4dHJhY3RlZFJlcXVlc3REYXRhID0gQXJyYXkuaXNBcnJheShpbmNsdWRlLnJlcXVlc3QpCiAgICAgID8gZXh0cmFjdFJlcXVlc3REYXRhKHJlcSwgeyBpbmNsdWRlOiBpbmNsdWRlLnJlcXVlc3QgfSkKICAgICAgOiBleHRyYWN0UmVxdWVzdERhdGEocmVxKTsKCiAgICBldmVudC5yZXF1ZXN0ID0gewogICAgICAuLi5ldmVudC5yZXF1ZXN0LAogICAgICAuLi5leHRyYWN0ZWRSZXF1ZXN0RGF0YSwKICAgIH07CiAgfQoKICBpZiAoaW5jbHVkZS51c2VyKSB7CiAgICBjb25zdCBleHRyYWN0ZWRVc2VyID0gcmVxLnVzZXIgJiYgaXNQbGFpbk9iamVjdChyZXEudXNlcikgPyBleHRyYWN0VXNlckRhdGEocmVxLnVzZXIsIGluY2x1ZGUudXNlcikgOiB7fTsKCiAgICBpZiAoT2JqZWN0LmtleXMoZXh0cmFjdGVkVXNlcikubGVuZ3RoKSB7CiAgICAgIGV2ZW50LnVzZXIgPSB7CiAgICAgICAgLi4uZXZlbnQudXNlciwKICAgICAgICAuLi5leHRyYWN0ZWRVc2VyLAogICAgICB9OwogICAgfQogIH0KCiAgLy8gY2xpZW50IGlwOgogIC8vICAgbm9kZSwgbmV4dGpzOiByZXEuc29ja2V0LnJlbW90ZUFkZHJlc3MKICAvLyAgIGV4cHJlc3MsIGtvYTogcmVxLmlwCiAgaWYgKGluY2x1ZGUuaXApIHsKICAgIGNvbnN0IGlwID0gcmVxLmlwIHx8IChyZXEuc29ja2V0ICYmIHJlcS5zb2NrZXQucmVtb3RlQWRkcmVzcyk7CiAgICBpZiAoaXApIHsKICAgICAgZXZlbnQudXNlciA9IHsKICAgICAgICAuLi5ldmVudC51c2VyLAogICAgICAgIGlwX2FkZHJlc3M6IGlwLAogICAgICB9OwogICAgfQogIH0KCiAgaWYgKGluY2x1ZGUudHJhbnNhY3Rpb24gJiYgIWV2ZW50LnRyYW5zYWN0aW9uKSB7CiAgICAvLyBUT0RPIGRvIHdlIGV2ZW4gbmVlZCB0aGlzIGFueW1vcmU/CiAgICAvLyBUT0RPIG1ha2UgdGhpcyB3b3JrIGZvciBuZXh0anMKICAgIGV2ZW50LnRyYW5zYWN0aW9uID0gZXh0cmFjdFRyYW5zYWN0aW9uKHJlcSwgaW5jbHVkZS50cmFuc2FjdGlvbik7CiAgfQoKICByZXR1cm4gZXZlbnQ7Cn0KCmZ1bmN0aW9uIGV4dHJhY3RRdWVyeVBhcmFtcyhyZXEpIHsKICAvLyB1cmwgKGluY2x1ZGluZyBwYXRoIGFuZCBxdWVyeSBzdHJpbmcpOgogIC8vICAgbm9kZSwgZXhwcmVzczogcmVxLm9yaWdpbmFsVXJsCiAgLy8gICBrb2EsIG5leHRqczogcmVxLnVybAogIGxldCBvcmlnaW5hbFVybCA9IHJlcS5vcmlnaW5hbFVybCB8fCByZXEudXJsIHx8ICcnOwoKICBpZiAoIW9yaWdpbmFsVXJsKSB7CiAgICByZXR1cm47CiAgfQoKICAvLyBUaGUgYFVSTGAgY29uc3RydWN0b3IgY2FuJ3QgaGFuZGxlIGludGVybmFsIFVSTHMgb2YgdGhlIGZvcm0gYC9zb21lL3BhdGgvaGVyZWAsIHNvIHN0aWNrIGEgZHVtbXkgcHJvdG9jb2wgYW5kCiAgLy8gaG9zdG5hbWUgb24gdGhlIGJlZ2lubmluZy4gU2luY2UgdGhlIHBvaW50IGhlcmUgaXMganVzdCB0byBncmFiIHRoZSBxdWVyeSBzdHJpbmcsIGl0IGRvZXNuJ3QgbWF0dGVyIHdoYXQgd2UgdXNlLgogIGlmIChvcmlnaW5hbFVybC5zdGFydHNXaXRoKCcvJykpIHsKICAgIG9yaWdpbmFsVXJsID0gYGh0dHA6Ly9kb2dzLmFyZS5ncmVhdCR7b3JpZ2luYWxVcmx9YDsKICB9CgogIHRyeSB7CiAgICBjb25zdCBxdWVyeVBhcmFtcyA9IHJlcS5xdWVyeSB8fCBuZXcgVVJMKG9yaWdpbmFsVXJsKS5zZWFyY2guc2xpY2UoMSk7CiAgICByZXR1cm4gcXVlcnlQYXJhbXMubGVuZ3RoID8gcXVlcnlQYXJhbXMgOiB1bmRlZmluZWQ7CiAgfSBjYXRjaCAoZTIpIHsKICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQp9CgovKioKICogRG9lcyB0aGlzIGZpbGVuYW1lIGxvb2sgbGlrZSBpdCdzIHBhcnQgb2YgdGhlIGFwcCBjb2RlPwogKi8KZnVuY3Rpb24gZmlsZW5hbWVJc0luQXBwKGZpbGVuYW1lLCBpc05hdGl2ZSA9IGZhbHNlKSB7CiAgY29uc3QgaXNJbnRlcm5hbCA9CiAgICBpc05hdGl2ZSB8fAogICAgKGZpbGVuYW1lICYmCiAgICAgIC8vIEl0J3Mgbm90IGludGVybmFsIGlmIGl0J3MgYW4gYWJzb2x1dGUgbGludXggcGF0aAogICAgICAhZmlsZW5hbWUuc3RhcnRzV2l0aCgnLycpICYmCiAgICAgIC8vIEl0J3Mgbm90IGludGVybmFsIGlmIGl0J3MgYW4gYWJzb2x1dGUgd2luZG93cyBwYXRoCiAgICAgICFmaWxlbmFtZS5tYXRjaCgvXltBLVpdOi8pICYmCiAgICAgIC8vIEl0J3Mgbm90IGludGVybmFsIGlmIHRoZSBwYXRoIGlzIHN0YXJ0aW5nIHdpdGggYSBkb3QKICAgICAgIWZpbGVuYW1lLnN0YXJ0c1dpdGgoJy4nKSAmJgogICAgICAvLyBJdCdzIG5vdCBpbnRlcm5hbCBpZiB0aGUgZnJhbWUgaGFzIGEgcHJvdG9jb2wuIEluIG5vZGUsIHRoaXMgaXMgdXN1YWxseSB0aGUgY2FzZSBpZiB0aGUgZmlsZSBnb3QgcHJlLXByb2Nlc3NlZCB3aXRoIGEgYnVuZGxlciBsaWtlIHdlYnBhY2sKICAgICAgIWZpbGVuYW1lLm1hdGNoKC9eW2EtekEtWl0oW2EtekEtWjAtOS5cLStdKSo6XC9cLy8pKTsgLy8gU2NoZW1hIGZyb206IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8zNjQxNzgyCgogIC8vIGluX2FwcCBpcyBhbGwgdGhhdCdzIG5vdCBhbiBpbnRlcm5hbCBOb2RlIGZ1bmN0aW9uIG9yIGEgbW9kdWxlIHdpdGhpbiBub2RlX21vZHVsZXMKICAvLyBub3RlIHRoYXQgaXNOYXRpdmUgYXBwZWFycyB0byByZXR1cm4gdHJ1ZSBldmVuIGZvciBub2RlIGNvcmUgbGlicmFyaWVzCiAgLy8gc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9nZXRzZW50cnkvcmF2ZW4tbm9kZS9pc3N1ZXMvMTc2CgogIHJldHVybiAhaXNJbnRlcm5hbCAmJiBmaWxlbmFtZSAhPT0gdW5kZWZpbmVkICYmICFmaWxlbmFtZS5pbmNsdWRlcygnbm9kZV9tb2R1bGVzLycpOwp9Cgpjb25zdCBPTkVfU0VDT05EX0lOX01TID0gMTAwMDsKCi8qKgogKiBBIHBhcnRpYWwgZGVmaW5pdGlvbiBvZiB0aGUgW1BlcmZvcm1hbmNlIFdlYiBBUElde0BsaW5rIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9QZXJmb3JtYW5jZX0KICogZm9yIGFjY2Vzc2luZyBhIGhpZ2gtcmVzb2x1dGlvbiBtb25vdG9uaWMgY2xvY2suCiAqLwoKLyoqCiAqIFJldHVybnMgYSB0aW1lc3RhbXAgaW4gc2Vjb25kcyBzaW5jZSB0aGUgVU5JWCBlcG9jaCB1c2luZyB0aGUgRGF0ZSBBUEkuCiAqCiAqIFRPRE8odjgpOiBSZXR1cm4gdHlwZSBzaG91bGQgYmUgcm91bmRlZC4KICovCmZ1bmN0aW9uIGRhdGVUaW1lc3RhbXBJblNlY29uZHMoKSB7CiAgcmV0dXJuIERhdGUubm93KCkgLyBPTkVfU0VDT05EX0lOX01TOwp9CgovKioKICogUmV0dXJucyBhIHdyYXBwZXIgYXJvdW5kIHRoZSBuYXRpdmUgUGVyZm9ybWFuY2UgQVBJIGJyb3dzZXIgaW1wbGVtZW50YXRpb24sIG9yIHVuZGVmaW5lZCBmb3IgYnJvd3NlcnMgdGhhdCBkbyBub3QKICogc3VwcG9ydCB0aGUgQVBJLgogKgogKiBXcmFwcGluZyB0aGUgbmF0aXZlIEFQSSB3b3JrcyBhcm91bmQgZGlmZmVyZW5jZXMgaW4gYmVoYXZpb3IgZnJvbSBkaWZmZXJlbnQgYnJvd3NlcnMuCiAqLwpmdW5jdGlvbiBjcmVhdGVVbml4VGltZXN0YW1wSW5TZWNvbmRzRnVuYygpIHsKICBjb25zdCB7IHBlcmZvcm1hbmNlIH0gPSBHTE9CQUxfT0JKIDsKICBpZiAoIXBlcmZvcm1hbmNlIHx8ICFwZXJmb3JtYW5jZS5ub3cpIHsKICAgIHJldHVybiBkYXRlVGltZXN0YW1wSW5TZWNvbmRzOwogIH0KCiAgLy8gU29tZSBicm93c2VyIGFuZCBlbnZpcm9ubWVudHMgZG9uJ3QgaGF2ZSBhIHRpbWVPcmlnaW4sIHNvIHdlIGZhbGxiYWNrIHRvCiAgLy8gdXNpbmcgRGF0ZS5ub3coKSB0byBjb21wdXRlIHRoZSBzdGFydGluZyB0aW1lLgogIGNvbnN0IGFwcHJveFN0YXJ0aW5nVGltZU9yaWdpbiA9IERhdGUubm93KCkgLSBwZXJmb3JtYW5jZS5ub3coKTsKICBjb25zdCB0aW1lT3JpZ2luID0gcGVyZm9ybWFuY2UudGltZU9yaWdpbiA9PSB1bmRlZmluZWQgPyBhcHByb3hTdGFydGluZ1RpbWVPcmlnaW4gOiBwZXJmb3JtYW5jZS50aW1lT3JpZ2luOwoKICAvLyBwZXJmb3JtYW5jZS5ub3coKSBpcyBhIG1vbm90b25pYyBjbG9jaywgd2hpY2ggbWVhbnMgaXQgc3RhcnRzIGF0IDAgd2hlbiB0aGUgcHJvY2VzcyBiZWdpbnMuIFRvIGdldCB0aGUgY3VycmVudAogIC8vIHdhbGwgY2xvY2sgdGltZSAoYWN0dWFsIFVOSVggdGltZXN0YW1wKSwgd2UgbmVlZCB0byBhZGQgdGhlIHN0YXJ0aW5nIHRpbWUgb3JpZ2luIGFuZCB0aGUgY3VycmVudCB0aW1lIGVsYXBzZWQuCiAgLy8KICAvLyBUT0RPOiBUaGlzIGRvZXMgbm90IGFjY291bnQgZm9yIHRoZSBjYXNlIHdoZXJlIHRoZSBtb25vdG9uaWMgY2xvY2sgdGhhdCBwb3dlcnMgcGVyZm9ybWFuY2Uubm93KCkgZHJpZnRzIGZyb20gdGhlCiAgLy8gd2FsbCBjbG9jayB0aW1lLCB3aGljaCBjYXVzZXMgdGhlIHJldHVybmVkIHRpbWVzdGFtcCB0byBiZSBpbmFjY3VyYXRlLiBXZSBzaG91bGQgaW52ZXN0aWdhdGUgaG93IHRvIGRldGVjdCBhbmQKICAvLyBjb3JyZWN0IGZvciB0aGlzLgogIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdC9pc3N1ZXMvMjU5MAogIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL21kbi9jb250ZW50L2lzc3Vlcy80NzEzCiAgLy8gU2VlOiBodHRwczovL2Rldi50by9ub2Ftci93aGVuLWEtbWlsbGlzZWNvbmQtaXMtbm90LWEtbWlsbGlzZWNvbmQtM2g2CiAgcmV0dXJuICgpID0+IHsKICAgIHJldHVybiAodGltZU9yaWdpbiArIHBlcmZvcm1hbmNlLm5vdygpKSAvIE9ORV9TRUNPTkRfSU5fTVM7CiAgfTsKfQoKLyoqCiAqIFJldHVybnMgYSB0aW1lc3RhbXAgaW4gc2Vjb25kcyBzaW5jZSB0aGUgVU5JWCBlcG9jaCB1c2luZyBlaXRoZXIgdGhlIFBlcmZvcm1hbmNlIG9yIERhdGUgQVBJcywgZGVwZW5kaW5nIG9uIHRoZQogKiBhdmFpbGFiaWxpdHkgb2YgdGhlIFBlcmZvcm1hbmNlIEFQSS4KICoKICogQlVHOiBOb3RlIHRoYXQgYmVjYXVzZSBvZiBob3cgYnJvd3NlcnMgaW1wbGVtZW50IHRoZSBQZXJmb3JtYW5jZSBBUEksIHRoZSBjbG9jayBtaWdodCBzdG9wIHdoZW4gdGhlIGNvbXB1dGVyIGlzCiAqIGFzbGVlcC4gVGhpcyBjcmVhdGVzIGEgc2tldyBiZXR3ZWVuIGBkYXRlVGltZXN0YW1wSW5TZWNvbmRzYCBhbmQgYHRpbWVzdGFtcEluU2Vjb25kc2AuIFRoZQogKiBza2V3IGNhbiBncm93IHRvIGFyYml0cmFyeSBhbW91bnRzIGxpa2UgZGF5cywgd2Vla3Mgb3IgbW9udGhzLgogKiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdC9pc3N1ZXMvMjU5MC4KICovCmNvbnN0IHRpbWVzdGFtcEluU2Vjb25kcyA9IGNyZWF0ZVVuaXhUaW1lc3RhbXBJblNlY29uZHNGdW5jKCk7CgovKioKICogVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgc2luY2UgdGhlIFVOSVggZXBvY2guIFRoaXMgdmFsdWUgaXMgb25seSB1c2FibGUgaW4gYSBicm93c2VyLCBhbmQgb25seSB3aGVuIHRoZQogKiBwZXJmb3JtYW5jZSBBUEkgaXMgYXZhaWxhYmxlLgogKi8KKCgpID0+IHsKICAvLyBVbmZvcnR1bmF0ZWx5IGJyb3dzZXJzIG1heSByZXBvcnQgYW4gaW5hY2N1cmF0ZSB0aW1lIG9yaWdpbiBkYXRhLCB0aHJvdWdoIGVpdGhlciBwZXJmb3JtYW5jZS50aW1lT3JpZ2luIG9yCiAgLy8gcGVyZm9ybWFuY2UudGltaW5nLm5hdmlnYXRpb25TdGFydCwgd2hpY2ggcmVzdWx0cyBpbiBwb29yIHJlc3VsdHMgaW4gcGVyZm9ybWFuY2UgZGF0YS4gV2Ugb25seSB0cmVhdCB0aW1lIG9yaWdpbgogIC8vIGRhdGEgYXMgcmVsaWFibGUgaWYgdGhleSBhcmUgd2l0aGluIGEgcmVhc29uYWJsZSB0aHJlc2hvbGQgb2YgdGhlIGN1cnJlbnQgdGltZS4KCiAgY29uc3QgeyBwZXJmb3JtYW5jZSB9ID0gR0xPQkFMX09CSiA7CiAgaWYgKCFwZXJmb3JtYW5jZSB8fCAhcGVyZm9ybWFuY2Uubm93KSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgY29uc3QgdGhyZXNob2xkID0gMzYwMCAqIDEwMDA7CiAgY29uc3QgcGVyZm9ybWFuY2VOb3cgPSBwZXJmb3JtYW5jZS5ub3coKTsKICBjb25zdCBkYXRlTm93ID0gRGF0ZS5ub3coKTsKCiAgLy8gaWYgdGltZU9yaWdpbiBpc24ndCBhdmFpbGFibGUgc2V0IGRlbHRhIHRvIHRocmVzaG9sZCBzbyBpdCBpc24ndCB1c2VkCiAgY29uc3QgdGltZU9yaWdpbkRlbHRhID0gcGVyZm9ybWFuY2UudGltZU9yaWdpbgogICAgPyBNYXRoLmFicyhwZXJmb3JtYW5jZS50aW1lT3JpZ2luICsgcGVyZm9ybWFuY2VOb3cgLSBkYXRlTm93KQogICAgOiB0aHJlc2hvbGQ7CiAgY29uc3QgdGltZU9yaWdpbklzUmVsaWFibGUgPSB0aW1lT3JpZ2luRGVsdGEgPCB0aHJlc2hvbGQ7CgogIC8vIFdoaWxlIHBlcmZvcm1hbmNlLnRpbWluZy5uYXZpZ2F0aW9uU3RhcnQgaXMgZGVwcmVjYXRlZCBpbiBmYXZvciBvZiBwZXJmb3JtYW5jZS50aW1lT3JpZ2luLCBwZXJmb3JtYW5jZS50aW1lT3JpZ2luCiAgLy8gaXMgbm90IGFzIHdpZGVseSBzdXBwb3J0ZWQuIE5hbWVseSwgcGVyZm9ybWFuY2UudGltZU9yaWdpbiBpcyB1bmRlZmluZWQgaW4gU2FmYXJpIGFzIG9mIHdyaXRpbmcuCiAgLy8gQWxzbyBhcyBvZiB3cml0aW5nLCBwZXJmb3JtYW5jZS50aW1pbmcgaXMgbm90IGF2YWlsYWJsZSBpbiBXZWIgV29ya2VycyBpbiBtYWluc3RyZWFtIGJyb3dzZXJzLCBzbyBpdCBpcyBub3QgYWx3YXlzCiAgLy8gYSB2YWxpZCBmYWxsYmFjay4gSW4gdGhlIGFic2VuY2Ugb2YgYW4gaW5pdGlhbCB0aW1lIHByb3ZpZGVkIGJ5IHRoZSBicm93c2VyLCBmYWxsYmFjayB0byB0aGUgY3VycmVudCB0aW1lIGZyb20gdGhlCiAgLy8gRGF0ZSBBUEkuCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgY29uc3QgbmF2aWdhdGlvblN0YXJ0ID0gcGVyZm9ybWFuY2UudGltaW5nICYmIHBlcmZvcm1hbmNlLnRpbWluZy5uYXZpZ2F0aW9uU3RhcnQ7CiAgY29uc3QgaGFzTmF2aWdhdGlvblN0YXJ0ID0gdHlwZW9mIG5hdmlnYXRpb25TdGFydCA9PT0gJ251bWJlcic7CiAgLy8gaWYgbmF2aWdhdGlvblN0YXJ0IGlzbid0IGF2YWlsYWJsZSBzZXQgZGVsdGEgdG8gdGhyZXNob2xkIHNvIGl0IGlzbid0IHVzZWQKICBjb25zdCBuYXZpZ2F0aW9uU3RhcnREZWx0YSA9IGhhc05hdmlnYXRpb25TdGFydCA/IE1hdGguYWJzKG5hdmlnYXRpb25TdGFydCArIHBlcmZvcm1hbmNlTm93IC0gZGF0ZU5vdykgOiB0aHJlc2hvbGQ7CiAgY29uc3QgbmF2aWdhdGlvblN0YXJ0SXNSZWxpYWJsZSA9IG5hdmlnYXRpb25TdGFydERlbHRhIDwgdGhyZXNob2xkOwoKICBpZiAodGltZU9yaWdpbklzUmVsaWFibGUgfHwgbmF2aWdhdGlvblN0YXJ0SXNSZWxpYWJsZSkgewogICAgLy8gVXNlIHRoZSBtb3JlIHJlbGlhYmxlIHRpbWUgb3JpZ2luCiAgICBpZiAodGltZU9yaWdpbkRlbHRhIDw9IG5hdmlnYXRpb25TdGFydERlbHRhKSB7CiAgICAgIHJldHVybiBwZXJmb3JtYW5jZS50aW1lT3JpZ2luOwogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuIG5hdmlnYXRpb25TdGFydDsKICAgIH0KICB9CiAgcmV0dXJuIGRhdGVOb3c7Cn0pKCk7CgovKioKICogQ3JlYXRlcyBhbiBlbnZlbG9wZS4KICogTWFrZSBzdXJlIHRvIGFsd2F5cyBleHBsaWNpdGx5IHByb3ZpZGUgdGhlIGdlbmVyaWMgdG8gdGhpcyBmdW5jdGlvbgogKiBzbyB0aGF0IHRoZSBlbnZlbG9wZSB0eXBlcyByZXNvbHZlIGNvcnJlY3RseS4KICovCmZ1bmN0aW9uIGNyZWF0ZUVudmVsb3BlKGhlYWRlcnMsIGl0ZW1zID0gW10pIHsKICByZXR1cm4gW2hlYWRlcnMsIGl0ZW1zXSA7Cn0KCi8qKgogKiBDb252ZW5pZW5jZSBmdW5jdGlvbiB0byBsb29wIHRocm91Z2ggdGhlIGl0ZW1zIGFuZCBpdGVtIHR5cGVzIG9mIGFuIGVudmVsb3BlLgogKiAoVGhpcyBmdW5jdGlvbiB3YXMgbW9zdGx5IGNyZWF0ZWQgYmVjYXVzZSB3b3JraW5nIHdpdGggZW52ZWxvcGUgdHlwZXMgaXMgcGFpbmZ1bCBhdCB0aGUgbW9tZW50KQogKgogKiBJZiB0aGUgY2FsbGJhY2sgcmV0dXJucyB0cnVlLCB0aGUgcmVzdCBvZiB0aGUgaXRlbXMgd2lsbCBiZSBza2lwcGVkLgogKi8KZnVuY3Rpb24gZm9yRWFjaEVudmVsb3BlSXRlbSgKICBlbnZlbG9wZSwKICBjYWxsYmFjaywKKSB7CiAgY29uc3QgZW52ZWxvcGVJdGVtcyA9IGVudmVsb3BlWzFdOwoKICBmb3IgKGNvbnN0IGVudmVsb3BlSXRlbSBvZiBlbnZlbG9wZUl0ZW1zKSB7CiAgICBjb25zdCBlbnZlbG9wZUl0ZW1UeXBlID0gZW52ZWxvcGVJdGVtWzBdLnR5cGU7CiAgICBjb25zdCByZXN1bHQgPSBjYWxsYmFjayhlbnZlbG9wZUl0ZW0sIGVudmVsb3BlSXRlbVR5cGUpOwoKICAgIGlmIChyZXN1bHQpIHsKICAgICAgcmV0dXJuIHRydWU7CiAgICB9CiAgfQoKICByZXR1cm4gZmFsc2U7Cn0KCi8qKgogKiBFbmNvZGUgYSBzdHJpbmcgdG8gVVRGOCBhcnJheS4KICovCmZ1bmN0aW9uIGVuY29kZVVURjgoaW5wdXQpIHsKICByZXR1cm4gbmV3IFRleHRFbmNvZGVyKCkuZW5jb2RlKGlucHV0KTsKfQoKLyoqCiAqIFNlcmlhbGl6ZXMgYW4gZW52ZWxvcGUuCiAqLwpmdW5jdGlvbiBzZXJpYWxpemVFbnZlbG9wZShlbnZlbG9wZSkgewogIGNvbnN0IFtlbnZIZWFkZXJzLCBpdGVtc10gPSBlbnZlbG9wZTsKCiAgLy8gSW5pdGlhbGx5IHdlIGNvbnN0cnVjdCBvdXIgZW52ZWxvcGUgYXMgYSBzdHJpbmcgYW5kIG9ubHkgY29udmVydCB0byBiaW5hcnkgY2h1bmtzIGlmIHdlIGVuY291bnRlciBiaW5hcnkgZGF0YQogIGxldCBwYXJ0cyA9IEpTT04uc3RyaW5naWZ5KGVudkhlYWRlcnMpOwoKICBmdW5jdGlvbiBhcHBlbmQobmV4dCkgewogICAgaWYgKHR5cGVvZiBwYXJ0cyA9PT0gJ3N0cmluZycpIHsKICAgICAgcGFydHMgPSB0eXBlb2YgbmV4dCA9PT0gJ3N0cmluZycgPyBwYXJ0cyArIG5leHQgOiBbZW5jb2RlVVRGOChwYXJ0cyksIG5leHRdOwogICAgfSBlbHNlIHsKICAgICAgcGFydHMucHVzaCh0eXBlb2YgbmV4dCA9PT0gJ3N0cmluZycgPyBlbmNvZGVVVEY4KG5leHQpIDogbmV4dCk7CiAgICB9CiAgfQoKICBmb3IgKGNvbnN0IGl0ZW0gb2YgaXRlbXMpIHsKICAgIGNvbnN0IFtpdGVtSGVhZGVycywgcGF5bG9hZF0gPSBpdGVtOwoKICAgIGFwcGVuZChgXG4ke0pTT04uc3RyaW5naWZ5KGl0ZW1IZWFkZXJzKX1cbmApOwoKICAgIGlmICh0eXBlb2YgcGF5bG9hZCA9PT0gJ3N0cmluZycgfHwgcGF5bG9hZCBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkpIHsKICAgICAgYXBwZW5kKHBheWxvYWQpOwogICAgfSBlbHNlIHsKICAgICAgbGV0IHN0cmluZ2lmaWVkUGF5bG9hZDsKICAgICAgdHJ5IHsKICAgICAgICBzdHJpbmdpZmllZFBheWxvYWQgPSBKU09OLnN0cmluZ2lmeShwYXlsb2FkKTsKICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIC8vIEluIGNhc2UsIGRlc3BpdGUgYWxsIG91ciBlZmZvcnRzIHRvIGtlZXAgYHBheWxvYWRgIGNpcmN1bGFyLWRlcGVuZGVuY3ktZnJlZSwgYEpTT04uc3RyaW5pZnkoKWAgc3RpbGwKICAgICAgICAvLyBmYWlscywgd2UgdHJ5IGFnYWluIGFmdGVyIG5vcm1hbGl6aW5nIGl0IGFnYWluIHdpdGggaW5maW5pdGUgbm9ybWFsaXphdGlvbiBkZXB0aC4gVGhpcyBvZiBjb3Vyc2UgaGFzIGEKICAgICAgICAvLyBwZXJmb3JtYW5jZSBpbXBhY3QgYnV0IGluIHRoaXMgY2FzZSBhIHBlcmZvcm1hbmNlIGhpdCBpcyBiZXR0ZXIgdGhhbiB0aHJvd2luZy4KICAgICAgICBzdHJpbmdpZmllZFBheWxvYWQgPSBKU09OLnN0cmluZ2lmeShub3JtYWxpemUocGF5bG9hZCkpOwogICAgICB9CiAgICAgIGFwcGVuZChzdHJpbmdpZmllZFBheWxvYWQpOwogICAgfQogIH0KCiAgcmV0dXJuIHR5cGVvZiBwYXJ0cyA9PT0gJ3N0cmluZycgPyBwYXJ0cyA6IGNvbmNhdEJ1ZmZlcnMocGFydHMpOwp9CgpmdW5jdGlvbiBjb25jYXRCdWZmZXJzKGJ1ZmZlcnMpIHsKICBjb25zdCB0b3RhbExlbmd0aCA9IGJ1ZmZlcnMucmVkdWNlKChhY2MsIGJ1ZikgPT4gYWNjICsgYnVmLmxlbmd0aCwgMCk7CgogIGNvbnN0IG1lcmdlZCA9IG5ldyBVaW50OEFycmF5KHRvdGFsTGVuZ3RoKTsKICBsZXQgb2Zmc2V0ID0gMDsKICBmb3IgKGNvbnN0IGJ1ZmZlciBvZiBidWZmZXJzKSB7CiAgICBtZXJnZWQuc2V0KGJ1ZmZlciwgb2Zmc2V0KTsKICAgIG9mZnNldCArPSBidWZmZXIubGVuZ3RoOwogIH0KCiAgcmV0dXJuIG1lcmdlZDsKfQoKY29uc3QgSVRFTV9UWVBFX1RPX0RBVEFfQ0FURUdPUllfTUFQID0gewogIHNlc3Npb246ICdzZXNzaW9uJywKICBzZXNzaW9uczogJ3Nlc3Npb24nLAogIGF0dGFjaG1lbnQ6ICdhdHRhY2htZW50JywKICB0cmFuc2FjdGlvbjogJ3RyYW5zYWN0aW9uJywKICBldmVudDogJ2Vycm9yJywKICBjbGllbnRfcmVwb3J0OiAnaW50ZXJuYWwnLAogIHVzZXJfcmVwb3J0OiAnZGVmYXVsdCcsCiAgcHJvZmlsZTogJ3Byb2ZpbGUnLAogIHJlcGxheV9ldmVudDogJ3JlcGxheScsCiAgcmVwbGF5X3JlY29yZGluZzogJ3JlcGxheScsCiAgY2hlY2tfaW46ICdtb25pdG9yJywKICBmZWVkYmFjazogJ2ZlZWRiYWNrJywKICAvLyBUT0RPOiBUaGlzIGlzIGEgdGVtcG9yYXJ5IHdvcmthcm91bmQgdW50aWwgd2UgaGF2ZSBhIHByb3BlciBkYXRhIGNhdGVnb3J5IGZvciBtZXRyaWNzCiAgc3RhdHNkOiAndW5rbm93bicsCn07CgovKioKICogTWFwcyB0aGUgdHlwZSBvZiBhbiBlbnZlbG9wZSBpdGVtIHRvIGEgZGF0YSBjYXRlZ29yeS4KICovCmZ1bmN0aW9uIGVudmVsb3BlSXRlbVR5cGVUb0RhdGFDYXRlZ29yeSh0eXBlKSB7CiAgcmV0dXJuIElURU1fVFlQRV9UT19EQVRBX0NBVEVHT1JZX01BUFt0eXBlXTsKfQoKLyoqIEV4dHJhY3RzIHRoZSBtaW5pbWFsIFNESyBpbmZvIGZyb20gZnJvbSB0aGUgbWV0YWRhdGEgb3IgYW4gZXZlbnRzICovCmZ1bmN0aW9uIGdldFNka01ldGFkYXRhRm9yRW52ZWxvcGVIZWFkZXIobWV0YWRhdGFPckV2ZW50KSB7CiAgaWYgKCFtZXRhZGF0YU9yRXZlbnQgfHwgIW1ldGFkYXRhT3JFdmVudC5zZGspIHsKICAgIHJldHVybjsKICB9CiAgY29uc3QgeyBuYW1lLCB2ZXJzaW9uIH0gPSBtZXRhZGF0YU9yRXZlbnQuc2RrOwogIHJldHVybiB7IG5hbWUsIHZlcnNpb24gfTsKfQoKLyoqCiAqIENyZWF0ZXMgZXZlbnQgZW52ZWxvcGUgaGVhZGVycywgYmFzZWQgb24gZXZlbnQsIHNkayBpbmZvIGFuZCB0dW5uZWwKICogTm90ZTogVGhpcyBmdW5jdGlvbiB3YXMgZXh0cmFjdGVkIGZyb20gdGhlIGNvcmUgcGFja2FnZSB0byBtYWtlIGl0IGF2YWlsYWJsZSBpbiBSZXBsYXkKICovCmZ1bmN0aW9uIGNyZWF0ZUV2ZW50RW52ZWxvcGVIZWFkZXJzKAogIGV2ZW50LAogIHNka0luZm8sCiAgdHVubmVsLAogIGRzbiwKKSB7CiAgY29uc3QgZHluYW1pY1NhbXBsaW5nQ29udGV4dCA9IGV2ZW50LnNka1Byb2Nlc3NpbmdNZXRhZGF0YSAmJiBldmVudC5zZGtQcm9jZXNzaW5nTWV0YWRhdGEuZHluYW1pY1NhbXBsaW5nQ29udGV4dDsKICByZXR1cm4gewogICAgZXZlbnRfaWQ6IGV2ZW50LmV2ZW50X2lkICwKICAgIHNlbnRfYXQ6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSwKICAgIC4uLihzZGtJbmZvICYmIHsgc2RrOiBzZGtJbmZvIH0pLAogICAgLi4uKCEhdHVubmVsICYmIGRzbiAmJiB7IGRzbjogZHNuVG9TdHJpbmcoZHNuKSB9KSwKICAgIC4uLihkeW5hbWljU2FtcGxpbmdDb250ZXh0ICYmIHsKICAgICAgdHJhY2U6IGRyb3BVbmRlZmluZWRLZXlzKHsgLi4uZHluYW1pY1NhbXBsaW5nQ29udGV4dCB9KSwKICAgIH0pLAogIH07Cn0KCi8vIEludGVudGlvbmFsbHkga2VlcGluZyB0aGUga2V5IGJyb2FkLCBhcyB3ZSBkb24ndCBrbm93IGZvciBzdXJlIHdoYXQgcmF0ZSBsaW1pdCBoZWFkZXJzIGdldCByZXR1cm5lZCBmcm9tIGJhY2tlbmQKCmNvbnN0IERFRkFVTFRfUkVUUllfQUZURVIgPSA2MCAqIDEwMDA7IC8vIDYwIHNlY29uZHMKCi8qKgogKiBFeHRyYWN0cyBSZXRyeS1BZnRlciB2YWx1ZSBmcm9tIHRoZSByZXF1ZXN0IGhlYWRlciBvciByZXR1cm5zIGRlZmF1bHQgdmFsdWUKICogQHBhcmFtIGhlYWRlciBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgJ1JldHJ5LUFmdGVyJyBoZWFkZXIKICogQHBhcmFtIG5vdyBjdXJyZW50IHVuaXggdGltZXN0YW1wCiAqCiAqLwpmdW5jdGlvbiBwYXJzZVJldHJ5QWZ0ZXJIZWFkZXIoaGVhZGVyLCBub3cgPSBEYXRlLm5vdygpKSB7CiAgY29uc3QgaGVhZGVyRGVsYXkgPSBwYXJzZUludChgJHtoZWFkZXJ9YCwgMTApOwogIGlmICghaXNOYU4oaGVhZGVyRGVsYXkpKSB7CiAgICByZXR1cm4gaGVhZGVyRGVsYXkgKiAxMDAwOwogIH0KCiAgY29uc3QgaGVhZGVyRGF0ZSA9IERhdGUucGFyc2UoYCR7aGVhZGVyfWApOwogIGlmICghaXNOYU4oaGVhZGVyRGF0ZSkpIHsKICAgIHJldHVybiBoZWFkZXJEYXRlIC0gbm93OwogIH0KCiAgcmV0dXJuIERFRkFVTFRfUkVUUllfQUZURVI7Cn0KCi8qKgogKiBHZXRzIHRoZSB0aW1lIHRoYXQgdGhlIGdpdmVuIGNhdGVnb3J5IGlzIGRpc2FibGVkIHVudGlsIGZvciByYXRlIGxpbWl0aW5nLgogKiBJbiBjYXNlIG5vIGNhdGVnb3J5LXNwZWNpZmljIGxpbWl0IGlzIHNldCBidXQgYSBnZW5lcmFsIHJhdGUgbGltaXQgYWNyb3NzIGFsbCBjYXRlZ29yaWVzIGlzIGFjdGl2ZSwKICogdGhhdCB0aW1lIGlzIHJldHVybmVkLgogKgogKiBAcmV0dXJuIHRoZSB0aW1lIGluIG1zIHRoYXQgdGhlIGNhdGVnb3J5IGlzIGRpc2FibGVkIHVudGlsIG9yIDAgaWYgdGhlcmUncyBubyBhY3RpdmUgcmF0ZSBsaW1pdC4KICovCmZ1bmN0aW9uIGRpc2FibGVkVW50aWwobGltaXRzLCBjYXRlZ29yeSkgewogIHJldHVybiBsaW1pdHNbY2F0ZWdvcnldIHx8IGxpbWl0cy5hbGwgfHwgMDsKfQoKLyoqCiAqIENoZWNrcyBpZiBhIGNhdGVnb3J5IGlzIHJhdGUgbGltaXRlZAogKi8KZnVuY3Rpb24gaXNSYXRlTGltaXRlZChsaW1pdHMsIGNhdGVnb3J5LCBub3cgPSBEYXRlLm5vdygpKSB7CiAgcmV0dXJuIGRpc2FibGVkVW50aWwobGltaXRzLCBjYXRlZ29yeSkgPiBub3c7Cn0KCi8qKgogKiBVcGRhdGUgcmF0ZWxpbWl0cyBmcm9tIGluY29taW5nIGhlYWRlcnMuCiAqCiAqIEByZXR1cm4gdGhlIHVwZGF0ZWQgUmF0ZUxpbWl0cyBvYmplY3QuCiAqLwpmdW5jdGlvbiB1cGRhdGVSYXRlTGltaXRzKAogIGxpbWl0cywKICB7IHN0YXR1c0NvZGUsIGhlYWRlcnMgfSwKICBub3cgPSBEYXRlLm5vdygpLAopIHsKICBjb25zdCB1cGRhdGVkUmF0ZUxpbWl0cyA9IHsKICAgIC4uLmxpbWl0cywKICB9OwoKICAvLyAiVGhlIG5hbWUgaXMgY2FzZS1pbnNlbnNpdGl2ZS4iCiAgLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0hlYWRlcnMvZ2V0CiAgY29uc3QgcmF0ZUxpbWl0SGVhZGVyID0gaGVhZGVycyAmJiBoZWFkZXJzWyd4LXNlbnRyeS1yYXRlLWxpbWl0cyddOwogIGNvbnN0IHJldHJ5QWZ0ZXJIZWFkZXIgPSBoZWFkZXJzICYmIGhlYWRlcnNbJ3JldHJ5LWFmdGVyJ107CgogIGlmIChyYXRlTGltaXRIZWFkZXIpIHsKICAgIC8qKgogICAgICogcmF0ZSBsaW1pdCBoZWFkZXJzIGFyZSBvZiB0aGUgZm9ybQogICAgICogICAgIDxoZWFkZXI+LDxoZWFkZXI+LC4uCiAgICAgKiB3aGVyZSBlYWNoIDxoZWFkZXI+IGlzIG9mIHRoZSBmb3JtCiAgICAgKiAgICAgPHJldHJ5X2FmdGVyPjogPGNhdGVnb3JpZXM+OiA8c2NvcGU+OiA8cmVhc29uX2NvZGU+CiAgICAgKiB3aGVyZQogICAgICogICAgIDxyZXRyeV9hZnRlcj4gaXMgYSBkZWxheSBpbiBzZWNvbmRzCiAgICAgKiAgICAgPGNhdGVnb3JpZXM+IGlzIHRoZSBldmVudCB0eXBlKHMpIChlcnJvciwgdHJhbnNhY3Rpb24sIGV0YykgYmVpbmcgcmF0ZSBsaW1pdGVkIGFuZCBpcyBvZiB0aGUgZm9ybQogICAgICogICAgICAgICA8Y2F0ZWdvcnk+OzxjYXRlZ29yeT47Li4uCiAgICAgKiAgICAgPHNjb3BlPiBpcyB3aGF0J3MgYmVpbmcgbGltaXRlZCAob3JnLCBwcm9qZWN0LCBvciBrZXkpIC0gaWdub3JlZCBieSBTREsKICAgICAqICAgICA8cmVhc29uX2NvZGU+IGlzIGFuIGFyYml0cmFyeSBzdHJpbmcgbGlrZSAib3JnX3F1b3RhIiAtIGlnbm9yZWQgYnkgU0RLCiAgICAgKi8KICAgIGZvciAoY29uc3QgbGltaXQgb2YgcmF0ZUxpbWl0SGVhZGVyLnRyaW0oKS5zcGxpdCgnLCcpKSB7CiAgICAgIGNvbnN0IFtyZXRyeUFmdGVyLCBjYXRlZ29yaWVzXSA9IGxpbWl0LnNwbGl0KCc6JywgMik7CiAgICAgIGNvbnN0IGhlYWRlckRlbGF5ID0gcGFyc2VJbnQocmV0cnlBZnRlciwgMTApOwogICAgICBjb25zdCBkZWxheSA9ICghaXNOYU4oaGVhZGVyRGVsYXkpID8gaGVhZGVyRGVsYXkgOiA2MCkgKiAxMDAwOyAvLyA2MHNlYyBkZWZhdWx0CiAgICAgIGlmICghY2F0ZWdvcmllcykgewogICAgICAgIHVwZGF0ZWRSYXRlTGltaXRzLmFsbCA9IG5vdyArIGRlbGF5OwogICAgICB9IGVsc2UgewogICAgICAgIGZvciAoY29uc3QgY2F0ZWdvcnkgb2YgY2F0ZWdvcmllcy5zcGxpdCgnOycpKSB7CiAgICAgICAgICB1cGRhdGVkUmF0ZUxpbWl0c1tjYXRlZ29yeV0gPSBub3cgKyBkZWxheTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9IGVsc2UgaWYgKHJldHJ5QWZ0ZXJIZWFkZXIpIHsKICAgIHVwZGF0ZWRSYXRlTGltaXRzLmFsbCA9IG5vdyArIHBhcnNlUmV0cnlBZnRlckhlYWRlcihyZXRyeUFmdGVySGVhZGVyLCBub3cpOwogIH0gZWxzZSBpZiAoc3RhdHVzQ29kZSA9PT0gNDI5KSB7CiAgICB1cGRhdGVkUmF0ZUxpbWl0cy5hbGwgPSBub3cgKyA2MCAqIDEwMDA7CiAgfQoKICByZXR1cm4gdXBkYXRlZFJhdGVMaW1pdHM7Cn0KCi8qKgogKiBBIG5vZGUuanMgd2F0Y2hkb2cgdGltZXIKICogQHBhcmFtIHBvbGxJbnRlcnZhbCBUaGUgaW50ZXJ2YWwgdGhhdCB3ZSBleHBlY3QgdG8gZ2V0IHBvbGxlZCBhdAogKiBAcGFyYW0gYW5yVGhyZXNob2xkIFRoZSB0aHJlc2hvbGQgZm9yIHdoZW4gd2UgY29uc2lkZXIgQU5SCiAqIEBwYXJhbSBjYWxsYmFjayBUaGUgY2FsbGJhY2sgdG8gY2FsbCBmb3IgQU5SCiAqIEByZXR1cm5zIEFuIG9iamVjdCB3aXRoIGBwb2xsYCBhbmQgYGVuYWJsZWRgIGZ1bmN0aW9ucyB7QGxpbmsgV2F0Y2hkb2dSZXR1cm59CiAqLwpmdW5jdGlvbiB3YXRjaGRvZ1RpbWVyKAogIGNyZWF0ZVRpbWVyLAogIHBvbGxJbnRlcnZhbCwKICBhbnJUaHJlc2hvbGQsCiAgY2FsbGJhY2ssCikgewogIGNvbnN0IHRpbWVyID0gY3JlYXRlVGltZXIoKTsKICBsZXQgdHJpZ2dlcmVkID0gZmFsc2U7CiAgbGV0IGVuYWJsZWQgPSB0cnVlOwoKICBzZXRJbnRlcnZhbCgoKSA9PiB7CiAgICBjb25zdCBkaWZmTXMgPSB0aW1lci5nZXRUaW1lTXMoKTsKCiAgICBpZiAodHJpZ2dlcmVkID09PSBmYWxzZSAmJiBkaWZmTXMgPiBwb2xsSW50ZXJ2YWwgKyBhbnJUaHJlc2hvbGQpIHsKICAgICAgdHJpZ2dlcmVkID0gdHJ1ZTsKICAgICAgaWYgKGVuYWJsZWQpIHsKICAgICAgICBjYWxsYmFjaygpOwogICAgICB9CiAgICB9CgogICAgaWYgKGRpZmZNcyA8IHBvbGxJbnRlcnZhbCArIGFuclRocmVzaG9sZCkgewogICAgICB0cmlnZ2VyZWQgPSBmYWxzZTsKICAgIH0KICB9LCAyMCk7CgogIHJldHVybiB7CiAgICBwb2xsOiAoKSA9PiB7CiAgICAgIHRpbWVyLnJlc2V0KCk7CiAgICB9LAogICAgZW5hYmxlZDogKHN0YXRlKSA9PiB7CiAgICAgIGVuYWJsZWQgPSBzdGF0ZTsKICAgIH0sCiAgfTsKfQoKLy8gdHlwZXMgY29waWVkIGZyb20gaW5zcGVjdG9yLmQudHMKCi8qKgogKiBDb252ZXJ0cyBEZWJ1Z2dlci5DYWxsRnJhbWUgdG8gU2VudHJ5IFN0YWNrRnJhbWUKICovCmZ1bmN0aW9uIGNhbGxGcmFtZVRvU3RhY2tGcmFtZSgKICBmcmFtZSwKICB1cmwsCiAgZ2V0TW9kdWxlRnJvbUZpbGVuYW1lLAopIHsKICBjb25zdCBmaWxlbmFtZSA9IHVybCA/IHVybC5yZXBsYWNlKC9eZmlsZTpcL1wvLywgJycpIDogdW5kZWZpbmVkOwoKICAvLyBDYWxsRnJhbWUgcm93L2NvbCBhcmUgMCBiYXNlZCwgd2hlcmVhcyBTdGFja0ZyYW1lIGFyZSAxIGJhc2VkCiAgY29uc3QgY29sbm8gPSBmcmFtZS5sb2NhdGlvbi5jb2x1bW5OdW1iZXIgPyBmcmFtZS5sb2NhdGlvbi5jb2x1bW5OdW1iZXIgKyAxIDogdW5kZWZpbmVkOwogIGNvbnN0IGxpbmVubyA9IGZyYW1lLmxvY2F0aW9uLmxpbmVOdW1iZXIgPyBmcmFtZS5sb2NhdGlvbi5saW5lTnVtYmVyICsgMSA6IHVuZGVmaW5lZDsKCiAgcmV0dXJuIGRyb3BVbmRlZmluZWRLZXlzKHsKICAgIGZpbGVuYW1lLAogICAgbW9kdWxlOiBnZXRNb2R1bGVGcm9tRmlsZW5hbWUoZmlsZW5hbWUpLAogICAgZnVuY3Rpb246IGZyYW1lLmZ1bmN0aW9uTmFtZSB8fCBVTktOT1dOX0ZVTkNUSU9OLAogICAgY29sbm8sCiAgICBsaW5lbm8sCiAgICBpbl9hcHA6IGZpbGVuYW1lID8gZmlsZW5hbWVJc0luQXBwKGZpbGVuYW1lKSA6IHVuZGVmaW5lZCwKICB9KTsKfQoKLyoqCiAqIFRoaXMgc2VydmVzIGFzIGEgYnVpbGQgdGltZSBmbGFnIHRoYXQgd2lsbCBiZSB0cnVlIGJ5IGRlZmF1bHQsIGJ1dCBmYWxzZSBpbiBub24tZGVidWcgYnVpbGRzIG9yIGlmIHVzZXJzIHJlcGxhY2UgYF9fU0VOVFJZX0RFQlVHX19gIGluIHRoZWlyIGdlbmVyYXRlZCBjb2RlLgogKgogKiBBVFRFTlRJT046IFRoaXMgY29uc3RhbnQgbXVzdCBuZXZlciBjcm9zcyBwYWNrYWdlIGJvdW5kYXJpZXMgKGkuZS4gYmUgZXhwb3J0ZWQpIHRvIGd1YXJhbnRlZSB0aGF0IGl0IGNhbiBiZSB1c2VkIGZvciB0cmVlIHNoYWtpbmcuCiAqLwpjb25zdCBERUJVR19CVUlMRCA9ICh0eXBlb2YgX19TRU5UUllfREVCVUdfXyA9PT0gJ3VuZGVmaW5lZCcgfHwgX19TRU5UUllfREVCVUdfXyk7CgovKioKICogQHByaXZhdGUgUHJpdmF0ZSBBUEkgd2l0aCBubyBzZW12ZXIgZ3VhcmFudGVlcyEKICoKICogU3RyYXRlZ3kgdXNlZCB0byB0cmFjayBhc3luYyBjb250ZXh0LgogKi8KCi8qKgogKiBSZXR1cm5zIHRoZSBnbG9iYWwgc2hpbSByZWdpc3RyeS4KICoKICogRklYTUU6IFRoaXMgZnVuY3Rpb24gaXMgcHJvYmxlbWF0aWMsIGJlY2F1c2UgZGVzcGl0ZSBhbHdheXMgcmV0dXJuaW5nIGEgdmFsaWQgQ2FycmllciwKICogaXQgaGFzIGFuIG9wdGlvbmFsIGBfX1NFTlRSWV9fYCBwcm9wZXJ0eSwgd2hpY2ggdGhlbiBpbiB0dXJuIHJlcXVpcmVzIHVzIHRvIGFsd2F5cyBwZXJmb3JtIGFuIHVubmVjZXNzYXJ5IGNoZWNrCiAqIGF0IHRoZSBjYWxsLXNpdGUuIFdlIGFsd2F5cyBhY2Nlc3MgdGhlIGNhcnJpZXIgdGhyb3VnaCB0aGlzIGZ1bmN0aW9uLCBzbyB3ZSBjYW4gZ3VhcmFudGVlIHRoYXQgYF9fU0VOVFJZX19gIGlzIHRoZXJlLgogKiovCmZ1bmN0aW9uIGdldE1haW5DYXJyaWVyKCkgewogIC8vIFRoaXMgZW5zdXJlcyBhIFNlbnRyeSBjYXJyaWVyIGV4aXN0cwogIGdldFNlbnRyeUNhcnJpZXIoR0xPQkFMX09CSik7CiAgcmV0dXJuIEdMT0JBTF9PQko7Cn0KCi8qKiBXaWxsIGVpdGhlciBnZXQgdGhlIGV4aXN0aW5nIHNlbnRyeSBjYXJyaWVyLCBvciBjcmVhdGUgYSBuZXcgb25lLiAqLwpmdW5jdGlvbiBnZXRTZW50cnlDYXJyaWVyKGNhcnJpZXIpIHsKICBpZiAoIWNhcnJpZXIuX19TRU5UUllfXykgewogICAgY2Fycmllci5fX1NFTlRSWV9fID0gewogICAgICBleHRlbnNpb25zOiB7fSwKICAgIH07CiAgfQogIHJldHVybiBjYXJyaWVyLl9fU0VOVFJZX187Cn0KCmNvbnN0IERFRkFVTFRfRU5WSVJPTk1FTlQgPSAncHJvZHVjdGlvbic7CgovKioKICogQ3JlYXRlcyBhIG5ldyBgU2Vzc2lvbmAgb2JqZWN0IGJ5IHNldHRpbmcgY2VydGFpbiBkZWZhdWx0IHBhcmFtZXRlcnMuIElmIG9wdGlvbmFsIEBwYXJhbSBjb250ZXh0CiAqIGlzIHBhc3NlZCwgdGhlIHBhc3NlZCBwcm9wZXJ0aWVzIGFyZSBhcHBsaWVkIHRvIHRoZSBzZXNzaW9uIG9iamVjdC4KICoKICogQHBhcmFtIGNvbnRleHQgKG9wdGlvbmFsKSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgdG8gYmUgYXBwbGllZCB0byB0aGUgcmV0dXJuZWQgc2Vzc2lvbiBvYmplY3QKICoKICogQHJldHVybnMgYSBuZXcgYFNlc3Npb25gIG9iamVjdAogKi8KZnVuY3Rpb24gbWFrZVNlc3Npb24oY29udGV4dCkgewogIC8vIEJvdGggdGltZXN0YW1wIGFuZCBzdGFydGVkIGFyZSBpbiBzZWNvbmRzIHNpbmNlIHRoZSBVTklYIGVwb2NoLgogIGNvbnN0IHN0YXJ0aW5nVGltZSA9IHRpbWVzdGFtcEluU2Vjb25kcygpOwoKICBjb25zdCBzZXNzaW9uID0gewogICAgc2lkOiB1dWlkNCgpLAogICAgaW5pdDogdHJ1ZSwKICAgIHRpbWVzdGFtcDogc3RhcnRpbmdUaW1lLAogICAgc3RhcnRlZDogc3RhcnRpbmdUaW1lLAogICAgZHVyYXRpb246IDAsCiAgICBzdGF0dXM6ICdvaycsCiAgICBlcnJvcnM6IDAsCiAgICBpZ25vcmVEdXJhdGlvbjogZmFsc2UsCiAgICB0b0pTT046ICgpID0+IHNlc3Npb25Ub0pTT04oc2Vzc2lvbiksCiAgfTsKCiAgaWYgKGNvbnRleHQpIHsKICAgIHVwZGF0ZVNlc3Npb24oc2Vzc2lvbiwgY29udGV4dCk7CiAgfQoKICByZXR1cm4gc2Vzc2lvbjsKfQoKLyoqCiAqIFVwZGF0ZXMgYSBzZXNzaW9uIG9iamVjdCB3aXRoIHRoZSBwcm9wZXJ0aWVzIHBhc3NlZCBpbiB0aGUgY29udGV4dC4KICoKICogTm90ZSB0aGF0IHRoaXMgZnVuY3Rpb24gbXV0YXRlcyB0aGUgcGFzc2VkIG9iamVjdCBhbmQgcmV0dXJucyB2b2lkLgogKiAoSGFkIHRvIGRvIHRoaXMgaW5zdGVhZCBvZiByZXR1cm5pbmcgYSBuZXcgYW5kIHVwZGF0ZWQgc2Vzc2lvbiBiZWNhdXNlIGNsb3NpbmcgYW5kIHNlbmRpbmcgYSBzZXNzaW9uCiAqIG1ha2VzIGFuIHVwZGF0ZSB0byB0aGUgc2Vzc2lvbiBhZnRlciBpdCB3YXMgcGFzc2VkIHRvIHRoZSBzZW5kaW5nIGxvZ2ljLgogKiBAc2VlIEJhc2VDbGllbnQuY2FwdHVyZVNlc3Npb24gKQogKgogKiBAcGFyYW0gc2Vzc2lvbiB0aGUgYFNlc3Npb25gIHRvIHVwZGF0ZQogKiBAcGFyYW0gY29udGV4dCB0aGUgYFNlc3Npb25Db250ZXh0YCBob2xkaW5nIHRoZSBwcm9wZXJ0aWVzIHRoYXQgc2hvdWxkIGJlIHVwZGF0ZWQgaW4gQHBhcmFtIHNlc3Npb24KICovCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5CmZ1bmN0aW9uIHVwZGF0ZVNlc3Npb24oc2Vzc2lvbiwgY29udGV4dCA9IHt9KSB7CiAgaWYgKGNvbnRleHQudXNlcikgewogICAgaWYgKCFzZXNzaW9uLmlwQWRkcmVzcyAmJiBjb250ZXh0LnVzZXIuaXBfYWRkcmVzcykgewogICAgICBzZXNzaW9uLmlwQWRkcmVzcyA9IGNvbnRleHQudXNlci5pcF9hZGRyZXNzOwogICAgfQoKICAgIGlmICghc2Vzc2lvbi5kaWQgJiYgIWNvbnRleHQuZGlkKSB7CiAgICAgIHNlc3Npb24uZGlkID0gY29udGV4dC51c2VyLmlkIHx8IGNvbnRleHQudXNlci5lbWFpbCB8fCBjb250ZXh0LnVzZXIudXNlcm5hbWU7CiAgICB9CiAgfQoKICBzZXNzaW9uLnRpbWVzdGFtcCA9IGNvbnRleHQudGltZXN0YW1wIHx8IHRpbWVzdGFtcEluU2Vjb25kcygpOwoKICBpZiAoY29udGV4dC5hYm5vcm1hbF9tZWNoYW5pc20pIHsKICAgIHNlc3Npb24uYWJub3JtYWxfbWVjaGFuaXNtID0gY29udGV4dC5hYm5vcm1hbF9tZWNoYW5pc207CiAgfQoKICBpZiAoY29udGV4dC5pZ25vcmVEdXJhdGlvbikgewogICAgc2Vzc2lvbi5pZ25vcmVEdXJhdGlvbiA9IGNvbnRleHQuaWdub3JlRHVyYXRpb247CiAgfQogIGlmIChjb250ZXh0LnNpZCkgewogICAgLy8gR29vZCBlbm91Z2ggdXVpZCB2YWxpZGF0aW9uLiDigJQgS2FtaWwKICAgIHNlc3Npb24uc2lkID0gY29udGV4dC5zaWQubGVuZ3RoID09PSAzMiA/IGNvbnRleHQuc2lkIDogdXVpZDQoKTsKICB9CiAgaWYgKGNvbnRleHQuaW5pdCAhPT0gdW5kZWZpbmVkKSB7CiAgICBzZXNzaW9uLmluaXQgPSBjb250ZXh0LmluaXQ7CiAgfQogIGlmICghc2Vzc2lvbi5kaWQgJiYgY29udGV4dC5kaWQpIHsKICAgIHNlc3Npb24uZGlkID0gYCR7Y29udGV4dC5kaWR9YDsKICB9CiAgaWYgKHR5cGVvZiBjb250ZXh0LnN0YXJ0ZWQgPT09ICdudW1iZXInKSB7CiAgICBzZXNzaW9uLnN0YXJ0ZWQgPSBjb250ZXh0LnN0YXJ0ZWQ7CiAgfQogIGlmIChzZXNzaW9uLmlnbm9yZUR1cmF0aW9uKSB7CiAgICBzZXNzaW9uLmR1cmF0aW9uID0gdW5kZWZpbmVkOwogIH0gZWxzZSBpZiAodHlwZW9mIGNvbnRleHQuZHVyYXRpb24gPT09ICdudW1iZXInKSB7CiAgICBzZXNzaW9uLmR1cmF0aW9uID0gY29udGV4dC5kdXJhdGlvbjsKICB9IGVsc2UgewogICAgY29uc3QgZHVyYXRpb24gPSBzZXNzaW9uLnRpbWVzdGFtcCAtIHNlc3Npb24uc3RhcnRlZDsKICAgIHNlc3Npb24uZHVyYXRpb24gPSBkdXJhdGlvbiA+PSAwID8gZHVyYXRpb24gOiAwOwogIH0KICBpZiAoY29udGV4dC5yZWxlYXNlKSB7CiAgICBzZXNzaW9uLnJlbGVhc2UgPSBjb250ZXh0LnJlbGVhc2U7CiAgfQogIGlmIChjb250ZXh0LmVudmlyb25tZW50KSB7CiAgICBzZXNzaW9uLmVudmlyb25tZW50ID0gY29udGV4dC5lbnZpcm9ubWVudDsKICB9CiAgaWYgKCFzZXNzaW9uLmlwQWRkcmVzcyAmJiBjb250ZXh0LmlwQWRkcmVzcykgewogICAgc2Vzc2lvbi5pcEFkZHJlc3MgPSBjb250ZXh0LmlwQWRkcmVzczsKICB9CiAgaWYgKCFzZXNzaW9uLnVzZXJBZ2VudCAmJiBjb250ZXh0LnVzZXJBZ2VudCkgewogICAgc2Vzc2lvbi51c2VyQWdlbnQgPSBjb250ZXh0LnVzZXJBZ2VudDsKICB9CiAgaWYgKHR5cGVvZiBjb250ZXh0LmVycm9ycyA9PT0gJ251bWJlcicpIHsKICAgIHNlc3Npb24uZXJyb3JzID0gY29udGV4dC5lcnJvcnM7CiAgfQogIGlmIChjb250ZXh0LnN0YXR1cykgewogICAgc2Vzc2lvbi5zdGF0dXMgPSBjb250ZXh0LnN0YXR1czsKICB9Cn0KCi8qKgogKiBDbG9zZXMgYSBzZXNzaW9uIGJ5IHNldHRpbmcgaXRzIHN0YXR1cyBhbmQgdXBkYXRpbmcgdGhlIHNlc3Npb24gb2JqZWN0IHdpdGggaXQuCiAqIEludGVybmFsbHkgY2FsbHMgYHVwZGF0ZVNlc3Npb25gIHRvIHVwZGF0ZSB0aGUgcGFzc2VkIHNlc3Npb24gb2JqZWN0LgogKgogKiBOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBtdXRhdGVzIHRoZSBwYXNzZWQgc2Vzc2lvbiAoQHNlZSB1cGRhdGVTZXNzaW9uIGZvciBleHBsYW5hdGlvbikuCiAqCiAqIEBwYXJhbSBzZXNzaW9uIHRoZSBgU2Vzc2lvbmAgb2JqZWN0IHRvIGJlIGNsb3NlZAogKiBAcGFyYW0gc3RhdHVzIHRoZSBgU2Vzc2lvblN0YXR1c2Agd2l0aCB3aGljaCB0aGUgc2Vzc2lvbiB3YXMgY2xvc2VkLiBJZiB5b3UgZG9uJ3QgcGFzcyBhIHN0YXR1cywKICogICAgICAgICAgICAgICB0aGlzIGZ1bmN0aW9uIHdpbGwga2VlcCB0aGUgcHJldmlvdXNseSBzZXQgc3RhdHVzLCB1bmxlc3MgaXQgd2FzIGAnb2snYCBpbiB3aGljaCBjYXNlCiAqICAgICAgICAgICAgICAgaXQgaXMgY2hhbmdlZCB0byBgJ2V4aXRlZCdgLgogKi8KZnVuY3Rpb24gY2xvc2VTZXNzaW9uKHNlc3Npb24sIHN0YXR1cykgewogIGxldCBjb250ZXh0ID0ge307CiAgaWYgKHN0YXR1cykgewogICAgY29udGV4dCA9IHsgc3RhdHVzIH07CiAgfSBlbHNlIGlmIChzZXNzaW9uLnN0YXR1cyA9PT0gJ29rJykgewogICAgY29udGV4dCA9IHsgc3RhdHVzOiAnZXhpdGVkJyB9OwogIH0KCiAgdXBkYXRlU2Vzc2lvbihzZXNzaW9uLCBjb250ZXh0KTsKfQoKLyoqCiAqIFNlcmlhbGl6ZXMgYSBwYXNzZWQgc2Vzc2lvbiBvYmplY3QgdG8gYSBKU09OIG9iamVjdCB3aXRoIGEgc2xpZ2h0bHkgZGlmZmVyZW50IHN0cnVjdHVyZS4KICogVGhpcyBpcyBuZWNlc3NhcnkgYmVjYXVzZSB0aGUgU2VudHJ5IGJhY2tlbmQgcmVxdWlyZXMgYSBzbGlnaHRseSBkaWZmZXJlbnQgc2NoZW1hIG9mIGEgc2Vzc2lvbgogKiB0aGFuIHRoZSBvbmUgdGhlIEpTIFNES3MgdXNlIGludGVybmFsbHkuCiAqCiAqIEBwYXJhbSBzZXNzaW9uIHRoZSBzZXNzaW9uIHRvIGJlIGNvbnZlcnRlZAogKgogKiBAcmV0dXJucyBhIEpTT04gb2JqZWN0IG9mIHRoZSBwYXNzZWQgc2Vzc2lvbgogKi8KZnVuY3Rpb24gc2Vzc2lvblRvSlNPTihzZXNzaW9uKSB7CiAgcmV0dXJuIGRyb3BVbmRlZmluZWRLZXlzKHsKICAgIHNpZDogYCR7c2Vzc2lvbi5zaWR9YCwKICAgIGluaXQ6IHNlc3Npb24uaW5pdCwKICAgIC8vIE1ha2Ugc3VyZSB0aGF0IHNlYyBpcyBjb252ZXJ0ZWQgdG8gbXMgZm9yIGRhdGUgY29uc3RydWN0b3IKICAgIHN0YXJ0ZWQ6IG5ldyBEYXRlKHNlc3Npb24uc3RhcnRlZCAqIDEwMDApLnRvSVNPU3RyaW5nKCksCiAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKHNlc3Npb24udGltZXN0YW1wICogMTAwMCkudG9JU09TdHJpbmcoKSwKICAgIHN0YXR1czogc2Vzc2lvbi5zdGF0dXMsCiAgICBlcnJvcnM6IHNlc3Npb24uZXJyb3JzLAogICAgZGlkOiB0eXBlb2Ygc2Vzc2lvbi5kaWQgPT09ICdudW1iZXInIHx8IHR5cGVvZiBzZXNzaW9uLmRpZCA9PT0gJ3N0cmluZycgPyBgJHtzZXNzaW9uLmRpZH1gIDogdW5kZWZpbmVkLAogICAgZHVyYXRpb246IHNlc3Npb24uZHVyYXRpb24sCiAgICBhYm5vcm1hbF9tZWNoYW5pc206IHNlc3Npb24uYWJub3JtYWxfbWVjaGFuaXNtLAogICAgYXR0cnM6IHsKICAgICAgcmVsZWFzZTogc2Vzc2lvbi5yZWxlYXNlLAogICAgICBlbnZpcm9ubWVudDogc2Vzc2lvbi5lbnZpcm9ubWVudCwKICAgICAgaXBfYWRkcmVzczogc2Vzc2lvbi5pcEFkZHJlc3MsCiAgICAgIHVzZXJfYWdlbnQ6IHNlc3Npb24udXNlckFnZW50LAogICAgfSwKICB9KTsKfQoKLyoqCiAqIERlZmF1bHQgdmFsdWUgZm9yIG1heGltdW0gbnVtYmVyIG9mIGJyZWFkY3J1bWJzIGFkZGVkIHRvIGFuIGV2ZW50LgogKi8KY29uc3QgREVGQVVMVF9NQVhfQlJFQURDUlVNQlMgPSAxMDA7CgovKioKICogSG9sZHMgYWRkaXRpb25hbCBldmVudCBpbmZvcm1hdGlvbi4KICovCmNsYXNzIFNjb3BlICB7CiAgLyoqIEZsYWcgaWYgbm90aWZ5aW5nIGlzIGhhcHBlbmluZy4gKi8KCiAgLyoqIENhbGxiYWNrIGZvciBjbGllbnQgdG8gcmVjZWl2ZSBzY29wZSBjaGFuZ2VzLiAqLwoKICAvKiogQ2FsbGJhY2sgbGlzdCB0aGF0IHdpbGwgYmUgY2FsbGVkIGR1cmluZyBldmVudCBwcm9jZXNzaW5nLiAqLwoKICAvKiogQXJyYXkgb2YgYnJlYWRjcnVtYnMuICovCgogIC8qKiBVc2VyICovCgogIC8qKiBUYWdzICovCgogIC8qKiBFeHRyYSAqLwoKICAvKiogQ29udGV4dHMgKi8KCiAgLyoqIEF0dGFjaG1lbnRzICovCgogIC8qKiBQcm9wYWdhdGlvbiBDb250ZXh0IGZvciBkaXN0cmlidXRlZCB0cmFjaW5nICovCgogIC8qKgogICAqIEEgcGxhY2UgdG8gc3Rhc2ggZGF0YSB3aGljaCBpcyBuZWVkZWQgYXQgc29tZSBwb2ludCBpbiB0aGUgU0RLJ3MgZXZlbnQgcHJvY2Vzc2luZyBwaXBlbGluZSBidXQgd2hpY2ggc2hvdWxkbid0IGdldAogICAqIHNlbnQgdG8gU2VudHJ5CiAgICovCgogIC8qKiBGaW5nZXJwcmludCAqLwoKICAvKiogU2V2ZXJpdHkgKi8KCiAgLyoqCiAgICogVHJhbnNhY3Rpb24gTmFtZQogICAqCiAgICogSU1QT1JUQU5UOiBUaGUgdHJhbnNhY3Rpb24gbmFtZSBvbiB0aGUgc2NvcGUgaGFzIG5vdGhpbmcgdG8gZG8gd2l0aCByb290IHNwYW5zL3RyYW5zYWN0aW9uIG9iamVjdHMuCiAgICogSXQncyBwdXJwb3NlIGlzIHRvIGFzc2lnbiBhIHRyYW5zYWN0aW9uIHRvIHRoZSBzY29wZSB0aGF0J3MgYWRkZWQgdG8gbm9uLXRyYW5zYWN0aW9uIGV2ZW50cy4KICAgKi8KCiAgLyoqIFNwYW4gKi8KCiAgLyoqIFNlc3Npb24gKi8KCiAgLyoqIFJlcXVlc3QgTW9kZSBTZXNzaW9uIFN0YXR1cyAqLwoKICAvKiogVGhlIGNsaWVudCBvbiB0aGlzIHNjb3BlICovCgogIC8vIE5PVEU6IEFueSBmaWVsZCB3aGljaCBnZXRzIGFkZGVkIGhlcmUgc2hvdWxkIGdldCBhZGRlZCBub3Qgb25seSB0byB0aGUgY29uc3RydWN0b3IgYnV0IGFsc28gdG8gdGhlIGBjbG9uZWAgbWV0aG9kLgoKICAgY29uc3RydWN0b3IoKSB7CiAgICB0aGlzLl9ub3RpZnlpbmdMaXN0ZW5lcnMgPSBmYWxzZTsKICAgIHRoaXMuX3Njb3BlTGlzdGVuZXJzID0gW107CiAgICB0aGlzLl9ldmVudFByb2Nlc3NvcnMgPSBbXTsKICAgIHRoaXMuX2JyZWFkY3J1bWJzID0gW107CiAgICB0aGlzLl9hdHRhY2htZW50cyA9IFtdOwogICAgdGhpcy5fdXNlciA9IHt9OwogICAgdGhpcy5fdGFncyA9IHt9OwogICAgdGhpcy5fZXh0cmEgPSB7fTsKICAgIHRoaXMuX2NvbnRleHRzID0ge307CiAgICB0aGlzLl9zZGtQcm9jZXNzaW5nTWV0YWRhdGEgPSB7fTsKICAgIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCA9IGdlbmVyYXRlUHJvcGFnYXRpb25Db250ZXh0KCk7CiAgfQoKICAvKioKICAgKiBJbmhlcml0IHZhbHVlcyBmcm9tIHRoZSBwYXJlbnQgc2NvcGUuCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBzY29wZS5jbG9uZSgpYCBhbmQgYG5ldyBTY29wZSgpYCBpbnN0ZWFkLgogICAqLwogICBzdGF0aWMgY2xvbmUoc2NvcGUpIHsKICAgIHJldHVybiBzY29wZSA/IHNjb3BlLmNsb25lKCkgOiBuZXcgU2NvcGUoKTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNsb25lKCkgewogICAgY29uc3QgbmV3U2NvcGUgPSBuZXcgU2NvcGUoKTsKICAgIG5ld1Njb3BlLl9icmVhZGNydW1icyA9IFsuLi50aGlzLl9icmVhZGNydW1ic107CiAgICBuZXdTY29wZS5fdGFncyA9IHsgLi4udGhpcy5fdGFncyB9OwogICAgbmV3U2NvcGUuX2V4dHJhID0geyAuLi50aGlzLl9leHRyYSB9OwogICAgbmV3U2NvcGUuX2NvbnRleHRzID0geyAuLi50aGlzLl9jb250ZXh0cyB9OwogICAgbmV3U2NvcGUuX3VzZXIgPSB0aGlzLl91c2VyOwogICAgbmV3U2NvcGUuX2xldmVsID0gdGhpcy5fbGV2ZWw7CiAgICBuZXdTY29wZS5fc3BhbiA9IHRoaXMuX3NwYW47CiAgICBuZXdTY29wZS5fc2Vzc2lvbiA9IHRoaXMuX3Nlc3Npb247CiAgICBuZXdTY29wZS5fdHJhbnNhY3Rpb25OYW1lID0gdGhpcy5fdHJhbnNhY3Rpb25OYW1lOwogICAgbmV3U2NvcGUuX2ZpbmdlcnByaW50ID0gdGhpcy5fZmluZ2VycHJpbnQ7CiAgICBuZXdTY29wZS5fZXZlbnRQcm9jZXNzb3JzID0gWy4uLnRoaXMuX2V2ZW50UHJvY2Vzc29yc107CiAgICBuZXdTY29wZS5fcmVxdWVzdFNlc3Npb24gPSB0aGlzLl9yZXF1ZXN0U2Vzc2lvbjsKICAgIG5ld1Njb3BlLl9hdHRhY2htZW50cyA9IFsuLi50aGlzLl9hdHRhY2htZW50c107CiAgICBuZXdTY29wZS5fc2RrUHJvY2Vzc2luZ01ldGFkYXRhID0geyAuLi50aGlzLl9zZGtQcm9jZXNzaW5nTWV0YWRhdGEgfTsKICAgIG5ld1Njb3BlLl9wcm9wYWdhdGlvbkNvbnRleHQgPSB7IC4uLnRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCB9OwogICAgbmV3U2NvcGUuX2NsaWVudCA9IHRoaXMuX2NsaWVudDsKCiAgICByZXR1cm4gbmV3U2NvcGU7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRDbGllbnQoY2xpZW50KSB7CiAgICB0aGlzLl9jbGllbnQgPSBjbGllbnQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRDbGllbnQoKSB7CiAgICByZXR1cm4gdGhpcy5fY2xpZW50IDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGFkZFNjb3BlTGlzdGVuZXIoY2FsbGJhY2spIHsKICAgIHRoaXMuX3Njb3BlTGlzdGVuZXJzLnB1c2goY2FsbGJhY2spOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgYWRkRXZlbnRQcm9jZXNzb3IoY2FsbGJhY2spIHsKICAgIHRoaXMuX2V2ZW50UHJvY2Vzc29ycy5wdXNoKGNhbGxiYWNrKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0VXNlcih1c2VyKSB7CiAgICAvLyBJZiBudWxsIGlzIHBhc3NlZCB3ZSB3YW50IHRvIHVuc2V0IGV2ZXJ5dGhpbmcsIGJ1dCBzdGlsbCBkZWZpbmUga2V5cywKICAgIC8vIHNvIHRoYXQgbGF0ZXIgZG93biBpbiB0aGUgcGlwZWxpbmUgYW55IGV4aXN0aW5nIHZhbHVlcyBhcmUgY2xlYXJlZC4KICAgIHRoaXMuX3VzZXIgPSB1c2VyIHx8IHsKICAgICAgZW1haWw6IHVuZGVmaW5lZCwKICAgICAgaWQ6IHVuZGVmaW5lZCwKICAgICAgaXBfYWRkcmVzczogdW5kZWZpbmVkLAogICAgICB1c2VybmFtZTogdW5kZWZpbmVkLAogICAgfTsKCiAgICBpZiAodGhpcy5fc2Vzc2lvbikgewogICAgICB1cGRhdGVTZXNzaW9uKHRoaXMuX3Nlc3Npb24sIHsgdXNlciB9KTsKICAgIH0KCiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRVc2VyKCkgewogICAgcmV0dXJuIHRoaXMuX3VzZXI7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRSZXF1ZXN0U2Vzc2lvbigpIHsKICAgIHJldHVybiB0aGlzLl9yZXF1ZXN0U2Vzc2lvbjsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldFJlcXVlc3RTZXNzaW9uKHJlcXVlc3RTZXNzaW9uKSB7CiAgICB0aGlzLl9yZXF1ZXN0U2Vzc2lvbiA9IHJlcXVlc3RTZXNzaW9uOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRUYWdzKHRhZ3MpIHsKICAgIHRoaXMuX3RhZ3MgPSB7CiAgICAgIC4uLnRoaXMuX3RhZ3MsCiAgICAgIC4uLnRhZ3MsCiAgICB9OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0VGFnKGtleSwgdmFsdWUpIHsKICAgIHRoaXMuX3RhZ3MgPSB7IC4uLnRoaXMuX3RhZ3MsIFtrZXldOiB2YWx1ZSB9OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0RXh0cmFzKGV4dHJhcykgewogICAgdGhpcy5fZXh0cmEgPSB7CiAgICAgIC4uLnRoaXMuX2V4dHJhLAogICAgICAuLi5leHRyYXMsCiAgICB9OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0RXh0cmEoa2V5LCBleHRyYSkgewogICAgdGhpcy5fZXh0cmEgPSB7IC4uLnRoaXMuX2V4dHJhLCBba2V5XTogZXh0cmEgfTsKICAgIHRoaXMuX25vdGlmeVNjb3BlTGlzdGVuZXJzKCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldEZpbmdlcnByaW50KGZpbmdlcnByaW50KSB7CiAgICB0aGlzLl9maW5nZXJwcmludCA9IGZpbmdlcnByaW50OwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0TGV2ZWwobGV2ZWwpIHsKICAgIHRoaXMuX2xldmVsID0gbGV2ZWw7CiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRUcmFuc2FjdGlvbk5hbWUobmFtZSkgewogICAgdGhpcy5fdHJhbnNhY3Rpb25OYW1lID0gbmFtZTsKICAgIHRoaXMuX25vdGlmeVNjb3BlTGlzdGVuZXJzKCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldENvbnRleHQoa2V5LCBjb250ZXh0KSB7CiAgICBpZiAoY29udGV4dCA9PT0gbnVsbCkgewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWR5bmFtaWMtZGVsZXRlCiAgICAgIGRlbGV0ZSB0aGlzLl9jb250ZXh0c1trZXldOwogICAgfSBlbHNlIHsKICAgICAgdGhpcy5fY29udGV4dHNba2V5XSA9IGNvbnRleHQ7CiAgICB9CgogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogU2V0cyB0aGUgU3BhbiBvbiB0aGUgc2NvcGUuCiAgICogQHBhcmFtIHNwYW4gU3BhbgogICAqIEBkZXByZWNhdGVkIEluc3RlYWQgb2Ygc2V0dGluZyBhIHNwYW4gb24gYSBzY29wZSwgdXNlIGBzdGFydFNwYW4oKWAvYHN0YXJ0U3Bhbk1hbnVhbCgpYCBpbnN0ZWFkLgogICAqLwogICBzZXRTcGFuKHNwYW4pIHsKICAgIHRoaXMuX3NwYW4gPSBzcGFuOwogICAgdGhpcy5fbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgYFNwYW5gIGlmIHRoZXJlIGlzIG9uZS4KICAgKiBAZGVwcmVjYXRlZCBVc2UgYGdldEFjdGl2ZVNwYW4oKWAgaW5zdGVhZC4KICAgKi8KICAgZ2V0U3BhbigpIHsKICAgIHJldHVybiB0aGlzLl9zcGFuOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgYFRyYW5zYWN0aW9uYCBhdHRhY2hlZCB0byB0aGUgc2NvcGUgKGlmIHRoZXJlIGlzIG9uZSkuCiAgICogQGRlcHJlY2F0ZWQgWW91IHNob3VsZCBub3QgcmVseSBvbiB0aGUgdHJhbnNhY3Rpb24sIGJ1dCBqdXN0IHVzZSBgc3RhcnRTcGFuKClgIEFQSXMgaW5zdGVhZC4KICAgKi8KICAgZ2V0VHJhbnNhY3Rpb24oKSB7CiAgICAvLyBPZnRlbiwgdGhpcyBzcGFuIChpZiBpdCBleGlzdHMgYXQgYWxsKSB3aWxsIGJlIGEgdHJhbnNhY3Rpb24sIGJ1dCBpdCdzIG5vdCBndWFyYW50ZWVkIHRvIGJlLiBSZWdhcmRsZXNzLCBpdCB3aWxsCiAgICAvLyBoYXZlIGEgcG9pbnRlciB0byB0aGUgY3VycmVudGx5LWFjdGl2ZSB0cmFuc2FjdGlvbi4KICAgIGNvbnN0IHNwYW4gPSB0aGlzLl9zcGFuOwoKICAgIC8vIENhbm5vdCByZXBsYWNlIHdpdGggZ2V0Um9vdFNwYW4gYmVjYXVzZSBnZXRSb290U3BhbiByZXR1cm5zIGEgc3Bhbiwgbm90IGEgdHJhbnNhY3Rpb24KICAgIC8vIEFsc28sIHRoaXMgbWV0aG9kIHdpbGwgYmUgcmVtb3ZlZCBhbnl3YXkuCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGlmIChzcGFuICYmIChzcGFuICkudHJhbnNhY3Rpb24pIHsKICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgIHJldHVybiAoc3BhbiApLnRyYW5zYWN0aW9uOwogICAgfQogICAgcmV0dXJuIHVuZGVmaW5lZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIHNldFNlc3Npb24oc2Vzc2lvbikgewogICAgaWYgKCFzZXNzaW9uKSB7CiAgICAgIGRlbGV0ZSB0aGlzLl9zZXNzaW9uOwogICAgfSBlbHNlIHsKICAgICAgdGhpcy5fc2Vzc2lvbiA9IHNlc3Npb247CiAgICB9CiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBnZXRTZXNzaW9uKCkgewogICAgcmV0dXJuIHRoaXMuX3Nlc3Npb247CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICB1cGRhdGUoY2FwdHVyZUNvbnRleHQpIHsKICAgIGlmICghY2FwdHVyZUNvbnRleHQpIHsKICAgICAgcmV0dXJuIHRoaXM7CiAgICB9CgogICAgY29uc3Qgc2NvcGVUb01lcmdlID0gdHlwZW9mIGNhcHR1cmVDb250ZXh0ID09PSAnZnVuY3Rpb24nID8gY2FwdHVyZUNvbnRleHQodGhpcykgOiBjYXB0dXJlQ29udGV4dDsKCiAgICBpZiAoc2NvcGVUb01lcmdlIGluc3RhbmNlb2YgU2NvcGUpIHsKICAgICAgY29uc3Qgc2NvcGVEYXRhID0gc2NvcGVUb01lcmdlLmdldFNjb3BlRGF0YSgpOwoKICAgICAgdGhpcy5fdGFncyA9IHsgLi4udGhpcy5fdGFncywgLi4uc2NvcGVEYXRhLnRhZ3MgfTsKICAgICAgdGhpcy5fZXh0cmEgPSB7IC4uLnRoaXMuX2V4dHJhLCAuLi5zY29wZURhdGEuZXh0cmEgfTsKICAgICAgdGhpcy5fY29udGV4dHMgPSB7IC4uLnRoaXMuX2NvbnRleHRzLCAuLi5zY29wZURhdGEuY29udGV4dHMgfTsKICAgICAgaWYgKHNjb3BlRGF0YS51c2VyICYmIE9iamVjdC5rZXlzKHNjb3BlRGF0YS51c2VyKS5sZW5ndGgpIHsKICAgICAgICB0aGlzLl91c2VyID0gc2NvcGVEYXRhLnVzZXI7CiAgICAgIH0KICAgICAgaWYgKHNjb3BlRGF0YS5sZXZlbCkgewogICAgICAgIHRoaXMuX2xldmVsID0gc2NvcGVEYXRhLmxldmVsOwogICAgICB9CiAgICAgIGlmIChzY29wZURhdGEuZmluZ2VycHJpbnQubGVuZ3RoKSB7CiAgICAgICAgdGhpcy5fZmluZ2VycHJpbnQgPSBzY29wZURhdGEuZmluZ2VycHJpbnQ7CiAgICAgIH0KICAgICAgaWYgKHNjb3BlVG9NZXJnZS5nZXRSZXF1ZXN0U2Vzc2lvbigpKSB7CiAgICAgICAgdGhpcy5fcmVxdWVzdFNlc3Npb24gPSBzY29wZVRvTWVyZ2UuZ2V0UmVxdWVzdFNlc3Npb24oKTsKICAgICAgfQogICAgICBpZiAoc2NvcGVEYXRhLnByb3BhZ2F0aW9uQ29udGV4dCkgewogICAgICAgIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCA9IHNjb3BlRGF0YS5wcm9wYWdhdGlvbkNvbnRleHQ7CiAgICAgIH0KICAgIH0gZWxzZSBpZiAoaXNQbGFpbk9iamVjdChzY29wZVRvTWVyZ2UpKSB7CiAgICAgIGNvbnN0IHNjb3BlQ29udGV4dCA9IGNhcHR1cmVDb250ZXh0IDsKICAgICAgdGhpcy5fdGFncyA9IHsgLi4udGhpcy5fdGFncywgLi4uc2NvcGVDb250ZXh0LnRhZ3MgfTsKICAgICAgdGhpcy5fZXh0cmEgPSB7IC4uLnRoaXMuX2V4dHJhLCAuLi5zY29wZUNvbnRleHQuZXh0cmEgfTsKICAgICAgdGhpcy5fY29udGV4dHMgPSB7IC4uLnRoaXMuX2NvbnRleHRzLCAuLi5zY29wZUNvbnRleHQuY29udGV4dHMgfTsKICAgICAgaWYgKHNjb3BlQ29udGV4dC51c2VyKSB7CiAgICAgICAgdGhpcy5fdXNlciA9IHNjb3BlQ29udGV4dC51c2VyOwogICAgICB9CiAgICAgIGlmIChzY29wZUNvbnRleHQubGV2ZWwpIHsKICAgICAgICB0aGlzLl9sZXZlbCA9IHNjb3BlQ29udGV4dC5sZXZlbDsKICAgICAgfQogICAgICBpZiAoc2NvcGVDb250ZXh0LmZpbmdlcnByaW50KSB7CiAgICAgICAgdGhpcy5fZmluZ2VycHJpbnQgPSBzY29wZUNvbnRleHQuZmluZ2VycHJpbnQ7CiAgICAgIH0KICAgICAgaWYgKHNjb3BlQ29udGV4dC5yZXF1ZXN0U2Vzc2lvbikgewogICAgICAgIHRoaXMuX3JlcXVlc3RTZXNzaW9uID0gc2NvcGVDb250ZXh0LnJlcXVlc3RTZXNzaW9uOwogICAgICB9CiAgICAgIGlmIChzY29wZUNvbnRleHQucHJvcGFnYXRpb25Db250ZXh0KSB7CiAgICAgICAgdGhpcy5fcHJvcGFnYXRpb25Db250ZXh0ID0gc2NvcGVDb250ZXh0LnByb3BhZ2F0aW9uQ29udGV4dDsKICAgICAgfQogICAgfQoKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgY2xlYXIoKSB7CiAgICAvLyBjbGllbnQgaXMgbm90IGNsZWFyZWQgaGVyZSBvbiBwdXJwb3NlIQogICAgdGhpcy5fYnJlYWRjcnVtYnMgPSBbXTsKICAgIHRoaXMuX3RhZ3MgPSB7fTsKICAgIHRoaXMuX2V4dHJhID0ge307CiAgICB0aGlzLl91c2VyID0ge307CiAgICB0aGlzLl9jb250ZXh0cyA9IHt9OwogICAgdGhpcy5fbGV2ZWwgPSB1bmRlZmluZWQ7CiAgICB0aGlzLl90cmFuc2FjdGlvbk5hbWUgPSB1bmRlZmluZWQ7CiAgICB0aGlzLl9maW5nZXJwcmludCA9IHVuZGVmaW5lZDsKICAgIHRoaXMuX3JlcXVlc3RTZXNzaW9uID0gdW5kZWZpbmVkOwogICAgdGhpcy5fc3BhbiA9IHVuZGVmaW5lZDsKICAgIHRoaXMuX3Nlc3Npb24gPSB1bmRlZmluZWQ7CiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwogICAgdGhpcy5fYXR0YWNobWVudHMgPSBbXTsKICAgIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dCA9IGdlbmVyYXRlUHJvcGFnYXRpb25Db250ZXh0KCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGFkZEJyZWFkY3J1bWIoYnJlYWRjcnVtYiwgbWF4QnJlYWRjcnVtYnMpIHsKICAgIGNvbnN0IG1heENydW1icyA9IHR5cGVvZiBtYXhCcmVhZGNydW1icyA9PT0gJ251bWJlcicgPyBtYXhCcmVhZGNydW1icyA6IERFRkFVTFRfTUFYX0JSRUFEQ1JVTUJTOwoKICAgIC8vIE5vIGRhdGEgaGFzIGJlZW4gY2hhbmdlZCwgc28gZG9uJ3Qgbm90aWZ5IHNjb3BlIGxpc3RlbmVycwogICAgaWYgKG1heENydW1icyA8PSAwKSB7CiAgICAgIHJldHVybiB0aGlzOwogICAgfQoKICAgIGNvbnN0IG1lcmdlZEJyZWFkY3J1bWIgPSB7CiAgICAgIHRpbWVzdGFtcDogZGF0ZVRpbWVzdGFtcEluU2Vjb25kcygpLAogICAgICAuLi5icmVhZGNydW1iLAogICAgfTsKCiAgICBjb25zdCBicmVhZGNydW1icyA9IHRoaXMuX2JyZWFkY3J1bWJzOwogICAgYnJlYWRjcnVtYnMucHVzaChtZXJnZWRCcmVhZGNydW1iKTsKICAgIHRoaXMuX2JyZWFkY3J1bWJzID0gYnJlYWRjcnVtYnMubGVuZ3RoID4gbWF4Q3J1bWJzID8gYnJlYWRjcnVtYnMuc2xpY2UoLW1heENydW1icykgOiBicmVhZGNydW1iczsKCiAgICB0aGlzLl9ub3RpZnlTY29wZUxpc3RlbmVycygpOwoKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgZ2V0TGFzdEJyZWFkY3J1bWIoKSB7CiAgICByZXR1cm4gdGhpcy5fYnJlYWRjcnVtYnNbdGhpcy5fYnJlYWRjcnVtYnMubGVuZ3RoIC0gMV07CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBjbGVhckJyZWFkY3J1bWJzKCkgewogICAgdGhpcy5fYnJlYWRjcnVtYnMgPSBbXTsKICAgIHRoaXMuX25vdGlmeVNjb3BlTGlzdGVuZXJzKCk7CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGFkZEF0dGFjaG1lbnQoYXR0YWNobWVudCkgewogICAgdGhpcy5fYXR0YWNobWVudHMucHVzaChhdHRhY2htZW50KTsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYGdldFNjb3BlRGF0YSgpYCBpbnN0ZWFkLgogICAqLwogICBnZXRBdHRhY2htZW50cygpIHsKICAgIGNvbnN0IGRhdGEgPSB0aGlzLmdldFNjb3BlRGF0YSgpOwoKICAgIHJldHVybiBkYXRhLmF0dGFjaG1lbnRzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgY2xlYXJBdHRhY2htZW50cygpIHsKICAgIHRoaXMuX2F0dGFjaG1lbnRzID0gW107CiAgICByZXR1cm4gdGhpczsKICB9CgogIC8qKiBAaW5oZXJpdERvYyAqLwogICBnZXRTY29wZURhdGEoKSB7CiAgICBjb25zdCB7CiAgICAgIF9icmVhZGNydW1icywKICAgICAgX2F0dGFjaG1lbnRzLAogICAgICBfY29udGV4dHMsCiAgICAgIF90YWdzLAogICAgICBfZXh0cmEsCiAgICAgIF91c2VyLAogICAgICBfbGV2ZWwsCiAgICAgIF9maW5nZXJwcmludCwKICAgICAgX2V2ZW50UHJvY2Vzc29ycywKICAgICAgX3Byb3BhZ2F0aW9uQ29udGV4dCwKICAgICAgX3Nka1Byb2Nlc3NpbmdNZXRhZGF0YSwKICAgICAgX3RyYW5zYWN0aW9uTmFtZSwKICAgICAgX3NwYW4sCiAgICB9ID0gdGhpczsKCiAgICByZXR1cm4gewogICAgICBicmVhZGNydW1iczogX2JyZWFkY3J1bWJzLAogICAgICBhdHRhY2htZW50czogX2F0dGFjaG1lbnRzLAogICAgICBjb250ZXh0czogX2NvbnRleHRzLAogICAgICB0YWdzOiBfdGFncywKICAgICAgZXh0cmE6IF9leHRyYSwKICAgICAgdXNlcjogX3VzZXIsCiAgICAgIGxldmVsOiBfbGV2ZWwsCiAgICAgIGZpbmdlcnByaW50OiBfZmluZ2VycHJpbnQgfHwgW10sCiAgICAgIGV2ZW50UHJvY2Vzc29yczogX2V2ZW50UHJvY2Vzc29ycywKICAgICAgcHJvcGFnYXRpb25Db250ZXh0OiBfcHJvcGFnYXRpb25Db250ZXh0LAogICAgICBzZGtQcm9jZXNzaW5nTWV0YWRhdGE6IF9zZGtQcm9jZXNzaW5nTWV0YWRhdGEsCiAgICAgIHRyYW5zYWN0aW9uTmFtZTogX3RyYW5zYWN0aW9uTmFtZSwKICAgICAgc3BhbjogX3NwYW4sCiAgICB9OwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgc2V0U0RLUHJvY2Vzc2luZ01ldGFkYXRhKG5ld0RhdGEpIHsKICAgIHRoaXMuX3Nka1Byb2Nlc3NpbmdNZXRhZGF0YSA9IHsgLi4udGhpcy5fc2RrUHJvY2Vzc2luZ01ldGFkYXRhLCAuLi5uZXdEYXRhIH07CgogICAgcmV0dXJuIHRoaXM7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqLwogICBzZXRQcm9wYWdhdGlvbkNvbnRleHQoY29udGV4dCkgewogICAgdGhpcy5fcHJvcGFnYXRpb25Db250ZXh0ID0gY29udGV4dDsKICAgIHJldHVybiB0aGlzOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKi8KICAgZ2V0UHJvcGFnYXRpb25Db250ZXh0KCkgewogICAgcmV0dXJuIHRoaXMuX3Byb3BhZ2F0aW9uQ29udGV4dDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNhcHR1cmVFeGNlcHRpb24oZXhjZXB0aW9uLCBoaW50KSB7CiAgICBjb25zdCBldmVudElkID0gaGludCAmJiBoaW50LmV2ZW50X2lkID8gaGludC5ldmVudF9pZCA6IHV1aWQ0KCk7CgogICAgaWYgKCF0aGlzLl9jbGllbnQpIHsKICAgICAgbG9nZ2VyLndhcm4oJ05vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBleGNlcHRpb24hJyk7CiAgICAgIHJldHVybiBldmVudElkOwogICAgfQoKICAgIGNvbnN0IHN5bnRoZXRpY0V4Y2VwdGlvbiA9IG5ldyBFcnJvcignU2VudHJ5IHN5bnRoZXRpY0V4Y2VwdGlvbicpOwoKICAgIHRoaXMuX2NsaWVudC5jYXB0dXJlRXhjZXB0aW9uKAogICAgICBleGNlcHRpb24sCiAgICAgIHsKICAgICAgICBvcmlnaW5hbEV4Y2VwdGlvbjogZXhjZXB0aW9uLAogICAgICAgIHN5bnRoZXRpY0V4Y2VwdGlvbiwKICAgICAgICAuLi5oaW50LAogICAgICAgIGV2ZW50X2lkOiBldmVudElkLAogICAgICB9LAogICAgICB0aGlzLAogICAgKTsKCiAgICByZXR1cm4gZXZlbnRJZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNhcHR1cmVNZXNzYWdlKG1lc3NhZ2UsIGxldmVsLCBoaW50KSB7CiAgICBjb25zdCBldmVudElkID0gaGludCAmJiBoaW50LmV2ZW50X2lkID8gaGludC5ldmVudF9pZCA6IHV1aWQ0KCk7CgogICAgaWYgKCF0aGlzLl9jbGllbnQpIHsKICAgICAgbG9nZ2VyLndhcm4oJ05vIGNsaWVudCBjb25maWd1cmVkIG9uIHNjb3BlIC0gd2lsbCBub3QgY2FwdHVyZSBtZXNzYWdlIScpOwogICAgICByZXR1cm4gZXZlbnRJZDsKICAgIH0KCiAgICBjb25zdCBzeW50aGV0aWNFeGNlcHRpb24gPSBuZXcgRXJyb3IobWVzc2FnZSk7CgogICAgdGhpcy5fY2xpZW50LmNhcHR1cmVNZXNzYWdlKAogICAgICBtZXNzYWdlLAogICAgICBsZXZlbCwKICAgICAgewogICAgICAgIG9yaWdpbmFsRXhjZXB0aW9uOiBtZXNzYWdlLAogICAgICAgIHN5bnRoZXRpY0V4Y2VwdGlvbiwKICAgICAgICAuLi5oaW50LAogICAgICAgIGV2ZW50X2lkOiBldmVudElkLAogICAgICB9LAogICAgICB0aGlzLAogICAgKTsKCiAgICByZXR1cm4gZXZlbnRJZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICovCiAgIGNhcHR1cmVFdmVudChldmVudCwgaGludCkgewogICAgY29uc3QgZXZlbnRJZCA9IGhpbnQgJiYgaGludC5ldmVudF9pZCA/IGhpbnQuZXZlbnRfaWQgOiB1dWlkNCgpOwoKICAgIGlmICghdGhpcy5fY2xpZW50KSB7CiAgICAgIGxvZ2dlci53YXJuKCdObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgZXZlbnQhJyk7CiAgICAgIHJldHVybiBldmVudElkOwogICAgfQoKICAgIHRoaXMuX2NsaWVudC5jYXB0dXJlRXZlbnQoZXZlbnQsIHsgLi4uaGludCwgZXZlbnRfaWQ6IGV2ZW50SWQgfSwgdGhpcyk7CgogICAgcmV0dXJuIGV2ZW50SWQ7CiAgfQoKICAvKioKICAgKiBUaGlzIHdpbGwgYmUgY2FsbGVkIG9uIGV2ZXJ5IHNldCBjYWxsLgogICAqLwogICBfbm90aWZ5U2NvcGVMaXN0ZW5lcnMoKSB7CiAgICAvLyBXZSBuZWVkIHRoaXMgY2hlY2sgZm9yIHRoaXMuX25vdGlmeWluZ0xpc3RlbmVycyB0byBiZSBhYmxlIHRvIHdvcmsgb24gc2NvcGUgZHVyaW5nIHVwZGF0ZXMKICAgIC8vIElmIHRoaXMgY2hlY2sgaXMgbm90IGhlcmUgd2UnbGwgcHJvZHVjZSBlbmRsZXNzIHJlY3Vyc2lvbiB3aGVuIHNvbWV0aGluZyBpcyBkb25lIHdpdGggdGhlIHNjb3BlCiAgICAvLyBkdXJpbmcgdGhlIGNhbGxiYWNrLgogICAgaWYgKCF0aGlzLl9ub3RpZnlpbmdMaXN0ZW5lcnMpIHsKICAgICAgdGhpcy5fbm90aWZ5aW5nTGlzdGVuZXJzID0gdHJ1ZTsKICAgICAgdGhpcy5fc2NvcGVMaXN0ZW5lcnMuZm9yRWFjaChjYWxsYmFjayA9PiB7CiAgICAgICAgY2FsbGJhY2sodGhpcyk7CiAgICAgIH0pOwogICAgICB0aGlzLl9ub3RpZnlpbmdMaXN0ZW5lcnMgPSBmYWxzZTsKICAgIH0KICB9Cn0KCmZ1bmN0aW9uIGdlbmVyYXRlUHJvcGFnYXRpb25Db250ZXh0KCkgewogIHJldHVybiB7CiAgICB0cmFjZUlkOiB1dWlkNCgpLAogICAgc3BhbklkOiB1dWlkNCgpLnN1YnN0cmluZygxNiksCiAgfTsKfQoKY29uc3QgU0RLX1ZFUlNJT04gPSAnOC4wLjAtYWxwaGEuMyc7CgovKioKICogQVBJIGNvbXBhdGliaWxpdHkgdmVyc2lvbiBvZiB0aGlzIGh1Yi4KICoKICogV0FSTklORzogVGhpcyBudW1iZXIgc2hvdWxkIG9ubHkgYmUgaW5jcmVhc2VkIHdoZW4gdGhlIGdsb2JhbCBpbnRlcmZhY2UKICogY2hhbmdlcyBhbmQgbmV3IG1ldGhvZHMgYXJlIGludHJvZHVjZWQuCiAqCiAqIEBoaWRkZW4KICovCmNvbnN0IEFQSV9WRVJTSU9OID0gcGFyc2VGbG9hdChTREtfVkVSU0lPTik7CgovKioKICogRGVmYXVsdCBtYXhpbXVtIG51bWJlciBvZiBicmVhZGNydW1icyBhZGRlZCB0byBhbiBldmVudC4gQ2FuIGJlIG92ZXJ3cml0dGVuCiAqIHdpdGgge0BsaW5rIE9wdGlvbnMubWF4QnJlYWRjcnVtYnN9LgogKi8KY29uc3QgREVGQVVMVF9CUkVBRENSVU1CUyQxID0gMTAwOwoKLyoqCiAqIEEgbGF5ZXIgaW4gdGhlIHByb2Nlc3Mgc3RhY2suCiAqIEBoaWRkZW4KICovCgovKioKICogQGluaGVyaXREb2MKICovCmNsYXNzIEh1YiAgewogIC8qKiBJcyBhIHtAbGluayBMYXllcn1bXSBjb250YWluaW5nIHRoZSBjbGllbnQgYW5kIHNjb3BlICovCgogIC8qKgogICAqIENyZWF0ZXMgYSBuZXcgaW5zdGFuY2Ugb2YgdGhlIGh1Yiwgd2lsbCBwdXNoIG9uZSB7QGxpbmsgTGF5ZXJ9IGludG8gdGhlCiAgICogaW50ZXJuYWwgc3RhY2sgb24gY3JlYXRpb24uCiAgICoKICAgKiBAcGFyYW0gY2xpZW50IGJvdW5kIHRvIHRoZSBodWIuCiAgICogQHBhcmFtIHNjb3BlIGJvdW5kIHRvIHRoZSBodWIuCiAgICogQHBhcmFtIHZlcnNpb24gbnVtYmVyLCBoaWdoZXIgbnVtYmVyIG1lYW5zIGhpZ2hlciBwcmlvcml0eS4KICAgKgogICAqIEBkZXByZWNhdGVkIEluc3RhbnRpYXRpb24gb2YgSHViIG9iamVjdHMgaXMgZGVwcmVjYXRlZCBhbmQgdGhlIGNvbnN0cnVjdG9yIHdpbGwgYmUgcmVtb3ZlZCBpbiB2ZXJzaW9uIDggb2YgdGhlIFNESy4KICAgKgogICAqIElmIHlvdSBhcmUgY3VycmVudGx5IHVzaW5nIHRoZSBIdWIgZm9yIG11bHRpLWNsaWVudCB1c2UgbGlrZSBzbzoKICAgKgogICAqIGBgYAogICAqIC8vIE9MRAogICAqIGNvbnN0IGh1YiA9IG5ldyBIdWIoKTsKICAgKiBodWIuYmluZENsaWVudChjbGllbnQpOwogICAqIG1ha2VNYWluKGh1YikKICAgKiBgYGAKICAgKgogICAqIGluc3RlYWQgaW5pdGlhbGl6ZSB0aGUgY2xpZW50IGFzIGZvbGxvd3M6CiAgICoKICAgKiBgYGAKICAgKiAvLyBORVcKICAgKiBTZW50cnkud2l0aElzb2xhdGlvblNjb3BlKCgpID0+IHsKICAgKiAgICBTZW50cnkuc2V0Q3VycmVudENsaWVudChjbGllbnQpOwogICAqICAgIGNsaWVudC5pbml0KCk7CiAgICogfSk7CiAgICogYGBgCiAgICoKICAgKiBJZiB5b3UgYXJlIHVzaW5nIHRoZSBIdWIgdG8gY2FwdHVyZSBldmVudHMgbGlrZSBzbzoKICAgKgogICAqIGBgYAogICAqIC8vIE9MRAogICAqIGNvbnN0IGNsaWVudCA9IG5ldyBDbGllbnQoKTsKICAgKiBjb25zdCBodWIgPSBuZXcgSHViKGNsaWVudCk7CiAgICogaHViLmNhcHR1cmVFeGNlcHRpb24oKQogICAqIGBgYAogICAqCiAgICogaW5zdGVhZCBjYXB0dXJlIGlzb2xhdGVkIGV2ZW50cyBhcyBmb2xsb3dzOgogICAqCiAgICogYGBgCiAgICogLy8gTkVXCiAgICogY29uc3QgY2xpZW50ID0gbmV3IENsaWVudCgpOwogICAqIGNvbnN0IHNjb3BlID0gbmV3IFNjb3BlKCk7CiAgICogc2NvcGUuc2V0Q2xpZW50KGNsaWVudCk7CiAgICogc2NvcGUuY2FwdHVyZUV4Y2VwdGlvbigpOwogICAqIGBgYAogICAqLwogICBjb25zdHJ1Y3RvcigKICAgIGNsaWVudCwKICAgIHNjb3BlLAogICAgaXNvbGF0aW9uU2NvcGUsCiAgICAgIF92ZXJzaW9uID0gQVBJX1ZFUlNJT04sCiAgKSB7dGhpcy5fdmVyc2lvbiA9IF92ZXJzaW9uOwogICAgbGV0IGFzc2lnbmVkU2NvcGU7CiAgICBpZiAoIXNjb3BlKSB7CiAgICAgIGFzc2lnbmVkU2NvcGUgPSBuZXcgU2NvcGUoKTsKICAgICAgYXNzaWduZWRTY29wZS5zZXRDbGllbnQoY2xpZW50KTsKICAgIH0gZWxzZSB7CiAgICAgIGFzc2lnbmVkU2NvcGUgPSBzY29wZTsKICAgIH0KCiAgICBsZXQgYXNzaWduZWRJc29sYXRpb25TY29wZTsKICAgIGlmICghaXNvbGF0aW9uU2NvcGUpIHsKICAgICAgYXNzaWduZWRJc29sYXRpb25TY29wZSA9IG5ldyBTY29wZSgpOwogICAgICBhc3NpZ25lZElzb2xhdGlvblNjb3BlLnNldENsaWVudChjbGllbnQpOwogICAgfSBlbHNlIHsKICAgICAgYXNzaWduZWRJc29sYXRpb25TY29wZSA9IGlzb2xhdGlvblNjb3BlOwogICAgfQoKICAgIHRoaXMuX3N0YWNrID0gW3sgc2NvcGU6IGFzc2lnbmVkU2NvcGUgfV07CgogICAgaWYgKGNsaWVudCkgewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgICAgdGhpcy5iaW5kQ2xpZW50KGNsaWVudCk7CiAgICB9CgogICAgdGhpcy5faXNvbGF0aW9uU2NvcGUgPSBhc3NpZ25lZElzb2xhdGlvblNjb3BlOwogIH0KCiAgLyoqCiAgICogQ2hlY2tzIGlmIHRoaXMgaHViJ3MgdmVyc2lvbiBpcyBvbGRlciB0aGFuIHRoZSBnaXZlbiB2ZXJzaW9uLgogICAqCiAgICogQHBhcmFtIHZlcnNpb24gQSB2ZXJzaW9uIG51bWJlciB0byBjb21wYXJlIHRvLgogICAqIEByZXR1cm4gVHJ1ZSBpZiB0aGUgZ2l2ZW4gdmVyc2lvbiBpcyBuZXdlcjsgb3RoZXJ3aXNlIGZhbHNlLgogICAqCiAgICogQGRlcHJlY2F0ZWQgVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjguCiAgICovCiAgIGlzT2xkZXJUaGFuKHZlcnNpb24pIHsKICAgIHJldHVybiB0aGlzLl92ZXJzaW9uIDwgdmVyc2lvbjsKICB9CgogIC8qKgogICAqIFRoaXMgYmluZHMgdGhlIGdpdmVuIGNsaWVudCB0byB0aGUgY3VycmVudCBzY29wZS4KICAgKiBAcGFyYW0gY2xpZW50IEFuIFNESyBjbGllbnQgKGNsaWVudCkgaW5zdGFuY2UuCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYGluaXRBbmRCaW5kKClgIGRpcmVjdGx5LCBvciBgc2V0Q3VycmVudENsaWVudCgpYCBhbmQvb3IgYGNsaWVudC5pbml0KClgIGluc3RlYWQuCiAgICovCiAgIGJpbmRDbGllbnQoY2xpZW50KSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IHRvcCA9IHRoaXMuZ2V0U3RhY2tUb3AoKTsKICAgIHRvcC5jbGllbnQgPSBjbGllbnQ7CiAgICB0b3Auc2NvcGUuc2V0Q2xpZW50KGNsaWVudCk7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGlmIChjbGllbnQgJiYgY2xpZW50LnNldHVwSW50ZWdyYXRpb25zKSB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgICBjbGllbnQuc2V0dXBJbnRlZ3JhdGlvbnMoKTsKICAgIH0KICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYHdpdGhTY29wZWAgaW5zdGVhZC4KICAgKi8KICAgcHVzaFNjb3BlKCkgewogICAgLy8gV2Ugd2FudCB0byBjbG9uZSB0aGUgY29udGVudCBvZiBwcmV2IHNjb3BlCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IHNjb3BlID0gdGhpcy5nZXRTY29wZSgpLmNsb25lKCk7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0U3RhY2soKS5wdXNoKHsKICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgIGNsaWVudDogdGhpcy5nZXRDbGllbnQoKSwKICAgICAgc2NvcGUsCiAgICB9KTsKICAgIHJldHVybiBzY29wZTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYHdpdGhTY29wZWAgaW5zdGVhZC4KICAgKi8KICAgcG9wU2NvcGUoKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGlmICh0aGlzLmdldFN0YWNrKCkubGVuZ3RoIDw9IDEpIHJldHVybiBmYWxzZTsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuICEhdGhpcy5nZXRTdGFjaygpLnBvcCgpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKgogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LndpdGhTY29wZSgpYCBpbnN0ZWFkLgogICAqLwogICB3aXRoU2NvcGUoY2FsbGJhY2spIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgY29uc3Qgc2NvcGUgPSB0aGlzLnB1c2hTY29wZSgpOwoKICAgIGxldCBtYXliZVByb21pc2VSZXN1bHQ7CiAgICB0cnkgewogICAgICBtYXliZVByb21pc2VSZXN1bHQgPSBjYWxsYmFjayhzY29wZSk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgICB0aGlzLnBvcFNjb3BlKCk7CiAgICAgIHRocm93IGU7CiAgICB9CgogICAgaWYgKGlzVGhlbmFibGUobWF5YmVQcm9taXNlUmVzdWx0KSkgewogICAgICAvLyBAdHMtZXhwZWN0LWVycm9yIC0gaXNUaGVuYWJsZSByZXR1cm5zIHRoZSB3cm9uZyB0eXBlCiAgICAgIHJldHVybiBtYXliZVByb21pc2VSZXN1bHQudGhlbigKICAgICAgICByZXMgPT4gewogICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgICAgICB0aGlzLnBvcFNjb3BlKCk7CiAgICAgICAgICByZXR1cm4gcmVzOwogICAgICAgIH0sCiAgICAgICAgZSA9PiB7CiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgICAgICAgIHRoaXMucG9wU2NvcGUoKTsKICAgICAgICAgIHRocm93IGU7CiAgICAgICAgfSwKICAgICAgKTsKICAgIH0KCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMucG9wU2NvcGUoKTsKICAgIHJldHVybiBtYXliZVByb21pc2VSZXN1bHQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuZ2V0Q2xpZW50KClgIGluc3RlYWQuCiAgICovCiAgIGdldENsaWVudCgpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5jbGllbnQgOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgc2NvcGUgb2YgdGhlIHRvcCBzdGFjay4KICAgKgogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LmdldEN1cnJlbnRTY29wZSgpYCBpbnN0ZWFkLgogICAqLwogICBnZXRTY29wZSgpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5zY29wZTsKICB9CgogIC8qKgogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LmdldElzb2xhdGlvblNjb3BlKClgIGluc3RlYWQuCiAgICovCiAgIGdldElzb2xhdGlvblNjb3BlKCkgewogICAgcmV0dXJuIHRoaXMuX2lzb2xhdGlvblNjb3BlOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgc2NvcGUgc3RhY2sgZm9yIGRvbWFpbnMgb3IgdGhlIHByb2Nlc3MuCiAgICogQGRlcHJlY2F0ZWQgVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjguCiAgICovCiAgIGdldFN0YWNrKCkgewogICAgcmV0dXJuIHRoaXMuX3N0YWNrOwogIH0KCiAgLyoqCiAgICogUmV0dXJucyB0aGUgdG9wbW9zdCBzY29wZSBsYXllciBpbiB0aGUgb3JkZXIgZG9tYWluID4gbG9jYWwgPiBwcm9jZXNzLgogICAqIEBkZXByZWNhdGVkIFRoaXMgd2lsbCBiZSByZW1vdmVkIGluIHY4LgogICAqLwogICBnZXRTdGFja1RvcCgpIHsKICAgIHJldHVybiB0aGlzLl9zdGFja1t0aGlzLl9zdGFjay5sZW5ndGggLSAxXTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5jYXB0dXJlRXhjZXB0aW9uKClgIGluc3RlYWQuCiAgICovCiAgIGNhcHR1cmVFeGNlcHRpb24oZXhjZXB0aW9uLCBoaW50KSB7CiAgICBjb25zdCBldmVudElkID0gaGludCAmJiBoaW50LmV2ZW50X2lkID8gaGludC5ldmVudF9pZCA6IHV1aWQ0KCk7CiAgICBjb25zdCBzeW50aGV0aWNFeGNlcHRpb24gPSBuZXcgRXJyb3IoJ1NlbnRyeSBzeW50aGV0aWNFeGNlcHRpb24nKTsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRTY29wZSgpLmNhcHR1cmVFeGNlcHRpb24oZXhjZXB0aW9uLCB7CiAgICAgIG9yaWdpbmFsRXhjZXB0aW9uOiBleGNlcHRpb24sCiAgICAgIHN5bnRoZXRpY0V4Y2VwdGlvbiwKICAgICAgLi4uaGludCwKICAgICAgZXZlbnRfaWQ6IGV2ZW50SWQsCiAgICB9KTsKCiAgICByZXR1cm4gZXZlbnRJZDsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgIGBTZW50cnkuY2FwdHVyZU1lc3NhZ2UoKWAgaW5zdGVhZC4KICAgKi8KICAgY2FwdHVyZU1lc3NhZ2UobWVzc2FnZSwgbGV2ZWwsIGhpbnQpIHsKICAgIGNvbnN0IGV2ZW50SWQgPSBoaW50ICYmIGhpbnQuZXZlbnRfaWQgPyBoaW50LmV2ZW50X2lkIDogdXVpZDQoKTsKICAgIGNvbnN0IHN5bnRoZXRpY0V4Y2VwdGlvbiA9IG5ldyBFcnJvcihtZXNzYWdlKTsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRTY29wZSgpLmNhcHR1cmVNZXNzYWdlKG1lc3NhZ2UsIGxldmVsLCB7CiAgICAgIG9yaWdpbmFsRXhjZXB0aW9uOiBtZXNzYWdlLAogICAgICBzeW50aGV0aWNFeGNlcHRpb24sCiAgICAgIC4uLmhpbnQsCiAgICAgIGV2ZW50X2lkOiBldmVudElkLAogICAgfSk7CgogICAgcmV0dXJuIGV2ZW50SWQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuY2FwdHVyZUV2ZW50KClgIGluc3RlYWQuCiAgICovCiAgIGNhcHR1cmVFdmVudChldmVudCwgaGludCkgewogICAgY29uc3QgZXZlbnRJZCA9IGhpbnQgJiYgaGludC5ldmVudF9pZCA/IGhpbnQuZXZlbnRfaWQgOiB1dWlkNCgpOwogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICB0aGlzLmdldFNjb3BlKCkuY2FwdHVyZUV2ZW50KGV2ZW50LCB7IC4uLmhpbnQsIGV2ZW50X2lkOiBldmVudElkIH0pOwogICAgcmV0dXJuIGV2ZW50SWQ7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuYWRkQnJlYWRjcnVtYigpYCBpbnN0ZWFkLgogICAqLwogICBhZGRCcmVhZGNydW1iKGJyZWFkY3J1bWIsIGhpbnQpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgY29uc3QgeyBjbGllbnQgfSA9IHRoaXMuZ2V0U3RhY2tUb3AoKTsKCiAgICBpZiAoIWNsaWVudCkgcmV0dXJuOwoKICAgIGNvbnN0IHsgYmVmb3JlQnJlYWRjcnVtYiA9IG51bGwsIG1heEJyZWFkY3J1bWJzID0gREVGQVVMVF9CUkVBRENSVU1CUyQxIH0gPQogICAgICAoY2xpZW50LmdldE9wdGlvbnMgJiYgY2xpZW50LmdldE9wdGlvbnMoKSkgfHwge307CgogICAgaWYgKG1heEJyZWFkY3J1bWJzIDw9IDApIHJldHVybjsKCiAgICBjb25zdCB0aW1lc3RhbXAgPSBkYXRlVGltZXN0YW1wSW5TZWNvbmRzKCk7CiAgICBjb25zdCBtZXJnZWRCcmVhZGNydW1iID0geyB0aW1lc3RhbXAsIC4uLmJyZWFkY3J1bWIgfTsKICAgIGNvbnN0IGZpbmFsQnJlYWRjcnVtYiA9IGJlZm9yZUJyZWFkY3J1bWIKICAgICAgPyAoY29uc29sZVNhbmRib3goKCkgPT4gYmVmb3JlQnJlYWRjcnVtYihtZXJnZWRCcmVhZGNydW1iLCBoaW50KSkgKQogICAgICA6IG1lcmdlZEJyZWFkY3J1bWI7CgogICAgaWYgKGZpbmFsQnJlYWRjcnVtYiA9PT0gbnVsbCkgcmV0dXJuOwoKICAgIGNsaWVudC5lbWl0KCdiZWZvcmVBZGRCcmVhZGNydW1iJywgZmluYWxCcmVhZGNydW1iLCBoaW50KTsKCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5hZGRCcmVhZGNydW1iKGZpbmFsQnJlYWRjcnVtYiwgbWF4QnJlYWRjcnVtYnMpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5zZXRVc2VyKClgIGluc3RlYWQuCiAgICovCiAgIHNldFVzZXIodXNlcikgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICB0aGlzLmdldElzb2xhdGlvblNjb3BlKCkuc2V0VXNlcih1c2VyKTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuc2V0VGFncygpYCBpbnN0ZWFkLgogICAqLwogICBzZXRUYWdzKHRhZ3MpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRJc29sYXRpb25TY29wZSgpLnNldFRhZ3ModGFncyk7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LnNldEV4dHJhcygpYCBpbnN0ZWFkLgogICAqLwogICBzZXRFeHRyYXMoZXh0cmFzKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5zZXRFeHRyYXMoZXh0cmFzKTsKICB9CgogIC8qKgogICAqIEBpbmhlcml0RG9jCiAgICogQGRlcHJlY2F0ZWQgVXNlIGBTZW50cnkuc2V0VGFnKClgIGluc3RlYWQuCiAgICovCiAgIHNldFRhZyhrZXksIHZhbHVlKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5zZXRUYWcoa2V5LCB2YWx1ZSk7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqIEBkZXByZWNhdGVkIFVzZSBgU2VudHJ5LnNldEV4dHJhKClgIGluc3RlYWQuCiAgICovCiAgIHNldEV4dHJhKGtleSwgZXh0cmEpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5nZXRJc29sYXRpb25TY29wZSgpLnNldEV4dHJhKGtleSwgZXh0cmEpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5zZXRDb250ZXh0KClgIGluc3RlYWQuCiAgICovCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkKICAgc2V0Q29udGV4dChuYW1lLCBjb250ZXh0KSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIHRoaXMuZ2V0SXNvbGF0aW9uU2NvcGUoKS5zZXRDb250ZXh0KG5hbWUsIGNvbnRleHQpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgYFNlbnRyeS5nZXRDbGllbnQoKS5nZXRJbnRlZ3JhdGlvbkJ5TmFtZSgpYCBpbnN0ZWFkLgogICAqLwogICBnZXRJbnRlZ3JhdGlvbihpbnRlZ3JhdGlvbikgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICBjb25zdCBjbGllbnQgPSB0aGlzLmdldENsaWVudCgpOwogICAgaWYgKCFjbGllbnQpIHJldHVybiBudWxsOwogICAgdHJ5IHsKICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICAgIHJldHVybiBjbGllbnQuZ2V0SW50ZWdyYXRpb24oaW50ZWdyYXRpb24pOwogICAgfSBjYXRjaCAoX29PKSB7CiAgICAgIERFQlVHX0JVSUxEICYmIGxvZ2dlci53YXJuKGBDYW5ub3QgcmV0cmlldmUgaW50ZWdyYXRpb24gJHtpbnRlZ3JhdGlvbi5pZH0gZnJvbSB0aGUgY3VycmVudCBIdWJgKTsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqCiAgICogQGRlcHJlY2F0ZWQgVXNlIHRvcCBsZXZlbCBgY2FwdHVyZVNlc3Npb25gIGluc3RlYWQuCiAgICovCiAgIGNhcHR1cmVTZXNzaW9uKGVuZFNlc3Npb24gPSBmYWxzZSkgewogICAgLy8gYm90aCBzZW5kIHRoZSB1cGRhdGUgYW5kIHB1bGwgdGhlIHNlc3Npb24gZnJvbSB0aGUgc2NvcGUKICAgIGlmIChlbmRTZXNzaW9uKSB7CiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgICByZXR1cm4gdGhpcy5lbmRTZXNzaW9uKCk7CiAgICB9CgogICAgLy8gb25seSBzZW5kIHRoZSB1cGRhdGUKICAgIHRoaXMuX3NlbmRTZXNzaW9uVXBkYXRlKCk7CiAgfQoKICAvKioKICAgKiBAaW5oZXJpdERvYwogICAqIEBkZXByZWNhdGVkIFVzZSB0b3AgbGV2ZWwgYGVuZFNlc3Npb25gIGluc3RlYWQuCiAgICovCiAgIGVuZFNlc3Npb24oKSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IGxheWVyID0gdGhpcy5nZXRTdGFja1RvcCgpOwogICAgY29uc3Qgc2NvcGUgPSBsYXllci5zY29wZTsKICAgIGNvbnN0IHNlc3Npb24gPSBzY29wZS5nZXRTZXNzaW9uKCk7CiAgICBpZiAoc2Vzc2lvbikgewogICAgICBjbG9zZVNlc3Npb24oc2Vzc2lvbik7CiAgICB9CiAgICB0aGlzLl9zZW5kU2Vzc2lvblVwZGF0ZSgpOwoKICAgIC8vIHRoZSBzZXNzaW9uIGlzIG92ZXI7IHRha2UgaXQgb2ZmIG9mIHRoZSBzY29wZQogICAgc2NvcGUuc2V0U2Vzc2lvbigpOwogIH0KCiAgLyoqCiAgICogQGluaGVyaXREb2MKICAgKiBAZGVwcmVjYXRlZCBVc2UgdG9wIGxldmVsIGBzdGFydFNlc3Npb25gIGluc3RlYWQuCiAgICovCiAgIHN0YXJ0U2Vzc2lvbihjb250ZXh0KSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGNvbnN0IHsgc2NvcGUsIGNsaWVudCB9ID0gdGhpcy5nZXRTdGFja1RvcCgpOwogICAgY29uc3QgeyByZWxlYXNlLCBlbnZpcm9ubWVudCA9IERFRkFVTFRfRU5WSVJPTk1FTlQgfSA9IChjbGllbnQgJiYgY2xpZW50LmdldE9wdGlvbnMoKSkgfHwge307CgogICAgLy8gV2lsbCBmZXRjaCB1c2VyQWdlbnQgaWYgY2FsbGVkIGZyb20gYnJvd3NlciBzZGsKICAgIGNvbnN0IHsgdXNlckFnZW50IH0gPSBHTE9CQUxfT0JKLm5hdmlnYXRvciB8fCB7fTsKCiAgICBjb25zdCBzZXNzaW9uID0gbWFrZVNlc3Npb24oewogICAgICByZWxlYXNlLAogICAgICBlbnZpcm9ubWVudCwKICAgICAgdXNlcjogc2NvcGUuZ2V0VXNlcigpLAogICAgICAuLi4odXNlckFnZW50ICYmIHsgdXNlckFnZW50IH0pLAogICAgICAuLi5jb250ZXh0LAogICAgfSk7CgogICAgLy8gRW5kIGV4aXN0aW5nIHNlc3Npb24gaWYgdGhlcmUncyBvbmUKICAgIGNvbnN0IGN1cnJlbnRTZXNzaW9uID0gc2NvcGUuZ2V0U2Vzc2lvbiAmJiBzY29wZS5nZXRTZXNzaW9uKCk7CiAgICBpZiAoY3VycmVudFNlc3Npb24gJiYgY3VycmVudFNlc3Npb24uc3RhdHVzID09PSAnb2snKSB7CiAgICAgIHVwZGF0ZVNlc3Npb24oY3VycmVudFNlc3Npb24sIHsgc3RhdHVzOiAnZXhpdGVkJyB9KTsKICAgIH0KICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgdGhpcy5lbmRTZXNzaW9uKCk7CgogICAgLy8gQWZ0ZXJ3YXJkcyB3ZSBzZXQgdGhlIG5ldyBzZXNzaW9uIG9uIHRoZSBzY29wZQogICAgc2NvcGUuc2V0U2Vzc2lvbihzZXNzaW9uKTsKCiAgICByZXR1cm4gc2Vzc2lvbjsKICB9CgogIC8qKgogICAqIFJldHVybnMgaWYgZGVmYXVsdCBQSUkgc2hvdWxkIGJlIHNlbnQgdG8gU2VudHJ5IGFuZCBwcm9wYWdhdGVkIGluIG91cmdvaW5nIHJlcXVlc3RzCiAgICogd2hlbiBUcmFjaW5nIGlzIHVzZWQuCiAgICoKICAgKiBAZGVwcmVjYXRlZCBVc2UgdG9wLWxldmVsIGBnZXRDbGllbnQoKS5nZXRPcHRpb25zKCkuc2VuZERlZmF1bHRQaWlgIGluc3RlYWQuIFRoaXMgZnVuY3Rpb24KICAgKiBvbmx5IHVubmVjZXNzYXJpbHkgaW5jcmVhc2VkIEFQSSBzdXJmYWNlIGJ1dCBvbmx5IHdyYXBwZWQgYWNjZXNzaW5nIHRoZSBvcHRpb24uCiAgICovCiAgIHNob3VsZFNlbmREZWZhdWx0UGlpKCkgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgICBjb25zdCBjbGllbnQgPSB0aGlzLmdldENsaWVudCgpOwogICAgY29uc3Qgb3B0aW9ucyA9IGNsaWVudCAmJiBjbGllbnQuZ2V0T3B0aW9ucygpOwogICAgcmV0dXJuIEJvb2xlYW4ob3B0aW9ucyAmJiBvcHRpb25zLnNlbmREZWZhdWx0UGlpKTsKICB9CgogIC8qKgogICAqIFNlbmRzIHRoZSBjdXJyZW50IFNlc3Npb24gb24gdGhlIHNjb3BlCiAgICovCiAgIF9zZW5kU2Vzc2lvblVwZGF0ZSgpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgY29uc3QgeyBzY29wZSwgY2xpZW50IH0gPSB0aGlzLmdldFN0YWNrVG9wKCk7CgogICAgY29uc3Qgc2Vzc2lvbiA9IHNjb3BlLmdldFNlc3Npb24oKTsKICAgIGlmIChzZXNzaW9uICYmIGNsaWVudCAmJiBjbGllbnQuY2FwdHVyZVNlc3Npb24pIHsKICAgICAgY2xpZW50LmNhcHR1cmVTZXNzaW9uKHNlc3Npb24pOwogICAgfQogIH0KCiAgLyoqCiAgICogQ2FsbHMgZ2xvYmFsIGV4dGVuc2lvbiBtZXRob2QgYW5kIGJpbmRpbmcgY3VycmVudCBpbnN0YW5jZSB0byB0aGUgZnVuY3Rpb24gY2FsbAogICAqLwogIC8vIEB0cy1leHBlY3QtZXJyb3IgRnVuY3Rpb24gbGFja3MgZW5kaW5nIHJldHVybiBzdGF0ZW1lbnQgYW5kIHJldHVybiB0eXBlIGRvZXMgbm90IGluY2x1ZGUgJ3VuZGVmaW5lZCcuIHRzKDIzNjYpCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnkKICAgX2NhbGxFeHRlbnNpb25NZXRob2QobWV0aG9kLCAuLi5hcmdzKSB7CiAgICBjb25zdCBjYXJyaWVyID0gZ2V0TWFpbkNhcnJpZXIoKTsKICAgIGNvbnN0IHNlbnRyeSA9IGdldFNlbnRyeUNhcnJpZXIoY2Fycmllcik7CiAgICBpZiAoc2VudHJ5LmV4dGVuc2lvbnMgJiYgdHlwZW9mIHNlbnRyeS5leHRlbnNpb25zW21ldGhvZF0gPT09ICdmdW5jdGlvbicpIHsKICAgICAgcmV0dXJuIHNlbnRyeS5leHRlbnNpb25zW21ldGhvZF0uYXBwbHkodGhpcywgYXJncyk7CiAgICB9CiAgICBERUJVR19CVUlMRCAmJiBsb2dnZXIud2FybihgRXh0ZW5zaW9uIG1ldGhvZCAke21ldGhvZH0gY291bGRuJ3QgYmUgZm91bmQsIGRvaW5nIG5vdGhpbmcuYCk7CiAgfQp9CgovKiogR2V0IHRoZSBkZWZhdWx0IGN1cnJlbnQgc2NvcGUuICovCmZ1bmN0aW9uIGdldERlZmF1bHRDdXJyZW50U2NvcGUoKSB7CiAgcmV0dXJuIGdldEdsb2JhbFNpbmdsZXRvbignZGVmYXVsdEN1cnJlbnRTY29wZScsICgpID0+IG5ldyBTY29wZSgpKTsKfQoKLyoqIEdldCB0aGUgZGVmYXVsdCBpc29sYXRpb24gc2NvcGUuICovCmZ1bmN0aW9uIGdldERlZmF1bHRJc29sYXRpb25TY29wZSgpIHsKICByZXR1cm4gZ2V0R2xvYmFsU2luZ2xldG9uKCdkZWZhdWx0SXNvbGF0aW9uU2NvcGUnLCAoKSA9PiBuZXcgU2NvcGUoKSk7Cn0KCi8qKgogKiBHZXQgdGhlIGdsb2JhbCBodWIuCiAqIFRoaXMgd2lsbCBiZSByZW1vdmVkIGR1cmluZyB0aGUgdjggY3ljbGUgYW5kIGlzIG9ubHkgaGVyZSB0byBtYWtlIG1pZ3JhdGlvbiBlYXNpZXIuCiAqLwpmdW5jdGlvbiBnZXRHbG9iYWxIdWIoKSB7CiAgY29uc3QgcmVnaXN0cnkgPSBnZXRNYWluQ2FycmllcigpOwogIGNvbnN0IHNlbnRyeSA9IGdldFNlbnRyeUNhcnJpZXIocmVnaXN0cnkpIDsKCiAgLy8gSWYgdGhlcmUncyBubyBodWIsIG9yIGl0cyBhbiBvbGQgQVBJLCBhc3NpZ24gYSBuZXcgb25lCiAgaWYgKHNlbnRyeS5odWIpIHsKICAgIHJldHVybiBzZW50cnkuaHViOwogIH0KCiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCiAgc2VudHJ5Lmh1YiA9IG5ldyBIdWIodW5kZWZpbmVkLCBnZXREZWZhdWx0Q3VycmVudFNjb3BlKCksIGdldERlZmF1bHRJc29sYXRpb25TY29wZSgpKTsKICByZXR1cm4gc2VudHJ5Lmh1YjsKfQoKLyoqCiAqIEdldCB0aGUgY3VycmVudCBhc3luYyBjb250ZXh0IHN0cmF0ZWd5LgogKiBJZiBub25lIGhhcyBiZWVuIHNldHVwLCB0aGUgZGVmYXVsdCB3aWxsIGJlIHVzZWQuCiAqLwpmdW5jdGlvbiBnZXRBc3luY0NvbnRleHRTdHJhdGVneShjYXJyaWVyKSB7CiAgY29uc3Qgc2VudHJ5ID0gZ2V0U2VudHJ5Q2FycmllcihjYXJyaWVyKTsKCiAgaWYgKHNlbnRyeS5hY3MpIHsKICAgIHJldHVybiBzZW50cnkuYWNzOwogIH0KCiAgLy8gT3RoZXJ3aXNlLCB1c2UgdGhlIGRlZmF1bHQgb25lCiAgcmV0dXJuIGdldEh1YlN0YWNrQXN5bmNDb250ZXh0U3RyYXRlZ3koKTsKfQoKZnVuY3Rpb24gd2l0aFNjb3BlJDEoY2FsbGJhY2spIHsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICByZXR1cm4gZ2V0R2xvYmFsSHViKCkud2l0aFNjb3BlKGNhbGxiYWNrKTsKfQoKZnVuY3Rpb24gd2l0aFNldFNjb3BlKHNjb3BlLCBjYWxsYmFjaykgewogIGNvbnN0IGh1YiA9IGdldEdsb2JhbEh1YigpIDsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICByZXR1cm4gaHViLndpdGhTY29wZSgoKSA9PiB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICAgIGh1Yi5nZXRTdGFja1RvcCgpLnNjb3BlID0gc2NvcGUgOwogICAgcmV0dXJuIGNhbGxiYWNrKHNjb3BlKTsKICB9KTsKfQoKZnVuY3Rpb24gd2l0aElzb2xhdGlvblNjb3BlJDEoY2FsbGJhY2spIHsKICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KICByZXR1cm4gZ2V0R2xvYmFsSHViKCkud2l0aFNjb3BlKCgpID0+IHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgogICAgcmV0dXJuIGNhbGxiYWNrKGdldEdsb2JhbEh1YigpLmdldElzb2xhdGlvblNjb3BlKCkpOwogIH0pOwp9CgovKiBlc2xpbnQtZGlzYWJsZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbiAqLwpmdW5jdGlvbiBnZXRIdWJTdGFja0FzeW5jQ29udGV4dFN0cmF0ZWd5KCkgewogIHJldHVybiB7CiAgICBnZXRDdXJyZW50SHViOiBnZXRHbG9iYWxIdWIsCiAgICB3aXRoSXNvbGF0aW9uU2NvcGU6IHdpdGhJc29sYXRpb25TY29wZSQxLAogICAgd2l0aFNjb3BlOiB3aXRoU2NvcGUkMSwKICAgIHdpdGhTZXRTY29wZSwKICAgIHdpdGhTZXRJc29sYXRpb25TY29wZTogKF9pc29sYXRpb25TY29wZSwgY2FsbGJhY2spID0+IHsKICAgICAgcmV0dXJuIHdpdGhJc29sYXRpb25TY29wZSQxKGNhbGxiYWNrKTsKICAgIH0sCiAgICBnZXRDdXJyZW50U2NvcGU6ICgpID0+IGdldEdsb2JhbEh1YigpLmdldFNjb3BlKCksCiAgICBnZXRJc29sYXRpb25TY29wZTogKCkgPT4gZ2V0R2xvYmFsSHViKCkuZ2V0SXNvbGF0aW9uU2NvcGUoKSwKICB9Owp9Ci8qIGVzbGludC1lbmFibGUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24gKi8KCi8qKgogKiBHZXQgdGhlIGN1cnJlbnRseSBhY3RpdmUgc2NvcGUuCiAqLwpmdW5jdGlvbiBnZXRDdXJyZW50U2NvcGUoKSB7CiAgY29uc3QgY2FycmllciA9IGdldE1haW5DYXJyaWVyKCk7CiAgY29uc3QgYWNzID0gZ2V0QXN5bmNDb250ZXh0U3RyYXRlZ3koY2Fycmllcik7CiAgcmV0dXJuIGFjcy5nZXRDdXJyZW50U2NvcGUoKTsKfQoKLyoqCiAqIEdldCB0aGUgY3VycmVudGx5IGFjdGl2ZSBjbGllbnQuCiAqLwpmdW5jdGlvbiBnZXRDbGllbnQoKSB7CiAgcmV0dXJuIGdldEN1cnJlbnRTY29wZSgpLmdldENsaWVudCgpOwp9CgovKioKICogRmV0Y2hlcyB0aGUgbWV0cmljIHN1bW1hcnkgaWYgaXQgZXhpc3RzIGZvciB0aGUgcGFzc2VkIHNwYW4KICovCmZ1bmN0aW9uIGdldE1ldHJpY1N1bW1hcnlKc29uRm9yU3BhbihzcGFuKSB7CgogIHsKICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQp9CgovKioKICogVXNlIHRoaXMgYXR0cmlidXRlIHRvIHJlcHJlc2VudCB0aGUgb3BlcmF0aW9uIG9mIGEgc3Bhbi4KICovCmNvbnN0IFNFTUFOVElDX0FUVFJJQlVURV9TRU5UUllfT1AgPSAnc2VudHJ5Lm9wJzsKCi8qKgogKiBVc2UgdGhpcyBhdHRyaWJ1dGUgdG8gcmVwcmVzZW50IHRoZSBvcmlnaW4gb2YgYSBzcGFuLgogKi8KY29uc3QgU0VNQU5USUNfQVRUUklCVVRFX1NFTlRSWV9PUklHSU4gPSAnc2VudHJ5Lm9yaWdpbic7Cgpjb25zdCBTUEFOX1NUQVRVU19VTlNFVCA9IDA7CmNvbnN0IFNQQU5fU1RBVFVTX09LID0gMTsKCi8qKgogKiBDb252ZXJ0IGEgc3BhbiB0aW1lIGlucHV0IGludHAgYSB0aW1lc3RhbXAgaW4gc2Vjb25kcy4KICovCmZ1bmN0aW9uIHNwYW5UaW1lSW5wdXRUb1NlY29uZHMoaW5wdXQpIHsKICBpZiAodHlwZW9mIGlucHV0ID09PSAnbnVtYmVyJykgewogICAgcmV0dXJuIGVuc3VyZVRpbWVzdGFtcEluU2Vjb25kcyhpbnB1dCk7CiAgfQoKICBpZiAoQXJyYXkuaXNBcnJheShpbnB1dCkpIHsKICAgIC8vIFNlZSB7QGxpbmsgSHJUaW1lfSBmb3IgdGhlIGFycmF5LWJhc2VkIHRpbWUgZm9ybWF0CiAgICByZXR1cm4gaW5wdXRbMF0gKyBpbnB1dFsxXSAvIDFlOTsKICB9CgogIGlmIChpbnB1dCBpbnN0YW5jZW9mIERhdGUpIHsKICAgIHJldHVybiBlbnN1cmVUaW1lc3RhbXBJblNlY29uZHMoaW5wdXQuZ2V0VGltZSgpKTsKICB9CgogIHJldHVybiB0aW1lc3RhbXBJblNlY29uZHMoKTsKfQoKLyoqCiAqIENvbnZlcnRzIGEgdGltZXN0YW1wIHRvIHNlY29uZCwgaWYgaXQgd2FzIGluIG1pbGxpc2Vjb25kcywgb3Iga2VlcHMgaXQgYXMgc2Vjb25kLgogKi8KZnVuY3Rpb24gZW5zdXJlVGltZXN0YW1wSW5TZWNvbmRzKHRpbWVzdGFtcCkgewogIGNvbnN0IGlzTXMgPSB0aW1lc3RhbXAgPiA5OTk5OTk5OTk5OwogIHJldHVybiBpc01zID8gdGltZXN0YW1wIC8gMTAwMCA6IHRpbWVzdGFtcDsKfQoKLyoqCiAqIENvbnZlcnQgYSBzcGFuIHRvIGEgSlNPTiByZXByZXNlbnRhdGlvbi4KICogTm90ZSB0aGF0IGFsbCBmaWVsZHMgcmV0dXJuZWQgaGVyZSBhcmUgb3B0aW9uYWwgYW5kIG5lZWQgdG8gYmUgZ3VhcmRlZCBhZ2FpbnN0LgogKgogKiBOb3RlOiBCZWNhdXNlIG9mIHRoaXMsIHdlIGN1cnJlbnRseSBoYXZlIGEgY2lyY3VsYXIgdHlwZSBkZXBlbmRlbmN5ICh3aGljaCB3ZSBvcHRlZCBvdXQgb2YgaW4gcGFja2FnZS5qc29uKS4KICogVGhpcyBpcyBub3QgYXZvaWRhYmxlIGFzIHdlIG5lZWQgYHNwYW5Ub0pTT05gIGluIGBzcGFuVXRpbHMudHNgLCB3aGljaCBpbiB0dXJuIGlzIG5lZWRlZCBieSBgc3Bhbi50c2AgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5LgogKiBBbmQgYHNwYW5Ub0pTT05gIG5lZWRzIHRoZSBTcGFuIGNsYXNzIGZyb20gYHNwYW4udHNgIHRvIGNoZWNrIGhlcmUuCiAqLwpmdW5jdGlvbiBzcGFuVG9KU09OKHNwYW4pIHsKICBpZiAoc3BhbklzU2VudHJ5U3BhbihzcGFuKSkgewogICAgcmV0dXJuIHNwYW4uZ2V0U3BhbkpTT04oKTsKICB9CgogIHRyeSB7CiAgICBjb25zdCB7IHNwYW5JZDogc3Bhbl9pZCwgdHJhY2VJZDogdHJhY2VfaWQgfSA9IHNwYW4uc3BhbkNvbnRleHQoKTsKCiAgICAvLyBIYW5kbGUgYSBzcGFuIGZyb20gQG9wZW50ZWxlbWV0cnkvc2RrLWJhc2UtdHJhY2UncyBgU3BhbmAgY2xhc3MKICAgIGlmIChzcGFuSXNPcGVuVGVsZW1ldHJ5U2RrVHJhY2VCYXNlU3BhbihzcGFuKSkgewogICAgICBjb25zdCB7IGF0dHJpYnV0ZXMsIHN0YXJ0VGltZSwgbmFtZSwgZW5kVGltZSwgcGFyZW50U3BhbklkLCBzdGF0dXMgfSA9IHNwYW47CgogICAgICByZXR1cm4gZHJvcFVuZGVmaW5lZEtleXMoewogICAgICAgIHNwYW5faWQsCiAgICAgICAgdHJhY2VfaWQsCiAgICAgICAgZGF0YTogYXR0cmlidXRlcywKICAgICAgICBkZXNjcmlwdGlvbjogbmFtZSwKICAgICAgICBwYXJlbnRfc3Bhbl9pZDogcGFyZW50U3BhbklkLAogICAgICAgIHN0YXJ0X3RpbWVzdGFtcDogc3BhblRpbWVJbnB1dFRvU2Vjb25kcyhzdGFydFRpbWUpLAogICAgICAgIC8vIFRoaXMgaXMgWzAsMF0gYnkgZGVmYXVsdCBpbiBPVEVMLCBpbiB3aGljaCBjYXNlIHdlIHdhbnQgdG8gaW50ZXJwcmV0IHRoaXMgYXMgbm8gZW5kIHRpbWUKICAgICAgICB0aW1lc3RhbXA6IHNwYW5UaW1lSW5wdXRUb1NlY29uZHMoZW5kVGltZSkgfHwgdW5kZWZpbmVkLAogICAgICAgIHN0YXR1czogZ2V0U3RhdHVzTWVzc2FnZShzdGF0dXMpLAogICAgICAgIG9wOiBhdHRyaWJ1dGVzW1NFTUFOVElDX0FUVFJJQlVURV9TRU5UUllfT1BdLAogICAgICAgIG9yaWdpbjogYXR0cmlidXRlc1tTRU1BTlRJQ19BVFRSSUJVVEVfU0VOVFJZX09SSUdJTl0gLAogICAgICAgIF9tZXRyaWNzX3N1bW1hcnk6IGdldE1ldHJpY1N1bW1hcnlKc29uRm9yU3BhbigpLAogICAgICB9KTsKICAgIH0KCiAgICAvLyBGaW5hbGx5LCBhdCBsZWFzdCB3ZSBoYXZlIGBzcGFuQ29udGV4dCgpYC4uLi4KICAgIHJldHVybiB7CiAgICAgIHNwYW5faWQsCiAgICAgIHRyYWNlX2lkLAogICAgfTsKICB9IGNhdGNoIChlKSB7CiAgICByZXR1cm4ge307CiAgfQp9CgpmdW5jdGlvbiBzcGFuSXNPcGVuVGVsZW1ldHJ5U2RrVHJhY2VCYXNlU3BhbihzcGFuKSB7CiAgY29uc3QgY2FzdFNwYW4gPSBzcGFuIDsKICByZXR1cm4gISFjYXN0U3Bhbi5hdHRyaWJ1dGVzICYmICEhY2FzdFNwYW4uc3RhcnRUaW1lICYmICEhY2FzdFNwYW4ubmFtZSAmJiAhIWNhc3RTcGFuLmVuZFRpbWUgJiYgISFjYXN0U3Bhbi5zdGF0dXM7Cn0KCi8qKiBFeHBvcnRlZCBvbmx5IGZvciB0ZXN0cy4gKi8KCi8qKgogKiBTYWRseSwgZHVlIHRvIGNpcmN1bGFyIGRlcGVuZGVuY3kgY2hlY2tzIHdlIGNhbm5vdCBhY3R1YWxseSBpbXBvcnQgdGhlIFNwYW4gY2xhc3MgaGVyZSBhbmQgY2hlY2sgZm9yIGluc3RhbmNlb2YuCiAqIDooIFNvIGluc3RlYWQgd2UgYXBwcm94aW1hdGUgdGhpcyBieSBjaGVja2luZyBpZiBpdCBoYXMgdGhlIGBnZXRTcGFuSlNPTmAgbWV0aG9kLgogKi8KZnVuY3Rpb24gc3BhbklzU2VudHJ5U3BhbihzcGFuKSB7CiAgcmV0dXJuIHR5cGVvZiAoc3BhbiApLmdldFNwYW5KU09OID09PSAnZnVuY3Rpb24nOwp9CgovKiogR2V0IHRoZSBzdGF0dXMgbWVzc2FnZSB0byB1c2UgZm9yIGEgSlNPTiByZXByZXNlbnRhdGlvbiBvZiBhIHNwYW4uICovCmZ1bmN0aW9uIGdldFN0YXR1c01lc3NhZ2Uoc3RhdHVzKSB7CiAgaWYgKCFzdGF0dXMgfHwgc3RhdHVzLmNvZGUgPT09IFNQQU5fU1RBVFVTX1VOU0VUKSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgaWYgKHN0YXR1cy5jb2RlID09PSBTUEFOX1NUQVRVU19PSykgewogICAgcmV0dXJuICdvayc7CiAgfQoKICByZXR1cm4gc3RhdHVzLm1lc3NhZ2UgfHwgJ3Vua25vd25fZXJyb3InOwp9CgovKioKICogQXBwbHkgU2RrSW5mbyAobmFtZSwgdmVyc2lvbiwgcGFja2FnZXMsIGludGVncmF0aW9ucykgdG8gdGhlIGNvcnJlc3BvbmRpbmcgZXZlbnQga2V5LgogKiBNZXJnZSB3aXRoIGV4aXN0aW5nIGRhdGEgaWYgYW55LgogKiovCmZ1bmN0aW9uIGVuaGFuY2VFdmVudFdpdGhTZGtJbmZvKGV2ZW50LCBzZGtJbmZvKSB7CiAgaWYgKCFzZGtJbmZvKSB7CiAgICByZXR1cm4gZXZlbnQ7CiAgfQogIGV2ZW50LnNkayA9IGV2ZW50LnNkayB8fCB7fTsKICBldmVudC5zZGsubmFtZSA9IGV2ZW50LnNkay5uYW1lIHx8IHNka0luZm8ubmFtZTsKICBldmVudC5zZGsudmVyc2lvbiA9IGV2ZW50LnNkay52ZXJzaW9uIHx8IHNka0luZm8udmVyc2lvbjsKICBldmVudC5zZGsuaW50ZWdyYXRpb25zID0gWy4uLihldmVudC5zZGsuaW50ZWdyYXRpb25zIHx8IFtdKSwgLi4uKHNka0luZm8uaW50ZWdyYXRpb25zIHx8IFtdKV07CiAgZXZlbnQuc2RrLnBhY2thZ2VzID0gWy4uLihldmVudC5zZGsucGFja2FnZXMgfHwgW10pLCAuLi4oc2RrSW5mby5wYWNrYWdlcyB8fCBbXSldOwogIHJldHVybiBldmVudDsKfQoKLyoqIENyZWF0ZXMgYW4gZW52ZWxvcGUgZnJvbSBhIFNlc3Npb24gKi8KZnVuY3Rpb24gY3JlYXRlU2Vzc2lvbkVudmVsb3BlKAogIHNlc3Npb24sCiAgZHNuLAogIG1ldGFkYXRhLAogIHR1bm5lbCwKKSB7CiAgY29uc3Qgc2RrSW5mbyA9IGdldFNka01ldGFkYXRhRm9yRW52ZWxvcGVIZWFkZXIobWV0YWRhdGEpOwogIGNvbnN0IGVudmVsb3BlSGVhZGVycyA9IHsKICAgIHNlbnRfYXQ6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSwKICAgIC4uLihzZGtJbmZvICYmIHsgc2RrOiBzZGtJbmZvIH0pLAogICAgLi4uKCEhdHVubmVsICYmIGRzbiAmJiB7IGRzbjogZHNuVG9TdHJpbmcoZHNuKSB9KSwKICB9OwoKICBjb25zdCBlbnZlbG9wZUl0ZW0gPQogICAgJ2FnZ3JlZ2F0ZXMnIGluIHNlc3Npb24gPyBbeyB0eXBlOiAnc2Vzc2lvbnMnIH0sIHNlc3Npb25dIDogW3sgdHlwZTogJ3Nlc3Npb24nIH0sIHNlc3Npb24udG9KU09OKCldOwoKICByZXR1cm4gY3JlYXRlRW52ZWxvcGUoZW52ZWxvcGVIZWFkZXJzLCBbZW52ZWxvcGVJdGVtXSk7Cn0KCi8qKgogKiBDcmVhdGUgYW4gRW52ZWxvcGUgZnJvbSBhbiBldmVudC4KICovCmZ1bmN0aW9uIGNyZWF0ZUV2ZW50RW52ZWxvcGUoCiAgZXZlbnQsCiAgZHNuLAogIG1ldGFkYXRhLAogIHR1bm5lbCwKKSB7CiAgY29uc3Qgc2RrSW5mbyA9IGdldFNka01ldGFkYXRhRm9yRW52ZWxvcGVIZWFkZXIobWV0YWRhdGEpOwoKICAvKgogICAgTm90ZTogRHVlIHRvIFRTLCBldmVudC50eXBlIG1heSBiZSBgcmVwbGF5X2V2ZW50YCwgdGhlb3JldGljYWxseS4KICAgIEluIHByYWN0aWNlLCB3ZSBuZXZlciBjYWxsIGBjcmVhdGVFdmVudEVudmVsb3BlYCB3aXRoIGByZXBsYXlfZXZlbnRgIHR5cGUsCiAgICBhbmQgd2UnZCBoYXZlIHRvIGFkanV0IGEgbG9vb3Qgb2YgdHlwZXMgdG8gbWFrZSB0aGlzIHdvcmsgcHJvcGVybHkuCiAgICBXZSB3YW50IHRvIGF2b2lkIGNhc3RpbmcgdGhpcyBhcm91bmQsIGFzIHRoYXQgY291bGQgbGVhZCB0byBidWdzIChlLmcuIHdoZW4gd2UgYWRkIGFub3RoZXIgdHlwZSkKICAgIFNvIHRoZSBzYWZlIGNob2ljZSBpcyB0byByZWFsbHkgZ3VhcmQgYWdhaW5zdCB0aGUgcmVwbGF5X2V2ZW50IHR5cGUgaGVyZS4KICAqLwogIGNvbnN0IGV2ZW50VHlwZSA9IGV2ZW50LnR5cGUgJiYgZXZlbnQudHlwZSAhPT0gJ3JlcGxheV9ldmVudCcgPyBldmVudC50eXBlIDogJ2V2ZW50JzsKCiAgZW5oYW5jZUV2ZW50V2l0aFNka0luZm8oZXZlbnQsIG1ldGFkYXRhICYmIG1ldGFkYXRhLnNkayk7CgogIGNvbnN0IGVudmVsb3BlSGVhZGVycyA9IGNyZWF0ZUV2ZW50RW52ZWxvcGVIZWFkZXJzKGV2ZW50LCBzZGtJbmZvLCB0dW5uZWwsIGRzbik7CgogIC8vIFByZXZlbnQgdGhpcyBkYXRhICh3aGljaCwgaWYgaXQgZXhpc3RzLCB3YXMgdXNlZCBpbiBlYXJsaWVyIHN0ZXBzIGluIHRoZSBwcm9jZXNzaW5nIHBpcGVsaW5lKSBmcm9tIGJlaW5nIHNlbnQgdG8KICAvLyBzZW50cnkuIChOb3RlOiBPdXIgdXNlIG9mIHRoaXMgcHJvcGVydHkgY29tZXMgYW5kIGdvZXMgd2l0aCB3aGF0ZXZlciB3ZSBtaWdodCBiZSBkZWJ1Z2dpbmcsIHdoYXRldmVyIGhhY2tzIHdlIG1heQogIC8vIGhhdmUgdGVtcG9yYXJpbHkgYWRkZWQsIGV0Yy4gRXZlbiBpZiB3ZSBkb24ndCBoYXBwZW4gdG8gYmUgdXNpbmcgaXQgYXQgc29tZSBwb2ludCBpbiB0aGUgZnV0dXJlLCBsZXQncyBub3QgZ2V0IHJpZAogIC8vIG9mIHRoaXMgYGRlbGV0ZWAsIGxlc3Qgd2UgbWlzcyBwdXR0aW5nIGl0IGJhY2sgaW4gdGhlIG5leHQgdGltZSB0aGUgcHJvcGVydHkgaXMgaW4gdXNlLikKICBkZWxldGUgZXZlbnQuc2RrUHJvY2Vzc2luZ01ldGFkYXRhOwoKICBjb25zdCBldmVudEl0ZW0gPSBbeyB0eXBlOiBldmVudFR5cGUgfSwgZXZlbnRdOwogIHJldHVybiBjcmVhdGVFbnZlbG9wZShlbnZlbG9wZUhlYWRlcnMsIFtldmVudEl0ZW1dKTsKfQoKY29uc3QgU0VOVFJZX0FQSV9WRVJTSU9OID0gJzcnOwoKLyoqIFJldHVybnMgdGhlIHByZWZpeCB0byBjb25zdHJ1Y3QgU2VudHJ5IGluZ2VzdGlvbiBBUEkgZW5kcG9pbnRzLiAqLwpmdW5jdGlvbiBnZXRCYXNlQXBpRW5kcG9pbnQoZHNuKSB7CiAgY29uc3QgcHJvdG9jb2wgPSBkc24ucHJvdG9jb2wgPyBgJHtkc24ucHJvdG9jb2x9OmAgOiAnJzsKICBjb25zdCBwb3J0ID0gZHNuLnBvcnQgPyBgOiR7ZHNuLnBvcnR9YCA6ICcnOwogIHJldHVybiBgJHtwcm90b2NvbH0vLyR7ZHNuLmhvc3R9JHtwb3J0fSR7ZHNuLnBhdGggPyBgLyR7ZHNuLnBhdGh9YCA6ICcnfS9hcGkvYDsKfQoKLyoqIFJldHVybnMgdGhlIGluZ2VzdCBBUEkgZW5kcG9pbnQgZm9yIHRhcmdldC4gKi8KZnVuY3Rpb24gX2dldEluZ2VzdEVuZHBvaW50KGRzbikgewogIHJldHVybiBgJHtnZXRCYXNlQXBpRW5kcG9pbnQoZHNuKX0ke2Rzbi5wcm9qZWN0SWR9L2VudmVsb3BlL2A7Cn0KCi8qKiBSZXR1cm5zIGEgVVJMLWVuY29kZWQgc3RyaW5nIHdpdGggYXV0aCBjb25maWcgc3VpdGFibGUgZm9yIGEgcXVlcnkgc3RyaW5nLiAqLwpmdW5jdGlvbiBfZW5jb2RlZEF1dGgoZHNuLCBzZGtJbmZvKSB7CiAgcmV0dXJuIHVybEVuY29kZSh7CiAgICAvLyBXZSBzZW5kIG9ubHkgdGhlIG1pbmltdW0gc2V0IG9mIHJlcXVpcmVkIGluZm9ybWF0aW9uLiBTZWUKICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9nZXRzZW50cnkvc2VudHJ5LWphdmFzY3JpcHQvaXNzdWVzLzI1NzIuCiAgICBzZW50cnlfa2V5OiBkc24ucHVibGljS2V5LAogICAgc2VudHJ5X3ZlcnNpb246IFNFTlRSWV9BUElfVkVSU0lPTiwKICAgIC4uLihzZGtJbmZvICYmIHsgc2VudHJ5X2NsaWVudDogYCR7c2RrSW5mby5uYW1lfS8ke3Nka0luZm8udmVyc2lvbn1gIH0pLAogIH0pOwp9CgovKioKICogUmV0dXJucyB0aGUgZW52ZWxvcGUgZW5kcG9pbnQgVVJMIHdpdGggYXV0aCBpbiB0aGUgcXVlcnkgc3RyaW5nLgogKgogKiBTZW5kaW5nIGF1dGggYXMgcGFydCBvZiB0aGUgcXVlcnkgc3RyaW5nIGFuZCBub3QgYXMgY3VzdG9tIEhUVFAgaGVhZGVycyBhdm9pZHMgQ09SUyBwcmVmbGlnaHQgcmVxdWVzdHMuCiAqLwpmdW5jdGlvbiBnZXRFbnZlbG9wZUVuZHBvaW50V2l0aFVybEVuY29kZWRBdXRoKAogIGRzbiwKICAvLyBUT0RPICh2OCk6IFJlbW92ZSBgdHVubmVsT3JPcHRpb25zYCBpbiBmYXZvciBvZiBgb3B0aW9uc2AsIGFuZCB1c2UgdGhlIHN1YnN0aXR1dGUgY29kZSBiZWxvdwogIC8vIG9wdGlvbnM6IENsaWVudE9wdGlvbnMgPSB7fSBhcyBDbGllbnRPcHRpb25zLAogIHR1bm5lbE9yT3B0aW9ucyA9IHt9ICwKKSB7CiAgLy8gVE9ETyAodjgpOiBVc2UgdGhpcyBjb2RlIGluc3RlYWQKICAvLyBjb25zdCB7IHR1bm5lbCwgX21ldGFkYXRhID0ge30gfSA9IG9wdGlvbnM7CiAgLy8gcmV0dXJuIHR1bm5lbCA/IHR1bm5lbCA6IGAke19nZXRJbmdlc3RFbmRwb2ludChkc24pfT8ke19lbmNvZGVkQXV0aChkc24sIF9tZXRhZGF0YS5zZGspfWA7CgogIGNvbnN0IHR1bm5lbCA9IHR5cGVvZiB0dW5uZWxPck9wdGlvbnMgPT09ICdzdHJpbmcnID8gdHVubmVsT3JPcHRpb25zIDogdHVubmVsT3JPcHRpb25zLnR1bm5lbDsKICBjb25zdCBzZGtJbmZvID0KICAgIHR5cGVvZiB0dW5uZWxPck9wdGlvbnMgPT09ICdzdHJpbmcnIHx8ICF0dW5uZWxPck9wdGlvbnMuX21ldGFkYXRhID8gdW5kZWZpbmVkIDogdHVubmVsT3JPcHRpb25zLl9tZXRhZGF0YS5zZGs7CgogIHJldHVybiB0dW5uZWwgPyB0dW5uZWwgOiBgJHtfZ2V0SW5nZXN0RW5kcG9pbnQoZHNuKX0/JHtfZW5jb2RlZEF1dGgoZHNuLCBzZGtJbmZvKX1gOwp9CgovKioKICogQ29udmVydCBhIG5ldyBpbnRlZ3JhdGlvbiBmdW5jdGlvbiB0byB0aGUgbGVnYWN5IGNsYXNzIHN5bnRheC4KICogSW4gdjgsIHdlIGNhbiByZW1vdmUgdGhpcyBhbmQgaW5zdGVhZCBleHBvcnQgdGhlIGludGVncmF0aW9uIGZ1bmN0aW9ucyBkaXJlY3RseS4KICoKICogQGRlcHJlY2F0ZWQgVGhpcyB3aWxsIGJlIHJlbW92ZWQgaW4gdjghCiAqLwpmdW5jdGlvbiBjb252ZXJ0SW50ZWdyYXRpb25GblRvQ2xhc3MoCiAgbmFtZSwKICBmbiwKKSB7CiAgcmV0dXJuIE9iamVjdC5hc3NpZ24oCiAgICBmdW5jdGlvbiBDb252ZXJ0ZWRJbnRlZ3JhdGlvbiguLi5hcmdzKSB7CiAgICAgIHJldHVybiBmbiguLi5hcmdzKTsKICAgIH0sCiAgICB7IGlkOiBuYW1lIH0sCiAgKSA7Cn0KCi8qKgogKiBEZWZpbmUgYW4gaW50ZWdyYXRpb24gZnVuY3Rpb24gdGhhdCBjYW4gYmUgdXNlZCB0byBjcmVhdGUgYW4gaW50ZWdyYXRpb24gaW5zdGFuY2UuCiAqIE5vdGUgdGhhdCB0aGlzIGJ5IGRlc2lnbiBoaWRlcyB0aGUgaW1wbGVtZW50YXRpb24gZGV0YWlscyBvZiB0aGUgaW50ZWdyYXRpb24sIGFzIHRoZXkgYXJlIGNvbnNpZGVyZWQgaW50ZXJuYWwuCiAqLwpmdW5jdGlvbiBkZWZpbmVJbnRlZ3JhdGlvbihmbikgewogIHJldHVybiBmbjsKfQoKY29uc3QgREVGQVVMVF9UUkFOU1BPUlRfQlVGRkVSX1NJWkUgPSAzMDsKCi8qKgogKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIGEgU2VudHJ5IGBUcmFuc3BvcnRgCiAqCiAqIEBwYXJhbSBvcHRpb25zCiAqIEBwYXJhbSBtYWtlUmVxdWVzdAogKi8KZnVuY3Rpb24gY3JlYXRlVHJhbnNwb3J0KAogIG9wdGlvbnMsCiAgbWFrZVJlcXVlc3QsCiAgYnVmZmVyID0gbWFrZVByb21pc2VCdWZmZXIoCiAgICBvcHRpb25zLmJ1ZmZlclNpemUgfHwgREVGQVVMVF9UUkFOU1BPUlRfQlVGRkVSX1NJWkUsCiAgKSwKKSB7CiAgbGV0IHJhdGVMaW1pdHMgPSB7fTsKICBjb25zdCBmbHVzaCA9ICh0aW1lb3V0KSA9PiBidWZmZXIuZHJhaW4odGltZW91dCk7CgogIGZ1bmN0aW9uIHNlbmQoZW52ZWxvcGUpIHsKICAgIGNvbnN0IGZpbHRlcmVkRW52ZWxvcGVJdGVtcyA9IFtdOwoKICAgIC8vIERyb3AgcmF0ZSBsaW1pdGVkIGl0ZW1zIGZyb20gZW52ZWxvcGUKICAgIGZvckVhY2hFbnZlbG9wZUl0ZW0oZW52ZWxvcGUsIChpdGVtLCB0eXBlKSA9PiB7CiAgICAgIGNvbnN0IGVudmVsb3BlSXRlbURhdGFDYXRlZ29yeSA9IGVudmVsb3BlSXRlbVR5cGVUb0RhdGFDYXRlZ29yeSh0eXBlKTsKICAgICAgaWYgKGlzUmF0ZUxpbWl0ZWQocmF0ZUxpbWl0cywgZW52ZWxvcGVJdGVtRGF0YUNhdGVnb3J5KSkgewogICAgICAgIGNvbnN0IGV2ZW50ID0gZ2V0RXZlbnRGb3JFbnZlbG9wZUl0ZW0oaXRlbSwgdHlwZSk7CiAgICAgICAgb3B0aW9ucy5yZWNvcmREcm9wcGVkRXZlbnQoJ3JhdGVsaW1pdF9iYWNrb2ZmJywgZW52ZWxvcGVJdGVtRGF0YUNhdGVnb3J5LCBldmVudCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZmlsdGVyZWRFbnZlbG9wZUl0ZW1zLnB1c2goaXRlbSk7CiAgICAgIH0KICAgIH0pOwoKICAgIC8vIFNraXAgc2VuZGluZyBpZiBlbnZlbG9wZSBpcyBlbXB0eSBhZnRlciBmaWx0ZXJpbmcgb3V0IHJhdGUgbGltaXRlZCBldmVudHMKICAgIGlmIChmaWx0ZXJlZEVudmVsb3BlSXRlbXMubGVuZ3RoID09PSAwKSB7CiAgICAgIHJldHVybiByZXNvbHZlZFN5bmNQcm9taXNlKHt9KTsKICAgIH0KCiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQogICAgY29uc3QgZmlsdGVyZWRFbnZlbG9wZSA9IGNyZWF0ZUVudmVsb3BlKGVudmVsb3BlWzBdLCBmaWx0ZXJlZEVudmVsb3BlSXRlbXMgKTsKCiAgICAvLyBDcmVhdGVzIGNsaWVudCByZXBvcnQgZm9yIGVhY2ggaXRlbSBpbiBhbiBlbnZlbG9wZQogICAgY29uc3QgcmVjb3JkRW52ZWxvcGVMb3NzID0gKHJlYXNvbikgPT4gewogICAgICBmb3JFYWNoRW52ZWxvcGVJdGVtKGZpbHRlcmVkRW52ZWxvcGUsIChpdGVtLCB0eXBlKSA9PiB7CiAgICAgICAgY29uc3QgZXZlbnQgPSBnZXRFdmVudEZvckVudmVsb3BlSXRlbShpdGVtLCB0eXBlKTsKICAgICAgICBvcHRpb25zLnJlY29yZERyb3BwZWRFdmVudChyZWFzb24sIGVudmVsb3BlSXRlbVR5cGVUb0RhdGFDYXRlZ29yeSh0eXBlKSwgZXZlbnQpOwogICAgICB9KTsKICAgIH07CgogICAgY29uc3QgcmVxdWVzdFRhc2sgPSAoKSA9PgogICAgICBtYWtlUmVxdWVzdCh7IGJvZHk6IHNlcmlhbGl6ZUVudmVsb3BlKGZpbHRlcmVkRW52ZWxvcGUpIH0pLnRoZW4oCiAgICAgICAgcmVzcG9uc2UgPT4gewogICAgICAgICAgLy8gV2UgZG9uJ3Qgd2FudCB0byB0aHJvdyBvbiBOT0sgcmVzcG9uc2VzLCBidXQgd2Ugd2FudCB0byBhdCBsZWFzdCBsb2cgdGhlbQogICAgICAgICAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgIT09IHVuZGVmaW5lZCAmJiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA8IDIwMCB8fCByZXNwb25zZS5zdGF0dXNDb2RlID49IDMwMCkpIHsKICAgICAgICAgICAgREVCVUdfQlVJTEQgJiYgbG9nZ2VyLndhcm4oYFNlbnRyeSByZXNwb25kZWQgd2l0aCBzdGF0dXMgY29kZSAke3Jlc3BvbnNlLnN0YXR1c0NvZGV9IHRvIHNlbnQgZXZlbnQuYCk7CiAgICAgICAgICB9CgogICAgICAgICAgcmF0ZUxpbWl0cyA9IHVwZGF0ZVJhdGVMaW1pdHMocmF0ZUxpbWl0cywgcmVzcG9uc2UpOwogICAgICAgICAgcmV0dXJuIHJlc3BvbnNlOwogICAgICAgIH0sCiAgICAgICAgZXJyb3IgPT4gewogICAgICAgICAgcmVjb3JkRW52ZWxvcGVMb3NzKCduZXR3b3JrX2Vycm9yJyk7CiAgICAgICAgICB0aHJvdyBlcnJvcjsKICAgICAgICB9LAogICAgICApOwoKICAgIHJldHVybiBidWZmZXIuYWRkKHJlcXVlc3RUYXNrKS50aGVuKAogICAgICByZXN1bHQgPT4gcmVzdWx0LAogICAgICBlcnJvciA9PiB7CiAgICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgU2VudHJ5RXJyb3IpIHsKICAgICAgICAgIERFQlVHX0JVSUxEICYmIGxvZ2dlci5lcnJvcignU2tpcHBlZCBzZW5kaW5nIGV2ZW50IGJlY2F1c2UgYnVmZmVyIGlzIGZ1bGwuJyk7CiAgICAgICAgICByZWNvcmRFbnZlbG9wZUxvc3MoJ3F1ZXVlX292ZXJmbG93Jyk7CiAgICAgICAgICByZXR1cm4gcmVzb2x2ZWRTeW5jUHJvbWlzZSh7fSk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHRocm93IGVycm9yOwogICAgICAgIH0KICAgICAgfSwKICAgICk7CiAgfQoKICByZXR1cm4gewogICAgc2VuZCwKICAgIGZsdXNoLAogIH07Cn0KCmZ1bmN0aW9uIGdldEV2ZW50Rm9yRW52ZWxvcGVJdGVtKGl0ZW0sIHR5cGUpIHsKICBpZiAodHlwZSAhPT0gJ2V2ZW50JyAmJiB0eXBlICE9PSAndHJhbnNhY3Rpb24nKSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgcmV0dXJuIEFycmF5LmlzQXJyYXkoaXRlbSkgPyAoaXRlbSApWzFdIDogdW5kZWZpbmVkOwp9CgovKiogS2V5cyBhcmUgc291cmNlIGZpbGVuYW1lL3VybCwgdmFsdWVzIGFyZSBtZXRhZGF0YSBvYmplY3RzLiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQpjb25zdCBmaWxlbmFtZU1ldGFkYXRhTWFwID0gbmV3IE1hcCgpOwovKiogU2V0IG9mIHN0YWNrIHN0cmluZ3MgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiBwYXJzZWQuICovCmNvbnN0IHBhcnNlZFN0YWNrcyA9IG5ldyBTZXQoKTsKCmZ1bmN0aW9uIGVuc3VyZU1ldGFkYXRhU3RhY2tzQXJlUGFyc2VkKHBhcnNlcikgewogIGlmICghR0xPQkFMX09CSi5fc2VudHJ5TW9kdWxlTWV0YWRhdGEpIHsKICAgIHJldHVybjsKICB9CgogIGZvciAoY29uc3Qgc3RhY2sgb2YgT2JqZWN0LmtleXMoR0xPQkFMX09CSi5fc2VudHJ5TW9kdWxlTWV0YWRhdGEpKSB7CiAgICBjb25zdCBtZXRhZGF0YSA9IEdMT0JBTF9PQkouX3NlbnRyeU1vZHVsZU1ldGFkYXRhW3N0YWNrXTsKCiAgICBpZiAocGFyc2VkU3RhY2tzLmhhcyhzdGFjaykpIHsKICAgICAgY29udGludWU7CiAgICB9CgogICAgLy8gRW5zdXJlIHRoaXMgc3RhY2sgZG9lc24ndCBnZXQgcGFyc2VkIGFnYWluCiAgICBwYXJzZWRTdGFja3MuYWRkKHN0YWNrKTsKCiAgICBjb25zdCBmcmFtZXMgPSBwYXJzZXIoc3RhY2spOwoKICAgIC8vIEdvIHRocm91Z2ggdGhlIGZyYW1lcyBzdGFydGluZyBmcm9tIHRoZSB0b3Agb2YgdGhlIHN0YWNrIGFuZCBmaW5kIHRoZSBmaXJzdCBvbmUgd2l0aCBhIGZpbGVuYW1lCiAgICBmb3IgKGNvbnN0IGZyYW1lIG9mIGZyYW1lcy5yZXZlcnNlKCkpIHsKICAgICAgaWYgKGZyYW1lLmZpbGVuYW1lKSB7CiAgICAgICAgLy8gU2F2ZSB0aGUgbWV0YWRhdGEgZm9yIHRoaXMgZmlsZW5hbWUKICAgICAgICBmaWxlbmFtZU1ldGFkYXRhTWFwLnNldChmcmFtZS5maWxlbmFtZSwgbWV0YWRhdGEpOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgfQp9CgovKioKICogUmV0cmlldmUgbWV0YWRhdGEgZm9yIGEgc3BlY2lmaWMgSmF2YVNjcmlwdCBmaWxlIFVSTC4KICoKICogTWV0YWRhdGEgaXMgaW5qZWN0ZWQgYnkgdGhlIFNlbnRyeSBidW5kbGVyIHBsdWdpbnMgdXNpbmcgdGhlIGBfZXhwZXJpbWVudHMubW9kdWxlTWV0YWRhdGFgIGNvbmZpZyBvcHRpb24uCiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQpmdW5jdGlvbiBnZXRNZXRhZGF0YUZvclVybChwYXJzZXIsIGZpbGVuYW1lKSB7CiAgZW5zdXJlTWV0YWRhdGFTdGFja3NBcmVQYXJzZWQocGFyc2VyKTsKICByZXR1cm4gZmlsZW5hbWVNZXRhZGF0YU1hcC5nZXQoZmlsZW5hbWUpOwp9CgovKioKICogQWRkcyBtZXRhZGF0YSB0byBzdGFjayBmcmFtZXMuCiAqCiAqIE1ldGFkYXRhIGlzIGluamVjdGVkIGJ5IHRoZSBTZW50cnkgYnVuZGxlciBwbHVnaW5zIHVzaW5nIHRoZSBgX2V4cGVyaW1lbnRzLm1vZHVsZU1ldGFkYXRhYCBjb25maWcgb3B0aW9uLgogKi8KZnVuY3Rpb24gYWRkTWV0YWRhdGFUb1N0YWNrRnJhbWVzKHBhcnNlciwgZXZlbnQpIHsKICB0cnkgewogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb24KICAgIGV2ZW50LmV4Y2VwdGlvbi52YWx1ZXMuZm9yRWFjaChleGNlcHRpb24gPT4gewogICAgICBpZiAoIWV4Y2VwdGlvbi5zdGFja3RyYWNlKSB7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICBmb3IgKGNvbnN0IGZyYW1lIG9mIGV4Y2VwdGlvbi5zdGFja3RyYWNlLmZyYW1lcyB8fCBbXSkgewogICAgICAgIGlmICghZnJhbWUuZmlsZW5hbWUpIHsKICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KCiAgICAgICAgY29uc3QgbWV0YWRhdGEgPSBnZXRNZXRhZGF0YUZvclVybChwYXJzZXIsIGZyYW1lLmZpbGVuYW1lKTsKCiAgICAgICAgaWYgKG1ldGFkYXRhKSB7CiAgICAgICAgICBmcmFtZS5tb2R1bGVfbWV0YWRhdGEgPSBtZXRhZGF0YTsKICAgICAgICB9CiAgICAgIH0KICAgIH0pOwogIH0gY2F0Y2ggKF8pIHsKICAgIC8vIFRvIHNhdmUgYnVuZGxlIHNpemUgd2UncmUganVzdCB0cnkgY2F0Y2hpbmcgaGVyZSBpbnN0ZWFkIG9mIGNoZWNraW5nIGZvciB0aGUgZXhpc3RlbmNlIG9mIGFsbCB0aGUgZGlmZmVyZW50IG9iamVjdHMuCiAgfQp9CgovKioKICogU3RyaXBzIG1ldGFkYXRhIGZyb20gc3RhY2sgZnJhbWVzLgogKi8KZnVuY3Rpb24gc3RyaXBNZXRhZGF0YUZyb21TdGFja0ZyYW1lcyhldmVudCkgewogIHRyeSB7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW5vbi1udWxsLWFzc2VydGlvbgogICAgZXZlbnQuZXhjZXB0aW9uLnZhbHVlcy5mb3JFYWNoKGV4Y2VwdGlvbiA9PiB7CiAgICAgIGlmICghZXhjZXB0aW9uLnN0YWNrdHJhY2UpIHsKICAgICAgICByZXR1cm47CiAgICAgIH0KCiAgICAgIGZvciAoY29uc3QgZnJhbWUgb2YgZXhjZXB0aW9uLnN0YWNrdHJhY2UuZnJhbWVzIHx8IFtdKSB7CiAgICAgICAgZGVsZXRlIGZyYW1lLm1vZHVsZV9tZXRhZGF0YTsKICAgICAgfQogICAgfSk7CiAgfSBjYXRjaCAoXykgewogICAgLy8gVG8gc2F2ZSBidW5kbGUgc2l6ZSB3ZSdyZSBqdXN0IHRyeSBjYXRjaGluZyBoZXJlIGluc3RlYWQgb2YgY2hlY2tpbmcgZm9yIHRoZSBleGlzdGVuY2Ugb2YgYWxsIHRoZSBkaWZmZXJlbnQgb2JqZWN0cy4KICB9Cn0KCmNvbnN0IElOVEVHUkFUSU9OX05BTUUkYSA9ICdNb2R1bGVNZXRhZGF0YSc7Cgpjb25zdCBfbW9kdWxlTWV0YWRhdGFJbnRlZ3JhdGlvbiA9ICgoKSA9PiB7CiAgcmV0dXJuIHsKICAgIG5hbWU6IElOVEVHUkFUSU9OX05BTUUkYSwKICAgIHNldHVwKGNsaWVudCkgewogICAgICAvLyBXZSBuZWVkIHRvIHN0cmlwIG1ldGFkYXRhIGZyb20gc3RhY2sgZnJhbWVzIGJlZm9yZSBzZW5kaW5nIHRoZW0gdG8gU2VudHJ5IHNpbmNlIHRoZXNlIGFyZSBjbGllbnQgc2lkZSBvbmx5LgogICAgICBjbGllbnQub24oJ2JlZm9yZUVudmVsb3BlJywgZW52ZWxvcGUgPT4gewogICAgICAgIGZvckVhY2hFbnZlbG9wZUl0ZW0oZW52ZWxvcGUsIChpdGVtLCB0eXBlKSA9PiB7CiAgICAgICAgICBpZiAodHlwZSA9PT0gJ2V2ZW50JykgewogICAgICAgICAgICBjb25zdCBldmVudCA9IEFycmF5LmlzQXJyYXkoaXRlbSkgPyAoaXRlbSApWzFdIDogdW5kZWZpbmVkOwoKICAgICAgICAgICAgaWYgKGV2ZW50KSB7CiAgICAgICAgICAgICAgc3RyaXBNZXRhZGF0YUZyb21TdGFja0ZyYW1lcyhldmVudCk7CiAgICAgICAgICAgICAgaXRlbVsxXSA9IGV2ZW50OwogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgIH0pOwogICAgfSwKCiAgICBwcm9jZXNzRXZlbnQoZXZlbnQsIF9oaW50LCBjbGllbnQpIHsKICAgICAgY29uc3Qgc3RhY2tQYXJzZXIgPSBjbGllbnQuZ2V0T3B0aW9ucygpLnN0YWNrUGFyc2VyOwogICAgICBhZGRNZXRhZGF0YVRvU3RhY2tGcmFtZXMoc3RhY2tQYXJzZXIsIGV2ZW50KTsKICAgICAgcmV0dXJuIGV2ZW50OwogICAgfSwKICB9Owp9KSA7Cgpjb25zdCBtb2R1bGVNZXRhZGF0YUludGVncmF0aW9uID0gZGVmaW5lSW50ZWdyYXRpb24oX21vZHVsZU1ldGFkYXRhSW50ZWdyYXRpb24pOwoKLyoqCiAqIEFkZHMgbW9kdWxlIG1ldGFkYXRhIHRvIHN0YWNrIGZyYW1lcy4KICoKICogTWV0YWRhdGEgY2FuIGJlIGluamVjdGVkIGJ5IHRoZSBTZW50cnkgYnVuZGxlciBwbHVnaW5zIHVzaW5nIHRoZSBgX2V4cGVyaW1lbnRzLm1vZHVsZU1ldGFkYXRhYCBjb25maWcgb3B0aW9uLgogKgogKiBXaGVuIHRoaXMgaW50ZWdyYXRpb24gaXMgYWRkZWQsIHRoZSBtZXRhZGF0YSBwYXNzZWQgdG8gdGhlIGJ1bmRsZXIgcGx1Z2luIGlzIGFkZGVkIHRvIHRoZSBzdGFjayBmcmFtZXMgb2YgYWxsIGV2ZW50cwogKiB1bmRlciB0aGUgYG1vZHVsZV9tZXRhZGF0YWAgcHJvcGVydHkuIFRoaXMgY2FuIGJlIHVzZWQgdG8gaGVscCBpbiB0YWdnaW5nIG9yIHJvdXRpbmcgb2YgZXZlbnRzIGZyb20gZGlmZmVyZW50IHRlYW1zCiAqIG91ciBzb3VyY2VzCiAqCiAqIEBkZXByZWNhdGVkIFVzZSBgbW9kdWxlTWV0YWRhdGFJbnRlZ3JhdGlvbigpYCBpbnN0ZWFkLgogKi8KLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGRlcHJlY2F0aW9uL2RlcHJlY2F0aW9uCmNvbnZlcnRJbnRlZ3JhdGlvbkZuVG9DbGFzcygKICBJTlRFR1JBVElPTl9OQU1FJGEsCiAgbW9kdWxlTWV0YWRhdGFJbnRlZ3JhdGlvbiwKKQoKOwoKY29uc3QgREVGQVVMVF9PUFRJT05TID0gewogIGluY2x1ZGU6IHsKICAgIGNvb2tpZXM6IHRydWUsCiAgICBkYXRhOiB0cnVlLAogICAgaGVhZGVyczogdHJ1ZSwKICAgIGlwOiBmYWxzZSwKICAgIHF1ZXJ5X3N0cmluZzogdHJ1ZSwKICAgIHVybDogdHJ1ZSwKICAgIHVzZXI6IHsKICAgICAgaWQ6IHRydWUsCiAgICAgIHVzZXJuYW1lOiB0cnVlLAogICAgICBlbWFpbDogdHJ1ZSwKICAgIH0sCiAgfSwKICB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZTogJ21ldGhvZFBhdGgnICwKfTsKCmNvbnN0IElOVEVHUkFUSU9OX05BTUUkOSA9ICdSZXF1ZXN0RGF0YSc7Cgpjb25zdCBfcmVxdWVzdERhdGFJbnRlZ3JhdGlvbiA9ICgob3B0aW9ucyA9IHt9KSA9PiB7CiAgY29uc3QgX29wdGlvbnMgPSB7CiAgICAuLi5ERUZBVUxUX09QVElPTlMsCiAgICAuLi5vcHRpb25zLAogICAgaW5jbHVkZTogewogICAgICAuLi5ERUZBVUxUX09QVElPTlMuaW5jbHVkZSwKICAgICAgLi4ub3B0aW9ucy5pbmNsdWRlLAogICAgICB1c2VyOgogICAgICAgIG9wdGlvbnMuaW5jbHVkZSAmJiB0eXBlb2Ygb3B0aW9ucy5pbmNsdWRlLnVzZXIgPT09ICdib29sZWFuJwogICAgICAgICAgPyBvcHRpb25zLmluY2x1ZGUudXNlcgogICAgICAgICAgOiB7CiAgICAgICAgICAgICAgLi4uREVGQVVMVF9PUFRJT05TLmluY2x1ZGUudXNlciwKICAgICAgICAgICAgICAvLyBVbmNsZWFyIHdoeSBUUyBzdGlsbCB0aGlua3MgYG9wdGlvbnMuaW5jbHVkZS51c2VyYCBjb3VsZCBiZSBhIGJvb2xlYW4gYXQgdGhpcyBwb2ludAogICAgICAgICAgICAgIC4uLigob3B0aW9ucy5pbmNsdWRlIHx8IHt9KS51c2VyICksCiAgICAgICAgICAgIH0sCiAgICB9LAogIH07CgogIHJldHVybiB7CiAgICBuYW1lOiBJTlRFR1JBVElPTl9OQU1FJDksCiAgICBwcm9jZXNzRXZlbnQoZXZlbnQsIF9oaW50LCBjbGllbnQpIHsKICAgICAgLy8gTm90ZTogSW4gdGhlIGxvbmcgcnVuLCBtb3N0IG9mIHRoZSBsb2dpYyBoZXJlIHNob3VsZCBwcm9iYWJseSBtb3ZlIGludG8gdGhlIHJlcXVlc3QgZGF0YSB1dGlsaXR5IGZ1bmN0aW9ucy4gRm9yCiAgICAgIC8vIHRoZSBtb21lbnQgaXQgbGl2ZXMgaGVyZSwgdGhvdWdoLCB1bnRpbCBodHRwczovL2dpdGh1Yi5jb20vZ2V0c2VudHJ5L3NlbnRyeS1qYXZhc2NyaXB0L2lzc3Vlcy81NzE4IGlzIGFkZHJlc3NlZC4KICAgICAgLy8gKFRMO0RSOiBUaG9zZSBmdW5jdGlvbnMgdG91Y2ggbWFueSBwYXJ0cyBvZiB0aGUgcmVwbyBpbiBtYW55IGRpZmZlcmVudCB3YXlzLCBhbmQgbmVlZCB0byBiZSBjbGVuZWQgdXAuIE9uY2UKICAgICAgLy8gdGhhdCdzIGhhcHBlbmVkLCBpdCB3aWxsIGJlIGVhc2llciB0byBhZGQgdGhpcyBsb2dpYyBpbiB3aXRob3V0IHdvcnJ5aW5nIGFib3V0IHVuZXhwZWN0ZWQgc2lkZSBlZmZlY3RzLikKICAgICAgY29uc3QgeyB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZSB9ID0gX29wdGlvbnM7CgogICAgICBjb25zdCB7IHNka1Byb2Nlc3NpbmdNZXRhZGF0YSA9IHt9IH0gPSBldmVudDsKICAgICAgY29uc3QgcmVxID0gc2RrUHJvY2Vzc2luZ01ldGFkYXRhLnJlcXVlc3Q7CgogICAgICBpZiAoIXJlcSkgewogICAgICAgIHJldHVybiBldmVudDsKICAgICAgfQoKICAgICAgY29uc3QgYWRkUmVxdWVzdERhdGFPcHRpb25zID0gY29udmVydFJlcURhdGFJbnRlZ3JhdGlvbk9wdHNUb0FkZFJlcURhdGFPcHRzKF9vcHRpb25zKTsKCiAgICAgIGNvbnN0IHByb2Nlc3NlZEV2ZW50ID0gYWRkUmVxdWVzdERhdGFUb0V2ZW50KGV2ZW50LCByZXEsIGFkZFJlcXVlc3REYXRhT3B0aW9ucyk7CgogICAgICAvLyBUcmFuc2FjdGlvbiBldmVudHMgYWxyZWFkeSBoYXZlIHRoZSByaWdodCBgdHJhbnNhY3Rpb25gIHZhbHVlCiAgICAgIGlmIChldmVudC50eXBlID09PSAndHJhbnNhY3Rpb24nIHx8IHRyYW5zYWN0aW9uTmFtaW5nU2NoZW1lID09PSAnaGFuZGxlcicpIHsKICAgICAgICByZXR1cm4gcHJvY2Vzc2VkRXZlbnQ7CiAgICAgIH0KCiAgICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgdXNlIHRoZSByZXF1ZXN0J3MgYXNzb2NpYXRlZCB0cmFuc2FjdGlvbiAoaWYgYW55KSB0byBvdmVyd3JpdGUgdGhlIGV2ZW50J3MgYHRyYW5zYWN0aW9uYAogICAgICAvLyB2YWx1ZSB3aXRoIGEgaGlnaC1xdWFsaXR5IG9uZQogICAgICBjb25zdCByZXFXaXRoVHJhbnNhY3Rpb24gPSByZXEgOwogICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IHJlcVdpdGhUcmFuc2FjdGlvbi5fc2VudHJ5VHJhbnNhY3Rpb247CiAgICAgIGlmICh0cmFuc2FjdGlvbikgewogICAgICAgIGNvbnN0IG5hbWUgPSBzcGFuVG9KU09OKHRyYW5zYWN0aW9uKS5kZXNjcmlwdGlvbiB8fCAnJzsKCiAgICAgICAgLy8gVE9ETyAodjgpOiBSZW1vdmUgdGhlIG5leHRqcyBjaGVjayBhbmQganVzdCBiYXNlIGl0IG9uIGB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZWAgZm9yIGFsbCBTREtzLiAoV2UgaGF2ZSB0bwogICAgICAgIC8vIGtlZXAgaXQgdGhlIHdheSBpdCBpcyBmb3IgdGhlIG1vbWVudCwgYmVjYXVzZSBjaGFuZ2luZyB0aGUgbmFtZXMgb2YgdHJhbnNhY3Rpb25zIGluIFNlbnRyeSBoYXMgdGhlIHBvdGVudGlhbAogICAgICAgIC8vIHRvIGJyZWFrIHRoaW5ncyBsaWtlIGFsZXJ0IHJ1bGVzLikKICAgICAgICBjb25zdCBzaG91bGRJbmNsdWRlTWV0aG9kSW5UcmFuc2FjdGlvbk5hbWUgPQogICAgICAgICAgZ2V0U0RLTmFtZShjbGllbnQpID09PSAnc2VudHJ5LmphdmFzY3JpcHQubmV4dGpzJwogICAgICAgICAgICA/IG5hbWUuc3RhcnRzV2l0aCgnL2FwaScpCiAgICAgICAgICAgIDogdHJhbnNhY3Rpb25OYW1pbmdTY2hlbWUgIT09ICdwYXRoJzsKCiAgICAgICAgY29uc3QgW3RyYW5zYWN0aW9uVmFsdWVdID0gZXh0cmFjdFBhdGhGb3JUcmFuc2FjdGlvbihyZXEsIHsKICAgICAgICAgIHBhdGg6IHRydWUsCiAgICAgICAgICBtZXRob2Q6IHNob3VsZEluY2x1ZGVNZXRob2RJblRyYW5zYWN0aW9uTmFtZSwKICAgICAgICAgIGN1c3RvbVJvdXRlOiBuYW1lLAogICAgICAgIH0pOwoKICAgICAgICBwcm9jZXNzZWRFdmVudC50cmFuc2FjdGlvbiA9IHRyYW5zYWN0aW9uVmFsdWU7CiAgICAgIH0KCiAgICAgIHJldHVybiBwcm9jZXNzZWRFdmVudDsKICAgIH0sCiAgfTsKfSkgOwoKY29uc3QgcmVxdWVzdERhdGFJbnRlZ3JhdGlvbiA9IGRlZmluZUludGVncmF0aW9uKF9yZXF1ZXN0RGF0YUludGVncmF0aW9uKTsKCi8qKgogKiBBZGQgZGF0YSBhYm91dCBhIHJlcXVlc3QgdG8gYW4gZXZlbnQuIFByaW1hcmlseSBmb3IgdXNlIGluIE5vZGUtYmFzZWQgU0RLcywgYnV0IGluY2x1ZGVkIGluIGBAc2VudHJ5L2NvcmVgCiAqIHNvIGl0IGNhbiBiZSB1c2VkIGluIGNyb3NzLXBsYXRmb3JtIFNES3MgbGlrZSBgQHNlbnRyeS9uZXh0anNgLgogKiBAZGVwcmVjYXRlZCBVc2UgYHJlcXVlc3REYXRhSW50ZWdyYXRpb24oKWAgaW5zdGVhZC4KICovCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBkZXByZWNhdGlvbi9kZXByZWNhdGlvbgpjb252ZXJ0SW50ZWdyYXRpb25GblRvQ2xhc3MoSU5URUdSQVRJT05fTkFNRSQ5LCByZXF1ZXN0RGF0YUludGVncmF0aW9uKQoKOwoKLyoqIENvbnZlcnQgdGhpcyBpbnRlZ3JhdGlvbidzIG9wdGlvbnMgdG8gbWF0Y2ggd2hhdCBgYWRkUmVxdWVzdERhdGFUb0V2ZW50YCBleHBlY3RzICovCi8qKiBUT0RPOiBDYW4gcG9zc2libHkgYmUgZGVsZXRlZCBvbmNlIGh0dHBzOi8vZ2l0aHViLmNvbS9nZXRzZW50cnkvc2VudHJ5LWphdmFzY3JpcHQvaXNzdWVzLzU3MTggaXMgZml4ZWQgKi8KZnVuY3Rpb24gY29udmVydFJlcURhdGFJbnRlZ3JhdGlvbk9wdHNUb0FkZFJlcURhdGFPcHRzKAogIGludGVncmF0aW9uT3B0aW9ucywKKSB7CiAgY29uc3QgewogICAgdHJhbnNhY3Rpb25OYW1pbmdTY2hlbWUsCiAgICBpbmNsdWRlOiB7IGlwLCB1c2VyLCAuLi5yZXF1ZXN0T3B0aW9ucyB9LAogIH0gPSBpbnRlZ3JhdGlvbk9wdGlvbnM7CgogIGNvbnN0IHJlcXVlc3RJbmNsdWRlS2V5cyA9IFsnbWV0aG9kJ107CiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocmVxdWVzdE9wdGlvbnMpKSB7CiAgICBpZiAodmFsdWUpIHsKICAgICAgcmVxdWVzdEluY2x1ZGVLZXlzLnB1c2goa2V5KTsKICAgIH0KICB9CgogIGxldCBhZGRSZXFEYXRhVXNlck9wdDsKICBpZiAodXNlciA9PT0gdW5kZWZpbmVkKSB7CiAgICBhZGRSZXFEYXRhVXNlck9wdCA9IHRydWU7CiAgfSBlbHNlIGlmICh0eXBlb2YgdXNlciA9PT0gJ2Jvb2xlYW4nKSB7CiAgICBhZGRSZXFEYXRhVXNlck9wdCA9IHVzZXI7CiAgfSBlbHNlIHsKICAgIGNvbnN0IHVzZXJJbmNsdWRlS2V5cyA9IFtdOwogICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXModXNlcikpIHsKICAgICAgaWYgKHZhbHVlKSB7CiAgICAgICAgdXNlckluY2x1ZGVLZXlzLnB1c2goa2V5KTsKICAgICAgfQogICAgfQogICAgYWRkUmVxRGF0YVVzZXJPcHQgPSB1c2VySW5jbHVkZUtleXM7CiAgfQoKICByZXR1cm4gewogICAgaW5jbHVkZTogewogICAgICBpcCwKICAgICAgdXNlcjogYWRkUmVxRGF0YVVzZXJPcHQsCiAgICAgIHJlcXVlc3Q6IHJlcXVlc3RJbmNsdWRlS2V5cy5sZW5ndGggIT09IDAgPyByZXF1ZXN0SW5jbHVkZUtleXMgOiB1bmRlZmluZWQsCiAgICAgIHRyYW5zYWN0aW9uOiB0cmFuc2FjdGlvbk5hbWluZ1NjaGVtZSwKICAgIH0sCiAgfTsKfQoKZnVuY3Rpb24gZ2V0U0RLTmFtZShjbGllbnQpIHsKICB0cnkgewogICAgLy8gRm9yIGEgbG9uZyBjaGFpbiBsaWtlIHRoaXMsIGl0J3MgZmV3ZXIgYnl0ZXMgdG8gY29tYmluZSBhIHRyeS1jYXRjaCB3aXRoIGFzc3VtaW5nIGV2ZXJ5dGhpbmcgaXMgdGhlcmUgdGhhbiB0bwogICAgLy8gd3JpdGUgb3V0IGEgbG9uZyBjaGFpbiBvZiBgYSAmJiBhLmIgJiYgYS5iLmMgJiYgLi4uYAogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb24KICAgIHJldHVybiBjbGllbnQuZ2V0T3B0aW9ucygpLl9tZXRhZGF0YS5zZGsubmFtZTsKICB9IGNhdGNoIChlcnIpIHsKICAgIC8vIEluIHRoZW9yeSB3ZSBzaG91bGQgbmV2ZXIgZ2V0IGhlcmUKICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQp9CgpsZXQgb3JpZ2luYWxGdW5jdGlvblRvU3RyaW5nOwoKY29uc3QgSU5URUdSQVRJT05fTkFNRSQ4ID0gJ0Z1bmN0aW9uVG9TdHJpbmcnOwoKY29uc3QgU0VUVVBfQ0xJRU5UUyA9IG5ldyBXZWFrTWFwKCk7Cgpjb25zdCBfZnVuY3Rpb25Ub1N0cmluZ0ludGVncmF0aW9uID0gKCgpID0+IHsKICByZXR1cm4gewogICAgbmFtZTogSU5URUdSQVRJT05fTkFNRSQ4LAogICAgc2V0dXBPbmNlKCkgewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L3VuYm91bmQtbWV0aG9kCiAgICAgIG9yaWdpbmFsRnVuY3Rpb25Ub1N0cmluZyA9IEZ1bmN0aW9uLnByb3RvdHlwZS50b1N0cmluZzsKCiAgICAgIC8vIGludHJpbnNpY3MgKGxpa2UgRnVuY3Rpb24ucHJvdG90eXBlKSBtaWdodCBiZSBpbW11dGFibGUgaW4gc29tZSBlbnZpcm9ubWVudHMKICAgICAgLy8gZS5nLiBOb2RlIHdpdGggLS1mcm96ZW4taW50cmluc2ljcywgWFMgKGFuIGVtYmVkZGVkIEphdmFTY3JpcHQgZW5naW5lKSBvciBTRVMgKGEgSmF2YVNjcmlwdCBwcm9wb3NhbCkKICAgICAgdHJ5IHsKICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueQogICAgICAgIEZ1bmN0aW9uLnByb3RvdHlwZS50b1N0cmluZyA9IGZ1bmN0aW9uICggLi4uYXJncykgewogICAgICAgICAgY29uc3Qgb3JpZ2luYWxGdW5jdGlvbiA9IGdldE9yaWdpbmFsRnVuY3Rpb24odGhpcyk7CiAgICAgICAgICBjb25zdCBjb250ZXh0ID0KICAgICAgICAgICAgU0VUVVBfQ0xJRU5UUy5oYXMoZ2V0Q2xpZW50KCkgKSAmJiBvcmlnaW5hbEZ1bmN0aW9uICE9PSB1bmRlZmluZWQgPyBvcmlnaW5hbEZ1bmN0aW9uIDogdGhpczsKICAgICAgICAgIHJldHVybiBvcmlnaW5hbEZ1bmN0aW9uVG9TdHJpbmcuYXBwbHkoY29udGV4dCwgYXJncyk7CiAgICAgICAgfTsKICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIC8vIGlnbm9yZSBlcnJvcnMgaGVyZSwganVzdCBkb24ndCBwYXRjaCB0aGlzCiAgICAgIH0KICAgIH0sCiAgICBzZXR1cChjbGllbnQpIHsKICAgICAgU0VUVVBfQ0xJRU5UUy5zZXQoY2xpZW50LCB0cnVlKTsKICAgIH0sCiAgfTsKfSkgOwoKLyoqCiAqIFBhdGNoIHRvU3RyaW5nIGNhbGxzIHRvIHJldHVybiBwcm9wZXIgbmFtZSBmb3Igd3JhcHBlZCBmdW5jdGlvbnMuCiAqCiAqIGBgYGpzCiAqIFNlbnRyeS5pbml0KHsKICogICBpbnRlZ3JhdGlvbnM6IFsKICogICAgIGZ1bmN0aW9uVG9TdHJpbmdJbnRlZ3JhdGlvbigpLAogKiAgIF0sCiAqIH0pOwogKiBgYGAKICovCmNvbnN0IGZ1bmN0aW9uVG9TdHJpbmdJbnRlZ3JhdGlvbiA9IGRlZmluZUludGVncmF0aW9uKF9mdW5jdGlvblRvU3RyaW5nSW50ZWdyYXRpb24pOwoKLyoqCiAqIFBhdGNoIHRvU3RyaW5nIGNhbGxzIHRvIHJldHVybiBwcm9wZXIgbmFtZSBmb3Igd3JhcHBlZCBmdW5jdGlvbnMuCiAqCiAqIEBkZXByZWNhdGVkIFVzZSBgZnVuY3Rpb25Ub1N0cmluZ0ludGVncmF0aW9uKClgIGluc3RlYWQuCiAqLwovLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb24KY29udmVydEludGVncmF0aW9uRm5Ub0NsYXNzKAogIElOVEVHUkFUSU9OX05BTUUkOCwKICBmdW5jdGlvblRvU3RyaW5nSW50ZWdyYXRpb24sCikgOwoKZnVuY3Rpb24gX251bGxpc2hDb2FsZXNjZSQyKGxocywgcmhzRm4pIHsgaWYgKGxocyAhPSBudWxsKSB7IHJldHVybiBsaHM7IH0gZWxzZSB7IHJldHVybiByaHNGbigpOyB9IH0vKioKICogVGhpcyBjb2RlIHdhcyBvcmlnaW5hbGx5IGZvcmtlZCBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9Ub29UYWxsTmF0ZS9wcm94eS1hZ2VudHMvdHJlZS9iMTMzMjk1ZmQxNmY2NDc1NTc4YjZiMTViZDliNGUzM2VjYjBkMGI3CiAqIFdpdGggdGhlIGZvbGxvd2luZyBsaWNlbmNlOgogKgogKiAoVGhlIE1JVCBMaWNlbnNlKQogKgogKiBDb3B5cmlnaHQgKGMpIDIwMTMgTmF0aGFuIFJhamxpY2ggPG5hdGhhbkB0b290YWxsbmF0ZS5uZXQ+KgogKgogKiBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcKICogYSBjb3B5IG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlCiAqICdTb2Z0d2FyZScpLCB0byBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcKICogd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLAogKiBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Igc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8KICogcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvCiAqIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoqCiAqCiAqIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlCiAqIGluY2x1ZGVkIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLioKICoKICogVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEICdBUyBJUycsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsCiAqIEVYUFJFU1MgT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRgogKiBNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuCiAqIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZCiAqIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsCiAqIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFCiAqIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFIFNPRlRXQVJFLgogKi8KCmNvbnN0IElOVEVSTkFMID0gU3ltYm9sKCdBZ2VudEJhc2VJbnRlcm5hbFN0YXRlJyk7CgpjbGFzcyBBZ2VudCBleHRlbmRzIGh0dHAuQWdlbnQgewoKICAvLyBTZXQgYnkgYGh0dHAuQWdlbnRgIC0gbWlzc2luZyBmcm9tIGBAdHlwZXMvbm9kZWAKCiAgY29uc3RydWN0b3Iob3B0cykgewogICAgc3VwZXIob3B0cyk7CiAgICB0aGlzW0lOVEVSTkFMXSA9IHt9OwogIH0KCiAgLyoqCiAgICogRGV0ZXJtaW5lIHdoZXRoZXIgdGhpcyBpcyBhbiBgaHR0cGAgb3IgYGh0dHBzYCByZXF1ZXN0LgogICAqLwogIGlzU2VjdXJlRW5kcG9pbnQob3B0aW9ucykgewogICAgaWYgKG9wdGlvbnMpIHsKICAgICAgLy8gRmlyc3QgY2hlY2sgdGhlIGBzZWN1cmVFbmRwb2ludGAgcHJvcGVydHkgZXhwbGljaXRseSwgc2luY2UgdGhpcwogICAgICAvLyBtZWFucyB0aGF0IGEgcGFyZW50IGBBZ2VudGAgaXMgInBhc3NpbmcgdGhyb3VnaCIgdG8gdGhpcyBpbnN0YW5jZS4KICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnksIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnNhZmUtbWVtYmVyLWFjY2VzcwogICAgICBpZiAodHlwZW9mIChvcHRpb25zICkuc2VjdXJlRW5kcG9pbnQgPT09ICdib29sZWFuJykgewogICAgICAgIHJldHVybiBvcHRpb25zLnNlY3VyZUVuZHBvaW50OwogICAgICB9CgogICAgICAvLyBJZiBubyBleHBsaWNpdCBgc2VjdXJlYCBlbmRwb2ludCwgY2hlY2sgaWYgYHByb3RvY29sYCBwcm9wZXJ0eSBpcwogICAgICAvLyBzZXQuIFRoaXMgd2lsbCB1c3VhbGx5IGJlIHRoZSBjYXNlIHNpbmNlIHVzaW5nIGEgZnVsbCBzdHJpbmcgVVJMCiAgICAgIC8vIG9yIGBVUkxgIGluc3RhbmNlIHNob3VsZCBiZSB0aGUgbW9zdCBjb21tb24gdXNhZ2UuCiAgICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5wcm90b2NvbCA9PT0gJ3N0cmluZycpIHsKICAgICAgICByZXR1cm4gb3B0aW9ucy5wcm90b2NvbCA9PT0gJ2h0dHBzOic7CiAgICAgIH0KICAgIH0KCiAgICAvLyBGaW5hbGx5LCBpZiBubyBgcHJvdG9jb2xgIHByb3BlcnR5IHdhcyBzZXQsIHRoZW4gZmFsbCBiYWNrIHRvCiAgICAvLyBjaGVja2luZyB0aGUgc3RhY2sgdHJhY2Ugb2YgdGhlIGN1cnJlbnQgY2FsbCBzdGFjaywgYW5kIHRyeSB0bwogICAgLy8gZGV0ZWN0IHRoZSAiaHR0cHMiIG1vZHVsZS4KICAgIGNvbnN0IHsgc3RhY2sgfSA9IG5ldyBFcnJvcigpOwogICAgaWYgKHR5cGVvZiBzdGFjayAhPT0gJ3N0cmluZycpIHJldHVybiBmYWxzZTsKICAgIHJldHVybiBzdGFjay5zcGxpdCgnXG4nKS5zb21lKGwgPT4gbC5pbmRleE9mKCcoaHR0cHMuanM6JykgIT09IC0xIHx8IGwuaW5kZXhPZignbm9kZTpodHRwczonKSAhPT0gLTEpOwogIH0KCiAgY3JlYXRlU29ja2V0KHJlcSwgb3B0aW9ucywgY2IpIHsKICAgIGNvbnN0IGNvbm5lY3RPcHRzID0gewogICAgICAuLi5vcHRpb25zLAogICAgICBzZWN1cmVFbmRwb2ludDogdGhpcy5pc1NlY3VyZUVuZHBvaW50KG9wdGlvbnMpLAogICAgfTsKICAgIFByb21pc2UucmVzb2x2ZSgpCiAgICAgIC50aGVuKCgpID0+IHRoaXMuY29ubmVjdChyZXEsIGNvbm5lY3RPcHRzKSkKICAgICAgLnRoZW4oc29ja2V0ID0+IHsKICAgICAgICBpZiAoc29ja2V0IGluc3RhbmNlb2YgaHR0cC5BZ2VudCkgewogICAgICAgICAgLy8gQHRzLWV4cGVjdC1lcnJvciBgYWRkUmVxdWVzdCgpYCBpc24ndCBkZWZpbmVkIGluIGBAdHlwZXMvbm9kZWAKICAgICAgICAgIHJldHVybiBzb2NrZXQuYWRkUmVxdWVzdChyZXEsIGNvbm5lY3RPcHRzKTsKICAgICAgICB9CiAgICAgICAgdGhpc1tJTlRFUk5BTF0uY3VycmVudFNvY2tldCA9IHNvY2tldDsKICAgICAgICAvLyBAdHMtZXhwZWN0LWVycm9yIGBjcmVhdGVTb2NrZXQoKWAgaXNuJ3QgZGVmaW5lZCBpbiBgQHR5cGVzL25vZGVgCiAgICAgICAgc3VwZXIuY3JlYXRlU29ja2V0KHJlcSwgb3B0aW9ucywgY2IpOwogICAgICB9LCBjYik7CiAgfQoKICBjcmVhdGVDb25uZWN0aW9uKCkgewogICAgY29uc3Qgc29ja2V0ID0gdGhpc1tJTlRFUk5BTF0uY3VycmVudFNvY2tldDsKICAgIHRoaXNbSU5URVJOQUxdLmN1cnJlbnRTb2NrZXQgPSB1bmRlZmluZWQ7CiAgICBpZiAoIXNvY2tldCkgewogICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHNvY2tldCB3YXMgcmV0dXJuZWQgaW4gdGhlIGBjb25uZWN0KClgIGZ1bmN0aW9uJyk7CiAgICB9CiAgICByZXR1cm4gc29ja2V0OwogIH0KCiAgZ2V0IGRlZmF1bHRQb3J0KCkgewogICAgcmV0dXJuIF9udWxsaXNoQ29hbGVzY2UkMih0aGlzW0lOVEVSTkFMXS5kZWZhdWx0UG9ydCwgKCkgPT4gKCAodGhpcy5wcm90b2NvbCA9PT0gJ2h0dHBzOicgPyA0NDMgOiA4MCkpKTsKICB9CgogIHNldCBkZWZhdWx0UG9ydCh2KSB7CiAgICBpZiAodGhpc1tJTlRFUk5BTF0pIHsKICAgICAgdGhpc1tJTlRFUk5BTF0uZGVmYXVsdFBvcnQgPSB2OwogICAgfQogIH0KCiAgZ2V0IHByb3RvY29sKCkgewogICAgcmV0dXJuIF9udWxsaXNoQ29hbGVzY2UkMih0aGlzW0lOVEVSTkFMXS5wcm90b2NvbCwgKCkgPT4gKCAodGhpcy5pc1NlY3VyZUVuZHBvaW50KCkgPyAnaHR0cHM6JyA6ICdodHRwOicpKSk7CiAgfQoKICBzZXQgcHJvdG9jb2wodikgewogICAgaWYgKHRoaXNbSU5URVJOQUxdKSB7CiAgICAgIHRoaXNbSU5URVJOQUxdLnByb3RvY29sID0gdjsKICAgIH0KICB9Cn0KCmZ1bmN0aW9uIGRlYnVnJDEoLi4uYXJncykgewogIGxvZ2dlci5sb2coJ1todHRwcy1wcm94eS1hZ2VudDpwYXJzZS1wcm94eS1yZXNwb25zZV0nLCAuLi5hcmdzKTsKfQoKZnVuY3Rpb24gcGFyc2VQcm94eVJlc3BvbnNlKHNvY2tldCkgewogIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICAvLyB3ZSBuZWVkIHRvIGJ1ZmZlciBhbnkgSFRUUCB0cmFmZmljIHRoYXQgaGFwcGVucyB3aXRoIHRoZSBwcm94eSBiZWZvcmUgd2UgZ2V0CiAgICAvLyB0aGUgQ09OTkVDVCByZXNwb25zZSwgc28gdGhhdCBpZiB0aGUgcmVzcG9uc2UgaXMgYW55dGhpbmcgb3RoZXIgdGhhbiBhbiAiMjAwIgogICAgLy8gcmVzcG9uc2UgY29kZSwgdGhlbiB3ZSBjYW4gcmUtcGxheSB0aGUgImRhdGEiIGV2ZW50cyBvbiB0aGUgc29ja2V0IG9uY2UgdGhlCiAgICAvLyBIVFRQIHBhcnNlciBpcyBob29rZWQgdXAuLi4KICAgIGxldCBidWZmZXJzTGVuZ3RoID0gMDsKICAgIGNvbnN0IGJ1ZmZlcnMgPSBbXTsKCiAgICBmdW5jdGlvbiByZWFkKCkgewogICAgICBjb25zdCBiID0gc29ja2V0LnJlYWQoKTsKICAgICAgaWYgKGIpIG9uZGF0YShiKTsKICAgICAgZWxzZSBzb2NrZXQub25jZSgncmVhZGFibGUnLCByZWFkKTsKICAgIH0KCiAgICBmdW5jdGlvbiBjbGVhbnVwKCkgewogICAgICBzb2NrZXQucmVtb3ZlTGlzdGVuZXIoJ2VuZCcsIG9uZW5kKTsKICAgICAgc29ja2V0LnJlbW92ZUxpc3RlbmVyKCdlcnJvcicsIG9uZXJyb3IpOwogICAgICBzb2NrZXQucmVtb3ZlTGlzdGVuZXIoJ3JlYWRhYmxlJywgcmVhZCk7CiAgICB9CgogICAgZnVuY3Rpb24gb25lbmQoKSB7CiAgICAgIGNsZWFudXAoKTsKICAgICAgZGVidWckMSgnb25lbmQnKTsKICAgICAgcmVqZWN0KG5ldyBFcnJvcignUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UnKSk7CiAgICB9CgogICAgZnVuY3Rpb24gb25lcnJvcihlcnIpIHsKICAgICAgY2xlYW51cCgpOwogICAgICBkZWJ1ZyQxKCdvbmVycm9yICVvJywgZXJyKTsKICAgICAgcmVqZWN0KGVycik7CiAgICB9CgogICAgZnVuY3Rpb24gb25kYXRhKGIpIHsKICAgICAgYnVmZmVycy5wdXNoKGIpOwogICAgICBidWZmZXJzTGVuZ3RoICs9IGIubGVuZ3RoOwoKICAgICAgY29uc3QgYnVmZmVyZWQgPSBCdWZmZXIuY29uY2F0KGJ1ZmZlcnMsIGJ1ZmZlcnNMZW5ndGgpOwogICAgICBjb25zdCBlbmRPZkhlYWRlcnMgPSBidWZmZXJlZC5pbmRleE9mKCdcclxuXHJcbicpOwoKICAgICAgaWYgKGVuZE9mSGVhZGVycyA9PT0gLTEpIHsKICAgICAgICAvLyBrZWVwIGJ1ZmZlcmluZwogICAgICAgIGRlYnVnJDEoJ2hhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uJyk7CiAgICAgICAgcmVhZCgpOwogICAgICAgIHJldHVybjsKICAgICAgfQoKICAgICAgY29uc3QgaGVhZGVyUGFydHMgPSBidWZmZXJlZC5zbGljZSgwLCBlbmRPZkhlYWRlcnMpLnRvU3RyaW5nKCdhc2NpaScpLnNwbGl0KCdcclxuJyk7CiAgICAgIGNvbnN0IGZpcnN0TGluZSA9IGhlYWRlclBhcnRzLnNoaWZ0KCk7CiAgICAgIGlmICghZmlyc3RMaW5lKSB7CiAgICAgICAgc29ja2V0LmRlc3Ryb3koKTsKICAgICAgICByZXR1cm4gcmVqZWN0KG5ldyBFcnJvcignTm8gaGVhZGVyIHJlY2VpdmVkIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZScpKTsKICAgICAgfQogICAgICBjb25zdCBmaXJzdExpbmVQYXJ0cyA9IGZpcnN0TGluZS5zcGxpdCgnICcpOwogICAgICBjb25zdCBzdGF0dXNDb2RlID0gK2ZpcnN0TGluZVBhcnRzWzFdOwogICAgICBjb25zdCBzdGF0dXNUZXh0ID0gZmlyc3RMaW5lUGFydHMuc2xpY2UoMikuam9pbignICcpOwogICAgICBjb25zdCBoZWFkZXJzID0ge307CiAgICAgIGZvciAoY29uc3QgaGVhZGVyIG9mIGhlYWRlclBhcnRzKSB7CiAgICAgICAgaWYgKCFoZWFkZXIpIGNvbnRpbnVlOwogICAgICAgIGNvbnN0IGZpcnN0Q29sb24gPSBoZWFkZXIuaW5kZXhPZignOicpOwogICAgICAgIGlmIChmaXJzdENvbG9uID09PSAtMSkgewogICAgICAgICAgc29ja2V0LmRlc3Ryb3koKTsKICAgICAgICAgIHJldHVybiByZWplY3QobmV3IEVycm9yKGBJbnZhbGlkIGhlYWRlciBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2U6ICIke2hlYWRlcn0iYCkpOwogICAgICAgIH0KICAgICAgICBjb25zdCBrZXkgPSBoZWFkZXIuc2xpY2UoMCwgZmlyc3RDb2xvbikudG9Mb3dlckNhc2UoKTsKICAgICAgICBjb25zdCB2YWx1ZSA9IGhlYWRlci5zbGljZShmaXJzdENvbG9uICsgMSkudHJpbVN0YXJ0KCk7CiAgICAgICAgY29uc3QgY3VycmVudCA9IGhlYWRlcnNba2V5XTsKICAgICAgICBpZiAodHlwZW9mIGN1cnJlbnQgPT09ICdzdHJpbmcnKSB7CiAgICAgICAgICBoZWFkZXJzW2tleV0gPSBbY3VycmVudCwgdmFsdWVdOwogICAgICAgIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheShjdXJyZW50KSkgewogICAgICAgICAgY3VycmVudC5wdXNoKHZhbHVlKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgaGVhZGVyc1trZXldID0gdmFsdWU7CiAgICAgICAgfQogICAgICB9CiAgICAgIGRlYnVnJDEoJ2dvdCBwcm94eSBzZXJ2ZXIgcmVzcG9uc2U6ICVvICVvJywgZmlyc3RMaW5lLCBoZWFkZXJzKTsKICAgICAgY2xlYW51cCgpOwogICAgICByZXNvbHZlKHsKICAgICAgICBjb25uZWN0OiB7CiAgICAgICAgICBzdGF0dXNDb2RlLAogICAgICAgICAgc3RhdHVzVGV4dCwKICAgICAgICAgIGhlYWRlcnMsCiAgICAgICAgfSwKICAgICAgICBidWZmZXJlZCwKICAgICAgfSk7CiAgICB9CgogICAgc29ja2V0Lm9uKCdlcnJvcicsIG9uZXJyb3IpOwogICAgc29ja2V0Lm9uKCdlbmQnLCBvbmVuZCk7CgogICAgcmVhZCgpOwogIH0pOwp9CgpmdW5jdGlvbiBfbnVsbGlzaENvYWxlc2NlJDEobGhzLCByaHNGbikgeyBpZiAobGhzICE9IG51bGwpIHsgcmV0dXJuIGxoczsgfSBlbHNlIHsgcmV0dXJuIHJoc0ZuKCk7IH0gfSBmdW5jdGlvbiBfb3B0aW9uYWxDaGFpbiQxKG9wcykgeyBsZXQgbGFzdEFjY2Vzc0xIUyA9IHVuZGVmaW5lZDsgbGV0IHZhbHVlID0gb3BzWzBdOyBsZXQgaSA9IDE7IHdoaWxlIChpIDwgb3BzLmxlbmd0aCkgeyBjb25zdCBvcCA9IG9wc1tpXTsgY29uc3QgZm4gPSBvcHNbaSArIDFdOyBpICs9IDI7IGlmICgob3AgPT09ICdvcHRpb25hbEFjY2VzcycgfHwgb3AgPT09ICdvcHRpb25hbENhbGwnKSAmJiB2YWx1ZSA9PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gaWYgKG9wID09PSAnYWNjZXNzJyB8fCBvcCA9PT0gJ29wdGlvbmFsQWNjZXNzJykgeyBsYXN0QWNjZXNzTEhTID0gdmFsdWU7IHZhbHVlID0gZm4odmFsdWUpOyB9IGVsc2UgaWYgKG9wID09PSAnY2FsbCcgfHwgb3AgPT09ICdvcHRpb25hbENhbGwnKSB7IHZhbHVlID0gZm4oKC4uLmFyZ3MpID0+IHZhbHVlLmNhbGwobGFzdEFjY2Vzc0xIUywgLi4uYXJncykpOyBsYXN0QWNjZXNzTEhTID0gdW5kZWZpbmVkOyB9IH0gcmV0dXJuIHZhbHVlOyB9CgpmdW5jdGlvbiBkZWJ1ZyguLi5hcmdzKSB7CiAgbG9nZ2VyLmxvZygnW2h0dHBzLXByb3h5LWFnZW50XScsIC4uLmFyZ3MpOwp9CgovKioKICogVGhlIGBIdHRwc1Byb3h5QWdlbnRgIGltcGxlbWVudHMgYW4gSFRUUCBBZ2VudCBzdWJjbGFzcyB0aGF0IGNvbm5lY3RzIHRvCiAqIHRoZSBzcGVjaWZpZWQgIkhUVFAocykgcHJveHkgc2VydmVyIiBpbiBvcmRlciB0byBwcm94eSBIVFRQUyByZXF1ZXN0cy4KICoKICogT3V0Z29pbmcgSFRUUCByZXF1ZXN0cyBhcmUgZmlyc3QgdHVubmVsZWQgdGhyb3VnaCB0aGUgcHJveHkgc2VydmVyIHVzaW5nIHRoZQogKiBgQ09OTkVDVGAgSFRUUCByZXF1ZXN0IG1ldGhvZCB0byBlc3RhYmxpc2ggYSBjb25uZWN0aW9uIHRvIHRoZSBwcm94eSBzZXJ2ZXIsCiAqIGFuZCB0aGVuIHRoZSBwcm94eSBzZXJ2ZXIgY29ubmVjdHMgdG8gdGhlIGRlc3RpbmF0aW9uIHRhcmdldCBhbmQgaXNzdWVzIHRoZQogKiBIVFRQIHJlcXVlc3QgZnJvbSB0aGUgcHJveHkgc2VydmVyLgogKgogKiBgaHR0cHM6YCByZXF1ZXN0cyBoYXZlIHRoZWlyIHNvY2tldCBjb25uZWN0aW9uIHVwZ3JhZGVkIHRvIFRMUyBvbmNlCiAqIHRoZSBjb25uZWN0aW9uIHRvIHRoZSBwcm94eSBzZXJ2ZXIgaGFzIGJlZW4gZXN0YWJsaXNoZWQuCiAqLwpjbGFzcyBIdHRwc1Byb3h5QWdlbnQgZXh0ZW5kcyBBZ2VudCB7CiAgc3RhdGljIF9faW5pdFN0YXRpYygpIHt0aGlzLnByb3RvY29scyA9IFsnaHR0cCcsICdodHRwcyddOyB9CgogIGNvbnN0cnVjdG9yKHByb3h5LCBvcHRzKSB7CiAgICBzdXBlcihvcHRzKTsKICAgIHRoaXMub3B0aW9ucyA9IHt9OwogICAgdGhpcy5wcm94eSA9IHR5cGVvZiBwcm94eSA9PT0gJ3N0cmluZycgPyBuZXcgVVJMKHByb3h5KSA6IHByb3h5OwogICAgdGhpcy5wcm94eUhlYWRlcnMgPSBfbnVsbGlzaENvYWxlc2NlJDEoX29wdGlvbmFsQ2hhaW4kMShbb3B0cywgJ29wdGlvbmFsQWNjZXNzJywgXzIgPT4gXzIuaGVhZGVyc10pLCAoKSA9PiAoIHt9KSk7CiAgICBkZWJ1ZygnQ3JlYXRpbmcgbmV3IEh0dHBzUHJveHlBZ2VudCBpbnN0YW5jZTogJW8nLCB0aGlzLnByb3h5LmhyZWYpOwoKICAgIC8vIFRyaW0gb2ZmIHRoZSBicmFja2V0cyBmcm9tIElQdjYgYWRkcmVzc2VzCiAgICBjb25zdCBob3N0ID0gKHRoaXMucHJveHkuaG9zdG5hbWUgfHwgdGhpcy5wcm94eS5ob3N0KS5yZXBsYWNlKC9eXFt8XF0kL2csICcnKTsKICAgIGNvbnN0IHBvcnQgPSB0aGlzLnByb3h5LnBvcnQgPyBwYXJzZUludCh0aGlzLnByb3h5LnBvcnQsIDEwKSA6IHRoaXMucHJveHkucHJvdG9jb2wgPT09ICdodHRwczonID8gNDQzIDogODA7CiAgICB0aGlzLmNvbm5lY3RPcHRzID0gewogICAgICAvLyBBdHRlbXB0IHRvIG5lZ290aWF0ZSBodHRwLzEuMSBmb3IgcHJveHkgc2VydmVycyB0aGF0IHN1cHBvcnQgaHR0cC8yCiAgICAgIEFMUE5Qcm90b2NvbHM6IFsnaHR0cC8xLjEnXSwKICAgICAgLi4uKG9wdHMgPyBvbWl0KG9wdHMsICdoZWFkZXJzJykgOiBudWxsKSwKICAgICAgaG9zdCwKICAgICAgcG9ydCwKICAgIH07CiAgfQoKICAvKioKICAgKiBDYWxsZWQgd2hlbiB0aGUgbm9kZS1jb3JlIEhUVFAgY2xpZW50IGxpYnJhcnkgaXMgY3JlYXRpbmcgYQogICAqIG5ldyBIVFRQIHJlcXVlc3QuCiAgICovCiAgYXN5bmMgY29ubmVjdChyZXEsIG9wdHMpIHsKICAgIGNvbnN0IHsgcHJveHkgfSA9IHRoaXM7CgogICAgaWYgKCFvcHRzLmhvc3QpIHsKICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignTm8gImhvc3QiIHByb3ZpZGVkJyk7CiAgICB9CgogICAgLy8gQ3JlYXRlIGEgc29ja2V0IGNvbm5lY3Rpb24gdG8gdGhlIHByb3h5IHNlcnZlci4KICAgIGxldCBzb2NrZXQ7CiAgICBpZiAocHJveHkucHJvdG9jb2wgPT09ICdodHRwczonKSB7CiAgICAgIGRlYnVnKCdDcmVhdGluZyBgdGxzLlNvY2tldGA6ICVvJywgdGhpcy5jb25uZWN0T3B0cyk7CiAgICAgIGNvbnN0IHNlcnZlcm5hbWUgPSB0aGlzLmNvbm5lY3RPcHRzLnNlcnZlcm5hbWUgfHwgdGhpcy5jb25uZWN0T3B0cy5ob3N0OwogICAgICBzb2NrZXQgPSB0bHMuY29ubmVjdCh7CiAgICAgICAgLi4udGhpcy5jb25uZWN0T3B0cywKICAgICAgICBzZXJ2ZXJuYW1lOiBzZXJ2ZXJuYW1lICYmIG5ldC5pc0lQKHNlcnZlcm5hbWUpID8gdW5kZWZpbmVkIDogc2VydmVybmFtZSwKICAgICAgfSk7CiAgICB9IGVsc2UgewogICAgICBkZWJ1ZygnQ3JlYXRpbmcgYG5ldC5Tb2NrZXRgOiAlbycsIHRoaXMuY29ubmVjdE9wdHMpOwogICAgICBzb2NrZXQgPSBuZXQuY29ubmVjdCh0aGlzLmNvbm5lY3RPcHRzKTsKICAgIH0KCiAgICBjb25zdCBoZWFkZXJzID0KICAgICAgdHlwZW9mIHRoaXMucHJveHlIZWFkZXJzID09PSAnZnVuY3Rpb24nID8gdGhpcy5wcm94eUhlYWRlcnMoKSA6IHsgLi4udGhpcy5wcm94eUhlYWRlcnMgfTsKICAgIGNvbnN0IGhvc3QgPSBuZXQuaXNJUHY2KG9wdHMuaG9zdCkgPyBgWyR7b3B0cy5ob3N0fV1gIDogb3B0cy5ob3N0OwogICAgbGV0IHBheWxvYWQgPSBgQ09OTkVDVCAke2hvc3R9OiR7b3B0cy5wb3J0fSBIVFRQLzEuMVxyXG5gOwoKICAgIC8vIEluamVjdCB0aGUgYFByb3h5LUF1dGhvcml6YXRpb25gIGhlYWRlciBpZiBuZWNlc3NhcnkuCiAgICBpZiAocHJveHkudXNlcm5hbWUgfHwgcHJveHkucGFzc3dvcmQpIHsKICAgICAgY29uc3QgYXV0aCA9IGAke2RlY29kZVVSSUNvbXBvbmVudChwcm94eS51c2VybmFtZSl9OiR7ZGVjb2RlVVJJQ29tcG9uZW50KHByb3h5LnBhc3N3b3JkKX1gOwogICAgICBoZWFkZXJzWydQcm94eS1BdXRob3JpemF0aW9uJ10gPSBgQmFzaWMgJHtCdWZmZXIuZnJvbShhdXRoKS50b1N0cmluZygnYmFzZTY0Jyl9YDsKICAgIH0KCiAgICBoZWFkZXJzLkhvc3QgPSBgJHtob3N0fToke29wdHMucG9ydH1gOwoKICAgIGlmICghaGVhZGVyc1snUHJveHktQ29ubmVjdGlvbiddKSB7CiAgICAgIGhlYWRlcnNbJ1Byb3h5LUNvbm5lY3Rpb24nXSA9IHRoaXMua2VlcEFsaXZlID8gJ0tlZXAtQWxpdmUnIDogJ2Nsb3NlJzsKICAgIH0KICAgIGZvciAoY29uc3QgbmFtZSBvZiBPYmplY3Qua2V5cyhoZWFkZXJzKSkgewogICAgICBwYXlsb2FkICs9IGAke25hbWV9OiAke2hlYWRlcnNbbmFtZV19XHJcbmA7CiAgICB9CgogICAgY29uc3QgcHJveHlSZXNwb25zZVByb21pc2UgPSBwYXJzZVByb3h5UmVzcG9uc2Uoc29ja2V0KTsKCiAgICBzb2NrZXQud3JpdGUoYCR7cGF5bG9hZH1cclxuYCk7CgogICAgY29uc3QgeyBjb25uZWN0LCBidWZmZXJlZCB9ID0gYXdhaXQgcHJveHlSZXNwb25zZVByb21pc2U7CiAgICByZXEuZW1pdCgncHJveHlDb25uZWN0JywgY29ubmVjdCk7CiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2Jhbi10cy1jb21tZW50CiAgICAvLyBAdHMtaWdub3JlIE5vdCBFdmVudEVtaXR0ZXIgaW4gTm9kZSB0eXBlcwogICAgdGhpcy5lbWl0KCdwcm94eUNvbm5lY3QnLCBjb25uZWN0LCByZXEpOwoKICAgIGlmIChjb25uZWN0LnN0YXR1c0NvZGUgPT09IDIwMCkgewogICAgICByZXEub25jZSgnc29ja2V0JywgcmVzdW1lKTsKCiAgICAgIGlmIChvcHRzLnNlY3VyZUVuZHBvaW50KSB7CiAgICAgICAgLy8gVGhlIHByb3h5IGlzIGNvbm5lY3RpbmcgdG8gYSBUTFMgc2VydmVyLCBzbyB1cGdyYWRlCiAgICAgICAgLy8gdGhpcyBzb2NrZXQgY29ubmVjdGlvbiB0byBhIFRMUyBjb25uZWN0aW9uLgogICAgICAgIGRlYnVnKCdVcGdyYWRpbmcgc29ja2V0IGNvbm5lY3Rpb24gdG8gVExTJyk7CiAgICAgICAgY29uc3Qgc2VydmVybmFtZSA9IG9wdHMuc2VydmVybmFtZSB8fCBvcHRzLmhvc3Q7CiAgICAgICAgcmV0dXJuIHRscy5jb25uZWN0KHsKICAgICAgICAgIC4uLm9taXQob3B0cywgJ2hvc3QnLCAncGF0aCcsICdwb3J0JyksCiAgICAgICAgICBzb2NrZXQsCiAgICAgICAgICBzZXJ2ZXJuYW1lOiBuZXQuaXNJUChzZXJ2ZXJuYW1lKSA/IHVuZGVmaW5lZCA6IHNlcnZlcm5hbWUsCiAgICAgICAgfSk7CiAgICAgIH0KCiAgICAgIHJldHVybiBzb2NrZXQ7CiAgICB9CgogICAgLy8gU29tZSBvdGhlciBzdGF0dXMgY29kZSB0aGF0J3Mgbm90IDIwMC4uLiBuZWVkIHRvIHJlLXBsYXkgdGhlIEhUVFAKICAgIC8vIGhlYWRlciAiZGF0YSIgZXZlbnRzIG9udG8gdGhlIHNvY2tldCBvbmNlIHRoZSBIVFRQIG1hY2hpbmVyeSBpcwogICAgLy8gYXR0YWNoZWQgc28gdGhhdCB0aGUgbm9kZSBjb3JlIGBodHRwYCBjYW4gcGFyc2UgYW5kIGhhbmRsZSB0aGUKICAgIC8vIGVycm9yIHN0YXR1cyBjb2RlLgoKICAgIC8vIENsb3NlIHRoZSBvcmlnaW5hbCBzb2NrZXQsIGFuZCBhIG5ldyAiZmFrZSIgc29ja2V0IGlzIHJldHVybmVkCiAgICAvLyBpbnN0ZWFkLCBzbyB0aGF0IHRoZSBwcm94eSBkb2Vzbid0IGdldCB0aGUgSFRUUCByZXF1ZXN0CiAgICAvLyB3cml0dGVuIHRvIGl0ICh3aGljaCBtYXkgY29udGFpbiBgQXV0aG9yaXphdGlvbmAgaGVhZGVycyBvciBvdGhlcgogICAgLy8gc2Vuc2l0aXZlIGRhdGEpLgogICAgLy8KICAgIC8vIFNlZTogaHR0cHM6Ly9oYWNrZXJvbmUuY29tL3JlcG9ydHMvNTQxNTAyCiAgICBzb2NrZXQuZGVzdHJveSgpOwoKICAgIGNvbnN0IGZha2VTb2NrZXQgPSBuZXcgbmV0LlNvY2tldCh7IHdyaXRhYmxlOiBmYWxzZSB9KTsKICAgIGZha2VTb2NrZXQucmVhZGFibGUgPSB0cnVlOwoKICAgIC8vIE5lZWQgdG8gd2FpdCBmb3IgdGhlICJzb2NrZXQiIGV2ZW50IHRvIHJlLXBsYXkgdGhlICJkYXRhIiBldmVudHMuCiAgICByZXEub25jZSgnc29ja2V0JywgKHMpID0+IHsKICAgICAgZGVidWcoJ1JlcGxheWluZyBwcm94eSBidWZmZXIgZm9yIGZhaWxlZCByZXF1ZXN0Jyk7CiAgICAgIC8vIFJlcGxheSB0aGUgImJ1ZmZlcmVkIiBCdWZmZXIgb250byB0aGUgZmFrZSBgc29ja2V0YCwgc2luY2UgYXQKICAgICAgLy8gdGhpcyBwb2ludCB0aGUgSFRUUCBtb2R1bGUgbWFjaGluZXJ5IGhhcyBiZWVuIGhvb2tlZCB1cCBmb3IKICAgICAgLy8gdGhlIHVzZXIuCiAgICAgIHMucHVzaChidWZmZXJlZCk7CiAgICAgIHMucHVzaChudWxsKTsKICAgIH0pOwoKICAgIHJldHVybiBmYWtlU29ja2V0OwogIH0KfSBIdHRwc1Byb3h5QWdlbnQuX19pbml0U3RhdGljKCk7CgpmdW5jdGlvbiByZXN1bWUoc29ja2V0KSB7CiAgc29ja2V0LnJlc3VtZSgpOwp9CgpmdW5jdGlvbiBvbWl0KAogIG9iaiwKICAuLi5rZXlzCikKCiB7CiAgY29uc3QgcmV0ID0ge30KCjsKICBsZXQga2V5OwogIGZvciAoa2V5IGluIG9iaikgewogICAgaWYgKCFrZXlzLmluY2x1ZGVzKGtleSkpIHsKICAgICAgcmV0W2tleV0gPSBvYmpba2V5XTsKICAgIH0KICB9CiAgcmV0dXJuIHJldDsKfQoKZnVuY3Rpb24gX251bGxpc2hDb2FsZXNjZShsaHMsIHJoc0ZuKSB7IGlmIChsaHMgIT0gbnVsbCkgeyByZXR1cm4gbGhzOyB9IGVsc2UgeyByZXR1cm4gcmhzRm4oKTsgfSB9Ci8vIEVzdGltYXRlZCBtYXhpbXVtIHNpemUgZm9yIHJlYXNvbmFibGUgc3RhbmRhbG9uZSBldmVudApjb25zdCBHWklQX1RIUkVTSE9MRCA9IDEwMjQgKiAzMjsKCi8qKgogKiBHZXRzIGEgc3RyZWFtIGZyb20gYSBVaW50OEFycmF5IG9yIHN0cmluZwogKiBSZWFkYWJsZS5mcm9tIGlzIGlkZWFsIGJ1dCB3YXMgYWRkZWQgaW4gbm9kZS5qcyB2MTIuMy4wIGFuZCB2MTAuMTcuMAogKi8KZnVuY3Rpb24gc3RyZWFtRnJvbUJvZHkoYm9keSkgewogIHJldHVybiBuZXcgUmVhZGFibGUoewogICAgcmVhZCgpIHsKICAgICAgdGhpcy5wdXNoKGJvZHkpOwogICAgICB0aGlzLnB1c2gobnVsbCk7CiAgICB9LAogIH0pOwp9CgovKioKICogQ3JlYXRlcyBhIFRyYW5zcG9ydCB0aGF0IHVzZXMgbmF0aXZlIHRoZSBuYXRpdmUgJ2h0dHAnIGFuZCAnaHR0cHMnIG1vZHVsZXMgdG8gc2VuZCBldmVudHMgdG8gU2VudHJ5LgogKi8KZnVuY3Rpb24gbWFrZU5vZGVUcmFuc3BvcnQob3B0aW9ucykgewogIGxldCB1cmxTZWdtZW50czsKCiAgdHJ5IHsKICAgIHVybFNlZ21lbnRzID0gbmV3IFVSTChvcHRpb25zLnVybCk7CiAgfSBjYXRjaCAoZSkgewogICAgY29uc29sZVNhbmRib3goKCkgPT4gewogICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZQogICAgICBjb25zb2xlLndhcm4oCiAgICAgICAgJ1tAc2VudHJ5L25vZGVdOiBJbnZhbGlkIGRzbiBvciB0dW5uZWwgb3B0aW9uLCB3aWxsIG5vdCBzZW5kIGFueSBldmVudHMuIFRoZSB0dW5uZWwgb3B0aW9uIG11c3QgYmUgYSBmdWxsIFVSTCB3aGVuIHVzZWQuJywKICAgICAgKTsKICAgIH0pOwogICAgcmV0dXJuIGNyZWF0ZVRyYW5zcG9ydChvcHRpb25zLCAoKSA9PiBQcm9taXNlLnJlc29sdmUoe30pKTsKICB9CgogIGNvbnN0IGlzSHR0cHMgPSB1cmxTZWdtZW50cy5wcm90b2NvbCA9PT0gJ2h0dHBzOic7CgogIC8vIFByb3h5IHByaW9yaXRpemF0aW9uOiBodHRwID0+IGBvcHRpb25zLnByb3h5YCB8IGBwcm9jZXNzLmVudi5odHRwX3Byb3h5YAogIC8vIFByb3h5IHByaW9yaXRpemF0aW9uOiBodHRwcyA9PiBgb3B0aW9ucy5wcm94eWAgfCBgcHJvY2Vzcy5lbnYuaHR0cHNfcHJveHlgIHwgYHByb2Nlc3MuZW52Lmh0dHBfcHJveHlgCiAgY29uc3QgcHJveHkgPSBhcHBseU5vUHJveHlPcHRpb24oCiAgICB1cmxTZWdtZW50cywKICAgIG9wdGlvbnMucHJveHkgfHwgKGlzSHR0cHMgPyBwcm9jZXNzLmVudi5odHRwc19wcm94eSA6IHVuZGVmaW5lZCkgfHwgcHJvY2Vzcy5lbnYuaHR0cF9wcm94eSwKICApOwoKICBjb25zdCBuYXRpdmVIdHRwTW9kdWxlID0gaXNIdHRwcyA/IGh0dHBzIDogaHR0cCQxOwogIGNvbnN0IGtlZXBBbGl2ZSA9IG9wdGlvbnMua2VlcEFsaXZlID09PSB1bmRlZmluZWQgPyBmYWxzZSA6IG9wdGlvbnMua2VlcEFsaXZlOwoKICAvLyBUT0RPKHY3KTogRXZhbHVhdGUgaWYgd2UgY2FuIHNldCBrZWVwQWxpdmUgdG8gdHJ1ZS4gVGhpcyB3b3VsZCBpbnZvbHZlIHRlc3RpbmcgZm9yIG1lbW9yeSBsZWFrcyBpbiBvbGRlciBub2RlCiAgLy8gdmVyc2lvbnMoPj0gOCkgYXMgdGhleSBoYWQgbWVtb3J5IGxlYWtzIHdoZW4gdXNpbmcgaXQ6ICMyNTU1CiAgY29uc3QgYWdlbnQgPSBwcm94eQogICAgPyAobmV3IEh0dHBzUHJveHlBZ2VudChwcm94eSkgKQogICAgOiBuZXcgbmF0aXZlSHR0cE1vZHVsZS5BZ2VudCh7IGtlZXBBbGl2ZSwgbWF4U29ja2V0czogMzAsIHRpbWVvdXQ6IDIwMDAgfSk7CgogIGNvbnN0IHJlcXVlc3RFeGVjdXRvciA9IGNyZWF0ZVJlcXVlc3RFeGVjdXRvcihvcHRpb25zLCBfbnVsbGlzaENvYWxlc2NlKG9wdGlvbnMuaHR0cE1vZHVsZSwgKCkgPT4gKCBuYXRpdmVIdHRwTW9kdWxlKSksIGFnZW50KTsKICByZXR1cm4gY3JlYXRlVHJhbnNwb3J0KG9wdGlvbnMsIHJlcXVlc3RFeGVjdXRvcik7Cn0KCi8qKgogKiBIb25vcnMgdGhlIGBub19wcm94eWAgZW52IHZhcmlhYmxlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdG8gYWxsb3cgZm9yIGhvc3RzIGV4Y2x1c2lvbi4KICoKICogQHBhcmFtIHRyYW5zcG9ydFVybCBUaGUgVVJMIHRoZSB0cmFuc3BvcnQgaW50ZW5kcyB0byBzZW5kIGV2ZW50cyB0by4KICogQHBhcmFtIHByb3h5IFRoZSBjbGllbnQgY29uZmlndXJlZCBwcm94eS4KICogQHJldHVybnMgQSBwcm94eSB0aGUgdHJhbnNwb3J0IHNob3VsZCB1c2UuCiAqLwpmdW5jdGlvbiBhcHBseU5vUHJveHlPcHRpb24odHJhbnNwb3J0VXJsU2VnbWVudHMsIHByb3h5KSB7CiAgY29uc3QgeyBub19wcm94eSB9ID0gcHJvY2Vzcy5lbnY7CgogIGNvbnN0IHVybElzRXhlbXB0RnJvbVByb3h5ID0KICAgIG5vX3Byb3h5ICYmCiAgICBub19wcm94eQogICAgICAuc3BsaXQoJywnKQogICAgICAuc29tZSgKICAgICAgICBleGVtcHRpb24gPT4gdHJhbnNwb3J0VXJsU2VnbWVudHMuaG9zdC5lbmRzV2l0aChleGVtcHRpb24pIHx8IHRyYW5zcG9ydFVybFNlZ21lbnRzLmhvc3RuYW1lLmVuZHNXaXRoKGV4ZW1wdGlvbiksCiAgICAgICk7CgogIGlmICh1cmxJc0V4ZW1wdEZyb21Qcm94eSkgewogICAgcmV0dXJuIHVuZGVmaW5lZDsKICB9IGVsc2UgewogICAgcmV0dXJuIHByb3h5OwogIH0KfQoKLyoqCiAqIENyZWF0ZXMgYSBSZXF1ZXN0RXhlY3V0b3IgdG8gYmUgdXNlZCB3aXRoIGBjcmVhdGVUcmFuc3BvcnRgLgogKi8KZnVuY3Rpb24gY3JlYXRlUmVxdWVzdEV4ZWN1dG9yKAogIG9wdGlvbnMsCiAgaHR0cE1vZHVsZSwKICBhZ2VudCwKKSB7CiAgY29uc3QgeyBob3N0bmFtZSwgcGF0aG5hbWUsIHBvcnQsIHByb3RvY29sLCBzZWFyY2ggfSA9IG5ldyBVUkwob3B0aW9ucy51cmwpOwogIHJldHVybiBmdW5jdGlvbiBtYWtlUmVxdWVzdChyZXF1ZXN0KSB7CiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4gewogICAgICBsZXQgYm9keSA9IHN0cmVhbUZyb21Cb2R5KHJlcXVlc3QuYm9keSk7CgogICAgICBjb25zdCBoZWFkZXJzID0geyAuLi5vcHRpb25zLmhlYWRlcnMgfTsKCiAgICAgIGlmIChyZXF1ZXN0LmJvZHkubGVuZ3RoID4gR1pJUF9USFJFU0hPTEQpIHsKICAgICAgICBoZWFkZXJzWydjb250ZW50LWVuY29kaW5nJ10gPSAnZ3ppcCc7CiAgICAgICAgYm9keSA9IGJvZHkucGlwZShjcmVhdGVHemlwKCkpOwogICAgICB9CgogICAgICBjb25zdCByZXEgPSBodHRwTW9kdWxlLnJlcXVlc3QoCiAgICAgICAgewogICAgICAgICAgbWV0aG9kOiAnUE9TVCcsCiAgICAgICAgICBhZ2VudCwKICAgICAgICAgIGhlYWRlcnMsCiAgICAgICAgICBob3N0bmFtZSwKICAgICAgICAgIHBhdGg6IGAke3BhdGhuYW1lfSR7c2VhcmNofWAsCiAgICAgICAgICBwb3J0LAogICAgICAgICAgcHJvdG9jb2wsCiAgICAgICAgICBjYTogb3B0aW9ucy5jYUNlcnRzLAogICAgICAgIH0sCiAgICAgICAgcmVzID0+IHsKICAgICAgICAgIHJlcy5vbignZGF0YScsICgpID0+IHsKICAgICAgICAgICAgLy8gRHJhaW4gc29ja2V0CiAgICAgICAgICB9KTsKCiAgICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHsKICAgICAgICAgICAgLy8gRHJhaW4gc29ja2V0CiAgICAgICAgICB9KTsKCiAgICAgICAgICByZXMuc2V0RW5jb2RpbmcoJ3V0ZjgnKTsKCiAgICAgICAgICAvLyAiS2V5LXZhbHVlIHBhaXJzIG9mIGhlYWRlciBuYW1lcyBhbmQgdmFsdWVzLiBIZWFkZXIgbmFtZXMgYXJlIGxvd2VyLWNhc2VkLiIKICAgICAgICAgIC8vIGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvaHR0cC5odG1sI2h0dHBfbWVzc2FnZV9oZWFkZXJzCiAgICAgICAgICBjb25zdCByZXRyeUFmdGVySGVhZGVyID0gX251bGxpc2hDb2FsZXNjZShyZXMuaGVhZGVyc1sncmV0cnktYWZ0ZXInXSwgKCkgPT4gKCBudWxsKSk7CiAgICAgICAgICBjb25zdCByYXRlTGltaXRzSGVhZGVyID0gX251bGxpc2hDb2FsZXNjZShyZXMuaGVhZGVyc1sneC1zZW50cnktcmF0ZS1saW1pdHMnXSwgKCkgPT4gKCBudWxsKSk7CgogICAgICAgICAgcmVzb2x2ZSh7CiAgICAgICAgICAgIHN0YXR1c0NvZGU6IHJlcy5zdGF0dXNDb2RlLAogICAgICAgICAgICBoZWFkZXJzOiB7CiAgICAgICAgICAgICAgJ3JldHJ5LWFmdGVyJzogcmV0cnlBZnRlckhlYWRlciwKICAgICAgICAgICAgICAneC1zZW50cnktcmF0ZS1saW1pdHMnOiBBcnJheS5pc0FycmF5KHJhdGVMaW1pdHNIZWFkZXIpID8gcmF0ZUxpbWl0c0hlYWRlclswXSA6IHJhdGVMaW1pdHNIZWFkZXIsCiAgICAgICAgICAgIH0sCiAgICAgICAgICB9KTsKICAgICAgICB9LAogICAgICApOwoKICAgICAgcmVxLm9uKCdlcnJvcicsIHJlamVjdCk7CiAgICAgIGJvZHkucGlwZShyZXEpOwogICAgfSk7CiAgfTsKfQoKLyoqIG5vcm1hbGl6ZXMgV2luZG93cyBwYXRocyAqLwpmdW5jdGlvbiBub3JtYWxpemVXaW5kb3dzUGF0aChwYXRoKSB7CiAgcmV0dXJuIHBhdGgKICAgIC5yZXBsYWNlKC9eW0EtWl06LywgJycpIC8vIHJlbW92ZSBXaW5kb3dzLXN0eWxlIHByZWZpeAogICAgLnJlcGxhY2UoL1xcL2csICcvJyk7IC8vIHJlcGxhY2UgYWxsIGBcYCBpbnN0YW5jZXMgd2l0aCBgL2AKfQoKLyoqIENyZWF0ZXMgYSBmdW5jdGlvbiB0aGF0IGdldHMgdGhlIG1vZHVsZSBuYW1lIGZyb20gYSBmaWxlbmFtZSAqLwpmdW5jdGlvbiBjcmVhdGVHZXRNb2R1bGVGcm9tRmlsZW5hbWUoCiAgYmFzZVBhdGggPSBwcm9jZXNzLmFyZ3ZbMV0gPyBkaXJuYW1lKHByb2Nlc3MuYXJndlsxXSkgOiBwcm9jZXNzLmN3ZCgpLAogIGlzV2luZG93cyA9IHNlcCA9PT0gJ1xcJywKKSB7CiAgY29uc3Qgbm9ybWFsaXplZEJhc2UgPSBpc1dpbmRvd3MgPyBub3JtYWxpemVXaW5kb3dzUGF0aChiYXNlUGF0aCkgOiBiYXNlUGF0aDsKCiAgcmV0dXJuIChmaWxlbmFtZSkgPT4gewogICAgaWYgKCFmaWxlbmFtZSkgewogICAgICByZXR1cm47CiAgICB9CgogICAgY29uc3Qgbm9ybWFsaXplZEZpbGVuYW1lID0gaXNXaW5kb3dzID8gbm9ybWFsaXplV2luZG93c1BhdGgoZmlsZW5hbWUpIDogZmlsZW5hbWU7CgogICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHByZWZlci1jb25zdAogICAgbGV0IHsgZGlyLCBiYXNlOiBmaWxlLCBleHQgfSA9IHBvc2l4LnBhcnNlKG5vcm1hbGl6ZWRGaWxlbmFtZSk7CgogICAgaWYgKGV4dCA9PT0gJy5qcycgfHwgZXh0ID09PSAnLm1qcycgfHwgZXh0ID09PSAnLmNqcycpIHsKICAgICAgZmlsZSA9IGZpbGUuc2xpY2UoMCwgZXh0Lmxlbmd0aCAqIC0xKTsKICAgIH0KCiAgICBpZiAoIWRpcikgewogICAgICAvLyBObyBkaXJuYW1lIHdoYXRzb2V2ZXIKICAgICAgZGlyID0gJy4nOwogICAgfQoKICAgIGNvbnN0IG4gPSBkaXIubGFzdEluZGV4T2YoJy9ub2RlX21vZHVsZXMnKTsKICAgIGlmIChuID4gLTEpIHsKICAgICAgcmV0dXJuIGAke2Rpci5zbGljZShuICsgMTQpLnJlcGxhY2UoL1wvL2csICcuJyl9OiR7ZmlsZX1gOwogICAgfQoKICAgIC8vIExldCdzIHNlZSBpZiBpdCdzIGEgcGFydCBvZiB0aGUgbWFpbiBtb2R1bGUKICAgIC8vIFRvIGJlIGEgcGFydCBvZiBtYWluIG1vZHVsZSwgaXQgaGFzIHRvIHNoYXJlIHRoZSBzYW1lIGJhc2UKICAgIGlmIChkaXIuc3RhcnRzV2l0aChub3JtYWxpemVkQmFzZSkpIHsKICAgICAgbGV0IG1vZHVsZU5hbWUgPSBkaXIuc2xpY2Uobm9ybWFsaXplZEJhc2UubGVuZ3RoICsgMSkucmVwbGFjZSgvXC8vZywgJy4nKTsKCiAgICAgIGlmIChtb2R1bGVOYW1lKSB7CiAgICAgICAgbW9kdWxlTmFtZSArPSAnOic7CiAgICAgIH0KICAgICAgbW9kdWxlTmFtZSArPSBmaWxlOwoKICAgICAgcmV0dXJuIG1vZHVsZU5hbWU7CiAgICB9CgogICAgcmV0dXJuIGZpbGU7CiAgfTsKfQoKZnVuY3Rpb24gX29wdGlvbmFsQ2hhaW4ob3BzKSB7IGxldCBsYXN0QWNjZXNzTEhTID0gdW5kZWZpbmVkOyBsZXQgdmFsdWUgPSBvcHNbMF07IGxldCBpID0gMTsgd2hpbGUgKGkgPCBvcHMubGVuZ3RoKSB7IGNvbnN0IG9wID0gb3BzW2ldOyBjb25zdCBmbiA9IG9wc1tpICsgMV07IGkgKz0gMjsgaWYgKChvcCA9PT0gJ29wdGlvbmFsQWNjZXNzJyB8fCBvcCA9PT0gJ29wdGlvbmFsQ2FsbCcpICYmIHZhbHVlID09IG51bGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSBpZiAob3AgPT09ICdhY2Nlc3MnIHx8IG9wID09PSAnb3B0aW9uYWxBY2Nlc3MnKSB7IGxhc3RBY2Nlc3NMSFMgPSB2YWx1ZTsgdmFsdWUgPSBmbih2YWx1ZSk7IH0gZWxzZSBpZiAob3AgPT09ICdjYWxsJyB8fCBvcCA9PT0gJ29wdGlvbmFsQ2FsbCcpIHsgdmFsdWUgPSBmbigoLi4uYXJncykgPT4gdmFsdWUuY2FsbChsYXN0QWNjZXNzTEhTLCAuLi5hcmdzKSk7IGxhc3RBY2Nlc3NMSFMgPSB1bmRlZmluZWQ7IH0gfSByZXR1cm4gdmFsdWU7IH0KY29uc3Qgb3B0aW9ucyA9IHdvcmtlckRhdGE7CmxldCBzZXNzaW9uOwpsZXQgaGFzU2VudEFuckV2ZW50ID0gZmFsc2U7CgpmdW5jdGlvbiBsb2cobXNnKSB7CiAgaWYgKG9wdGlvbnMuZGVidWcpIHsKICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlCiAgICBjb25zb2xlLmxvZyhgW0FOUiBXb3JrZXJdICR7bXNnfWApOwogIH0KfQoKY29uc3QgdXJsID0gZ2V0RW52ZWxvcGVFbmRwb2ludFdpdGhVcmxFbmNvZGVkQXV0aChvcHRpb25zLmRzbik7CmNvbnN0IHRyYW5zcG9ydCA9IG1ha2VOb2RlVHJhbnNwb3J0KHsKICB1cmwsCiAgcmVjb3JkRHJvcHBlZEV2ZW50OiAoKSA9PiB7CiAgICAvLwogIH0sCn0pOwoKYXN5bmMgZnVuY3Rpb24gc2VuZEFibm9ybWFsU2Vzc2lvbigpIHsKICAvLyBvZiB3ZSBoYXZlIGFuIGV4aXN0aW5nIHNlc3Npb24gcGFzc2VkIGZyb20gdGhlIG1haW4gdGhyZWFkLCBzZW5kIGl0IGFzIGFibm9ybWFsCiAgaWYgKHNlc3Npb24pIHsKICAgIGxvZygnU2VuZGluZyBhYm5vcm1hbCBzZXNzaW9uJyk7CiAgICB1cGRhdGVTZXNzaW9uKHNlc3Npb24sIHsgc3RhdHVzOiAnYWJub3JtYWwnLCBhYm5vcm1hbF9tZWNoYW5pc206ICdhbnJfZm9yZWdyb3VuZCcgfSk7CgogICAgY29uc3QgZW52ZWxvcGUgPSBjcmVhdGVTZXNzaW9uRW52ZWxvcGUoc2Vzc2lvbiwgb3B0aW9ucy5kc24sIG9wdGlvbnMuc2RrTWV0YWRhdGEpOwogICAgLy8gTG9nIHRoZSBlbnZlbG9wZSBzbyB0byBhaWQgaW4gdGVzdGluZwogICAgbG9nKEpTT04uc3RyaW5naWZ5KGVudmVsb3BlKSk7CgogICAgYXdhaXQgdHJhbnNwb3J0LnNlbmQoZW52ZWxvcGUpOwoKICAgIHRyeSB7CiAgICAgIC8vIE5vdGlmeSB0aGUgbWFpbiBwcm9jZXNzIHRoYXQgdGhlIHNlc3Npb24gaGFzIGVuZGVkIHNvIHRoZSBzZXNzaW9uIGNhbiBiZSBjbGVhcmVkIGZyb20gdGhlIHNjb3BlCiAgICAgIF9vcHRpb25hbENoYWluKFtwYXJlbnRQb3J0LCAnb3B0aW9uYWxBY2Nlc3MnLCBfMiA9PiBfMi5wb3N0TWVzc2FnZSwgJ2NhbGwnLCBfMyA9PiBfMygnc2Vzc2lvbi1lbmRlZCcpXSk7CiAgICB9IGNhdGNoIChfKSB7CiAgICAgIC8vIGlnbm9yZQogICAgfQogIH0KfQoKbG9nKCdTdGFydGVkJyk7CgpmdW5jdGlvbiBwcmVwYXJlU3RhY2tGcmFtZXMoc3RhY2tGcmFtZXMpIHsKICBpZiAoIXN0YWNrRnJhbWVzKSB7CiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgLy8gU3RyaXAgU2VudHJ5IGZyYW1lcyBhbmQgcmV2ZXJzZSB0aGUgc3RhY2sgZnJhbWVzIHNvIHRoZXkgYXJlIGluIHRoZSBjb3JyZWN0IG9yZGVyCiAgY29uc3Qgc3RyaXBwZWRGcmFtZXMgPSBzdHJpcFNlbnRyeUZyYW1lc0FuZFJldmVyc2Uoc3RhY2tGcmFtZXMpOwoKICAvLyBJZiB3ZSBoYXZlIGFuIGFwcCByb290IHBhdGgsIHJld3JpdGUgdGhlIGZpbGVuYW1lcyB0byBiZSByZWxhdGl2ZSB0byB0aGUgYXBwIHJvb3QKICBpZiAob3B0aW9ucy5hcHBSb290UGF0aCkgewogICAgZm9yIChjb25zdCBmcmFtZSBvZiBzdHJpcHBlZEZyYW1lcykgewogICAgICBpZiAoIWZyYW1lLmZpbGVuYW1lKSB7CiAgICAgICAgY29udGludWU7CiAgICAgIH0KCiAgICAgIGZyYW1lLmZpbGVuYW1lID0gbm9ybWFsaXplVXJsVG9CYXNlKGZyYW1lLmZpbGVuYW1lLCBvcHRpb25zLmFwcFJvb3RQYXRoKTsKICAgIH0KICB9CgogIHJldHVybiBzdHJpcHBlZEZyYW1lczsKfQoKYXN5bmMgZnVuY3Rpb24gc2VuZEFuckV2ZW50KGZyYW1lcywgdHJhY2VDb250ZXh0KSB7CiAgaWYgKGhhc1NlbnRBbnJFdmVudCkgewogICAgcmV0dXJuOwogIH0KCiAgaGFzU2VudEFuckV2ZW50ID0gdHJ1ZTsKCiAgYXdhaXQgc2VuZEFibm9ybWFsU2Vzc2lvbigpOwoKICBsb2coJ1NlbmRpbmcgZXZlbnQnKTsKCiAgY29uc3QgZXZlbnQgPSB7CiAgICBldmVudF9pZDogdXVpZDQoKSwKICAgIGNvbnRleHRzOiB7IC4uLm9wdGlvbnMuY29udGV4dHMsIHRyYWNlOiB0cmFjZUNvbnRleHQgfSwKICAgIHJlbGVhc2U6IG9wdGlvbnMucmVsZWFzZSwKICAgIGVudmlyb25tZW50OiBvcHRpb25zLmVudmlyb25tZW50LAogICAgZGlzdDogb3B0aW9ucy5kaXN0LAogICAgcGxhdGZvcm06ICdub2RlJywKICAgIGxldmVsOiAnZXJyb3InLAogICAgZXhjZXB0aW9uOiB7CiAgICAgIHZhbHVlczogWwogICAgICAgIHsKICAgICAgICAgIHR5cGU6ICdBcHBsaWNhdGlvbk5vdFJlc3BvbmRpbmcnLAogICAgICAgICAgdmFsdWU6IGBBcHBsaWNhdGlvbiBOb3QgUmVzcG9uZGluZyBmb3IgYXQgbGVhc3QgJHtvcHRpb25zLmFuclRocmVzaG9sZH0gbXNgLAogICAgICAgICAgc3RhY2t0cmFjZTogeyBmcmFtZXM6IHByZXBhcmVTdGFja0ZyYW1lcyhmcmFtZXMpIH0sCiAgICAgICAgICAvLyBUaGlzIGVuc3VyZXMgdGhlIFVJIGRvZXNuJ3Qgc2F5ICdDcmFzaGVkIGluJyBmb3IgdGhlIHN0YWNrIHRyYWNlCiAgICAgICAgICBtZWNoYW5pc206IHsgdHlwZTogJ0FOUicgfSwKICAgICAgICB9LAogICAgICBdLAogICAgfSwKICAgIHRhZ3M6IG9wdGlvbnMuc3RhdGljVGFncywKICB9OwoKICBjb25zdCBlbnZlbG9wZSA9IGNyZWF0ZUV2ZW50RW52ZWxvcGUoZXZlbnQsIG9wdGlvbnMuZHNuLCBvcHRpb25zLnNka01ldGFkYXRhKTsKICAvLyBMb2cgdGhlIGVudmVsb3BlIHNvIHRvIGFpZCBpbiB0ZXN0aW5nCiAgbG9nKEpTT04uc3RyaW5naWZ5KGVudmVsb3BlKSk7CgogIGF3YWl0IHRyYW5zcG9ydC5zZW5kKGVudmVsb3BlKTsKICBhd2FpdCB0cmFuc3BvcnQuZmx1c2goMjAwMCk7CgogIC8vIERlbGF5IGZvciA1IHNlY29uZHMgc28gdGhhdCBzdGRpbyBjYW4gZmx1c2ggaW4gdGhlIG1haW4gZXZlbnQgbG9vcCBldmVyIHJlc3RhcnRzLgogIC8vIFRoaXMgaXMgbWFpbmx5IGZvciB0aGUgYmVuZWZpdCBvZiBsb2dnaW5nL2RlYnVnZ2luZyBpc3N1ZXMuCiAgc2V0VGltZW91dCgoKSA9PiB7CiAgICBwcm9jZXNzLmV4aXQoMCk7CiAgfSwgNTAwMCk7Cn0KCmxldCBkZWJ1Z2dlclBhdXNlOwoKaWYgKG9wdGlvbnMuY2FwdHVyZVN0YWNrVHJhY2UpIHsKICBsb2coJ0Nvbm5lY3RpbmcgdG8gZGVidWdnZXInKTsKCiAgY29uc3Qgc2Vzc2lvbiA9IG5ldyBTZXNzaW9uKCk7CiAgc2Vzc2lvbi5jb25uZWN0VG9NYWluVGhyZWFkKCk7CgogIGxvZygnQ29ubmVjdGVkIHRvIGRlYnVnZ2VyJyk7CgogIC8vIENvbGxlY3Qgc2NyaXB0SWQgLT4gdXJsIG1hcCBzbyB3ZSBjYW4gbG9vayB1cCB0aGUgZmlsZW5hbWVzIGxhdGVyCiAgY29uc3Qgc2NyaXB0cyA9IG5ldyBNYXAoKTsKCiAgc2Vzc2lvbi5vbignRGVidWdnZXIuc2NyaXB0UGFyc2VkJywgZXZlbnQgPT4gewogICAgc2NyaXB0cy5zZXQoZXZlbnQucGFyYW1zLnNjcmlwdElkLCBldmVudC5wYXJhbXMudXJsKTsKICB9KTsKCiAgc2Vzc2lvbi5vbignRGVidWdnZXIucGF1c2VkJywgZXZlbnQgPT4gewogICAgaWYgKGV2ZW50LnBhcmFtcy5yZWFzb24gIT09ICdvdGhlcicpIHsKICAgICAgcmV0dXJuOwogICAgfQoKICAgIHRyeSB7CiAgICAgIGxvZygnRGVidWdnZXIgcGF1c2VkJyk7CgogICAgICAvLyBjb3B5IHRoZSBmcmFtZXMKICAgICAgY29uc3QgY2FsbEZyYW1lcyA9IFsuLi5ldmVudC5wYXJhbXMuY2FsbEZyYW1lc107CgogICAgICBjb25zdCBnZXRNb2R1bGVOYW1lID0gb3B0aW9ucy5hcHBSb290UGF0aCA/IGNyZWF0ZUdldE1vZHVsZUZyb21GaWxlbmFtZShvcHRpb25zLmFwcFJvb3RQYXRoKSA6ICgpID0+IHVuZGVmaW5lZDsKICAgICAgY29uc3Qgc3RhY2tGcmFtZXMgPSBjYWxsRnJhbWVzLm1hcChmcmFtZSA9PgogICAgICAgIGNhbGxGcmFtZVRvU3RhY2tGcmFtZShmcmFtZSwgc2NyaXB0cy5nZXQoZnJhbWUubG9jYXRpb24uc2NyaXB0SWQpLCBnZXRNb2R1bGVOYW1lKSwKICAgICAgKTsKCiAgICAgIC8vIEV2YWx1YXRlIGEgc2NyaXB0IGluIHRoZSBjdXJyZW50bHkgcGF1c2VkIGNvbnRleHQKICAgICAgc2Vzc2lvbi5wb3N0KAogICAgICAgICdSdW50aW1lLmV2YWx1YXRlJywKICAgICAgICB7CiAgICAgICAgICAvLyBHcmFiIHRoZSB0cmFjZSBjb250ZXh0IGZyb20gdGhlIGN1cnJlbnQgc2NvcGUKICAgICAgICAgIGV4cHJlc3Npb246CiAgICAgICAgICAgICdjb25zdCBjdHggPSBfX1NFTlRSWV9fLmFjcz8uZ2V0Q3VycmVudFNjb3BlKCkuZ2V0UHJvcGFnYXRpb25Db250ZXh0KCkgfHwge307IGN0eC50cmFjZUlkICsgIi0iICsgY3R4LnNwYW5JZCArICItIiArIGN0eC5wYXJlbnRTcGFuSWQnLAogICAgICAgICAgLy8gRG9uJ3QgcmUtdHJpZ2dlciB0aGUgZGVidWdnZXIgaWYgdGhpcyBjYXVzZXMgYW4gZXJyb3IKICAgICAgICAgIHNpbGVudDogdHJ1ZSwKICAgICAgICB9LAogICAgICAgIChfLCBwYXJhbSkgPT4gewogICAgICAgICAgY29uc3QgdHJhY2VJZCA9IHBhcmFtICYmIHBhcmFtLnJlc3VsdCA/IChwYXJhbS5yZXN1bHQudmFsdWUgKSA6ICctLSc7CiAgICAgICAgICBjb25zdCBbdHJhY2VfaWQsIHNwYW5faWQsIHBhcmVudF9zcGFuX2lkXSA9IHRyYWNlSWQuc3BsaXQoJy0nKSA7CgogICAgICAgICAgc2Vzc2lvbi5wb3N0KCdEZWJ1Z2dlci5yZXN1bWUnKTsKICAgICAgICAgIHNlc3Npb24ucG9zdCgnRGVidWdnZXIuZGlzYWJsZScpOwoKICAgICAgICAgIGNvbnN0IGNvbnRleHQgPSBfb3B0aW9uYWxDaGFpbihbdHJhY2VfaWQsICdvcHRpb25hbEFjY2VzcycsIF80ID0+IF80Lmxlbmd0aF0pICYmIF9vcHRpb25hbENoYWluKFtzcGFuX2lkLCAnb3B0aW9uYWxBY2Nlc3MnLCBfNSA9PiBfNS5sZW5ndGhdKSA/IHsgdHJhY2VfaWQsIHNwYW5faWQsIHBhcmVudF9zcGFuX2lkIH0gOiB1bmRlZmluZWQ7CiAgICAgICAgICBzZW5kQW5yRXZlbnQoc3RhY2tGcmFtZXMsIGNvbnRleHQpLnRoZW4obnVsbCwgKCkgPT4gewogICAgICAgICAgICBsb2coJ1NlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4nKTsKICAgICAgICAgIH0pOwogICAgICAgIH0sCiAgICAgICk7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHNlc3Npb24ucG9zdCgnRGVidWdnZXIucmVzdW1lJyk7CiAgICAgIHNlc3Npb24ucG9zdCgnRGVidWdnZXIuZGlzYWJsZScpOwogICAgICB0aHJvdyBlOwogICAgfQogIH0pOwoKICBkZWJ1Z2dlclBhdXNlID0gKCkgPT4gewogICAgdHJ5IHsKICAgICAgc2Vzc2lvbi5wb3N0KCdEZWJ1Z2dlci5lbmFibGUnLCAoKSA9PiB7CiAgICAgICAgc2Vzc2lvbi5wb3N0KCdEZWJ1Z2dlci5wYXVzZScpOwogICAgICB9KTsKICAgIH0gY2F0Y2ggKF8pIHsKICAgICAgLy8KICAgIH0KICB9Owp9CgpmdW5jdGlvbiBjcmVhdGVIclRpbWVyKCkgewogIC8vIFRPRE8gKHY4KTogV2UgY2FuIHVzZSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKSBhZnRlciB3ZSBkcm9wIG5vZGUgdjgKICBsZXQgbGFzdFBvbGwgPSBwcm9jZXNzLmhydGltZSgpOwoKICByZXR1cm4gewogICAgZ2V0VGltZU1zOiAoKSA9PiB7CiAgICAgIGNvbnN0IFtzZWNvbmRzLCBuYW5vU2Vjb25kc10gPSBwcm9jZXNzLmhydGltZShsYXN0UG9sbCk7CiAgICAgIHJldHVybiBNYXRoLmZsb29yKHNlY29uZHMgKiAxZTMgKyBuYW5vU2Vjb25kcyAvIDFlNik7CiAgICB9LAogICAgcmVzZXQ6ICgpID0+IHsKICAgICAgbGFzdFBvbGwgPSBwcm9jZXNzLmhydGltZSgpOwogICAgfSwKICB9Owp9CgpmdW5jdGlvbiB3YXRjaGRvZ1RpbWVvdXQoKSB7CiAgbG9nKCdXYXRjaGRvZyB0aW1lb3V0Jyk7CgogIGlmIChkZWJ1Z2dlclBhdXNlKSB7CiAgICBsb2coJ1BhdXNpbmcgZGVidWdnZXIgdG8gY2FwdHVyZSBzdGFjayB0cmFjZScpOwogICAgZGVidWdnZXJQYXVzZSgpOwogIH0gZWxzZSB7CiAgICBsb2coJ0NhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UnKTsKICAgIHNlbmRBbnJFdmVudCgpLnRoZW4obnVsbCwgKCkgPT4gewogICAgICBsb2coJ1NlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZCBvbiB3YXRjaGRvZyB0aW1lb3V0LicpOwogICAgfSk7CiAgfQp9Cgpjb25zdCB7IHBvbGwgfSA9IHdhdGNoZG9nVGltZXIoY3JlYXRlSHJUaW1lciwgb3B0aW9ucy5wb2xsSW50ZXJ2YWwsIG9wdGlvbnMuYW5yVGhyZXNob2xkLCB3YXRjaGRvZ1RpbWVvdXQpOwoKX29wdGlvbmFsQ2hhaW4oW3BhcmVudFBvcnQsICdvcHRpb25hbEFjY2VzcycsIF82ID0+IF82Lm9uLCAnY2FsbCcsIF83ID0+IF83KCdtZXNzYWdlJywgKG1zZykgPT4gewogIGlmIChtc2cuc2Vzc2lvbikgewogICAgc2Vzc2lvbiA9IG1ha2VTZXNzaW9uKG1zZy5zZXNzaW9uKTsKICB9CgogIHBvbGwoKTsKfSldKTs=';
|
|
1643
|
+
|
|
1644
|
+
function _optionalChain$4(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1645
|
+
const DEFAULT_INTERVAL = 50;
|
|
1646
|
+
const DEFAULT_HANG_THRESHOLD = 5000;
|
|
1647
|
+
|
|
1648
|
+
function log(message, ...args) {
|
|
1649
|
+
logger.log(`[ANR] ${message}`, ...args);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/**
|
|
1653
|
+
* Gets contexts by calling all event processors. This relies on being called after all integrations are setup
|
|
1654
|
+
*/
|
|
1655
|
+
async function getContexts(client) {
|
|
1656
|
+
let event = { message: 'ANR' };
|
|
1657
|
+
const eventHint = {};
|
|
1658
|
+
|
|
1659
|
+
for (const processor of client.getEventProcessors()) {
|
|
1660
|
+
if (event === null) break;
|
|
1661
|
+
event = await processor(event, eventHint);
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
return _optionalChain$4([event, 'optionalAccess', _2 => _2.contexts]) || {};
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
const INTEGRATION_NAME$1 = 'Anr';
|
|
1668
|
+
|
|
1669
|
+
const _anrIntegration = ((options = {}) => {
|
|
1670
|
+
return {
|
|
1671
|
+
name: INTEGRATION_NAME$1,
|
|
1672
|
+
setup(client) {
|
|
1673
|
+
if (NODE_MAJOR < 16 || (NODE_MAJOR === 16 && (NODE_VERSION.minor || 0) < 17)) {
|
|
1674
|
+
throw new Error('ANR detection requires Node 16.17.0 or later');
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// setImmediate is used to ensure that all other integrations have been setup
|
|
1678
|
+
setImmediate(() => _startWorker(client, options));
|
|
1679
|
+
},
|
|
1680
|
+
};
|
|
1681
|
+
}) ;
|
|
1682
|
+
|
|
1683
|
+
const anrIntegration = defineIntegration(_anrIntegration);
|
|
1684
|
+
|
|
1685
|
+
/**
|
|
1686
|
+
* Starts the ANR worker thread
|
|
1687
|
+
*/
|
|
1688
|
+
async function _startWorker(client, _options) {
|
|
1689
|
+
const contexts = await getContexts(client);
|
|
1690
|
+
const dsn = client.getDsn();
|
|
1691
|
+
|
|
1692
|
+
if (!dsn) {
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// These will not be accurate if sent later from the worker thread
|
|
1697
|
+
_optionalChainDelete([contexts, 'access', _3 => _3.app, 'optionalAccess', _4 => delete _4.app_memory]);
|
|
1698
|
+
_optionalChainDelete([contexts, 'access', _5 => _5.device, 'optionalAccess', _6 => delete _6.free_memory]);
|
|
1699
|
+
|
|
1700
|
+
const initOptions = client.getOptions();
|
|
1701
|
+
|
|
1702
|
+
const sdkMetadata = client.getSdkMetadata() || {};
|
|
1703
|
+
if (sdkMetadata.sdk) {
|
|
1704
|
+
sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
const options = {
|
|
1708
|
+
debug: logger.isEnabled(),
|
|
1709
|
+
dsn,
|
|
1710
|
+
environment: initOptions.environment || 'production',
|
|
1711
|
+
release: initOptions.release,
|
|
1712
|
+
dist: initOptions.dist,
|
|
1713
|
+
sdkMetadata,
|
|
1714
|
+
appRootPath: _options.appRootPath,
|
|
1715
|
+
pollInterval: _options.pollInterval || DEFAULT_INTERVAL,
|
|
1716
|
+
anrThreshold: _options.anrThreshold || DEFAULT_HANG_THRESHOLD,
|
|
1717
|
+
captureStackTrace: !!_options.captureStackTrace,
|
|
1718
|
+
staticTags: _options.staticTags || {},
|
|
1719
|
+
contexts,
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
if (options.captureStackTrace) {
|
|
1723
|
+
if (!inspector.url()) {
|
|
1724
|
+
inspector.open(0);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
const worker = new Worker(new URL(`data:application/javascript;base64,${base64WorkerScript}`), {
|
|
1729
|
+
workerData: options,
|
|
1730
|
+
});
|
|
1731
|
+
|
|
1732
|
+
process.on('exit', () => {
|
|
1733
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1734
|
+
worker.terminate();
|
|
1735
|
+
});
|
|
1736
|
+
|
|
1737
|
+
const timer = setInterval(() => {
|
|
1738
|
+
try {
|
|
1739
|
+
const currentSession = getCurrentScope().getSession();
|
|
1740
|
+
// We need to copy the session object and remove the toJSON method so it can be sent to the worker
|
|
1741
|
+
// serialized without making it a SerializedSession
|
|
1742
|
+
const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;
|
|
1743
|
+
// message the worker to tell it the main event loop is still running
|
|
1744
|
+
worker.postMessage({ session });
|
|
1745
|
+
} catch (_) {
|
|
1746
|
+
//
|
|
1747
|
+
}
|
|
1748
|
+
}, options.pollInterval);
|
|
1749
|
+
// Timer should not block exit
|
|
1750
|
+
timer.unref();
|
|
1751
|
+
|
|
1752
|
+
worker.on('message', (msg) => {
|
|
1753
|
+
if (msg === 'session-ended') {
|
|
1754
|
+
log('ANR event sent from ANR worker. Clearing session in this thread.');
|
|
1755
|
+
getCurrentScope().setSession(undefined);
|
|
1756
|
+
}
|
|
1757
|
+
});
|
|
1758
|
+
|
|
1759
|
+
worker.once('error', (err) => {
|
|
1760
|
+
clearInterval(timer);
|
|
1761
|
+
log('ANR worker error', err);
|
|
1762
|
+
});
|
|
1763
|
+
|
|
1764
|
+
worker.once('exit', (code) => {
|
|
1765
|
+
clearInterval(timer);
|
|
1766
|
+
log('ANR worker exit', code);
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
// Ensure this thread can't block app exit
|
|
1770
|
+
worker.unref();
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
function _optionalChain$3(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1774
|
+
|
|
1775
|
+
const _expressIntegration = (() => {
|
|
1776
|
+
return {
|
|
1777
|
+
name: 'Express',
|
|
1778
|
+
setupOnce() {
|
|
1779
|
+
registerInstrumentations({
|
|
1780
|
+
instrumentations: [
|
|
1781
|
+
new ExpressInstrumentation({
|
|
1782
|
+
requestHook(span) {
|
|
1783
|
+
addOriginToSpan(span, 'auto.http.otel.express');
|
|
1784
|
+
},
|
|
1785
|
+
}),
|
|
1786
|
+
],
|
|
1787
|
+
});
|
|
1788
|
+
},
|
|
1789
|
+
};
|
|
1790
|
+
}) ;
|
|
1791
|
+
|
|
1792
|
+
/**
|
|
1793
|
+
* Express integration
|
|
1794
|
+
*
|
|
1795
|
+
* Capture tracing data for express.
|
|
1796
|
+
* In order to capture exceptions, you have to call `setupExpressErrorHandler(app)` before any other middleware and after all controllers.
|
|
1797
|
+
*/
|
|
1798
|
+
const expressIntegration = defineIntegration(_expressIntegration);
|
|
1799
|
+
|
|
1800
|
+
/**
|
|
1801
|
+
* An Express-compatible error handler.
|
|
1802
|
+
*/
|
|
1803
|
+
function expressErrorHandler(options
|
|
1804
|
+
|
|
1805
|
+
) {
|
|
1806
|
+
return function sentryErrorMiddleware(
|
|
1807
|
+
error,
|
|
1808
|
+
_req,
|
|
1809
|
+
res,
|
|
1810
|
+
next,
|
|
1811
|
+
) {
|
|
1812
|
+
const shouldHandleError = _optionalChain$3([options, 'optionalAccess', _ => _.shouldHandleError]) || defaultShouldHandleError;
|
|
1813
|
+
|
|
1814
|
+
if (shouldHandleError(error)) {
|
|
1815
|
+
const client = getClient$2();
|
|
1816
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
1817
|
+
// Check if the `SessionFlusher` is instantiated on the client to go into this branch that marks the
|
|
1818
|
+
// `requestSession.status` as `Crashed`, and this check is necessary because the `SessionFlusher` is only
|
|
1819
|
+
// instantiated when the the`requestHandler` middleware is initialised, which indicates that we should be
|
|
1820
|
+
// running in SessionAggregates mode
|
|
1821
|
+
const isSessionAggregatesMode = client['_sessionFlusher'] !== undefined;
|
|
1822
|
+
if (isSessionAggregatesMode) {
|
|
1823
|
+
const requestSession = getIsolationScope().getRequestSession();
|
|
1824
|
+
// If an error bubbles to the `errorHandler`, then this is an unhandled error, and should be reported as a
|
|
1825
|
+
// Crashed session. The `_requestSession.status` is checked to ensure that this error is happening within
|
|
1826
|
+
// the bounds of a request, and if so the status is updated
|
|
1827
|
+
if (requestSession && requestSession.status !== undefined) {
|
|
1828
|
+
requestSession.status = 'crashed';
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
const eventId = captureException(error, { mechanism: { type: 'middleware', handled: false } });
|
|
1834
|
+
(res ).sentry = eventId;
|
|
1835
|
+
next(error);
|
|
1836
|
+
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
next(error);
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
/**
|
|
1845
|
+
* Setup an error handler for Express.
|
|
1846
|
+
* The error handler must be before any other middleware and after all controllers.
|
|
1847
|
+
*/
|
|
1848
|
+
function setupExpressErrorHandler(app) {
|
|
1849
|
+
app.use(expressErrorHandler());
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
function getStatusCodeFromResponse(error) {
|
|
1853
|
+
const statusCode = error.status || error.statusCode || error.status_code || (error.output && error.output.statusCode);
|
|
1854
|
+
return statusCode ? parseInt(statusCode , 10) : 500;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
/** Returns true if response code is internal server error */
|
|
1858
|
+
function defaultShouldHandleError(error) {
|
|
1859
|
+
const status = getStatusCodeFromResponse(error);
|
|
1860
|
+
return status >= 500;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
const _fastifyIntegration = (() => {
|
|
1864
|
+
return {
|
|
1865
|
+
name: 'Fastify',
|
|
1866
|
+
setupOnce() {
|
|
1867
|
+
registerInstrumentations({
|
|
1868
|
+
instrumentations: [
|
|
1869
|
+
new FastifyInstrumentation({
|
|
1870
|
+
requestHook(span) {
|
|
1871
|
+
addOriginToSpan(span, 'auto.http.otel.fastify');
|
|
1872
|
+
},
|
|
1873
|
+
}),
|
|
1874
|
+
],
|
|
1875
|
+
});
|
|
1876
|
+
},
|
|
1877
|
+
};
|
|
1878
|
+
}) ;
|
|
1879
|
+
|
|
1880
|
+
/**
|
|
1881
|
+
* Express integration
|
|
1882
|
+
*
|
|
1883
|
+
* Capture tracing data for fastify.
|
|
1884
|
+
*/
|
|
1885
|
+
const fastifyIntegration = defineIntegration(_fastifyIntegration);
|
|
1886
|
+
|
|
1887
|
+
// We inline the types we care about here
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* Setup an error handler for Fastify.
|
|
1891
|
+
*/
|
|
1892
|
+
function setupFastifyErrorHandler(fastify) {
|
|
1893
|
+
const plugin = Object.assign(
|
|
1894
|
+
function (fastify, options, done) {
|
|
1895
|
+
fastify.addHook('onError', async (_request, _reply, error) => {
|
|
1896
|
+
captureException(error);
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1899
|
+
done();
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
[Symbol.for('skip-override')]: true,
|
|
1903
|
+
[Symbol.for('fastify.display-name')]: 'sentry-fastify-error-handler',
|
|
1904
|
+
},
|
|
1905
|
+
);
|
|
1906
|
+
|
|
1907
|
+
fastify.register(plugin);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
const _graphqlIntegration = (() => {
|
|
1911
|
+
return {
|
|
1912
|
+
name: 'Graphql',
|
|
1913
|
+
setupOnce() {
|
|
1914
|
+
registerInstrumentations({
|
|
1915
|
+
instrumentations: [
|
|
1916
|
+
new GraphQLInstrumentation({
|
|
1917
|
+
ignoreTrivialResolveSpans: true,
|
|
1918
|
+
responseHook(span) {
|
|
1919
|
+
addOriginToSpan(span, 'auto.graphql.otel.graphql');
|
|
1920
|
+
},
|
|
1921
|
+
}),
|
|
1922
|
+
],
|
|
1923
|
+
});
|
|
1924
|
+
},
|
|
1925
|
+
};
|
|
1926
|
+
}) ;
|
|
1927
|
+
|
|
1928
|
+
/**
|
|
1929
|
+
* GraphQL integration
|
|
1930
|
+
*
|
|
1931
|
+
* Capture tracing data for GraphQL.
|
|
1932
|
+
*/
|
|
1933
|
+
const graphqlIntegration = defineIntegration(_graphqlIntegration);
|
|
1934
|
+
|
|
1935
|
+
const _mongoIntegration = (() => {
|
|
1936
|
+
return {
|
|
1937
|
+
name: 'Mongo',
|
|
1938
|
+
setupOnce() {
|
|
1939
|
+
registerInstrumentations({
|
|
1940
|
+
instrumentations: [
|
|
1941
|
+
new MongoDBInstrumentation({
|
|
1942
|
+
responseHook(span) {
|
|
1943
|
+
addOriginToSpan(span, 'auto.db.otel.mongo');
|
|
1944
|
+
},
|
|
1945
|
+
}),
|
|
1946
|
+
],
|
|
1947
|
+
});
|
|
1948
|
+
},
|
|
1949
|
+
};
|
|
1950
|
+
}) ;
|
|
1951
|
+
|
|
1952
|
+
/**
|
|
1953
|
+
* MongoDB integration
|
|
1954
|
+
*
|
|
1955
|
+
* Capture tracing data for MongoDB.
|
|
1956
|
+
*/
|
|
1957
|
+
const mongoIntegration = defineIntegration(_mongoIntegration);
|
|
1958
|
+
|
|
1959
|
+
const _mongooseIntegration = (() => {
|
|
1960
|
+
return {
|
|
1961
|
+
name: 'Mongoose',
|
|
1962
|
+
setupOnce() {
|
|
1963
|
+
registerInstrumentations({
|
|
1964
|
+
instrumentations: [
|
|
1965
|
+
new MongooseInstrumentation({
|
|
1966
|
+
responseHook(span) {
|
|
1967
|
+
addOriginToSpan(span, 'auto.db.otel.mongoose');
|
|
1968
|
+
},
|
|
1969
|
+
}),
|
|
1970
|
+
],
|
|
1971
|
+
});
|
|
1972
|
+
},
|
|
1973
|
+
};
|
|
1974
|
+
}) ;
|
|
1975
|
+
|
|
1976
|
+
/**
|
|
1977
|
+
* Mongoose integration
|
|
1978
|
+
*
|
|
1979
|
+
* Capture tracing data for Mongoose.
|
|
1980
|
+
*/
|
|
1981
|
+
const mongooseIntegration = defineIntegration(_mongooseIntegration);
|
|
1982
|
+
|
|
1983
|
+
const _mysqlIntegration = (() => {
|
|
1984
|
+
return {
|
|
1985
|
+
name: 'Mysql',
|
|
1986
|
+
setupOnce() {
|
|
1987
|
+
registerInstrumentations({
|
|
1988
|
+
instrumentations: [new MySQLInstrumentation({})],
|
|
1989
|
+
});
|
|
1990
|
+
},
|
|
1991
|
+
};
|
|
1992
|
+
}) ;
|
|
1993
|
+
|
|
1994
|
+
/**
|
|
1995
|
+
* MySQL integration
|
|
1996
|
+
*
|
|
1997
|
+
* Capture tracing data for mysql.
|
|
1998
|
+
*/
|
|
1999
|
+
const mysqlIntegration = defineIntegration(_mysqlIntegration);
|
|
2000
|
+
|
|
2001
|
+
const _mysql2Integration = (() => {
|
|
2002
|
+
return {
|
|
2003
|
+
name: 'Mysql2',
|
|
2004
|
+
setupOnce() {
|
|
2005
|
+
registerInstrumentations({
|
|
2006
|
+
instrumentations: [
|
|
2007
|
+
new MySQL2Instrumentation({
|
|
2008
|
+
responseHook(span) {
|
|
2009
|
+
addOriginToSpan(span, 'auto.db.otel.mysql2');
|
|
2010
|
+
},
|
|
2011
|
+
}),
|
|
2012
|
+
],
|
|
2013
|
+
});
|
|
2014
|
+
},
|
|
2015
|
+
};
|
|
2016
|
+
}) ;
|
|
2017
|
+
|
|
2018
|
+
/**
|
|
2019
|
+
* MySQL2 integration
|
|
2020
|
+
*
|
|
2021
|
+
* Capture tracing data for mysql2
|
|
2022
|
+
*/
|
|
2023
|
+
const mysql2Integration = defineIntegration(_mysql2Integration);
|
|
2024
|
+
|
|
2025
|
+
const _nestIntegration = (() => {
|
|
2026
|
+
return {
|
|
2027
|
+
name: 'Nest',
|
|
2028
|
+
setupOnce() {
|
|
2029
|
+
registerInstrumentations({
|
|
2030
|
+
instrumentations: [new NestInstrumentation({})],
|
|
2031
|
+
});
|
|
2032
|
+
},
|
|
2033
|
+
};
|
|
2034
|
+
}) ;
|
|
2035
|
+
|
|
2036
|
+
/**
|
|
2037
|
+
* Nest framework integration
|
|
2038
|
+
*
|
|
2039
|
+
* Capture tracing data for nest.
|
|
2040
|
+
*/
|
|
2041
|
+
const nestIntegration = defineIntegration(_nestIntegration);
|
|
2042
|
+
|
|
2043
|
+
const _postgresIntegration = (() => {
|
|
2044
|
+
return {
|
|
2045
|
+
name: 'Postgres',
|
|
2046
|
+
setupOnce() {
|
|
2047
|
+
registerInstrumentations({
|
|
2048
|
+
instrumentations: [
|
|
2049
|
+
new PgInstrumentation({
|
|
2050
|
+
requireParentSpan: true,
|
|
2051
|
+
requestHook(span) {
|
|
2052
|
+
addOriginToSpan(span, 'auto.db.otel.postgres');
|
|
2053
|
+
},
|
|
2054
|
+
}),
|
|
2055
|
+
],
|
|
2056
|
+
});
|
|
2057
|
+
},
|
|
2058
|
+
};
|
|
2059
|
+
}) ;
|
|
2060
|
+
|
|
2061
|
+
/**
|
|
2062
|
+
* Postgres integration
|
|
2063
|
+
*
|
|
2064
|
+
* Capture tracing data for pg.
|
|
2065
|
+
*/
|
|
2066
|
+
const postgresIntegration = defineIntegration(_postgresIntegration);
|
|
2067
|
+
|
|
2068
|
+
const _prismaIntegration = (() => {
|
|
2069
|
+
return {
|
|
2070
|
+
name: 'Prisma',
|
|
2071
|
+
setupOnce() {
|
|
2072
|
+
registerInstrumentations({
|
|
2073
|
+
instrumentations: [
|
|
2074
|
+
// does not have a hook to adjust spans & add origin
|
|
2075
|
+
new prismaInstrumentation.PrismaInstrumentation({}),
|
|
2076
|
+
],
|
|
2077
|
+
});
|
|
2078
|
+
},
|
|
2079
|
+
};
|
|
2080
|
+
}) ;
|
|
2081
|
+
|
|
2082
|
+
/**
|
|
2083
|
+
* Prisma integration
|
|
2084
|
+
*
|
|
2085
|
+
* Capture tracing data for prisma.
|
|
2086
|
+
* Note: This requieres to set:
|
|
2087
|
+
* previewFeatures = ["tracing"]
|
|
2088
|
+
* For the prisma client.
|
|
2089
|
+
* See https://www.prisma.io/docs/concepts/components/prisma-client/opentelemetry-tracing for more details.
|
|
2090
|
+
*/
|
|
2091
|
+
const prismaIntegration = defineIntegration(_prismaIntegration);
|
|
2092
|
+
|
|
2093
|
+
const _hapiIntegration = (() => {
|
|
2094
|
+
return {
|
|
2095
|
+
name: 'Hapi',
|
|
2096
|
+
setupOnce() {
|
|
2097
|
+
registerInstrumentations({
|
|
2098
|
+
instrumentations: [new HapiInstrumentation()],
|
|
2099
|
+
});
|
|
2100
|
+
},
|
|
2101
|
+
};
|
|
2102
|
+
}) ;
|
|
2103
|
+
|
|
2104
|
+
/**
|
|
2105
|
+
* Hapi integration
|
|
2106
|
+
*
|
|
2107
|
+
* Capture tracing data for Hapi.
|
|
2108
|
+
* If you also want to capture errors, you need to call `setupHapiErrorHandler(server)` after you set up your server.
|
|
2109
|
+
*/
|
|
2110
|
+
const hapiIntegration = defineIntegration(_hapiIntegration);
|
|
2111
|
+
|
|
2112
|
+
function isBoomObject(response) {
|
|
2113
|
+
return response && (response ).isBoom !== undefined;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
function isErrorEvent(event) {
|
|
2117
|
+
return event && (event ).error !== undefined;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
function sendErrorToSentry(errorData) {
|
|
2121
|
+
captureException(errorData, {
|
|
2122
|
+
mechanism: {
|
|
2123
|
+
type: 'hapi',
|
|
2124
|
+
handled: false,
|
|
2125
|
+
data: {
|
|
2126
|
+
function: 'hapiErrorPlugin',
|
|
2127
|
+
},
|
|
2128
|
+
},
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
const hapiErrorPlugin = {
|
|
2133
|
+
name: 'SentryHapiErrorPlugin',
|
|
2134
|
+
version: SDK_VERSION,
|
|
2135
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2136
|
+
register: async function (serverArg) {
|
|
2137
|
+
const server = serverArg ;
|
|
2138
|
+
|
|
2139
|
+
server.events.on('request', (request, event) => {
|
|
2140
|
+
const activeSpan = getActiveSpan();
|
|
2141
|
+
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
|
|
2142
|
+
|
|
2143
|
+
if (request.response && isBoomObject(request.response)) {
|
|
2144
|
+
sendErrorToSentry(request.response);
|
|
2145
|
+
} else if (isErrorEvent(event)) {
|
|
2146
|
+
sendErrorToSentry(event.error);
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
if (rootSpan) {
|
|
2150
|
+
rootSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
|
|
2151
|
+
rootSpan.end();
|
|
2152
|
+
}
|
|
2153
|
+
});
|
|
2154
|
+
},
|
|
2155
|
+
};
|
|
2156
|
+
|
|
2157
|
+
/**
|
|
2158
|
+
* Add a Hapi plugin to capture errors to Sentry.
|
|
2159
|
+
*/
|
|
2160
|
+
async function setupHapiErrorHandler(server) {
|
|
2161
|
+
await server.register(hapiErrorPlugin);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
const INTEGRATION_NAME = 'Spotlight';
|
|
2165
|
+
|
|
2166
|
+
const _spotlightIntegration = ((options = {}) => {
|
|
2167
|
+
const _options = {
|
|
2168
|
+
sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',
|
|
2169
|
+
};
|
|
2170
|
+
|
|
2171
|
+
return {
|
|
2172
|
+
name: INTEGRATION_NAME,
|
|
2173
|
+
setup(client) {
|
|
2174
|
+
if (typeof process === 'object' && process.env && process.env.NODE_ENV !== 'development') {
|
|
2175
|
+
logger.warn("[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?");
|
|
2176
|
+
}
|
|
2177
|
+
connectToSpotlight(client, _options);
|
|
2178
|
+
},
|
|
2179
|
+
};
|
|
2180
|
+
}) ;
|
|
2181
|
+
|
|
2182
|
+
/**
|
|
2183
|
+
* Use this integration to send errors and transactions to Spotlight.
|
|
2184
|
+
*
|
|
2185
|
+
* Learn more about spotlight at https://spotlightjs.com
|
|
2186
|
+
*
|
|
2187
|
+
* Important: This integration only works with Node 18 or newer.
|
|
2188
|
+
*/
|
|
2189
|
+
const spotlightIntegration = defineIntegration(_spotlightIntegration);
|
|
2190
|
+
|
|
2191
|
+
function connectToSpotlight(client, options) {
|
|
2192
|
+
const spotlightUrl = parseSidecarUrl(options.sidecarUrl);
|
|
2193
|
+
if (!spotlightUrl) {
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
let failedRequests = 0;
|
|
2198
|
+
|
|
2199
|
+
client.on('beforeEnvelope', (envelope) => {
|
|
2200
|
+
if (failedRequests > 3) {
|
|
2201
|
+
logger.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
const serializedEnvelope = serializeEnvelope(envelope);
|
|
2206
|
+
|
|
2207
|
+
const request = getNativeHttpRequest();
|
|
2208
|
+
const req = request(
|
|
2209
|
+
{
|
|
2210
|
+
method: 'POST',
|
|
2211
|
+
path: spotlightUrl.pathname,
|
|
2212
|
+
hostname: spotlightUrl.hostname,
|
|
2213
|
+
port: spotlightUrl.port,
|
|
2214
|
+
headers: {
|
|
2215
|
+
'Content-Type': 'application/x-sentry-envelope',
|
|
2216
|
+
},
|
|
2217
|
+
},
|
|
2218
|
+
res => {
|
|
2219
|
+
res.on('data', () => {
|
|
2220
|
+
// Drain socket
|
|
2221
|
+
});
|
|
2222
|
+
|
|
2223
|
+
res.on('end', () => {
|
|
2224
|
+
// Drain socket
|
|
2225
|
+
});
|
|
2226
|
+
res.setEncoding('utf8');
|
|
2227
|
+
},
|
|
2228
|
+
);
|
|
2229
|
+
|
|
2230
|
+
req.on('error', () => {
|
|
2231
|
+
failedRequests++;
|
|
2232
|
+
logger.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');
|
|
2233
|
+
});
|
|
2234
|
+
req.write(serializedEnvelope);
|
|
2235
|
+
req.end();
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
function parseSidecarUrl(url) {
|
|
2240
|
+
try {
|
|
2241
|
+
return new URL(`${url}`);
|
|
2242
|
+
} catch (e) {
|
|
2243
|
+
logger.warn(`[Spotlight] Invalid sidecar URL: ${url}`);
|
|
2244
|
+
return undefined;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
/**
|
|
2249
|
+
* We want to get an unpatched http request implementation to avoid capturing our own calls.
|
|
2250
|
+
*/
|
|
2251
|
+
function getNativeHttpRequest() {
|
|
2252
|
+
const { request } = http;
|
|
2253
|
+
if (isWrapped(request)) {
|
|
2254
|
+
return request.__sentry_original__;
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
return request;
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
function isWrapped(impl) {
|
|
2261
|
+
return '__sentry_original__' in impl;
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
const _koaIntegration = (() => {
|
|
2265
|
+
return {
|
|
2266
|
+
name: 'Koa',
|
|
2267
|
+
setupOnce() {
|
|
2268
|
+
registerInstrumentations({
|
|
2269
|
+
instrumentations: [new KoaInstrumentation()],
|
|
2270
|
+
});
|
|
2271
|
+
},
|
|
2272
|
+
};
|
|
2273
|
+
}) ;
|
|
2274
|
+
|
|
2275
|
+
const koaIntegration = defineIntegration(_koaIntegration);
|
|
2276
|
+
|
|
2277
|
+
/**
|
|
2278
|
+
* With OTEL, all performance integrations will be added, as OTEL only initializes them when the patched package is actually required.
|
|
2279
|
+
*/
|
|
2280
|
+
function getAutoPerformanceIntegrations() {
|
|
2281
|
+
return [
|
|
2282
|
+
expressIntegration(),
|
|
2283
|
+
fastifyIntegration(),
|
|
2284
|
+
graphqlIntegration(),
|
|
2285
|
+
mongoIntegration(),
|
|
2286
|
+
mongooseIntegration(),
|
|
2287
|
+
mysqlIntegration(),
|
|
2288
|
+
mysql2Integration(),
|
|
2289
|
+
postgresIntegration(),
|
|
2290
|
+
prismaIntegration(),
|
|
2291
|
+
nestIntegration(),
|
|
2292
|
+
hapiIntegration(),
|
|
2293
|
+
koaIntegration(),
|
|
2294
|
+
];
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }/**
|
|
2298
|
+
* This code was originally forked from https://github.com/TooTallNate/proxy-agents/tree/b133295fd16f6475578b6b15bd9b4e33ecb0d0b7
|
|
2299
|
+
* With the following licence:
|
|
2300
|
+
*
|
|
2301
|
+
* (The MIT License)
|
|
2302
|
+
*
|
|
2303
|
+
* Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>*
|
|
2304
|
+
*
|
|
2305
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
|
2306
|
+
* a copy of this software and associated documentation files (the
|
|
2307
|
+
* 'Software'), to deal in the Software without restriction, including
|
|
2308
|
+
* without limitation the rights to use, copy, modify, merge, publish,
|
|
2309
|
+
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
2310
|
+
* permit persons to whom the Software is furnished to do so, subject to
|
|
2311
|
+
* the following conditions:*
|
|
2312
|
+
*
|
|
2313
|
+
* The above copyright notice and this permission notice shall be
|
|
2314
|
+
* included in all copies or substantial portions of the Software.*
|
|
2315
|
+
*
|
|
2316
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
2317
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
2318
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
2319
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
2320
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
2321
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
2322
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
2323
|
+
*/
|
|
2324
|
+
|
|
2325
|
+
const INTERNAL = Symbol('AgentBaseInternalState');
|
|
2326
|
+
|
|
2327
|
+
class Agent extends http.Agent {
|
|
2328
|
+
|
|
2329
|
+
// Set by `http.Agent` - missing from `@types/node`
|
|
2330
|
+
|
|
2331
|
+
constructor(opts) {
|
|
2332
|
+
super(opts);
|
|
2333
|
+
this[INTERNAL] = {};
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
/**
|
|
2337
|
+
* Determine whether this is an `http` or `https` request.
|
|
2338
|
+
*/
|
|
2339
|
+
isSecureEndpoint(options) {
|
|
2340
|
+
if (options) {
|
|
2341
|
+
// First check the `secureEndpoint` property explicitly, since this
|
|
2342
|
+
// means that a parent `Agent` is "passing through" to this instance.
|
|
2343
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
2344
|
+
if (typeof (options ).secureEndpoint === 'boolean') {
|
|
2345
|
+
return options.secureEndpoint;
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// If no explicit `secure` endpoint, check if `protocol` property is
|
|
2349
|
+
// set. This will usually be the case since using a full string URL
|
|
2350
|
+
// or `URL` instance should be the most common usage.
|
|
2351
|
+
if (typeof options.protocol === 'string') {
|
|
2352
|
+
return options.protocol === 'https:';
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
// Finally, if no `protocol` property was set, then fall back to
|
|
2357
|
+
// checking the stack trace of the current call stack, and try to
|
|
2358
|
+
// detect the "https" module.
|
|
2359
|
+
const { stack } = new Error();
|
|
2360
|
+
if (typeof stack !== 'string') return false;
|
|
2361
|
+
return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
createSocket(req, options, cb) {
|
|
2365
|
+
const connectOpts = {
|
|
2366
|
+
...options,
|
|
2367
|
+
secureEndpoint: this.isSecureEndpoint(options),
|
|
2368
|
+
};
|
|
2369
|
+
Promise.resolve()
|
|
2370
|
+
.then(() => this.connect(req, connectOpts))
|
|
2371
|
+
.then(socket => {
|
|
2372
|
+
if (socket instanceof http.Agent) {
|
|
2373
|
+
// @ts-expect-error `addRequest()` isn't defined in `@types/node`
|
|
2374
|
+
return socket.addRequest(req, connectOpts);
|
|
2375
|
+
}
|
|
2376
|
+
this[INTERNAL].currentSocket = socket;
|
|
2377
|
+
// @ts-expect-error `createSocket()` isn't defined in `@types/node`
|
|
2378
|
+
super.createSocket(req, options, cb);
|
|
2379
|
+
}, cb);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
createConnection() {
|
|
2383
|
+
const socket = this[INTERNAL].currentSocket;
|
|
2384
|
+
this[INTERNAL].currentSocket = undefined;
|
|
2385
|
+
if (!socket) {
|
|
2386
|
+
throw new Error('No socket was returned in the `connect()` function');
|
|
2387
|
+
}
|
|
2388
|
+
return socket;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
get defaultPort() {
|
|
2392
|
+
return _nullishCoalesce$2(this[INTERNAL].defaultPort, () => ( (this.protocol === 'https:' ? 443 : 80)));
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
set defaultPort(v) {
|
|
2396
|
+
if (this[INTERNAL]) {
|
|
2397
|
+
this[INTERNAL].defaultPort = v;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
get protocol() {
|
|
2402
|
+
return _nullishCoalesce$2(this[INTERNAL].protocol, () => ( (this.isSecureEndpoint() ? 'https:' : 'http:')));
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
set protocol(v) {
|
|
2406
|
+
if (this[INTERNAL]) {
|
|
2407
|
+
this[INTERNAL].protocol = v;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
function debug$1(...args) {
|
|
2413
|
+
logger.log('[https-proxy-agent:parse-proxy-response]', ...args);
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
function parseProxyResponse(socket) {
|
|
2417
|
+
return new Promise((resolve, reject) => {
|
|
2418
|
+
// we need to buffer any HTTP traffic that happens with the proxy before we get
|
|
2419
|
+
// the CONNECT response, so that if the response is anything other than an "200"
|
|
2420
|
+
// response code, then we can re-play the "data" events on the socket once the
|
|
2421
|
+
// HTTP parser is hooked up...
|
|
2422
|
+
let buffersLength = 0;
|
|
2423
|
+
const buffers = [];
|
|
2424
|
+
|
|
2425
|
+
function read() {
|
|
2426
|
+
const b = socket.read();
|
|
2427
|
+
if (b) ondata(b);
|
|
2428
|
+
else socket.once('readable', read);
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
function cleanup() {
|
|
2432
|
+
socket.removeListener('end', onend);
|
|
2433
|
+
socket.removeListener('error', onerror);
|
|
2434
|
+
socket.removeListener('readable', read);
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
function onend() {
|
|
2438
|
+
cleanup();
|
|
2439
|
+
debug$1('onend');
|
|
2440
|
+
reject(new Error('Proxy connection ended before receiving CONNECT response'));
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
function onerror(err) {
|
|
2444
|
+
cleanup();
|
|
2445
|
+
debug$1('onerror %o', err);
|
|
2446
|
+
reject(err);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
function ondata(b) {
|
|
2450
|
+
buffers.push(b);
|
|
2451
|
+
buffersLength += b.length;
|
|
2452
|
+
|
|
2453
|
+
const buffered = Buffer.concat(buffers, buffersLength);
|
|
2454
|
+
const endOfHeaders = buffered.indexOf('\r\n\r\n');
|
|
2455
|
+
|
|
2456
|
+
if (endOfHeaders === -1) {
|
|
2457
|
+
// keep buffering
|
|
2458
|
+
debug$1('have not received end of HTTP headers yet...');
|
|
2459
|
+
read();
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
const headerParts = buffered.slice(0, endOfHeaders).toString('ascii').split('\r\n');
|
|
2464
|
+
const firstLine = headerParts.shift();
|
|
2465
|
+
if (!firstLine) {
|
|
2466
|
+
socket.destroy();
|
|
2467
|
+
return reject(new Error('No header received from proxy CONNECT response'));
|
|
2468
|
+
}
|
|
2469
|
+
const firstLineParts = firstLine.split(' ');
|
|
2470
|
+
const statusCode = +firstLineParts[1];
|
|
2471
|
+
const statusText = firstLineParts.slice(2).join(' ');
|
|
2472
|
+
const headers = {};
|
|
2473
|
+
for (const header of headerParts) {
|
|
2474
|
+
if (!header) continue;
|
|
2475
|
+
const firstColon = header.indexOf(':');
|
|
2476
|
+
if (firstColon === -1) {
|
|
2477
|
+
socket.destroy();
|
|
2478
|
+
return reject(new Error(`Invalid header from proxy CONNECT response: "${header}"`));
|
|
2479
|
+
}
|
|
2480
|
+
const key = header.slice(0, firstColon).toLowerCase();
|
|
2481
|
+
const value = header.slice(firstColon + 1).trimStart();
|
|
2482
|
+
const current = headers[key];
|
|
2483
|
+
if (typeof current === 'string') {
|
|
2484
|
+
headers[key] = [current, value];
|
|
2485
|
+
} else if (Array.isArray(current)) {
|
|
2486
|
+
current.push(value);
|
|
2487
|
+
} else {
|
|
2488
|
+
headers[key] = value;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
debug$1('got proxy server response: %o %o', firstLine, headers);
|
|
2492
|
+
cleanup();
|
|
2493
|
+
resolve({
|
|
2494
|
+
connect: {
|
|
2495
|
+
statusCode,
|
|
2496
|
+
statusText,
|
|
2497
|
+
headers,
|
|
2498
|
+
},
|
|
2499
|
+
buffered,
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
socket.on('error', onerror);
|
|
2504
|
+
socket.on('end', onend);
|
|
2505
|
+
|
|
2506
|
+
read();
|
|
2507
|
+
});
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }function _optionalChain$2(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2511
|
+
|
|
2512
|
+
function debug(...args) {
|
|
2513
|
+
logger.log('[https-proxy-agent]', ...args);
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
/**
|
|
2517
|
+
* The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to
|
|
2518
|
+
* the specified "HTTP(s) proxy server" in order to proxy HTTPS requests.
|
|
2519
|
+
*
|
|
2520
|
+
* Outgoing HTTP requests are first tunneled through the proxy server using the
|
|
2521
|
+
* `CONNECT` HTTP request method to establish a connection to the proxy server,
|
|
2522
|
+
* and then the proxy server connects to the destination target and issues the
|
|
2523
|
+
* HTTP request from the proxy server.
|
|
2524
|
+
*
|
|
2525
|
+
* `https:` requests have their socket connection upgraded to TLS once
|
|
2526
|
+
* the connection to the proxy server has been established.
|
|
2527
|
+
*/
|
|
2528
|
+
class HttpsProxyAgent extends Agent {
|
|
2529
|
+
static __initStatic() {this.protocols = ['http', 'https']; }
|
|
2530
|
+
|
|
2531
|
+
constructor(proxy, opts) {
|
|
2532
|
+
super(opts);
|
|
2533
|
+
this.options = {};
|
|
2534
|
+
this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy;
|
|
2535
|
+
this.proxyHeaders = _nullishCoalesce$1(_optionalChain$2([opts, 'optionalAccess', _2 => _2.headers]), () => ( {}));
|
|
2536
|
+
debug('Creating new HttpsProxyAgent instance: %o', this.proxy.href);
|
|
2537
|
+
|
|
2538
|
+
// Trim off the brackets from IPv6 addresses
|
|
2539
|
+
const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, '');
|
|
2540
|
+
const port = this.proxy.port ? parseInt(this.proxy.port, 10) : this.proxy.protocol === 'https:' ? 443 : 80;
|
|
2541
|
+
this.connectOpts = {
|
|
2542
|
+
// Attempt to negotiate http/1.1 for proxy servers that support http/2
|
|
2543
|
+
ALPNProtocols: ['http/1.1'],
|
|
2544
|
+
...(opts ? omit(opts, 'headers') : null),
|
|
2545
|
+
host,
|
|
2546
|
+
port,
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
/**
|
|
2551
|
+
* Called when the node-core HTTP client library is creating a
|
|
2552
|
+
* new HTTP request.
|
|
2553
|
+
*/
|
|
2554
|
+
async connect(req, opts) {
|
|
2555
|
+
const { proxy } = this;
|
|
2556
|
+
|
|
2557
|
+
if (!opts.host) {
|
|
2558
|
+
throw new TypeError('No "host" provided');
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
// Create a socket connection to the proxy server.
|
|
2562
|
+
let socket;
|
|
2563
|
+
if (proxy.protocol === 'https:') {
|
|
2564
|
+
debug('Creating `tls.Socket`: %o', this.connectOpts);
|
|
2565
|
+
const servername = this.connectOpts.servername || this.connectOpts.host;
|
|
2566
|
+
socket = tls.connect({
|
|
2567
|
+
...this.connectOpts,
|
|
2568
|
+
servername: servername && net.isIP(servername) ? undefined : servername,
|
|
2569
|
+
});
|
|
2570
|
+
} else {
|
|
2571
|
+
debug('Creating `net.Socket`: %o', this.connectOpts);
|
|
2572
|
+
socket = net.connect(this.connectOpts);
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
const headers =
|
|
2576
|
+
typeof this.proxyHeaders === 'function' ? this.proxyHeaders() : { ...this.proxyHeaders };
|
|
2577
|
+
const host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host;
|
|
2578
|
+
let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r\n`;
|
|
2579
|
+
|
|
2580
|
+
// Inject the `Proxy-Authorization` header if necessary.
|
|
2581
|
+
if (proxy.username || proxy.password) {
|
|
2582
|
+
const auth = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`;
|
|
2583
|
+
headers['Proxy-Authorization'] = `Basic ${Buffer.from(auth).toString('base64')}`;
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
headers.Host = `${host}:${opts.port}`;
|
|
2587
|
+
|
|
2588
|
+
if (!headers['Proxy-Connection']) {
|
|
2589
|
+
headers['Proxy-Connection'] = this.keepAlive ? 'Keep-Alive' : 'close';
|
|
2590
|
+
}
|
|
2591
|
+
for (const name of Object.keys(headers)) {
|
|
2592
|
+
payload += `${name}: ${headers[name]}\r\n`;
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
const proxyResponsePromise = parseProxyResponse(socket);
|
|
2596
|
+
|
|
2597
|
+
socket.write(`${payload}\r\n`);
|
|
2598
|
+
|
|
2599
|
+
const { connect, buffered } = await proxyResponsePromise;
|
|
2600
|
+
req.emit('proxyConnect', connect);
|
|
2601
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2602
|
+
// @ts-ignore Not EventEmitter in Node types
|
|
2603
|
+
this.emit('proxyConnect', connect, req);
|
|
2604
|
+
|
|
2605
|
+
if (connect.statusCode === 200) {
|
|
2606
|
+
req.once('socket', resume);
|
|
2607
|
+
|
|
2608
|
+
if (opts.secureEndpoint) {
|
|
2609
|
+
// The proxy is connecting to a TLS server, so upgrade
|
|
2610
|
+
// this socket connection to a TLS connection.
|
|
2611
|
+
debug('Upgrading socket connection to TLS');
|
|
2612
|
+
const servername = opts.servername || opts.host;
|
|
2613
|
+
return tls.connect({
|
|
2614
|
+
...omit(opts, 'host', 'path', 'port'),
|
|
2615
|
+
socket,
|
|
2616
|
+
servername: net.isIP(servername) ? undefined : servername,
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
return socket;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
// Some other status code that's not 200... need to re-play the HTTP
|
|
2624
|
+
// header "data" events onto the socket once the HTTP machinery is
|
|
2625
|
+
// attached so that the node core `http` can parse and handle the
|
|
2626
|
+
// error status code.
|
|
2627
|
+
|
|
2628
|
+
// Close the original socket, and a new "fake" socket is returned
|
|
2629
|
+
// instead, so that the proxy doesn't get the HTTP request
|
|
2630
|
+
// written to it (which may contain `Authorization` headers or other
|
|
2631
|
+
// sensitive data).
|
|
2632
|
+
//
|
|
2633
|
+
// See: https://hackerone.com/reports/541502
|
|
2634
|
+
socket.destroy();
|
|
2635
|
+
|
|
2636
|
+
const fakeSocket = new net.Socket({ writable: false });
|
|
2637
|
+
fakeSocket.readable = true;
|
|
2638
|
+
|
|
2639
|
+
// Need to wait for the "socket" event to re-play the "data" events.
|
|
2640
|
+
req.once('socket', (s) => {
|
|
2641
|
+
debug('Replaying proxy buffer for failed request');
|
|
2642
|
+
// Replay the "buffered" Buffer onto the fake `socket`, since at
|
|
2643
|
+
// this point the HTTP module machinery has been hooked up for
|
|
2644
|
+
// the user.
|
|
2645
|
+
s.push(buffered);
|
|
2646
|
+
s.push(null);
|
|
2647
|
+
});
|
|
2648
|
+
|
|
2649
|
+
return fakeSocket;
|
|
2650
|
+
}
|
|
2651
|
+
}HttpsProxyAgent.__initStatic();
|
|
2652
|
+
|
|
2653
|
+
function resume(socket) {
|
|
2654
|
+
socket.resume();
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
function omit(
|
|
2658
|
+
obj,
|
|
2659
|
+
...keys
|
|
2660
|
+
)
|
|
2661
|
+
|
|
2662
|
+
{
|
|
2663
|
+
const ret = {}
|
|
2664
|
+
|
|
2665
|
+
;
|
|
2666
|
+
let key;
|
|
2667
|
+
for (key in obj) {
|
|
2668
|
+
if (!keys.includes(key)) {
|
|
2669
|
+
ret[key] = obj[key];
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
return ret;
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
// Estimated maximum size for reasonable standalone event
|
|
2676
|
+
const GZIP_THRESHOLD = 1024 * 32;
|
|
2677
|
+
|
|
2678
|
+
/**
|
|
2679
|
+
* Gets a stream from a Uint8Array or string
|
|
2680
|
+
* Readable.from is ideal but was added in node.js v12.3.0 and v10.17.0
|
|
2681
|
+
*/
|
|
2682
|
+
function streamFromBody(body) {
|
|
2683
|
+
return new Readable({
|
|
2684
|
+
read() {
|
|
2685
|
+
this.push(body);
|
|
2686
|
+
this.push(null);
|
|
2687
|
+
},
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
/**
|
|
2692
|
+
* Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry.
|
|
2693
|
+
*/
|
|
2694
|
+
function makeNodeTransport(options) {
|
|
2695
|
+
let urlSegments;
|
|
2696
|
+
|
|
2697
|
+
try {
|
|
2698
|
+
urlSegments = new URL(options.url);
|
|
2699
|
+
} catch (e) {
|
|
2700
|
+
consoleSandbox(() => {
|
|
2701
|
+
// eslint-disable-next-line no-console
|
|
2702
|
+
console.warn(
|
|
2703
|
+
'[@sentry/node]: Invalid dsn or tunnel option, will not send any events. The tunnel option must be a full URL when used.',
|
|
2704
|
+
);
|
|
2705
|
+
});
|
|
2706
|
+
return createTransport(options, () => Promise.resolve({}));
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
const isHttps = urlSegments.protocol === 'https:';
|
|
2710
|
+
|
|
2711
|
+
// Proxy prioritization: http => `options.proxy` | `process.env.http_proxy`
|
|
2712
|
+
// Proxy prioritization: https => `options.proxy` | `process.env.https_proxy` | `process.env.http_proxy`
|
|
2713
|
+
const proxy = applyNoProxyOption(
|
|
2714
|
+
urlSegments,
|
|
2715
|
+
options.proxy || (isHttps ? process.env.https_proxy : undefined) || process.env.http_proxy,
|
|
2716
|
+
);
|
|
2717
|
+
|
|
2718
|
+
const nativeHttpModule = isHttps ? https : http$1;
|
|
2719
|
+
const keepAlive = options.keepAlive === undefined ? false : options.keepAlive;
|
|
2720
|
+
|
|
2721
|
+
// TODO(v7): Evaluate if we can set keepAlive to true. This would involve testing for memory leaks in older node
|
|
2722
|
+
// versions(>= 8) as they had memory leaks when using it: #2555
|
|
2723
|
+
const agent = proxy
|
|
2724
|
+
? (new HttpsProxyAgent(proxy) )
|
|
2725
|
+
: new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 });
|
|
2726
|
+
|
|
2727
|
+
const requestExecutor = createRequestExecutor(options, _nullishCoalesce(options.httpModule, () => ( nativeHttpModule)), agent);
|
|
2728
|
+
return createTransport(options, requestExecutor);
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
/**
|
|
2732
|
+
* Honors the `no_proxy` env variable with the highest priority to allow for hosts exclusion.
|
|
2733
|
+
*
|
|
2734
|
+
* @param transportUrl The URL the transport intends to send events to.
|
|
2735
|
+
* @param proxy The client configured proxy.
|
|
2736
|
+
* @returns A proxy the transport should use.
|
|
2737
|
+
*/
|
|
2738
|
+
function applyNoProxyOption(transportUrlSegments, proxy) {
|
|
2739
|
+
const { no_proxy } = process.env;
|
|
2740
|
+
|
|
2741
|
+
const urlIsExemptFromProxy =
|
|
2742
|
+
no_proxy &&
|
|
2743
|
+
no_proxy
|
|
2744
|
+
.split(',')
|
|
2745
|
+
.some(
|
|
2746
|
+
exemption => transportUrlSegments.host.endsWith(exemption) || transportUrlSegments.hostname.endsWith(exemption),
|
|
2747
|
+
);
|
|
2748
|
+
|
|
2749
|
+
if (urlIsExemptFromProxy) {
|
|
2750
|
+
return undefined;
|
|
2751
|
+
} else {
|
|
2752
|
+
return proxy;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
/**
|
|
2757
|
+
* Creates a RequestExecutor to be used with `createTransport`.
|
|
2758
|
+
*/
|
|
2759
|
+
function createRequestExecutor(
|
|
2760
|
+
options,
|
|
2761
|
+
httpModule,
|
|
2762
|
+
agent,
|
|
2763
|
+
) {
|
|
2764
|
+
const { hostname, pathname, port, protocol, search } = new URL(options.url);
|
|
2765
|
+
return function makeRequest(request) {
|
|
2766
|
+
return new Promise((resolve, reject) => {
|
|
2767
|
+
let body = streamFromBody(request.body);
|
|
2768
|
+
|
|
2769
|
+
const headers = { ...options.headers };
|
|
2770
|
+
|
|
2771
|
+
if (request.body.length > GZIP_THRESHOLD) {
|
|
2772
|
+
headers['content-encoding'] = 'gzip';
|
|
2773
|
+
body = body.pipe(createGzip());
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
const req = httpModule.request(
|
|
2777
|
+
{
|
|
2778
|
+
method: 'POST',
|
|
2779
|
+
agent,
|
|
2780
|
+
headers,
|
|
2781
|
+
hostname,
|
|
2782
|
+
path: `${pathname}${search}`,
|
|
2783
|
+
port,
|
|
2784
|
+
protocol,
|
|
2785
|
+
ca: options.caCerts,
|
|
2786
|
+
},
|
|
2787
|
+
res => {
|
|
2788
|
+
res.on('data', () => {
|
|
2789
|
+
// Drain socket
|
|
2790
|
+
});
|
|
2791
|
+
|
|
2792
|
+
res.on('end', () => {
|
|
2793
|
+
// Drain socket
|
|
2794
|
+
});
|
|
2795
|
+
|
|
2796
|
+
res.setEncoding('utf8');
|
|
2797
|
+
|
|
2798
|
+
// "Key-value pairs of header names and values. Header names are lower-cased."
|
|
2799
|
+
// https://nodejs.org/api/http.html#http_message_headers
|
|
2800
|
+
const retryAfterHeader = _nullishCoalesce(res.headers['retry-after'], () => ( null));
|
|
2801
|
+
const rateLimitsHeader = _nullishCoalesce(res.headers['x-sentry-rate-limits'], () => ( null));
|
|
2802
|
+
|
|
2803
|
+
resolve({
|
|
2804
|
+
statusCode: res.statusCode,
|
|
2805
|
+
headers: {
|
|
2806
|
+
'retry-after': retryAfterHeader,
|
|
2807
|
+
'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] : rateLimitsHeader,
|
|
2808
|
+
},
|
|
2809
|
+
});
|
|
2810
|
+
},
|
|
2811
|
+
);
|
|
2812
|
+
|
|
2813
|
+
req.on('error', reject);
|
|
2814
|
+
body.pipe(req);
|
|
2815
|
+
});
|
|
2816
|
+
};
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
/** normalizes Windows paths */
|
|
2820
|
+
function normalizeWindowsPath(path) {
|
|
2821
|
+
return path
|
|
2822
|
+
.replace(/^[A-Z]:/, '') // remove Windows-style prefix
|
|
2823
|
+
.replace(/\\/g, '/'); // replace all `\` instances with `/`
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
/** Creates a function that gets the module name from a filename */
|
|
2827
|
+
function createGetModuleFromFilename(
|
|
2828
|
+
basePath = process.argv[1] ? dirname$1(process.argv[1]) : process.cwd(),
|
|
2829
|
+
isWindows = sep === '\\',
|
|
2830
|
+
) {
|
|
2831
|
+
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
2832
|
+
|
|
2833
|
+
return (filename) => {
|
|
2834
|
+
if (!filename) {
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename;
|
|
2839
|
+
|
|
2840
|
+
// eslint-disable-next-line prefer-const
|
|
2841
|
+
let { dir, base: file, ext } = posix.parse(normalizedFilename);
|
|
2842
|
+
|
|
2843
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
2844
|
+
file = file.slice(0, ext.length * -1);
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
if (!dir) {
|
|
2848
|
+
// No dirname whatsoever
|
|
2849
|
+
dir = '.';
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
const n = dir.lastIndexOf('/node_modules');
|
|
2853
|
+
if (n > -1) {
|
|
2854
|
+
return `${dir.slice(n + 14).replace(/\//g, '.')}:${file}`;
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
// Let's see if it's a part of the main module
|
|
2858
|
+
// To be a part of main module, it has to share the same base
|
|
2859
|
+
if (dir.startsWith(normalizedBase)) {
|
|
2860
|
+
let moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, '.');
|
|
2861
|
+
|
|
2862
|
+
if (moduleName) {
|
|
2863
|
+
moduleName += ':';
|
|
2864
|
+
}
|
|
2865
|
+
moduleName += file;
|
|
2866
|
+
|
|
2867
|
+
return moduleName;
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
return file;
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
// PUBLIC APIS
|
|
2875
|
+
|
|
2876
|
+
|
|
2877
|
+
/** Get the currently active client. */
|
|
2878
|
+
function getClient() {
|
|
2879
|
+
const currentScope = getCurrentScope();
|
|
2880
|
+
|
|
2881
|
+
const client = currentScope.getClient();
|
|
2882
|
+
if (client) {
|
|
2883
|
+
return client ;
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
// TODO otherwise ensure we use a noop client
|
|
2887
|
+
return {} ;
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
/**
|
|
2891
|
+
* Returns a release dynamically from environment variables.
|
|
2892
|
+
*/
|
|
2893
|
+
function getSentryRelease(fallback) {
|
|
2894
|
+
// Always read first as Sentry takes this as precedence
|
|
2895
|
+
if (process.env.SENTRY_RELEASE) {
|
|
2896
|
+
return process.env.SENTRY_RELEASE;
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
// This supports the variable that sentry-webpack-plugin injects
|
|
2900
|
+
if (GLOBAL_OBJ.SENTRY_RELEASE && GLOBAL_OBJ.SENTRY_RELEASE.id) {
|
|
2901
|
+
return GLOBAL_OBJ.SENTRY_RELEASE.id;
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
return (
|
|
2905
|
+
// GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
|
|
2906
|
+
process.env.GITHUB_SHA ||
|
|
2907
|
+
// Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata
|
|
2908
|
+
process.env.COMMIT_REF ||
|
|
2909
|
+
// Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables
|
|
2910
|
+
process.env.VERCEL_GIT_COMMIT_SHA ||
|
|
2911
|
+
process.env.VERCEL_GITHUB_COMMIT_SHA ||
|
|
2912
|
+
process.env.VERCEL_GITLAB_COMMIT_SHA ||
|
|
2913
|
+
process.env.VERCEL_BITBUCKET_COMMIT_SHA ||
|
|
2914
|
+
// Zeit (now known as Vercel)
|
|
2915
|
+
process.env.ZEIT_GITHUB_COMMIT_SHA ||
|
|
2916
|
+
process.env.ZEIT_GITLAB_COMMIT_SHA ||
|
|
2917
|
+
process.env.ZEIT_BITBUCKET_COMMIT_SHA ||
|
|
2918
|
+
// Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables
|
|
2919
|
+
process.env.CF_PAGES_COMMIT_SHA ||
|
|
2920
|
+
fallback
|
|
2921
|
+
);
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
/** Node.js stack parser */
|
|
2925
|
+
const defaultStackParser = createStackParser(nodeStackLineParser(createGetModuleFromFilename()));
|
|
2926
|
+
|
|
2927
|
+
/**
|
|
2928
|
+
* This method is a noop and only here to ensure vite-plugin v0.6.0 (which is used by Sveltekit) still works,
|
|
2929
|
+
* as that uses this.
|
|
2930
|
+
*
|
|
2931
|
+
* @deprecated This will be removed before v8 is finalized.
|
|
2932
|
+
*/
|
|
2933
|
+
function makeMain(hub) {
|
|
2934
|
+
// noop
|
|
2935
|
+
return hub;
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2939
|
+
/** A client for using Sentry with Node & OpenTelemetry. */
|
|
2940
|
+
class NodeClient extends ServerRuntimeClient {
|
|
2941
|
+
|
|
2942
|
+
constructor(options) {
|
|
2943
|
+
const clientOptions = {
|
|
2944
|
+
...options,
|
|
2945
|
+
platform: 'node',
|
|
2946
|
+
runtime: { name: 'node', version: global.process.version },
|
|
2947
|
+
serverName: options.serverName || global.process.env.SENTRY_NAME || os.hostname(),
|
|
2948
|
+
};
|
|
2949
|
+
|
|
2950
|
+
applySdkMetadata(clientOptions, 'node');
|
|
2951
|
+
|
|
2952
|
+
super(clientOptions);
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
/** Get the OTEL tracer. */
|
|
2956
|
+
get tracer() {
|
|
2957
|
+
if (this._tracer) {
|
|
2958
|
+
return this._tracer;
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
const name = '@sentry/node';
|
|
2962
|
+
const version = SDK_VERSION;
|
|
2963
|
+
const tracer = trace.getTracer(name, version);
|
|
2964
|
+
this._tracer = tracer;
|
|
2965
|
+
|
|
2966
|
+
return tracer;
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
/**
|
|
2970
|
+
* @inheritDoc
|
|
2971
|
+
*/
|
|
2972
|
+
async flush(timeout) {
|
|
2973
|
+
const provider = this.traceProvider;
|
|
2974
|
+
const spanProcessor = _optionalChain$1([provider, 'optionalAccess', _ => _.activeSpanProcessor]);
|
|
2975
|
+
|
|
2976
|
+
if (spanProcessor) {
|
|
2977
|
+
await spanProcessor.forceFlush();
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
return super.flush(timeout);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
/**
|
|
2985
|
+
* This is a custom ContextManager for OpenTelemetry, which extends the default AsyncLocalStorageContextManager.
|
|
2986
|
+
* It ensures that we create a new hub per context, so that the OTEL Context & the Sentry Hub are always in sync.
|
|
2987
|
+
*
|
|
2988
|
+
* Note that we currently only support AsyncHooks with this,
|
|
2989
|
+
* but since this should work for Node 14+ anyhow that should be good enough.
|
|
2990
|
+
*/
|
|
2991
|
+
const SentryContextManager = wrapContextManagerClass(AsyncLocalStorageContextManager);
|
|
2992
|
+
|
|
2993
|
+
/**
|
|
2994
|
+
* Initialize OpenTelemetry for Node.
|
|
2995
|
+
*/
|
|
2996
|
+
function initOtel() {
|
|
2997
|
+
const client = getClient();
|
|
2998
|
+
|
|
2999
|
+
if (!client) {
|
|
3000
|
+
DEBUG_BUILD &&
|
|
3001
|
+
logger.warn(
|
|
3002
|
+
'No client available, skipping OpenTelemetry setup. This probably means that `Sentry.init()` was not called before `initOtel()`.',
|
|
3003
|
+
);
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
if (client.getOptions().debug) {
|
|
3008
|
+
const otelLogger = new Proxy(logger , {
|
|
3009
|
+
get(target, prop, receiver) {
|
|
3010
|
+
const actualProp = prop === 'verbose' ? 'debug' : prop;
|
|
3011
|
+
return Reflect.get(target, actualProp, receiver);
|
|
3012
|
+
},
|
|
3013
|
+
});
|
|
3014
|
+
|
|
3015
|
+
diag.setLogger(otelLogger, DiagLogLevel.DEBUG);
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
setupEventContextTrace(client);
|
|
3019
|
+
|
|
3020
|
+
const provider = setupOtel(client);
|
|
3021
|
+
client.traceProvider = provider;
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
/** Just exported for tests. */
|
|
3025
|
+
function setupOtel(client) {
|
|
3026
|
+
// Create and configure NodeTracerProvider
|
|
3027
|
+
const provider = new BasicTracerProvider({
|
|
3028
|
+
sampler: new SentrySampler(client),
|
|
3029
|
+
resource: new Resource({
|
|
3030
|
+
[SemanticResourceAttributes.SERVICE_NAME]: 'node',
|
|
3031
|
+
[SemanticResourceAttributes.SERVICE_NAMESPACE]: 'sentry',
|
|
3032
|
+
[SemanticResourceAttributes.SERVICE_VERSION]: SDK_VERSION,
|
|
3033
|
+
}),
|
|
3034
|
+
forceFlushTimeoutMillis: 500,
|
|
3035
|
+
});
|
|
3036
|
+
provider.addSpanProcessor(new SentrySpanProcessor());
|
|
3037
|
+
|
|
3038
|
+
// Initialize the provider
|
|
3039
|
+
provider.register({
|
|
3040
|
+
propagator: new SentryPropagator(),
|
|
3041
|
+
contextManager: new SentryContextManager(),
|
|
3042
|
+
});
|
|
3043
|
+
|
|
3044
|
+
return provider;
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
function getCjsOnlyIntegrations(isCjs = typeof require !== 'undefined') {
|
|
3048
|
+
return isCjs ? [modulesIntegration()] : [];
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
/** Get the default integrations for the Node Experimental SDK. */
|
|
3052
|
+
function getDefaultIntegrations(options) {
|
|
3053
|
+
return [
|
|
3054
|
+
// Common
|
|
3055
|
+
inboundFiltersIntegration(),
|
|
3056
|
+
functionToStringIntegration(),
|
|
3057
|
+
linkedErrorsIntegration(),
|
|
3058
|
+
requestDataIntegration(),
|
|
3059
|
+
// Native Wrappers
|
|
3060
|
+
consoleIntegration(),
|
|
3061
|
+
httpIntegration(),
|
|
3062
|
+
nativeNodeFetchIntegration(),
|
|
3063
|
+
// Global Handlers
|
|
3064
|
+
onUncaughtExceptionIntegration(),
|
|
3065
|
+
onUnhandledRejectionIntegration(),
|
|
3066
|
+
// Event Info
|
|
3067
|
+
contextLinesIntegration(),
|
|
3068
|
+
localVariablesIntegration(),
|
|
3069
|
+
nodeContextIntegration(),
|
|
3070
|
+
httpIntegration(),
|
|
3071
|
+
...getCjsOnlyIntegrations(),
|
|
3072
|
+
...(hasTracingEnabled(options) ? getAutoPerformanceIntegrations() : []),
|
|
3073
|
+
];
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
/**
|
|
3077
|
+
* Initialize Sentry for Node.
|
|
3078
|
+
*/
|
|
3079
|
+
function init(options = {}) {
|
|
3080
|
+
const clientOptions = getClientOptions(options);
|
|
3081
|
+
|
|
3082
|
+
if (clientOptions.debug === true) {
|
|
3083
|
+
if (DEBUG_BUILD) {
|
|
3084
|
+
logger.enable();
|
|
3085
|
+
} else {
|
|
3086
|
+
// use `console.warn` rather than `logger.warn` since by non-debug bundles have all `logger.x` statements stripped
|
|
3087
|
+
consoleSandbox(() => {
|
|
3088
|
+
// eslint-disable-next-line no-console
|
|
3089
|
+
console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
setOpenTelemetryContextAsyncContextStrategy();
|
|
3095
|
+
|
|
3096
|
+
const scope = getCurrentScope();
|
|
3097
|
+
scope.update(options.initialScope);
|
|
3098
|
+
|
|
3099
|
+
const client = new NodeClient(clientOptions);
|
|
3100
|
+
// The client is on the current scope, from where it generally is inherited
|
|
3101
|
+
getCurrentScope().setClient(client);
|
|
3102
|
+
|
|
3103
|
+
if (isEnabled(client)) {
|
|
3104
|
+
client.init();
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
if (options.autoSessionTracking) {
|
|
3108
|
+
startSessionTracking();
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
updateScopeFromEnvVariables();
|
|
3112
|
+
|
|
3113
|
+
if (options.spotlight) {
|
|
3114
|
+
// force integrations to be setup even if no DSN was set
|
|
3115
|
+
// If they have already been added before, they will be ignored anyhow
|
|
3116
|
+
const integrations = client.getOptions().integrations;
|
|
3117
|
+
for (const integration of integrations) {
|
|
3118
|
+
client.addIntegration(integration);
|
|
3119
|
+
}
|
|
3120
|
+
client.addIntegration(
|
|
3121
|
+
spotlightIntegration({
|
|
3122
|
+
sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,
|
|
3123
|
+
}),
|
|
3124
|
+
);
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
// If users opt-out of this, they _have_ to set up OpenTelemetry themselves
|
|
3128
|
+
// There is no way to use this SDK without OpenTelemetry!
|
|
3129
|
+
if (!options.skipOpenTelemetrySetup) {
|
|
3130
|
+
initOtel();
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
validateOpenTelemetrySetup();
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
function validateOpenTelemetrySetup() {
|
|
3137
|
+
if (!DEBUG_BUILD) {
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
const setup = openTelemetrySetupCheck();
|
|
3142
|
+
|
|
3143
|
+
const required = ['SentrySpanProcessor', 'SentryContextManager', 'SentryPropagator'] ;
|
|
3144
|
+
for (const k of required) {
|
|
3145
|
+
if (!setup.includes(k)) {
|
|
3146
|
+
logger.error(
|
|
3147
|
+
`You have to set up the ${k}. Without this, the OpenTelemetry & Sentry integration will not work properly.`,
|
|
3148
|
+
);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
if (!setup.includes('SentrySampler')) {
|
|
3153
|
+
logger.warn(
|
|
3154
|
+
'You have to set up the SentrySampler. Without this, the OpenTelemetry & Sentry integration may still work, but sample rates set for the Sentry SDK will not be respected.',
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
function getClientOptions(options) {
|
|
3160
|
+
if (options.defaultIntegrations === undefined) {
|
|
3161
|
+
options.defaultIntegrations = getDefaultIntegrations(options);
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
const release = getRelease(options.release);
|
|
3165
|
+
|
|
3166
|
+
const autoSessionTracking =
|
|
3167
|
+
typeof release !== 'string'
|
|
3168
|
+
? false
|
|
3169
|
+
: options.autoSessionTracking === undefined
|
|
3170
|
+
? true
|
|
3171
|
+
: options.autoSessionTracking;
|
|
3172
|
+
|
|
3173
|
+
const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate);
|
|
3174
|
+
|
|
3175
|
+
const baseOptions = dropUndefinedKeys({
|
|
3176
|
+
transport: makeNodeTransport,
|
|
3177
|
+
dsn: process.env.SENTRY_DSN,
|
|
3178
|
+
environment: process.env.SENTRY_ENVIRONMENT,
|
|
3179
|
+
});
|
|
3180
|
+
|
|
3181
|
+
const overwriteOptions = dropUndefinedKeys({
|
|
3182
|
+
release,
|
|
3183
|
+
autoSessionTracking,
|
|
3184
|
+
tracesSampleRate,
|
|
3185
|
+
});
|
|
3186
|
+
|
|
3187
|
+
const clientOptions = {
|
|
3188
|
+
...baseOptions,
|
|
3189
|
+
...options,
|
|
3190
|
+
...overwriteOptions,
|
|
3191
|
+
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
|
|
3192
|
+
integrations: getIntegrationsToSetup({
|
|
3193
|
+
defaultIntegrations: options.defaultIntegrations,
|
|
3194
|
+
integrations: options.integrations,
|
|
3195
|
+
}),
|
|
3196
|
+
};
|
|
3197
|
+
|
|
3198
|
+
return clientOptions;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
function getRelease(release) {
|
|
3202
|
+
if (release !== undefined) {
|
|
3203
|
+
return release;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
const detectedRelease = getSentryRelease();
|
|
3207
|
+
if (detectedRelease !== undefined) {
|
|
3208
|
+
return detectedRelease;
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3211
|
+
return undefined;
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
function getTracesSampleRate(tracesSampleRate) {
|
|
3215
|
+
if (tracesSampleRate !== undefined) {
|
|
3216
|
+
return tracesSampleRate;
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
const sampleRateFromEnv = process.env.SENTRY_TRACES_SAMPLE_RATE;
|
|
3220
|
+
if (!sampleRateFromEnv) {
|
|
3221
|
+
return undefined;
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
const parsed = parseFloat(sampleRateFromEnv);
|
|
3225
|
+
return isFinite(parsed) ? parsed : undefined;
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
/**
|
|
3229
|
+
* Update scope and propagation context based on environmental variables.
|
|
3230
|
+
*
|
|
3231
|
+
* See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md
|
|
3232
|
+
* for more details.
|
|
3233
|
+
*/
|
|
3234
|
+
function updateScopeFromEnvVariables() {
|
|
3235
|
+
const sentryUseEnvironment = (process.env.SENTRY_USE_ENVIRONMENT || '').toLowerCase();
|
|
3236
|
+
if (!['false', 'n', 'no', 'off', '0'].includes(sentryUseEnvironment)) {
|
|
3237
|
+
const sentryTraceEnv = process.env.SENTRY_TRACE;
|
|
3238
|
+
const baggageEnv = process.env.SENTRY_BAGGAGE;
|
|
3239
|
+
const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv);
|
|
3240
|
+
getCurrentScope().setPropagationContext(propagationContext);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
/**
|
|
3245
|
+
* Enable automatic Session Tracking for the node process.
|
|
3246
|
+
*/
|
|
3247
|
+
function startSessionTracking() {
|
|
3248
|
+
const client = getClient$2();
|
|
3249
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
3250
|
+
client.initSessionFlusher();
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
startSession();
|
|
3254
|
+
|
|
3255
|
+
// Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because
|
|
3256
|
+
// The 'beforeExit' event is not emitted for conditions causing explicit termination,
|
|
3257
|
+
// such as calling process.exit() or uncaught exceptions.
|
|
3258
|
+
// Ref: https://nodejs.org/api/process.html#process_event_beforeexit
|
|
3259
|
+
process.on('beforeExit', () => {
|
|
3260
|
+
const session = getIsolationScope().getSession();
|
|
3261
|
+
|
|
3262
|
+
// Only call endSession, if the Session exists on Scope and SessionStatus is not a
|
|
3263
|
+
// Terminal Status i.e. Exited or Crashed because
|
|
3264
|
+
// "When a session is moved away from ok it must not be updated anymore."
|
|
3265
|
+
// Ref: https://develop.sentry.dev/sdk/sessions/
|
|
3266
|
+
if (session && session.status !== 'ok') {
|
|
3267
|
+
endSession();
|
|
3268
|
+
}
|
|
3269
|
+
});
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
function isEnabled(client) {
|
|
3273
|
+
return client.getOptions().enabled !== false && client.getTransport() !== undefined;
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
/**
|
|
3277
|
+
* This is for legacy reasons, and returns a proxy object instead of a hub to be used.
|
|
3278
|
+
* @deprecated Use the methods directly.
|
|
3279
|
+
*/
|
|
3280
|
+
function getCurrentHub() {
|
|
3281
|
+
return {
|
|
3282
|
+
isOlderThan(_version) {
|
|
3283
|
+
return false;
|
|
3284
|
+
},
|
|
3285
|
+
|
|
3286
|
+
bindClient(client) {
|
|
3287
|
+
const scope = getCurrentScope();
|
|
3288
|
+
scope.setClient(client);
|
|
3289
|
+
},
|
|
3290
|
+
|
|
3291
|
+
pushScope() {
|
|
3292
|
+
// TODO: This does not work and is actually deprecated
|
|
3293
|
+
return getCurrentScope();
|
|
3294
|
+
},
|
|
3295
|
+
|
|
3296
|
+
popScope() {
|
|
3297
|
+
// TODO: This does not work and is actually deprecated
|
|
3298
|
+
return false;
|
|
3299
|
+
},
|
|
3300
|
+
|
|
3301
|
+
withScope,
|
|
3302
|
+
getClient: () => getClient() ,
|
|
3303
|
+
getScope: getCurrentScope,
|
|
3304
|
+
getIsolationScope,
|
|
3305
|
+
captureException: (exception, hint) => {
|
|
3306
|
+
return getCurrentScope().captureException(exception, hint);
|
|
3307
|
+
},
|
|
3308
|
+
captureMessage: (message, level, hint) => {
|
|
3309
|
+
return getCurrentScope().captureMessage(message, level, hint);
|
|
3310
|
+
},
|
|
3311
|
+
captureEvent,
|
|
3312
|
+
addBreadcrumb,
|
|
3313
|
+
setUser,
|
|
3314
|
+
setTags,
|
|
3315
|
+
setTag,
|
|
3316
|
+
setExtra,
|
|
3317
|
+
setExtras,
|
|
3318
|
+
setContext,
|
|
3319
|
+
|
|
3320
|
+
getIntegration(integration) {
|
|
3321
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3322
|
+
return getClient().getIntegration(integration);
|
|
3323
|
+
},
|
|
3324
|
+
|
|
3325
|
+
startSession,
|
|
3326
|
+
|
|
3327
|
+
endSession,
|
|
3328
|
+
|
|
3329
|
+
captureSession(endSession) {
|
|
3330
|
+
// both send the update and pull the session from the scope
|
|
3331
|
+
if (endSession) {
|
|
3332
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3333
|
+
return this.endSession();
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
// only send the update
|
|
3337
|
+
_sendSessionUpdate();
|
|
3338
|
+
},
|
|
3339
|
+
|
|
3340
|
+
shouldSendDefaultPii() {
|
|
3341
|
+
const client = getClient();
|
|
3342
|
+
const options = client.getOptions();
|
|
3343
|
+
return Boolean(options.sendDefaultPii);
|
|
3344
|
+
},
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
/**
|
|
3349
|
+
* Sends the current Session on the scope
|
|
3350
|
+
*/
|
|
3351
|
+
function _sendSessionUpdate() {
|
|
3352
|
+
const scope = getCurrentScope();
|
|
3353
|
+
const client = getClient();
|
|
3354
|
+
|
|
3355
|
+
const session = scope.getSession();
|
|
3356
|
+
if (session) {
|
|
3357
|
+
client.captureSession(session);
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3361
|
+
const replacements = [
|
|
3362
|
+
['january', '1'],
|
|
3363
|
+
['february', '2'],
|
|
3364
|
+
['march', '3'],
|
|
3365
|
+
['april', '4'],
|
|
3366
|
+
['may', '5'],
|
|
3367
|
+
['june', '6'],
|
|
3368
|
+
['july', '7'],
|
|
3369
|
+
['august', '8'],
|
|
3370
|
+
['september', '9'],
|
|
3371
|
+
['october', '10'],
|
|
3372
|
+
['november', '11'],
|
|
3373
|
+
['december', '12'],
|
|
3374
|
+
['jan', '1'],
|
|
3375
|
+
['feb', '2'],
|
|
3376
|
+
['mar', '3'],
|
|
3377
|
+
['apr', '4'],
|
|
3378
|
+
['may', '5'],
|
|
3379
|
+
['jun', '6'],
|
|
3380
|
+
['jul', '7'],
|
|
3381
|
+
['aug', '8'],
|
|
3382
|
+
['sep', '9'],
|
|
3383
|
+
['oct', '10'],
|
|
3384
|
+
['nov', '11'],
|
|
3385
|
+
['dec', '12'],
|
|
3386
|
+
['sunday', '0'],
|
|
3387
|
+
['monday', '1'],
|
|
3388
|
+
['tuesday', '2'],
|
|
3389
|
+
['wednesday', '3'],
|
|
3390
|
+
['thursday', '4'],
|
|
3391
|
+
['friday', '5'],
|
|
3392
|
+
['saturday', '6'],
|
|
3393
|
+
['sun', '0'],
|
|
3394
|
+
['mon', '1'],
|
|
3395
|
+
['tue', '2'],
|
|
3396
|
+
['wed', '3'],
|
|
3397
|
+
['thu', '4'],
|
|
3398
|
+
['fri', '5'],
|
|
3399
|
+
['sat', '6'],
|
|
3400
|
+
];
|
|
3401
|
+
|
|
3402
|
+
/**
|
|
3403
|
+
* Replaces names in cron expressions
|
|
3404
|
+
*/
|
|
3405
|
+
function replaceCronNames(cronExpression) {
|
|
3406
|
+
return replacements.reduce(
|
|
3407
|
+
// eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor
|
|
3408
|
+
(acc, [name, replacement]) => acc.replace(new RegExp(name, 'gi'), replacement),
|
|
3409
|
+
cronExpression,
|
|
3410
|
+
);
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
const ERROR_TEXT = 'Automatic instrumentation of CronJob only supports crontab string';
|
|
3414
|
+
|
|
3415
|
+
/**
|
|
3416
|
+
* Instruments the `cron` library to send a check-in event to Sentry for each job execution.
|
|
3417
|
+
*
|
|
3418
|
+
* ```ts
|
|
3419
|
+
* import * as Sentry from '@sentry/node';
|
|
3420
|
+
* import { CronJob } from 'cron';
|
|
3421
|
+
*
|
|
3422
|
+
* const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job');
|
|
3423
|
+
*
|
|
3424
|
+
* // use the constructor
|
|
3425
|
+
* const job = new CronJobWithCheckIn('* * * * *', () => {
|
|
3426
|
+
* console.log('You will see this message every minute');
|
|
3427
|
+
* });
|
|
3428
|
+
*
|
|
3429
|
+
* // or from
|
|
3430
|
+
* const job = CronJobWithCheckIn.from({ cronTime: '* * * * *', onTick: () => {
|
|
3431
|
+
* console.log('You will see this message every minute');
|
|
3432
|
+
* });
|
|
3433
|
+
* ```
|
|
3434
|
+
*/
|
|
3435
|
+
function instrumentCron(lib, monitorSlug) {
|
|
3436
|
+
let jobScheduled = false;
|
|
3437
|
+
|
|
3438
|
+
return new Proxy(lib, {
|
|
3439
|
+
construct(target, args) {
|
|
3440
|
+
const [cronTime, onTick, onComplete, start, timeZone, ...rest] = args;
|
|
3441
|
+
|
|
3442
|
+
if (typeof cronTime !== 'string') {
|
|
3443
|
+
throw new Error(ERROR_TEXT);
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
if (jobScheduled) {
|
|
3447
|
+
throw new Error(`A job named '${monitorSlug}' has already been scheduled`);
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
jobScheduled = true;
|
|
3451
|
+
|
|
3452
|
+
const cronString = replaceCronNames(cronTime);
|
|
3453
|
+
|
|
3454
|
+
function monitoredTick(context, onComplete) {
|
|
3455
|
+
return withMonitor(
|
|
3456
|
+
monitorSlug,
|
|
3457
|
+
() => {
|
|
3458
|
+
return onTick(context, onComplete);
|
|
3459
|
+
},
|
|
3460
|
+
{
|
|
3461
|
+
schedule: { type: 'crontab', value: cronString },
|
|
3462
|
+
...(timeZone ? { timeZone } : {}),
|
|
3463
|
+
},
|
|
3464
|
+
);
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
return new target(cronTime, monitoredTick, onComplete, start, timeZone, ...rest);
|
|
3468
|
+
},
|
|
3469
|
+
get(target, prop) {
|
|
3470
|
+
if (prop === 'from') {
|
|
3471
|
+
return (param) => {
|
|
3472
|
+
const { cronTime, onTick, timeZone } = param;
|
|
3473
|
+
|
|
3474
|
+
if (typeof cronTime !== 'string') {
|
|
3475
|
+
throw new Error(ERROR_TEXT);
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
if (jobScheduled) {
|
|
3479
|
+
throw new Error(`A job named '${monitorSlug}' has already been scheduled`);
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
jobScheduled = true;
|
|
3483
|
+
|
|
3484
|
+
const cronString = replaceCronNames(cronTime);
|
|
3485
|
+
|
|
3486
|
+
param.onTick = (context, onComplete) => {
|
|
3487
|
+
return withMonitor(
|
|
3488
|
+
monitorSlug,
|
|
3489
|
+
() => {
|
|
3490
|
+
return onTick(context, onComplete);
|
|
3491
|
+
},
|
|
3492
|
+
{
|
|
3493
|
+
schedule: { type: 'crontab', value: cronString },
|
|
3494
|
+
...(timeZone ? { timeZone } : {}),
|
|
3495
|
+
},
|
|
3496
|
+
);
|
|
3497
|
+
};
|
|
3498
|
+
|
|
3499
|
+
return target.from(param);
|
|
3500
|
+
};
|
|
3501
|
+
} else {
|
|
3502
|
+
return target[prop];
|
|
3503
|
+
}
|
|
3504
|
+
},
|
|
3505
|
+
});
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
/**
|
|
3509
|
+
* Wraps the `node-cron` library with check-in monitoring.
|
|
3510
|
+
*
|
|
3511
|
+
* ```ts
|
|
3512
|
+
* import * as Sentry from "@sentry/node";
|
|
3513
|
+
* import cron from "node-cron";
|
|
3514
|
+
*
|
|
3515
|
+
* const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron);
|
|
3516
|
+
*
|
|
3517
|
+
* cronWithCheckIn.schedule(
|
|
3518
|
+
* "* * * * *",
|
|
3519
|
+
* () => {
|
|
3520
|
+
* console.log("running a task every minute");
|
|
3521
|
+
* },
|
|
3522
|
+
* { name: "my-cron-job" },
|
|
3523
|
+
* );
|
|
3524
|
+
* ```
|
|
3525
|
+
*/
|
|
3526
|
+
function instrumentNodeCron(lib) {
|
|
3527
|
+
return new Proxy(lib, {
|
|
3528
|
+
get(target, prop) {
|
|
3529
|
+
if (prop === 'schedule' && target.schedule) {
|
|
3530
|
+
// When 'get' is called for schedule, return a proxied version of the schedule function
|
|
3531
|
+
return new Proxy(target.schedule, {
|
|
3532
|
+
apply(target, thisArg, argArray) {
|
|
3533
|
+
const [expression, , options] = argArray;
|
|
3534
|
+
|
|
3535
|
+
if (!_optionalChain([options, 'optionalAccess', _ => _.name])) {
|
|
3536
|
+
throw new Error('Missing "name" for scheduled job. A name is required for Sentry check-in monitoring.');
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
return withMonitor(
|
|
3540
|
+
options.name,
|
|
3541
|
+
() => {
|
|
3542
|
+
return target.apply(thisArg, argArray);
|
|
3543
|
+
},
|
|
3544
|
+
{
|
|
3545
|
+
schedule: { type: 'crontab', value: replaceCronNames(expression) },
|
|
3546
|
+
timezone: _optionalChain([options, 'optionalAccess', _2 => _2.timezone]),
|
|
3547
|
+
},
|
|
3548
|
+
);
|
|
3549
|
+
},
|
|
3550
|
+
});
|
|
3551
|
+
} else {
|
|
3552
|
+
return target[prop];
|
|
3553
|
+
}
|
|
3554
|
+
},
|
|
3555
|
+
});
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
/**
|
|
3559
|
+
* Instruments the `node-schedule` library to send a check-in event to Sentry for each job execution.
|
|
3560
|
+
*
|
|
3561
|
+
* ```ts
|
|
3562
|
+
* import * as Sentry from '@sentry/node';
|
|
3563
|
+
* import * as schedule from 'node-schedule';
|
|
3564
|
+
*
|
|
3565
|
+
* const scheduleWithCheckIn = Sentry.cron.instrumentNodeSchedule(schedule);
|
|
3566
|
+
*
|
|
3567
|
+
* const job = scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * * *', () => {
|
|
3568
|
+
* console.log('You will see this message every minute');
|
|
3569
|
+
* });
|
|
3570
|
+
* ```
|
|
3571
|
+
*/
|
|
3572
|
+
function instrumentNodeSchedule(lib) {
|
|
3573
|
+
return new Proxy(lib, {
|
|
3574
|
+
get(target, prop) {
|
|
3575
|
+
if (prop === 'scheduleJob') {
|
|
3576
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
3577
|
+
return new Proxy(target.scheduleJob, {
|
|
3578
|
+
apply(target, thisArg, argArray) {
|
|
3579
|
+
const [nameOrExpression, expressionOrCallback] = argArray;
|
|
3580
|
+
|
|
3581
|
+
if (typeof nameOrExpression !== 'string' || typeof expressionOrCallback !== 'string') {
|
|
3582
|
+
throw new Error(
|
|
3583
|
+
"Automatic instrumentation of 'node-schedule' requires the first parameter of 'scheduleJob' to be a job name string and the second parameter to be a crontab string",
|
|
3584
|
+
);
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3587
|
+
const monitorSlug = nameOrExpression;
|
|
3588
|
+
const expression = expressionOrCallback;
|
|
3589
|
+
|
|
3590
|
+
return withMonitor(
|
|
3591
|
+
monitorSlug,
|
|
3592
|
+
() => {
|
|
3593
|
+
return target.apply(thisArg, argArray);
|
|
3594
|
+
},
|
|
3595
|
+
{
|
|
3596
|
+
schedule: { type: 'crontab', value: replaceCronNames(expression) },
|
|
3597
|
+
},
|
|
3598
|
+
);
|
|
3599
|
+
},
|
|
3600
|
+
});
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
return target[prop];
|
|
3604
|
+
},
|
|
3605
|
+
});
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
/** Methods to instrument cron libraries for Sentry check-ins */
|
|
3609
|
+
const cron = {
|
|
3610
|
+
instrumentCron,
|
|
3611
|
+
instrumentNodeCron,
|
|
3612
|
+
instrumentNodeSchedule,
|
|
3613
|
+
};
|
|
3614
|
+
|
|
3615
|
+
export { NodeClient, anrIntegration, consoleIntegration, contextLinesIntegration, createGetModuleFromFilename, cron, defaultStackParser, expressErrorHandler, expressIntegration, fastifyIntegration, getAutoPerformanceIntegrations, getClient, getCurrentHub, getDefaultIntegrations, getSentryRelease, graphqlIntegration, hapiIntegration, httpIntegration, init, localVariablesIntegration, makeMain, makeNodeTransport, modulesIntegration, mongoIntegration, mongooseIntegration, mysql2Integration, mysqlIntegration, nativeNodeFetchIntegration, nestIntegration, nodeContextIntegration, onUncaughtExceptionIntegration, onUnhandledRejectionIntegration, postgresIntegration, prismaIntegration, setupExpressErrorHandler, setupFastifyErrorHandler, setupHapiErrorHandler, spotlightIntegration };
|
|
33
3616
|
//# sourceMappingURL=index.js.map
|