@dbx-tools/databricks 0.3.44 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * as cloud from "./src/cloud.js";
2
+ export * as net from "./src/net.js";
3
+ export * as workspace from "./src/workspace.js";
4
+ export { RANGE_CACHE_TTL_MS, CloudProvider, resolveCloudLocation, loadProviderRanges } from "./src/cloud.js";
5
+ export type { CloudLocation } from "./src/cloud.js";
6
+ export { getPublicIp, resolveHostIps } from "./src/net.js";
7
+ export { getWorkspaceUrl, getWorkspaceId } from "./src/workspace.js";
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+ export * as cloud from "./src/cloud.js";
5
+ export * as net from "./src/net.js";
6
+ export * as workspace from "./src/workspace.js";
7
+ export { RANGE_CACHE_TTL_MS, CloudProvider, resolveCloudLocation, loadProviderRanges } from "./src/cloud.js";
8
+ export { getPublicIp, resolveHostIps } from "./src/net.js";
9
+ export { getWorkspaceUrl, getWorkspaceId } from "./src/workspace.js";
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssS0FBSyxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEtBQUssR0FBRyxNQUFNLFdBQVcsQ0FBQztBQUNqQyxPQUFPLEtBQUssU0FBUyxNQUFNLGlCQUFpQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxhQUFhLEVBQUUsb0JBQW9CLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFMUcsT0FBTyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDeEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCAqIGFzIGNsb3VkIGZyb20gXCIuL3NyYy9jbG91ZFwiO1xuZXhwb3J0ICogYXMgbmV0IGZyb20gXCIuL3NyYy9uZXRcIjtcbmV4cG9ydCAqIGFzIHdvcmtzcGFjZSBmcm9tIFwiLi9zcmMvd29ya3NwYWNlXCI7XG5leHBvcnQgeyBSQU5HRV9DQUNIRV9UVExfTVMsIENsb3VkUHJvdmlkZXIsIHJlc29sdmVDbG91ZExvY2F0aW9uLCBsb2FkUHJvdmlkZXJSYW5nZXMgfSBmcm9tIFwiLi9zcmMvY2xvdWRcIjtcbmV4cG9ydCB0eXBlIHsgQ2xvdWRMb2NhdGlvbiB9IGZyb20gXCIuL3NyYy9jbG91ZFwiO1xuZXhwb3J0IHsgZ2V0UHVibGljSXAsIHJlc29sdmVIb3N0SXBzIH0gZnJvbSBcIi4vc3JjL25ldFwiO1xuZXhwb3J0IHsgZ2V0V29ya3NwYWNlVXJsLCBnZXRXb3Jrc3BhY2VJZCB9IGZyb20gXCIuL3NyYy93b3Jrc3BhY2VcIjtcbiJdfQ==
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Best-effort geolocation of a host to the cloud provider and region it
3
+ * runs in. Resolves the host to its IP address(es) via DNS, then matches
4
+ * those addresses against the public IP-range feeds each hyperscaler
5
+ * publishes (AWS, Azure, GCP). Handy for placing a Databricks workspace
6
+ * URL, but works for any host.
7
+ *
8
+ * The range feeds are large and change slowly, so each provider's feed
9
+ * is cached for {@link RANGE_CACHE_TTL_MS} (24 hours) at two layers: an
10
+ * on-disk copy under the OS temp dir (survives process restarts, shared
11
+ * across processes) plus an in-process memoized parse. A provider whose
12
+ * feed fails to load is skipped rather than failing the whole lookup -
13
+ * the other providers still answer.
14
+ *
15
+ * Server-only: DNS resolution needs `node:dns` (via `./net.ts`), the
16
+ * disk cache needs `node:fs` / `node:os` / `node:path`, and the feeds
17
+ * are fetched with the global `fetch`.
18
+ *
19
+ * @module
20
+ */
21
+ import { net } from "@dbx-tools/shared-core";
22
+ /** How long a fetched provider IP-range feed is reused before refetch. */
23
+ export declare const RANGE_CACHE_TTL_MS: number;
24
+ /** Cloud hyperscaler a host resolves into. */
25
+ export declare enum CloudProvider {
26
+ Aws = "aws",
27
+ Azure = "azure",
28
+ Gcp = "gcp"
29
+ }
30
+ /**
31
+ * Where a host lives: the {@link CloudProvider}, the provider-native
32
+ * `region` string (whatever the provider's feed calls it, e.g.
33
+ * `"us-east-1"` on AWS, `"eastus2"` on Azure, `"us-east1"` on GCP), the
34
+ * resolved `ip` that matched, and the `cidr` block it fell in.
35
+ */
36
+ export interface CloudLocation {
37
+ provider: CloudProvider;
38
+ region: string;
39
+ ip: string;
40
+ cidr: string;
41
+ }
42
+ /** A parsed CIDR block tagged with the region it belongs to. */
43
+ interface RegionCidr extends net.Cidr {
44
+ region: string;
45
+ }
46
+ /** One provider's parsed, region-tagged range table. */
47
+ interface ProviderRanges {
48
+ provider: CloudProvider;
49
+ ranges: RegionCidr[];
50
+ }
51
+ /**
52
+ * Resolve `input`'s host to a {@link CloudLocation}, or `null` when the
53
+ * host can't be resolved or none of its IPs match a known cloud range.
54
+ * `input` is any {@link net.UrlLike} - a URL, a bare host, or a `{ url }`
55
+ * wrapper.
56
+ *
57
+ * The provider range feeds are cached for {@link RANGE_CACHE_TTL_MS}, so
58
+ * only the first call in a 24-hour window pays the fetch cost; the DNS
59
+ * lookup happens on every call.
60
+ *
61
+ * @example
62
+ * await resolveCloudLocation("https://adb-1234567890.7.azuredatabricks.net");
63
+ * // { provider: CloudProvider.Azure,
64
+ * // region: "eastus2", ip: "...", cidr: "..." }
65
+ *
66
+ * await resolveCloudLocation("dbc-abc123.cloud.databricks.com");
67
+ * // { provider: CloudProvider.Aws,
68
+ * // region: "us-west-2", ip: "...", cidr: "..." }
69
+ */
70
+ export declare function resolveCloudLocation(input: net.UrlLike): Promise<CloudLocation | null>;
71
+ /**
72
+ * Load every provider's region-tagged range table, each cached for
73
+ * {@link RANGE_CACHE_TTL_MS}. Providers are loaded in parallel and a
74
+ * feed that fails to fetch or parse is dropped (logged, not thrown) so
75
+ * a single flaky feed never sinks the whole lookup. Exposed for callers
76
+ * that want to match many IPs against one cached snapshot without a DNS
77
+ * step per address.
78
+ */
79
+ export declare function loadProviderRanges(): Promise<ProviderRanges[]>;
80
+ export {};
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Best-effort geolocation of a host to the cloud provider and region it
3
+ * runs in. Resolves the host to its IP address(es) via DNS, then matches
4
+ * those addresses against the public IP-range feeds each hyperscaler
5
+ * publishes (AWS, Azure, GCP). Handy for placing a Databricks workspace
6
+ * URL, but works for any host.
7
+ *
8
+ * The range feeds are large and change slowly, so each provider's feed
9
+ * is cached for {@link RANGE_CACHE_TTL_MS} (24 hours) at two layers: an
10
+ * on-disk copy under the OS temp dir (survives process restarts, shared
11
+ * across processes) plus an in-process memoized parse. A provider whose
12
+ * feed fails to load is skipped rather than failing the whole lookup -
13
+ * the other providers still answer.
14
+ *
15
+ * Server-only: DNS resolution needs `node:dns` (via `./net.ts`), the
16
+ * disk cache needs `node:fs` / `node:os` / `node:path`, and the feeds
17
+ * are fetched with the global `fetch`.
18
+ *
19
+ * @module
20
+ */
21
+ import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
22
+ import { tmpdir } from "node:os";
23
+ import { dirname, join } from "node:path";
24
+ import { file } from "@dbx-tools/core";
25
+ import { error, functionModule, hash, http, log, net } from "@dbx-tools/shared-core";
26
+ import { resolveHostIps } from "./net.js";
27
+ const logger = log.logger("cloud");
28
+ /** How long a fetched provider IP-range feed is reused before refetch. */
29
+ export const RANGE_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
30
+ /** AWS publishes a single stable, unauthenticated ranges feed. */
31
+ const AWS_RANGES_URL = "https://ip-ranges.amazonaws.com/ip-ranges.json";
32
+ /** GCP publishes a single stable, unauthenticated ranges feed. */
33
+ const GCP_RANGES_URL = "https://www.gstatic.com/ipranges/cloud.json";
34
+ /**
35
+ * Azure has no stable feed URL - the JSON download link (with a
36
+ * rotating date stamp) lives on this human-facing download page and
37
+ * must be scraped out. See {@link fetchAzureRanges}.
38
+ */
39
+ const AZURE_DOWNLOAD_PAGE = "https://www.microsoft.com/en-us/download/details.aspx?id=56519";
40
+ /** Regex that plucks the current `ServiceTags_Public_<date>.json` link off the Azure page. */
41
+ const AZURE_JSON_LINK = /https:\/\/download\.microsoft\.com\/download\/[^"']*ServiceTags_Public_\d+\.json/;
42
+ /** Cloud hyperscaler a host resolves into. */
43
+ export var CloudProvider;
44
+ (function (CloudProvider) {
45
+ CloudProvider["Aws"] = "aws";
46
+ CloudProvider["Azure"] = "azure";
47
+ CloudProvider["Gcp"] = "gcp";
48
+ })(CloudProvider || (CloudProvider = {}));
49
+ /**
50
+ * Resolve `input`'s host to a {@link CloudLocation}, or `null` when the
51
+ * host can't be resolved or none of its IPs match a known cloud range.
52
+ * `input` is any {@link net.UrlLike} - a URL, a bare host, or a `{ url }`
53
+ * wrapper.
54
+ *
55
+ * The provider range feeds are cached for {@link RANGE_CACHE_TTL_MS}, so
56
+ * only the first call in a 24-hour window pays the fetch cost; the DNS
57
+ * lookup happens on every call.
58
+ *
59
+ * @example
60
+ * await resolveCloudLocation("https://adb-1234567890.7.azuredatabricks.net");
61
+ * // { provider: CloudProvider.Azure,
62
+ * // region: "eastus2", ip: "...", cidr: "..." }
63
+ *
64
+ * await resolveCloudLocation("dbc-abc123.cloud.databricks.com");
65
+ * // { provider: CloudProvider.Aws,
66
+ * // region: "us-west-2", ip: "...", cidr: "..." }
67
+ */
68
+ export async function resolveCloudLocation(input) {
69
+ const ips = await resolveHostIps(input);
70
+ if (ips.length === 0) {
71
+ logger.debug("no ips resolved", { input: String(input) });
72
+ return null;
73
+ }
74
+ const providers = await loadProviderRanges();
75
+ for (const ip of ips) {
76
+ const parsed = net.parseIp(ip);
77
+ if (!parsed)
78
+ continue;
79
+ for (const { provider, ranges } of providers) {
80
+ const match = net.findContainingCidr(parsed, ranges);
81
+ if (match) {
82
+ return { provider, region: match.region, ip, cidr: match.cidr };
83
+ }
84
+ }
85
+ }
86
+ logger.debug("no cloud range matched", { ips });
87
+ return null;
88
+ }
89
+ /**
90
+ * Load every provider's region-tagged range table, each cached for
91
+ * {@link RANGE_CACHE_TTL_MS}. Providers are loaded in parallel and a
92
+ * feed that fails to fetch or parse is dropped (logged, not thrown) so
93
+ * a single flaky feed never sinks the whole lookup. Exposed for callers
94
+ * that want to match many IPs against one cached snapshot without a DNS
95
+ * step per address.
96
+ */
97
+ export async function loadProviderRanges() {
98
+ const settled = await Promise.allSettled([loadAwsRanges(), loadAzureRanges(), loadGcpRanges()]);
99
+ const out = [];
100
+ for (const result of settled) {
101
+ if (result.status === "fulfilled")
102
+ out.push(result.value);
103
+ else
104
+ logger.warn("provider range load failed", { error: error.errorMessage(result.reason) });
105
+ }
106
+ return out;
107
+ }
108
+ // ────────────────────────────────────────────────────────────────
109
+ // Per-provider cached loaders
110
+ // ────────────────────────────────────────────────────────────────
111
+ const loadAwsRanges = functionModule.memoize(fetchAwsRanges, { ttlMs: RANGE_CACHE_TTL_MS });
112
+ const loadAzureRanges = functionModule.memoize(fetchAzureRanges, { ttlMs: RANGE_CACHE_TTL_MS });
113
+ const loadGcpRanges = functionModule.memoize(fetchGcpRanges, { ttlMs: RANGE_CACHE_TTL_MS });
114
+ /**
115
+ * AWS `ip-ranges.json`: a flat list of IPv4 (`prefixes`) and IPv6
116
+ * (`ipv6_prefixes`) blocks, each already tagged with a `region` such
117
+ * as `"us-east-1"` (or `"GLOBAL"` for edge ranges).
118
+ */
119
+ async function fetchAwsRanges() {
120
+ const feed = await fetchJson(AWS_RANGES_URL);
121
+ const ranges = [];
122
+ for (const entry of feed.prefixes ?? []) {
123
+ addRange(ranges, entry.ip_prefix, entry.region);
124
+ }
125
+ for (const entry of feed.ipv6_prefixes ?? []) {
126
+ addRange(ranges, entry.ipv6_prefix, entry.region);
127
+ }
128
+ logger.debug("loaded aws ranges", { count: ranges.length });
129
+ return { provider: CloudProvider.Aws, ranges };
130
+ }
131
+ /**
132
+ * GCP `cloud.json`: a list of blocks each carrying an `ipv4Prefix` or
133
+ * `ipv6Prefix` and a `scope` that is the region (`"us-east1"`) or
134
+ * `"global"` for non-regional ranges.
135
+ */
136
+ async function fetchGcpRanges() {
137
+ const feed = await fetchJson(GCP_RANGES_URL);
138
+ const ranges = [];
139
+ for (const entry of feed.prefixes ?? []) {
140
+ addRange(ranges, entry.ipv4Prefix ?? entry.ipv6Prefix, entry.scope);
141
+ }
142
+ logger.debug("loaded gcp ranges", { count: ranges.length });
143
+ return { provider: CloudProvider.Gcp, ranges };
144
+ }
145
+ /**
146
+ * Azure service tags: Microsoft ships no stable feed URL, so scrape the
147
+ * current `ServiceTags_Public_<date>.json` link off the download page
148
+ * and fetch it. Only the regional `AzureCloud.<region>` aggregates
149
+ * (which have a non-empty `region`) are kept; the cloud-wide
150
+ * `AzureCloud` tag and per-service tags are ignored so an address maps
151
+ * cleanly to one region.
152
+ */
153
+ async function fetchAzureRanges() {
154
+ const page = await fetchText(AZURE_DOWNLOAD_PAGE);
155
+ const jsonUrl = page.match(AZURE_JSON_LINK)?.[0];
156
+ if (!jsonUrl) {
157
+ throw new Error("could not find ServiceTags JSON link on Azure download page");
158
+ }
159
+ const feed = await fetchJson(jsonUrl);
160
+ const ranges = [];
161
+ for (const value of feed.values ?? []) {
162
+ const region = value.properties?.region;
163
+ if (!region || !value.name?.startsWith("AzureCloud."))
164
+ continue;
165
+ for (const prefix of value.properties?.addressPrefixes ?? []) {
166
+ addRange(ranges, prefix, region);
167
+ }
168
+ }
169
+ logger.debug("loaded azure ranges", { count: ranges.length });
170
+ return { provider: CloudProvider.Azure, ranges };
171
+ }
172
+ // ────────────────────────────────────────────────────────────────
173
+ // Helpers
174
+ // ────────────────────────────────────────────────────────────────
175
+ /** Parse `cidr` and, when valid and `region` is set, push a tagged range. */
176
+ function addRange(ranges, cidr, region) {
177
+ if (!cidr || !region)
178
+ return;
179
+ const parsed = net.parseCidr(cidr);
180
+ if (parsed)
181
+ ranges.push({ ...parsed, region });
182
+ }
183
+ async function fetchJson(url) {
184
+ const text = await fetchText(url);
185
+ return JSON.parse(text);
186
+ }
187
+ /**
188
+ * Fetch `url` as text with a 24h on-disk cache under the OS temp dir
189
+ * (keyed by an FNV hash of the URL). A fresh cache file is returned
190
+ * directly; a miss/expiry fetches, writes to a unique temp file, and
191
+ * atomically renames it into place so concurrent callers never observe
192
+ * a half-written cache entry.
193
+ */
194
+ async function fetchText(url) {
195
+ const cacheDir = join(tmpdir(), "dbx-tools", "shared", "cloud");
196
+ const cacheDirCreated = await mkdir(cacheDir, { recursive: true });
197
+ const cachePath = join(cacheDir, `fetch-${hash.fnvHash(url)}.txt`);
198
+ const createdAt = cacheDirCreated ? undefined : await getCreated(cachePath);
199
+ if (createdAt) {
200
+ const expiresAt = new Date(createdAt.getTime() + RANGE_CACHE_TTL_MS);
201
+ if (expiresAt > new Date()) {
202
+ logger.debug("cached fetch hit", { url, cachePath });
203
+ return readFile(cachePath, "utf8");
204
+ }
205
+ }
206
+ let tempPath = join(cacheDir, `${hash.id()}.txt`);
207
+ try {
208
+ const response = await fetch(url);
209
+ if (!response.ok) {
210
+ throw await http.createFetchError(response, url);
211
+ }
212
+ const responseText = await response.text();
213
+ await mkdir(dirname(tempPath), { recursive: true });
214
+ await writeFile(tempPath, responseText);
215
+ await rename(tempPath, cachePath);
216
+ logger.debug("cached fetch load", { url, cachePath });
217
+ tempPath = null;
218
+ return responseText;
219
+ }
220
+ finally {
221
+ if (tempPath) {
222
+ await unlink(tempPath);
223
+ }
224
+ }
225
+ }
226
+ /** Birth time of `path`, or `undefined` when it doesn't exist yet. */
227
+ async function getCreated(path) {
228
+ return (await file.statSync(path))?.birthtime;
229
+ }
230
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xvdWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFFSCxPQUFPLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDakMsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFMUMsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxLQUFLLEVBQUUsY0FBYyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFFdkMsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUVuQywwRUFBMEU7QUFDMUUsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBRXRELGtFQUFrRTtBQUNsRSxNQUFNLGNBQWMsR0FBRyxnREFBZ0QsQ0FBQztBQUN4RSxrRUFBa0U7QUFDbEUsTUFBTSxjQUFjLEdBQUcsNkNBQTZDLENBQUM7QUFDckU7Ozs7R0FJRztBQUNILE1BQU0sbUJBQW1CLEdBQUcsZ0VBQWdFLENBQUM7QUFDN0YsOEZBQThGO0FBQzlGLE1BQU0sZUFBZSxHQUNuQixrRkFBa0YsQ0FBQztBQUVyRiw4Q0FBOEM7QUFDOUMsTUFBTSxDQUFOLElBQVksYUFJWDtBQUpELFdBQVksYUFBYTtJQUN2Qiw0QkFBVyxDQUFBO0lBQ1gsZ0NBQWUsQ0FBQTtJ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QUFDO0FBQ2hELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEJlc3QtZWZmb3J0IGdlb2xvY2F0aW9uIG9mIGEgaG9zdCB0byB0aGUgY2xvdWQgcHJvdmlkZXIgYW5kIHJlZ2lvbiBpdFxuICogcnVucyBpbi4gUmVzb2x2ZXMgdGhlIGhvc3QgdG8gaXRzIElQIGFkZHJlc3MoZXMpIHZpYSBETlMsIHRoZW4gbWF0Y2hlc1xuICogdGhvc2UgYWRkcmVzc2VzIGFnYWluc3QgdGhlIHB1YmxpYyBJUC1yYW5nZSBmZWVkcyBlYWNoIGh5cGVyc2NhbGVyXG4gKiBwdWJsaXNoZXMgKEFXUywgQXp1cmUsIEdDUCkuIEhhbmR5IGZvciBwbGFjaW5nIGEgRGF0YWJyaWNrcyB3b3Jrc3BhY2VcbiAqIFVSTCwgYnV0IHdvcmtzIGZvciBhbnkgaG9zdC5cbiAqXG4gKiBUaGUgcmFuZ2UgZmVlZHMgYXJlIGxhcmdlIGFuZCBjaGFuZ2Ugc2xvd2x5LCBzbyBlYWNoIHByb3ZpZGVyJ3MgZmVlZFxuICogaXMgY2FjaGVkIGZvciB7QGxpbmsgUkFOR0VfQ0FDSEVfVFRMX01TfSAoMjQgaG91cnMpIGF0IHR3byBsYXllcnM6IGFuXG4gKiBvbi1kaXNrIGNvcHkgdW5kZXIgdGhlIE9TIHRlbXAgZGlyIChzdXJ2aXZlcyBwcm9jZXNzIHJlc3RhcnRzLCBzaGFyZWRcbiAqIGFjcm9zcyBwcm9jZXNzZXMpIHBsdXMgYW4gaW4tcHJvY2VzcyBtZW1vaXplZCBwYXJzZS4gQSBwcm92aWRlciB3aG9zZVxuICogZmVlZCBmYWlscyB0byBsb2FkIGlzIHNraXBwZWQgcmF0aGVyIHRoYW4gZmFpbGluZyB0aGUgd2hvbGUgbG9va3VwIC1cbiAqIHRoZSBvdGhlciBwcm92aWRlcnMgc3RpbGwgYW5zd2VyLlxuICpcbiAqIFNlcnZlci1vbmx5OiBETlMgcmVzb2x1dGlvbiBuZWVkcyBgbm9kZTpkbnNgICh2aWEgYC4vbmV0LnRzYCksIHRoZVxuICogZGlzayBjYWNoZSBuZWVkcyBgbm9kZTpmc2AgLyBgbm9kZTpvc2AgLyBgbm9kZTpwYXRoYCwgYW5kIHRoZSBmZWVkc1xuICogYXJlIGZldGNoZWQgd2l0aCB0aGUgZ2xvYmFsIGBmZXRjaGAuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IG1rZGlyLCByZWFkRmlsZSwgcmVuYW1lLCB1bmxpbmssIHdyaXRlRmlsZSB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgeyB0bXBkaXIgfSBmcm9tIFwibm9kZTpvc1wiO1xuaW1wb3J0IHsgZGlybmFtZSwgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcblxuaW1wb3J0IHsgZmlsZSB9IGZyb20gXCJAZGJ4LXRvb2xzL2NvcmVcIjtcbmltcG9ydCB7IGVycm9yLCBmdW5jdGlvbk1vZHVsZSwgaGFzaCwgaHR0cCwgbG9nLCBuZXQgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgcmVzb2x2ZUhvc3RJcHMgfSBmcm9tIFwiLi9uZXRcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImNsb3VkXCIpO1xuXG4vKiogSG93IGxvbmcgYSBmZXRjaGVkIHByb3ZpZGVyIElQLXJhbmdlIGZlZWQgaXMgcmV1c2VkIGJlZm9yZSByZWZldGNoLiAqL1xuZXhwb3J0IGNvbnN0IFJBTkdFX0NBQ0hFX1RUTF9NUyA9IDI0ICogNjAgKiA2MCAqIDEwMDA7XG5cbi8qKiBBV1MgcHVibGlzaGVzIGEgc2luZ2xlIHN0YWJsZSwgdW5hdXRoZW50aWNhdGVkIHJhbmdlcyBmZWVkLiAqL1xuY29uc3QgQVdTX1JBTkdFU19VUkwgPSBcImh0dHBzOi8vaXAtcmFuZ2VzLmFtYXpvbmF3cy5jb20vaXAtcmFuZ2VzLmpzb25cIjtcbi8qKiBHQ1AgcHVibGlzaGVzIGEgc2luZ2xlIHN0YWJsZSwgdW5hdXRoZW50aWNhdGVkIHJhbmdlcyBmZWVkLiAqL1xuY29uc3QgR0NQX1JBTkdFU19VUkwgPSBcImh0dHBzOi8vd3d3LmdzdGF0aWMuY29tL2lwcmFuZ2VzL2Nsb3VkLmpzb25cIjtcbi8qKlxuICogQXp1cmUgaGFzIG5vIHN0YWJsZSBmZWVkIFVSTCAtIHRoZSBKU09OIGRvd25sb2FkIGxpbmsgKHdpdGggYVxuICogcm90YXRpbmcgZGF0ZSBzdGFtcCkgbGl2ZXMgb24gdGhpcyBodW1hbi1mYWNpbmcgZG93bmxvYWQgcGFnZSBhbmRcbiAqIG11c3QgYmUgc2NyYXBlZCBvdXQuIFNlZSB7QGxpbmsgZmV0Y2hBenVyZVJhbmdlc30uXG4gKi9cbmNvbnN0IEFaVVJFX0RPV05MT0FEX1BBR0UgPSBcImh0dHBzOi8vd3d3Lm1pY3Jvc29mdC5jb20vZW4tdXMvZG93bmxvYWQvZGV0YWlscy5hc3B4P2lkPTU2NTE5XCI7XG4vKiogUmVnZXggdGhhdCBwbHVja3MgdGhlIGN1cnJlbnQgYFNlcnZpY2VUYWdzX1B1YmxpY188ZGF0ZT4uanNvbmAgbGluayBvZmYgdGhlIEF6dXJlIHBhZ2UuICovXG5jb25zdCBBWlVSRV9KU09OX0xJTksgPVxuICAvaHR0cHM6XFwvXFwvZG93bmxvYWRcXC5taWNyb3NvZnRcXC5jb21cXC9kb3dubG9hZFxcL1teXCInXSpTZXJ2aWNlVGFnc19QdWJsaWNfXFxkK1xcLmpzb24vO1xuXG4vKiogQ2xvdWQgaHlwZXJzY2FsZXIgYSBob3N0IHJlc29sdmVzIGludG8uICovXG5leHBvcnQgZW51bSBDbG91ZFByb3ZpZGVyIHtcbiAgQXdzID0gXCJhd3NcIixcbiAgQXp1cmUgPSBcImF6dXJlXCIsXG4gIEdjcCA9IFwiZ2NwXCIsXG59XG5cbi8qKlxuICogV2hlcmUgYSBob3N0IGxpdmVzOiB0aGUge0BsaW5rIENsb3VkUHJvdmlkZXJ9LCB0aGUgcHJvdmlkZXItbmF0aXZlXG4gKiBgcmVnaW9uYCBzdHJpbmcgKHdoYXRldmVyIHRoZSBwcm92aWRlcidzIGZlZWQgY2FsbHMgaXQsIGUuZy5cbiAqIGBcInVzLWVhc3QtMVwiYCBvbiBBV1MsIGBcImVhc3R1czJcImAgb24gQXp1cmUsIGBcInVzLWVhc3QxXCJgIG9uIEdDUCksIHRoZVxuICogcmVzb2x2ZWQgYGlwYCB0aGF0IG1hdGNoZWQsIGFuZCB0aGUgYGNpZHJgIGJsb2NrIGl0IGZlbGwgaW4uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2xvdWRMb2NhdGlvbiB7XG4gIHByb3ZpZGVyOiBDbG91ZFByb3ZpZGVyO1xuICByZWdpb246IHN0cmluZztcbiAgaXA6IHN0cmluZztcbiAgY2lkcjogc3RyaW5nO1xufVxuXG4vKiogQSBwYXJzZWQgQ0lEUiBibG9jayB0YWdnZWQgd2l0aCB0aGUgcmVnaW9uIGl0IGJlbG9uZ3MgdG8uICovXG5pbnRlcmZhY2UgUmVnaW9uQ2lkciBleHRlbmRzIG5ldC5DaWRyIHtcbiAgcmVnaW9uOiBzdHJpbmc7XG59XG5cbi8qKiBPbmUgcHJvdmlkZXIncyBwYXJzZWQsIHJlZ2lvbi10YWdnZWQgcmFuZ2UgdGFibGUuICovXG5pbnRlcmZhY2UgUHJvdmlkZXJSYW5nZXMge1xuICBwcm92aWRlcjogQ2xvdWRQcm92aWRlcjtcbiAgcmFuZ2VzOiBSZWdpb25DaWRyW107XG59XG5cbi8qKlxuICogUmVzb2x2ZSBgaW5wdXRgJ3MgaG9zdCB0byBhIHtAbGluayBDbG91ZExvY2F0aW9ufSwgb3IgYG51bGxgIHdoZW4gdGhlXG4gKiBob3N0IGNhbid0IGJlIHJlc29sdmVkIG9yIG5vbmUgb2YgaXRzIElQcyBtYXRjaCBhIGtub3duIGNsb3VkIHJhbmdlLlxuICogYGlucHV0YCBpcyBhbnkge0BsaW5rIG5ldC5VcmxMaWtlfSAtIGEgVVJMLCBhIGJhcmUgaG9zdCwgb3IgYSBgeyB1cmwgfWBcbiAqIHdyYXBwZXIuXG4gKlxuICogVGhlIHByb3ZpZGVyIHJhbmdlIGZlZWRzIGFyZSBjYWNoZWQgZm9yIHtAbGluayBSQU5HRV9DQUNIRV9UVExfTVN9LCBzb1xuICogb25seSB0aGUgZmlyc3QgY2FsbCBpbiBhIDI0LWhvdXIgd2luZG93IHBheXMgdGhlIGZldGNoIGNvc3Q7IHRoZSBETlNcbiAqIGxvb2t1cCBoYXBwZW5zIG9uIGV2ZXJ5IGNhbGwuXG4gKlxuICogQGV4YW1wbGVcbiAqIGF3YWl0IHJlc29sdmVDbG91ZExvY2F0aW9uKFwiaHR0cHM6Ly9hZGItMTIzNDU2Nzg5MC43LmF6dXJlZGF0YWJyaWNrcy5uZXRcIik7XG4gKiAvLyB7IHByb3ZpZGVyOiBDbG91ZFByb3ZpZGVyLkF6dXJlLFxuICogLy8gICByZWdpb246IFwiZWFzdHVzMlwiLCBpcDogXCIuLi5cIiwgY2lkcjogXCIuLi5cIiB9XG4gKlxuICogYXdhaXQgcmVzb2x2ZUNsb3VkTG9jYXRpb24oXCJkYmMtYWJjMTIzLmNsb3VkLmRhdGFicmlja3MuY29tXCIpO1xuICogLy8geyBwcm92aWRlcjogQ2xvdWRQcm92aWRlci5Bd3MsXG4gKiAvLyAgIHJlZ2lvbjogXCJ1cy13ZXN0LTJcIiwgaXA6IFwiLi4uXCIsIGNpZHI6IFwiLi4uXCIgfVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZUNsb3VkTG9jYXRpb24oaW5wdXQ6IG5ldC5VcmxMaWtlKTogUHJvbWlzZTxDbG91ZExvY2F0aW9uIHwgbnVsbD4ge1xuICBjb25zdCBpcHMgPSBhd2FpdCByZXNvbHZlSG9zdElwcyhpbnB1dCk7XG4gIGlmIChpcHMubGVuZ3RoID09PSAwKSB7XG4gICAgbG9nZ2VyLmRlYnVnKFwibm8gaXBzIHJlc29sdmVkXCIsIHsgaW5wdXQ6IFN0cmluZyhpbnB1dCkgfSk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgcHJvdmlkZXJzID0gYXdhaXQgbG9hZFByb3ZpZGVyUmFuZ2VzKCk7XG4gIGZvciAoY29uc3QgaXAgb2YgaXBzKSB7XG4gICAgY29uc3QgcGFyc2VkID0gbmV0LnBhcnNlSXAoaXApO1xuICAgIGlmICghcGFyc2VkKSBjb250aW51ZTtcbiAgICBmb3IgKGNvbnN0IHsgcHJvdmlkZXIsIHJhbmdlcyB9IG9mIHByb3ZpZGVycykge1xuICAgICAgY29uc3QgbWF0Y2ggPSBuZXQuZmluZENvbnRhaW5pbmdDaWRyKHBhcnNlZCwgcmFuZ2VzKTtcbiAgICAgIGlmIChtYXRjaCkge1xuICAgICAgICByZXR1cm4geyBwcm92aWRlciwgcmVnaW9uOiBtYXRjaC5yZWdpb24sIGlwLCBjaWRyOiBtYXRjaC5jaWRyIH07XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGxvZ2dlci5kZWJ1ZyhcIm5vIGNsb3VkIHJhbmdlIG1hdGNoZWRcIiwgeyBpcHMgfSk7XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIExvYWQgZXZlcnkgcHJvdmlkZXIncyByZWdpb24tdGFnZ2VkIHJhbmdlIHRhYmxlLCBlYWNoIGNhY2hlZCBmb3JcbiAqIHtAbGluayBSQU5HRV9DQUNIRV9UVExfTVN9LiBQcm92aWRlcnMgYXJlIGxvYWRlZCBpbiBwYXJhbGxlbCBhbmQgYVxuICogZmVlZCB0aGF0IGZhaWxzIHRvIGZldGNoIG9yIHBhcnNlIGlzIGRyb3BwZWQgKGxvZ2dlZCwgbm90IHRocm93bikgc29cbiAqIGEgc2luZ2xlIGZsYWt5IGZlZWQgbmV2ZXIgc2lua3MgdGhlIHdob2xlIGxvb2t1cC4gRXhwb3NlZCBmb3IgY2FsbGVyc1xuICogdGhhdCB3YW50IHRvIG1hdGNoIG1hbnkgSVBzIGFnYWluc3Qgb25lIGNhY2hlZCBzbmFwc2hvdCB3aXRob3V0IGEgRE5TXG4gKiBzdGVwIHBlciBhZGRyZXNzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZFByb3ZpZGVyUmFuZ2VzKCk6IFByb21pc2U8UHJvdmlkZXJSYW5nZXNbXT4ge1xuICBjb25zdCBzZXR0bGVkID0gYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtsb2FkQXdzUmFuZ2VzKCksIGxvYWRBenVyZVJhbmdlcygpLCBsb2FkR2NwUmFuZ2VzKCldKTtcbiAgY29uc3Qgb3V0OiBQcm92aWRlclJhbmdlc1tdID0gW107XG4gIGZvciAoY29uc3QgcmVzdWx0IG9mIHNldHRsZWQpIHtcbiAgICBpZiAocmVzdWx0LnN0YXR1cyA9PT0gXCJmdWxmaWxsZWRcIikgb3V0LnB1c2gocmVzdWx0LnZhbHVlKTtcbiAgICBlbHNlIGxvZ2dlci53YXJuKFwicHJvdmlkZXIgcmFuZ2UgbG9hZCBmYWlsZWRcIiwgeyBlcnJvcjogZXJyb3IuZXJyb3JNZXNzYWdlKHJlc3VsdC5yZWFzb24pIH0pO1xuICB9XG4gIHJldHVybiBvdXQ7XG59XG5cbi8vIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuLy8gUGVyLXByb3ZpZGVyIGNhY2hlZCBsb2FkZXJzXG4vLyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuY29uc3QgbG9hZEF3c1JhbmdlcyA9IGZ1bmN0aW9uTW9kdWxlLm1lbW9pemUoZmV0Y2hBd3NSYW5nZXMsIHsgdHRsTXM6IFJBTkdFX0NBQ0hFX1RUTF9NUyB9KTtcbmNvbnN0IGxvYWRBenVyZVJhbmdlcyA9IGZ1bmN0aW9uTW9kdWxlLm1lbW9pemUoZmV0Y2hBenVyZVJhbmdlcywgeyB0dGxNczogUkFOR0VfQ0FDSEVfVFRMX01TIH0pO1xuY29uc3QgbG9hZEdjcFJhbmdlcyA9IGZ1bmN0aW9uTW9kdWxlLm1lbW9pemUoZmV0Y2hHY3BSYW5nZXMsIHsgdHRsTXM6IFJBTkdFX0NBQ0hFX1RUTF9NUyB9KTtcblxuLy8g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4vLyBQcm92aWRlciBmZWVkIGZldGNoICsgcGFyc2Vcbi8vIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG5pbnRlcmZhY2UgQXdzRmVlZCB7XG4gIHByZWZpeGVzPzogeyBpcF9wcmVmaXg/OiBzdHJpbmc7IHJlZ2lvbj86IHN0cmluZyB9W107XG4gIGlwdjZfcHJlZml4ZXM/OiB7IGlwdjZfcHJlZml4Pzogc3RyaW5nOyByZWdpb24/OiBzdHJpbmcgfVtdO1xufVxuXG4vKipcbiAqIEFXUyBgaXAtcmFuZ2VzLmpzb25gOiBhIGZsYXQgbGlzdCBvZiBJUHY0IChgcHJlZml4ZXNgKSBhbmQgSVB2NlxuICogKGBpcHY2X3ByZWZpeGVzYCkgYmxvY2tzLCBlYWNoIGFscmVhZHkgdGFnZ2VkIHdpdGggYSBgcmVnaW9uYCBzdWNoXG4gKiBhcyBgXCJ1cy1lYXN0LTFcImAgKG9yIGBcIkdMT0JBTFwiYCBmb3IgZWRnZSByYW5nZXMpLlxuICovXG5hc3luYyBmdW5jdGlvbiBmZXRjaEF3c1JhbmdlcygpOiBQcm9taXNlPFByb3ZpZGVyUmFuZ2VzPiB7XG4gIGNvbnN0IGZlZWQgPSBhd2FpdCBmZXRjaEpzb248QXdzRmVlZD4oQVdTX1JBTkdFU19VUkwpO1xuICBjb25zdCByYW5nZXM6IFJlZ2lvbkNpZHJbXSA9IFtdO1xuICBmb3IgKGNvbnN0IGVudHJ5IG9mIGZlZWQucHJlZml4ZXMgPz8gW10pIHtcbiAgICBhZGRSYW5nZShyYW5nZXMsIGVudHJ5LmlwX3ByZWZpeCwgZW50cnkucmVnaW9uKTtcbiAgfVxuICBmb3IgKGNvbnN0IGVudHJ5IG9mIGZlZWQuaXB2Nl9wcmVmaXhlcyA/PyBbXSkge1xuICAgIGFkZFJhbmdlKHJhbmdlcywgZW50cnkuaXB2Nl9wcmVmaXgsIGVudHJ5LnJlZ2lvbik7XG4gIH1cbiAgbG9nZ2VyLmRlYnVnKFwibG9hZGVkIGF3cyByYW5nZXNcIiwgeyBjb3VudDogcmFuZ2VzLmxlbmd0aCB9KTtcbiAgcmV0dXJuIHsgcHJvdmlkZXI6IENsb3VkUHJvdmlkZXIuQXdzLCByYW5nZXMgfTtcbn1cblxuaW50ZXJmYWNlIEdjcEZlZWQge1xuICBwcmVmaXhlcz86IHsgaXB2NFByZWZpeD86IHN0cmluZzsgaXB2NlByZWZpeD86IHN0cmluZzsgc2NvcGU/OiBzdHJpbmcgfVtdO1xufVxuXG4vKipcbiAqIEdDUCBgY2xvdWQuanNvbmA6IGEgbGlzdCBvZiBibG9ja3MgZWFjaCBjYXJyeWluZyBhbiBgaXB2NFByZWZpeGAgb3JcbiAqIGBpcHY2UHJlZml4YCBhbmQgYSBgc2NvcGVgIHRoYXQgaXMgdGhlIHJlZ2lvbiAoYFwidXMtZWFzdDFcImApIG9yXG4gKiBgXCJnbG9iYWxcImAgZm9yIG5vbi1yZWdpb25hbCByYW5nZXMuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGZldGNoR2NwUmFuZ2VzKCk6IFByb21pc2U8UHJvdmlkZXJSYW5nZXM+IHtcbiAgY29uc3QgZmVlZCA9IGF3YWl0IGZldGNoSnNvbjxHY3BGZWVkPihHQ1BfUkFOR0VTX1VSTCk7XG4gIGNvbnN0IHJhbmdlczogUmVnaW9uQ2lkcltdID0gW107XG4gIGZvciAoY29uc3QgZW50cnkgb2YgZmVlZC5wcmVmaXhlcyA/PyBbXSkge1xuICAgIGFkZFJhbmdlKHJhbmdlcywgZW50cnkuaXB2NFByZWZpeCA/PyBlbnRyeS5pcHY2UHJlZml4LCBlbnRyeS5zY29wZSk7XG4gIH1cbiAgbG9nZ2VyLmRlYnVnKFwibG9hZGVkIGdjcCByYW5nZXNcIiwgeyBjb3VudDogcmFuZ2VzLmxlbmd0aCB9KTtcbiAgcmV0dXJuIHsgcHJvdmlkZXI6IENsb3VkUHJvdmlkZXIuR2NwLCByYW5nZXMgfTtcbn1cblxuaW50ZXJmYWNlIEF6dXJlRmVlZCB7XG4gIHZhbHVlcz86IHtcbiAgICBuYW1lPzogc3RyaW5nO1xuICAgIHByb3BlcnRpZXM/OiB7IHJlZ2lvbj86IHN0cmluZzsgYWRkcmVzc1ByZWZpeGVzPzogc3RyaW5nW10gfTtcbiAgfVtdO1xufVxuXG4vKipcbiAqIEF6dXJlIHNlcnZpY2UgdGFnczogTWljcm9zb2Z0IHNoaXBzIG5vIHN0YWJsZSBmZWVkIFVSTCwgc28gc2NyYXBlIHRoZVxuICogY3VycmVudCBgU2VydmljZVRhZ3NfUHVibGljXzxkYXRlPi5qc29uYCBsaW5rIG9mZiB0aGUgZG93bmxvYWQgcGFnZVxuICogYW5kIGZldGNoIGl0LiBPbmx5IHRoZSByZWdpb25hbCBgQXp1cmVDbG91ZC48cmVnaW9uPmAgYWdncmVnYXRlc1xuICogKHdoaWNoIGhhdmUgYSBub24tZW1wdHkgYHJlZ2lvbmApIGFyZSBrZXB0OyB0aGUgY2xvdWQtd2lkZVxuICogYEF6dXJlQ2xvdWRgIHRhZyBhbmQgcGVyLXNlcnZpY2UgdGFncyBhcmUgaWdub3JlZCBzbyBhbiBhZGRyZXNzIG1hcHNcbiAqIGNsZWFubHkgdG8gb25lIHJlZ2lvbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZmV0Y2hBenVyZVJhbmdlcygpOiBQcm9taXNlPFByb3ZpZGVyUmFuZ2VzPiB7XG4gIGNvbnN0IHBhZ2UgPSBhd2FpdCBmZXRjaFRleHQoQVpVUkVfRE9XTkxPQURfUEFHRSk7XG4gIGNvbnN0IGpzb25VcmwgPSBwYWdlLm1hdGNoKEFaVVJFX0pTT05fTElOSyk/LlswXTtcbiAgaWYgKCFqc29uVXJsKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiY291bGQgbm90IGZpbmQgU2VydmljZVRhZ3MgSlNPTiBsaW5rIG9uIEF6dXJlIGRvd25sb2FkIHBhZ2VcIik7XG4gIH1cbiAgY29uc3QgZmVlZCA9IGF3YWl0IGZldGNoSnNvbjxBenVyZUZlZWQ+KGpzb25VcmwpO1xuICBjb25zdCByYW5nZXM6IFJlZ2lvbkNpZHJbXSA9IFtdO1xuICBmb3IgKGNvbnN0IHZhbHVlIG9mIGZlZWQudmFsdWVzID8/IFtdKSB7XG4gICAgY29uc3QgcmVnaW9uID0gdmFsdWUucHJvcGVydGllcz8ucmVnaW9uO1xuICAgIGlmICghcmVnaW9uIHx8ICF2YWx1ZS5uYW1lPy5zdGFydHNXaXRoKFwiQXp1cmVDbG91ZC5cIikpIGNvbnRpbnVlO1xuICAgIGZvciAoY29uc3QgcHJlZml4IG9mIHZhbHVlLnByb3BlcnRpZXM/LmFkZHJlc3NQcmVmaXhlcyA/PyBbXSkge1xuICAgICAgYWRkUmFuZ2UocmFuZ2VzLCBwcmVmaXgsIHJlZ2lvbik7XG4gICAgfVxuICB9XG4gIGxvZ2dlci5kZWJ1ZyhcImxvYWRlZCBhenVyZSByYW5nZXNcIiwgeyBjb3VudDogcmFuZ2VzLmxlbmd0aCB9KTtcbiAgcmV0dXJuIHsgcHJvdmlkZXI6IENsb3VkUHJvdmlkZXIuQXp1cmUsIHJhbmdlcyB9O1xufVxuXG4vLyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbi8vIEhlbHBlcnNcbi8vIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKiogUGFyc2UgYGNpZHJgIGFuZCwgd2hlbiB2YWxpZCBhbmQgYHJlZ2lvbmAgaXMgc2V0LCBwdXNoIGEgdGFnZ2VkIHJhbmdlLiAqL1xuZnVuY3Rpb24gYWRkUmFuZ2UocmFuZ2VzOiBSZWdpb25DaWRyW10sIGNpZHI/OiBzdHJpbmcsIHJlZ2lvbj86IHN0cmluZyk6IHZvaWQge1xuICBpZiAoIWNpZHIgfHwgIXJlZ2lvbikgcmV0dXJuO1xuICBjb25zdCBwYXJzZWQgPSBuZXQucGFyc2VDaWRyKGNpZHIpO1xuICBpZiAocGFyc2VkKSByYW5nZXMucHVzaCh7IC4uLnBhcnNlZCwgcmVnaW9uIH0pO1xufVxuXG5hc3luYyBmdW5jdGlvbiBmZXRjaEpzb248VD4odXJsOiBzdHJpbmcpOiBQcm9taXNlPFQ+IHtcbiAgY29uc3QgdGV4dCA9IGF3YWl0IGZldGNoVGV4dCh1cmwpO1xuICByZXR1cm4gSlNPTi5wYXJzZSh0ZXh0KSBhcyBUO1xufVxuXG4vKipcbiAqIEZldGNoIGB1cmxgIGFzIHRleHQgd2l0aCBhIDI0aCBvbi1kaXNrIGNhY2hlIHVuZGVyIHRoZSBPUyB0ZW1wIGRpclxuICogKGtleWVkIGJ5IGFuIEZOViBoYXNoIG9mIHRoZSBVUkwpLiBBIGZyZXNoIGNhY2hlIGZpbGUgaXMgcmV0dXJuZWRcbiAqIGRpcmVjdGx5OyBhIG1pc3MvZXhwaXJ5IGZldGNoZXMsIHdyaXRlcyB0byBhIHVuaXF1ZSB0ZW1wIGZpbGUsIGFuZFxuICogYXRvbWljYWxseSByZW5hbWVzIGl0IGludG8gcGxhY2Ugc28gY29uY3VycmVudCBjYWxsZXJzIG5ldmVyIG9ic2VydmVcbiAqIGEgaGFsZi13cml0dGVuIGNhY2hlIGVudHJ5LlxuICovXG5hc3luYyBmdW5jdGlvbiBmZXRjaFRleHQodXJsOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBjYWNoZURpciA9IGpvaW4odG1wZGlyKCksIFwiZGJ4LXRvb2xzXCIsIFwic2hhcmVkXCIsIFwiY2xvdWRcIik7XG4gIGNvbnN0IGNhY2hlRGlyQ3JlYXRlZCA9IGF3YWl0IG1rZGlyKGNhY2hlRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgY29uc3QgY2FjaGVQYXRoID0gam9pbihjYWNoZURpciwgYGZldGNoLSR7aGFzaC5mbnZIYXNoKHVybCl9LnR4dGApO1xuICBjb25zdCBjcmVhdGVkQXQgPSBjYWNoZURpckNyZWF0ZWQgPyB1bmRlZmluZWQgOiBhd2FpdCBnZXRDcmVhdGVkKGNhY2hlUGF0aCk7XG4gIGlmIChjcmVhdGVkQXQpIHtcbiAgICBjb25zdCBleHBpcmVzQXQgPSBuZXcgRGF0ZShjcmVhdGVkQXQuZ2V0VGltZSgpICsgUkFOR0VfQ0FDSEVfVFRMX01TKTtcbiAgICBpZiAoZXhwaXJlc0F0ID4gbmV3IERhdGUoKSkge1xuICAgICAgbG9nZ2VyLmRlYnVnKFwiY2FjaGVkIGZldGNoIGhpdFwiLCB7IHVybCwgY2FjaGVQYXRoIH0pO1xuICAgICAgcmV0dXJuIHJlYWRGaWxlKGNhY2hlUGF0aCwgXCJ1dGY4XCIpO1xuICAgIH1cbiAgfVxuICBsZXQgdGVtcFBhdGg6IHN0cmluZyB8IG51bGwgPSBqb2luKGNhY2hlRGlyLCBgJHtoYXNoLmlkKCl9LnR4dGApO1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godXJsKTtcbiAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICB0aHJvdyBhd2FpdCBodHRwLmNyZWF0ZUZldGNoRXJyb3IocmVzcG9uc2UsIHVybCk7XG4gICAgfVxuICAgIGNvbnN0IHJlc3BvbnNlVGV4dCA9IGF3YWl0IHJlc3BvbnNlLnRleHQoKTtcbiAgICBhd2FpdCBta2RpcihkaXJuYW1lKHRlbXBQYXRoKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgYXdhaXQgd3JpdGVGaWxlKHRlbXBQYXRoLCByZXNwb25zZVRleHQpO1xuXG4gICAgYXdhaXQgcmVuYW1lKHRlbXBQYXRoLCBjYWNoZVBhdGgpO1xuICAgIGxvZ2dlci5kZWJ1ZyhcImNhY2hlZCBmZXRjaCBsb2FkXCIsIHsgdXJsLCBjYWNoZVBhdGggfSk7XG4gICAgdGVtcFBhdGggPSBudWxsO1xuICAgIHJldHVybiByZXNwb25zZVRleHQ7XG4gIH0gZmluYWxseSB7XG4gICAgaWYgKHRlbXBQYXRoKSB7XG4gICAgICBhd2FpdCB1bmxpbmsodGVtcFBhdGgpO1xuICAgIH1cbiAgfVxufVxuXG4vKiogQmlydGggdGltZSBvZiBgcGF0aGAsIG9yIGB1bmRlZmluZWRgIHdoZW4gaXQgZG9lc24ndCBleGlzdCB5ZXQuICovXG5hc3luYyBmdW5jdGlvbiBnZXRDcmVhdGVkKHBhdGg6IHN0cmluZyk6IFByb21pc2U8RGF0ZSB8IHVuZGVmaW5lZD4ge1xuICByZXR1cm4gKGF3YWl0IGZpbGUuc3RhdFN5bmMocGF0aCkpPy5iaXJ0aHRpbWU7XG59XG4iXX0=
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Server-side networking helpers that need Node builtins, layered over the
3
+ * browser-safe URL / IP toolkit in `@dbx-tools/shared-core`'s `net` module: DNS
4
+ * resolution ({@link resolveHostIps}, `node:dns`) and public-IP discovery
5
+ * ({@link getPublicIp}).
6
+ *
7
+ * @module
8
+ */
9
+ import { net } from "@dbx-tools/shared-core";
10
+ /**
11
+ * This process's outbound public IP, cached for 5 minutes. Asks Cloudflare's
12
+ * `cdn-cgi/trace` first and falls back to ipify. Useful for allowlisting or for
13
+ * reasoning about egress from a Databricks App.
14
+ */
15
+ export declare const getPublicIp: () => Promise<string>;
16
+ /**
17
+ * Resolve the host of `input` to its IP address(es) via the OS resolver
18
+ * (`dns.lookup` with `all: true`, so both A and AAAA records are returned when
19
+ * the host is dual-stacked). Accepts any `net.UrlLike` - a bare hostname, a full
20
+ * URL, or a `{ url }` wrapper - and returns the deduplicated list of literal
21
+ * addresses. Never throws: an unparseable input, an IP-literal host (returned
22
+ * as-is without a DNS round-trip), or a resolution failure all yield the
23
+ * appropriate list or `[]`.
24
+ *
25
+ * `dns.lookup` (not `resolve4` / `resolve6`) is used so `/etc/hosts` and the
26
+ * platform resolver order are honored, matching what an outbound connection to
27
+ * the host would actually use.
28
+ */
29
+ export declare function resolveHostIps(input: net.UrlLike): Promise<string[]>;
package/lib/src/net.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Server-side networking helpers that need Node builtins, layered over the
3
+ * browser-safe URL / IP toolkit in `@dbx-tools/shared-core`'s `net` module: DNS
4
+ * resolution ({@link resolveHostIps}, `node:dns`) and public-IP discovery
5
+ * ({@link getPublicIp}).
6
+ *
7
+ * @module
8
+ */
9
+ import { lookup } from "node:dns/promises";
10
+ import { functionModule, http, net } from "@dbx-tools/shared-core";
11
+ /**
12
+ * This process's outbound public IP, cached for 5 minutes. Asks Cloudflare's
13
+ * `cdn-cgi/trace` first and falls back to ipify. Useful for allowlisting or for
14
+ * reasoning about egress from a Databricks App.
15
+ */
16
+ export const getPublicIp = functionModule.memoize(async () => {
17
+ const cloudflareResponse = await fetch("https://cloudflare.com/cdn-cgi/trace");
18
+ if (cloudflareResponse.ok) {
19
+ const trace = await cloudflareResponse.text();
20
+ const ipKey = "ip=";
21
+ const ip = trace
22
+ .split("\n")
23
+ .map((line) => line.trim())
24
+ .find((line) => line.startsWith(ipKey))
25
+ ?.slice(ipKey.length);
26
+ if (ip)
27
+ return ip;
28
+ }
29
+ const ipifyResponse = await fetch("https://api.ipify.org?format=json");
30
+ if (!ipifyResponse.ok) {
31
+ throw await http.createFetchError(ipifyResponse);
32
+ }
33
+ const ipifyData = (await ipifyResponse.json());
34
+ const ip = ipifyData?.ip;
35
+ if (ip)
36
+ return ip;
37
+ throw new Error("Could not determine public IP");
38
+ }, { ttlMs: 1000 * 60 * 5 });
39
+ /**
40
+ * Resolve the host of `input` to its IP address(es) via the OS resolver
41
+ * (`dns.lookup` with `all: true`, so both A and AAAA records are returned when
42
+ * the host is dual-stacked). Accepts any `net.UrlLike` - a bare hostname, a full
43
+ * URL, or a `{ url }` wrapper - and returns the deduplicated list of literal
44
+ * addresses. Never throws: an unparseable input, an IP-literal host (returned
45
+ * as-is without a DNS round-trip), or a resolution failure all yield the
46
+ * appropriate list or `[]`.
47
+ *
48
+ * `dns.lookup` (not `resolve4` / `resolve6`) is used so `/etc/hosts` and the
49
+ * platform resolver order are honored, matching what an outbound connection to
50
+ * the host would actually use.
51
+ */
52
+ export async function resolveHostIps(input) {
53
+ const host = net.urlBuilder(input)?.hostname;
54
+ if (!host)
55
+ return [];
56
+ // WHATWG `URL.hostname` brackets IPv6 literals; strip before parsing.
57
+ const literal = net.parseIp(host.replace(/^\[|\]$/g, ""));
58
+ if (literal)
59
+ return [host.replace(/^\[|\]$/g, "")];
60
+ try {
61
+ const results = await lookup(host, { all: true });
62
+ return [...new Set(results.map((r) => r.address))];
63
+ }
64
+ catch {
65
+ return [];
66
+ }
67
+ }
68
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmV0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25ldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztHQU9HO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBRW5FOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsY0FBYyxDQUFDLE9BQU8sQ0FDL0MsS0FBSyxJQUFJLEVBQUU7SUFDVCxNQUFNLGtCQUFrQixHQUFHLE1BQU0sS0FBSyxDQUFDLHNDQUFzQyxDQUFDLENBQUM7SUFDL0UsSUFBSSxrQkFBa0IsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUMxQixNQUFNLEtBQUssR0FBRyxNQUFNLGtCQUFrQixDQUFDLElBQUksRUFBRSxDQUFDO1FBQzlDLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQztRQUNwQixNQUFNLEVBQUUsR0FBRyxLQUFLO2FBQ2IsS0FBSyxDQUFDLElBQUksQ0FBQzthQUNYLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO2FBQzFCLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUN2QyxFQUFFLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDeEIsSUFBSSxFQUFFO1lBQUUsT0FBTyxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQUNELE1BQU0sYUFBYSxHQUFHLE1BQU0sS0FBSyxDQUFDLG1DQUFtQyxDQUFDLENBQUM7SUFDdkUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUN0QixNQUFNLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFDRCxNQUFNLFNBQVMsR0FBRyxDQUFDLE1BQU0sYUFBYSxDQUFDLElBQUksRUFBRSxDQUFvQixDQUFDO0lBQ2xFLE1BQU0sRUFBRSxHQUFHLFNBQVMsRUFBRSxFQUFFLENBQUM7SUFDekIsSUFBSSxFQUFFO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFDbEIsTUFBTSxJQUFJLEtBQUssQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO0FBQ25ELENBQUMsRUFDRCxFQUFFLEtBQUssRUFBRSxJQUFJLEdBQUcsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUN6QixDQUFDO0FBRUY7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjLENBQUMsS0FBa0I7SUFDckQsTUFBTSxJQUFJLEdBQUcsR0FBRyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsRUFBRSxRQUFRLENBQUM7SUFDN0MsSUFBSSxDQUFDLElBQUk7UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUNyQixzRUFBc0U7SUFDdEUsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzFELElBQUksT0FBTztRQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ25ELElBQUksQ0FBQztRQUNILE1BQU0sT0FBTyxHQUFHLE1BQU0sTUFBTSxDQUFDLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ2xELE9BQU8sQ0FBQyxHQUFHLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNlcnZlci1zaWRlIG5ldHdvcmtpbmcgaGVscGVycyB0aGF0IG5lZWQgTm9kZSBidWlsdGlucywgbGF5ZXJlZCBvdmVyIHRoZVxuICogYnJvd3Nlci1zYWZlIFVSTCAvIElQIHRvb2xraXQgaW4gYEBkYngtdG9vbHMvc2hhcmVkLWNvcmVgJ3MgYG5ldGAgbW9kdWxlOiBETlNcbiAqIHJlc29sdXRpb24gKHtAbGluayByZXNvbHZlSG9zdElwc30sIGBub2RlOmRuc2ApIGFuZCBwdWJsaWMtSVAgZGlzY292ZXJ5XG4gKiAoe0BsaW5rIGdldFB1YmxpY0lwfSkuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGxvb2t1cCB9IGZyb20gXCJub2RlOmRucy9wcm9taXNlc1wiO1xuaW1wb3J0IHsgZnVuY3Rpb25Nb2R1bGUsIGh0dHAsIG5ldCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5cbi8qKlxuICogVGhpcyBwcm9jZXNzJ3Mgb3V0Ym91bmQgcHVibGljIElQLCBjYWNoZWQgZm9yIDUgbWludXRlcy4gQXNrcyBDbG91ZGZsYXJlJ3NcbiAqIGBjZG4tY2dpL3RyYWNlYCBmaXJzdCBhbmQgZmFsbHMgYmFjayB0byBpcGlmeS4gVXNlZnVsIGZvciBhbGxvd2xpc3Rpbmcgb3IgZm9yXG4gKiByZWFzb25pbmcgYWJvdXQgZWdyZXNzIGZyb20gYSBEYXRhYnJpY2tzIEFwcC5cbiAqL1xuZXhwb3J0IGNvbnN0IGdldFB1YmxpY0lwID0gZnVuY3Rpb25Nb2R1bGUubWVtb2l6ZShcbiAgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGNsb3VkZmxhcmVSZXNwb25zZSA9IGF3YWl0IGZldGNoKFwiaHR0cHM6Ly9jbG91ZGZsYXJlLmNvbS9jZG4tY2dpL3RyYWNlXCIpO1xuICAgIGlmIChjbG91ZGZsYXJlUmVzcG9uc2Uub2spIHtcbiAgICAgIGNvbnN0IHRyYWNlID0gYXdhaXQgY2xvdWRmbGFyZVJlc3BvbnNlLnRleHQoKTtcbiAgICAgIGNvbnN0IGlwS2V5ID0gXCJpcD1cIjtcbiAgICAgIGNvbnN0IGlwID0gdHJhY2VcbiAgICAgICAgLnNwbGl0KFwiXFxuXCIpXG4gICAgICAgIC5tYXAoKGxpbmUpID0+IGxpbmUudHJpbSgpKVxuICAgICAgICAuZmluZCgobGluZSkgPT4gbGluZS5zdGFydHNXaXRoKGlwS2V5KSlcbiAgICAgICAgPy5zbGljZShpcEtleS5sZW5ndGgpO1xuICAgICAgaWYgKGlwKSByZXR1cm4gaXA7XG4gICAgfVxuICAgIGNvbnN0IGlwaWZ5UmVzcG9uc2UgPSBhd2FpdCBmZXRjaChcImh0dHBzOi8vYXBpLmlwaWZ5Lm9yZz9mb3JtYXQ9anNvblwiKTtcbiAgICBpZiAoIWlwaWZ5UmVzcG9uc2Uub2spIHtcbiAgICAgIHRocm93IGF3YWl0IGh0dHAuY3JlYXRlRmV0Y2hFcnJvcihpcGlmeVJlc3BvbnNlKTtcbiAgICB9XG4gICAgY29uc3QgaXBpZnlEYXRhID0gKGF3YWl0IGlwaWZ5UmVzcG9uc2UuanNvbigpKSBhcyB7IGlwPzogc3RyaW5nIH07XG4gICAgY29uc3QgaXAgPSBpcGlmeURhdGE/LmlwO1xuICAgIGlmIChpcCkgcmV0dXJuIGlwO1xuICAgIHRocm93IG5ldyBFcnJvcihcIkNvdWxkIG5vdCBkZXRlcm1pbmUgcHVibGljIElQXCIpO1xuICB9LFxuICB7IHR0bE1zOiAxMDAwICogNjAgKiA1IH0sXG4pO1xuXG4vKipcbiAqIFJlc29sdmUgdGhlIGhvc3Qgb2YgYGlucHV0YCB0byBpdHMgSVAgYWRkcmVzcyhlcykgdmlhIHRoZSBPUyByZXNvbHZlclxuICogKGBkbnMubG9va3VwYCB3aXRoIGBhbGw6IHRydWVgLCBzbyBib3RoIEEgYW5kIEFBQUEgcmVjb3JkcyBhcmUgcmV0dXJuZWQgd2hlblxuICogdGhlIGhvc3QgaXMgZHVhbC1zdGFja2VkKS4gQWNjZXB0cyBhbnkgYG5ldC5VcmxMaWtlYCAtIGEgYmFyZSBob3N0bmFtZSwgYSBmdWxsXG4gKiBVUkwsIG9yIGEgYHsgdXJsIH1gIHdyYXBwZXIgLSBhbmQgcmV0dXJucyB0aGUgZGVkdXBsaWNhdGVkIGxpc3Qgb2YgbGl0ZXJhbFxuICogYWRkcmVzc2VzLiBOZXZlciB0aHJvd3M6IGFuIHVucGFyc2VhYmxlIGlucHV0LCBhbiBJUC1saXRlcmFsIGhvc3QgKHJldHVybmVkXG4gKiBhcy1pcyB3aXRob3V0IGEgRE5TIHJvdW5kLXRyaXApLCBvciBhIHJlc29sdXRpb24gZmFpbHVyZSBhbGwgeWllbGQgdGhlXG4gKiBhcHByb3ByaWF0ZSBsaXN0IG9yIGBbXWAuXG4gKlxuICogYGRucy5sb29rdXBgIChub3QgYHJlc29sdmU0YCAvIGByZXNvbHZlNmApIGlzIHVzZWQgc28gYC9ldGMvaG9zdHNgIGFuZCB0aGVcbiAqIHBsYXRmb3JtIHJlc29sdmVyIG9yZGVyIGFyZSBob25vcmVkLCBtYXRjaGluZyB3aGF0IGFuIG91dGJvdW5kIGNvbm5lY3Rpb24gdG9cbiAqIHRoZSBob3N0IHdvdWxkIGFjdHVhbGx5IHVzZS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlc29sdmVIb3N0SXBzKGlucHV0OiBuZXQuVXJsTGlrZSk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgY29uc3QgaG9zdCA9IG5ldC51cmxCdWlsZGVyKGlucHV0KT8uaG9zdG5hbWU7XG4gIGlmICghaG9zdCkgcmV0dXJuIFtdO1xuICAvLyBXSEFUV0cgYFVSTC5ob3N0bmFtZWAgYnJhY2tldHMgSVB2NiBsaXRlcmFsczsgc3RyaXAgYmVmb3JlIHBhcnNpbmcuXG4gIGNvbnN0IGxpdGVyYWwgPSBuZXQucGFyc2VJcChob3N0LnJlcGxhY2UoL15cXFt8XFxdJC9nLCBcIlwiKSk7XG4gIGlmIChsaXRlcmFsKSByZXR1cm4gW2hvc3QucmVwbGFjZSgvXlxcW3xcXF0kL2csIFwiXCIpXTtcbiAgdHJ5IHtcbiAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgbG9va3VwKGhvc3QsIHsgYWxsOiB0cnVlIH0pO1xuICAgIHJldHVybiBbLi4ubmV3IFNldChyZXN1bHRzLm1hcCgocikgPT4gci5hZGRyZXNzKSldO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gW107XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Resolve the current Databricks workspace's URL and numeric id from the active
3
+ * execution context (AppKit, when initialized), a default `WorkspaceClient`, or
4
+ * the environment. Server-only.
5
+ *
6
+ * @module
7
+ */
8
+ import { net } from "@dbx-tools/shared-core";
9
+ /**
10
+ * Resolve the current workspace host as a `net.UrlBuilder`: the workspace
11
+ * `Config` host first, then the `DATABRICKS_HOST` env var, else `undefined`.
12
+ */
13
+ export declare function getWorkspaceUrl(): Promise<net.UrlBuilder | undefined>;
14
+ /**
15
+ * Resolve the numeric workspace id: the workspace `Config`'s `workspaceId`
16
+ * first, else the 10-20 digit run of `workspaceHost` (defaulting to
17
+ * {@link getWorkspaceUrl}'s host). `undefined` when neither yields an id.
18
+ */
19
+ export declare function getWorkspaceId(workspaceHost?: string): Promise<string | undefined>;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Resolve the current Databricks workspace's URL and numeric id from the active
3
+ * execution context (AppKit, when initialized), a default `WorkspaceClient`, or
4
+ * the environment. Server-only.
5
+ *
6
+ * @module
7
+ */
8
+ import { WorkspaceClient } from "@databricks/sdk-experimental";
9
+ import { appkit } from "@dbx-tools/appkit";
10
+ import { functionModule, net } from "@dbx-tools/shared-core";
11
+ /** Databricks workspace ids are a 10-20 digit run embedded in the host. */
12
+ const WORKSPACE_ID_REGEX = /\d{10,20}/;
13
+ /**
14
+ * Lazily-constructed default `WorkspaceClient` (env / profile auth), memoized so
15
+ * construction happens at most once per process. Used only when there's no
16
+ * AppKit execution context to borrow a client from.
17
+ */
18
+ const getDefaultWorkspaceClient = functionModule.memoize(async () => new WorkspaceClient({}));
19
+ /**
20
+ * The active workspace `Config`: the AppKit execution-context client's config
21
+ * when AppKit is initialized, else the default client's. Returns `undefined`
22
+ * (never throws) when neither is available.
23
+ */
24
+ async function getWorkspaceConfig() {
25
+ let client = appkit.tryGetExecutionContext()?.client;
26
+ if (!client) {
27
+ try {
28
+ client = await getDefaultWorkspaceClient();
29
+ }
30
+ catch {
31
+ // no client available; fall back to the environment
32
+ }
33
+ }
34
+ return client?.config;
35
+ }
36
+ /**
37
+ * Resolve the current workspace host as a `net.UrlBuilder`: the workspace
38
+ * `Config` host first, then the `DATABRICKS_HOST` env var, else `undefined`.
39
+ */
40
+ export async function getWorkspaceUrl() {
41
+ const config = await getWorkspaceConfig();
42
+ if (config) {
43
+ const configHost = net.urlBuilder(await config.getHost());
44
+ if (configHost)
45
+ return configHost;
46
+ }
47
+ const databricksHost = net.urlBuilder(process.env.DATABRICKS_HOST);
48
+ if (databricksHost)
49
+ return databricksHost;
50
+ return undefined;
51
+ }
52
+ /**
53
+ * Resolve the numeric workspace id: the workspace `Config`'s `workspaceId`
54
+ * first, else the 10-20 digit run of `workspaceHost` (defaulting to
55
+ * {@link getWorkspaceUrl}'s host). `undefined` when neither yields an id.
56
+ */
57
+ export async function getWorkspaceId(workspaceHost) {
58
+ const workspaceId = (await getWorkspaceConfig())?.workspaceId;
59
+ if (workspaceId)
60
+ return workspaceId;
61
+ workspaceHost = workspaceHost ?? (await getWorkspaceUrl())?.host;
62
+ if (workspaceHost) {
63
+ const match = workspaceHost.match(WORKSPACE_ID_REGEX)?.[0];
64
+ if (match)
65
+ return match;
66
+ }
67
+ return undefined;
68
+ }
69
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya3NwYWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dvcmtzcGFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEVBQWUsZUFBZSxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDNUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxjQUFjLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFN0QsMkVBQTJFO0FBQzNFLE1BQU0sa0JBQWtCLEdBQUcsV0FBVyxDQUFDO0FBRXZDOzs7O0dBSUc7QUFDSCxNQUFNLHlCQUF5QixHQUFHLGNBQWMsQ0FBQyxPQUFPLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxJQUFJLGVBQWUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBRTlGOzs7O0dBSUc7QUFDSCxLQUFLLFVBQVUsa0JBQWtCO0lBQy9CLElBQUksTUFBTSxHQUFHLE1BQU0sQ0FBQyxzQkFBc0IsRUFBRSxFQUFFLE1BQXFDLENBQUM7SUFDcEYsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ1osSUFBSSxDQUFDO1lBQ0gsTUFBTSxHQUFHLE1BQU0seUJBQXlCLEVBQUUsQ0FBQztRQUM3QyxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1Asb0RBQW9EO1FBQ3RELENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxNQUFNLEVBQUUsTUFBTSxDQUFDO0FBQ3hCLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGVBQWU7SUFDbkMsTUFBTSxNQUFNLEdBQUcsTUFBTSxrQkFBa0IsRUFBRSxDQUFDO0lBQzFDLElBQUksTUFBTSxFQUFFLENBQUM7UUFDWCxNQUFNLFVBQVUsR0FBRyxHQUFHLENBQUMsVUFBVSxDQUFDLE1BQU0sTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDMUQsSUFBSSxVQUFVO1lBQUUsT0FBTyxVQUFVLENBQUM7SUFDcEMsQ0FBQztJQUNELE1BQU0sY0FBYyxHQUFHLEdBQUcsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUNuRSxJQUFJLGNBQWM7UUFBRSxPQUFPLGNBQWMsQ0FBQztJQUMxQyxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsY0FBYyxDQUFDLGFBQXNCO0lBQ3pELE1BQU0sV0FBVyxHQUFHLENBQUMsTUFBTSxrQkFBa0IsRUFBRSxDQUFDLEVBQUUsV0FBVyxDQUFDO0lBQzlELElBQUksV0FBVztRQUFFLE9BQU8sV0FBVyxDQUFDO0lBQ3BDLGFBQWEsR0FBRyxhQUFhLElBQUksQ0FBQyxNQUFNLGVBQWUsRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDO0lBQ2pFLElBQUksYUFBYSxFQUFFLENBQUM7UUFDbEIsTUFBTSxLQUFLLEdBQUcsYUFBYSxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDM0QsSUFBSSxLQUFLO1lBQUUsT0FBTyxLQUFLLENBQUM7SUFDMUIsQ0FBQztJQUNELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFJlc29sdmUgdGhlIGN1cnJlbnQgRGF0YWJyaWNrcyB3b3Jrc3BhY2UncyBVUkwgYW5kIG51bWVyaWMgaWQgZnJvbSB0aGUgYWN0aXZlXG4gKiBleGVjdXRpb24gY29udGV4dCAoQXBwS2l0LCB3aGVuIGluaXRpYWxpemVkKSwgYSBkZWZhdWx0IGBXb3Jrc3BhY2VDbGllbnRgLCBvclxuICogdGhlIGVudmlyb25tZW50LiBTZXJ2ZXItb25seS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgdHlwZSBDb25maWcsIFdvcmtzcGFjZUNsaWVudCB9IGZyb20gXCJAZGF0YWJyaWNrcy9zZGstZXhwZXJpbWVudGFsXCI7XG5pbXBvcnQgeyBhcHBraXQgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB7IGZ1bmN0aW9uTW9kdWxlLCBuZXQgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuXG4vKiogRGF0YWJyaWNrcyB3b3Jrc3BhY2UgaWRzIGFyZSBhIDEwLTIwIGRpZ2l0IHJ1biBlbWJlZGRlZCBpbiB0aGUgaG9zdC4gKi9cbmNvbnN0IFdPUktTUEFDRV9JRF9SRUdFWCA9IC9cXGR7MTAsMjB9LztcblxuLyoqXG4gKiBMYXppbHktY29uc3RydWN0ZWQgZGVmYXVsdCBgV29ya3NwYWNlQ2xpZW50YCAoZW52IC8gcHJvZmlsZSBhdXRoKSwgbWVtb2l6ZWQgc29cbiAqIGNvbnN0cnVjdGlvbiBoYXBwZW5zIGF0IG1vc3Qgb25jZSBwZXIgcHJvY2Vzcy4gVXNlZCBvbmx5IHdoZW4gdGhlcmUncyBub1xuICogQXBwS2l0IGV4ZWN1dGlvbiBjb250ZXh0IHRvIGJvcnJvdyBhIGNsaWVudCBmcm9tLlxuICovXG5jb25zdCBnZXREZWZhdWx0V29ya3NwYWNlQ2xpZW50ID0gZnVuY3Rpb25Nb2R1bGUubWVtb2l6ZShhc3luYyAoKSA9PiBuZXcgV29ya3NwYWNlQ2xpZW50KHt9KSk7XG5cbi8qKlxuICogVGhlIGFjdGl2ZSB3b3Jrc3BhY2UgYENvbmZpZ2A6IHRoZSBBcHBLaXQgZXhlY3V0aW9uLWNvbnRleHQgY2xpZW50J3MgY29uZmlnXG4gKiB3aGVuIEFwcEtpdCBpcyBpbml0aWFsaXplZCwgZWxzZSB0aGUgZGVmYXVsdCBjbGllbnQncy4gUmV0dXJucyBgdW5kZWZpbmVkYFxuICogKG5ldmVyIHRocm93cykgd2hlbiBuZWl0aGVyIGlzIGF2YWlsYWJsZS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0V29ya3NwYWNlQ29uZmlnKCk6IFByb21pc2U8Q29uZmlnIHwgdW5kZWZpbmVkPiB7XG4gIGxldCBjbGllbnQgPSBhcHBraXQudHJ5R2V0RXhlY3V0aW9uQ29udGV4dCgpPy5jbGllbnQgYXMgV29ya3NwYWNlQ2xpZW50IHwgdW5kZWZpbmVkO1xuICBpZiAoIWNsaWVudCkge1xuICAgIHRyeSB7XG4gICAgICBjbGllbnQgPSBhd2FpdCBnZXREZWZhdWx0V29ya3NwYWNlQ2xpZW50KCk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBubyBjbGllbnQgYXZhaWxhYmxlOyBmYWxsIGJhY2sgdG8gdGhlIGVudmlyb25tZW50XG4gICAgfVxuICB9XG4gIHJldHVybiBjbGllbnQ/LmNvbmZpZztcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBjdXJyZW50IHdvcmtzcGFjZSBob3N0IGFzIGEgYG5ldC5VcmxCdWlsZGVyYDogdGhlIHdvcmtzcGFjZVxuICogYENvbmZpZ2AgaG9zdCBmaXJzdCwgdGhlbiB0aGUgYERBVEFCUklDS1NfSE9TVGAgZW52IHZhciwgZWxzZSBgdW5kZWZpbmVkYC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldFdvcmtzcGFjZVVybCgpOiBQcm9taXNlPG5ldC5VcmxCdWlsZGVyIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IGNvbmZpZyA9IGF3YWl0IGdldFdvcmtzcGFjZUNvbmZpZygpO1xuICBpZiAoY29uZmlnKSB7XG4gICAgY29uc3QgY29uZmlnSG9zdCA9IG5ldC51cmxCdWlsZGVyKGF3YWl0IGNvbmZpZy5nZXRIb3N0KCkpO1xuICAgIGlmIChjb25maWdIb3N0KSByZXR1cm4gY29uZmlnSG9zdDtcbiAgfVxuICBjb25zdCBkYXRhYnJpY2tzSG9zdCA9IG5ldC51cmxCdWlsZGVyKHByb2Nlc3MuZW52LkRBVEFCUklDS1NfSE9TVCk7XG4gIGlmIChkYXRhYnJpY2tzSG9zdCkgcmV0dXJuIGRhdGFicmlja3NIb3N0O1xuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIFJlc29sdmUgdGhlIG51bWVyaWMgd29ya3NwYWNlIGlkOiB0aGUgd29ya3NwYWNlIGBDb25maWdgJ3MgYHdvcmtzcGFjZUlkYFxuICogZmlyc3QsIGVsc2UgdGhlIDEwLTIwIGRpZ2l0IHJ1biBvZiBgd29ya3NwYWNlSG9zdGAgKGRlZmF1bHRpbmcgdG9cbiAqIHtAbGluayBnZXRXb3Jrc3BhY2VVcmx9J3MgaG9zdCkuIGB1bmRlZmluZWRgIHdoZW4gbmVpdGhlciB5aWVsZHMgYW4gaWQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRXb3Jrc3BhY2VJZCh3b3Jrc3BhY2VIb3N0Pzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgY29uc3Qgd29ya3NwYWNlSWQgPSAoYXdhaXQgZ2V0V29ya3NwYWNlQ29uZmlnKCkpPy53b3Jrc3BhY2VJZDtcbiAgaWYgKHdvcmtzcGFjZUlkKSByZXR1cm4gd29ya3NwYWNlSWQ7XG4gIHdvcmtzcGFjZUhvc3QgPSB3b3Jrc3BhY2VIb3N0ID8/IChhd2FpdCBnZXRXb3Jrc3BhY2VVcmwoKSk/Lmhvc3Q7XG4gIGlmICh3b3Jrc3BhY2VIb3N0KSB7XG4gICAgY29uc3QgbWF0Y2ggPSB3b3Jrc3BhY2VIb3N0Lm1hdGNoKFdPUktTUEFDRV9JRF9SRUdFWCk/LlswXTtcbiAgICBpZiAobWF0Y2gpIHJldHVybiBtYXRjaDtcbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuIl19
@@ -0,0 +1 @@
1
+ {"root":["../src/cloud.ts","../src/net.ts","../src/workspace.ts","../index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -12,25 +12,29 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@databricks/sdk-experimental": "^0.17.0",
15
- "@dbx-tools/core": "0.3.44",
16
- "@dbx-tools/appkit": "0.3.44",
17
- "@dbx-tools/shared-core": "0.3.44"
15
+ "@dbx-tools/core": "0.4.0",
16
+ "@dbx-tools/appkit": "0.4.0",
17
+ "@dbx-tools/shared-core": "0.4.0"
18
18
  },
19
- "main": "index.ts",
19
+ "main": "./lib/index.js",
20
20
  "license": "UNLICENSED",
21
21
  "publishConfig": {
22
22
  "access": "public"
23
23
  },
24
- "version": "0.3.44",
25
- "types": "index.ts",
24
+ "version": "0.4.0",
25
+ "types": "./lib/index.d.ts",
26
26
  "type": "module",
27
27
  "exports": {
28
- ".": "./index.ts",
28
+ ".": {
29
+ "types": "./lib/index.d.ts",
30
+ "default": "./lib/index.js"
31
+ },
29
32
  "./package.json": "./package.json"
30
33
  },
31
34
  "files": [
32
35
  "index.ts",
33
- "src"
36
+ "src",
37
+ "lib"
34
38
  ],
35
39
  "dbxToolsConfig": {
36
40
  "tags": [