parse-server 9.9.0-alpha.2 → 9.9.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 +44 -0
- package/lib/Config.js +35 -2
- package/lib/Deprecator/Deprecations.js +5 -1
- package/lib/InstallationDedup.js +178 -0
- package/lib/Options/Definitions.js +26 -1
- package/lib/Options/docs.js +9 -1
- package/lib/Options/index.js +1 -1
- package/lib/RestWrite.js +36 -35
- package/package.json +1 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.applyDuplicateDeviceTokenMerge = applyDuplicateDeviceTokenMerge;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
exports.removeConflictingDeviceToken = removeConflictingDeviceToken;
|
|
9
|
+
var _node = _interopRequireDefault(require("parse/node"));
|
|
10
|
+
var _logger = _interopRequireDefault(require("./logger"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
const CLASS_NAME = '_Installation';
|
|
13
|
+
function logResult(action, count, err) {
|
|
14
|
+
if (err && err.code === _node.default.Error.OBJECT_NOT_FOUND) {
|
|
15
|
+
_logger.default.verbose(`Installation dedup ${action} matched no rows; nothing to do.`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (err && err.code === _node.default.Error.OPERATION_FORBIDDEN) {
|
|
19
|
+
_logger.default.warn(`Installation dedup ${action} skipped: caller has no permission to ${action} the conflicting row(s). The conflicting row remains.`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (err) {
|
|
23
|
+
_logger.default.error(`Installation dedup ${action} failed: ${err.message || err}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
_logger.default.verbose(`Installation dedup ${action} applied to ${count == null ? 'matching' : count} conflicting row(s).`);
|
|
27
|
+
}
|
|
28
|
+
async function performAction({
|
|
29
|
+
database,
|
|
30
|
+
query,
|
|
31
|
+
action,
|
|
32
|
+
fieldToClear,
|
|
33
|
+
runOptions,
|
|
34
|
+
many,
|
|
35
|
+
validSchemaController
|
|
36
|
+
}) {
|
|
37
|
+
if (action === 'delete') {
|
|
38
|
+
return database.destroy(CLASS_NAME, query, runOptions, validSchemaController);
|
|
39
|
+
}
|
|
40
|
+
if (action === 'update') {
|
|
41
|
+
return database.update(CLASS_NAME, query, {
|
|
42
|
+
[fieldToClear]: {
|
|
43
|
+
__op: 'Delete'
|
|
44
|
+
}
|
|
45
|
+
}, {
|
|
46
|
+
...runOptions,
|
|
47
|
+
many
|
|
48
|
+
}, false, false, validSchemaController);
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Unknown installation dedup action: ${action}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Removes or updates `_Installation` rows that hold a `deviceToken` matching the query,
|
|
55
|
+
* allowing the caller to claim that `deviceToken` exclusively. Used when a new or updated
|
|
56
|
+
* install collides with one or more existing rows on `deviceToken`.
|
|
57
|
+
*
|
|
58
|
+
* @param {Object} options
|
|
59
|
+
* @param {DatabaseController} options.database
|
|
60
|
+
* @param {Object} options.query e.g. { deviceToken: 'X', installationId: { $ne: 'I' } }
|
|
61
|
+
* @param {'delete'|'update'} options.action
|
|
62
|
+
* @param {boolean} options.enforceAuth
|
|
63
|
+
* @param {Object} options.runOptions RestWrite.runOptions
|
|
64
|
+
* @param {SchemaController} options.validSchemaController
|
|
65
|
+
*/
|
|
66
|
+
async function removeConflictingDeviceToken({
|
|
67
|
+
database,
|
|
68
|
+
query,
|
|
69
|
+
action,
|
|
70
|
+
enforceAuth,
|
|
71
|
+
runOptions,
|
|
72
|
+
validSchemaController
|
|
73
|
+
}) {
|
|
74
|
+
const opts = enforceAuth ? runOptions : {};
|
|
75
|
+
try {
|
|
76
|
+
await performAction({
|
|
77
|
+
database,
|
|
78
|
+
query,
|
|
79
|
+
action,
|
|
80
|
+
fieldToClear: 'deviceToken',
|
|
81
|
+
runOptions: opts,
|
|
82
|
+
many: true,
|
|
83
|
+
validSchemaController
|
|
84
|
+
});
|
|
85
|
+
logResult(action, null, null);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
if (err && err.code === _node.default.Error.OBJECT_NOT_FOUND) {
|
|
88
|
+
logResult(action, 0, err);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (err && err.code === _node.default.Error.OPERATION_FORBIDDEN) {
|
|
92
|
+
logResult(action, null, err);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
logResult(action, null, err);
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolves a merge conflict between two `_Installation` rows that together represent the
|
|
102
|
+
* same install: one matched by `installationId`/`objectId` (`idMatch`), and another holding
|
|
103
|
+
* the same `deviceToken` but no `installationId` (`deviceTokenMatch`). The `mergePriority`
|
|
104
|
+
* determines which row survives; the loser receives the configured `action`. Returns the
|
|
105
|
+
* survivor's `objectId` so the save flow can target it.
|
|
106
|
+
*
|
|
107
|
+
* @param {Object} options
|
|
108
|
+
* @param {DatabaseController} options.database
|
|
109
|
+
* @param {{ objectId: string, installationId?: string, deviceToken?: string }} options.idMatch
|
|
110
|
+
* @param {{ objectId: string, deviceToken?: string }} options.deviceTokenMatch
|
|
111
|
+
* @param {'delete'|'update'} options.action
|
|
112
|
+
* @param {'deviceToken'|'installationId'} options.mergePriority
|
|
113
|
+
* @param {boolean} options.enforceAuth
|
|
114
|
+
* @param {Object} options.runOptions
|
|
115
|
+
* @param {SchemaController} options.validSchemaController
|
|
116
|
+
* @returns {Promise<string>} survivor's objectId
|
|
117
|
+
*/
|
|
118
|
+
async function applyDuplicateDeviceTokenMerge({
|
|
119
|
+
database,
|
|
120
|
+
idMatch,
|
|
121
|
+
deviceTokenMatch,
|
|
122
|
+
action,
|
|
123
|
+
mergePriority,
|
|
124
|
+
enforceAuth,
|
|
125
|
+
runOptions,
|
|
126
|
+
validSchemaController
|
|
127
|
+
}) {
|
|
128
|
+
// Self-merge guard: when both matches resolve to the same row, there's
|
|
129
|
+
// nothing to clean up. Skip the action so we don't destroy/update the row
|
|
130
|
+
// we're about to return as the survivor.
|
|
131
|
+
if (idMatch.objectId === deviceTokenMatch.objectId) {
|
|
132
|
+
return idMatch.objectId;
|
|
133
|
+
}
|
|
134
|
+
const opts = enforceAuth ? runOptions : {};
|
|
135
|
+
let loser;
|
|
136
|
+
let survivorId;
|
|
137
|
+
let fieldToClear;
|
|
138
|
+
if (mergePriority === 'deviceToken') {
|
|
139
|
+
loser = idMatch;
|
|
140
|
+
survivorId = deviceTokenMatch.objectId;
|
|
141
|
+
fieldToClear = 'installationId';
|
|
142
|
+
} else if (mergePriority === 'installationId') {
|
|
143
|
+
loser = deviceTokenMatch;
|
|
144
|
+
survivorId = idMatch.objectId;
|
|
145
|
+
fieldToClear = 'deviceToken';
|
|
146
|
+
} else {
|
|
147
|
+
throw new Error(`Unknown installation dedup mergePriority: ${mergePriority}`);
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
await performAction({
|
|
151
|
+
database,
|
|
152
|
+
query: {
|
|
153
|
+
objectId: loser.objectId
|
|
154
|
+
},
|
|
155
|
+
action,
|
|
156
|
+
fieldToClear,
|
|
157
|
+
runOptions: opts,
|
|
158
|
+
many: false,
|
|
159
|
+
validSchemaController
|
|
160
|
+
});
|
|
161
|
+
logResult(action, 1, null);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
if (err && err.code === _node.default.Error.OBJECT_NOT_FOUND) {
|
|
164
|
+
logResult(action, 0, err);
|
|
165
|
+
} else if (err && err.code === _node.default.Error.OPERATION_FORBIDDEN) {
|
|
166
|
+
logResult(action, null, err);
|
|
167
|
+
} else {
|
|
168
|
+
logResult(action, null, err);
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return survivorId;
|
|
173
|
+
}
|
|
174
|
+
var _default = exports.default = {
|
|
175
|
+
removeConflictingDeviceToken,
|
|
176
|
+
applyDuplicateDeviceTokenMerge
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbm9kZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2xvZ2dlciIsImUiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsIkNMQVNTX05BTUUiLCJsb2dSZXN1bHQiLCJhY3Rpb24iLCJjb3VudCIsImVyciIsImNvZGUiLCJQYXJzZSIsIkVycm9yIiwiT0JKRUNUX05PVF9GT1VORCIsImxvZ2dlciIsInZlcmJvc2UiLCJPUEVSQVRJT05fRk9SQklEREVOIiwid2FybiIsImVycm9yIiwibWVzc2FnZSIsInBlcmZvcm1BY3Rpb24iLCJkYXRhYmFzZSIsInF1ZXJ5IiwiZmllbGRUb0NsZWFyIiwicnVuT3B0aW9ucyIsIm1hbnkiLCJ2YWxpZFNjaGVtYUNvbnRyb2xsZXIiLCJkZXN0cm95IiwidXBkYXRlIiwiX19vcCIsInJlbW92ZUNvbmZsaWN0aW5nRGV2aWNlVG9rZW4iLCJlbmZvcmNlQXV0aCIsIm9wdHMiLCJhcHBseUR1cGxpY2F0ZURldmljZVRva2VuTWVyZ2UiLCJpZE1hdGNoIiwiZGV2aWNlVG9rZW5NYXRjaCIsIm1lcmdlUHJpb3JpdHkiLCJvYmplY3RJZCIsImxvc2VyIiwic3Vydml2b3JJZCIsIl9kZWZhdWx0IiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uL3NyYy9JbnN0YWxsYXRpb25EZWR1cC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUGFyc2UgZnJvbSAncGFyc2Uvbm9kZSc7XG5pbXBvcnQgbG9nZ2VyIGZyb20gJy4vbG9nZ2VyJztcblxuY29uc3QgQ0xBU1NfTkFNRSA9ICdfSW5zdGFsbGF0aW9uJztcblxuZnVuY3Rpb24gbG9nUmVzdWx0KGFjdGlvbiwgY291bnQsIGVycikge1xuICBpZiAoZXJyICYmIGVyci5jb2RlID09PSBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5EKSB7XG4gICAgbG9nZ2VyLnZlcmJvc2UoYEluc3RhbGxhdGlvbiBkZWR1cCAke2FjdGlvbn0gbWF0Y2hlZCBubyByb3dzOyBub3RoaW5nIHRvIGRvLmApO1xuICAgIHJldHVybjtcbiAgfVxuICBpZiAoZXJyICYmIGVyci5jb2RlID09PSBQYXJzZS5FcnJvci5PUEVSQVRJT05fRk9SQklEREVOKSB7XG4gICAgbG9nZ2VyLndhcm4oXG4gICAgICBgSW5zdGFsbGF0aW9uIGRlZHVwICR7YWN0aW9ufSBza2lwcGVkOiBjYWxsZXIgaGFzIG5vIHBlcm1pc3Npb24gdG8gJHthY3Rpb259IHRoZSBjb25mbGljdGluZyByb3cocykuIFRoZSBjb25mbGljdGluZyByb3cgcmVtYWlucy5gXG4gICAgKTtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKGVycikge1xuICAgIGxvZ2dlci5lcnJvcihgSW5zdGFsbGF0aW9uIGRlZHVwICR7YWN0aW9ufSBmYWlsZWQ6ICR7ZXJyLm1lc3NhZ2UgfHwgZXJyfWApO1xuICAgIHJldHVybjtcbiAgfVxuICBsb2dnZXIudmVyYm9zZShcbiAgICBgSW5zdGFsbGF0aW9uIGRlZHVwICR7YWN0aW9ufSBhcHBsaWVkIHRvICR7Y291bnQgPT0gbnVsbCA/ICdtYXRjaGluZycgOiBjb3VudH0gY29uZmxpY3Rpbmcgcm93KHMpLmBcbiAgKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcGVyZm9ybUFjdGlvbih7XG4gIGRhdGFiYXNlLFxuICBxdWVyeSxcbiAgYWN0aW9uLFxuICBmaWVsZFRvQ2xlYXIsXG4gIHJ1bk9wdGlvbnMsXG4gIG1hbnksXG4gIHZhbGlkU2NoZW1hQ29udHJvbGxlcixcbn0pIHtcbiAgaWYgKGFjdGlvbiA9PT0gJ2RlbGV0ZScpIHtcbiAgICByZXR1cm4gZGF0YWJhc2UuZGVzdHJveShDTEFTU19OQU1FLCBxdWVyeSwgcnVuT3B0aW9ucywgdmFsaWRTY2hlbWFDb250cm9sbGVyKTtcbiAgfVxuICBpZiAoYWN0aW9uID09PSAndXBkYXRlJykge1xuICAgIHJldHVybiBkYXRhYmFzZS51cGRhdGUoXG4gICAgICBDTEFTU19OQU1FLFxuICAgICAgcXVlcnksXG4gICAgICB7IFtmaWVsZFRvQ2xlYXJdOiB7IF9fb3A6ICdEZWxldGUnIH0gfSxcbiAgICAgIHsgLi4ucnVuT3B0aW9ucywgbWFueSB9LFxuICAgICAgZmFsc2UsXG4gICAgICBmYWxzZSxcbiAgICAgIHZhbGlkU2NoZW1hQ29udHJvbGxlclxuICAgICk7XG4gIH1cbiAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIGluc3RhbGxhdGlvbiBkZWR1cCBhY3Rpb246ICR7YWN0aW9ufWApO1xufVxuXG4vKipcbiAqIFJlbW92ZXMgb3IgdXBkYXRlcyBgX0luc3RhbGxhdGlvbmAgcm93cyB0aGF0IGhvbGQgYSBgZGV2aWNlVG9rZW5gIG1hdGNoaW5nIHRoZSBxdWVyeSxcbiAqIGFsbG93aW5nIHRoZSBjYWxsZXIgdG8gY2xhaW0gdGhhdCBgZGV2aWNlVG9rZW5gIGV4Y2x1c2l2ZWx5LiBVc2VkIHdoZW4gYSBuZXcgb3IgdXBkYXRlZFxuICogaW5zdGFsbCBjb2xsaWRlcyB3aXRoIG9uZSBvciBtb3JlIGV4aXN0aW5nIHJvd3Mgb24gYGRldmljZVRva2VuYC5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICogQHBhcmFtIHtEYXRhYmFzZUNvbnRyb2xsZXJ9IG9wdGlvbnMuZGF0YWJhc2VcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zLnF1ZXJ5IGUuZy4geyBkZXZpY2VUb2tlbjogJ1gnLCBpbnN0YWxsYXRpb25JZDogeyAkbmU6ICdJJyB9IH1cbiAqIEBwYXJhbSB7J2RlbGV0ZSd8J3VwZGF0ZSd9IG9wdGlvbnMuYWN0aW9uXG4gKiBAcGFyYW0ge2Jvb2xlYW59IG9wdGlvbnMuZW5mb3JjZUF1dGhcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zLnJ1bk9wdGlvbnMgUmVzdFdyaXRlLnJ1bk9wdGlvbnNcbiAqIEBwYXJhbSB7U2NoZW1hQ29udHJvbGxlcn0gb3B0aW9ucy52YWxpZFNjaGVtYUNvbnRyb2xsZXJcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlbW92ZUNvbmZsaWN0aW5nRGV2aWNlVG9rZW4oe1xuICBkYXRhYmFzZSxcbiAgcXVlcnksXG4gIGFjdGlvbixcbiAgZW5mb3JjZUF1dGgsXG4gIHJ1bk9wdGlvbnMsXG4gIHZhbGlkU2NoZW1hQ29udHJvbGxlcixcbn0pIHtcbiAgY29uc3Qgb3B0cyA9IGVuZm9yY2VBdXRoID8gcnVuT3B0aW9ucyA6IHt9O1xuICB0cnkge1xuICAgIGF3YWl0IHBlcmZvcm1BY3Rpb24oe1xuICAgICAgZGF0YWJhc2UsXG4gICAgICBxdWVyeSxcbiAgICAgIGFjdGlvbixcbiAgICAgIGZpZWxkVG9DbGVhcjogJ2RldmljZVRva2VuJyxcbiAgICAgIHJ1bk9wdGlvbnM6IG9wdHMsXG4gICAgICBtYW55OiB0cnVlLFxuICAgICAgdmFsaWRTY2hlbWFDb250cm9sbGVyLFxuICAgIH0pO1xuICAgIGxvZ1Jlc3VsdChhY3Rpb24sIG51bGwsIG51bGwpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBpZiAoZXJyICYmIGVyci5jb2RlID09PSBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5EKSB7XG4gICAgICBsb2dSZXN1bHQoYWN0aW9uLCAwLCBlcnIpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoZXJyICYmIGVyci5jb2RlID09PSBQYXJzZS5FcnJvci5PUEVSQVRJT05fRk9SQklEREVOKSB7XG4gICAgICBsb2dSZXN1bHQoYWN0aW9uLCBudWxsLCBlcnIpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsb2dSZXN1bHQoYWN0aW9uLCBudWxsLCBlcnIpO1xuICAgIHRocm93IGVycjtcbiAgfVxufVxuXG4vKipcbiAqIFJlc29sdmVzIGEgbWVyZ2UgY29uZmxpY3QgYmV0d2VlbiB0d28gYF9JbnN0YWxsYXRpb25gIHJvd3MgdGhhdCB0b2dldGhlciByZXByZXNlbnQgdGhlXG4gKiBzYW1lIGluc3RhbGw6IG9uZSBtYXRjaGVkIGJ5IGBpbnN0YWxsYXRpb25JZGAvYG9iamVjdElkYCAoYGlkTWF0Y2hgKSwgYW5kIGFub3RoZXIgaG9sZGluZ1xuICogdGhlIHNhbWUgYGRldmljZVRva2VuYCBidXQgbm8gYGluc3RhbGxhdGlvbklkYCAoYGRldmljZVRva2VuTWF0Y2hgKS4gVGhlIGBtZXJnZVByaW9yaXR5YFxuICogZGV0ZXJtaW5lcyB3aGljaCByb3cgc3Vydml2ZXM7IHRoZSBsb3NlciByZWNlaXZlcyB0aGUgY29uZmlndXJlZCBgYWN0aW9uYC4gUmV0dXJucyB0aGVcbiAqIHN1cnZpdm9yJ3MgYG9iamVjdElkYCBzbyB0aGUgc2F2ZSBmbG93IGNhbiB0YXJnZXQgaXQuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnNcbiAqIEBwYXJhbSB7RGF0YWJhc2VDb250cm9sbGVyfSBvcHRpb25zLmRhdGFiYXNlXG4gKiBAcGFyYW0ge3sgb2JqZWN0SWQ6IHN0cmluZywgaW5zdGFsbGF0aW9uSWQ/OiBzdHJpbmcsIGRldmljZVRva2VuPzogc3RyaW5nIH19IG9wdGlvbnMuaWRNYXRjaFxuICogQHBhcmFtIHt7IG9iamVjdElkOiBzdHJpbmcsIGRldmljZVRva2VuPzogc3RyaW5nIH19IG9wdGlvbnMuZGV2aWNlVG9rZW5NYXRjaFxuICogQHBhcmFtIHsnZGVsZXRlJ3wndXBkYXRlJ30gb3B0aW9ucy5hY3Rpb25cbiAqIEBwYXJhbSB7J2RldmljZVRva2VuJ3wnaW5zdGFsbGF0aW9uSWQnfSBvcHRpb25zLm1lcmdlUHJpb3JpdHlcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gb3B0aW9ucy5lbmZvcmNlQXV0aFxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMucnVuT3B0aW9uc1xuICogQHBhcmFtIHtTY2hlbWFDb250cm9sbGVyfSBvcHRpb25zLnZhbGlkU2NoZW1hQ29udHJvbGxlclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gc3Vydml2b3IncyBvYmplY3RJZFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYXBwbHlEdXBsaWNhdGVEZXZpY2VUb2tlbk1lcmdlKHtcbiAgZGF0YWJhc2UsXG4gIGlkTWF0Y2gsXG4gIGRldmljZVRva2VuTWF0Y2gsXG4gIGFjdGlvbixcbiAgbWVyZ2VQcmlvcml0eSxcbiAgZW5mb3JjZUF1dGgsXG4gIHJ1bk9wdGlvbnMsXG4gIHZhbGlkU2NoZW1hQ29udHJvbGxlcixcbn0pIHtcbiAgLy8gU2VsZi1tZXJnZSBndWFyZDogd2hlbiBib3RoIG1hdGNoZXMgcmVzb2x2ZSB0byB0aGUgc2FtZSByb3csIHRoZXJlJ3NcbiAgLy8gbm90aGluZyB0byBjbGVhbiB1cC4gU2tpcCB0aGUgYWN0aW9uIHNvIHdlIGRvbid0IGRlc3Ryb3kvdXBkYXRlIHRoZSByb3dcbiAgLy8gd2UncmUgYWJvdXQgdG8gcmV0dXJuIGFzIHRoZSBzdXJ2aXZvci5cbiAgaWYgKGlkTWF0Y2gub2JqZWN0SWQgPT09IGRldmljZVRva2VuTWF0Y2gub2JqZWN0SWQpIHtcbiAgICByZXR1cm4gaWRNYXRjaC5vYmplY3RJZDtcbiAgfVxuICBjb25zdCBvcHRzID0gZW5mb3JjZUF1dGggPyBydW5PcHRpb25zIDoge307XG4gIGxldCBsb3NlcjtcbiAgbGV0IHN1cnZpdm9ySWQ7XG4gIGxldCBmaWVsZFRvQ2xlYXI7XG4gIGlmIChtZXJnZVByaW9yaXR5ID09PSAnZGV2aWNlVG9rZW4nKSB7XG4gICAgbG9zZXIgPSBpZE1hdGNoO1xuICAgIHN1cnZpdm9ySWQgPSBkZXZpY2VUb2tlbk1hdGNoLm9iamVjdElkO1xuICAgIGZpZWxkVG9DbGVhciA9ICdpbnN0YWxsYXRpb25JZCc7XG4gIH0gZWxzZSBpZiAobWVyZ2VQcmlvcml0eSA9PT0gJ2luc3RhbGxhdGlvbklkJykge1xuICAgIGxvc2VyID0gZGV2aWNlVG9rZW5NYXRjaDtcbiAgICBzdXJ2aXZvcklkID0gaWRNYXRjaC5vYmplY3RJZDtcbiAgICBmaWVsZFRvQ2xlYXIgPSAnZGV2aWNlVG9rZW4nO1xuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biBpbnN0YWxsYXRpb24gZGVkdXAgbWVyZ2VQcmlvcml0eTogJHttZXJnZVByaW9yaXR5fWApO1xuICB9XG5cbiAgdHJ5IHtcbiAgICBhd2FpdCBwZXJmb3JtQWN0aW9uKHtcbiAgICAgIGRhdGFiYXNlLFxuICAgICAgcXVlcnk6IHsgb2JqZWN0SWQ6IGxvc2VyLm9iamVjdElkIH0sXG4gICAgICBhY3Rpb24sXG4gICAgICBmaWVsZFRvQ2xlYXIsXG4gICAgICBydW5PcHRpb25zOiBvcHRzLFxuICAgICAgbWFueTogZmFsc2UsXG4gICAgICB2YWxpZFNjaGVtYUNvbnRyb2xsZXIsXG4gICAgfSk7XG4gICAgbG9nUmVzdWx0KGFjdGlvbiwgMSwgbnVsbCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGlmIChlcnIgJiYgZXJyLmNvZGUgPT09IFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQpIHtcbiAgICAgIGxvZ1Jlc3VsdChhY3Rpb24sIDAsIGVycik7XG4gICAgfSBlbHNlIGlmIChlcnIgJiYgZXJyLmNvZGUgPT09IFBhcnNlLkVycm9yLk9QRVJBVElPTl9GT1JCSURERU4pIHtcbiAgICAgIGxvZ1Jlc3VsdChhY3Rpb24sIG51bGwsIGVycik7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvZ1Jlc3VsdChhY3Rpb24sIG51bGwsIGVycik7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICB9XG4gIHJldHVybiBzdXJ2aXZvcklkO1xufVxuXG5leHBvcnQgZGVmYXVsdCB7XG4gIHJlbW92ZUNvbmZsaWN0aW5nRGV2aWNlVG9rZW4sXG4gIGFwcGx5RHVwbGljYXRlRGV2aWNlVG9rZW5NZXJnZSxcbn07XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQUEsSUFBQUEsS0FBQSxHQUFBQyxzQkFBQSxDQUFBQyxPQUFBO0FBQ0EsSUFBQUMsT0FBQSxHQUFBRixzQkFBQSxDQUFBQyxPQUFBO0FBQThCLFNBQUFELHVCQUFBRyxDQUFBLFdBQUFBLENBQUEsSUFBQUEsQ0FBQSxDQUFBQyxVQUFBLEdBQUFELENBQUEsS0FBQUUsT0FBQSxFQUFBRixDQUFBO0FBRTlCLE1BQU1HLFVBQVUsR0FBRyxlQUFlO0FBRWxDLFNBQVNDLFNBQVNBLENBQUNDLE1BQU0sRUFB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
|