gx
chenyc
2025-02-12 ea42ff3ebee1eeb3fb29423aa848a249441db81c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/**
 * @license
 * Copyright 2018 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */
/**
 * Unit tests for passthrough IOHandlers.
 */
import * as tf from '../index';
import { BROWSER_ENVS, describeWithFlags } from '../jasmine_util';
const modelTopology1 = {
    'class_name': 'Sequential',
    'keras_version': '2.1.4',
    'config': [{
            'class_name': 'Dense',
            'config': {
                'kernel_initializer': {
                    'class_name': 'VarianceScaling',
                    'config': {
                        'distribution': 'uniform',
                        'scale': 1.0,
                        'seed': null,
                        'mode': 'fan_avg'
                    }
                },
                'name': 'dense',
                'kernel_constraint': null,
                'bias_regularizer': null,
                'bias_constraint': null,
                'dtype': 'float32',
                'activation': 'linear',
                'trainable': true,
                'kernel_regularizer': null,
                'bias_initializer': { 'class_name': 'Zeros', 'config': {} },
                'units': 1,
                'batch_input_shape': [null, 3],
                'use_bias': true,
                'activity_regularizer': null
            }
        }],
    'backend': 'tensorflow'
};
const weightSpecs1 = [
    {
        name: 'dense/kernel',
        shape: [3, 1],
        dtype: 'float32',
    },
    {
        name: 'dense/bias',
        shape: [1],
        dtype: 'float32',
    }
];
const weightData1 = new ArrayBuffer(16);
const artifacts1 = {
    modelTopology: modelTopology1,
    weightSpecs: weightSpecs1,
    weightData: weightData1,
};
describeWithFlags('Passthrough Saver', BROWSER_ENVS, () => {
    it('passes provided arguments through on save', async () => {
        const testStartDate = new Date();
        let savedArtifacts = null;
        async function saveHandler(artifacts) {
            savedArtifacts = artifacts;
            return {
                modelArtifactsInfo: {
                    dateSaved: testStartDate,
                    modelTopologyType: 'JSON',
                    modelTopologyBytes: JSON.stringify(modelTopology1).length,
                    weightSpecsBytes: JSON.stringify(weightSpecs1).length,
                    weightDataBytes: weightData1.byteLength,
                }
            };
        }
        const saveTrigger = tf.io.withSaveHandler(saveHandler);
        const saveResult = await saveTrigger.save(artifacts1);
        expect(saveResult.errors).toEqual(undefined);
        const artifactsInfo = saveResult.modelArtifactsInfo;
        expect(artifactsInfo.dateSaved.getTime())
            .toBeGreaterThanOrEqual(testStartDate.getTime());
        expect(saveResult.modelArtifactsInfo.modelTopologyBytes)
            .toEqual(JSON.stringify(modelTopology1).length);
        expect(saveResult.modelArtifactsInfo.weightSpecsBytes)
            .toEqual(JSON.stringify(weightSpecs1).length);
        expect(saveResult.modelArtifactsInfo.weightDataBytes).toEqual(16);
        expect(savedArtifacts.modelTopology).toEqual(modelTopology1);
        expect(savedArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(savedArtifacts.weightData).toEqual(weightData1);
    });
});
describeWithFlags('Passthrough Saver Sync', BROWSER_ENVS, () => {
    it('passes provided arguments through on save', () => {
        const testStartDate = new Date();
        let savedArtifacts = null;
        function saveHandler(artifacts) {
            savedArtifacts = artifacts;
            return {
                modelArtifactsInfo: {
                    dateSaved: testStartDate,
                    modelTopologyType: 'JSON',
                    modelTopologyBytes: JSON.stringify(modelTopology1).length,
                    weightSpecsBytes: JSON.stringify(weightSpecs1).length,
                    weightDataBytes: weightData1.byteLength,
                }
            };
        }
        const saveTrigger = tf.io.withSaveHandlerSync(saveHandler);
        const saveResult = saveTrigger.save(artifacts1);
        expect(saveResult.errors).toEqual(undefined);
        const artifactsInfo = saveResult.modelArtifactsInfo;
        expect(artifactsInfo.dateSaved.getTime())
            .toBeGreaterThanOrEqual(testStartDate.getTime());
        expect(saveResult.modelArtifactsInfo.modelTopologyBytes)
            .toEqual(JSON.stringify(modelTopology1).length);
        expect(saveResult.modelArtifactsInfo.weightSpecsBytes)
            .toEqual(JSON.stringify(weightSpecs1).length);
        expect(saveResult.modelArtifactsInfo.weightDataBytes).toEqual(16);
        expect(savedArtifacts.modelTopology).toEqual(modelTopology1);
        expect(savedArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(savedArtifacts.weightData).toEqual(weightData1);
    });
});
describeWithFlags('Passthrough Loader', BROWSER_ENVS, () => {
    it('load topology and weights: legacy signature', async () => {
        const passthroughHandler = tf.io.fromMemory(modelTopology1, weightSpecs1, weightData1);
        const modelArtifacts = await passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(modelArtifacts.weightData).toEqual(weightData1);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load topology and weights', async () => {
        const passthroughHandler = tf.io.fromMemory({
            modelTopology: modelTopology1,
            weightSpecs: weightSpecs1,
            weightData: weightData1
        });
        const modelArtifacts = await passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(modelArtifacts.weightData).toEqual(weightData1);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load model topology only: legacy signature', async () => {
        const passthroughHandler = tf.io.fromMemory(modelTopology1);
        const modelArtifacts = await passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(undefined);
        expect(modelArtifacts.weightData).toEqual(undefined);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load model topology only', async () => {
        const passthroughHandler = tf.io.fromMemory({ modelTopology: modelTopology1 });
        const modelArtifacts = await passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(undefined);
        expect(modelArtifacts.weightData).toEqual(undefined);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load topology, weights, and user-defined metadata', async () => {
        const userDefinedMetadata = { 'fooField': 'fooValue' };
        const passthroughHandler = tf.io.fromMemory({
            modelTopology: modelTopology1,
            weightSpecs: weightSpecs1,
            weightData: weightData1,
            userDefinedMetadata
        });
        const modelArtifacts = await passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(modelArtifacts.weightData).toEqual(weightData1);
        expect(modelArtifacts.userDefinedMetadata).toEqual(userDefinedMetadata);
    });
});
describeWithFlags('Passthrough Loader Sync', BROWSER_ENVS, () => {
    it('load topology and weights: legacy signature', () => {
        const passthroughHandler = tf.io.fromMemorySync(modelTopology1, weightSpecs1, weightData1);
        const modelArtifacts = passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(modelArtifacts.weightData).toEqual(weightData1);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load topology and weights', () => {
        const passthroughHandler = tf.io.fromMemorySync({
            modelTopology: modelTopology1,
            weightSpecs: weightSpecs1,
            weightData: weightData1
        });
        const modelArtifacts = passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(modelArtifacts.weightData).toEqual(weightData1);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load model topology only: legacy signature', () => {
        const passthroughHandler = tf.io.fromMemorySync(modelTopology1);
        const modelArtifacts = passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(undefined);
        expect(modelArtifacts.weightData).toEqual(undefined);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load model topology only', () => {
        const passthroughHandler = tf.io.fromMemorySync({ modelTopology: modelTopology1 });
        const modelArtifacts = passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(undefined);
        expect(modelArtifacts.weightData).toEqual(undefined);
        expect(modelArtifacts.userDefinedMetadata).toEqual(undefined);
    });
    it('load topology, weights, and user-defined metadata', () => {
        const userDefinedMetadata = { 'fooField': 'fooValue' };
        const passthroughHandler = tf.io.fromMemorySync({
            modelTopology: modelTopology1,
            weightSpecs: weightSpecs1,
            weightData: weightData1,
            userDefinedMetadata
        });
        const modelArtifacts = passthroughHandler.load();
        expect(modelArtifacts.modelTopology).toEqual(modelTopology1);
        expect(modelArtifacts.weightSpecs).toEqual(weightSpecs1);
        expect(modelArtifacts.weightData).toEqual(weightData1);
        expect(modelArtifacts.userDefinedMetadata).toEqual(userDefinedMetadata);
    });
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFzc3Rocm91Z2hfdGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvaW8vcGFzc3Rocm91Z2hfdGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFFSDs7R0FFRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9CLE9BQU8sRUFBQyxZQUFZLEVBQUUsaUJBQWlCLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQztBQUVoRSxNQUFNLGNBQWMsR0FBTztJQUN6QixZQUFZLEVBQUUsWUFBWTtJQUMxQixlQUFlLEVBQUUsT0FBTztJQUN4QixRQUFRLEVBQUUsQ0FBQztZQUNULFlBQVksRUFBRSxPQUFPO1lBQ3JCLFFBQVEsRUFBRTtnQkFDUixvQkFBb0IsRUFBRTtvQkFDcEIsWUFBWSxFQUFFLGlCQUFpQjtvQkFDL0IsUUFBUSxFQUFFO3dCQUNSLGNBQWMsRUFBRSxTQUFTO3dCQUN6QixPQUFPLEVBQUUsR0FBRzt3QkFDWixNQUFNLEVBQUUsSUFBSTt3QkFDWixNQUFNLEVBQUUsU0FBUztxQkFDbEI7aUJBQ0Y7Z0JBQ0QsTUFBTSxFQUFFLE9BQU87Z0JBQ2YsbUJBQW1CLEVBQUUsSUFBSTtnQkFDekIsa0JBQWtCLEVBQUUsSUFBSTtnQkFDeEIsaUJBQWlCLEVBQUUsSUFBSTtnQkFDdkIsT0FBTyxFQUFFLFNBQVM7Z0JBQ2xCLFlBQVksRUFBRSxRQUFRO2dCQUN0QixXQUFXLEVBQUUsSUFBSTtnQkFDakIsb0JBQW9CLEVBQUUsSUFBSTtnQkFDMUIsa0JBQWtCLEVBQUUsRUFBQyxZQUFZLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUM7Z0JBQ3pELE9BQU8sRUFBRSxDQUFDO2dCQUNWLG1CQUFtQixFQUFFLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDOUIsVUFBVSxFQUFFLElBQUk7Z0JBQ2hCLHNCQUFzQixF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Q0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsQ0FBQztJQUMxRSxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMTggR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuXG4vKipcbiAqIFVuaXQgdGVzdHMgZm9yIHBhc3N0aHJvdWdoIElPSGFuZGxlcnMuXG4gKi9cblxuaW1wb3J0ICogYXMgdGYgZnJvbSAnLi4vaW5kZXgnO1xuaW1wb3J0IHtCUk9XU0VSX0VOVlMsIGRlc2NyaWJlV2l0aEZsYWdzfSBmcm9tICcuLi9qYXNtaW5lX3V0aWwnO1xuXG5jb25zdCBtb2RlbFRvcG9sb2d5MToge30gPSB7XG4gICdjbGFzc19uYW1lJzogJ1NlcXVlbnRpYWwnLFxuICAna2VyYXNfdmVyc2lvbic6ICcyLjEuNCcsXG4gICdjb25maWcnOiBbe1xuICAgICdjbGFzc19uYW1lJzogJ0RlbnNlJyxcbiAgICAnY29uZmlnJzoge1xuICAgICAgJ2tlcm5lbF9pbml0aWFsaXplcic6IHtcbiAgICAgICAgJ2NsYXNzX25hbWUnOiAnVmFyaWFuY2VTY2FsaW5nJyxcbiAgICAgICAgJ2NvbmZpZyc6IHtcbiAgICAgICAgICAnZGlzdHJpYnV0aW9uJzogJ3VuaWZvcm0nLFxuICAgICAgICAgICdzY2FsZSc6IDEuMCxcbiAgICAgICAgICAnc2VlZCc6IG51bGwsXG4gICAgICAgICAgJ21vZGUnOiAnZmFuX2F2ZydcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgICduYW1lJzogJ2RlbnNlJyxcbiAgICAgICdrZXJuZWxfY29uc3RyYWludCc6IG51bGwsXG4gICAgICAnYmlhc19yZWd1bGFyaXplcic6IG51bGwsXG4gICAgICAnYmlhc19jb25zdHJhaW50JzogbnVsbCxcbiAgICAgICdkdHlwZSc6ICdmbG9hdDMyJyxcbiAgICAgICdhY3RpdmF0aW9uJzogJ2xpbmVhcicsXG4gICAgICAndHJhaW5hYmxlJzogdHJ1ZSxcbiAgICAgICdrZXJuZWxfcmVndWxhcml6ZXInOiBudWxsLFxuICAgICAgJ2JpYXNfaW5pdGlhbGl6ZXInOiB7J2NsYXNzX25hbWUnOiAnWmVyb3MnLCAnY29uZmlnJzoge319LFxuICAgICAgJ3VuaXRzJzogMSxcbiAgICAgICdiYXRjaF9pbnB1dF9zaGFwZSc6IFtudWxsLCAzXSxcbiAgICAgICd1c2VfYmlhcyc6IHRydWUsXG4gICAgICAnYWN0aXZpdHlfcmVndWxhcml6ZXInOiBudWxsXG4gICAgfVxuICB9XSxcbiAgJ2JhY2tlbmQnOiAndGVuc29yZmxvdydcbn07XG5cbmNvbnN0IHdlaWdodFNwZWNzMTogdGYuaW8uV2VpZ2h0c01hbmlmZXN0RW50cnlbXSA9IFtcbiAge1xuICAgIG5hbWU6ICdkZW5zZS9rZXJuZWwnLFxuICAgIHNoYXBlOiBbMywgMV0sXG4gICAgZHR5cGU6ICdmbG9hdDMyJyxcbiAgfSxcbiAge1xuICAgIG5hbWU6ICdkZW5zZS9iaWFzJyxcbiAgICBzaGFwZTogWzFdLFxuICAgIGR0eXBlOiAnZmxvYXQzMicsXG4gIH1cbl07XG5cbmNvbnN0IHdlaWdodERhdGExID0gbmV3IEFycmF5QnVmZmVyKDE2KTtcbmNvbnN0IGFydGlmYWN0czE6IHRmLmlvLk1vZGVsQXJ0aWZhY3RzID0ge1xuICBtb2RlbFRvcG9sb2d5OiBtb2RlbFRvcG9sb2d5MSxcbiAgd2VpZ2h0U3BlY3M6IHdlaWdodFNwZWNzMSxcbiAgd2VpZ2h0RGF0YTogd2VpZ2h0RGF0YTEsXG59O1xuXG5kZXNjcmliZVdpdGhGbGFncygnUGFzc3Rocm91Z2ggU2F2ZXInLCBCUk9XU0VSX0VOVlMsICgpID0+IHtcbiAgaXQoJ3Bhc3NlcyBwcm92aWRlZCBhcmd1bWVudHMgdGhyb3VnaCBvbiBzYXZlJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHRlc3RTdGFydERhdGUgPSBuZXcgRGF0ZSgpO1xuICAgIGxldCBzYXZlZEFydGlmYWN0czogdGYuaW8uTW9kZWxBcnRpZmFjdHMgPSBudWxsO1xuXG4gICAgYXN5bmMgZnVuY3Rpb24gc2F2ZUhhbmRsZXIoYXJ0aWZhY3RzOiB0Zi5pby5Nb2RlbEFydGlmYWN0cyk6XG4gICAgICAgIFByb21pc2U8dGYuaW8uU2F2ZVJlc3VsdD4ge1xuICAgICAgc2F2ZWRBcnRpZmFjdHMgPSBhcnRpZmFjdHM7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBtb2RlbEFydGlmYWN0c0luZm86IHtcbiAgICAgICAgICBkYXRlU2F2ZWQ6IHRlc3RTdGFydERhdGUsXG4gICAgICAgICAgbW9kZWxUb3BvbG9neVR5cGU6ICdKU09OJyxcbiAgICAgICAgICBtb2RlbFRvcG9sb2d5Qnl0ZXM6IEpTT04uc3RyaW5naWZ5KG1vZGVsVG9wb2xvZ3kxKS5sZW5ndGgsXG4gICAgICAgICAgd2VpZ2h0U3BlY3NCeXRlczogSlNPTi5zdHJpbmdpZnkod2VpZ2h0U3BlY3MxKS5sZW5ndGgsXG4gICAgICAgICAgd2VpZ2h0RGF0YUJ5dGVzOiB3ZWlnaHREYXRhMS5ieXRlTGVuZ3RoLFxuICAgICAgICB9XG4gICAgICB9O1xuICAgIH1cblxuICAgIGNvbnN0IHNhdmVUcmlnZ2VyID0gdGYuaW8ud2l0aFNhdmVIYW5kbGVyKHNhdmVIYW5kbGVyKTtcbiAgICBjb25zdCBzYXZlUmVzdWx0ID0gYXdhaXQgc2F2ZVRyaWdnZXIuc2F2ZShhcnRpZmFjdHMxKTtcblxuICAgIGV4cGVjdChzYXZlUmVzdWx0LmVycm9ycykudG9FcXVhbCh1bmRlZmluZWQpO1xuICAgIGNvbnN0IGFydGlmYWN0c0luZm8gPSBzYXZlUmVzdWx0Lm1vZGVsQXJ0aWZhY3RzSW5mbztcbiAgICBleHBlY3QoYXJ0aWZhY3RzSW5mby5kYXRlU2F2ZWQuZ2V0VGltZSgpKVxuICAgICAgICAudG9CZUdyZWF0ZXJUaGFuT3JFcXVhbCh0ZXN0U3RhcnREYXRlLmdldFRpbWUoKSk7XG4gICAgZXhwZWN0KHNhdmVSZXN1bHQubW9kZWxBcnRpZmFjdHNJbmZvLm1vZGVsVG9wb2xvZ3lCeXRlcylcbiAgICAgICAgLnRvRXF1YWwoSlNPTi5zdHJpbmdpZnkobW9kZWxUb3BvbG9neTEpLmxlbmd0aCk7XG4gICAgZXhwZWN0KHNhdmVSZXN1bHQubW9kZWxBcnRpZmFjdHNJbmZvLndlaWdodFNwZWNzQnl0ZXMpXG4gICAgICAgIC50b0VxdWFsKEpTT04uc3RyaW5naWZ5KHdlaWdodFNwZWNzMSkubGVuZ3RoKTtcbiAgICBleHBlY3Qoc2F2ZVJlc3VsdC5tb2RlbEFydGlmYWN0c0luZm8ud2VpZ2h0RGF0YUJ5dGVzKS50b0VxdWFsKDE2KTtcblxuICAgIGV4cGVjdChzYXZlZEFydGlmYWN0cy5tb2RlbFRvcG9sb2d5KS50b0VxdWFsKG1vZGVsVG9wb2xvZ3kxKTtcbiAgICBleHBlY3Qoc2F2ZWRBcnRpZmFjdHMud2VpZ2h0U3BlY3MpLnRvRXF1YWwod2VpZ2h0U3BlY3MxKTtcbiAgICBleHBlY3Qoc2F2ZWRBcnRpZmFjdHMud2VpZ2h0RGF0YSkudG9FcXVhbCh3ZWlnaHREYXRhMSk7XG4gIH0pO1xufSk7XG5cbmRlc2NyaWJlV2l0aEZsYWdzKCdQYXNzdGhyb3VnaCBTYXZlciBTeW5jJywgQlJPV1NFUl9FTlZTLCAoKSA9PiB7XG4gIGl0KCdwYXNzZXMgcHJvdmlkZWQgYXJndW1lbnRzIHRocm91Z2ggb24gc2F2ZScsICgpID0+IHtcbiAgICBjb25zdCB0ZXN0U3RhcnREYXRlID0gbmV3IERhdGUoKTtcbiAgICBsZXQgc2F2ZWRBcnRpZmFjdHM6IHRmLmlvLk1vZGVsQXJ0aWZhY3RzID0gbnVsbDtcblxuICAgIGZ1bmN0aW9uIHNhdmVIYW5kbGVyKGFydGlmYWN0czogdGYuaW8uTW9kZWxBcnRpZmFjdHMpOlxuICAgICAgICB0Zi5pby5TYXZlUmVzdWx0IHtcbiAgICAgIHNhdmVkQXJ0aWZhY3RzID0gYXJ0aWZhY3RzO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbW9kZWxBcnRpZmFjdHNJbmZvOiB7XG4gICAgICAgICAgZGF0ZVNhdmVkOiB0ZXN0U3RhcnREYXRlLFxuICAgICAgICAgIG1vZGVsVG9wb2xvZ3lUeXBlOiAnSlNPTicsXG4gICAgICAgICAgbW9kZWxUb3BvbG9neUJ5dGVzOiBKU09OLnN0cmluZ2lmeShtb2RlbFRvcG9sb2d5MSkubGVuZ3RoLFxuICAgICAgICAgIHdlaWdodFNwZWNzQnl0ZXM6IEpTT04uc3RyaW5naWZ5KHdlaWdodFNwZWNzMSkubGVuZ3RoLFxuICAgICAgICAgIHdlaWdodERhdGFCeXRlczogd2VpZ2h0RGF0YTEuYnl0ZUxlbmd0aCxcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICB9XG5cbiAgICBjb25zdCBzYXZlVHJpZ2dlciA9IHRmLmlvLndpdGhTYXZlSGFuZGxlclN5bmMoc2F2ZUhhbmRsZXIpO1xuICAgIGNvbnN0IHNhdmVSZXN1bHQgPSBzYXZlVHJpZ2dlci5zYXZlKGFydGlmYWN0czEpO1xuXG4gICAgZXhwZWN0KHNhdmVSZXN1bHQuZXJyb3JzKS50b0VxdWFsKHVuZGVmaW5lZCk7XG4gICAgY29uc3QgYXJ0aWZhY3RzSW5mbyA9IHNhdmVSZXN1bHQubW9kZWxBcnRpZmFjdHNJbmZvO1xuICAgIGV4cGVjdChhcnRpZmFjdHNJbmZvLmRhdGVTYXZlZC5nZXRUaW1lKCkpXG4gICAgICAgIC50b0JlR3JlYXRlclRoYW5PckVxdWFsKHRlc3RTdGFydERhdGUuZ2V0VGltZSgpKTtcbiAgICBleHBlY3Qoc2F2ZVJlc3VsdC5tb2RlbEFydGlmYWN0c0luZm8ubW9kZWxUb3BvbG9neUJ5dGVzKVxuICAgICAgICAudG9FcXVhbChKU09OLnN0cmluZ2lmeShtb2RlbFRvcG9sb2d5MSkubGVuZ3RoKTtcbiAgICBleHBlY3Qoc2F2ZVJlc3VsdC5tb2RlbEFydGlmYWN0c0luZm8ud2VpZ2h0U3BlY3NCeXRlcylcbiAgICAgICAgLnRvRXF1YWwoSlNPTi5zdHJpbmdpZnkod2VpZ2h0U3BlY3MxKS5sZW5ndGgpO1xuICAgIGV4cGVjdChzYXZlUmVzdWx0Lm1vZGVsQXJ0aWZhY3RzSW5mby53ZWlnaHREYXRhQnl0ZXMpLnRvRXF1YWwoMTYpO1xuXG4gICAgZXhwZWN0KHNhdmVkQXJ0aWZhY3RzLm1vZGVsVG9wb2xvZ3kpLnRvRXF1YWwobW9kZWxUb3BvbG9neTEpO1xuICAgIGV4cGVjdChzYXZlZEFydGlmYWN0cy53ZWlnaHRTcGVjcykudG9FcXVhbCh3ZWlnaHRTcGVjczEpO1xuICAgIGV4cGVjdChzYXZlZEFydGlmYWN0cy53ZWlnaHREYXRhKS50b0VxdWFsKHdlaWdodERhdGExKTtcbiAgfSk7XG59KTtcblxuZGVzY3JpYmVXaXRoRmxhZ3MoJ1Bhc3N0aHJvdWdoIExvYWRlcicsIEJST1dTRVJfRU5WUywgKCkgPT4ge1xuICBpdCgnbG9hZCB0b3BvbG9neSBhbmQgd2VpZ2h0czogbGVnYWN5IHNpZ25hdHVyZScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBwYXNzdGhyb3VnaEhhbmRsZXIgPVxuICAgICAgICB0Zi5pby5mcm9tTWVtb3J5KG1vZGVsVG9wb2xvZ3kxLCB3ZWlnaHRTcGVjczEsIHdlaWdodERhdGExKTtcbiAgICBjb25zdCBtb2RlbEFydGlmYWN0cyA9IGF3YWl0IHBhc3N0aHJvdWdoSGFuZGxlci5sb2FkKCk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLm1vZGVsVG9wb2xvZ3kpLnRvRXF1YWwobW9kZWxUb3BvbG9neTEpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy53ZWlnaHRTcGVjcykudG9FcXVhbCh3ZWlnaHRTcGVjczEpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy53ZWlnaHREYXRhKS50b0VxdWFsKHdlaWdodERhdGExKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMudXNlckRlZmluZWRNZXRhZGF0YSkudG9FcXVhbCh1bmRlZmluZWQpO1xuICB9KTtcblxuICBpdCgnbG9hZCB0b3BvbG9neSBhbmQgd2VpZ2h0cycsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBwYXNzdGhyb3VnaEhhbmRsZXIgPSB0Zi5pby5mcm9tTWVtb3J5KHtcbiAgICAgIG1vZGVsVG9wb2xvZ3k6IG1vZGVsVG9wb2xvZ3kxLFxuICAgICAgd2VpZ2h0U3BlY3M6IHdlaWdodFNwZWNzMSxcbiAgICAgIHdlaWdodERhdGE6IHdlaWdodERhdGExXG4gICAgfSk7XG4gICAgY29uc3QgbW9kZWxBcnRpZmFjdHMgPSBhd2FpdCBwYXNzdGhyb3VnaEhhbmRsZXIubG9hZCgpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy5tb2RlbFRvcG9sb2d5KS50b0VxdWFsKG1vZGVsVG9wb2xvZ3kxKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0U3BlY3MpLnRvRXF1YWwod2VpZ2h0U3BlY3MxKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0RGF0YSkudG9FcXVhbCh3ZWlnaHREYXRhMSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLnVzZXJEZWZpbmVkTWV0YWRhdGEpLnRvRXF1YWwodW5kZWZpbmVkKTtcbiAgfSk7XG5cbiAgaXQoJ2xvYWQgbW9kZWwgdG9wb2xvZ3kgb25seTogbGVnYWN5IHNpZ25hdHVyZScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBwYXNzdGhyb3VnaEhhbmRsZXIgPSB0Zi5pby5mcm9tTWVtb3J5KG1vZGVsVG9wb2xvZ3kxKTtcbiAgICBjb25zdCBtb2RlbEFydGlmYWN0cyA9IGF3YWl0IHBhc3N0aHJvdWdoSGFuZGxlci5sb2FkKCk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLm1vZGVsVG9wb2xvZ3kpLnRvRXF1YWwobW9kZWxUb3BvbG9neTEpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy53ZWlnaHRTcGVjcykudG9FcXVhbCh1bmRlZmluZWQpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy53ZWlnaHREYXRhKS50b0VxdWFsKHVuZGVmaW5lZCk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLnVzZXJEZWZpbmVkTWV0YWRhdGEpLnRvRXF1YWwodW5kZWZpbmVkKTtcbiAgfSk7XG5cbiAgaXQoJ2xvYWQgbW9kZWwgdG9wb2xvZ3kgb25seScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBwYXNzdGhyb3VnaEhhbmRsZXIgPVxuICAgICAgICB0Zi5pby5mcm9tTWVtb3J5KHttb2RlbFRvcG9sb2d5OiBtb2RlbFRvcG9sb2d5MX0pO1xuICAgIGNvbnN0IG1vZGVsQXJ0aWZhY3RzID0gYXdhaXQgcGFzc3Rocm91Z2hIYW5kbGVyLmxvYWQoKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMubW9kZWxUb3BvbG9neSkudG9FcXVhbChtb2RlbFRvcG9sb2d5MSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodFNwZWNzKS50b0VxdWFsKHVuZGVmaW5lZCk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodERhdGEpLnRvRXF1YWwodW5kZWZpbmVkKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMudXNlckRlZmluZWRNZXRhZGF0YSkudG9FcXVhbCh1bmRlZmluZWQpO1xuICB9KTtcblxuICBpdCgnbG9hZCB0b3BvbG9neSwgd2VpZ2h0cywgYW5kIHVzZXItZGVmaW5lZCBtZXRhZGF0YScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCB1c2VyRGVmaW5lZE1ldGFkYXRhOiB7fSA9IHsnZm9vRmllbGQnOiAnZm9vVmFsdWUnfTtcbiAgICBjb25zdCBwYXNzdGhyb3VnaEhhbmRsZXIgPSB0Zi5pby5mcm9tTWVtb3J5KHtcbiAgICAgIG1vZGVsVG9wb2xvZ3k6IG1vZGVsVG9wb2xvZ3kxLFxuICAgICAgd2VpZ2h0U3BlY3M6IHdlaWdodFNwZWNzMSxcbiAgICAgIHdlaWdodERhdGE6IHdlaWdodERhdGExLFxuICAgICAgdXNlckRlZmluZWRNZXRhZGF0YVxuICAgIH0pO1xuICAgIGNvbnN0IG1vZGVsQXJ0aWZhY3RzID0gYXdhaXQgcGFzc3Rocm91Z2hIYW5kbGVyLmxvYWQoKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMubW9kZWxUb3BvbG9neSkudG9FcXVhbChtb2RlbFRvcG9sb2d5MSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodFNwZWNzKS50b0VxdWFsKHdlaWdodFNwZWNzMSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodERhdGEpLnRvRXF1YWwod2VpZ2h0RGF0YTEpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy51c2VyRGVmaW5lZE1ldGFkYXRhKS50b0VxdWFsKHVzZXJEZWZpbmVkTWV0YWRhdGEpO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZVdpdGhGbGFncygnUGFzc3Rocm91Z2ggTG9hZGVyIFN5bmMnLCBCUk9XU0VSX0VOVlMsICgpID0+IHtcbiAgaXQoJ2xvYWQgdG9wb2xvZ3kgYW5kIHdlaWdodHM6IGxlZ2FjeSBzaWduYXR1cmUnLCAoKSA9PiB7XG4gICAgY29uc3QgcGFzc3Rocm91Z2hIYW5kbGVyID1cbiAgICAgICAgdGYuaW8uZnJvbU1lbW9yeVN5bmMobW9kZWxUb3BvbG9neTEsIHdlaWdodFNwZWNzMSwgd2VpZ2h0RGF0YTEpO1xuICAgIGNvbnN0IG1vZGVsQXJ0aWZhY3RzID0gcGFzc3Rocm91Z2hIYW5kbGVyLmxvYWQoKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMubW9kZWxUb3BvbG9neSkudG9FcXVhbChtb2RlbFRvcG9sb2d5MSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodFNwZWNzKS50b0VxdWFsKHdlaWdodFNwZWNzMSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodERhdGEpLnRvRXF1YWwod2VpZ2h0RGF0YTEpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy51c2VyRGVmaW5lZE1ldGFkYXRhKS50b0VxdWFsKHVuZGVmaW5lZCk7XG4gIH0pO1xuXG4gIGl0KCdsb2FkIHRvcG9sb2d5IGFuZCB3ZWlnaHRzJywgKCkgPT4ge1xuICAgIGNvbnN0IHBhc3N0aHJvdWdoSGFuZGxlciA9IHRmLmlvLmZyb21NZW1vcnlTeW5jKHtcbiAgICAgIG1vZGVsVG9wb2xvZ3k6IG1vZGVsVG9wb2xvZ3kxLFxuICAgICAgd2VpZ2h0U3BlY3M6IHdlaWdodFNwZWNzMSxcbiAgICAgIHdlaWdodERhdGE6IHdlaWdodERhdGExXG4gICAgfSk7XG4gICAgY29uc3QgbW9kZWxBcnRpZmFjdHMgPSBwYXNzdGhyb3VnaEhhbmRsZXIubG9hZCgpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy5tb2RlbFRvcG9sb2d5KS50b0VxdWFsKG1vZGVsVG9wb2xvZ3kxKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0U3BlY3MpLnRvRXF1YWwod2VpZ2h0U3BlY3MxKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0RGF0YSkudG9FcXVhbCh3ZWlnaHREYXRhMSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLnVzZXJEZWZpbmVkTWV0YWRhdGEpLnRvRXF1YWwodW5kZWZpbmVkKTtcbiAgfSk7XG5cbiAgaXQoJ2xvYWQgbW9kZWwgdG9wb2xvZ3kgb25seTogbGVnYWN5IHNpZ25hdHVyZScsICgpID0+IHtcbiAgICBjb25zdCBwYXNzdGhyb3VnaEhhbmRsZXIgPSB0Zi5pby5mcm9tTWVtb3J5U3luYyhtb2RlbFRvcG9sb2d5MSk7XG4gICAgY29uc3QgbW9kZWxBcnRpZmFjdHMgPSBwYXNzdGhyb3VnaEhhbmRsZXIubG9hZCgpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy5tb2RlbFRvcG9sb2d5KS50b0VxdWFsKG1vZGVsVG9wb2xvZ3kxKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0U3BlY3MpLnRvRXF1YWwodW5kZWZpbmVkKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0RGF0YSkudG9FcXVhbCh1bmRlZmluZWQpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy51c2VyRGVmaW5lZE1ldGFkYXRhKS50b0VxdWFsKHVuZGVmaW5lZCk7XG4gIH0pO1xuXG4gIGl0KCdsb2FkIG1vZGVsIHRvcG9sb2d5IG9ubHknLCAoKSA9PiB7XG4gICAgY29uc3QgcGFzc3Rocm91Z2hIYW5kbGVyID1cbiAgICAgICAgdGYuaW8uZnJvbU1lbW9yeVN5bmMoe21vZGVsVG9wb2xvZ3k6IG1vZGVsVG9wb2xvZ3kxfSk7XG4gICAgY29uc3QgbW9kZWxBcnRpZmFjdHMgPSBwYXNzdGhyb3VnaEhhbmRsZXIubG9hZCgpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy5tb2RlbFRvcG9sb2d5KS50b0VxdWFsKG1vZGVsVG9wb2xvZ3kxKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0U3BlY3MpLnRvRXF1YWwodW5kZWZpbmVkKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMud2VpZ2h0RGF0YSkudG9FcXVhbCh1bmRlZmluZWQpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy51c2VyRGVmaW5lZE1ldGFkYXRhKS50b0VxdWFsKHVuZGVmaW5lZCk7XG4gIH0pO1xuXG4gIGl0KCdsb2FkIHRvcG9sb2d5LCB3ZWlnaHRzLCBhbmQgdXNlci1kZWZpbmVkIG1ldGFkYXRhJywgKCkgPT4ge1xuICAgIGNvbnN0IHVzZXJEZWZpbmVkTWV0YWRhdGE6IHt9ID0geydmb29GaWVsZCc6ICdmb29WYWx1ZSd9O1xuICAgIGNvbnN0IHBhc3N0aHJvdWdoSGFuZGxlciA9IHRmLmlvLmZyb21NZW1vcnlTeW5jKHtcbiAgICAgIG1vZGVsVG9wb2xvZ3k6IG1vZGVsVG9wb2xvZ3kxLFxuICAgICAgd2VpZ2h0U3BlY3M6IHdlaWdodFNwZWNzMSxcbiAgICAgIHdlaWdodERhdGE6IHdlaWdodERhdGExLFxuICAgICAgdXNlckRlZmluZWRNZXRhZGF0YVxuICAgIH0pO1xuICAgIGNvbnN0IG1vZGVsQXJ0aWZhY3RzID0gcGFzc3Rocm91Z2hIYW5kbGVyLmxvYWQoKTtcbiAgICBleHBlY3QobW9kZWxBcnRpZmFjdHMubW9kZWxUb3BvbG9neSkudG9FcXVhbChtb2RlbFRvcG9sb2d5MSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodFNwZWNzKS50b0VxdWFsKHdlaWdodFNwZWNzMSk7XG4gICAgZXhwZWN0KG1vZGVsQXJ0aWZhY3RzLndlaWdodERhdGEpLnRvRXF1YWwod2VpZ2h0RGF0YTEpO1xuICAgIGV4cGVjdChtb2RlbEFydGlmYWN0cy51c2VyRGVmaW5lZE1ldGFkYXRhKS50b0VxdWFsKHVzZXJEZWZpbmVkTWV0YWRhdGEpO1xuICB9KTtcbn0pO1xuIl19