ftown 0.20.9 → 0.21.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.
Files changed (118) hide show
  1. package/dist/centrifugo-client.d.ts +15 -11
  2. package/dist/centrifugo-client.js +63 -70
  3. package/dist/centrifugo-client.js.map +1 -1
  4. package/dist/cli-client.d.ts +11 -1
  5. package/dist/cli-client.js +23 -3
  6. package/dist/cli-client.js.map +1 -1
  7. package/dist/command-rpc.js +4 -8
  8. package/dist/command-rpc.js.map +1 -1
  9. package/dist/identity-reset.d.ts +1 -0
  10. package/dist/identity-reset.js +31 -5
  11. package/dist/identity-reset.js.map +1 -1
  12. package/dist/index.js +184 -288
  13. package/dist/index.js.map +1 -1
  14. package/dist/ledger-pairing-client.d.ts +28 -0
  15. package/dist/ledger-pairing-client.js +152 -0
  16. package/dist/ledger-pairing-client.js.map +1 -0
  17. package/dist/ledger-relay.d.ts +124 -0
  18. package/dist/ledger-relay.js +453 -0
  19. package/dist/ledger-relay.js.map +1 -0
  20. package/dist/ledger-startup.d.ts +151 -0
  21. package/dist/ledger-startup.js +203 -0
  22. package/dist/ledger-startup.js.map +1 -0
  23. package/dist/relay-token.d.ts +39 -0
  24. package/dist/relay-token.js +130 -0
  25. package/dist/relay-token.js.map +1 -0
  26. package/dist/revocation-gating.d.ts +0 -13
  27. package/dist/revocation-gating.js +0 -16
  28. package/dist/revocation-gating.js.map +1 -1
  29. package/dist/security/bridge/identity.d.ts +2 -47
  30. package/dist/security/bridge/identity.js +3 -3
  31. package/dist/security/bridge/identity.js.map +1 -1
  32. package/dist/security/bridge/ledger-identity.d.ts +48 -0
  33. package/dist/security/bridge/ledger-identity.js +342 -0
  34. package/dist/security/bridge/ledger-identity.js.map +1 -0
  35. package/dist/security/bridge/ledger-store.d.ts +84 -0
  36. package/dist/security/bridge/ledger-store.js +485 -0
  37. package/dist/security/bridge/ledger-store.js.map +1 -0
  38. package/dist/security/bridge/metadata.d.ts +2 -2
  39. package/dist/security/bridge/permissions.js +2 -2
  40. package/dist/security/bridge/permissions.js.map +1 -1
  41. package/dist/security/bridge/policy.d.ts +42 -23
  42. package/dist/security/bridge/policy.js +214 -146
  43. package/dist/security/bridge/policy.js.map +1 -1
  44. package/dist/security/bridge/security.d.ts +84 -33
  45. package/dist/security/bridge/security.js +240 -166
  46. package/dist/security/bridge/security.js.map +1 -1
  47. package/dist/security/crypto.js +1 -1
  48. package/dist/security/crypto.js.map +1 -1
  49. package/dist/security/ledger/channels.d.ts +5 -0
  50. package/dist/security/ledger/channels.js +21 -0
  51. package/dist/security/ledger/channels.js.map +1 -0
  52. package/dist/security/ledger/ledger.d.ts +61 -0
  53. package/dist/security/ledger/ledger.js +597 -0
  54. package/dist/security/ledger/ledger.js.map +1 -0
  55. package/dist/security/ledger/pairing.d.ts +81 -0
  56. package/dist/security/ledger/pairing.js +414 -0
  57. package/dist/security/ledger/pairing.js.map +1 -0
  58. package/dist/security/ledger/proof.d.ts +24 -0
  59. package/dist/security/ledger/proof.js +161 -0
  60. package/dist/security/ledger/proof.js.map +1 -0
  61. package/dist/security/ledger/protocol.d.ts +100 -0
  62. package/dist/security/ledger/protocol.js +16 -0
  63. package/dist/security/ledger/protocol.js.map +1 -0
  64. package/dist/security/ledger/testing.d.ts +27 -0
  65. package/dist/security/ledger/testing.js +70 -0
  66. package/dist/security/ledger/testing.js.map +1 -0
  67. package/dist/security/ledger/webauthn.d.ts +12 -0
  68. package/dist/security/ledger/webauthn.js +147 -0
  69. package/dist/security/ledger/webauthn.js.map +1 -0
  70. package/dist/security/protocol.d.ts +5 -10
  71. package/dist/security-cli.d.ts +5 -0
  72. package/dist/security-cli.js +28 -16
  73. package/dist/security-cli.js.map +1 -1
  74. package/dist/token-provider.d.ts +39 -0
  75. package/dist/token-provider.js +101 -0
  76. package/dist/token-provider.js.map +1 -0
  77. package/dist/types.d.ts +1 -1
  78. package/package.json +2 -2
  79. package/skills/ftown/references/sessions.md +3 -2
  80. package/dist/bridge-auth.d.ts +0 -31
  81. package/dist/bridge-auth.js +0 -70
  82. package/dist/bridge-auth.js.map +0 -1
  83. package/dist/pairing-client.d.ts +0 -62
  84. package/dist/pairing-client.js +0 -469
  85. package/dist/pairing-client.js.map +0 -1
  86. package/dist/rotating-token-refresher.d.ts +0 -42
  87. package/dist/rotating-token-refresher.js +0 -91
  88. package/dist/rotating-token-refresher.js.map +0 -1
  89. package/dist/security/account-crypto.d.ts +0 -33
  90. package/dist/security/account-crypto.js +0 -649
  91. package/dist/security/account-crypto.js.map +0 -1
  92. package/dist/security/account-protocol.d.ts +0 -228
  93. package/dist/security/account-protocol.js +0 -2
  94. package/dist/security/account-protocol.js.map +0 -1
  95. package/dist/security/bridge/account-client.d.ts +0 -7
  96. package/dist/security/bridge/account-client.js +0 -101
  97. package/dist/security/bridge/account-client.js.map +0 -1
  98. package/dist/security/bridge/account-state.d.ts +0 -105
  99. package/dist/security/bridge/account-state.js +0 -79
  100. package/dist/security/bridge/account-state.js.map +0 -1
  101. package/dist/security/bridge/pending-approval.d.ts +0 -21
  102. package/dist/security/bridge/pending-approval.js +0 -80
  103. package/dist/security/bridge/pending-approval.js.map +0 -1
  104. package/dist/security/bridge/pending-enrollment.d.ts +0 -33
  105. package/dist/security/bridge/pending-enrollment.js +0 -177
  106. package/dist/security/bridge/pending-enrollment.js.map +0 -1
  107. package/dist/security/bridge/revocation.d.ts +0 -14
  108. package/dist/security/bridge/revocation.js +0 -40
  109. package/dist/security/bridge/revocation.js.map +0 -1
  110. package/dist/security/pending-approval-crypto.d.ts +0 -29
  111. package/dist/security/pending-approval-crypto.js +0 -716
  112. package/dist/security/pending-approval-crypto.js.map +0 -1
  113. package/dist/security/pending-approval-protocol.d.ts +0 -329
  114. package/dist/security/pending-approval-protocol.js +0 -2
  115. package/dist/security/pending-approval-protocol.js.map +0 -1
  116. package/dist/security/security-nudge.d.ts +0 -31
  117. package/dist/security/security-nudge.js +0 -98
  118. package/dist/security/security-nudge.js.map +0 -1
@@ -1,469 +0,0 @@
1
- /**
2
- * Device-pairing onboarding client (contract docs/plans/device-pairing-contract.md).
3
- *
4
- * OAuth-device-authorization-style flow: the bridge asks the server to start a
5
- * pairing request, prints a short user code + verification URL, and polls until
6
- * the logged-in user approves the named machine in the browser. On approval the
7
- * server returns the SAME token bundle shape as `/api/auth/bridge`.
8
- *
9
- * P8: no secret is ever logged. `deviceCode` (the poll credential) and every
10
- * returned token/refreshToken stay out of the `log` sink; only the low-value,
11
- * single-use `userCode` and the verification URL are printed.
12
- */
13
- import { encodeAccountEnrollmentLink, parseAccountEnrollmentLink } from './security/account-crypto.js';
14
- import { accountReadUrl } from './security/bridge/account-client.js';
15
- import { readAccountHistory } from './security/bridge/account-client.js';
16
- import { AccountTrustError } from './security/account-crypto.js';
17
- import { createPendingConfirmation, createPendingHello, normalizePendingAccountHint, openPendingResult, parsePendingOffer, parsePendingState, pendingComparisonCode, pendingHelloHash, pendingOfferHash, PENDING_APPROVAL_LIMITS } from './security/pending-approval-crypto.js';
18
- import { isDeepStrictEqual } from 'node:util';
19
- import { matchesPendingBridgeDecision } from './security/bridge/pending-approval.js';
20
- /** Servers deny a revoked bridgeId, and trusted browsers refuse to approve it; only a new identity pairs again. */
21
- export const REMOVED_COMPUTER_HINT = 'If this computer was removed from your account before, run `ftown start --new-identity`.';
22
- const realSleep = (ms) => new Promise((r) => setTimeout(r, ms));
23
- export async function runPairing(opts) {
24
- if (opts.pending)
25
- return runPendingPairing(opts, opts.pending);
26
- const { apiUrl, bridgeId, hostname, platform, localPort, localNonce, fetchImpl = fetch, sleepImpl = realSleep, log = (msg) => console.log(msg), } = opts;
27
- const enrollment = opts.enrollment ? await parseAccountEnrollmentLink(opts.enrollment) : undefined;
28
- if (enrollment && (enrollment.request.kind !== 'bridge' || enrollment.request.bridgeId !== bridgeId))
29
- throw new Error('Invalid encrypted bridge enrollment');
30
- const start = await postJson(fetchImpl, `${apiUrl}/api/auth/bridge/pair/start`, {
31
- bridgeId,
32
- hostname,
33
- platform,
34
- ...(enrollment ? { enrollment: enrollment.request } : {}),
35
- });
36
- // P8: userCode + URL are printed; deviceCode is NEVER logged.
37
- if (enrollment) {
38
- accountReadUrl(apiUrl);
39
- if (typeof start.userCode !== 'string' || !/^[A-Za-z0-9-]{1,100}$/.test(start.userCode)
40
- || typeof start.deviceCode !== 'string' || start.deviceCode.length < 1 || start.deviceCode.length > 200
41
- || !Number.isInteger(start.intervalMs) || start.intervalMs < 100 || start.intervalMs > 60000
42
- || !Number.isInteger(start.expiresInMs) || start.expiresInMs < 1 || start.expiresInMs > 300000)
43
- throw new Error('Invalid encrypted bridge approval response');
44
- await opts.onPairingCode?.(start.userCode);
45
- const url = new URL('/pair', apiUrl);
46
- url.searchParams.set('code', start.userCode);
47
- url.hash = `account=${encodeAccountEnrollmentLink(enrollment)}`;
48
- // An interactive CLI may display this directly; redirected logs only receive instructions.
49
- if (opts.displayEnrollmentUrl)
50
- opts.displayEnrollmentUrl(url.href);
51
- else if (process.stderr.isTTY)
52
- process.stderr.write(`Approve this computer: ${url.href}\n`);
53
- log('Waiting for encrypted computer approval. Run ftown security pair-link to display the current private approval link.');
54
- }
55
- else
56
- log('Approve this machine to connect it to ftown:\n' +
57
- ` ${start.verificationUri}\n` +
58
- ` code: ${start.userCode}\n` +
59
- 'Waiting for approval…');
60
- const deviceCode = start.deviceCode;
61
- const intervalMs = start.intervalMs;
62
- const deadline = Math.min(Date.now() + start.expiresInMs, enrollment ? Date.parse(enrollment.request.expiresAt) : Infinity);
63
- // Wait one interval before the first poll (the user needs time to approve).
64
- await sleepImpl(intervalMs);
65
- for (;;) {
66
- if (Date.now() >= deadline) {
67
- log(REMOVED_COMPUTER_HINT);
68
- throw new Error('Pairing timed out');
69
- }
70
- const poll = await postJson(fetchImpl, `${apiUrl}/api/auth/bridge/pair/poll`, {
71
- deviceCode,
72
- localPort,
73
- });
74
- switch (poll.status) {
75
- case 'approved': {
76
- if (!poll.ownerId || !poll.token || !poll.refreshToken || !poll.centrifugoUrl || !poll.userId) {
77
- throw new Error('Pairing approved but token bundle was incomplete');
78
- }
79
- if (enrollment && (!poll.accountApproval || !poll.accountHistory))
80
- throw new Error('Encrypted bridge approval is required; no legacy fallback');
81
- return {
82
- ownerId: poll.ownerId,
83
- token: poll.token,
84
- refreshToken: poll.refreshToken,
85
- centrifugoUrl: poll.centrifugoUrl,
86
- userId: poll.userId,
87
- ...(enrollment ? { accountApproval: poll.accountApproval, accountHistory: poll.accountHistory } : {}),
88
- };
89
- }
90
- case 'pending':
91
- await sleepImpl(intervalMs);
92
- break;
93
- case 'slow_down':
94
- // Advisory back-off: wait an extra interval on top of the normal one.
95
- await sleepImpl(intervalMs * 2);
96
- break;
97
- case 'denied':
98
- log(REMOVED_COMPUTER_HINT);
99
- throw new Error('Pairing denied');
100
- case 'expired':
101
- log(REMOVED_COMPUTER_HINT);
102
- throw new Error('Pairing request expired before approval');
103
- case 'consumed':
104
- throw new Error('Pairing code was already consumed');
105
- case 'unknown':
106
- throw new Error('Pairing request not found (unknown device code)');
107
- default:
108
- throw new Error(`Unexpected pairing status: ${String(poll.status)}`);
109
- }
110
- }
111
- }
112
- class PendingNetworkError extends Error {
113
- constructor() { super('Computer approval service is temporarily unavailable.'); }
114
- }
115
- function record(value) {
116
- if (!value || typeof value !== 'object' || Array.isArray(value))
117
- throw new AccountTrustError('account_enrollment_invalid');
118
- return value;
119
- }
120
- function text(value, limit = 16384) {
121
- if (typeof value !== 'string' || value.length < 1 || value.length > limit)
122
- throw new AccountTrustError('account_enrollment_invalid');
123
- return value;
124
- }
125
- async function abortable(promise, signal) {
126
- signal.throwIfAborted();
127
- let listener;
128
- try {
129
- return await Promise.race([promise, new Promise((_resolve, reject) => {
130
- listener = () => reject(signal.reason);
131
- signal.addEventListener('abort', listener, { once: true });
132
- if (signal.aborted)
133
- listener();
134
- })]);
135
- }
136
- finally {
137
- if (listener)
138
- signal.removeEventListener('abort', listener);
139
- }
140
- }
141
- /** Bound the entire fetch AND body read, and never send a credential through a redirect. */
142
- async function pendingJson(opts, path, body, lifetime) {
143
- const base = accountReadUrl(opts.apiUrl);
144
- const url = new URL(path, base.origin);
145
- if (url.origin !== base.origin || !path.startsWith('/api/'))
146
- throw new AccountTrustError('account_enrollment_invalid');
147
- const encoded = JSON.stringify(body);
148
- if (Buffer.byteLength(encoded) > PENDING_APPROVAL_LIMITS.maxWireBytes)
149
- throw new AccountTrustError('account_capacity_exceeded');
150
- const timeout = new AbortController();
151
- const timer = setTimeout(() => timeout.abort(new PendingNetworkError()), PENDING_APPROVAL_LIMITS.requestTimeoutMs);
152
- const signal = AbortSignal.any([lifetime, timeout.signal]);
153
- try {
154
- const result = await abortable((async () => {
155
- let response;
156
- try {
157
- response = await (opts.fetchImpl ?? fetch)(url, { method: 'POST', headers: { 'Content-Type': 'application/json' },
158
- body: encoded, redirect: 'manual', cache: 'no-store', signal });
159
- }
160
- catch {
161
- if (lifetime.aborted)
162
- lifetime.throwIfAborted();
163
- throw new PendingNetworkError();
164
- }
165
- if (response.status >= 300 && response.status < 400)
166
- throw new AccountTrustError('account_enrollment_invalid');
167
- if (response.status === 429 || response.status >= 500)
168
- throw new PendingNetworkError();
169
- if (!response.ok)
170
- throw new AccountTrustError(response.status === 409 ? 'account_trust_conflict'
171
- : response.status === 410 ? 'account_enrollment_expired' : 'account_enrollment_invalid');
172
- const declared = response.headers.get('content-length');
173
- if (declared !== null && (!/^\d+$/.test(declared) || Number(declared) > PENDING_APPROVAL_LIMITS.maxWireBytes))
174
- throw new AccountTrustError('account_capacity_exceeded');
175
- const reader = response.body?.getReader();
176
- if (!reader)
177
- throw new AccountTrustError('account_enrollment_invalid');
178
- const cancel = () => { void reader.cancel().catch(() => undefined); };
179
- signal.addEventListener('abort', cancel, { once: true });
180
- if (signal.aborted)
181
- cancel();
182
- const chunks = [];
183
- let size = 0;
184
- try {
185
- for (;;) {
186
- let next;
187
- try {
188
- next = await reader.read();
189
- }
190
- catch {
191
- signal.throwIfAborted();
192
- throw new PendingNetworkError();
193
- }
194
- const { done, value } = next;
195
- if (done)
196
- break;
197
- size += value.byteLength;
198
- if (size > PENDING_APPROVAL_LIMITS.maxWireBytes)
199
- throw new AccountTrustError('account_capacity_exceeded');
200
- chunks.push(value);
201
- }
202
- const bytes = Buffer.concat(chunks);
203
- try {
204
- return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
205
- }
206
- catch {
207
- throw new AccountTrustError('account_enrollment_invalid');
208
- }
209
- }
210
- finally {
211
- signal.removeEventListener('abort', cancel);
212
- if (signal.aborted || size > PENDING_APPROVAL_LIMITS.maxWireBytes)
213
- cancel();
214
- reader.releaseLock();
215
- }
216
- })(), signal);
217
- lifetime.throwIfAborted();
218
- return result;
219
- }
220
- finally {
221
- clearTimeout(timer);
222
- }
223
- }
224
- async function runPendingPairing(opts, ctx) {
225
- if (opts.enrollment || ctx.request.kind !== 'bridge' || ctx.request.bridgeId !== opts.bridgeId
226
- || !isDeepStrictEqual(ctx.request.candidate, ctx.identity.endpoint) || !/^[A-Za-z0-9_-]{43}$/.test(ctx.candidateToken))
227
- throw new AccountTrustError('account_enrollment_invalid');
228
- const hint = normalizePendingAccountHint(opts.accountHint);
229
- const expires = Date.parse(ctx.request.expiresAt);
230
- const expiry = new AbortController();
231
- const timer = setTimeout(() => expiry.abort(new AccountTrustError('account_enrollment_expired')), Math.max(0, expires - Date.now()));
232
- const signal = AbortSignal.any([ctx.signal, expiry.signal]);
233
- const processNonce = Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('base64url');
234
- let offer;
235
- let packet;
236
- let confirmedOfferHash;
237
- let credentials;
238
- let localStatus = { version: 1, requestId: ctx.request.requestId, processNonce,
239
- expiresAt: ctx.request.expiresAt, phase: 'waiting', offerHash: null, comparisonCode: null };
240
- let decision;
241
- let decisionController = new AbortController();
242
- let statusKey = '';
243
- const assertLive = () => { signal.throwIfAborted(); if (Date.now() >= expires)
244
- throw new AccountTrustError('account_enrollment_expired'); };
245
- const updateStatus = async (next) => {
246
- const key = JSON.stringify(next);
247
- if (key === statusKey)
248
- return;
249
- assertLive();
250
- await ctx.onLocalStatus(next);
251
- assertLive();
252
- statusKey = key;
253
- localStatus = next;
254
- decision = undefined;
255
- decisionController.abort();
256
- decisionController = new AbortController();
257
- if (next.phase === 'waiting' || next.phase === 'comparing' || next.phase === 'confirming') {
258
- const current = decisionController;
259
- void ctx.waitForLocalDecision(next, AbortSignal.any([signal, current.signal])).then(value => {
260
- if (!current.signal.aborted && !signal.aborted && matchesPendingBridgeDecision(next, value))
261
- decision = value;
262
- }).catch(error => { if (!current.signal.aborted && !signal.aborted)
263
- expiry.abort(error); });
264
- }
265
- };
266
- const wait = () => abortable((opts.sleepImpl ?? realSleep)(PENDING_APPROVAL_LIMITS.pollIntervalMs), signal);
267
- const rpc = (action) => pendingJson(opts, `/api/security/approvals/${ctx.request.requestId}/candidate`, { candidateToken: ctx.candidateToken, action }, signal);
268
- let deviceCode;
269
- let latest;
270
- const checkRejection = async () => {
271
- if (decision?.decision !== 'reject' || !matchesPendingBridgeDecision(localStatus, decision))
272
- return;
273
- await updateStatus({ ...localStatus, phase: 'cancelled', comparisonCode: null });
274
- await rpc({ type: 'cancel', expectedRevision: latest?.revision ?? 0 }).catch(() => undefined);
275
- throw new Error('Computer approval cancelled locally.');
276
- };
277
- try {
278
- await updateStatus(localStatus);
279
- (opts.log ?? console.log)('Waiting for approval in your trusted browser. Run ftown security pair-status to compare, then pair-confirm CODE or pair-reject.');
280
- // Retry EXACT create payload: token and request were durable before this first HTTP call.
281
- const startBody = { bridgeId: opts.bridgeId, hostname: opts.hostname, platform: opts.platform,
282
- approvalMethod: 'pending-v1', accountHint: hint, candidateToken: ctx.candidateToken, enrollment: ctx.request };
283
- for (;;) {
284
- assertLive();
285
- await checkRejection();
286
- try {
287
- const start = record(await pendingJson(opts, '/api/auth/bridge/pair/start', startBody, signal));
288
- deviceCode = text(start.deviceCode, 200);
289
- text(start.userCode, 100);
290
- text(start.verificationUri, 4096);
291
- if (!Number.isInteger(start.intervalMs) || Number(start.intervalMs) < 100 || Number(start.intervalMs) > 60000
292
- || !Number.isInteger(start.expiresInMs) || Number(start.expiresInMs) < 1 || Number(start.expiresInMs) > PENDING_APPROVAL_LIMITS.lifetimeMs)
293
- throw new AccountTrustError('account_enrollment_invalid');
294
- break;
295
- }
296
- catch (error) {
297
- if (!(error instanceof PendingNetworkError))
298
- throw error;
299
- await wait();
300
- }
301
- }
302
- for (;;) {
303
- assertLive();
304
- await checkRejection();
305
- try {
306
- const state = await parsePendingState(await rpc({ type: 'read' }));
307
- if (!isDeepStrictEqual(state.request, ctx.request))
308
- throw new AccountTrustError('account_enrollment_invalid');
309
- if (latest && state.revision < latest.revision)
310
- throw new AccountTrustError('account_trust_conflict');
311
- if (latest && state.revision === latest.revision && !isDeepStrictEqual(state, latest))
312
- throw new AccountTrustError('account_trust_conflict');
313
- latest = state;
314
- if (['rejected', 'cancelled', 'expired', 'conflict'].includes(state.status)) {
315
- await updateStatus({ ...localStatus, phase: state.status, comparisonCode: null });
316
- throw new Error(`Computer approval ${state.status}. Start a new explicit request.`);
317
- }
318
- const selectedDecision = decision;
319
- if (selectedDecision?.decision === 'reject')
320
- await checkRejection();
321
- if (state.status === 'pending') {
322
- await wait();
323
- continue;
324
- }
325
- if (!('offer' in state))
326
- throw new AccountTrustError('account_enrollment_invalid');
327
- const receivedOffer = await parsePendingOffer(state.offer, ctx.request);
328
- if (!offer) {
329
- await ctx.pinOffer(receivedOffer);
330
- assertLive();
331
- offer = receivedOffer;
332
- await updateStatus({ ...localStatus, phase: 'waiting', offerHash: await pendingOfferHash(offer), comparisonCode: null });
333
- packet = await createPendingHello(ctx.identity, offer);
334
- assertLive();
335
- await ctx.pinHello(packet.hello);
336
- assertLive();
337
- }
338
- else if (await pendingOfferHash(receivedOffer) !== await pendingOfferHash(offer))
339
- throw new AccountTrustError('account_trust_conflict');
340
- if (!packet)
341
- throw new AccountTrustError('account_enrollment_invalid');
342
- if (state.status === 'offered') {
343
- await rpc({ type: 'hello', expectedRevision: state.revision, hello: packet.hello });
344
- continue;
345
- }
346
- if (!('hello' in state) || await pendingHelloHash(state.hello) !== await pendingHelloHash(packet.hello))
347
- throw new AccountTrustError('account_trust_conflict');
348
- if (state.status === 'submitted') {
349
- await wait();
350
- continue;
351
- }
352
- if (!('reveal' in state))
353
- throw new AccountTrustError('account_enrollment_invalid');
354
- const comparisonCode = await pendingComparisonCode(offer, packet.secret, state.reveal);
355
- const offerHash = await pendingOfferHash(offer);
356
- if (!confirmedOfferHash) {
357
- if (selectedDecision?.decision !== 'confirm' || !matchesPendingBridgeDecision(localStatus, selectedDecision)) {
358
- await updateStatus({ ...localStatus, phase: 'comparing', offerHash, comparisonCode });
359
- await wait();
360
- continue;
361
- }
362
- await updateStatus({ ...localStatus, phase: 'confirming', offerHash, comparisonCode });
363
- confirmedOfferHash = offerHash;
364
- }
365
- const proof = await createPendingConfirmation(offer, packet.secret, state.reveal, confirmedOfferHash);
366
- assertLive();
367
- await checkRejection();
368
- if (state.status === 'comparing') {
369
- await rpc({ type: 'confirm', expectedRevision: state.revision, confirmation: proof });
370
- continue;
371
- }
372
- if (!('confirmation' in state) || !isDeepStrictEqual(state.confirmation, proof))
373
- throw new AccountTrustError('account_enrollment_invalid');
374
- if (state.status === 'confirmed') {
375
- await wait();
376
- continue;
377
- }
378
- if (state.status !== 'approved')
379
- throw new AccountTrustError('account_enrollment_invalid');
380
- if (!credentials) {
381
- const polled = record(await pendingJson(opts, '/api/auth/bridge/pair/poll', { deviceCode, localPort: opts.localPort }, signal));
382
- if (polled.status === 'pending' || polled.status === 'slow_down') {
383
- await wait();
384
- continue;
385
- }
386
- if (polled.status !== 'approved' || !polled.pendingApproval)
387
- throw new AccountTrustError('account_enrollment_invalid');
388
- credentials = { status: 'approved', ownerId: text(polled.ownerId, 100), token: text(polled.token), refreshToken: text(polled.refreshToken),
389
- centrifugoUrl: text(polled.centrifugoUrl, 4096), userId: text(polled.userId, 254), pendingApproval: state.result };
390
- }
391
- // The candidate RPC result may be renewed after credentials/pair bundle were consumed.
392
- const readTimeout = new AbortController();
393
- const readTimer = setTimeout(() => readTimeout.abort(new PendingNetworkError()), PENDING_APPROVAL_LIMITS.requestTimeoutMs);
394
- let history;
395
- try {
396
- const readSignal = AbortSignal.any([signal, readTimeout.signal]);
397
- history = await abortable(readAccountHistory(opts.apiUrl, credentials.token, readSignal, opts.fetchImpl ?? fetch), readSignal);
398
- }
399
- finally {
400
- clearTimeout(readTimer);
401
- }
402
- if (!history)
403
- throw new AccountTrustError('account_approval_required');
404
- const readAt = Date.now();
405
- assertLive();
406
- await checkRejection();
407
- const opened = await openPendingResult(ctx.identity, offer, packet.secret, state.reveal, confirmedOfferHash, state.result, history);
408
- assertLive();
409
- await checkRejection();
410
- if (opened.status === 'awaiting-delivery-renewal') {
411
- await wait();
412
- continue;
413
- }
414
- if (opened.trust.checkpoint.ownerId !== credentials.ownerId)
415
- throw new AccountTrustError('account_enrollment_invalid');
416
- await updateStatus({ ...localStatus, phase: 'approved', offerHash, comparisonCode: null });
417
- return { ...credentials, pendingApproval: state.result, approvalMethod: 'pending-v1', verifiedAccountTrust: opened.trust, lastServerReadAt: readAt };
418
- }
419
- catch (error) {
420
- // Availability only: malformed proof/history or conflicts never turn into retries.
421
- if (!(error instanceof PendingNetworkError) && !(error instanceof Error && error.name === 'AccountReadUnavailable'))
422
- throw error;
423
- await wait();
424
- }
425
- }
426
- }
427
- catch (error) {
428
- let finalPhase = localStatus.phase;
429
- if (!['approved', 'rejected', 'cancelled', 'expired', 'conflict'].includes(localStatus.phase)) {
430
- const phase = error instanceof AccountTrustError && error.code === 'account_enrollment_expired' ? 'expired'
431
- : error instanceof AccountTrustError && error.code === 'account_trust_conflict' ? 'conflict' : 'cancelled';
432
- finalPhase = phase;
433
- await ctx.onLocalStatus({ ...localStatus, phase, comparisonCode: null }).catch(() => undefined);
434
- }
435
- if (['rejected', 'expired'].includes(finalPhase))
436
- (opts.log ?? console.log)(REMOVED_COMPUTER_HINT);
437
- throw error;
438
- }
439
- finally {
440
- clearTimeout(timer);
441
- decisionController.abort();
442
- expiry.abort();
443
- packet = undefined;
444
- }
445
- }
446
- /** Best effort only; the new process always uses a different request and keeps its own key. */
447
- export async function cancelPreviousPendingPairing(apiUrl, previous, signal) {
448
- const options = { apiUrl };
449
- try {
450
- const path = `/api/security/approvals/${previous.requestId}/candidate`;
451
- const state = await parsePendingState(await pendingJson(options, path, { candidateToken: previous.candidateToken, action: { type: 'read' } }, signal));
452
- if (['approved', 'rejected', 'cancelled', 'expired', 'conflict'].includes(state.status))
453
- return;
454
- await pendingJson(options, path, { candidateToken: previous.candidateToken, action: { type: 'cancel', expectedRevision: state.revision } }, signal);
455
- }
456
- catch { /* Cancellation cannot grant trust and never causes an automatic fallback. */ }
457
- }
458
- async function postJson(fetchImpl, url, body) {
459
- const res = await fetchImpl(url, {
460
- method: 'POST',
461
- headers: { 'Content-Type': 'application/json' },
462
- body: JSON.stringify(body),
463
- });
464
- if (!res.ok) {
465
- throw new Error(`Pairing request failed (${res.status})`);
466
- }
467
- return res.json();
468
- }
469
- //# sourceMappingURL=pairing-client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pairing-client.js","sourceRoot":"","sources":["../src/pairing-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAEvG,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,iBAAiB,EACpG,iBAAiB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAG1K,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAErF,mHAAmH;AACnH,MAAM,CAAC,MAAM,qBAAqB,GAAG,0FAA0F,CAAC;AA2EhI,MAAM,SAAS,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAUvF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAuB;IACtD,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/D,MAAM,EACJ,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,SAAS,GAAG,KAAK,EACjB,SAAS,GAAG,SAAS,EACrB,GAAG,GAAG,CAAC,GAAW,EAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAC9C,GAAG,IAAI,CAAC;IACT,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnG,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAE7J,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAoB,SAAS,EAAE,GAAG,MAAM,6BAA6B,EAAE;QACjG,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1D,CAAC,CAAC;IAEH,8DAA8D;IAC9D,IAAI,UAAU,EAAE,CAAC;QACf,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;eAClF,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,GAAG;eACpG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,UAAU,GAAG,GAAG,IAAI,KAAK,CAAC,UAAU,GAAG,KAAK;eACzF,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,WAAW,GAAG,CAAC,IAAI,KAAK,CAAC,WAAW,GAAG,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChK,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnF,GAAG,CAAC,IAAI,GAAG,WAAW,2BAA2B,CAAC,UAAU,CAAC,EAAE,CAAC;QAChE,2FAA2F;QAC3F,IAAI,IAAI,CAAC,oBAAoB;YAAE,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;aAC9D,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;QAC5F,GAAG,CAAC,qHAAqH,CAAC,CAAC;IAC7H,CAAC;;QAAM,GAAG,CACR,gDAAgD;YAC9C,OAAO,KAAK,CAAC,eAAe,IAAI;YAChC,aAAa,KAAK,CAAC,QAAQ,IAAI;YAC/B,uBAAuB,CAC1B,CAAC;IAEF,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACpC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE5H,4EAA4E;IAC5E,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;IAE5B,SAAS,CAAC;QACR,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC3B,GAAG,CAAC,qBAAqB,CAAC,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAmB,SAAS,EAAE,GAAG,MAAM,4BAA4B,EAAE;YAC9F,UAAU;YACV,SAAS;SACV,CAAC,CAAC;QAEH,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBAC9F,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;gBACtE,CAAC;gBACD,IAAI,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;gBAChJ,OAAO;oBACL,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,YAAY,EAAE,IAAI,CAAC,YAAY;oBAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;oBACjC,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,eAAgB,EAAE,cAAc,EAAE,IAAI,CAAC,cAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACxG,CAAC;YACJ,CAAC;YACD,KAAK,SAAS;gBACZ,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;gBAC5B,MAAM;YACR,KAAK,WAAW;gBACd,sEAAsE;gBACtE,MAAM,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBAChC,MAAM;YACR,KAAK,QAAQ;gBACX,GAAG,CAAC,qBAAqB,CAAC,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACpC,KAAK,SAAS;gBACZ,GAAG,CAAC,qBAAqB,CAAC,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,KAAK,UAAU;gBACb,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACvD,KAAK,SAAS;gBACZ,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACrE;gBACE,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,CAAE,IAA2B,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,mBAAoB,SAAQ,KAAK;IACrC,gBAAgB,KAAK,CAAC,uDAAuD,CAAC,CAAC,CAAC,CAAC;CAClF;AACD,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;IAC3H,OAAO,KAAgC,CAAC;AAC1C,CAAC;AACD,SAAS,IAAI,CAAC,KAAc,EAAE,KAAK,GAAG,KAAK;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK;QAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;IACrI,OAAO,KAAK,CAAC;AACf,CAAC;AACD,KAAK,UAAU,SAAS,CAAI,OAAmB,EAAE,MAAmB;IAClE,MAAM,CAAC,cAAc,EAAE,CAAC;IACxB,IAAI,QAAkC,CAAC;IACvC,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;gBAC1E,QAAQ,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBACnG,IAAI,MAAM,CAAC,OAAO;oBAAE,QAAQ,EAAE,CAAC;YACjC,CAAC,CAAC,CAAC,CAAC,CAAC;IACP,CAAC;YAAS,CAAC;QAAC,IAAI,QAAQ;YAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAAC,CAAC;AAC5E,CAAC;AACD,4FAA4F;AAC5F,KAAK,UAAU,WAAW,CAAC,IAAqD,EAAE,IAAY,EAAE,IAAa,EAAE,QAAqB;IAClI,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;IACvH,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,uBAAuB,CAAC,YAAY;QAAE,MAAM,IAAI,iBAAiB,CAAC,2BAA2B,CAAC,CAAC;IAChI,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,mBAAmB,EAAE,CAAC,EAAE,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;IACnH,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE;YACzC,IAAI,QAAkB,CAAC;YACvB,IAAI,CAAC;gBAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBACrH,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;YAAC,CAAC;YACpE,MAAM,CAAC;gBAAC,IAAI,QAAQ,CAAC,OAAO;oBAAE,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAAC,MAAM,IAAI,mBAAmB,EAAE,CAAC;YAAC,CAAC;YAC3F,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG;gBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;YAC/G,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG;gBAAE,MAAM,IAAI,mBAAmB,EAAE,CAAC;YACvF,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,wBAAwB;oBAC9F,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC;YAC3F,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;YACxD,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,uBAAuB,CAAC,YAAY,CAAC;gBAAE,MAAM,IAAI,iBAAiB,CAAC,2BAA2B,CAAC,CAAC;YACxK,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;YAC1C,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;YACvE,MAAM,MAAM,GAAG,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACtE,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,IAAI,MAAM,CAAC,OAAO;gBAAE,MAAM,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAiB,EAAE,CAAC;YAAC,IAAI,IAAI,GAAG,CAAC,CAAC;YAC9C,IAAI,CAAC;gBACH,SAAS,CAAC;oBACR,IAAI,IAA0C,CAAC;oBAC/C,IAAI,CAAC;wBAAC,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;oBAAC,CAAC;oBACnC,MAAM,CAAC;wBAAC,MAAM,CAAC,cAAc,EAAE,CAAC;wBAAC,MAAM,IAAI,mBAAmB,EAAE,CAAC;oBAAC,CAAC;oBACnE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;oBAAC,IAAI,IAAI;wBAAE,MAAM;oBAC9C,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;oBACzB,IAAI,IAAI,GAAG,uBAAuB,CAAC,YAAY;wBAAE,MAAM,IAAI,iBAAiB,CAAC,2BAA2B,CAAC,CAAC;oBAC1G,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrB,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACpC,IAAI,CAAC;oBAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAY,CAAC;gBAAC,CAAC;gBAC9F,MAAM,CAAC;oBAAC,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBAAC,CAAC;YACtE,CAAC;oBAAS,CAAC;gBAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAAC,IAAI,MAAM,CAAC,OAAO,IAAI,IAAI,GAAG,uBAAuB,CAAC,YAAY;oBAAE,MAAM,EAAE,CAAC;gBAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAAC,CAAC;QAC/J,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACd,QAAQ,CAAC,cAAc,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;YAAS,CAAC;QAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAuB,EAAE,GAAgC;IACxF,IAAI,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ;WACzF,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;IACpL,MAAM,IAAI,GAAG,2BAA2B,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACrI,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACnG,IAAI,KAA+B,CAAC;IAAC,IAAI,MAAqC,CAAC;IAC/E,IAAI,kBAAsC,CAAC;IAAC,IAAI,WAA+C,CAAC;IAChG,IAAI,WAAW,GAAwB,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,YAAY;QACjG,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;IAC9F,IAAI,QAA2C,CAAC;IAChD,IAAI,kBAAkB,GAAG,IAAI,eAAe,EAAE,CAAC;IAC/C,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,MAAM,UAAU,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,OAAO;QAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5I,MAAM,YAAY,GAAG,KAAK,EAAE,IAAyB,EAAE,EAAE;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO;QAC9B,UAAU,EAAE,CAAC;QAAC,MAAM,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAAC,UAAU,EAAE,CAAC;QAC1D,SAAS,GAAG,GAAG,CAAC;QAAC,WAAW,GAAG,IAAI,CAAC;QAAC,QAAQ,GAAG,SAAS,CAAC;QAC1D,kBAAkB,CAAC,KAAK,EAAE,CAAC;QAAC,kBAAkB,GAAG,IAAI,eAAe,EAAE,CAAC;QACvE,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,IAAI,IAAI,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;YAC1F,MAAM,OAAO,GAAG,kBAAkB,CAAC;YACnC,KAAK,GAAG,CAAC,oBAAoB,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;gBAC1F,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,4BAA4B,CAAC,IAAI,EAAE,KAAK,CAAC;oBAAE,QAAQ,GAAG,KAAK,CAAC;YAChH,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,uBAAuB,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5G,MAAM,GAAG,GAAG,CAAC,MAAe,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,2BAA2B,GAAG,CAAC,OAAO,CAAC,SAAS,YAAY,EAAE,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;IACzK,IAAI,UAA8B,CAAC;IAAC,IAAI,MAAwC,CAAC;IACjF,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE;QAChC,IAAI,QAAQ,EAAE,QAAQ,KAAK,QAAQ,IAAI,CAAC,4BAA4B,CAAC,WAAW,EAAE,QAAQ,CAAC;YAAE,OAAO;QACpG,MAAM,YAAY,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QACjF,MAAM,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9F,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,iIAAiI,CAAC,CAAC;QAC7J,0FAA0F;QAC1F,MAAM,SAAS,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ;YAC3F,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACjH,SAAS,CAAC;YACR,UAAU,EAAE,CAAC;YACb,MAAM,cAAc,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,WAAW,CAAC,IAAI,EAAE,6BAA6B,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;gBAChG,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;gBAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;gBAAC,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;gBACvG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,KAAK;uBACxG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,uBAAuB,CAAC,UAAU;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBACxM,MAAM;YACR,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAAC,IAAI,CAAC,CAAC,KAAK,YAAY,mBAAmB,CAAC;oBAAE,MAAM,KAAK,CAAC;gBAAC,MAAM,IAAI,EAAE,CAAC;YAAC,CAAC;QAC7F,CAAC;QACD,SAAS,CAAC;YACR,UAAU,EAAE,CAAC;YACb,MAAM,cAAc,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;gBACnE,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBAC9G,IAAI,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ;oBAAE,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;gBACtG,IAAI,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC;oBAAE,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;gBAC7I,MAAM,GAAG,KAAK,CAAC;gBACf,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC5E,MAAM,YAAY,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,MAA2D,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;oBACvI,MAAM,IAAI,KAAK,CAAC,qBAAqB,KAAK,CAAC,MAAM,iCAAiC,CAAC,CAAC;gBACtF,CAAC;gBACD,MAAM,gBAAgB,GAAG,QAAQ,CAAC;gBAClC,IAAI,gBAAgB,EAAE,QAAQ,KAAK,QAAQ;oBAAE,MAAM,cAAc,EAAE,CAAC;gBACpE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAAC,MAAM,IAAI,EAAE,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBAC3D,IAAI,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBACnF,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBACxE,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;oBAAC,UAAU,EAAE,CAAC;oBAAC,KAAK,GAAG,aAAa,CAAC;oBACvE,MAAM,YAAY,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC,KAAK,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;oBACzH,MAAM,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;oBAAC,UAAU,EAAE,CAAC;oBACrE,MAAM,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAAC,UAAU,EAAE,CAAC;gBACjD,CAAC;qBAAM,IAAI,MAAM,gBAAgB,CAAC,aAAa,CAAC,KAAK,MAAM,gBAAgB,CAAC,KAAK,CAAC;oBAAE,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;gBAC1I,IAAI,CAAC,MAAM;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBACvE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC/B,MAAM,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;oBACpF,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,MAAM,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,MAAM,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC;oBAAE,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;gBAC/J,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAAC,MAAM,IAAI,EAAE,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBAC7D,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBACpF,MAAM,cAAc,GAAG,MAAM,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;gBACvF,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;gBAChD,IAAI,CAAC,kBAAkB,EAAE,CAAC;oBACxB,IAAI,gBAAgB,EAAE,QAAQ,KAAK,SAAS,IAAI,CAAC,4BAA4B,CAAC,WAAW,EAAE,gBAAgB,CAAC,EAAE,CAAC;wBAC7G,MAAM,YAAY,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;wBACtF,MAAM,IAAI,EAAE,CAAC;wBAAC,SAAS;oBACzB,CAAC;oBACD,MAAM,YAAY,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;oBACvF,kBAAkB,GAAG,SAAS,CAAC;gBACjC,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;gBACtG,UAAU,EAAE,CAAC;gBACb,MAAM,cAAc,EAAE,CAAC;gBACvB,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAAC,MAAM,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBACtI,IAAI,CAAC,CAAC,cAAc,IAAI,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBAC3I,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAAC,MAAM,IAAI,EAAE,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBAC3F,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,WAAW,CAAC,IAAI,EAAE,4BAA4B,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;oBAChI,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;wBAAC,MAAM,IAAI,EAAE,CAAC;wBAAC,SAAS;oBAAC,CAAC;oBAC7F,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,MAAM,CAAC,eAAe;wBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;oBACvH,WAAW,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;wBACxI,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;gBACvH,CAAC;gBACD,uFAAuF;gBACvF,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;gBAC1C,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,mBAAmB,EAAE,CAAC,EAAE,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;gBAC3H,IAAI,OAA8B,CAAC;gBACnC,IAAI,CAAC;oBACH,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;oBACjE,OAAO,GAAG,MAAM,SAAS,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;gBACjI,CAAC;wBAAS,CAAC;oBAAC,YAAY,CAAC,SAAS,CAAC,CAAC;gBAAC,CAAC;gBACtC,IAAI,CAAC,OAAO;oBAAE,MAAM,IAAI,iBAAiB,CAAC,2BAA2B,CAAC,CAAC;gBACvE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAAC,UAAU,EAAE,CAAC;gBACxC,MAAM,cAAc,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACpI,UAAU,EAAE,CAAC;gBACb,MAAM,cAAc,EAAE,CAAC;gBACvB,IAAI,MAAM,CAAC,MAAM,KAAK,2BAA2B,EAAE,CAAC;oBAAC,MAAM,IAAI,EAAE,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBAC9E,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,KAAK,WAAW,CAAC,OAAO;oBAAE,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC;gBACvH,MAAM,YAAY,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3F,OAAO,EAAE,GAAG,WAAW,EAAE,eAAe,EAAE,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC;YACvJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,mFAAmF;gBACnF,IAAI,CAAC,CAAC,KAAK,YAAY,mBAAmB,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,CAAC;oBAAE,MAAM,KAAK,CAAC;gBACjI,MAAM,IAAI,EAAE,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,UAAU,GAAiC,WAAW,CAAC,KAAK,CAAC;QACjE,IAAI,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9F,MAAM,KAAK,GAAG,KAAK,YAAY,iBAAiB,IAAI,KAAK,CAAC,IAAI,KAAK,4BAA4B,CAAC,CAAC,CAAC,SAAS;gBACzG,CAAC,CAAC,KAAK,YAAY,iBAAiB,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;YAC7G,UAAU,GAAG,KAAK,CAAC;YACnB,MAAM,GAAG,CAAC,aAAa,CAAC,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,qBAAqB,CAAC,CAAC;QACnG,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;QAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAAC,MAAM,GAAG,SAAS,CAAC;IAAC,CAAC;AACpG,CAAC;AAED,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,MAAc,EAAE,QAAuD,EAAE,MAAmB;IAC7I,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,2BAA2B,QAAQ,CAAC,SAAS,YAAY,CAAC;QACvE,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,MAAM,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QACvJ,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,OAAO;QAChG,MAAM,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IACtJ,CAAC;IAAC,MAAM,CAAC,CAAC,6EAA6E,CAAC,CAAC;AAC3F,CAAC;AAED,KAAK,UAAU,QAAQ,CAAI,SAAuB,EAAE,GAAW,EAAE,IAAa;IAC5E,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;QAC/B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAC;AAClC,CAAC"}
@@ -1,42 +0,0 @@
1
- import type { BridgeAuthResponse } from './bridge-auth.js';
2
- export interface RotatingTokenRefresherOptions {
3
- initialRefreshToken: string;
4
- refresh: (refreshToken: string) => Promise<BridgeAuthResponse>;
5
- loadPersistedRefreshToken: () => string | undefined;
6
- persistRefreshToken: (refreshToken: string) => void;
7
- onPersistedTokenRecovery?: () => void;
8
- /** Monotonic clock in ms; injectable for tests. */
9
- now?: () => number;
10
- /** Jitter source in [0, 1); injectable for tests. */
11
- random?: () => number;
12
- }
13
- /**
14
- * Owns the bridge's single-use refresh token.
15
- *
16
- * Token rotation is single-flight inside this process: if centrifuge-js asks
17
- * for a token twice concurrently, both callers share one exchange instead of
18
- * racing the same credential. If another process already rotated the token and
19
- * persisted its replacement, a rejected in-memory token gets one recovery
20
- * attempt from disk — the same state repair that previously required restart.
21
- *
22
- * Transient failures (network errors, 5xx) arm an exponential cooldown during
23
- * which getToken() rejects without calling the API, because centrifuge-js
24
- * retries a failed token fetch every 5-10 s with no growth. The cooldown error
25
- * is deliberately not an UnauthorizedError: centrifuge-js treats that as
26
- * permanent and stops retrying for good.
27
- */
28
- export declare class RotatingTokenRefresher {
29
- private readonly options;
30
- private currentRefreshToken;
31
- private inFlight;
32
- private consecutiveFailures;
33
- private cooldownUntil;
34
- private readonly now;
35
- private readonly random;
36
- constructor(options: RotatingTokenRefresherOptions);
37
- cooldownRemainingMs(): number;
38
- getToken(): Promise<string>;
39
- private refreshAndRotate;
40
- private armCooldown;
41
- private exchange;
42
- }
@@ -1,91 +0,0 @@
1
- import { UnauthorizedError } from 'centrifuge';
2
- const COOLDOWN_BASE_MS = 15_000;
3
- const COOLDOWN_MAX_MS = 600_000;
4
- /**
5
- * Owns the bridge's single-use refresh token.
6
- *
7
- * Token rotation is single-flight inside this process: if centrifuge-js asks
8
- * for a token twice concurrently, both callers share one exchange instead of
9
- * racing the same credential. If another process already rotated the token and
10
- * persisted its replacement, a rejected in-memory token gets one recovery
11
- * attempt from disk — the same state repair that previously required restart.
12
- *
13
- * Transient failures (network errors, 5xx) arm an exponential cooldown during
14
- * which getToken() rejects without calling the API, because centrifuge-js
15
- * retries a failed token fetch every 5-10 s with no growth. The cooldown error
16
- * is deliberately not an UnauthorizedError: centrifuge-js treats that as
17
- * permanent and stops retrying for good.
18
- */
19
- export class RotatingTokenRefresher {
20
- options;
21
- currentRefreshToken;
22
- inFlight = null;
23
- consecutiveFailures = 0;
24
- cooldownUntil = 0;
25
- now;
26
- random;
27
- constructor(options) {
28
- this.options = options;
29
- this.currentRefreshToken = options.initialRefreshToken;
30
- this.now = options.now ?? (() => performance.now());
31
- this.random = options.random ?? Math.random;
32
- }
33
- cooldownRemainingMs() {
34
- return Math.max(0, this.cooldownUntil - this.now());
35
- }
36
- getToken() {
37
- if (this.inFlight)
38
- return this.inFlight;
39
- const remaining = this.cooldownRemainingMs();
40
- if (remaining > 0) {
41
- return Promise.reject(new Error(`Token refresh cooling down for ${Math.ceil(remaining / 1000)}s after ${this.consecutiveFailures} failed attempts`));
42
- }
43
- const operation = this.refreshAndRotate().finally(() => {
44
- if (this.inFlight === operation)
45
- this.inFlight = null;
46
- });
47
- this.inFlight = operation;
48
- return operation;
49
- }
50
- async refreshAndRotate() {
51
- try {
52
- const token = await this.exchange();
53
- this.consecutiveFailures = 0;
54
- this.cooldownUntil = 0;
55
- return token;
56
- }
57
- catch (error) {
58
- if (!(error instanceof UnauthorizedError))
59
- this.armCooldown();
60
- throw error;
61
- }
62
- }
63
- armCooldown() {
64
- this.consecutiveFailures++;
65
- const base = Math.min(COOLDOWN_BASE_MS * 2 ** (this.consecutiveFailures - 1), COOLDOWN_MAX_MS);
66
- this.cooldownUntil = this.now() + base * (0.8 + 0.4 * this.random());
67
- }
68
- async exchange() {
69
- let refreshed;
70
- try {
71
- refreshed = await this.options.refresh(this.currentRefreshToken);
72
- }
73
- catch (error) {
74
- if (!(error instanceof UnauthorizedError))
75
- throw error;
76
- const persistedToken = this.options.loadPersistedRefreshToken();
77
- if (!persistedToken || persistedToken === this.currentRefreshToken) {
78
- throw error;
79
- }
80
- // Another bridge process sharing this data directory completed a
81
- // rotation first. Adopt its persisted token and retry exactly once.
82
- this.currentRefreshToken = persistedToken;
83
- this.options.onPersistedTokenRecovery?.();
84
- refreshed = await this.options.refresh(this.currentRefreshToken);
85
- }
86
- this.currentRefreshToken = refreshed.refreshToken;
87
- this.options.persistRefreshToken(this.currentRefreshToken);
88
- return refreshed.token;
89
- }
90
- }
91
- //# sourceMappingURL=rotating-token-refresher.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rotating-token-refresher.js","sourceRoot":"","sources":["../src/rotating-token-refresher.ts"],"names":[],"mappings":"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"}