@dbx-tools/teams 0.3.44 → 0.4.1

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.
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Configuration for the Teams plugin: the typed {@link TeamsPluginConfig}
3
+ * (the plugin's slice of AppKit config), the JSON Schema the manifest
4
+ * publishes for it, and {@link resolveTeamsConfig} which layers that config
5
+ * over environment defaults into the concrete {@link ResolvedTeamsConfig} the
6
+ * runtime + tools read.
7
+ *
8
+ * Building a card is a pure transform, so the config is deliberately small:
9
+ * the Adaptive Card version the builder targets, an optional Teams
10
+ * incoming-webhook URL a deployment can wire up to actually POST cards to a
11
+ * channel, and which sibling plugin owns the agents the conversation endpoint
12
+ * answers with.
13
+ *
14
+ * The remaining fields are the Azure Bot registration a real Teams channel
15
+ * needs: `appId` / `appPassword` (the bot's Entra application credentials, used
16
+ * both to validate the inbound JWT audience and to fetch the outbound Connector
17
+ * token) and `appTenantId` (set only for a single-tenant bot). When no `appId`
18
+ * is configured the `/messages` endpoint stays MOUNTED but refuses every
19
+ * request, so an unconfigured deployment cannot accidentally expose an
20
+ * unauthenticated bot endpoint.
21
+ *
22
+ * Env fallbacks: `TEAMS_CARD_VERSION`, `TEAMS_WEBHOOK_URL`,
23
+ * `TEAMS_AGENT_PLUGIN`, `TEAMS_APP_ID`, `TEAMS_APP_PASSWORD`,
24
+ * `TEAMS_APP_TENANT_ID`. The `MICROSOFT_APP_*` spellings the Bot Framework SDK
25
+ * uses are accepted as aliases so an existing bot's environment drops in
26
+ * unchanged.
27
+ *
28
+ * @module
29
+ */
30
+ import { type BasePluginConfig } from "@databricks/appkit";
31
+ import type { JSONSchema7 } from "json-schema";
32
+ /** Dedicated override for the Adaptive Card schema version the builder targets. */
33
+ export declare const CARD_VERSION_ENV = "TEAMS_CARD_VERSION";
34
+ /** Environment name for an optional Teams incoming-webhook URL. */
35
+ export declare const WEBHOOK_URL_ENV = "TEAMS_WEBHOOK_URL";
36
+ /** Environment name overriding which sibling plugin provides the agents. */
37
+ export declare const AGENT_PLUGIN_ENV = "TEAMS_AGENT_PLUGIN";
38
+ /**
39
+ * Environment names for the bot's Entra app id, in precedence order. The
40
+ * `MICROSOFT_APP_ID` alias is what the Bot Framework SDK and the Azure portal's
41
+ * generated settings use, so an existing bot deployment needs no new variable.
42
+ */
43
+ export declare const APP_ID_ENVS: readonly ["TEAMS_APP_ID", "MICROSOFT_APP_ID"];
44
+ /** Environment names for the bot's client secret, in precedence order. */
45
+ export declare const APP_PASSWORD_ENVS: readonly ["TEAMS_APP_PASSWORD", "MICROSOFT_APP_PASSWORD"];
46
+ /**
47
+ * Environment names for the bot's tenant, in precedence order. Set only for a
48
+ * single-tenant bot; a multi-tenant bot leaves it unset.
49
+ */
50
+ export declare const APP_TENANT_ENVS: readonly ["TEAMS_APP_TENANT_ID", "MICROSOFT_APP_TENANT_ID"];
51
+ /**
52
+ * Environment name for the unauthenticated-messaging escape hatch. Named
53
+ * `TEAMS_ALLOW_UNAUTHENTICATED` (rather than something softer like `TEAMS_DEV`)
54
+ * so what it disables is unmistakable in a shell history or deploy manifest.
55
+ */
56
+ export declare const ALLOW_UNAUTHENTICATED_ENV = "TEAMS_ALLOW_UNAUTHENTICATED";
57
+ /**
58
+ * Default registered name of the plugin the conversation endpoint asks for an
59
+ * agent. `mastra` is the name `@dbx-tools/appkit-mastra` registers under.
60
+ */
61
+ export declare const DEFAULT_AGENT_PLUGIN = "mastra";
62
+ /** AppKit config accepted by the Teams plugin. */
63
+ export interface TeamsPluginConfig extends BasePluginConfig {
64
+ /**
65
+ * Adaptive Card schema version the builder targets. Defaults to
66
+ * {@link card.ADAPTIVE_CARD_VERSION} (`"1.5"`, what Teams supports). Falls
67
+ * back to `TEAMS_CARD_VERSION`.
68
+ */
69
+ cardVersion?: string;
70
+ /**
71
+ * Optional Teams incoming-webhook URL. When set, the plugin's `postCard`
72
+ * export / route posts the compiled card to this channel; when unset,
73
+ * posting is disabled and the plugin only builds cards for the UI to render.
74
+ * Falls back to `TEAMS_WEBHOOK_URL`.
75
+ */
76
+ webhookUrl?: string;
77
+ /**
78
+ * Registered name of the sibling plugin whose agents answer a conversation
79
+ * turn. Defaults to {@link DEFAULT_AGENT_PLUGIN} (`"mastra"`); set it when the
80
+ * Mastra plugin is mounted under a `config.name` override. Falls back to
81
+ * `TEAMS_AGENT_PLUGIN`.
82
+ */
83
+ agentPlugin?: string;
84
+ /**
85
+ * The bot's Entra application (client) id from its Azure Bot registration.
86
+ * Required before `POST /api/teams/messages` will accept a request: it is the
87
+ * audience an inbound Bot Framework token must carry, and the client id used
88
+ * to fetch an outbound Connector token. Falls back to `TEAMS_APP_ID` /
89
+ * `MICROSOFT_APP_ID`.
90
+ */
91
+ appId?: string;
92
+ /**
93
+ * Client secret for {@link appId}. Needed to send replies through the
94
+ * Connector API; without it inbound activities still validate but the bot
95
+ * cannot answer. Falls back to `TEAMS_APP_PASSWORD` /
96
+ * `MICROSOFT_APP_PASSWORD`.
97
+ */
98
+ appPassword?: string;
99
+ /**
100
+ * Tenant id for a SINGLE-tenant bot registration. Leave unset for a
101
+ * multi-tenant bot. Falls back to `TEAMS_APP_TENANT_ID` /
102
+ * `MICROSOFT_APP_TENANT_ID`.
103
+ */
104
+ appTenantId?: string;
105
+ /**
106
+ * Serve `POST /messages` with NO inbound token validation, and reply in the
107
+ * HTTP response instead of through the Connector API.
108
+ *
109
+ * This exists so the messaging endpoint can be exercised locally - by the
110
+ * in-repo preview chat, a `curl`, or the Bot Framework Emulator - without an
111
+ * Azure Bot registration. It removes the endpoint's ONLY trust boundary: any
112
+ * caller that can reach the route can drive the agent and read its answers.
113
+ *
114
+ * Never enable it on a deployment reachable from the internet. It is ignored
115
+ * unless `NODE_ENV` is `development` (see {@link resolveTeamsConfig}), so a
116
+ * production build cannot be talked into it by an environment variable alone.
117
+ * Falls back to `TEAMS_ALLOW_UNAUTHENTICATED`.
118
+ */
119
+ allowUnauthenticated?: boolean;
120
+ }
121
+ /** The concrete config the runtime reads, after config + env resolution. */
122
+ export interface ResolvedTeamsConfig {
123
+ /** Adaptive Card schema version the builder stamps onto every document. */
124
+ cardVersion: string;
125
+ /** Teams incoming-webhook URL, or `undefined` when posting is disabled. */
126
+ webhookUrl?: string;
127
+ /** Registered name of the plugin the conversation endpoint resolves agents from. */
128
+ agentPlugin: string;
129
+ /** Bot app (client) id, or `undefined` when no bot registration is configured. */
130
+ appId?: string;
131
+ /** Bot client secret, or `undefined` when replies cannot be sent. */
132
+ appPassword?: string;
133
+ /** Tenant id for a single-tenant bot, or `undefined` for a multi-tenant one. */
134
+ appTenantId?: string;
135
+ /**
136
+ * Whether `POST /messages` serves turns with NO token validation. See
137
+ * {@link TeamsPluginConfig.allowUnauthenticated}.
138
+ */
139
+ allowUnauthenticated: boolean;
140
+ }
141
+ /** JSON Schema published in the plugin manifest for {@link TeamsPluginConfig}. */
142
+ export declare const TEAMS_CONFIG_SCHEMA: JSONSchema7;
143
+ /**
144
+ * Layer the plugin config over environment defaults into the concrete config
145
+ * the runtime uses. Fails loudly on a webhook URL that is present but not a
146
+ * valid absolute URL, since that is a deploy-time mistake.
147
+ */
148
+ export declare function resolveTeamsConfig(overrides?: TeamsPluginConfig): ResolvedTeamsConfig;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Configuration for the Teams plugin: the typed {@link TeamsPluginConfig}
3
+ * (the plugin's slice of AppKit config), the JSON Schema the manifest
4
+ * publishes for it, and {@link resolveTeamsConfig} which layers that config
5
+ * over environment defaults into the concrete {@link ResolvedTeamsConfig} the
6
+ * runtime + tools read.
7
+ *
8
+ * Building a card is a pure transform, so the config is deliberately small:
9
+ * the Adaptive Card version the builder targets, an optional Teams
10
+ * incoming-webhook URL a deployment can wire up to actually POST cards to a
11
+ * channel, and which sibling plugin owns the agents the conversation endpoint
12
+ * answers with.
13
+ *
14
+ * The remaining fields are the Azure Bot registration a real Teams channel
15
+ * needs: `appId` / `appPassword` (the bot's Entra application credentials, used
16
+ * both to validate the inbound JWT audience and to fetch the outbound Connector
17
+ * token) and `appTenantId` (set only for a single-tenant bot). When no `appId`
18
+ * is configured the `/messages` endpoint stays MOUNTED but refuses every
19
+ * request, so an unconfigured deployment cannot accidentally expose an
20
+ * unauthenticated bot endpoint.
21
+ *
22
+ * Env fallbacks: `TEAMS_CARD_VERSION`, `TEAMS_WEBHOOK_URL`,
23
+ * `TEAMS_AGENT_PLUGIN`, `TEAMS_APP_ID`, `TEAMS_APP_PASSWORD`,
24
+ * `TEAMS_APP_TENANT_ID`. The `MICROSOFT_APP_*` spellings the Bot Framework SDK
25
+ * uses are accepted as aliases so an existing bot's environment drops in
26
+ * unchanged.
27
+ *
28
+ * @module
29
+ */
30
+ import { ValidationError } from "@databricks/appkit";
31
+ import { object, string } from "@dbx-tools/shared-core";
32
+ import { card } from "@dbx-tools/shared-teams";
33
+ /** Dedicated override for the Adaptive Card schema version the builder targets. */
34
+ export const CARD_VERSION_ENV = "TEAMS_CARD_VERSION";
35
+ /** Environment name for an optional Teams incoming-webhook URL. */
36
+ export const WEBHOOK_URL_ENV = "TEAMS_WEBHOOK_URL";
37
+ /** Environment name overriding which sibling plugin provides the agents. */
38
+ export const AGENT_PLUGIN_ENV = "TEAMS_AGENT_PLUGIN";
39
+ /**
40
+ * Environment names for the bot's Entra app id, in precedence order. The
41
+ * `MICROSOFT_APP_ID` alias is what the Bot Framework SDK and the Azure portal's
42
+ * generated settings use, so an existing bot deployment needs no new variable.
43
+ */
44
+ export const APP_ID_ENVS = ["TEAMS_APP_ID", "MICROSOFT_APP_ID"];
45
+ /** Environment names for the bot's client secret, in precedence order. */
46
+ export const APP_PASSWORD_ENVS = ["TEAMS_APP_PASSWORD", "MICROSOFT_APP_PASSWORD"];
47
+ /**
48
+ * Environment names for the bot's tenant, in precedence order. Set only for a
49
+ * single-tenant bot; a multi-tenant bot leaves it unset.
50
+ */
51
+ export const APP_TENANT_ENVS = ["TEAMS_APP_TENANT_ID", "MICROSOFT_APP_TENANT_ID"];
52
+ /**
53
+ * Environment name for the unauthenticated-messaging escape hatch. Named
54
+ * `TEAMS_ALLOW_UNAUTHENTICATED` (rather than something softer like `TEAMS_DEV`)
55
+ * so what it disables is unmistakable in a shell history or deploy manifest.
56
+ */
57
+ export const ALLOW_UNAUTHENTICATED_ENV = "TEAMS_ALLOW_UNAUTHENTICATED";
58
+ /**
59
+ * Default registered name of the plugin the conversation endpoint asks for an
60
+ * agent. `mastra` is the name `@dbx-tools/appkit-mastra` registers under.
61
+ */
62
+ export const DEFAULT_AGENT_PLUGIN = "mastra";
63
+ /** JSON Schema published in the plugin manifest for {@link TeamsPluginConfig}. */
64
+ export const TEAMS_CONFIG_SCHEMA = {
65
+ type: "object",
66
+ additionalProperties: false,
67
+ properties: {
68
+ cardVersion: {
69
+ type: "string",
70
+ description: `Adaptive Card schema version the builder targets. ${CARD_VERSION_ENV} overrides it.`,
71
+ },
72
+ webhookUrl: {
73
+ type: "string",
74
+ description: `Optional Teams incoming-webhook URL used to post cards. ${WEBHOOK_URL_ENV} overrides it.`,
75
+ },
76
+ agentPlugin: {
77
+ type: "string",
78
+ description: `Registered name of the sibling plugin whose agents answer a conversation turn. ${AGENT_PLUGIN_ENV} overrides it.`,
79
+ },
80
+ appId: {
81
+ type: "string",
82
+ description: `Entra app (client) id of the Azure Bot registration. Required for the Teams messaging endpoint. ${APP_ID_ENVS[0]} overrides it.`,
83
+ },
84
+ appPassword: {
85
+ type: "string",
86
+ description: `Client secret for the bot app id, used to fetch an outbound Connector token. ${APP_PASSWORD_ENVS[0]} overrides it.`,
87
+ },
88
+ appTenantId: {
89
+ type: "string",
90
+ description: `Tenant id for a single-tenant bot registration; unset for multi-tenant. ${APP_TENANT_ENVS[0]} overrides it.`,
91
+ },
92
+ allowUnauthenticated: {
93
+ type: "boolean",
94
+ description: "Serve the Teams messaging endpoint with NO token validation, replying in " +
95
+ "the HTTP response. Local development only; ignored unless NODE_ENV is " +
96
+ `development. ${ALLOW_UNAUTHENTICATED_ENV} overrides it.`,
97
+ },
98
+ },
99
+ };
100
+ /**
101
+ * Layer the plugin config over environment defaults into the concrete config
102
+ * the runtime uses. Fails loudly on a webhook URL that is present but not a
103
+ * valid absolute URL, since that is a deploy-time mistake.
104
+ */
105
+ export function resolveTeamsConfig(overrides) {
106
+ const cardVersion = string.trimToNull(overrides?.cardVersion) ??
107
+ string.trimToNull(process.env[CARD_VERSION_ENV]) ??
108
+ card.ADAPTIVE_CARD_VERSION;
109
+ const webhookUrl = string.trimToNull(overrides?.webhookUrl) ?? string.trimToNull(process.env[WEBHOOK_URL_ENV]);
110
+ if (webhookUrl !== null && !isAbsoluteUrl(webhookUrl)) {
111
+ throw ValidationError.invalidValue(WEBHOOK_URL_ENV, webhookUrl, "an absolute https URL for the Teams webhook");
112
+ }
113
+ const agentPlugin = string.trimToNull(overrides?.agentPlugin) ??
114
+ string.trimToNull(process.env[AGENT_PLUGIN_ENV]) ??
115
+ DEFAULT_AGENT_PLUGIN;
116
+ // Two independent conditions must BOTH hold: the operator asked for it, and
117
+ // this is a development build. Gating on `NODE_ENV` as well means a stray
118
+ // variable in a production environment cannot silently expose the endpoint.
119
+ const requested = overrides?.allowUnauthenticated ?? object.toBoolean(process.env[ALLOW_UNAUTHENTICATED_ENV]);
120
+ const allowUnauthenticated = requested === true && process.env.NODE_ENV === "development";
121
+ return {
122
+ cardVersion,
123
+ agentPlugin,
124
+ allowUnauthenticated,
125
+ ...(webhookUrl !== null ? { webhookUrl } : {}),
126
+ ...spread("appId", string.trimToNull(overrides?.appId) ?? fromEnv(APP_ID_ENVS)),
127
+ ...spread("appPassword", string.trimToNull(overrides?.appPassword) ?? fromEnv(APP_PASSWORD_ENVS)),
128
+ ...spread("appTenantId", string.trimToNull(overrides?.appTenantId) ?? fromEnv(APP_TENANT_ENVS)),
129
+ };
130
+ }
131
+ /**
132
+ * First non-empty value among `names` in the environment, or `null`. Lets a
133
+ * `TEAMS_*` variable win over the `MICROSOFT_APP_*` alias the Bot Framework SDK
134
+ * uses without duplicating the lookup per field.
135
+ */
136
+ function fromEnv(names) {
137
+ for (const name of names) {
138
+ const value = string.trimToNull(process.env[name]);
139
+ if (value !== null)
140
+ return value;
141
+ }
142
+ return null;
143
+ }
144
+ /**
145
+ * `{ [key]: value }` when `value` is present, otherwise nothing - so an absent
146
+ * optional field stays ABSENT rather than becoming an explicit `undefined`,
147
+ * which `exactOptionalPropertyTypes` rejects.
148
+ */
149
+ function spread(key, value) {
150
+ return value === null ? undefined : { [key]: value };
151
+ }
152
+ /** Whether `value` parses as an absolute URL. */
153
+ function isAbsoluteUrl(value) {
154
+ try {
155
+ // eslint-disable-next-line no-new
156
+ new URL(value);
157
+ return true;
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTRCRztBQUVILE9BQU8sRUFBRSxlQUFlLEVBQXlCLE1BQU0sb0JBQW9CLENBQUM7QUFDNUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN4RCxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFHL0MsbUZBQW1GO0FBQ25GLE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLG9CQUFvQixDQUFDO0FBRXJELG1FQUFtRTtBQUNuRSxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsbUJBQW1CLENBQUM7QUFFbkQsNEVBQTRFO0FBQzVFLE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLG9CQUFvQixDQUFDO0FBRXJEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsQ0FBQyxjQUFjLEVBQUUsa0JBQWtCLENBQVUsQ0FBQztBQUV6RSwwRUFBMEU7QUFDMUUsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxvQkFBb0IsRUFBRSx3QkFBd0IsQ0FBVSxDQUFDO0FBRTNGOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxDQUFDLHFCQUFxQixFQUFFLHlCQUF5QixDQUFVLENBQUM7QUFFM0Y7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFHLDZCQUE2QixDQUFDO0FBRXZFOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLFFBQVEsQ0FBQztBQW1GN0Msa0ZBQWtGO0FBQ2xGLE1BQU0sQ0FBQyxNQUFNLG1CQUFtQixHQUFnQjtJQUM5QyxJQUFJLEVBQUUsUUFBUTtJQUNkLG9CQUFvQixFQUFFLEtBQUs7SUFDM0IsVUFB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eEU7UUFDRCxHQUFHLE1BQU0sQ0FBQyxhQUFhLEVBQUUsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLEVBQUUsV0FBVyxDQUFDLElBQUksT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0tBQ2hHLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQVMsT0FBTyxDQUFDLEtBQXdCO0lBQ3ZDLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7UUFDekIsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDbkQsSUFBSSxLQUFLLEtBQUssSUFBSTtZQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ25DLENBQUM7SUFDRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBUyxNQUFNLENBQW1CLEdBQU0sRUFBRSxLQUFvQjtJQUM1RCxPQUFPLEtBQUssS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEtBQUssRUFBd0IsQ0FBQztBQUM5RSxDQUFDO0FBRUQsaURBQWlEO0FBQ2pELFNBQVMsYUFBYSxDQUFDLEtBQWE7SUFDbEMsSUFBSSxDQUFDO1FBQ0gsa0NBQWtDO1FBQ2xDLElBQUksR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2YsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29uZmlndXJhdGlvbiBmb3IgdGhlIFRlYW1zIHBsdWdpbjogdGhlIHR5cGVkIHtAbGluayBUZWFtc1BsdWdpbkNvbmZpZ31cbiAqICh0aGUgcGx1Z2luJ3Mgc2xpY2Ugb2YgQXBwS2l0IGNvbmZpZyksIHRoZSBKU09OIFNjaGVtYSB0aGUgbWFuaWZlc3RcbiAqIHB1Ymxpc2hlcyBmb3IgaXQsIGFuZCB7QGxpbmsgcmVzb2x2ZVRlYW1zQ29uZmlnfSB3aGljaCBsYXllcnMgdGhhdCBjb25maWdcbiAqIG92ZXIgZW52aXJvbm1lbnQgZGVmYXVsdHMgaW50byB0aGUgY29uY3JldGUge0BsaW5rIFJlc29sdmVkVGVhbXNDb25maWd9IHRoZVxuICogcnVudGltZSArIHRvb2xzIHJlYWQuXG4gKlxuICogQnVpbGRpbmcgYSBjYXJkIGlzIGEgcHVyZSB0cmFuc2Zvcm0sIHNvIHRoZSBjb25maWcgaXMgZGVsaWJlcmF0ZWx5IHNtYWxsOlxuICogdGhlIEFkYXB0aXZlIENhcmQgdmVyc2lvbiB0aGUgYnVpbGRlciB0YXJnZXRzLCBhbiBvcHRpb25hbCBUZWFtc1xuICogaW5jb21pbmctd2ViaG9vayBVUkwgYSBkZXBsb3ltZW50IGNhbiB3aXJlIHVwIHRvIGFjdHVhbGx5IFBPU1QgY2FyZHMgdG8gYVxuICogY2hhbm5lbCwgYW5kIHdoaWNoIHNpYmxpbmcgcGx1Z2luIG93bnMgdGhlIGFnZW50cyB0aGUgY29udmVyc2F0aW9uIGVuZHBvaW50XG4gKiBhbnN3ZXJzIHdpdGguXG4gKlxuICogVGhlIHJlbWFpbmluZyBmaWVsZHMgYXJlIHRoZSBBenVyZSBCb3QgcmVnaXN0cmF0aW9uIGEgcmVhbCBUZWFtcyBjaGFubmVsXG4gKiBuZWVkczogYGFwcElkYCAvIGBhcHBQYXNzd29yZGAgKHRoZSBib3QncyBFbnRyYSBhcHBsaWNhdGlvbiBjcmVkZW50aWFscywgdXNlZFxuICogYm90aCB0byB2YWxpZGF0ZSB0aGUgaW5ib3VuZCBKV1QgYXVkaWVuY2UgYW5kIHRvIGZldGNoIHRoZSBvdXRib3VuZCBDb25uZWN0b3JcbiAqIHRva2VuKSBhbmQgYGFwcFRlbmFudElkYCAoc2V0IG9ubHkgZm9yIGEgc2luZ2xlLXRlbmFudCBib3QpLiBXaGVuIG5vIGBhcHBJZGBcbiAqIGlzIGNvbmZpZ3VyZWQgdGhlIGAvbWVzc2FnZXNgIGVuZHBvaW50IHN0YXlzIE1PVU5URUQgYnV0IHJlZnVzZXMgZXZlcnlcbiAqIHJlcXVlc3QsIHNvIGFuIHVuY29uZmlndXJlZCBkZXBsb3ltZW50IGNhbm5vdCBhY2NpZGVudGFsbHkgZXhwb3NlIGFuXG4gKiB1bmF1dGhlbnRpY2F0ZWQgYm90IGVuZHBvaW50LlxuICpcbiAqIEVudiBmYWxsYmFja3M6IGBURUFNU19DQVJEX1ZFUlNJT05gLCBgVEVBTVNfV0VCSE9PS19VUkxgLFxuICogYFRFQU1TX0FHRU5UX1BMVUdJTmAsIGBURUFNU19BUFBfSURgLCBgVEVBTVNfQVBQX1BBU1NXT1JEYCxcbiAqIGBURUFNU19BUFBfVEVOQU5UX0lEYC4gVGhlIGBNSUNST1NPRlRfQVBQXypgIHNwZWxsaW5ncyB0aGUgQm90IEZyYW1ld29yayBTREtcbiAqIHVzZXMgYXJlIGFjY2VwdGVkIGFzIGFsaWFzZXMgc28gYW4gZXhpc3RpbmcgYm90J3MgZW52aXJvbm1lbnQgZHJvcHMgaW5cbiAqIHVuY2hhbmdlZC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgVmFsaWRhdGlvbkVycm9yLCB0eXBlIEJhc2VQbHVnaW5Db25maWcgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQgeyBvYmplY3QsIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBjYXJkIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLXRlYW1zXCI7XG5pbXBvcnQgdHlwZSB7IEpTT05TY2hlbWE3IH0gZnJvbSBcImpzb24tc2NoZW1hXCI7XG5cbi8qKiBEZWRpY2F0ZWQgb3ZlcnJpZGUgZm9yIHRoZSBBZGFwdGl2ZSBDYXJkIHNjaGVtYSB2ZXJzaW9uIHRoZSBidWlsZGVyIHRhcmdldHMuICovXG5leHBvcnQgY29uc3QgQ0FSRF9WRVJTSU9OX0VOViA9IFwiVEVBTVNfQ0FSRF9WRVJTSU9OXCI7XG5cbi8qKiBFbnZpcm9ubWVudCBuYW1lIGZvciBhbiBvcHRpb25hbCBUZWFtcyBpbmNvbWluZy13ZWJob29rIFVSTC4gKi9cbmV4cG9ydCBjb25zdCBXRUJIT09LX1VSTF9FTlYgPSBcIlRFQU1TX1dFQkhPT0tfVVJMXCI7XG5cbi8qKiBFbnZpcm9ubWVudCBuYW1lIG92ZXJyaWRpbmcgd2hpY2ggc2libGluZyBwbHVnaW4gcHJvdmlkZXMgdGhlIGFnZW50cy4gKi9cbmV4cG9ydCBjb25zdCBBR0VOVF9QTFVHSU5fRU5WID0gXCJURUFNU19BR0VOVF9QTFVHSU5cIjtcblxuLyoqXG4gKiBFbnZpcm9ubWVudCBuYW1lcyBmb3IgdGhlIGJvdCdzIEVudHJhIGFwcCBpZCwgaW4gcHJlY2VkZW5jZSBvcmRlci4gVGhlXG4gKiBgTUlDUk9TT0ZUX0FQUF9JRGAgYWxpYXMgaXMgd2hhdCB0aGUgQm90IEZyYW1ld29yayBTREsgYW5kIHRoZSBBenVyZSBwb3J0YWwnc1xuICogZ2VuZXJhdGVkIHNldHRpbmdzIHVzZSwgc28gYW4gZXhpc3RpbmcgYm90IGRlcGxveW1lbnQgbmVlZHMgbm8gbmV3IHZhcmlhYmxlLlxuICovXG5leHBvcnQgY29uc3QgQVBQX0lEX0VOVlMgPSBbXCJURUFNU19BUFBfSURcIiwgXCJNSUNST1NPRlRfQVBQX0lEXCJdIGFzIGNvbnN0O1xuXG4vKiogRW52aXJvbm1lbnQgbmFtZXMgZm9yIHRoZSBib3QncyBjbGllbnQgc2VjcmV0LCBpbiBwcmVjZWRlbmNlIG9yZGVyLiAqL1xuZXhwb3J0IGNvbnN0IEFQUF9QQVNTV09SRF9FTlZTID0gW1wiVEVBTVNfQVBQX1BBU1NXT1JEXCIsIFwiTUlDUk9TT0ZUX0FQUF9QQVNTV09SRFwiXSBhcyBjb25zdDtcblxuLyoqXG4gKiBFbnZpcm9ubWVudCBuYW1lcyBmb3IgdGhlIGJvdCdzIHRlbmFudCwgaW4gcHJlY2VkZW5jZSBvcmRlci4gU2V0IG9ubHkgZm9yIGFcbiAqIHNpbmdsZS10ZW5hbnQgYm90OyBhIG11bHRpLXRlbmFudCBib3QgbGVhdmVzIGl0IHVuc2V0LlxuICovXG5leHBvcnQgY29uc3QgQVBQX1RFTkFOVF9FTlZTID0gW1wiVEVBTVNfQVBQX1RFTkFOVF9JRFwiLCBcIk1JQ1JPU09GVF9BUFBfVEVOQU5UX0lEXCJdIGFzIGNvbnN0O1xuXG4vKipcbiAqIEVudmlyb25tZW50IG5hbWUgZm9yIHRoZSB1bmF1dGhlbnRpY2F0ZWQtbWVzc2FnaW5nIGVzY2FwZSBoYXRjaC4gTmFtZWRcbiAqIGBURUFNU19BTExPV19VTkFVVEhFTlRJQ0FURURgIChyYXRoZXIgdGhhbiBzb21ldGhpbmcgc29mdGVyIGxpa2UgYFRFQU1TX0RFVmApXG4gKiBzbyB3aGF0IGl0IGRpc2FibGVzIGlzIHVubWlzdGFrYWJsZSBpbiBhIHNoZWxsIGhpc3Rvcnkgb3IgZGVwbG95IG1hbmlmZXN0LlxuICovXG5leHBvcnQgY29uc3QgQUxMT1dfVU5BVVRIRU5USUNBVEVEX0VOViA9IFwiVEVBTVNfQUxMT1dfVU5BVVRIRU5USUNBVEVEXCI7XG5cbi8qKlxuICogRGVmYXVsdCByZWdpc3RlcmVkIG5hbWUgb2YgdGhlIHBsdWdpbiB0aGUgY29udmVyc2F0aW9uIGVuZHBvaW50IGFza3MgZm9yIGFuXG4gKiBhZ2VudC4gYG1hc3RyYWAgaXMgdGhlIG5hbWUgYEBkYngtdG9vbHMvYXBwa2l0LW1hc3RyYWAgcmVnaXN0ZXJzIHVuZGVyLlxuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9BR0VOVF9QTFVHSU4gPSBcIm1hc3RyYVwiO1xuXG4vKiogQXBwS2l0IGNvbmZpZyBhY2NlcHRlZCBieSB0aGUgVGVhbXMgcGx1Z2luLiAqL1xuZXhwb3J0IGludGVyZmFjZSBUZWFtc1BsdWdpbkNvbmZpZyBleHRlbmRzIEJhc2VQbHVnaW5Db25maWcge1xuICAvKipcbiAgICogQWRhcHRpdmUgQ2FyZCBzY2hlbWEgdmVyc2lvbiB0aGUgYnVpbGRlciB0YXJnZXRzLiBEZWZhdWx0cyB0b1xuICAgKiB7QGxpbmsgY2FyZC5BREFQVElWRV9DQVJEX1ZFUlNJT059IChgXCIxLjVcImAsIHdoYXQgVGVhbXMgc3VwcG9ydHMpLiBGYWxsc1xuICAgKiBiYWNrIHRvIGBURUFNU19DQVJEX1ZFUlNJT05gLlxuICAgKi9cbiAgY2FyZFZlcnNpb24/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBPcHRpb25hbCBUZWFtcyBpbmNvbWluZy13ZWJob29rIFVSTC4gV2hlbiBzZXQsIHRoZSBwbHVnaW4ncyBgcG9zdENhcmRgXG4gICAqIGV4cG9ydCAvIHJvdXRlIHBvc3RzIHRoZSBjb21waWxlZCBjYXJkIHRvIHRoaXMgY2hhbm5lbDsgd2hlbiB1bnNldCxcbiAgICogcG9zdGluZyBpcyBkaXNhYmxlZCBhbmQgdGhlIHBsdWdpbiBvbmx5IGJ1aWxkcyBjYXJkcyBmb3IgdGhlIFVJIHRvIHJlbmRlci5cbiAgICogRmFsbHMgYmFjayB0byBgVEVBTVNfV0VCSE9PS19VUkxgLlxuICAgKi9cbiAgd2ViaG9va1VybD86IHN0cmluZztcbiAgLyoqXG4gICAqIFJlZ2lzdGVyZWQgbmFtZSBvZiB0aGUgc2libGluZyBwbHVnaW4gd2hvc2UgYWdlbnRzIGFuc3dlciBhIGNvbnZlcnNhdGlvblxuICAgKiB0dXJuLiBEZWZhdWx0cyB0byB7QGxpbmsgREVGQVVMVF9BR0VOVF9QTFVHSU59IChgXCJtYXN0cmFcImApOyBzZXQgaXQgd2hlbiB0aGVcbiAgICogTWFzdHJhIHBsdWdpbiBpcyBtb3VudGVkIHVuZGVyIGEgYGNvbmZpZy5uYW1lYCBvdmVycmlkZS4gRmFsbHMgYmFjayB0b1xuICAgKiBgVEVBTVNfQUdFTlRfUExVR0lOYC5cbiAgICovXG4gIGFnZW50UGx1Z2luPzogc3RyaW5nO1xuICAvKipcbiAgICogVGhlIGJvdCdzIEVudHJhIGFwcGxpY2F0aW9uIChjbGllbnQpIGlkIGZyb20gaXRzIEF6dXJlIEJvdCByZWdpc3RyYXRpb24uXG4gICAqIFJlcXVpcmVkIGJlZm9yZSBgUE9TVCAvYXBpL3RlYW1zL21lc3NhZ2VzYCB3aWxsIGFjY2VwdCBhIHJlcXVlc3Q6IGl0IGlzIHRoZVxuICAgKiBhdWRpZW5jZSBhbiBpbmJvdW5kIEJvdCBGcmFtZXdvcmsgdG9rZW4gbXVzdCBjYXJyeSwgYW5kIHRoZSBjbGllbnQgaWQgdXNlZFxuICAgKiB0byBmZXRjaCBhbiBvdXRib3VuZCBDb25uZWN0b3IgdG9rZW4uIEZhbGxzIGJhY2sgdG8gYFRFQU1TX0FQUF9JRGAgL1xuICAgKiBgTUlDUk9TT0ZUX0FQUF9JRGAuXG4gICAqL1xuICBhcHBJZD86IHN0cmluZztcbiAgLyoqXG4gICAqIENsaWVudCBzZWNyZXQgZm9yIHtAbGluayBhcHBJZH0uIE5lZWRlZCB0byBzZW5kIHJlcGxpZXMgdGhyb3VnaCB0aGVcbiAgICogQ29ubmVjdG9yIEFQSTsgd2l0aG91dCBpdCBpbmJvdW5kIGFjdGl2aXRpZXMgc3RpbGwgdmFsaWRhdGUgYnV0IHRoZSBib3RcbiAgICogY2Fubm90IGFuc3dlci4gRmFsbHMgYmFjayB0byBgVEVBTVNfQVBQX1BBU1NXT1JEYCAvXG4gICAqIGBNSUNST1NPRlRfQVBQX1BBU1NXT1JEYC5cbiAgICovXG4gIGFwcFBhc3N3b3JkPzogc3RyaW5nO1xuICAvKipcbiAgICogVGVuYW50IGlkIGZvciBhIFNJTkdMRS10ZW5hbnQgYm90IHJlZ2lzdHJhdGlvbi4gTGVhdmUgdW5zZXQgZm9yIGFcbiAgICogbXVsdGktdGVuYW50IGJvdC4gRmFsbHMgYmFjayB0byBgVEVBTVNfQVBQX1RFTkFOVF9JRGAgL1xuICAgKiBgTUlDUk9TT0ZUX0FQUF9URU5BTlRfSURgLlxuICAgKi9cbiAgYXBwVGVuYW50SWQ/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBTZXJ2ZSBgUE9TVCAvbWVzc2FnZXNgIHdpdGggTk8gaW5ib3VuZCB0b2tlbiB2YWxpZGF0aW9uLCBhbmQgcmVwbHkgaW4gdGhlXG4gICAqIEhUVFAgcmVzcG9uc2UgaW5zdGVhZCBvZiB0aHJvdWdoIHRoZSBDb25uZWN0b3IgQVBJLlxuICAgKlxuICAgKiBUaGlzIGV4aXN0cyBzbyB0aGUgbWVzc2FnaW5nIGVuZHBvaW50IGNhbiBiZSBleGVyY2lzZWQgbG9jYWxseSAtIGJ5IHRoZVxuICAgKiBpbi1yZXBvIHByZXZpZXcgY2hhdCwgYSBgY3VybGAsIG9yIHRoZSBCb3QgRnJhbWV3b3JrIEVtdWxhdG9yIC0gd2l0aG91dCBhblxuICAgKiBBenVyZSBCb3QgcmVnaXN0cmF0aW9uLiBJdCByZW1vdmVzIHRoZSBlbmRwb2ludCdzIE9OTFkgdHJ1c3QgYm91bmRhcnk6IGFueVxuICAgKiBjYWxsZXIgdGhhdCBjYW4gcmVhY2ggdGhlIHJvdXRlIGNhbiBkcml2ZSB0aGUgYWdlbnQgYW5kIHJlYWQgaXRzIGFuc3dlcnMuXG4gICAqXG4gICAqIE5ldmVyIGVuYWJsZSBpdCBvbiBhIGRlcGxveW1lbnQgcmVhY2hhYmxlIGZyb20gdGhlIGludGVybmV0LiBJdCBpcyBpZ25vcmVkXG4gICAqIHVubGVzcyBgTk9ERV9FTlZgIGlzIGBkZXZlbG9wbWVudGAgKHNlZSB7QGxpbmsgcmVzb2x2ZVRlYW1zQ29uZmlnfSksIHNvIGFcbiAgICogcHJvZHVjdGlvbiBidWlsZCBjYW5ub3QgYmUgdGFsa2VkIGludG8gaXQgYnkgYW4gZW52aXJvbm1lbnQgdmFyaWFibGUgYWxvbmUuXG4gICAqIEZhbGxzIGJhY2sgdG8gYFRFQU1TX0FMTE9XX1VOQVVUSEVOVElDQVRFRGAuXG4gICAqL1xuICBhbGxvd1VuYXV0aGVudGljYXRlZD86IGJvb2xlYW47XG59XG5cbi8qKiBUaGUgY29uY3JldGUgY29uZmlnIHRoZSBydW50aW1lIHJlYWRzLCBhZnRlciBjb25maWcgKyBlbnYgcmVzb2x1dGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb2x2ZWRUZWFtc0NvbmZpZyB7XG4gIC8qKiBBZGFwdGl2ZSBDYXJkIHNjaGVtYSB2ZXJzaW9uIHRoZSBidWlsZGVyIHN0YW1wcyBvbnRvIGV2ZXJ5IGRvY3VtZW50LiAqL1xuICBjYXJkVmVyc2lvbjogc3RyaW5nO1xuICAvKiogVGVhbXMgaW5jb21pbmctd2ViaG9vayBVUkwsIG9yIGB1bmRlZmluZWRgIHdoZW4gcG9zdGluZyBpcyBkaXNhYmxlZC4gKi9cbiAgd2ViaG9va1VybD86IHN0cmluZztcbiAgLyoqIFJlZ2lzdGVyZWQgbmFtZSBvZiB0aGUgcGx1Z2luIHRoZSBjb252ZXJzYXRpb24gZW5kcG9pbnQgcmVzb2x2ZXMgYWdlbnRzIGZyb20uICovXG4gIGFnZW50UGx1Z2luOiBzdHJpbmc7XG4gIC8qKiBCb3QgYXBwIChjbGllbnQpIGlkLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5vIGJvdCByZWdpc3RyYXRpb24gaXMgY29uZmlndXJlZC4gKi9cbiAgYXBwSWQ/OiBzdHJpbmc7XG4gIC8qKiBCb3QgY2xpZW50IHNlY3JldCwgb3IgYHVuZGVmaW5lZGAgd2hlbiByZXBsaWVzIGNhbm5vdCBiZSBzZW50LiAqL1xuICBhcHBQYXNzd29yZD86IHN0cmluZztcbiAgLyoqIFRlbmFudCBpZCBmb3IgYSBzaW5nbGUtdGVuYW50IGJvdCwgb3IgYHVuZGVmaW5lZGAgZm9yIGEgbXVsdGktdGVuYW50IG9uZS4gKi9cbiAgYXBwVGVuYW50SWQ/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBXaGV0aGVyIGBQT1NUIC9tZXNzYWdlc2Agc2VydmVzIHR1cm5zIHdpdGggTk8gdG9rZW4gdmFsaWRhdGlvbi4gU2VlXG4gICAqIHtAbGluayBUZWFtc1BsdWdpbkNvbmZpZy5hbGxvd1VuYXV0aGVudGljYXRlZH0uXG4gICAqL1xuICBhbGxvd1VuYXV0aGVudGljYXRlZDogYm9vbGVhbjtcbn1cblxuLyoqIEpTT04gU2NoZW1hIHB1Ymxpc2hlZCBpbiB0aGUgcGx1Z2luIG1hbmlmZXN0IGZvciB7QGxpbmsgVGVhbXNQbHVnaW5Db25maWd9LiAqL1xuZXhwb3J0IGNvbnN0IFRFQU1TX0NPTkZJR19TQ0hFTUE6IEpTT05TY2hlbWE3ID0ge1xuICB0eXBlOiBcIm9iamVjdFwiLFxuICBhZGRpdGlvbmFsUHJvcGVydGllczogZmFsc2UsXG4gIHByb3BlcnRpZXM6IHtcbiAgICBjYXJkVmVyc2lvbjoge1xuICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgIGRlc2NyaXB0aW9uOiBgQWRhcHRpdmUgQ2FyZCBzY2hlbWEgdmVyc2lvbiB0aGUgYnVpbGRlciB0YXJnZXRzLiAke0NBUkRfVkVSU0lPTl9FTlZ9IG92ZXJyaWRlcyBpdC5gLFxuICAgIH0sXG4gICAgd2ViaG9va1VybDoge1xuICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgIGRlc2NyaXB0aW9uOiBgT3B0aW9uYWwgVGVhbXMgaW5jb21pbmctd2ViaG9vayBVUkwgdXNlZCB0byBwb3N0IGNhcmRzLiAke1dFQkhPT0tfVVJMX0VOVn0gb3ZlcnJpZGVzIGl0LmAsXG4gICAgfSxcbiAgICBhZ2VudFBsdWdpbjoge1xuICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgIGRlc2NyaXB0aW9uOiBgUmVnaXN0ZXJlZCBuYW1lIG9mIHRoZSBzaWJsaW5nIHBsdWdpbiB3aG9zZSBhZ2VudHMgYW5zd2VyIGEgY29udmVyc2F0aW9uIHR1cm4uICR7QUdFTlRfUExVR0lOX0VOVn0gb3ZlcnJpZGVzIGl0LmAsXG4gICAgfSxcbiAgICBhcHBJZDoge1xuICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgIGRlc2NyaXB0aW9uOiBgRW50cmEgYXBwIChjbGllbnQpIGlkIG9mIHRoZSBBenVyZSBCb3QgcmVnaXN0cmF0aW9uLiBSZXF1aXJlZCBmb3IgdGhlIFRlYW1zIG1lc3NhZ2luZyBlbmRwb2ludC4gJHtBUFBfSURfRU5WU1swXX0gb3ZlcnJpZGVzIGl0LmAsXG4gICAgfSxcbiAgICBhcHBQYXNzd29yZDoge1xuICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgIGRlc2NyaXB0aW9uOiBgQ2xpZW50IHNlY3JldCBmb3IgdGhlIGJvdCBhcHAgaWQsIHVzZWQgdG8gZmV0Y2ggYW4gb3V0Ym91bmQgQ29ubmVjdG9yIHRva2VuLiAke0FQUF9QQVNTV09SRF9FTlZTWzBdfSBvdmVycmlkZXMgaXQuYCxcbiAgICB9LFxuICAgIGFwcFRlbmFudElkOiB7XG4gICAgICB0eXBlOiBcInN0cmluZ1wiLFxuICAgICAgZGVzY3JpcHRpb246IGBUZW5hbnQgaWQgZm9yIGEgc2luZ2xlLXRlbmFudCBib3QgcmVnaXN0cmF0aW9uOyB1bnNldCBmb3IgbXVsdGktdGVuYW50LiAke0FQUF9URU5BTlRfRU5WU1swXX0gb3ZlcnJpZGVzIGl0LmAsXG4gICAgfSxcbiAgICBhbGxvd1VuYXV0aGVudGljYXRlZDoge1xuICAgICAgdHlwZTogXCJib29sZWFuXCIsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgXCJTZXJ2ZSB0aGUgVGVhbXMgbWVzc2FnaW5nIGVuZHBvaW50IHdpdGggTk8gdG9rZW4gdmFsaWRhdGlvbiwgcmVwbHlpbmcgaW4gXCIgK1xuICAgICAgICBcInRoZSBIVFRQIHJlc3BvbnNlLiBMb2NhbCBkZXZlbG9wbWVudCBvbmx5OyBpZ25vcmVkIHVubGVzcyBOT0RFX0VOViBpcyBcIiArXG4gICAgICAgIGBkZXZlbG9wbWVudC4gJHtBTExPV19VTkFVVEhFTlRJQ0FURURfRU5WfSBvdmVycmlkZXMgaXQuYCxcbiAgICB9LFxuICB9LFxufTtcblxuLyoqXG4gKiBMYXllciB0aGUgcGx1Z2luIGNvbmZpZyBvdmVyIGVudmlyb25tZW50IGRlZmF1bHRzIGludG8gdGhlIGNvbmNyZXRlIGNvbmZpZ1xuICogdGhlIHJ1bnRpbWUgdXNlcy4gRmFpbHMgbG91ZGx5IG9uIGEgd2ViaG9vayBVUkwgdGhhdCBpcyBwcmVzZW50IGJ1dCBub3QgYVxuICogdmFsaWQgYWJzb2x1dGUgVVJMLCBzaW5jZSB0aGF0IGlzIGEgZGVwbG95LXRpbWUgbWlzdGFrZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVUZWFtc0NvbmZpZyhvdmVycmlkZXM/OiBUZWFtc1BsdWdpbkNvbmZpZyk6IFJlc29sdmVkVGVhbXNDb25maWcge1xuICBjb25zdCBjYXJkVmVyc2lvbiA9XG4gICAgc3RyaW5nLnRyaW1Ub051bGwob3ZlcnJpZGVzPy5jYXJkVmVyc2lvbikgPz9cbiAgICBzdHJpbmcudHJpbVRvTnVsbChwcm9jZXNzLmVudltDQVJEX1ZFUlNJT05fRU5WXSkgPz9cbiAgICBjYXJkLkFEQVBUSVZFX0NBUkRfVkVSU0lPTjtcbiAgY29uc3Qgd2ViaG9va1VybCA9XG4gICAgc3RyaW5nLnRyaW1Ub051bGwob3ZlcnJpZGVzPy53ZWJob29rVXJsKSA/PyBzdHJpbmcudHJpbVRvTnVsbChwcm9jZXNzLmVudltXRUJIT09LX1VSTF9FTlZdKTtcbiAgaWYgKHdlYmhvb2tVcmwgIT09IG51bGwgJiYgIWlzQWJzb2x1dGVVcmwod2ViaG9va1VybCkpIHtcbiAgICB0aHJvdyBWYWxpZGF0aW9uRXJyb3IuaW52YWxpZFZhbHVlKFxuICAgICAgV0VCSE9PS19VUkxfRU5WLFxuICAgICAgd2ViaG9va1VybCxcbiAgICAgIFwiYW4gYWJzb2x1dGUgaHR0cHMgVVJMIGZvciB0aGUgVGVhbXMgd2ViaG9va1wiLFxuICAgICk7XG4gIH1cbiAgY29uc3QgYWdlbnRQbHVnaW4gPVxuICAgIHN0cmluZy50cmltVG9OdWxsKG92ZXJyaWRlcz8uYWdlbnRQbHVnaW4pID8/XG4gICAgc3RyaW5nLnRyaW1Ub051bGwocHJvY2Vzcy5lbnZbQUdFTlRfUExVR0lOX0VOVl0pID8/XG4gICAgREVGQVVMVF9BR0VOVF9QTFVHSU47XG4gIC8vIFR3byBpbmRlcGVuZGVudCBjb25kaXRpb25zIG11c3QgQk9USCBob2xkOiB0aGUgb3BlcmF0b3IgYXNrZWQgZm9yIGl0LCBhbmRcbiAgLy8gdGhpcyBpcyBhIGRldmVsb3BtZW50IGJ1aWxkLiBHYXRpbmcgb24gYE5PREVfRU5WYCBhcyB3ZWxsIG1lYW5zIGEgc3RyYXlcbiAgLy8gdmFyaWFibGUgaW4gYSBwcm9kdWN0aW9uIGVudmlyb25tZW50IGNhbm5vdCBzaWxlbnRseSBleHBvc2UgdGhlIGVuZHBvaW50LlxuICBjb25zdCByZXF1ZXN0ZWQgPVxuICAgIG92ZXJyaWRlcz8uYWxsb3dVbmF1dGhlbnRpY2F0ZWQgPz8gb2JqZWN0LnRvQm9vbGVhbihwcm9jZXNzLmVudltBTExPV19VTkFVVEhFTlRJQ0FURURfRU5WXSk7XG4gIGNvbnN0IGFsbG93VW5hdXRoZW50aWNhdGVkID0gcmVxdWVzdGVkID09PSB0cnVlICYmIHByb2Nlc3MuZW52Lk5PREVfRU5WID09PSBcImRldmVsb3BtZW50XCI7XG4gIHJldHVybiB7XG4gICAgY2FyZFZlcnNpb24sXG4gICAgYWdlbnRQbHVnaW4sXG4gICAgYWxsb3dVbmF1dGhlbnRpY2F0ZWQsXG4gICAgLi4uKHdlYmhvb2tVcmwgIT09IG51bGwgPyB7IHdlYmhvb2tVcmwgfSA6IHt9KSxcbiAgICAuLi5zcHJlYWQoXCJhcHBJZFwiLCBzdHJpbmcudHJpbVRvTnVsbChvdmVycmlkZXM/LmFwcElkKSA/PyBmcm9tRW52KEFQUF9JRF9FTlZTKSksXG4gICAgLi4uc3ByZWFkKFxuICAgICAgXCJhcHBQYXNzd29yZFwiLFxuICAgICAgc3RyaW5nLnRyaW1Ub051bGwob3ZlcnJpZGVzPy5hcHBQYXNzd29yZCkgPz8gZnJvbUVudihBUFBfUEFTU1dPUkRfRU5WUyksXG4gICAgKSxcbiAgICAuLi5zcHJlYWQoXCJhcHBUZW5hbnRJZFwiLCBzdHJpbmcudHJpbVRvTnVsbChvdmVycmlkZXM/LmFwcFRlbmFudElkKSA/PyBmcm9tRW52KEFQUF9URU5BTlRfRU5WUykpLFxuICB9O1xufVxuXG4vKipcbiAqIEZpcnN0IG5vbi1lbXB0eSB2YWx1ZSBhbW9uZyBgbmFtZXNgIGluIHRoZSBlbnZpcm9ubWVudCwgb3IgYG51bGxgLiBMZXRzIGFcbiAqIGBURUFNU18qYCB2YXJpYWJsZSB3aW4gb3ZlciB0aGUgYE1JQ1JPU09GVF9BUFBfKmAgYWxpYXMgdGhlIEJvdCBGcmFtZXdvcmsgU0RLXG4gKiB1c2VzIHdpdGhvdXQgZHVwbGljYXRpbmcgdGhlIGxvb2t1cCBwZXIgZmllbGQuXG4gKi9cbmZ1bmN0aW9uIGZyb21FbnYobmFtZXM6IHJlYWRvbmx5IHN0cmluZ1tdKTogc3RyaW5nIHwgbnVsbCB7XG4gIGZvciAoY29uc3QgbmFtZSBvZiBuYW1lcykge1xuICAgIGNvbnN0IHZhbHVlID0gc3RyaW5nLnRyaW1Ub051bGwocHJvY2Vzcy5lbnZbbmFtZV0pO1xuICAgIGlmICh2YWx1ZSAhPT0gbnVsbCkgcmV0dXJuIHZhbHVlO1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIGB7IFtrZXldOiB2YWx1ZSB9YCB3aGVuIGB2YWx1ZWAgaXMgcHJlc2VudCwgb3RoZXJ3aXNlIG5vdGhpbmcgLSBzbyBhbiBhYnNlbnRcbiAqIG9wdGlvbmFsIGZpZWxkIHN0YXlzIEFCU0VOVCByYXRoZXIgdGhhbiBiZWNvbWluZyBhbiBleHBsaWNpdCBgdW5kZWZpbmVkYCxcbiAqIHdoaWNoIGBleGFjdE9wdGlvbmFsUHJvcGVydHlUeXBlc2AgcmVqZWN0cy5cbiAqL1xuZnVuY3Rpb24gc3ByZWFkPEsgZXh0ZW5kcyBzdHJpbmc+KGtleTogSywgdmFsdWU6IHN0cmluZyB8IG51bGwpOiBSZWNvcmQ8Sywgc3RyaW5nPiB8IHVuZGVmaW5lZCB7XG4gIHJldHVybiB2YWx1ZSA9PT0gbnVsbCA/IHVuZGVmaW5lZCA6ICh7IFtrZXldOiB2YWx1ZSB9IGFzIFJlY29yZDxLLCBzdHJpbmc+KTtcbn1cblxuLyoqIFdoZXRoZXIgYHZhbHVlYCBwYXJzZXMgYXMgYW4gYWJzb2x1dGUgVVJMLiAqL1xuZnVuY3Rpb24gaXNBYnNvbHV0ZVVybCh2YWx1ZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHRyeSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLW5ld1xuICAgIG5ldyBVUkwodmFsdWUpO1xuICAgIHJldHVybiB0cnVlO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Bot Framework Connector API client - how a reply actually reaches Teams.
3
+ *
4
+ * This is the part that makes the difference between a chat API and a bot. Azure
5
+ * Bot Service does NOT read replies from the body of the `/messages` response:
6
+ * it expects `200` (an acknowledgement that the activity was accepted) and then
7
+ * a separate, authenticated call back to the `serviceUrl` the activity arrived
8
+ * with. So a turn is inherently asynchronous - acknowledge, think, then deliver.
9
+ *
10
+ * Two calls are modelled, both `POST` to the conversation's activity collection:
11
+ *
12
+ * - {@link sendActivity} - `POST /v3/conversations/{id}/activities/{replyToId}`
13
+ * delivers a reply threaded under the user's message.
14
+ * - {@link sendTyping} - the same call with a `typing` activity, which is what
15
+ * puts the "…" indicator in the channel while the agent works.
16
+ *
17
+ * `serviceUrl` is always supplied by the CALLER from a validated source (see
18
+ * `isAllowedServiceUrl`), never read straight off an untrusted request body:
19
+ * these calls carry the bot's bearer token, so the destination host is a
20
+ * security-relevant input.
21
+ *
22
+ * @module
23
+ */
24
+ import type { activity as activityContract } from "@dbx-tools/shared-teams";
25
+ /** Everything needed to address one Connector call. */
26
+ export interface ConnectorTarget {
27
+ /** Base URL the activity arrived from, e.g. `https://smba.trafficmanager.net/amer/`. */
28
+ serviceUrl: string;
29
+ /** Conversation the reply belongs to. */
30
+ conversationId: string;
31
+ /** Bearer token for the bot's app registration. */
32
+ token: string;
33
+ /** Activity the reply threads under, when replying to a specific message. */
34
+ replyToId?: string;
35
+ /** Cancels the request with the turn. */
36
+ signal?: AbortSignal;
37
+ }
38
+ /**
39
+ * Deliver one activity to a conversation through the Connector API.
40
+ *
41
+ * Returns the id the channel assigned the posted activity, when it reports one -
42
+ * useful for a later update/delete, and for correlating logs with what a user
43
+ * sees in the channel.
44
+ */
45
+ export declare const sendActivity: (activity: activityContract.Activity, target: ConnectorTarget) => Promise<string | undefined>;
46
+ /**
47
+ * Show the typing indicator in the conversation.
48
+ *
49
+ * Best-effort by design: a failed indicator must never fail the turn, because
50
+ * the card that follows is the actual answer. A failure is logged at debug and
51
+ * swallowed.
52
+ */
53
+ export declare const sendTyping: (target: ConnectorTarget) => Promise<void>;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Bot Framework Connector API client - how a reply actually reaches Teams.
3
+ *
4
+ * This is the part that makes the difference between a chat API and a bot. Azure
5
+ * Bot Service does NOT read replies from the body of the `/messages` response:
6
+ * it expects `200` (an acknowledgement that the activity was accepted) and then
7
+ * a separate, authenticated call back to the `serviceUrl` the activity arrived
8
+ * with. So a turn is inherently asynchronous - acknowledge, think, then deliver.
9
+ *
10
+ * Two calls are modelled, both `POST` to the conversation's activity collection:
11
+ *
12
+ * - {@link sendActivity} - `POST /v3/conversations/{id}/activities/{replyToId}`
13
+ * delivers a reply threaded under the user's message.
14
+ * - {@link sendTyping} - the same call with a `typing` activity, which is what
15
+ * puts the "…" indicator in the channel while the agent works.
16
+ *
17
+ * `serviceUrl` is always supplied by the CALLER from a validated source (see
18
+ * `isAllowedServiceUrl`), never read straight off an untrusted request body:
19
+ * these calls carry the bot's bearer token, so the destination host is a
20
+ * security-relevant input.
21
+ *
22
+ * @module
23
+ */
24
+ import { log } from "@dbx-tools/shared-core";
25
+ const logger = log.logger("teams:connector");
26
+ /**
27
+ * Build the activities URL for a conversation.
28
+ *
29
+ * `replyToId` selects the threaded form, which is what makes a reply appear
30
+ * attached to the user's message rather than as a loose channel post.
31
+ */
32
+ const activitiesUrl = (target) => {
33
+ const base = target.serviceUrl.replace(/\/+$/, "");
34
+ const conversation = encodeURIComponent(target.conversationId);
35
+ const suffix = target.replyToId ? `/${encodeURIComponent(target.replyToId)}` : "";
36
+ return `${base}/v3/conversations/${conversation}/activities${suffix}`;
37
+ };
38
+ /**
39
+ * Deliver one activity to a conversation through the Connector API.
40
+ *
41
+ * Returns the id the channel assigned the posted activity, when it reports one -
42
+ * useful for a later update/delete, and for correlating logs with what a user
43
+ * sees in the channel.
44
+ */
45
+ export const sendActivity = async (activity, target) => {
46
+ const response = await fetch(activitiesUrl(target), {
47
+ method: "POST",
48
+ headers: {
49
+ "content-type": "application/json",
50
+ authorization: `Bearer ${target.token}`,
51
+ },
52
+ body: JSON.stringify(activity),
53
+ ...(target.signal ? { signal: target.signal } : {}),
54
+ });
55
+ if (!response.ok) {
56
+ const detail = await response.text().catch(() => "");
57
+ throw new Error(`teams: connector rejected the activity (${response.status}) ${detail}`.trim());
58
+ }
59
+ const payload = (await response.json().catch(() => null));
60
+ const id = typeof payload?.id === "string" ? payload.id : undefined;
61
+ logger.debug("activity delivered", { conversation: target.conversationId, id });
62
+ return id;
63
+ };
64
+ /**
65
+ * Show the typing indicator in the conversation.
66
+ *
67
+ * Best-effort by design: a failed indicator must never fail the turn, because
68
+ * the card that follows is the actual answer. A failure is logged at debug and
69
+ * swallowed.
70
+ */
71
+ export const sendTyping = async (target) => {
72
+ try {
73
+ await sendActivity({ type: "typing" }, target);
74
+ }
75
+ catch (err) {
76
+ logger.debug("typing indicator failed", {
77
+ conversation: target.conversationId,
78
+ error: err.message,
79
+ });
80
+ }
81
+ };
82
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2Nvbm5lY3Rvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQUVILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUc3QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7QUFnQjdDOzs7OztHQUtHO0FBQ0gsTUFBTSxhQUFhLEdBQUcsQ0FBQyxNQUF1QixFQUFVLEVBQUU7SUFDeEQsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ25ELE1BQU0sWUFBWSxHQUFHLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUMvRCxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDbEYsT0FBTyxHQUFHLElBQUkscUJBQXFCLFlBQVksY0FBYyxNQUFNLEVBQUUsQ0FBQztBQUN4RSxDQUFDLENBQUM7QUFFRjs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUcsS0FBSyxFQUMvQixRQUFtQyxFQUNuQyxNQUF1QixFQUNNLEVBQUU7SUFDL0IsTUFBTSxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxFQUFFO1FBQ2xELE1BQU0sRUFBRSxNQUFNO1FBQ2QsT0FBTyxFQUFFO1lBQ1AsY0FBYyxFQUFFLGtCQUFrQjtZQUNsQyxhQUFhLEVBQUUsVUFBVSxNQUFNLENBQUMsS0FBSyxFQUFFO1NBQ3hDO1FBQ0QsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDO1FBQzlCLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztLQUNwRCxDQUFDLENBQUM7SUFDSCxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ2pCLE1BQU0sTUFBTSxHQUFHLE1BQU0sUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxRQUFRLENBQUMsTUFBTSxLQUFLLE1BQU0sRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDbEcsQ0FBQztJQUNELE1BQU0sT0FBTyxHQUFHLENBQUMsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUE0QixDQUFDO0lBQ3JGLE1BQU0sRUFBRSxHQUFHLE9BQU8sT0FBTyxFQUFFLEVBQUUsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNwRSxNQUFNLENBQUMsS0FBSyxDQUFDLG9CQUFvQixFQUFFLEVBQUUsWUFBWSxFQUFFLE1BQU0sQ0FBQyxjQUFjLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNoRixPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUMsQ0FBQztBQUVGOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxLQUFLLEVBQUUsTUFBdUIsRUFBaUIsRUFBRTtJQUN6RSxJQUFJLENBQUM7UUFDSCxNQUFNLFlBQVksQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE1BQU0sQ0FBQyxLQUFLLENBQUMseUJBQXlCLEVBQUU7WUFDdEMsWUFBWSxFQUFFLE1BQU0sQ0FBQyxjQUFjO1lBQ25DLEtBQUssRUFBRyxHQUFhLENBQUMsT0FBTztTQUM5QixDQUFDLENBQUM7SUFDTCxDQUFDO0FBQ0gsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBCb3QgRnJhbWV3b3JrIENvbm5lY3RvciBBUEkgY2xpZW50IC0gaG93IGEgcmVwbHkgYWN0dWFsbHkgcmVhY2hlcyBUZWFtcy5cbiAqXG4gKiBUaGlzIGlzIHRoZSBwYXJ0IHRoYXQgbWFrZXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGNoYXQgQVBJIGFuZCBhIGJvdC4gQXp1cmVcbiAqIEJvdCBTZXJ2aWNlIGRvZXMgTk9UIHJlYWQgcmVwbGllcyBmcm9tIHRoZSBib2R5IG9mIHRoZSBgL21lc3NhZ2VzYCByZXNwb25zZTpcbiAqIGl0IGV4cGVjdHMgYDIwMGAgKGFuIGFja25vd2xlZGdlbWVudCB0aGF0IHRoZSBhY3Rpdml0eSB3YXMgYWNjZXB0ZWQpIGFuZCB0aGVuXG4gKiBhIHNlcGFyYXRlLCBhdXRoZW50aWNhdGVkIGNhbGwgYmFjayB0byB0aGUgYHNlcnZpY2VVcmxgIHRoZSBhY3Rpdml0eSBhcnJpdmVkXG4gKiB3aXRoLiBTbyBhIHR1cm4gaXMgaW5oZXJlbnRseSBhc3luY2hyb25vdXMgLSBhY2tub3dsZWRnZSwgdGhpbmssIHRoZW4gZGVsaXZlci5cbiAqXG4gKiBUd28gY2FsbHMgYXJlIG1vZGVsbGVkLCBib3RoIGBQT1NUYCB0byB0aGUgY29udmVyc2F0aW9uJ3MgYWN0aXZpdHkgY29sbGVjdGlvbjpcbiAqXG4gKiAgIC0ge0BsaW5rIHNlbmRBY3Rpdml0eX0gLSBgUE9TVCAvdjMvY29udmVyc2F0aW9ucy97aWR9L2FjdGl2aXRpZXMve3JlcGx5VG9JZH1gXG4gKiAgICAgZGVsaXZlcnMgYSByZXBseSB0aHJlYWRlZCB1bmRlciB0aGUgdXNlcidzIG1lc3NhZ2UuXG4gKiAgIC0ge0BsaW5rIHNlbmRUeXBpbmd9IC0gdGhlIHNhbWUgY2FsbCB3aXRoIGEgYHR5cGluZ2AgYWN0aXZpdHksIHdoaWNoIGlzIHdoYXRcbiAqICAgICBwdXRzIHRoZSBcIuKAplwiIGluZGljYXRvciBpbiB0aGUgY2hhbm5lbCB3aGlsZSB0aGUgYWdlbnQgd29ya3MuXG4gKlxuICogYHNlcnZpY2VVcmxgIGlzIGFsd2F5cyBzdXBwbGllZCBieSB0aGUgQ0FMTEVSIGZyb20gYSB2YWxpZGF0ZWQgc291cmNlIChzZWVcbiAqIGBpc0FsbG93ZWRTZXJ2aWNlVXJsYCksIG5ldmVyIHJlYWQgc3RyYWlnaHQgb2ZmIGFuIHVudHJ1c3RlZCByZXF1ZXN0IGJvZHk6XG4gKiB0aGVzZSBjYWxscyBjYXJyeSB0aGUgYm90J3MgYmVhcmVyIHRva2VuLCBzbyB0aGUgZGVzdGluYXRpb24gaG9zdCBpcyBhXG4gKiBzZWN1cml0eS1yZWxldmFudCBpbnB1dC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB0eXBlIHsgYWN0aXZpdHkgYXMgYWN0aXZpdHlDb250cmFjdCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC10ZWFtc1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwidGVhbXM6Y29ubmVjdG9yXCIpO1xuXG4vKiogRXZlcnl0aGluZyBuZWVkZWQgdG8gYWRkcmVzcyBvbmUgQ29ubmVjdG9yIGNhbGwuICovXG5leHBvcnQgaW50ZXJmYWNlIENvbm5lY3RvclRhcmdldCB7XG4gIC8qKiBCYXNlIFVSTCB0aGUgYWN0aXZpdHkgYXJyaXZlZCBmcm9tLCBlLmcuIGBodHRwczovL3NtYmEudHJhZmZpY21hbmFnZXIubmV0L2FtZXIvYC4gKi9cbiAgc2VydmljZVVybDogc3RyaW5nO1xuICAvKiogQ29udmVyc2F0aW9uIHRoZSByZXBseSBiZWxvbmdzIHRvLiAqL1xuICBjb252ZXJzYXRpb25JZDogc3RyaW5nO1xuICAvKiogQmVhcmVyIHRva2VuIGZvciB0aGUgYm90J3MgYXBwIHJlZ2lzdHJhdGlvbi4gKi9cbiAgdG9rZW46IHN0cmluZztcbiAgLyoqIEFjdGl2aXR5IHRoZSByZXBseSB0aHJlYWRzIHVuZGVyLCB3aGVuIHJlcGx5aW5nIHRvIGEgc3BlY2lmaWMgbWVzc2FnZS4gKi9cbiAgcmVwbHlUb0lkPzogc3RyaW5nO1xuICAvKiogQ2FuY2VscyB0aGUgcmVxdWVzdCB3aXRoIHRoZSB0dXJuLiAqL1xuICBzaWduYWw/OiBBYm9ydFNpZ25hbDtcbn1cblxuLyoqXG4gKiBCdWlsZCB0aGUgYWN0aXZpdGllcyBVUkwgZm9yIGEgY29udmVyc2F0aW9uLlxuICpcbiAqIGByZXBseVRvSWRgIHNlbGVjdHMgdGhlIHRocmVhZGVkIGZvcm0sIHdoaWNoIGlzIHdoYXQgbWFrZXMgYSByZXBseSBhcHBlYXJcbiAqIGF0dGFjaGVkIHRvIHRoZSB1c2VyJ3MgbWVzc2FnZSByYXRoZXIgdGhhbiBhcyBhIGxvb3NlIGNoYW5uZWwgcG9zdC5cbiAqL1xuY29uc3QgYWN0aXZpdGllc1VybCA9ICh0YXJnZXQ6IENvbm5lY3RvclRhcmdldCk6IHN0cmluZyA9PiB7XG4gIGNvbnN0IGJhc2UgPSB0YXJnZXQuc2VydmljZVVybC5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpO1xuICBjb25zdCBjb252ZXJzYXRpb24gPSBlbmNvZGVVUklDb21wb25lbnQodGFyZ2V0LmNvbnZlcnNhdGlvbklkKTtcbiAgY29uc3Qgc3VmZml4ID0gdGFyZ2V0LnJlcGx5VG9JZCA/IGAvJHtlbmNvZGVVUklDb21wb25lbnQodGFyZ2V0LnJlcGx5VG9JZCl9YCA6IFwiXCI7XG4gIHJldHVybiBgJHtiYXNlfS92My9jb252ZXJzYXRpb25zLyR7Y29udmVyc2F0aW9ufS9hY3Rpdml0aWVzJHtzdWZmaXh9YDtcbn07XG5cbi8qKlxuICogRGVsaXZlciBvbmUgYWN0aXZpdHkgdG8gYSBjb252ZXJzYXRpb24gdGhyb3VnaCB0aGUgQ29ubmVjdG9yIEFQSS5cbiAqXG4gKiBSZXR1cm5zIHRoZSBpZCB0aGUgY2hhbm5lbCBhc3NpZ25lZCB0aGUgcG9zdGVkIGFjdGl2aXR5LCB3aGVuIGl0IHJlcG9ydHMgb25lIC1cbiAqIHVzZWZ1bCBmb3IgYSBsYXRlciB1cGRhdGUvZGVsZXRlLCBhbmQgZm9yIGNvcnJlbGF0aW5nIGxvZ3Mgd2l0aCB3aGF0IGEgdXNlclxuICogc2VlcyBpbiB0aGUgY2hhbm5lbC5cbiAqL1xuZXhwb3J0IGNvbnN0IHNlbmRBY3Rpdml0eSA9IGFzeW5jIChcbiAgYWN0aXZpdHk6IGFjdGl2aXR5Q29udHJhY3QuQWN0aXZpdHksXG4gIHRhcmdldDogQ29ubmVjdG9yVGFyZ2V0LFxuKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+ID0+IHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChhY3Rpdml0aWVzVXJsKHRhcmdldCksIHtcbiAgICBtZXRob2Q6IFwiUE9TVFwiLFxuICAgIGhlYWRlcnM6IHtcbiAgICAgIFwiY29udGVudC10eXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiLFxuICAgICAgYXV0aG9yaXphdGlvbjogYEJlYXJlciAke3RhcmdldC50b2tlbn1gLFxuICAgIH0sXG4gICAgYm9keTogSlNPTi5zdHJpbmdpZnkoYWN0aXZpdHkpLFxuICAgIC4uLih0YXJnZXQuc2lnbmFsID8geyBzaWduYWw6IHRhcmdldC5zaWduYWwgfSA6IHt9KSxcbiAgfSk7XG4gIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICBjb25zdCBkZXRhaWwgPSBhd2FpdCByZXNwb25zZS50ZXh0KCkuY2F0Y2goKCkgPT4gXCJcIik7XG4gICAgdGhyb3cgbmV3IEVycm9yKGB0ZWFtczogY29ubmVjdG9yIHJlamVjdGVkIHRoZSBhY3Rpdml0eSAoJHtyZXNwb25zZS5zdGF0dXN9KSAke2RldGFpbH1gLnRyaW0oKSk7XG4gIH1cbiAgY29uc3QgcGF5bG9hZCA9IChhd2FpdCByZXNwb25zZS5qc29uKCkuY2F0Y2goKCkgPT4gbnVsbCkpIGFzIHsgaWQ/OiB1bmtub3duIH0gfCBudWxsO1xuICBjb25zdCBpZCA9IHR5cGVvZiBwYXlsb2FkPy5pZCA9PT0gXCJzdHJpbmdcIiA/IHBheWxvYWQuaWQgOiB1bmRlZmluZWQ7XG4gIGxvZ2dlci5kZWJ1ZyhcImFjdGl2aXR5IGRlbGl2ZXJlZFwiLCB7IGNvbnZlcnNhdGlvbjogdGFyZ2V0LmNvbnZlcnNhdGlvbklkLCBpZCB9KTtcbiAgcmV0dXJuIGlkO1xufTtcblxuLyoqXG4gKiBTaG93IHRoZSB0eXBpbmcgaW5kaWNhdG9yIGluIHRoZSBjb252ZXJzYXRpb24uXG4gKlxuICogQmVzdC1lZmZvcnQgYnkgZGVzaWduOiBhIGZhaWxlZCBpbmRpY2F0b3IgbXVzdCBuZXZlciBmYWlsIHRoZSB0dXJuLCBiZWNhdXNlXG4gKiB0aGUgY2FyZCB0aGF0IGZvbGxvd3MgaXMgdGhlIGFjdHVhbCBhbnN3ZXIuIEEgZmFpbHVyZSBpcyBsb2dnZWQgYXQgZGVidWcgYW5kXG4gKiBzd2FsbG93ZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBzZW5kVHlwaW5nID0gYXN5bmMgKHRhcmdldDogQ29ubmVjdG9yVGFyZ2V0KTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIHRyeSB7XG4gICAgYXdhaXQgc2VuZEFjdGl2aXR5KHsgdHlwZTogXCJ0eXBpbmdcIiB9LCB0YXJnZXQpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2dnZXIuZGVidWcoXCJ0eXBpbmcgaW5kaWNhdG9yIGZhaWxlZFwiLCB7XG4gICAgICBjb252ZXJzYXRpb246IHRhcmdldC5jb252ZXJzYXRpb25JZCxcbiAgICAgIGVycm9yOiAoZXJyIGFzIEVycm9yKS5tZXNzYWdlLFxuICAgIH0pO1xuICB9XG59O1xuIl19