@jax-data-science/sci-image-visualizer 0.3.3 → 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/README.md +208 -4
- package/esm2022/index.mjs +20 -1
- package/esm2022/lib/contracts/display-types.mjs +20 -2
- package/esm2022/lib/contracts/image.contract.mjs +1 -1
- package/esm2022/lib/contracts/plot-type.mjs +36 -1
- package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -0
- package/esm2022/lib/contracts/spatial-dataset.contract.mjs +45 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +10 -19
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +71 -7
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +96 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +1934 -55
- package/esm2022/lib/implementations/napari-js/napari-zoom.mjs +18 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +5 -3
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +3 -4
- package/esm2022/lib/implementations/osd/osd-zoom.mjs +20 -0
- package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +555 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +13 -7
- package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +304 -0
- package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +179 -0
- package/esm2022/lib/routing-visualizer.service.mjs +223 -12
- package/esm2022/lib/spatial/embedding-compute.mjs +142 -0
- package/esm2022/lib/spatial/gene-search.mjs +77 -0
- package/esm2022/lib/spatial/spatial-density.mjs +224 -0
- package/esm2022/lib/spatial/spatial-encoding.mjs +232 -0
- package/esm2022/lib/spatial/spatial-expression.mjs +257 -0
- package/esm2022/lib/spatial/spatial-framing.mjs +60 -0
- package/esm2022/lib/spatial/spatial-heatmap.mjs +185 -0
- package/esm2022/lib/spatial/spatial-hover.mjs +79 -0
- package/esm2022/lib/spatial/spatial-sections.mjs +96 -0
- package/esm2022/lib/spatial/spatial-selection.mjs +264 -0
- package/esm2022/lib/spatial/spatial-volume-image.mjs +163 -0
- package/esm2022/lib/spatial/tsne-worker.mjs +10 -0
- package/esm2022/lib/spatial/world-grid.mjs +77 -0
- package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +1278 -0
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +621 -0
- package/esm2022/lib/store/spatial-selection.service.mjs +47 -0
- package/esm2022/lib/store/visualizer-store.service.mjs +23 -1
- package/esm2022/lib/toolbar/toolbar.component.mjs +30 -7
- package/esm2022/lib/visualization.module.mjs +23 -5
- package/esm2022/lib/visualizer.component.mjs +283 -22
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs} +6 -4
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs.map} +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +15769 -8316
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/fesm2022/tsne.worker.js +19091 -0
- package/index.d.ts +15 -0
- package/lib/contracts/display-types.d.ts +131 -0
- package/lib/contracts/image.contract.d.ts +7 -0
- package/lib/contracts/plot-type.d.ts +42 -2
- package/lib/contracts/ports/spatial-data.port.d.ts +90 -0
- package/lib/contracts/spatial-dataset.contract.d.ts +281 -0
- package/lib/contracts/visualizer.contract.d.ts +99 -1
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +9 -7
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +38 -4
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +7 -3
- package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +32 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +497 -16
- package/lib/implementations/napari-js/napari-zoom.d.ts +15 -0
- package/lib/implementations/osd/osd-zoom.d.ts +19 -0
- package/lib/implementations/plotly/omics-trace-builders.d.ts +193 -0
- package/lib/implementations/plotly/plotly.service.d.ts +9 -0
- package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +123 -0
- package/lib/implementations/spatial-data-http/spatial-wire.d.ts +115 -0
- package/lib/routing-visualizer.service.d.ts +45 -6
- package/lib/spatial/embedding-compute.d.ts +73 -0
- package/lib/spatial/gene-search.d.ts +48 -0
- package/lib/spatial/spatial-density.d.ts +73 -0
- package/lib/spatial/spatial-encoding.d.ts +155 -0
- package/lib/spatial/spatial-expression.d.ts +144 -0
- package/lib/spatial/spatial-framing.d.ts +37 -0
- package/lib/spatial/spatial-heatmap.d.ts +111 -0
- package/lib/spatial/spatial-hover.d.ts +49 -0
- package/lib/spatial/spatial-sections.d.ts +59 -0
- package/lib/spatial/spatial-selection.d.ts +113 -0
- package/lib/spatial/spatial-volume-image.d.ts +67 -0
- package/lib/spatial/tsne-worker.d.ts +7 -0
- package/lib/spatial/world-grid.d.ts +58 -0
- package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +439 -0
- package/lib/spatial-controls/spatial-controls.component.d.ts +257 -0
- package/lib/store/spatial-selection.service.d.ts +27 -0
- package/lib/store/visualizer-store.service.d.ts +13 -0
- package/lib/toolbar/toolbar.component.d.ts +19 -3
- package/lib/visualization.module.d.ts +28 -24
- package/lib/visualizer.component.d.ts +73 -2
- package/package.json +6 -2
- package/src/lib/assets/spatial-omics-3d.svg +31 -0
- package/src/lib/assets/spatial-omics.svg +27 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { Injectable } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
|
4
|
+
import { timeout } from 'rxjs/operators';
|
|
5
|
+
import { findColumnMeta, } from '../../contracts/spatial-dataset.contract';
|
|
6
|
+
import { assertManifestVersion, datasetFromManifest, decodeColumn, decodeCoords, decodeEmbedding, decodeFeatureVector, decodePolygons, decodeRadius, } from './spatial-wire';
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
import * as i1 from "@angular/common/http";
|
|
9
|
+
/**
|
|
10
|
+
* Reference {@link SpatialDataPort} adapter for the wire format the bundled
|
|
11
|
+
* example server speaks (see `spatial-wire.ts` and
|
|
12
|
+
* `examples/tile-server/lib/spatial.mjs`).
|
|
13
|
+
*
|
|
14
|
+
* OPTIONAL AND UNBOUND BY DEFAULT — this service is `@Injectable()` without
|
|
15
|
+
* `providedIn`, so nothing gets it unless a host explicitly provides it:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* providers: [
|
|
19
|
+
* SpatialDataHttpService,
|
|
20
|
+
* { provide: SPATIAL_DATA_PORT, useExisting: SpatialDataHttpService },
|
|
21
|
+
* ]
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* That mirrors `CellposeSegmenterService`: a concrete implementation the
|
|
25
|
+
* library ships for convenience, not a default the port inversion is quietly
|
|
26
|
+
* giving up on. A host with its own backend implements `SpatialDataPort`
|
|
27
|
+
* directly and never touches this class.
|
|
28
|
+
*
|
|
29
|
+
* Requests go through Angular's `HttpClient` (not `fetch`) so the host's
|
|
30
|
+
* interceptors — auth headers above all — apply, matching `tile-client.ts`.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* A dataset selection that a newer selection (or a `clear`) overtook.
|
|
34
|
+
*
|
|
35
|
+
* Thrown rather than resolved so a caller cannot mistake it for "this dataset is
|
|
36
|
+
* now loaded": nothing was published, on purpose.
|
|
37
|
+
*/
|
|
38
|
+
export class SupersededError extends Error {
|
|
39
|
+
constructor(id) {
|
|
40
|
+
super(`[spatial] selection of "${id}" was superseded by a newer one`);
|
|
41
|
+
this.name = 'SupersededError';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class SpatialDataHttpService {
|
|
45
|
+
static { this.CACHE_LIMIT = 32; }
|
|
46
|
+
constructor(http) {
|
|
47
|
+
this.http = http;
|
|
48
|
+
/** Server root, normalised to end with exactly one `/`. */
|
|
49
|
+
this.baseUrl = '';
|
|
50
|
+
/** Per-request ceiling. Vectors are small; a hung request should not wedge
|
|
51
|
+
* the picker. */
|
|
52
|
+
this.timeoutMs = 30_000;
|
|
53
|
+
this.dataset$ = new BehaviorSubject(null);
|
|
54
|
+
this.manifest = null;
|
|
55
|
+
/** Bumped by every `selectDataset` and every `clear`, so a selection that
|
|
56
|
+
* finishes after a newer intent can tell and drop what it fetched. */
|
|
57
|
+
this.selectToken = 0;
|
|
58
|
+
/**
|
|
59
|
+
* Loaded vectors, keyed `column:<name>` / `feature:<name>`. Bounded LRU:
|
|
60
|
+
* colouring by cluster → by a gene → back by cluster must not refetch, but an
|
|
61
|
+
* afternoon of browsing genes must not grow without limit either (one vector
|
|
62
|
+
* is `4·N` bytes — 2 MB at 500k cells).
|
|
63
|
+
*/
|
|
64
|
+
this.cache = new Map();
|
|
65
|
+
/** In-flight requests, so double-clicking a gene issues one fetch. */
|
|
66
|
+
this.inFlight = new Map();
|
|
67
|
+
this.volumePromise = null;
|
|
68
|
+
this.polygonsPromise = null;
|
|
69
|
+
}
|
|
70
|
+
// ── configuration ───────────────────────────────────────────────────────
|
|
71
|
+
/** Point the adapter at a server. Clears any loaded dataset. */
|
|
72
|
+
configure(options) {
|
|
73
|
+
const raw = options.baseUrl ?? '';
|
|
74
|
+
this.baseUrl = raw.endsWith('/') ? raw : `${raw}/`;
|
|
75
|
+
if (options.timeoutMs !== undefined)
|
|
76
|
+
this.timeoutMs = options.timeoutMs;
|
|
77
|
+
this.clear();
|
|
78
|
+
}
|
|
79
|
+
/** Datasets this server offers, for a host-side picker. */
|
|
80
|
+
listDatasets() {
|
|
81
|
+
return this.getJson('spatial/datasets')
|
|
82
|
+
.then((r) => r.datasets ?? []);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* One dataset's manifest, WITHOUT selecting it. Lets a host inspect what a
|
|
86
|
+
* server offers — column and feature metadata, and which image a dataset
|
|
87
|
+
* registers onto — while building a picker, rather than having to load each
|
|
88
|
+
* dataset to find out.
|
|
89
|
+
*/
|
|
90
|
+
readManifest(id) {
|
|
91
|
+
return this.getJson(`spatial/${encodeURIComponent(id)}/manifest`)
|
|
92
|
+
.then((manifest) => {
|
|
93
|
+
assertManifestVersion(manifest);
|
|
94
|
+
return manifest;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Load a dataset and publish it on {@link getDataset$}. Fetches the manifest,
|
|
99
|
+
* then the vectors that are always needed (coordinates, and ids/radius when
|
|
100
|
+
* the manifest says they exist) — nothing else.
|
|
101
|
+
*/
|
|
102
|
+
async selectDataset(id) {
|
|
103
|
+
this.clear();
|
|
104
|
+
// Selections are SEQUENCED: this is four awaits deep, so a slower earlier
|
|
105
|
+
// call would otherwise assign `this.manifest` or publish its dataset after a
|
|
106
|
+
// later one — leaving the manifest and the observations from two different
|
|
107
|
+
// datasets, which is worse than either being late.
|
|
108
|
+
const mine = ++this.selectToken;
|
|
109
|
+
const superseded = () => mine !== this.selectToken;
|
|
110
|
+
const path = `spatial/${encodeURIComponent(id)}`;
|
|
111
|
+
const manifest = await this.getJson(`${path}/manifest`);
|
|
112
|
+
assertManifestVersion(manifest);
|
|
113
|
+
if (superseded())
|
|
114
|
+
throw new SupersededError(id);
|
|
115
|
+
this.manifest = manifest;
|
|
116
|
+
const coordsBuf = await this.getBinary(`${path}/coords`);
|
|
117
|
+
if (superseded())
|
|
118
|
+
throw new SupersededError(id);
|
|
119
|
+
const coords = decodeCoords(coordsBuf, manifest.count, !!manifest.hasZ);
|
|
120
|
+
const ids = manifest.hasIds
|
|
121
|
+
? (await this.getJson(`${path}/ids`)).ids
|
|
122
|
+
: undefined;
|
|
123
|
+
const radius = manifest.radius?.mode === 'per-observation'
|
|
124
|
+
? decodeRadius(await this.getBinary(`${path}/radius`), manifest.count)
|
|
125
|
+
: undefined;
|
|
126
|
+
if (superseded())
|
|
127
|
+
throw new SupersededError(id);
|
|
128
|
+
const dataset = datasetFromManifest(manifest, coords, { ids, radius });
|
|
129
|
+
this.dataset$.next(dataset);
|
|
130
|
+
return dataset;
|
|
131
|
+
}
|
|
132
|
+
/** Drop the loaded dataset and every cached vector. */
|
|
133
|
+
clear() {
|
|
134
|
+
// A clear is itself the newest intent, so it supersedes any selection still
|
|
135
|
+
// in flight rather than letting one land afterwards.
|
|
136
|
+
this.selectToken++;
|
|
137
|
+
this.manifest = null;
|
|
138
|
+
this.cache.clear();
|
|
139
|
+
this.inFlight.clear();
|
|
140
|
+
this.polygonsPromise = null;
|
|
141
|
+
this.volumePromise = null;
|
|
142
|
+
if (this.dataset$.value !== null)
|
|
143
|
+
this.dataset$.next(null);
|
|
144
|
+
}
|
|
145
|
+
// ── SpatialDataPort ─────────────────────────────────────────────────────
|
|
146
|
+
getDataset$() {
|
|
147
|
+
return this.dataset$.asObservable();
|
|
148
|
+
}
|
|
149
|
+
getColumn(name) {
|
|
150
|
+
const manifest = this.requireManifest();
|
|
151
|
+
const dataset = this.dataset$.value;
|
|
152
|
+
const meta = findColumnMeta(dataset, name);
|
|
153
|
+
if (!meta) {
|
|
154
|
+
// Reject rather than resolve empty: a typo must surface as an error, not
|
|
155
|
+
// as a plot that silently renders every point as "no data".
|
|
156
|
+
return Promise.reject(new Error(`[spatial] unknown column "${name}". Available: ${dataset.columns.map((c) => c.name).join(', ')}`));
|
|
157
|
+
}
|
|
158
|
+
return this.fetchCached(`column:${name}`, () => this.getBinary(`spatial/${encodeURIComponent(manifest.id)}/column/${encodeURIComponent(name)}`)
|
|
159
|
+
.then((buf) => decodeColumn(buf, meta, manifest.count)));
|
|
160
|
+
}
|
|
161
|
+
getEmbedding(name) {
|
|
162
|
+
const manifest = this.requireManifest();
|
|
163
|
+
const meta = manifest.embeddings?.find((e) => e.name === name);
|
|
164
|
+
if (!meta) {
|
|
165
|
+
return Promise.reject(new Error(`[spatial] unknown embedding "${name}"`));
|
|
166
|
+
}
|
|
167
|
+
return this.fetchCached(`embedding:${name}`, () => this.getBinary(`spatial/${encodeURIComponent(manifest.id)}/embedding/${encodeURIComponent(name)}`).then((buf) => decodeEmbedding(buf, meta, manifest.count)));
|
|
168
|
+
}
|
|
169
|
+
getFeatureVector(name) {
|
|
170
|
+
const manifest = this.requireManifest();
|
|
171
|
+
const names = manifest.features?.names;
|
|
172
|
+
if (names && !names.includes(name)) {
|
|
173
|
+
return Promise.reject(new Error(`[spatial] unknown feature "${name}"`));
|
|
174
|
+
}
|
|
175
|
+
return this.fetchCached(`feature:${name}`, () => this.getBinary(`spatial/${encodeURIComponent(manifest.id)}/feature/${encodeURIComponent(name)}`)
|
|
176
|
+
.then((buf) => decodeFeatureVector(buf, manifest.count)));
|
|
177
|
+
}
|
|
178
|
+
async searchFeatures(query, limit = 50) {
|
|
179
|
+
const manifest = this.requireManifest();
|
|
180
|
+
// A dataset that inlined its names (targeted panel) is filtered locally —
|
|
181
|
+
// no round-trip for a keystroke.
|
|
182
|
+
const names = manifest.features?.names;
|
|
183
|
+
if (names) {
|
|
184
|
+
const q = query.toLowerCase();
|
|
185
|
+
return names.filter((n) => n.toLowerCase().includes(q)).slice(0, limit);
|
|
186
|
+
}
|
|
187
|
+
const url = `spatial/${encodeURIComponent(manifest.id)}/features`
|
|
188
|
+
+ `?q=${encodeURIComponent(query)}&limit=${limit}`;
|
|
189
|
+
return (await this.getJson(url)).names ?? [];
|
|
190
|
+
}
|
|
191
|
+
getPolygons() {
|
|
192
|
+
const manifest = this.requireManifest();
|
|
193
|
+
if (!manifest.polygons) {
|
|
194
|
+
return Promise.reject(new Error('[spatial] this dataset has no polygon geometry'));
|
|
195
|
+
}
|
|
196
|
+
// Geometry is one large blob rather than a per-name vector, so it gets its
|
|
197
|
+
// own single-flight slot instead of a cache entry.
|
|
198
|
+
this.polygonsPromise ??= this
|
|
199
|
+
.getBinary(`spatial/${encodeURIComponent(manifest.id)}/polygons`)
|
|
200
|
+
.then(decodePolygons)
|
|
201
|
+
.catch((err) => { this.polygonsPromise = null; throw err; });
|
|
202
|
+
return this.polygonsPromise;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The reference volume's voxels, single-flighted like the polygons.
|
|
206
|
+
*
|
|
207
|
+
* Validated against the manifest's declared dimensions before it is handed
|
|
208
|
+
* back: a short or long buffer read as a 3D texture does not fail, it shears
|
|
209
|
+
* the anatomy into diagonal streaks, and that is far harder to recognise as a
|
|
210
|
+
* transport problem than an error is.
|
|
211
|
+
*/
|
|
212
|
+
getVolume() {
|
|
213
|
+
const manifest = this.requireManifest();
|
|
214
|
+
const meta = manifest.volume;
|
|
215
|
+
if (!meta) {
|
|
216
|
+
return Promise.reject(new Error('[spatial] this dataset has no reference volume'));
|
|
217
|
+
}
|
|
218
|
+
this.volumePromise ??= this
|
|
219
|
+
.getBinary(`spatial/${encodeURIComponent(manifest.id)}/volume`)
|
|
220
|
+
.then((buf) => {
|
|
221
|
+
const want = meta.width * meta.height * meta.depth;
|
|
222
|
+
if (buf.byteLength !== want) {
|
|
223
|
+
throw new Error(`[spatial] volume is ${buf.byteLength} bytes, expected ${want} `
|
|
224
|
+
+ `(${meta.width}x${meta.height}x${meta.depth})`);
|
|
225
|
+
}
|
|
226
|
+
return new Uint8Array(buf);
|
|
227
|
+
})
|
|
228
|
+
.catch((err) => { this.volumePromise = null; throw err; });
|
|
229
|
+
return this.volumePromise;
|
|
230
|
+
}
|
|
231
|
+
// ── internals ───────────────────────────────────────────────────────────
|
|
232
|
+
requireManifest() {
|
|
233
|
+
if (!this.manifest || !this.dataset$.value) {
|
|
234
|
+
throw new Error('[spatial] no dataset selected — call selectDataset() first');
|
|
235
|
+
}
|
|
236
|
+
return this.manifest;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Cache + single-flight around a vector fetch.
|
|
240
|
+
*
|
|
241
|
+
* Keys are LOGICAL — `feature:GAPDH`, not `<dataset>/feature:GAPDH` — because `clear()`
|
|
242
|
+
* empties the maps on every switch, so two datasets never hold entries at once. What
|
|
243
|
+
* that does not cover is a request still in the air when the switch happens: `clear()`
|
|
244
|
+
* cannot cancel an HTTP call, so a vector fetched for dataset A can resolve after B has
|
|
245
|
+
* been selected and write itself into B's cache under a key B would read. Both datasets
|
|
246
|
+
* having a gene of that name is the common case, not the unlucky one, and the result is
|
|
247
|
+
* a plot of A's expression over B's cells — which looks like data, not like a fault.
|
|
248
|
+
*
|
|
249
|
+
* So the generation is checked after the await, and the in-flight slot is cleared only
|
|
250
|
+
* when the promise still sitting in it is this one: a late arrival must not evict the
|
|
251
|
+
* entry a newer request for the same key is already sharing.
|
|
252
|
+
*/
|
|
253
|
+
fetchCached(key, load) {
|
|
254
|
+
const hit = this.cache.get(key);
|
|
255
|
+
if (hit) {
|
|
256
|
+
// Re-insert to mark most-recently-used (Map preserves insertion order).
|
|
257
|
+
this.cache.delete(key);
|
|
258
|
+
this.cache.set(key, hit);
|
|
259
|
+
return Promise.resolve(hit);
|
|
260
|
+
}
|
|
261
|
+
const pending = this.inFlight.get(key);
|
|
262
|
+
if (pending)
|
|
263
|
+
return pending;
|
|
264
|
+
const mine = this.selectToken;
|
|
265
|
+
const promise = load()
|
|
266
|
+
.then((value) => {
|
|
267
|
+
// Still returned to the caller that asked: it may well be a component that is
|
|
268
|
+
// itself being torn down, and rejecting here would surface a switch as an error.
|
|
269
|
+
// What must not happen is the value being kept for whoever comes next.
|
|
270
|
+
if (mine === this.selectToken) {
|
|
271
|
+
this.cache.set(key, value);
|
|
272
|
+
this.evict();
|
|
273
|
+
}
|
|
274
|
+
return value;
|
|
275
|
+
})
|
|
276
|
+
.finally(() => {
|
|
277
|
+
if (this.inFlight.get(key) === promise)
|
|
278
|
+
this.inFlight.delete(key);
|
|
279
|
+
});
|
|
280
|
+
this.inFlight.set(key, promise);
|
|
281
|
+
return promise;
|
|
282
|
+
}
|
|
283
|
+
evict() {
|
|
284
|
+
while (this.cache.size > SpatialDataHttpService.CACHE_LIMIT) {
|
|
285
|
+
const oldest = this.cache.keys().next().value;
|
|
286
|
+
if (oldest === undefined)
|
|
287
|
+
break;
|
|
288
|
+
this.cache.delete(oldest);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
getJson(path) {
|
|
292
|
+
return firstValueFrom(this.http.get(`${this.baseUrl}${path}`).pipe(timeout(this.timeoutMs)));
|
|
293
|
+
}
|
|
294
|
+
getBinary(path) {
|
|
295
|
+
return firstValueFrom(this.http.get(`${this.baseUrl}${path}`, { responseType: 'arraybuffer' })
|
|
296
|
+
.pipe(timeout(this.timeoutMs)));
|
|
297
|
+
}
|
|
298
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialDataHttpService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
299
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialDataHttpService }); }
|
|
300
|
+
}
|
|
301
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialDataHttpService, decorators: [{
|
|
302
|
+
type: Injectable
|
|
303
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }] });
|
|
304
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1kYXRhLWh0dHAuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvaW1wbGVtZW50YXRpb25zL3NwYXRpYWwtZGF0YS1odHRwL3NwYXRpYWwtZGF0YS1odHRwLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyxFQUFFLGVBQWUsRUFBYyxjQUFjLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDbkUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBR3pDLE9BQU8sRUFLTCxjQUFjLEdBQ2YsTUFBTSwwQ0FBMEMsQ0FBQztBQUNsRCxPQUFPLEVBR0wscUJBQXFCLEVBQ3JCLG1CQUFtQixFQUNuQixZQUFZLEVBQ1osWUFBWSxFQUNaLGVBQWUsRUFDZixtQkFBbUIsRUFDbkIsY0FBYyxFQUNkLFlBQVksR0FDYixNQUFNLGdCQUFnQixDQUFDOzs7QUFFeEI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FzQkc7QUFDSDs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxlQUFnQixTQUFRLEtBQUs7SUFDeEMsWUFBWSxFQUFVO1FBQ3BCLEtBQUssQ0FBQywyQkFBMkIsRUFBRSxpQ0FBaUMsQ0FBQyxDQUFDO1FBQ3RFLElBQUksQ0FBQyxJQUFJLEdBQUcsaUJBQWlCLENBQUM7SUFDaEMsQ0FBQztDQUNGO0FBU0QsTUFBTSxPQUFPLHNCQUFzQjthQW9CVCxnQkFBVyxHQUFHLEVBQUUsQUFBTCxDQUFNO0lBUXpDLFlBQW9CLElBQWdCO1FBQWhCLFNBQUksR0FBSixJQUFJLENBQVk7UUEzQnBDLDJEQUEyRDtRQUNuRCxZQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ3JCOzBCQUNrQjtRQUNWLGNBQVMsR0FBRyxNQUFNLENB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LENBQUMsQ0FBQyxDQUN6RSxDQUFDO0lBQ0osQ0FBQztJQUVPLFNBQVMsQ0FBQyxJQUFZO1FBQzVCLE9BQU8sY0FBYyxDQUNuQixJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxFQUFFLEVBQUUsRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLENBQUM7YUFDckUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FDakMsQ0FBQztJQUNKLENBQUM7OEdBcFNVLHNCQUFzQjtrSEFBdEIsc0JBQXNCOzsyRkFBdEIsc0JBQXNCO2tCQURsQyxVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSHR0cENsaWVudCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSwgZmlyc3RWYWx1ZUZyb20gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHRpbWVvdXQgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7IFNwYXRpYWxEYXRhUG9ydCB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wb3J0cy9zcGF0aWFsLWRhdGEucG9ydCc7XG5pbXBvcnQge1xuICBTcGF0aWFsQ29sdW1uLFxuICBTcGF0aWFsRGF0YXNldCxcbiAgU3BhdGlhbEVtYmVkZGluZyxcbiAgU3BhdGlhbFBvbHlnb25zLFxuICBmaW5kQ29sdW1uTWV0YSxcbn0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5pbXBvcnQge1xuICBTcGF0aWFsRGF0YXNldFN1bW1hcnksXG4gIFNwYXRpYWxNYW5pZmVzdCxcbiAgYXNzZXJ0TWFuaWZlc3RWZXJzaW9uLFxuICBkYXRhc2V0RnJvbU1hbmlmZXN0LFxuICBkZWNvZGVDb2x1bW4sXG4gIGRlY29kZUNvb3JkcyxcbiAgZGVjb2RlRW1iZWRkaW5nLFxuICBkZWNvZGVGZWF0dXJlVmVjdG9yLFxuICBkZWNvZGVQb2x5Z29ucyxcbiAgZGVjb2RlUmFkaXVzLFxufSBmcm9tICcuL3NwYXRpYWwtd2lyZSc7XG5cbi8qKlxuICogUmVmZXJlbmNlIHtAbGluayBTcGF0aWFsRGF0YVBvcnR9IGFkYXB0ZXIgZm9yIHRoZSB3aXJlIGZvcm1hdCB0aGUgYnVuZGxlZFxuICogZXhhbXBsZSBzZXJ2ZXIgc3BlYWtzIChzZWUgYHNwYXRpYWwtd2lyZS50c2AgYW5kXG4gKiBgZXhhbXBsZXMvdGlsZS1zZXJ2ZXIvbGliL3NwYXRpYWwubWpzYCkuXG4gKlxuICogT1BUSU9OQUwgQU5EIFVOQk9VTkQgQlkgREVGQVVMVCDigJQgdGhpcyBzZXJ2aWNlIGlzIGBASW5qZWN0YWJsZSgpYCB3aXRob3V0XG4gKiBgcHJvdmlkZWRJbmAsIHNvIG5vdGhpbmcgZ2V0cyBpdCB1bmxlc3MgYSBob3N0IGV4cGxpY2l0bHkgcHJvdmlkZXMgaXQ6XG4gKlxuICogYGBgdHNcbiAqIHByb3ZpZGVyczogW1xuICogICBTcGF0aWFsRGF0YUh0dHBTZXJ2aWNlLFxuICogICB7IHByb3ZpZGU6IFNQQVRJQUxfREFUQV9QT1JULCB1c2VFeGlzdGluZzogU3BhdGlhbERhdGFIdHRwU2VydmljZSB9LFxuICogXVxuICogYGBgXG4gKlxuICogVGhhdCBtaXJyb3JzIGBDZWxscG9zZVNlZ21lbnRlclNlcnZpY2VgOiBhIGNvbmNyZXRlIGltcGxlbWVudGF0aW9uIHRoZVxuICogbGlicmFyeSBzaGlwcyBmb3IgY29udmVuaWVuY2UsIG5vdCBhIGRlZmF1bHQgdGhlIHBvcnQgaW52ZXJzaW9uIGlzIHF1aWV0bHlcbiAqIGdpdmluZyB1cCBvbi4gQSBob3N0IHdpdGggaXRzIG93biBiYWNrZW5kIGltcGxlbWVudHMgYFNwYXRpYWxEYXRhUG9ydGBcbiAqIGRpcmVjdGx5IGFuZCBuZXZlciB0b3VjaGVzIHRoaXMgY2xhc3MuXG4gKlxuICogUmVxdWVzdHMgZ28gdGhyb3VnaCBBbmd1bGFyJ3MgYEh0dHBDbGllbnRgIChub3QgYGZldGNoYCkgc28gdGhlIGhvc3Qnc1xuICogaW50ZXJjZXB0b3JzIOKAlCBhdXRoIGhlYWRlcnMgYWJvdmUgYWxsIOKAlCBhcHBseSwgbWF0Y2hpbmcgYHRpbGUtY2xpZW50LnRzYC5cbiAqL1xuLyoqXG4gKiBBIGRhdGFzZXQgc2VsZWN0aW9uIHRoYXQgYSBuZXdlciBzZWxlY3Rpb24gKG9yIGEgYGNsZWFyYCkgb3ZlcnRvb2suXG4gKlxuICogVGhyb3duIHJhdGhlciB0aGFuIHJlc29sdmVkIHNvIGEgY2FsbGVyIGNhbm5vdCBtaXN0YWtlIGl0IGZvciBcInRoaXMgZGF0YXNldCBpc1xuICogbm93IGxvYWRlZFwiOiBub3RoaW5nIHdhcyBwdWJsaXNoZWQsIG9uIHB1cnBvc2UuXG4gKi9cbmV4cG9ydCBjbGFzcyBTdXBlcnNlZGVkRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKGlkOiBzdHJpbmcpIHtcbiAgICBzdXBlcihgW3NwYXRpYWxdIHNlbGVjdGlvbiBvZiBcIiR7aWR9XCIgd2FzIHN1cGVyc2VkZWQgYnkgYSBuZXdlciBvbmVgKTtcbiAgICB0aGlzLm5hbWUgPSAnU3VwZXJzZWRlZEVycm9yJztcbiAgfVxufVxuXG4vKipcbiAqIFdoYXQgdGhlIExSVSBjYW4gaG9sZC4gT25lIGFsaWFzIHJhdGhlciB0aGFuIGEgdW5pb24gcmVwZWF0ZWQgYXQgZWFjaCB1c2UsIHNvIGFkZGluZyBhIHBheWxvYWRcbiAqIGtpbmQgaXMgYSBzaW5nbGUgZWRpdCBpbnN0ZWFkIG9mIGZvdXIgdGhhdCBjYW4gZHJpZnQgYXBhcnQuXG4gKi9cbnR5cGUgQ2FjaGVkUGF5bG9hZCA9IFNwYXRpYWxDb2x1bW4gfCBGbG9hdDMyQXJyYXkgfCBTcGF0aWFsRW1iZWRkaW5nO1xuXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgU3BhdGlhbERhdGFIdHRwU2VydmljZSBpbXBsZW1lbnRzIFNwYXRpYWxEYXRhUG9ydCB7XG4gIC8qKiBTZXJ2ZXIgcm9vdCwgbm9ybWFsaXNlZCB0byBlbmQgd2l0aCBleGFjdGx5IG9uZSBgL2AuICovXG4gIHByaXZhdGUgYmFzZVVybCA9ICcnO1xuICAvKiogUGVyLXJlcXVlc3QgY2VpbGluZy4gVmVjdG9ycyBhcmUgc21hbGw7IGEgaHVuZyByZXF1ZXN0IHNob3VsZCBub3Qgd2VkZ2VcbiAgICogIHRoZSBwaWNrZXIuICovXG4gIHByaXZhdGUgdGltZW91dE1zID0gMzBfMDAwO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgZGF0YXNldCQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFNwYXRpYWxEYXRhc2V0IHwgbnVsbD4obnVsbCk7XG4gIHByaXZhdGUgbWFuaWZlc3Q6IFNwYXRpYWxNYW5pZmVzdCB8IG51bGwgPSBudWxsO1xuICAvKiogQnVtcGVkIGJ5IGV2ZXJ5IGBzZWxlY3REYXRhc2V0YCBhbmQgZXZlcnkgYGNsZWFyYCwgc28gYSBzZWxlY3Rpb24gdGhhdFxuICAgKiAgZmluaXNoZXMgYWZ0ZXIgYSBuZXdlciBpbnRlbnQgY2FuIHRlbGwgYW5kIGRyb3Agd2hhdCBpdCBmZXRjaGVkLiAqL1xuICBwcml2YXRlIHNlbGVjdFRva2VuID0gMDtcblxuICAvKipcbiAgICogTG9hZGVkIHZlY3RvcnMsIGtleWVkIGBjb2x1bW46PG5hbWU+YCAvIGBmZWF0dXJlOjxuYW1lPmAuIEJvdW5kZWQgTFJVOlxuICAgKiBjb2xvdXJpbmcgYnkgY2x1c3RlciDihpIgYnkgYSBnZW5lIOKGkiBiYWNrIGJ5IGNsdXN0ZXIgbXVzdCBub3QgcmVmZXRjaCwgYnV0IGFuXG4gICAqIGFmdGVybm9vbiBvZiBicm93c2luZyBnZW5lcyBtdXN0IG5vdCBncm93IHdpdGhvdXQgbGltaXQgZWl0aGVyIChvbmUgdmVjdG9yXG4gICAqIGlzIGA0wrdOYCBieXRlcyDigJQgMiBNQiBhdCA1MDBrIGNlbGxzKS5cbiAgICovXG4gIHByaXZhdGUgcmVhZG9ubHkgY2FjaGUgPSBuZXcgTWFwPHN0cmluZywgQ2FjaGVkUGF5bG9hZD4oKTtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQ0FDSEVfTElNSVQgPSAzMjtcblxuICAvKiogSW4tZmxpZ2h0IHJlcXVlc3RzLCBzbyBkb3VibGUtY2xpY2tpbmcgYSBnZW5lIGlzc3VlcyBvbmUgZmV0Y2guICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaW5GbGlnaHQgPSBuZXcgTWFwPHN0cmluZywgUHJvbWlzZTxDYWNoZWRQYXlsb2FkPj4oKTtcblxuICBwcml2YXRlIHZvbHVtZVByb21pc2U6IFByb21pc2U8VWludDhBcnJheT4gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBwb2x5Z29uc1Byb21pc2U6IFByb21pc2U8U3BhdGlhbFBvbHlnb25zPiB8IG51bGwgPSBudWxsO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgaHR0cDogSHR0cENsaWVudCkge31cblxuICAvLyDilIDilIAgY29uZmlndXJhdGlvbiDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKiogUG9pbnQgdGhlIGFkYXB0ZXIgYXQgYSBzZXJ2ZXIuIENsZWFycyBhbnkgbG9hZGVkIGRhdGFzZXQuICovXG4gIGNvbmZpZ3VyZShvcHRpb25zOiB7IGJhc2VVcmw6IHN0cmluZzsgdGltZW91dE1zPzogbnVtYmVyIH0pOiB2b2lkIHtcbiAgICBjb25zdCByYXcgPSBvcHRpb25zLmJhc2VVcmwgPz8gJyc7XG4gICAgdGhpcy5iYXNlVXJsID0gcmF3LmVuZHNXaXRoKCcvJykgPyByYXcgOiBgJHtyYXd9L2A7XG4gICAgaWYgKG9wdGlvbnMudGltZW91dE1zICE9PSB1bmRlZmluZWQpIHRoaXMudGltZW91dE1zID0gb3B0aW9ucy50aW1lb3V0TXM7XG4gICAgdGhpcy5jbGVhcigpO1xuICB9XG5cbiAgLyoqIERhdGFzZXRzIHRoaXMgc2VydmVyIG9mZmVycywgZm9yIGEgaG9zdC1zaWRlIHBpY2tlci4gKi9cbiAgbGlzdERhdGFzZXRzKCk6IFByb21pc2U8U3BhdGlhbERhdGFzZXRTdW1tYXJ5W10+IHtcbiAgICByZXR1cm4gdGhpcy5nZXRKc29uPHsgZGF0YXNldHM6IFNwYXRpYWxEYXRhc2V0U3VtbWFyeVtdIH0+KCdzcGF0aWFsL2RhdGFzZXRzJylcbiAgICAgIC50aGVuKChyKSA9PiByLmRhdGFzZXRzID8/IFtdKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPbmUgZGF0YXNldCdzIG1hbmlmZXN0LCBXSVRIT1VUIHNlbGVjdGluZyBpdC4gTGV0cyBhIGhvc3QgaW5zcGVjdCB3aGF0IGFcbiAgICogc2VydmVyIG9mZmVycyDigJQgY29sdW1uIGFuZCBmZWF0dXJlIG1ldGFkYXRhLCBhbmQgd2hpY2ggaW1hZ2UgYSBkYXRhc2V0XG4gICAqIHJlZ2lzdGVycyBvbnRvIOKAlCB3aGlsZSBidWlsZGluZyBhIHBpY2tlciwgcmF0aGVyIHRoYW4gaGF2aW5nIHRvIGxvYWQgZWFjaFxuICAgKiBkYXRhc2V0IHRvIGZpbmQgb3V0LlxuICAgKi9cbiAgcmVhZE1hbmlmZXN0KGlkOiBzdHJpbmcpOiBQcm9taXNlPFNwYXRpYWxNYW5pZmVzdD4ge1xuICAgIHJldHVybiB0aGlzLmdldEpzb248U3BhdGlhbE1hbmlmZXN0Pihgc3BhdGlhbC8ke2VuY29kZVVSSUNvbXBvbmVudChpZCl9L21hbmlmZXN0YClcbiAgICAgIC50aGVuKChtYW5pZmVzdCkgPT4ge1xuICAgICAgICBhc3NlcnRNYW5pZmVzdFZlcnNpb24obWFuaWZlc3QpO1xuICAgICAgICByZXR1cm4gbWFuaWZlc3Q7XG4gICAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMb2FkIGEgZGF0YXNldCBhbmQgcHVibGlzaCBpdCBvbiB7QGxpbmsgZ2V0RGF0YXNldCR9LiBGZXRjaGVzIHRoZSBtYW5pZmVzdCxcbiAgICogdGhlbiB0aGUgdmVjdG9ycyB0aGF0IGFyZSBhbHdheXMgbmVlZGVkIChjb29yZGluYXRlcywgYW5kIGlkcy9yYWRpdXMgd2hlblxuICAgKiB0aGUgbWFuaWZlc3Qgc2F5cyB0aGV5IGV4aXN0KSDigJQgbm90aGluZyBlbHNlLlxuICAgKi9cbiAgYXN5bmMgc2VsZWN0RGF0YXNldChpZDogc3RyaW5nKTogUHJvbWlzZTxTcGF0aWFsRGF0YXNldD4ge1xuICAgIHRoaXMuY2xlYXIoKTtcbiAgICAvLyBTZWxlY3Rpb25zIGFyZSBTRVFVRU5DRUQ6IHRoaXMgaXMgZm91ciBhd2FpdHMgZGVlcCwgc28gYSBzbG93ZXIgZWFybGllclxuICAgIC8vIGNhbGwgd291bGQgb3RoZXJ3aXNlIGFzc2lnbiBgdGhpcy5tYW5pZmVzdGAgb3IgcHVibGlzaCBpdHMgZGF0YXNldCBhZnRlciBhXG4gICAgLy8gbGF0ZXIgb25lIOKAlCBsZWF2aW5nIHRoZSBtYW5pZmVzdCBhbmQgdGhlIG9ic2VydmF0aW9ucyBmcm9tIHR3byBkaWZmZXJlbnRcbiAgICAvLyBkYXRhc2V0cywgd2hpY2ggaXMgd29yc2UgdGhhbiBlaXRoZXIgYmVpbmcgbGF0ZS5cbiAgICBjb25zdCBtaW5lID0gKyt0aGlzLnNlbGVjdFRva2VuO1xuICAgIGNvbnN0IHN1cGVyc2VkZWQgPSAoKSA9PiBtaW5lICE9PSB0aGlzLnNlbGVjdFRva2VuO1xuICAgIGNvbnN0IHBhdGggPSBgc3BhdGlhbC8ke2VuY29kZVVSSUNvbXBvbmVudChpZCl9YDtcblxuICAgIGNvbnN0IG1hbmlmZXN0ID0gYXdhaXQgdGhpcy5nZXRKc29uPFNwYXRpYWxNYW5pZmVzdD4oYCR7cGF0aH0vbWFuaWZlc3RgKTtcbiAgICBhc3NlcnRNYW5pZmVzdFZlcnNpb24obWFuaWZlc3QpO1xuICAgIGlmIChzdXBlcnNlZGVkKCkpIHRocm93IG5ldyBTdXBlcnNlZGVkRXJyb3IoaWQpO1xuICAgIHRoaXMubWFuaWZlc3QgPSBtYW5pZmVzdDtcblxuICAgIGNvbnN0IGNvb3Jkc0J1ZiA9IGF3YWl0IHRoaXMuZ2V0QmluYXJ5KGAke3BhdGh9L2Nvb3Jkc2ApO1xuICAgIGlmIChzdXBlcnNlZGVkKCkpIHRocm93IG5ldyBTdXBlcnNlZGVkRXJyb3IoaWQpO1xuICAgIGNvbnN0IGNvb3JkcyA9IGRlY29kZUNvb3Jkcyhjb29yZHNCdWYsIG1hbmlmZXN0LmNvdW50LCAhIW1hbmlmZXN0Lmhhc1opO1xuXG4gICAgY29uc3QgaWRzID0gbWFuaWZlc3QuaGFzSWRzXG4gICAgICA/IChhd2FpdCB0aGlzLmdldEpzb248eyBpZHM6IHN0cmluZ1tdIH0+KGAke3BhdGh9L2lkc2ApKS5pZHNcbiAgICAgIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IHJhZGl1cyA9IG1hbmlmZXN0LnJhZGl1cz8ubW9kZSA9PT0gJ3Blci1vYnNlcnZhdGlvbidcbiAgICAgID8gZGVjb2RlUmFkaXVzKGF3YWl0IHRoaXMuZ2V0QmluYXJ5KGAke3BhdGh9L3JhZGl1c2ApLCBtYW5pZmVzdC5jb3VudClcbiAgICAgIDogdW5kZWZpbmVkO1xuICAgIGlmIChzdXBlcnNlZGVkKCkpIHRocm93IG5ldyBTdXBlcnNlZGVkRXJyb3IoaWQpO1xuXG4gICAgY29uc3QgZGF0YXNldCA9IGRhdGFzZXRGcm9tTWFuaWZlc3QobWFuaWZlc3QsIGNvb3JkcywgeyBpZHMsIHJhZGl1cyB9KTtcbiAgICB0aGlzLmRhdGFzZXQkLm5leHQoZGF0YXNldCk7XG4gICAgcmV0dXJuIGRhdGFzZXQ7XG4gIH1cblxuICAvKiogRHJvcCB0aGUgbG9hZGVkIGRhdGFzZXQgYW5kIGV2ZXJ5IGNhY2hlZCB2ZWN0b3IuICovXG4gIGNsZWFyKCk6IHZvaWQge1xuICAgIC8vIEEgY2xlYXIgaXMgaXRzZWxmIHRoZSBuZXdlc3QgaW50ZW50LCBzbyBpdCBzdXBlcnNlZGVzIGFueSBzZWxlY3Rpb24gc3RpbGxcbiAgICAvLyBpbiBmbGlnaHQgcmF0aGVyIHRoYW4gbGV0dGluZyBvbmUgbGFuZCBhZnRlcndhcmRzLlxuICAgIHRoaXMuc2VsZWN0VG9rZW4rKztcbiAgICB0aGlzLm1hbmlmZXN0ID0gbnVsbDtcbiAgICB0aGlzLmNhY2hlLmNsZWFyKCk7XG4gICAgdGhpcy5pbkZsaWdodC5jbGVhcigpO1xuICAgIHRoaXMucG9seWdvbnNQcm9taXNlID0gbnVsbDtcbiAgICB0aGlzLnZvbHVtZVByb21pc2UgPSBudWxsO1xuICAgIGlmICh0aGlzLmRhdGFzZXQkLnZhbHVlICE9PSBudWxsKSB0aGlzLmRhdGFzZXQkLm5leHQobnVsbCk7XG4gIH1cblxuICAvLyDilIDilIAgU3BhdGlhbERhdGFQb3J0IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGdldERhdGFzZXQkKCk6IE9ic2VydmFibGU8U3BhdGlhbERhdGFzZXQgfCBudWxsPiB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YXNldCQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICBnZXRDb2x1bW4obmFtZTogc3RyaW5nKTogUHJvbWlzZTxTcGF0aWFsQ29sdW1uPiB7XG4gICAgY29uc3QgbWFuaWZlc3QgPSB0aGlzLnJlcXVpcmVNYW5pZmVzdCgpO1xuICAgIGNvbnN0IGRhdGFzZXQgPSB0aGlzLmRhdGFzZXQkLnZhbHVlITtcbiAgICBjb25zdCBtZXRhID0gZmluZENvbHVtbk1ldGEoZGF0YXNldCwgbmFtZSk7XG4gICAgaWYgKCFtZXRhKSB7XG4gICAgICAvLyBSZWplY3QgcmF0aGVyIHRoYW4gcmVzb2x2ZSBlbXB0eTogYSB0eXBvIG11c3Qgc3VyZmFjZSBhcyBhbiBlcnJvciwgbm90XG4gICAgICAvLyBhcyBhIHBsb3QgdGhhdCBzaWxlbnRseSByZW5kZXJzIGV2ZXJ5IHBvaW50IGFzIFwibm8gZGF0YVwiLlxuICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcbiAgICAgICAgYFtzcGF0aWFsXSB1bmtub3duIGNvbHVtbiBcIiR7bmFtZX1cIi4gQXZhaWxhYmxlOiAke2RhdGFzZXQuY29sdW1ucy5tYXAoKGMpID0+IGMubmFtZSkuam9pbignLCAnKX1gLFxuICAgICAgKSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmZldGNoQ2FjaGVkKFxuICAgICAgYGNvbHVtbjoke25hbWV9YCxcbiAgICAgICgpID0+IHRoaXMuZ2V0QmluYXJ5KGBzcGF0aWFsLyR7ZW5jb2RlVVJJQ29tcG9uZW50KG1hbmlmZXN0LmlkKX0vY29sdW1uLyR7ZW5jb2RlVVJJQ29tcG9uZW50KG5hbWUpfWApXG4gICAgICAgIC50aGVuKChidWYpID0+IGRlY29kZUNvbHVtbihidWYsIG1ldGEsIG1hbmlmZXN0LmNvdW50KSksXG4gICAgKSBhcyBQcm9taXNlPFNwYXRpYWxDb2x1bW4+O1xuICB9XG5cbiAgZ2V0RW1iZWRkaW5nKG5hbWU6IHN0cmluZyk6IFByb21pc2U8U3BhdGlhbEVtYmVkZGluZz4ge1xuICAgIGNvbnN0IG1hbmlmZXN0ID0gdGhpcy5yZXF1aXJlTWFuaWZlc3QoKTtcbiAgICBjb25zdCBtZXRhID0gbWFuaWZlc3QuZW1iZWRkaW5ncz8uZmluZCgoZSkgPT4gZS5uYW1lID09PSBuYW1lKTtcbiAgICBpZiAoIW1ldGEpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoYFtzcGF0aWFsXSB1bmtub3duIGVtYmVkZGluZyBcIiR7bmFtZX1cImApKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZmV0Y2hDYWNoZWQoXG4gICAgICBgZW1iZWRkaW5nOiR7bmFtZX1gLFxuICAgICAgKCkgPT4gdGhpcy5nZXRCaW5hcnkoXG4gICAgICAgIGBzcGF0aWFsLyR7ZW5jb2RlVVJJQ29tcG9uZW50KG1hbmlmZXN0LmlkKX0vZW1iZWRkaW5nLyR7ZW5jb2RlVVJJQ29tcG9uZW50KG5hbWUpfWAsXG4gICAgICApLnRoZW4oKGJ1ZikgPT4gZGVjb2RlRW1iZWRkaW5nKGJ1ZiwgbWV0YSwgbWFuaWZlc3QuY291bnQpKSxcbiAgICApIGFzIFByb21pc2U8U3BhdGlhbEVtYmVkZGluZz47XG4gIH1cblxuICBnZXRGZWF0dXJlVmVjdG9yKG5hbWU6IHN0cmluZyk6IFByb21pc2U8RmxvYXQzMkFycmF5PiB7XG4gICAgY29uc3QgbWFuaWZlc3QgPSB0aGlzLnJlcXVpcmVNYW5pZmVzdCgpO1xuICAgIGNvbnN0IG5hbWVzID0gbWFuaWZlc3QuZmVhdHVyZXM/Lm5hbWVzO1xuICAgIGlmIChuYW1lcyAmJiAhbmFtZXMuaW5jbHVkZXMobmFtZSkpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoYFtzcGF0aWFsXSB1bmtub3duIGZlYXR1cmUgXCIke25hbWV9XCJgKSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmZldGNoQ2FjaGVkKFxuICAgICAgYGZlYXR1cmU6JHtuYW1lfWAsXG4gICAgICAoKSA9PiB0aGlzLmdldEJpbmFyeShgc3BhdGlhbC8ke2VuY29kZVVSSUNvbXBvbmVudChtYW5pZmVzdC5pZCl9L2ZlYXR1cmUvJHtlbmNvZGVVUklDb21wb25lbnQobmFtZSl9YClcbiAgICAgICAgLnRoZW4oKGJ1ZikgPT4gZGVjb2RlRmVhdHVyZVZlY3RvcihidWYsIG1hbmlmZXN0LmNvdW50KSksXG4gICAgKSBhcyBQcm9taXNlPEZsb2F0MzJBcnJheT47XG4gIH1cblxuICBhc3luYyBzZWFyY2hGZWF0dXJlcyhxdWVyeTogc3RyaW5nLCBsaW1pdCA9IDUwKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIGNvbnN0IG1hbmlmZXN0ID0gdGhpcy5yZXF1aXJlTWFuaWZlc3QoKTtcbiAgICAvLyBBIGRhdGFzZXQgdGhhdCBpbmxpbmVkIGl0cyBuYW1lcyAodGFyZ2V0ZWQgcGFuZWwpIGlzIGZpbHRlcmVkIGxvY2FsbHkg4oCUXG4gICAgLy8gbm8gcm91bmQtdHJpcCBmb3IgYSBrZXlzdHJva2UuXG4gICAgY29uc3QgbmFtZXMgPSBtYW5pZmVzdC5mZWF0dXJlcz8ubmFtZXM7XG4gICAgaWYgKG5hbWVzKSB7XG4gICAgICBjb25zdCBxID0gcXVlcnkudG9Mb3dlckNhc2UoKTtcbiAgICAgIHJldHVybiBuYW1lcy5maWx0ZXIoKG4pID0+IG4udG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhxKSkuc2xpY2UoMCwgbGltaXQpO1xuICAgIH1cbiAgICBjb25zdCB1cmwgPSBgc3BhdGlhbC8ke2VuY29kZVVSSUNvbXBvbmVudChtYW5pZmVzdC5pZCl9L2ZlYXR1cmVzYFxuICAgICAgKyBgP3E9JHtlbmNvZGVVUklDb21wb25lbnQocXVlcnkpfSZsaW1pdD0ke2xpbWl0fWA7XG4gICAgcmV0dXJuIChhd2FpdCB0aGlzLmdldEpzb248eyBuYW1lczogc3RyaW5nW10gfT4odXJsKSkubmFtZXMgPz8gW107XG4gIH1cblxuICBnZXRQb2x5Z29ucygpOiBQcm9taXNlPFNwYXRpYWxQb2x5Z29ucz4ge1xuICAgIGNvbnN0IG1hbmlmZXN0ID0gdGhpcy5yZXF1aXJlTWFuaWZlc3QoKTtcbiAgICBpZiAoIW1hbmlmZXN0LnBvbHlnb25zKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKCdbc3BhdGlhbF0gdGhpcyBkYXRhc2V0IGhhcyBubyBwb2x5Z29uIGdlb21ldHJ5JykpO1xuICAgIH1cbiAgICAvLyBHZW9tZXRyeSBpcyBvbmUgbGFyZ2UgYmxvYiByYXRoZXIgdGhhbiBhIHBlci1uYW1lIHZlY3Rvciwgc28gaXQgZ2V0cyBpdHNcbiAgICAvLyBvd24gc2luZ2xlLWZsaWdodCBzbG90IGluc3RlYWQgb2YgYSBjYWNoZSBlbnRyeS5cbiAgICB0aGlzLnBvbHlnb25zUHJvbWlzZSA/Pz0gdGhpc1xuICAgICAgLmdldEJpbmFyeShgc3BhdGlhbC8ke2VuY29kZVVSSUNvbXBvbmVudChtYW5pZmVzdC5pZCl9L3BvbHlnb25zYClcbiAgICAgIC50aGVuKGRlY29kZVBvbHlnb25zKVxuICAgICAgLmNhdGNoKChlcnIpID0+IHsgdGhpcy5wb2x5Z29uc1Byb21pc2UgPSBudWxsOyB0aHJvdyBlcnI7IH0pO1xuICAgIHJldHVybiB0aGlzLnBvbHlnb25zUHJvbWlzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgcmVmZXJlbmNlIHZvbHVtZSdzIHZveGVscywgc2luZ2xlLWZsaWdodGVkIGxpa2UgdGhlIHBvbHlnb25zLlxuICAgKlxuICAgKiBWYWxpZGF0ZWQgYWdhaW5zdCB0aGUgbWFuaWZlc3QncyBkZWNsYXJlZCBkaW1lbnNpb25zIGJlZm9yZSBpdCBpcyBoYW5kZWRcbiAgICogYmFjazogYSBzaG9ydCBvciBsb25nIGJ1ZmZlciByZWFkIGFzIGEgM0QgdGV4dHVyZSBkb2VzIG5vdCBmYWlsLCBpdCBzaGVhcnNcbiAgICogdGhlIGFuYXRvbXkgaW50byBkaWFnb25hbCBzdHJlYWtzLCBhbmQgdGhhdCBpcyBmYXIgaGFyZGVyIHRvIHJlY29nbmlzZSBhcyBhXG4gICAqIHRyYW5zcG9ydCBwcm9ibGVtIHRoYW4gYW4gZXJyb3IgaXMuXG4gICAqL1xuICBnZXRWb2x1bWUoKTogUHJvbWlzZTxVaW50OEFycmF5PiB7XG4gICAgY29uc3QgbWFuaWZlc3QgPSB0aGlzLnJlcXVpcmVNYW5pZmVzdCgpO1xuICAgIGNvbnN0IG1ldGEgPSBtYW5pZmVzdC52b2x1bWU7XG4gICAgaWYgKCFtZXRhKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKCdbc3BhdGlhbF0gdGhpcyBkYXRhc2V0IGhhcyBubyByZWZlcmVuY2Ugdm9sdW1lJykpO1xuICAgIH1cbiAgICB0aGlzLnZvbHVtZVByb21pc2UgPz89IHRoaXNcbiAgICAgIC5nZXRCaW5hcnkoYHNwYXRpYWwvJHtlbmNvZGVVUklDb21wb25lbnQobWFuaWZlc3QuaWQpfS92b2x1bWVgKVxuICAgICAgLnRoZW4oKGJ1ZikgPT4ge1xuICAgICAgICBjb25zdCB3YW50ID0gbWV0YS53aWR0aCAqIG1ldGEuaGVpZ2h0ICogbWV0YS5kZXB0aDtcbiAgICAgICAgaWYgKGJ1Zi5ieXRlTGVuZ3RoICE9PSB3YW50KSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYFtzcGF0aWFsXSB2b2x1bWUgaXMgJHtidWYuYnl0ZUxlbmd0aH0gYnl0ZXMsIGV4cGVjdGVkICR7d2FudH0gYFxuICAgICAgICAgICAgKyBgKCR7bWV0YS53aWR0aH14JHttZXRhLmhlaWdodH14JHttZXRhLmRlcHRofSlgLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KGJ1Zik7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKChlcnIpID0+IHsgdGhpcy52b2x1bWVQcm9taXNlID0gbnVsbDsgdGhyb3cgZXJyOyB9KTtcbiAgICByZXR1cm4gdGhpcy52b2x1bWVQcm9taXNlO1xuICB9XG5cbiAgLy8g4pSA4pSAIGludGVybmFscyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICBwcml2YXRlIHJlcXVpcmVNYW5pZmVzdCgpOiBTcGF0aWFsTWFuaWZlc3Qge1xuICAgIGlmICghdGhpcy5tYW5pZmVzdCB8fCAhdGhpcy5kYXRhc2V0JC52YWx1ZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdbc3BhdGlhbF0gbm8gZGF0YXNldCBzZWxlY3RlZCDigJQgY2FsbCBzZWxlY3REYXRhc2V0KCkgZmlyc3QnKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMubWFuaWZlc3Q7XG4gIH1cblxuICAvKipcbiAgICogQ2FjaGUgKyBzaW5nbGUtZmxpZ2h0IGFyb3VuZCBhIHZlY3RvciBmZXRjaC5cbiAgICpcbiAgICogS2V5cyBhcmUgTE9HSUNBTCDigJQgYGZlYXR1cmU6R0FQREhgLCBub3QgYDxkYXRhc2V0Pi9mZWF0dXJlOkdBUERIYCDigJQgYmVjYXVzZSBgY2xlYXIoKWBcbiAgICogZW1wdGllcyB0aGUgbWFwcyBvbiBldmVyeSBzd2l0Y2gsIHNvIHR3byBkYXRhc2V0cyBuZXZlciBob2xkIGVudHJpZXMgYXQgb25jZS4gV2hhdFxuICAgKiB0aGF0IGRvZXMgbm90IGNvdmVyIGlzIGEgcmVxdWVzdCBzdGlsbCBpbiB0aGUgYWlyIHdoZW4gdGhlIHN3aXRjaCBoYXBwZW5zOiBgY2xlYXIoKWBcbiAgICogY2Fubm90IGNhbmNlbCBhbiBIVFRQIGNhbGwsIHNvIGEgdmVjdG9yIGZldGNoZWQgZm9yIGRhdGFzZXQgQSBjYW4gcmVzb2x2ZSBhZnRlciBCIGhhc1xuICAgKiBiZWVuIHNlbGVjdGVkIGFuZCB3cml0ZSBpdHNlbGYgaW50byBCJ3MgY2FjaGUgdW5kZXIgYSBrZXkgQiB3b3VsZCByZWFkLiBCb3RoIGRhdGFzZXRzXG4gICAqIGhhdmluZyBhIGdlbmUgb2YgdGhhdCBuYW1lIGlzIHRoZSBjb21tb24gY2FzZSwgbm90IHRoZSB1bmx1Y2t5IG9uZSwgYW5kIHRoZSByZXN1bHQgaXNcbiAgICogYSBwbG90IG9mIEEncyBleHByZXNzaW9uIG92ZXIgQidzIGNlbGxzIOKAlCB3aGljaCBsb29rcyBsaWtlIGRhdGEsIG5vdCBsaWtlIGEgZmF1bHQuXG4gICAqXG4gICAqIFNvIHRoZSBnZW5lcmF0aW9uIGlzIGNoZWNrZWQgYWZ0ZXIgdGhlIGF3YWl0LCBhbmQgdGhlIGluLWZsaWdodCBzbG90IGlzIGNsZWFyZWQgb25seVxuICAgKiB3aGVuIHRoZSBwcm9taXNlIHN0aWxsIHNpdHRpbmcgaW4gaXQgaXMgdGhpcyBvbmU6IGEgbGF0ZSBhcnJpdmFsIG11c3Qgbm90IGV2aWN0IHRoZVxuICAgKiBlbnRyeSBhIG5ld2VyIHJlcXVlc3QgZm9yIHRoZSBzYW1lIGtleSBpcyBhbHJlYWR5IHNoYXJpbmcuXG4gICAqL1xuICBwcml2YXRlIGZldGNoQ2FjaGVkKGtleTogc3RyaW5nLCBsb2FkOiAoKSA9PiBQcm9taXNlPENhY2hlZFBheWxvYWQ+KTogUHJvbWlzZTxDYWNoZWRQYXlsb2FkPiB7XG4gICAgY29uc3QgaGl0ID0gdGhpcy5jYWNoZS5nZXQoa2V5KTtcbiAgICBpZiAoaGl0KSB7XG4gICAgICAvLyBSZS1pbnNlcnQgdG8gbWFyayBtb3N0LXJlY2VudGx5LXVzZWQgKE1hcCBwcmVzZXJ2ZXMgaW5zZXJ0aW9uIG9yZGVyKS5cbiAgICAgIHRoaXMuY2FjaGUuZGVsZXRlKGtleSk7XG4gICAgICB0aGlzLmNhY2hlLnNldChrZXksIGhpdCk7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGhpdCk7XG4gICAgfVxuICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLmluRmxpZ2h0LmdldChrZXkpO1xuICAgIGlmIChwZW5kaW5nKSByZXR1cm4gcGVuZGluZztcblxuICAgIGNvbnN0IG1pbmUgPSB0aGlzLnNlbGVjdFRva2VuO1xuICAgIGNvbnN0IHByb21pc2U6IFByb21pc2U8Q2FjaGVkUGF5bG9hZD4gPSBsb2FkKClcbiAgICAgIC50aGVuKCh2YWx1ZSkgPT4ge1xuICAgICAgICAvLyBTdGlsbCByZXR1cm5lZCB0byB0aGUgY2FsbGVyIHRoYXQgYXNrZWQ6IGl0IG1heSB3ZWxsIGJlIGEgY29tcG9uZW50IHRoYXQgaXNcbiAgICAgICAgLy8gaXRzZWxmIGJlaW5nIHRvcm4gZG93biwgYW5kIHJlamVjdGluZyBoZXJlIHdvdWxkIHN1cmZhY2UgYSBzd2l0Y2ggYXMgYW4gZXJyb3IuXG4gICAgICAgIC8vIFdoYXQgbXVzdCBub3QgaGFwcGVuIGlzIHRoZSB2YWx1ZSBiZWluZyBrZXB0IGZvciB3aG9ldmVyIGNvbWVzIG5leHQuXG4gICAgICAgIGlmIChtaW5lID09PSB0aGlzLnNlbGVjdFRva2VuKSB7XG4gICAgICAgICAgdGhpcy5jYWNoZS5zZXQoa2V5LCB2YWx1ZSk7XG4gICAgICAgICAgdGhpcy5ldmljdCgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgIH0pXG4gICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLmluRmxpZ2h0LmdldChrZXkpID09PSBwcm9taXNlKSB0aGlzLmluRmxpZ2h0LmRlbGV0ZShrZXkpO1xuICAgICAgfSk7XG4gICAgdGhpcy5pbkZsaWdodC5zZXQoa2V5LCBwcm9taXNlKTtcbiAgICByZXR1cm4gcHJvbWlzZTtcbiAgfVxuXG4gIHByaXZhdGUgZXZpY3QoKTogdm9pZCB7XG4gICAgd2hpbGUgKHRoaXMuY2FjaGUuc2l6ZSA+IFNwYXRpYWxEYXRhSHR0cFNlcnZpY2UuQ0FDSEVfTElNSVQpIHtcbiAgICAgIGNvbnN0IG9sZGVzdCA9IHRoaXMuY2FjaGUua2V5cygpLm5leHQoKS52YWx1ZSBhcyBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gICAgICBpZiAob2xkZXN0ID09PSB1bmRlZmluZWQpIGJyZWFrO1xuICAgICAgdGhpcy5jYWNoZS5kZWxldGUob2xkZXN0KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldEpzb248VD4ocGF0aDogc3RyaW5nKTogUHJvbWlzZTxUPiB7XG4gICAgcmV0dXJuIGZpcnN0VmFsdWVGcm9tKFxuICAgICAgdGhpcy5odHRwLmdldDxUPihgJHt0aGlzLmJhc2VVcmx9JHtwYXRofWApLnBpcGUodGltZW91dCh0aGlzLnRpbWVvdXRNcykpLFxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIGdldEJpbmFyeShwYXRoOiBzdHJpbmcpOiBQcm9taXNlPEFycmF5QnVmZmVyPiB7XG4gICAgcmV0dXJuIGZpcnN0VmFsdWVGcm9tKFxuICAgICAgdGhpcy5odHRwLmdldChgJHt0aGlzLmJhc2VVcmx9JHtwYXRofWAsIHsgcmVzcG9uc2VUeXBlOiAnYXJyYXlidWZmZXInIH0pXG4gICAgICAgIC5waXBlKHRpbWVvdXQodGhpcy50aW1lb3V0TXMpKSxcbiAgICApO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { NO_CATEGORY, } from '../../contracts/spatial-dataset.contract';
|
|
2
|
+
/**
|
|
3
|
+
* The wire format the bundled example server speaks, and its decoders.
|
|
4
|
+
*
|
|
5
|
+
* Pure by design — no Angular, no RxJS, no HTTP — so the format is testable on
|
|
6
|
+
* its own and reusable by any transport (`SpatialDataHttpService` is one).
|
|
7
|
+
* Mirrors the split in `plotly-trace-builders.ts`: the shape lives in a pure
|
|
8
|
+
* module, the service just moves bytes.
|
|
9
|
+
*
|
|
10
|
+
* WHY BINARY
|
|
11
|
+
* ----------
|
|
12
|
+
* Vectors are one `Float32Array`/`Uint16Array` per request, delivered as raw
|
|
13
|
+
* little-endian bytes. JSON would cost ~8–12× the size and a parse that
|
|
14
|
+
* allocates one JS number per value; a typed-array view over the response
|
|
15
|
+
* buffer is zero-copy and uploads to the GPU directly.
|
|
16
|
+
*
|
|
17
|
+
* ENDIANNESS
|
|
18
|
+
* ----------
|
|
19
|
+
* Little-endian, asserted at decode. Every platform a browser runs on today is
|
|
20
|
+
* little-endian, so a `DataView` byte-by-byte read would cost real time for a
|
|
21
|
+
* case that does not occur — but failing loudly beats rendering transposed
|
|
22
|
+
* garbage if it ever does.
|
|
23
|
+
*
|
|
24
|
+
* ENDPOINTS (base = the server root)
|
|
25
|
+
* ----------------------------------
|
|
26
|
+
* ```
|
|
27
|
+
* GET {base}/spatial/datasets -> { datasets: [{ id, name, count }] }
|
|
28
|
+
* GET {base}/spatial/{id}/manifest -> SpatialManifest
|
|
29
|
+
* GET {base}/spatial/{id}/coords -> f32[N] x, f32[N] y, f32[N] z?
|
|
30
|
+
* GET {base}/spatial/{id}/radius -> f32[N] (per-observation radius only)
|
|
31
|
+
* GET {base}/spatial/{id}/ids -> { ids: string[] } (hasIds only)
|
|
32
|
+
* GET {base}/spatial/{id}/column/{name} -> u16[N] codes | f32[N] values
|
|
33
|
+
* GET {base}/spatial/{id}/feature/{name} -> f32[N]
|
|
34
|
+
* GET {base}/spatial/{id}/features?q=&limit= -> { names: string[] }
|
|
35
|
+
* GET {base}/spatial/{id}/polygons -> u32 count, u32[count+1] offsets, f32[2*rings] coords
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
/** Bumped when the layout changes incompatibly; the client refuses anything else. */
|
|
39
|
+
export const SPATIAL_WIRE_VERSION = 1;
|
|
40
|
+
/** True on a little-endian host. */
|
|
41
|
+
export function isLittleEndian() {
|
|
42
|
+
const probe = new Uint16Array([1]);
|
|
43
|
+
return new Uint8Array(probe.buffer)[0] === 1;
|
|
44
|
+
}
|
|
45
|
+
function assertLittleEndian() {
|
|
46
|
+
if (!isLittleEndian()) {
|
|
47
|
+
throw new Error('[spatial] wire format is little-endian; this platform is big-endian. ' +
|
|
48
|
+
'Serve JSON vectors, or byte-swap in a custom SpatialDataPort adapter.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Guard a decode against a truncated or oversized response. */
|
|
52
|
+
function assertByteLength(buf, expected, what) {
|
|
53
|
+
if (buf.byteLength !== expected) {
|
|
54
|
+
throw new Error(`[spatial] ${what}: expected ${expected} bytes, got ${buf.byteLength}. ` +
|
|
55
|
+
'Manifest count and served vector length disagree.');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Reject a manifest this client cannot read, with a message naming the fix. */
|
|
59
|
+
export function assertManifestVersion(manifest) {
|
|
60
|
+
if (manifest.version !== SPATIAL_WIRE_VERSION) {
|
|
61
|
+
throw new Error(`[spatial] unsupported wire version ${manifest.version} ` +
|
|
62
|
+
`(this client speaks ${SPATIAL_WIRE_VERSION}). Update the server or the library.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `GET /coords` → x/y(/z). One request rather than two or three: the vectors
|
|
67
|
+
* are always fetched together, and a single response keeps them consistent if
|
|
68
|
+
* the dataset changes underneath.
|
|
69
|
+
*/
|
|
70
|
+
export function decodeCoords(buf, count, hasZ = false) {
|
|
71
|
+
assertLittleEndian();
|
|
72
|
+
const axes = hasZ ? 3 : 2;
|
|
73
|
+
assertByteLength(buf, count * axes * 4, 'coords');
|
|
74
|
+
const x = new Float32Array(buf, 0, count);
|
|
75
|
+
const y = new Float32Array(buf, count * 4, count);
|
|
76
|
+
const z = hasZ ? new Float32Array(buf, count * 8, count) : undefined;
|
|
77
|
+
return { x, y, z };
|
|
78
|
+
}
|
|
79
|
+
/** `GET /radius` → per-observation radii in image pixels. */
|
|
80
|
+
export function decodeRadius(buf, count) {
|
|
81
|
+
assertLittleEndian();
|
|
82
|
+
assertByteLength(buf, count * 4, 'radius');
|
|
83
|
+
return new Float32Array(buf, 0, count);
|
|
84
|
+
}
|
|
85
|
+
/** `GET /feature/{name}` → one gene's expression vector. */
|
|
86
|
+
/**
|
|
87
|
+
* `GET /spatial/{id}/embedding/{name}` — one plane per dimension, same layout as coords.
|
|
88
|
+
*
|
|
89
|
+
* Struct of arrays, not interleaved: every dimension is contiguous, so an axis can be handed to
|
|
90
|
+
* the renderer as a view with no copy and no stride.
|
|
91
|
+
*/
|
|
92
|
+
export function decodeEmbedding(buf, meta, count) {
|
|
93
|
+
assertLittleEndian();
|
|
94
|
+
assertByteLength(buf, count * meta.dims * 4, `embedding "${meta.name}"`);
|
|
95
|
+
return {
|
|
96
|
+
meta,
|
|
97
|
+
x: new Float32Array(buf, 0, count),
|
|
98
|
+
y: new Float32Array(buf, count * 4, count),
|
|
99
|
+
...(meta.dims === 3 ? { z: new Float32Array(buf, count * 8, count) } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function decodeFeatureVector(buf, count) {
|
|
103
|
+
assertLittleEndian();
|
|
104
|
+
assertByteLength(buf, count * 4, 'feature vector');
|
|
105
|
+
return new Float32Array(buf, 0, count);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* `GET /column/{name}` → a loaded column, typed by its descriptor: `u16` codes
|
|
109
|
+
* for a categorical, `f32` values for a continuous. Codes outside the category
|
|
110
|
+
* list are normalised to {@link NO_CATEGORY} so a renderer can trust the
|
|
111
|
+
* invariant instead of bounds-checking every point.
|
|
112
|
+
*/
|
|
113
|
+
export function decodeColumn(buf, meta, count) {
|
|
114
|
+
assertLittleEndian();
|
|
115
|
+
if (meta.kind === 'categorical') {
|
|
116
|
+
assertByteLength(buf, count * 2, `column "${meta.name}"`);
|
|
117
|
+
const codes = new Uint16Array(buf, 0, count);
|
|
118
|
+
const n = meta.categories.length;
|
|
119
|
+
for (let i = 0; i < codes.length; i++) {
|
|
120
|
+
if (codes[i] >= n)
|
|
121
|
+
codes[i] = NO_CATEGORY;
|
|
122
|
+
}
|
|
123
|
+
return { meta: meta, codes };
|
|
124
|
+
}
|
|
125
|
+
assertByteLength(buf, count * 4, `column "${meta.name}"`);
|
|
126
|
+
return { meta: meta, values: new Float32Array(buf, 0, count) };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* `GET /polygons` → boundary rings. Layout is
|
|
130
|
+
* `[u32 count][u32 offsets × (count+1)][f32 coords × 2·offsets[count]]`; every
|
|
131
|
+
* field is 4-byte wide so each typed-array view stays aligned.
|
|
132
|
+
*/
|
|
133
|
+
export function decodePolygons(buf) {
|
|
134
|
+
assertLittleEndian();
|
|
135
|
+
if (buf.byteLength < 8) {
|
|
136
|
+
throw new Error('[spatial] polygons: response too short for a header');
|
|
137
|
+
}
|
|
138
|
+
const count = new Uint32Array(buf, 0, 1)[0];
|
|
139
|
+
const offsets = new Uint32Array(buf, 4, count + 1);
|
|
140
|
+
const vertexCount = offsets[count];
|
|
141
|
+
const coordsByteOffset = 4 + (count + 1) * 4;
|
|
142
|
+
assertByteLength(buf, coordsByteOffset + vertexCount * 2 * 4, 'polygons');
|
|
143
|
+
const coords = new Float32Array(buf, coordsByteOffset, vertexCount * 2);
|
|
144
|
+
return { coords, offsets, count };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Fold a manifest plus its fetched coordinate/id/radius vectors into the
|
|
148
|
+
* library-facing {@link SpatialDataset}. Column and feature *values* are not
|
|
149
|
+
* part of this — they stay lazy behind the port.
|
|
150
|
+
*/
|
|
151
|
+
export function datasetFromManifest(manifest, coords, extras = {}) {
|
|
152
|
+
assertManifestVersion(manifest);
|
|
153
|
+
const radius = manifest.radius?.mode === 'uniform'
|
|
154
|
+
? manifest.radius.value
|
|
155
|
+
: extras.radius;
|
|
156
|
+
const observations = {
|
|
157
|
+
count: manifest.count,
|
|
158
|
+
x: coords.x,
|
|
159
|
+
y: coords.y,
|
|
160
|
+
...(coords.z ? { z: coords.z } : {}),
|
|
161
|
+
...(extras.ids ? { ids: extras.ids } : {}),
|
|
162
|
+
...(radius !== undefined ? { radius } : {}),
|
|
163
|
+
};
|
|
164
|
+
return {
|
|
165
|
+
id: manifest.id,
|
|
166
|
+
name: manifest.name,
|
|
167
|
+
observations,
|
|
168
|
+
columns: manifest.columns ?? [],
|
|
169
|
+
...(manifest.features ? { features: manifest.features } : {}),
|
|
170
|
+
...(manifest.polygons ? { polygons: manifest.polygons } : {}),
|
|
171
|
+
...(manifest.imageRef ? { imageRef: manifest.imageRef } : {}),
|
|
172
|
+
...(manifest.volume ? { volume: manifest.volume } : {}),
|
|
173
|
+
...(manifest.embeddings?.length ? { embeddings: manifest.embeddings } : {}),
|
|
174
|
+
...(manifest.micronsPerUnit !== undefined
|
|
175
|
+
? { micronsPerUnit: manifest.micronsPerUnit }
|
|
176
|
+
: {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC13aXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9pbXBsZW1lbnRhdGlvbnMvc3BhdGlhbC1kYXRhLWh0dHAvc3BhdGlhbC13aXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFHTCxXQUFXLEdBV1osTUFBTSwwQ0FBMEMsQ0FBQztBQUVsRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQ0c7QUFFSCxxRkFBcUY7QUFDckYsTUFBTSxDQUFDLE1BQU0sb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO0FBaUN0QyxvQ0FBb0M7QUFDcEMsTUFBTSxVQUFVLGNBQWM7SUFDNUIsTUFBTSxLQUFLLEdBQUcsSUFBSSxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ25DLE9BQU8sSUFBSSxVQUFVLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUMvQyxDQUFDO0FBRUQsU0FBUyxrQkFBa0I7SUFDekIsSUFBSSxDQUFDLGNBQWMsRUFBRSxFQUFFLENBQUM7UUFDdEIsTUFBTSxJQUFJLEtBQUssQ0FDYix1RUFBdUU7WUFDdkUsdUVBQXVFLENBQ3hFLENBQUM7SUFDSixDQUFDO0FBQ0gsQ0FBQztBQUVELGdFQUFnRTtBQUNoRSxTQUFTLGdCQUFnQixDQUFDLEdBQWdCLEVBQUUsUUFBZ0IsRUFBRSxJQUFZO0lBQ3hFLElBQUksR0FBRyxDQUFDLFVBQVUsS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUNoQyxNQUFNLElBQUksS0FBSyxDQUNiLGFBQWEsSUFBSSxjQUFjLFFBQVEsZUFBZSxHQUFHLENBQUMsVUFBVSxJQUFJO1lBQ3hFLG1EQUFtRCxDQUNwRCxDQUFDO0lBQ0osQ0FBQztBQUNILENBQUM7QUFFRCxnRkFBZ0Y7QUFDaEYsTUFBTSxVQUFVLHFCQUFxQixDQUFDLFFBQXlCO0lBQzdELElBQUks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QyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDMUMsR0FBRyxDQUFDLE1BQU0sS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztLQUM1QyxDQUFDO0lBQ0YsT0FBTztRQUNMLEVBQUUsRUFBRSxRQUFRLENBQUMsRUFBRTtRQUNmLElBQUksRUFBRSxRQUFRLENBQUMsSUFBSTtRQUNuQixZQUFZO1FBQ1osT0FBTyxFQUFFLFFBQVEsQ0FBQyxPQUFPLElBQUksRUFBRTtRQUMvQixHQUFHLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDN0QsR0FBRyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzdELEdBQUcsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUM3RCxHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDdkQsR0FBRyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMzRSxHQUFHLENBQUMsUUFBUSxDQUFDLGNBQWMsS0FBSyxTQUFTO1lBQ3ZDLENBQUMsQ0FBQyxFQUFFLGNBQWMsRUFBRSxRQUFRLENBQUMsY0FBYyxFQUFFO1lBQzdDLENBQUMsQ0FBQyxFQUFFLENBQUM7S0FDUixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENhdGVnb3JpY2FsQ29sdW1uTWV0YSxcbiAgQ29udGludW91c0NvbHVtbk1ldGEsXG4gIE5PX0NBVEVHT1JZLFxuICBTcGF0aWFsQ29sdW1uLFxuICBTcGF0aWFsQ29sdW1uTWV0YSxcbiAgU3BhdGlhbERhdGFzZXQsXG4gIFNwYXRpYWxFbWJlZGRpbmcsXG4gIFNwYXRpYWxFbWJlZGRpbmdNZXRhLFxuICBTcGF0aWFsRmVhdHVyZU1ldGEsXG4gIFNwYXRpYWxJbWFnZVJlZixcbiAgU3BhdGlhbE9ic2VydmF0aW9ucyxcbiAgU3BhdGlhbFBvbHlnb25zLFxuICBTcGF0aWFsVm9sdW1lTWV0YSxcbn0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5cbi8qKlxuICogVGhlIHdpcmUgZm9ybWF0IHRoZSBidW5kbGVkIGV4YW1wbGUgc2VydmVyIHNwZWFrcywgYW5kIGl0cyBkZWNvZGVycy5cbiAqXG4gKiBQdXJlIGJ5IGRlc2lnbiDigJQgbm8gQW5ndWxhciwgbm8gUnhKUywgbm8gSFRUUCDigJQgc28gdGhlIGZvcm1hdCBpcyB0ZXN0YWJsZSBvblxuICogaXRzIG93biBhbmQgcmV1c2FibGUgYnkgYW55IHRyYW5zcG9ydCAoYFNwYXRpYWxEYXRhSHR0cFNlcnZpY2VgIGlzIG9uZSkuXG4gKiBNaXJyb3JzIHRoZSBzcGxpdCBpbiBgcGxvdGx5LXRyYWNlLWJ1aWxkZXJzLnRzYDogdGhlIHNoYXBlIGxpdmVzIGluIGEgcHVyZVxuICogbW9kdWxlLCB0aGUgc2VydmljZSBqdXN0IG1vdmVzIGJ5dGVzLlxuICpcbiAqIFdIWSBCSU5BUllcbiAqIC0tLS0tLS0tLS1cbiAqIFZlY3RvcnMgYXJlIG9uZSBgRmxvYXQzMkFycmF5YC9gVWludDE2QXJyYXlgIHBlciByZXF1ZXN0LCBkZWxpdmVyZWQgYXMgcmF3XG4gKiBsaXR0bGUtZW5kaWFuIGJ5dGVzLiBKU09OIHdvdWxkIGNvc3QgfjjigJMxMsOXIHRoZSBzaXplIGFuZCBhIHBhcnNlIHRoYXRcbiAqIGFsbG9jYXRlcyBvbmUgSlMgbnVtYmVyIHBlciB2YWx1ZTsgYSB0eXBlZC1hcnJheSB2aWV3IG92ZXIgdGhlIHJlc3BvbnNlXG4gKiBidWZmZXIgaXMgemVyby1jb3B5IGFuZCB1cGxvYWRzIHRvIHRoZSBHUFUgZGlyZWN0bHkuXG4gKlxuICogRU5ESUFOTkVTU1xuICogLS0tLS0tLS0tLVxuICogTGl0dGxlLWVuZGlhbiwgYXNzZXJ0ZWQgYXQgZGVjb2RlLiBFdmVyeSBwbGF0Zm9ybSBhIGJyb3dzZXIgcnVucyBvbiB0b2RheSBpc1xuICogbGl0dGxlLWVuZGlhbiwgc28gYSBgRGF0YVZpZXdgIGJ5dGUtYnktYnl0ZSByZWFkIHdvdWxkIGNvc3QgcmVhbCB0aW1lIGZvciBhXG4gKiBjYXNlIHRoYXQgZG9lcyBub3Qgb2NjdXIg4oCUIGJ1dCBmYWlsaW5nIGxvdWRseSBiZWF0cyByZW5kZXJpbmcgdHJhbnNwb3NlZFxuICogZ2FyYmFnZSBpZiBpdCBldmVyIGRvZXMuXG4gKlxuICogRU5EUE9JTlRTIChiYXNlID0gdGhlIHNlcnZlciByb290KVxuICogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogYGBgXG4gKiBHRVQge2Jhc2V9L3NwYXRpYWwvZGF0YXNldHMgICAgICAgICAgICAgIC0+IHsgZGF0YXNldHM6IFt7IGlkLCBuYW1lLCBjb3VudCB9XSB9XG4gKiBHRVQge2Jhc2V9L3NwYXRpYWwve2lkfS9tYW5pZmVzdCAgICAgICAgIC0+IFNwYXRpYWxNYW5pZmVzdFxuICogR0VUIHtiYXNlfS9zcGF0aWFsL3tpZH0vY29vcmRzICAgICAgICAgICAtPiBmMzJbTl0geCwgZjMyW05dIHksIGYzMltOXSB6P1xuICogR0VUIHtiYXNlfS9zcGF0aWFsL3tpZH0vcmFkaXVzICAgICAgICAgICAtPiBmMzJbTl0gICAgICAgICAgIChwZXItb2JzZXJ2YXRpb24gcmFkaXVzIG9ubHkpXG4gKiBHRVQge2Jhc2V9L3NwYXRpYWwve2lkfS9pZHMgICAgICAgICAgICAgIC0+IHsgaWRzOiBzdHJpbmdbXSB9IChoYXNJZHMgb25seSlcbiAqIEdFVCB7YmFzZX0vc3BhdGlhbC97aWR9L2NvbHVtbi97bmFtZX0gICAgLT4gdTE2W05dIGNvZGVzIHwgZjMyW05dIHZhbHVlc1xuICogR0VUIHtiYXNlfS9zcGF0aWFsL3tpZH0vZmVhdHVyZS97bmFtZX0gICAtPiBmMzJbTl1cbiAqIEdFVCB7YmFzZX0vc3BhdGlhbC97aWR9L2ZlYXR1cmVzP3E9JmxpbWl0PSAtPiB7IG5hbWVzOiBzdHJpbmdbXSB9XG4gKiBHRVQge2Jhc2V9L3NwYXRpYWwve2lkfS9wb2x5Z29ucyAgICAgICAgIC0+IHUzMiBjb3VudCwgdTMyW2NvdW50KzFdIG9mZnNldHMsIGYzMlsyKnJpbmdzXSBjb29yZHNcbiAqIGBgYFxuICovXG5cbi8qKiBCdW1wZWQgd2hlbiB0aGUgbGF5b3V0IGNoYW5nZXMgaW5jb21wYXRpYmx5OyB0aGUgY2xpZW50IHJlZnVzZXMgYW55dGhpbmcgZWxzZS4gKi9cbmV4cG9ydCBjb25zdCBTUEFUSUFMX1dJUkVfVkVSU0lPTiA9IDE7XG5cbi8qKiBQZXItb2JzZXJ2YXRpb24gbWFya2VyIHJhZGl1czogb25lIHNoYXJlZCB2YWx1ZSwgb3IgYSBzZXJ2ZWQgZjMyIHZlY3Rvci4gKi9cbmV4cG9ydCB0eXBlIFNwYXRpYWxSYWRpdXNTcGVjID1cbiAgfCB7IG1vZGU6ICd1bmlmb3JtJzsgdmFsdWU6IG51bWJlciB9XG4gIHwgeyBtb2RlOiAncGVyLW9ic2VydmF0aW9uJyB9O1xuXG4vKiogYEdFVCAvc3BhdGlhbC97aWR9L21hbmlmZXN0YCDigJQgZXZlcnl0aGluZyBjaGVhcCBlbm91Z2ggdG8gc2VuZCB1cCBmcm9udC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3BhdGlhbE1hbmlmZXN0IHtcbiAgdmVyc2lvbjogbnVtYmVyO1xuICBpZDogc3RyaW5nO1xuICBuYW1lOiBzdHJpbmc7XG4gIC8qKiBOIOKAlCBvYnNlcnZhdGlvbiBjb3VudDsgZXZlcnkgc2VydmVkIHZlY3RvciBoYXMgZXhhY3RseSB0aGlzIGxlbmd0aC4gKi9cbiAgY291bnQ6IG51bWJlcjtcbiAgaGFzWj86IGJvb2xlYW47XG4gIGhhc0lkcz86IGJvb2xlYW47XG4gIHJhZGl1cz86IFNwYXRpYWxSYWRpdXNTcGVjO1xuICBjb2x1bW5zOiBTcGF0aWFsQ29sdW1uTWV0YVtdO1xuICBmZWF0dXJlcz86IFNwYXRpYWxGZWF0dXJlTWV0YTtcbiAgcG9seWdvbnM/OiB7IGNvdW50OiBudW1iZXIgfTtcbiAgaW1hZ2VSZWY/OiBTcGF0aWFsSW1hZ2VSZWY7XG4gIHZvbHVtZT86IFNwYXRpYWxWb2x1bWVNZXRhO1xuICBlbWJlZGRpbmdzPzogU3BhdGlhbEVtYmVkZGluZ01ldGFbXTtcbiAgbWljcm9uc1BlclVuaXQ/OiBudW1iZXI7XG59XG5cbi8qKiBgR0VUIC9zcGF0aWFsL2RhdGFzZXRzYCAqL1xuZXhwb3J0IGludGVyZmFjZSBTcGF0aWFsRGF0YXNldFN1bW1hcnkge1xuICBpZDogc3RyaW5nO1xuICBuYW1lOiBzdHJpbmc7XG4gIGNvdW50OiBudW1iZXI7XG59XG5cbi8qKiBUcnVlIG9uIGEgbGl0dGxlLWVuZGlhbiBob3N0LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzTGl0dGxlRW5kaWFuKCk6IGJvb2xlYW4ge1xuICBjb25zdCBwcm9iZSA9IG5ldyBVaW50MTZBcnJheShbMV0pO1xuICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkocHJvYmUuYnVmZmVyKVswXSA9PT0gMTtcbn1cblxuZnVuY3Rpb24gYXNzZXJ0TGl0dGxlRW5kaWFuKCk6IHZvaWQge1xuICBpZiAoIWlzTGl0dGxlRW5kaWFuKCkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAnW3NwYXRpYWxdIHdpcmUgZm9ybWF0IGlzIGxpdHRsZS1lbmRpYW47IHRoaXMgcGxhdGZvcm0gaXMgYmlnLWVuZGlhbi4gJyArXG4gICAgICAnU2VydmUgSlNPTiB2ZWN0b3JzLCBvciBieXRlLXN3YXAgaW4gYSBjdXN0b20gU3BhdGlhbERhdGFQb3J0IGFkYXB0ZXIuJyxcbiAgICApO1xuICB9XG59XG5cbi8qKiBHdWFyZCBhIGRlY29kZSBhZ2FpbnN0IGEgdHJ1bmNhdGVkIG9yIG92ZXJzaXplZCByZXNwb25zZS4gKi9cbmZ1bmN0aW9uIGFzc2VydEJ5dGVMZW5ndGgoYnVmOiBBcnJheUJ1ZmZlciwgZXhwZWN0ZWQ6IG51bWJlciwgd2hhdDogc3RyaW5nKTogdm9pZCB7XG4gIGlmIChidWYuYnl0ZUxlbmd0aCAhPT0gZXhwZWN0ZWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgW3NwYXRpYWxdICR7d2hhdH06IGV4cGVjdGVkICR7ZXhwZWN0ZWR9IGJ5dGVzLCBnb3QgJHtidWYuYnl0ZUxlbmd0aH0uIGAgK1xuICAgICAgJ01hbmlmZXN0IGNvdW50IGFuZCBzZXJ2ZWQgdmVjdG9yIGxlbmd0aCBkaXNhZ3JlZS4nLFxuICAgICk7XG4gIH1cbn1cblxuLyoqIFJlamVjdCBhIG1hbmlmZXN0IHRoaXMgY2xpZW50IGNhbm5vdCByZWFkLCB3aXRoIGEgbWVzc2FnZSBuYW1pbmcgdGhlIGZpeC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnRNYW5pZmVzdFZlcnNpb24obWFuaWZlc3Q6IFNwYXRpYWxNYW5pZmVzdCk6IHZvaWQge1xuICBpZiAobWFuaWZlc3QudmVyc2lvbiAhPT0gU1BBVElBTF9XSVJFX1ZFUlNJT04pIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgW3NwYXRpYWxdIHVuc3VwcG9ydGVkIHdpcmUgdmVyc2lvbiAke21hbmlmZXN0LnZlcnNpb259IGAgK1xuICAgICAgYCh0aGlzIGNsaWVudCBzcGVha3MgJHtTUEFUSUFMX1dJUkVfVkVSU0lPTn0pLiBVcGRhdGUgdGhlIHNlcnZlciBvciB0aGUgbGlicmFyeS5gLFxuICAgICk7XG4gIH1cbn1cblxuLyoqXG4gKiBgR0VUIC9jb29yZHNgIOKGkiB4L3koL3opLiBPbmUgcmVxdWVzdCByYXRoZXIgdGhhbiB0d28gb3IgdGhyZWU6IHRoZSB2ZWN0b3JzXG4gKiBhcmUgYWx3YXlzIGZldGNoZWQgdG9nZXRoZXIsIGFuZCBhIHNpbmdsZSByZXNwb25zZSBrZWVwcyB0aGVtIGNvbnNpc3RlbnQgaWZcbiAqIHRoZSBkYXRhc2V0IGNoYW5nZXMgdW5kZXJuZWF0aC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZUNvb3JkcyhcbiAgYnVmOiBBcnJheUJ1ZmZlciwgY291bnQ6IG51bWJlciwgaGFzWiA9IGZhbHNlLFxuKTogUGljazxTcGF0aWFsT2JzZXJ2YXRpb25zLCAneCcgfCAneScgfCAneic+IHtcbiAgYXNzZXJ0TGl0dGxlRW5kaWFuKCk7XG4gIGNvbnN0IGF4ZXMgPSBoYXNaID8gMyA6IDI7XG4gIGFzc2VydEJ5dGVMZW5ndGgoYnVmLCBjb3VudCAqIGF4ZXMgKiA0LCAnY29vcmRzJyk7XG4gIGNvbnN0IHggPSBuZXcgRmxvYXQzMkFycmF5KGJ1ZiwgMCwgY291bnQpO1xuICBjb25zdCB5ID0gbmV3IEZsb2F0MzJBcnJheShidWYsIGNvdW50ICogNCwgY291bnQpO1xuICBjb25zdCB6ID0gaGFzWiA/IG5ldyBGbG9hdDMyQXJyYXkoYnVmLCBjb3VudCAqIDgsIGNvdW50KSA6IHVuZGVmaW5lZDtcbiAgcmV0dXJuIHsgeCwgeSwgeiB9O1xufVxuXG4vKiogYEdFVCAvcmFkaXVzYCDihpIgcGVyLW9ic2VydmF0aW9uIHJhZGlpIGluIGltYWdlIHBpeGVscy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVSYWRpdXMoYnVmOiBBcnJheUJ1ZmZlciwgY291bnQ6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGFzc2VydExpdHRsZUVuZGlhbigpO1xuICBhc3NlcnRCeXRlTGVuZ3RoKGJ1ZiwgY291bnQgKiA0LCAncmFkaXVzJyk7XG4gIHJldHVybiBuZXcgRmxvYXQzMkFycmF5KGJ1ZiwgMCwgY291bnQpO1xufVxuXG4vKiogYEdFVCAvZmVhdHVyZS97bmFtZX1gIOKGkiBvbmUgZ2VuZSdzIGV4cHJlc3Npb24gdmVjdG9yLiAqL1xuLyoqXG4gKiBgR0VUIC9zcGF0aWFsL3tpZH0vZW1iZWRkaW5nL3tuYW1lfWAg4oCUIG9uZSBwbGFuZSBwZXIgZGltZW5zaW9uLCBzYW1lIGxheW91dCBhcyBjb29yZHMuXG4gKlxuICogU3RydWN0IG9mIGFycmF5cywgbm90IGludGVybGVhdmVkOiBldmVyeSBkaW1lbnNpb24gaXMgY29udGlndW91cywgc28gYW4gYXhpcyBjYW4gYmUgaGFuZGVkIHRvXG4gKiB0aGUgcmVuZGVyZXIgYXMgYSB2aWV3IHdpdGggbm8gY29weSBhbmQgbm8gc3RyaWRlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGVjb2RlRW1iZWRkaW5nKFxuICBidWY6IEFycmF5QnVmZmVyLCBtZXRhOiBTcGF0aWFsRW1iZWRkaW5nTWV0YSwgY291bnQ6IG51bWJlcixcbik6IFNwYXRpYWxFbWJlZGRpbmcge1xuICBhc3NlcnRMaXR0bGVFbmRpYW4oKTtcbiAgYXNzZXJ0Qnl0ZUxlbmd0aChidWYsIGNvdW50ICogbWV0YS5kaW1zICogNCwgYGVtYmVkZGluZyBcIiR7bWV0YS5uYW1lfVwiYCk7XG4gIHJldHVybiB7XG4gICAgbWV0YSxcbiAgICB4OiBuZXcgRmxvYXQzMkFycmF5KGJ1ZiwgMCwgY291bnQpLFxuICAgIHk6IG5ldyBGbG9hdDMyQXJyYXkoYnVmLCBjb3VudCAqIDQsIGNvdW50KSxcbiAgICAuLi4obWV0YS5kaW1zID09PSAzID8geyB6OiBuZXcgRmxvYXQzMkFycmF5KGJ1ZiwgY291bnQgKiA4LCBjb3VudCkgfSA6IHt9KSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZUZlYXR1cmVWZWN0b3IoYnVmOiBBcnJheUJ1ZmZlciwgY291bnQ6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGFzc2VydExpdHRsZUVuZGlhbigpO1xuICBhc3NlcnRCeXRlTGVuZ3RoKGJ1ZiwgY291bnQgKiA0LCAnZmVhdHVyZSB2ZWN0b3InKTtcbiAgcmV0dXJuIG5ldyBGbG9hdDMyQXJyYXkoYnVmLCAwLCBjb3VudCk7XG59XG5cbi8qKlxuICogYEdFVCAvY29sdW1uL3tuYW1lfWAg4oaSIGEgbG9hZGVkIGNvbHVtbiwgdHlwZWQgYnkgaXRzIGRlc2NyaXB0b3I6IGB1MTZgIGNvZGVzXG4gKiBmb3IgYSBjYXRlZ29yaWNhbCwgYGYzMmAgdmFsdWVzIGZvciBhIGNvbnRpbnVvdXMuIENvZGVzIG91dHNpZGUgdGhlIGNhdGVnb3J5XG4gKiBsaXN0IGFyZSBub3JtYWxpc2VkIHRvIHtAbGluayBOT19DQVRFR09SWX0gc28gYSByZW5kZXJlciBjYW4gdHJ1c3QgdGhlXG4gKiBpbnZhcmlhbnQgaW5zdGVhZCBvZiBib3VuZHMtY2hlY2tpbmcgZXZlcnkgcG9pbnQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVDb2x1bW4oXG4gIGJ1ZjogQXJyYXlCdWZmZXIsIG1ldGE6IFNwYXRpYWxDb2x1bW5NZXRhLCBjb3VudDogbnVtYmVyLFxuKTogU3BhdGlhbENvbHVtbiB7XG4gIGFzc2VydExpdHRsZUVuZGlhbigpO1xuICBpZiAobWV0YS5raW5kID09PSAnY2F0ZWdvcmljYWwnKSB7XG4gICAgYXNzZXJ0Qnl0ZUxlbmd0aChidWYsIGNvdW50ICogMiwgYGNvbHVtbiBcIiR7bWV0YS5uYW1lfVwiYCk7XG4gICAgY29uc3QgY29kZXMgPSBuZXcgVWludDE2QXJyYXkoYnVmLCAwLCBjb3VudCk7XG4gICAgY29uc3QgbiA9IChtZXRhIGFzIENhdGVnb3JpY2FsQ29sdW1uTWV0YSkuY2F0ZWdvcmllcy5sZW5ndGg7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjb2Rlcy5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKGNvZGVzW2ldID49IG4pIGNvZGVzW2ldID0gTk9fQ0FURUdPUlk7XG4gICAgfVxuICAgIHJldHVybiB7IG1ldGE6IG1ldGEgYXMgQ2F0ZWdvcmljYWxDb2x1bW5NZXRhLCBjb2RlcyB9O1xuICB9XG4gIGFzc2VydEJ5dGVMZW5ndGgoYnVmLCBjb3VudCAqIDQsIGBjb2x1bW4gXCIke21ldGEubmFtZX1cImApO1xuICByZXR1cm4geyBtZXRhOiBtZXRhIGFzIENvbnRpbnVvdXNDb2x1bW5NZXRhLCB2YWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkoYnVmLCAwLCBjb3VudCkgfTtcbn1cblxuLyoqXG4gKiBgR0VUIC9wb2x5Z29uc2Ag4oaSIGJvdW5kYXJ5IHJpbmdzLiBMYXlvdXQgaXNcbiAqIGBbdTMyIGNvdW50XVt1MzIgb2Zmc2V0cyDDlyAoY291bnQrMSldW2YzMiBjb29yZHMgw5cgMsK3b2Zmc2V0c1tjb3VudF1dYDsgZXZlcnlcbiAqIGZpZWxkIGlzIDQtYnl0ZSB3aWRlIHNvIGVhY2ggdHlwZWQtYXJyYXkgdmlldyBzdGF5cyBhbGlnbmVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGVjb2RlUG9seWdvbnMoYnVmOiBBcnJheUJ1ZmZlcik6IFNwYXRpYWxQb2x5Z29ucyB7XG4gIGFzc2VydExpdHRsZUVuZGlhbigpO1xuICBpZiAoYnVmLmJ5dGVMZW5ndGggPCA4KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdbc3BhdGlhbF0gcG9seWdvbnM6IHJlc3BvbnNlIHRvbyBzaG9ydCBmb3IgYSBoZWFkZXInKTtcbiAgfVxuICBjb25zdCBjb3VudCA9IG5ldyBVaW50MzJBcnJheShidWYsIDAsIDEpWzBdO1xuICBjb25zdCBvZmZzZXRzID0gbmV3IFVpbnQzMkFycmF5KGJ1ZiwgNCwgY291bnQgKyAxKTtcbiAgY29uc3QgdmVydGV4Q291bnQgPSBvZmZzZXRzW2NvdW50XTtcbiAgY29uc3QgY29vcmRzQnl0ZU9mZnNldCA9IDQgKyAoY291bnQgKyAxKSAqIDQ7XG4gIGFzc2VydEJ5dGVMZW5ndGgoYnVmLCBjb29yZHNCeXRlT2Zmc2V0ICsgdmVydGV4Q291bnQgKiAyICogNCwgJ3BvbHlnb25zJyk7XG4gIGNvbnN0IGNvb3JkcyA9IG5ldyBGbG9hdDMyQXJyYXkoYnVmLCBjb29yZHNCeXRlT2Zmc2V0LCB2ZXJ0ZXhDb3VudCAqIDIpO1xuICByZXR1cm4geyBjb29yZHMsIG9mZnNldHMsIGNvdW50IH07XG59XG5cbi8qKlxuICogRm9sZCBhIG1hbmlmZXN0IHBsdXMgaXRzIGZldGNoZWQgY29vcmRpbmF0ZS9pZC9yYWRpdXMgdmVjdG9ycyBpbnRvIHRoZVxuICogbGlicmFyeS1mYWNpbmcge0BsaW5rIFNwYXRpYWxEYXRhc2V0fS4gQ29sdW1uIGFuZCBmZWF0dXJlICp2YWx1ZXMqIGFyZSBub3RcbiAqIHBhcnQgb2YgdGhpcyDigJQgdGhleSBzdGF5IGxhenkgYmVoaW5kIHRoZSBwb3J0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGF0YXNldEZyb21NYW5pZmVzdChcbiAgbWFuaWZlc3Q6IFNwYXRpYWxNYW5pZmVzdCxcbiAgY29vcmRzOiBQaWNrPFNwYXRpYWxPYnNlcnZhdGlvbnMsICd4JyB8ICd5JyB8ICd6Jz4sXG4gIGV4dHJhczogeyBpZHM/OiBzdHJpbmdbXTsgcmFkaXVzPzogRmxvYXQzMkFycmF5IH0gPSB7fSxcbik6IFNwYXRpYWxEYXRhc2V0IHtcbiAgYXNzZXJ0TWFuaWZlc3RWZXJzaW9uKG1hbmlmZXN0KTtcbiAgY29uc3QgcmFkaXVzID0gbWFuaWZlc3QucmFkaXVzPy5tb2RlID09PSAndW5pZm9ybSdcbiAgICA/IG1hbmlmZXN0LnJhZGl1cy52YWx1ZVxuICAgIDogZXh0cmFzLnJhZGl1cztcbiAgY29uc3Qgb2JzZXJ2YXRpb25zOiBTcGF0aWFsT2JzZXJ2YXRpb25zID0ge1xuICAgIGNvdW50OiBtYW5pZmVzdC5jb3VudCxcbiAgICB4OiBjb29yZHMueCxcbiAgICB5OiBjb29yZHMueSxcbiAgICAuLi4oY29vcmRzLnogPyB7IHo6IGNvb3Jkcy56IH0gOiB7fSksXG4gICAgLi4uKGV4dHJhcy5pZHMgPyB7IGlkczogZXh0cmFzLmlkcyB9IDoge30pLFxuICAgIC4uLihyYWRpdXMgIT09IHVuZGVmaW5lZCA/IHsgcmFkaXVzIH0gOiB7fSksXG4gIH07XG4gIHJldHVybiB7XG4gICAgaWQ6IG1hbmlmZXN0LmlkLFxuICAgIG5hbWU6IG1hbmlmZXN0Lm5hbWUsXG4gICAgb2JzZXJ2YXRpb25zLFxuICAgIGNvbHVtbnM6IG1hbmlmZXN0LmNvbHVtbnMgPz8gW10sXG4gICAgLi4uKG1hbmlmZXN0LmZlYXR1cmVzID8geyBmZWF0dXJlczogbWFuaWZlc3QuZmVhdHVyZXMgfSA6IHt9KSxcbiAgICAuLi4obWFuaWZlc3QucG9seWdvbnMgPyB7IHBvbHlnb25zOiBtYW5pZmVzdC5wb2x5Z29ucyB9IDoge30pLFxuICAgIC4uLihtYW5pZmVzdC5pbWFnZVJlZiA/IHsgaW1hZ2VSZWY6IG1hbmlmZXN0LmltYWdlUmVmIH0gOiB7fSksXG4gICAgLi4uKG1hbmlmZXN0LnZvbHVtZSA/IHsgdm9sdW1lOiBtYW5pZmVzdC52b2x1bWUgfSA6IHt9KSxcbiAgICAuLi4obWFuaWZlc3QuZW1iZWRkaW5ncz8ubGVuZ3RoID8geyBlbWJlZGRpbmdzOiBtYW5pZmVzdC5lbWJlZGRpbmdzIH0gOiB7fSksXG4gICAgLi4uKG1hbmlmZXN0Lm1pY3JvbnNQZXJVbml0ICE9PSB1bmRlZmluZWRcbiAgICAgID8geyBtaWNyb25zUGVyVW5pdDogbWFuaWZlc3QubWljcm9uc1BlclVuaXQgfVxuICAgICAgOiB7fSksXG4gIH07XG59XG4iXX0=
|