@proteinjs/user-server 1.20.2 → 1.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/generated/index.js +1 -1
  3. package/dist/generated/index.js.map +1 -1
  4. package/dist/src/authentication/PasswordResetToken.d.ts +45 -0
  5. package/dist/src/authentication/PasswordResetToken.d.ts.map +1 -0
  6. package/dist/src/authentication/PasswordResetToken.js +138 -0
  7. package/dist/src/authentication/PasswordResetToken.js.map +1 -0
  8. package/dist/src/routes/executePasswordReset.d.ts +4 -4
  9. package/dist/src/routes/executePasswordReset.d.ts.map +1 -1
  10. package/dist/src/routes/executePasswordReset.js +27 -26
  11. package/dist/src/routes/executePasswordReset.js.map +1 -1
  12. package/dist/src/routes/initiatePasswordReset.js +2 -2
  13. package/dist/src/routes/initiatePasswordReset.js.map +1 -1
  14. package/dist/src/routes/validateResetPasswordToken.d.ts.map +1 -1
  15. package/dist/src/routes/validateResetPasswordToken.js +13 -16
  16. package/dist/src/routes/validateResetPasswordToken.js.map +1 -1
  17. package/dist/src/services/Signup.d.ts +14 -0
  18. package/dist/src/services/Signup.d.ts.map +1 -1
  19. package/dist/src/services/Signup.js +102 -27
  20. package/dist/src/services/Signup.js.map +1 -1
  21. package/dist/test/ExecutePasswordReset.test.d.ts +2 -0
  22. package/dist/test/ExecutePasswordReset.test.d.ts.map +1 -0
  23. package/dist/test/ExecutePasswordReset.test.js +394 -0
  24. package/dist/test/ExecutePasswordReset.test.js.map +1 -0
  25. package/dist/test/SignupResendInvite.integration.test.d.ts +2 -0
  26. package/dist/test/SignupResendInvite.integration.test.d.ts.map +1 -0
  27. package/dist/test/SignupResendInvite.integration.test.js +235 -0
  28. package/dist/test/SignupResendInvite.integration.test.js.map +1 -0
  29. package/dist/test/ValidateResetToken.test.js +30 -7
  30. package/dist/test/ValidateResetToken.test.js.map +1 -1
  31. package/generated/index.ts +1 -1
  32. package/package.json +3 -3
  33. package/src/authentication/PasswordResetToken.ts +96 -0
  34. package/src/routes/executePasswordReset.ts +25 -24
  35. package/src/routes/initiatePasswordReset.ts +2 -2
  36. package/src/routes/validateResetPasswordToken.ts +12 -15
  37. package/src/services/Signup.ts +71 -21
  38. package/test/ExecutePasswordReset.test.ts +224 -0
  39. package/test/SignupResendInvite.integration.test.ts +163 -0
  40. package/test/ValidateResetToken.test.ts +24 -6
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var moment_1 = __importDefault(require("moment"));
43
+ /**
44
+ * Re-sending an invite (`SignupService.resendInvite`), against the emulator — outcomes on the row
45
+ * and on the transport fake, never call counts alone:
46
+ *
47
+ * - the token in the earlier email stops resolving, the fresh one resolves to the SAME invite;
48
+ * - the row count for the address stays one, its expiry re-stamped from the TTL knob, its inviter kept;
49
+ * - exactly one email goes out for the re-send, to the address, carrying the fresh signup link;
50
+ * - an address with no standing invite is refused: nothing written, nothing sent;
51
+ * - an address that already has an account is refused the same way (the invite was used).
52
+ *
53
+ * The email transport is stubbed at the module boundary (the SignupInvite.test.ts pattern) — SMTP
54
+ * is an external transport; the config fake echoes the signup path so the link can be asserted.
55
+ * The service door (`serviceMetadata.auth`) is pinned here too: re-send rides the 'users' permission
56
+ * exactly like send and revoke — the earlier gap that left invite minting public must not reopen.
57
+ */
58
+ var sendEmail = jest.fn();
59
+ jest.mock('@proteinjs/email-server', function () { return ({
60
+ EmailSender: jest.fn().mockImplementation(function () { return ({ sendEmail: sendEmail }); }),
61
+ getDefaultInviteEmailConfigFactory: function () { return ({
62
+ getConfig: function () { return ({
63
+ options: { subject: 'Your invite' },
64
+ getEmailContent: function (signupPathWithToken) { return ({
65
+ text: "Accept: /".concat(signupPathWithToken),
66
+ html: "<a href=\"/".concat(signupPathWithToken, "\">Accept</a>"),
67
+ }); },
68
+ }); },
69
+ }); },
70
+ getDefaultSignupConfirmationEmailConfigFactory: function () { return ({
71
+ getConfig: function () { return ({ getNewUserEmailContent: function () { return ({ text: 'welcome', html: '<p>welcome</p>' }); } }); },
72
+ }); },
73
+ }); });
74
+ var db_1 = require("@proteinjs/db");
75
+ var reflection_1 = require("@proteinjs/reflection");
76
+ var user_1 = require("@proteinjs/user");
77
+ var Signup_1 = require("../src/services/Signup");
78
+ var UserServerTestEnvironment_1 = require("./UserServerTestEnvironment");
79
+ var TIMEOUT = 60000;
80
+ /** Tolerance for the clock advancing between the stamp and the assertion. */
81
+ var CLOCK_SKEW_TOLERANCE_SECONDS = 60;
82
+ var inviteRows = function (email) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
83
+ switch (_a.label) {
84
+ case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.Invite, { email: email })];
85
+ case 1: return [2 /*return*/, (_a.sent())];
86
+ }
87
+ }); }); };
88
+ /** The re-send's own transport call: the one addressed to `email` after the earlier `before` calls. */
89
+ var emailsTo = function (email) {
90
+ return sendEmail.mock.calls.map(function (_a) {
91
+ var message = _a[0];
92
+ return message;
93
+ }).filter(function (message) { return message.to === email; });
94
+ };
95
+ describe('SignupService.resendInvite — against the emulator', function () {
96
+ var env = new UserServerTestEnvironment_1.UserServerTestEnvironment();
97
+ var inviter;
98
+ beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
99
+ return __generator(this, function (_a) {
100
+ switch (_a.label) {
101
+ case 0: return [4 /*yield*/, env.beforeAll()];
102
+ case 1:
103
+ _a.sent();
104
+ // The invite-mode factory the token lookup consults (`initializeSignup`) — seeded like the
105
+ // environment seeds its own loadables (the suites load src, never the generated source
106
+ // graph, so an unseeded lookup has no graph to walk); invite-optional, the library default.
107
+ reflection_1.SourceRepository.get().objectCache['@proteinjs/user-server/DefaultInviteConfigFactory'] = [{ getConfig: function () { return ({ isInviteOnly: false }); } }];
108
+ return [4 /*yield*/, env.createUser({ name: 'Inviter', email: 'inviter@example.com', roles: ['admin'] })];
109
+ case 2:
110
+ inviter = _a.sent();
111
+ env.actAs(inviter);
112
+ return [2 /*return*/];
113
+ }
114
+ });
115
+ }); }, TIMEOUT);
116
+ afterAll(function () { return __awaiter(void 0, void 0, void 0, function () {
117
+ return __generator(this, function (_a) {
118
+ switch (_a.label) {
119
+ case 0: return [4 /*yield*/, env.afterAll()];
120
+ case 1:
121
+ _a.sent();
122
+ return [2 /*return*/];
123
+ }
124
+ });
125
+ }); }, TIMEOUT);
126
+ beforeEach(function () {
127
+ sendEmail.mockClear();
128
+ });
129
+ it('retires the earlier token, mints a fresh one on the SAME row, and emails the fresh link exactly once', function () { return __awaiter(void 0, void 0, void 0, function () {
130
+ var email, sent, standing, earlierToken, earlierExpiry, resent, rows, refreshed, earlier, fresh, messages, resend;
131
+ var _a, _b;
132
+ return __generator(this, function (_c) {
133
+ switch (_c.label) {
134
+ case 0:
135
+ email = 'guest@example.com';
136
+ return [4 /*yield*/, new Signup_1.Signup().sendInvite(email)];
137
+ case 1:
138
+ sent = _c.sent();
139
+ expect(sent).toEqual({ sent: true });
140
+ return [4 /*yield*/, inviteRows(email)];
141
+ case 2:
142
+ standing = (_c.sent())[0];
143
+ earlierToken = standing.token;
144
+ earlierExpiry = (0, moment_1.default)(standing.tokenExpiresAt);
145
+ expect(emailsTo(email)).toHaveLength(1);
146
+ return [4 /*yield*/, new Signup_1.Signup().resendInvite('Guest@Example.com')];
147
+ case 3:
148
+ resent = _c.sent();
149
+ expect(resent).toEqual({ sent: true });
150
+ return [4 /*yield*/, inviteRows(email)];
151
+ case 4:
152
+ rows = _c.sent();
153
+ expect(rows).toHaveLength(1);
154
+ refreshed = rows[0];
155
+ expect(refreshed.id).toBe(standing.id);
156
+ expect(refreshed.token).not.toBe(earlierToken);
157
+ expect((_a = refreshed.invitedBy) === null || _a === void 0 ? void 0 : _a._id).toBe(inviter.id);
158
+ // The expiry is re-stamped from the knob (fresh window), never left at the earlier stamp.
159
+ expect((0, moment_1.default)(refreshed.tokenExpiresAt).isSameOrAfter(earlierExpiry)).toBe(true);
160
+ expect(Math.abs((0, moment_1.default)(refreshed.tokenExpiresAt).diff((0, moment_1.default)().add(Signup_1.INVITE_TOKEN_TTL_DAYS, 'days'), 'seconds'))).toBeLessThan(CLOCK_SKEW_TOLERANCE_SECONDS);
161
+ return [4 /*yield*/, new Signup_1.Signup().initializeSignup(earlierToken)];
162
+ case 5:
163
+ earlier = _c.sent();
164
+ expect(earlier.isReady).toBe(false);
165
+ expect(earlier.error).toMatch(/no longer valid/i);
166
+ return [4 /*yield*/, new Signup_1.Signup().initializeSignup(refreshed.token)];
167
+ case 6:
168
+ fresh = _c.sent();
169
+ expect(fresh.isReady).toBe(true);
170
+ expect((_b = fresh.invite) === null || _b === void 0 ? void 0 : _b.email).toBe(email);
171
+ messages = emailsTo(email);
172
+ expect(messages).toHaveLength(2);
173
+ resend = messages[1];
174
+ expect(resend.subject).toBe('Your invite');
175
+ expect(resend.text).toContain("signup?token=".concat(refreshed.token));
176
+ expect(resend.html).toContain("signup?token=".concat(refreshed.token));
177
+ expect(resend.text).not.toContain(earlierToken);
178
+ return [2 /*return*/];
179
+ }
180
+ });
181
+ }); }, TIMEOUT);
182
+ it('refuses an address with no standing invite — nothing written, nothing sent', function () { return __awaiter(void 0, void 0, void 0, function () {
183
+ var email, response, _a;
184
+ return __generator(this, function (_b) {
185
+ switch (_b.label) {
186
+ case 0:
187
+ email = 'nobody@example.com';
188
+ return [4 /*yield*/, new Signup_1.Signup().resendInvite(email)];
189
+ case 1:
190
+ response = _b.sent();
191
+ expect(response.sent).toBe(false);
192
+ expect(response.error).toMatch(/no invite/i);
193
+ _a = expect;
194
+ return [4 /*yield*/, inviteRows(email)];
195
+ case 2:
196
+ _a.apply(void 0, [_b.sent()]).toHaveLength(0);
197
+ expect(emailsTo(email)).toHaveLength(0);
198
+ return [2 /*return*/];
199
+ }
200
+ });
201
+ }); }, TIMEOUT);
202
+ it('refuses an address that already has an account — the invite was used', function () { return __awaiter(void 0, void 0, void 0, function () {
203
+ var email, response;
204
+ return __generator(this, function (_a) {
205
+ switch (_a.label) {
206
+ case 0:
207
+ email = 'member@example.com';
208
+ return [4 /*yield*/, env.createUser({ name: 'Member', email: email })];
209
+ case 1:
210
+ _a.sent();
211
+ return [4 /*yield*/, new Signup_1.Signup().resendInvite(email)];
212
+ case 2:
213
+ response = _a.sent();
214
+ expect(response.sent).toBe(false);
215
+ expect(response.error).toMatch(/already exists/i);
216
+ expect(emailsTo(email)).toHaveLength(0);
217
+ return [2 /*return*/];
218
+ }
219
+ });
220
+ }); }, TIMEOUT);
221
+ it('rides the users-permission door like send and revoke (never public)', function () {
222
+ var hasPermission = jest.spyOn(user_1.UserAuth, 'hasPermission');
223
+ try {
224
+ hasPermission.mockReturnValue(false);
225
+ expect(new Signup_1.Signup().serviceMetadata.auth.canAccess('resendInvite', ['x@example.com'])).toBe(false);
226
+ hasPermission.mockReturnValue(true);
227
+ expect(new Signup_1.Signup().serviceMetadata.auth.canAccess('resendInvite', ['x@example.com'])).toBe(true);
228
+ expect(hasPermission).toHaveBeenCalledWith(user_1.USER_PERMISSIONS.users);
229
+ }
230
+ finally {
231
+ hasPermission.mockRestore();
232
+ }
233
+ });
234
+ });
235
+ //# sourceMappingURL=SignupResendInvite.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignupResendInvite.integration.test.js","sourceRoot":"","sources":["../../test/SignupResendInvite.integration.test.ts"],"names":[],"mappings":"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"}
@@ -43,6 +43,7 @@ var moment_1 = __importDefault(require("moment"));
43
43
  var db_1 = require("@proteinjs/db");
44
44
  var user_1 = require("@proteinjs/user");
45
45
  var validateResetPasswordToken_1 = require("../src/routes/validateResetPasswordToken");
46
+ var PasswordResetToken_1 = require("../src/authentication/PasswordResetToken");
46
47
  var UserServerTestEnvironment_1 = require("./UserServerTestEnvironment");
47
48
  var testEnv = new UserServerTestEnvironment_1.UserServerTestEnvironment();
48
49
  var invokeValidate = function (query) { return __awaiter(void 0, void 0, void 0, function () {
@@ -67,6 +68,7 @@ var invokeValidate = function (query) { return __awaiter(void 0, void 0, void 0,
67
68
  }
68
69
  });
69
70
  }); };
71
+ var mintToken = function () { return new PasswordResetToken_1.PasswordResetToken().mint(); };
70
72
  var armResetToken = function (email, token, expiration) { return __awaiter(void 0, void 0, void 0, function () {
71
73
  var user;
72
74
  return __generator(this, function (_a) {
@@ -107,13 +109,15 @@ describe('validateResetPasswordToken route', function () {
107
109
  });
108
110
  }); });
109
111
  it('a valid token resolves isValid WITH the account email (the reset form identifier)', function () { return __awaiter(void 0, void 0, void 0, function () {
110
- var outcome;
112
+ var token, outcome;
111
113
  return __generator(this, function (_a) {
112
114
  switch (_a.label) {
113
- case 0: return [4 /*yield*/, armResetToken('reset-valid@test.local', 'tok-valid-1', (0, moment_1.default)().add(1, 'hour'))];
115
+ case 0:
116
+ token = mintToken();
117
+ return [4 /*yield*/, armResetToken('reset-valid@test.local', token, (0, moment_1.default)().add(1, 'hour'))];
114
118
  case 1:
115
119
  _a.sent();
116
- return [4 /*yield*/, invokeValidate({ token: 'tok-valid-1' })];
120
+ return [4 /*yield*/, invokeValidate({ token: token })];
117
121
  case 2:
118
122
  outcome = _a.sent();
119
123
  expect(outcome.status).toBe(200);
@@ -123,13 +127,15 @@ describe('validateResetPasswordToken route', function () {
123
127
  });
124
128
  }); });
125
129
  it('an expired token resolves invalid and leaks no email', function () { return __awaiter(void 0, void 0, void 0, function () {
126
- var outcome;
130
+ var token, outcome;
127
131
  return __generator(this, function (_a) {
128
132
  switch (_a.label) {
129
- case 0: return [4 /*yield*/, armResetToken('reset-expired@test.local', 'tok-expired-1', (0, moment_1.default)().subtract(1, 'minute'))];
133
+ case 0:
134
+ token = mintToken();
135
+ return [4 /*yield*/, armResetToken('reset-expired@test.local', token, (0, moment_1.default)().subtract(1, 'minute'))];
130
136
  case 1:
131
137
  _a.sent();
132
- return [4 /*yield*/, invokeValidate({ token: 'tok-expired-1' })];
138
+ return [4 /*yield*/, invokeValidate({ token: token })];
133
139
  case 2:
134
140
  outcome = _a.sent();
135
141
  expect(outcome.status).toBe(200);
@@ -143,7 +149,7 @@ describe('validateResetPasswordToken route', function () {
143
149
  var outcome;
144
150
  return __generator(this, function (_a) {
145
151
  switch (_a.label) {
146
- case 0: return [4 /*yield*/, invokeValidate({ token: 'tok-never-issued' })];
152
+ case 0: return [4 /*yield*/, invokeValidate({ token: mintToken() })];
147
153
  case 1:
148
154
  outcome = _a.sent();
149
155
  expect(outcome.status).toBe(200);
@@ -153,6 +159,23 @@ describe('validateResetPasswordToken route', function () {
153
159
  }
154
160
  });
155
161
  }); });
162
+ it.each([
163
+ ['a string of another shape', 'tok-never-issued'],
164
+ ['a parsed array', ['a', 'b']],
165
+ ['a parsed object', { passwordResetToken: null }],
166
+ ])('%s resolves invalid without a lookup and leaks no email', function (_label, token) { return __awaiter(void 0, void 0, void 0, function () {
167
+ var outcome;
168
+ return __generator(this, function (_a) {
169
+ switch (_a.label) {
170
+ case 0: return [4 /*yield*/, invokeValidate({ token: token })];
171
+ case 1:
172
+ outcome = _a.sent();
173
+ expect(outcome.status).toBe(200);
174
+ expect(outcome.body).toEqual({ isValid: false, message: 'Invalid token' });
175
+ return [2 /*return*/];
176
+ }
177
+ });
178
+ }); });
156
179
  it('a missing token is a 400', function () { return __awaiter(void 0, void 0, void 0, function () {
157
180
  var outcome;
158
181
  return __generator(this, function (_a) {
@@ -1 +1 @@
1
- {"version":3,"file":"ValidateResetToken.test.js","sourceRoot":"","sources":["../../test/ValidateResetToken.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ValidateResetToken.test.js","sourceRoot":"","sources":["../../test/ValidateResetToken.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAA4B;AAC5B,oCAA8C;AAC9C,wCAAyC;AACzC,uFAAsF;AACtF,+EAA8E;AAC9E,yEAAwE;AAExE,IAAM,OAAO,GAAG,IAAI,qDAAyB,EAAE,CAAC;AAehD,IAAM,cAAc,GAAG,UAAO,KAA8B;;;;;gBACpD,OAAO,GAAiB,EAAE,CAAC;gBAC3B,QAAQ,GAAG;oBACf,MAAM,YAAC,IAAY;wBACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;wBACtB,OAAO,IAAI,CAAC;oBACd,CAAC;oBACD,IAAI,YAAC,IAAc;wBACjB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;oBACtB,CAAC;iBACF,CAAC;gBACF,qBAAM,uDAA0B,CAAC,SAAS,CAAC,EAAE,KAAK,OAAA,EAAW,EAAE,QAAiB,CAAC,EAAA;;gBAAjF,SAAiF,CAAC;gBAClF,sBAAO,OAAO,EAAC;;;KAChB,CAAC;AAEF,IAAM,SAAS,GAAG,cAAM,OAAA,IAAI,uCAAkB,EAAE,CAAC,IAAI,EAAE,EAA/B,CAA+B,CAAC;AAExD,IAAM,aAAa,GAAG,UAAO,KAAa,EAAE,KAAa,EAAE,UAAyB;;;;oBACrE,qBAAM,OAAO,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,OAAA,EAAE,CAAC,EAAA;;gBAA9D,IAAI,GAAG,SAAuD;gBACpE,qBAAM,IAAA,kBAAa,GAAE,CAAC,MAAM,CAAC,aAAM,CAAC,IAAI,EAAE;wBACxC,EAAE,EAAE,IAAI,CAAC,EAAE;wBACX,kBAAkB,EAAE,KAAK;wBACzB,4BAA4B,EAAE,UAAU;qBACzC,CAAC,EAAA;;gBAJF,SAIE,CAAC;;;;KACJ,CAAC;AAEF,QAAQ,CAAC,kCAAkC,EAAE;IAC3C,SAAS,CAAC;;;wBACR,qBAAM,OAAO,CAAC,SAAS,EAAE,EAAA;;oBAAzB,SAAyB,CAAC;;;;SAC3B,CAAC,CAAC;IAEH,QAAQ,CAAC;;;wBACP,qBAAM,OAAO,CAAC,QAAQ,EAAE,EAAA;;oBAAxB,SAAwB,CAAC;;;;SAC1B,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE;;;;;oBAChF,KAAK,GAAG,SAAS,EAAE,CAAC;oBAC1B,qBAAM,aAAa,CAAC,wBAAwB,EAAE,KAAK,EAAE,IAAA,gBAAM,GAAE,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAA;;oBAA7E,SAA6E,CAAC;oBAE9D,qBAAM,cAAc,CAAC,EAAE,KAAK,OAAA,EAAE,CAAC,EAAA;;oBAAzC,OAAO,GAAG,SAA+B;oBAE/C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACjC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAC;;;;SAClF,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE;;;;;oBACnD,KAAK,GAAG,SAAS,EAAE,CAAC;oBAC1B,qBAAM,aAAa,CAAC,0BAA0B,EAAE,KAAK,EAAE,IAAA,gBAAM,GAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAA;;oBAAtF,SAAsF,CAAC;oBAEvE,qBAAM,cAAc,CAAC,EAAE,KAAK,OAAA,EAAE,CAAC,EAAA;;oBAAzC,OAAO,GAAG,SAA+B;oBAE/C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACjC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACzC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;;;;SAC5C,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE;;;;wBACzC,qBAAM,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,EAAA;;oBAAtD,OAAO,GAAG,SAA4C;oBAE5D,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACjC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACzC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;;;;SAC5C,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACN,CAAC,2BAA2B,EAAE,kBAAkB,CAAC;QACjD,CAAC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC9B,CAAC,iBAAiB,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;KAClD,CAAC,CAAC,yDAAyD,EAAE,UAAO,MAAM,EAAE,KAAK;;;;wBAChE,qBAAM,cAAc,CAAC,EAAE,KAAK,OAAA,EAAE,CAAC,EAAA;;oBAAzC,OAAO,GAAG,SAA+B;oBAE/C,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACjC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;;;;SAC5E,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE;;;;wBACb,qBAAM,cAAc,CAAC,EAAE,CAAC,EAAA;;oBAAlC,OAAO,GAAG,SAAwB;oBAExC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACjC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;;;SAC1C,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}