@openzeppelin/guardian-client 0.15.2 → 0.16.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 +16 -0
- package/dist/conversion.d.ts.map +1 -1
- package/dist/conversion.js +4 -2
- package/dist/conversion.js.map +1 -1
- package/dist/error-codes.d.ts +30 -0
- package/dist/error-codes.d.ts.map +1 -0
- package/dist/error-codes.js +81 -0
- package/dist/error-codes.js.map +1 -0
- package/dist/http.d.ts +62 -10
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +146 -29
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/server-types.d.ts +13 -0
- package/dist/server-types.d.ts.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/conversion.test.ts +16 -0
- package/src/conversion.ts +4 -2
- package/src/error-codes.test.ts +69 -0
- package/src/error-codes.ts +91 -0
- package/src/http.test.ts +216 -29
- package/src/http.ts +180 -29
- package/src/index.ts +9 -0
- package/src/server-types.ts +15 -1
- package/src/types.ts +23 -1
- package/dist/auth-request.test.d.ts +0 -2
- package/dist/auth-request.test.d.ts.map +0 -1
- package/dist/auth-request.test.js +0 -10
- package/dist/auth-request.test.js.map +0 -1
- package/dist/conversion.test.d.ts +0 -2
- package/dist/conversion.test.d.ts.map +0 -1
- package/dist/conversion.test.js +0 -332
- package/dist/conversion.test.js.map +0 -1
- package/dist/http.test.d.ts +0 -2
- package/dist/http.test.d.ts.map +0 -1
- package/dist/http.test.js +0 -750
- package/dist/http.test.js.map +0 -1
package/src/http.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type GuardianErrorCode,
|
|
3
|
+
normalizeGuardianErrorCode,
|
|
4
|
+
} from './error-codes.js';
|
|
1
5
|
import type {
|
|
6
|
+
AbandonCandidateResponse,
|
|
7
|
+
AbandonStatus,
|
|
2
8
|
ConfigureRequest,
|
|
3
9
|
ConfigureResponse,
|
|
4
10
|
DeltaObject,
|
|
@@ -16,6 +22,8 @@ import type {
|
|
|
16
22
|
} from './types.js';
|
|
17
23
|
import { RequestAuthPayload } from './auth-request.js';
|
|
18
24
|
import type {
|
|
25
|
+
ServerAbandonCandidateRequest,
|
|
26
|
+
ServerAbandonCandidateResponse,
|
|
19
27
|
ServerDeltaObject,
|
|
20
28
|
ServerDeltaProposalResponse,
|
|
21
29
|
ServerLookupResponse,
|
|
@@ -38,21 +46,105 @@ import {
|
|
|
38
46
|
} from './conversion.js';
|
|
39
47
|
|
|
40
48
|
/**
|
|
41
|
-
*
|
|
49
|
+
* Structured machine-readable side-data on a GUARDIAN error
|
|
50
|
+
* (feature `009-human-readable-errors`). `retryable` is always present.
|
|
51
|
+
*/
|
|
52
|
+
export interface GuardianErrorMeta {
|
|
53
|
+
retryable: boolean;
|
|
54
|
+
retryAfterSecs?: number;
|
|
55
|
+
missingPermissions?: string[];
|
|
56
|
+
pausedAt?: string;
|
|
57
|
+
pausedReason?: string | null;
|
|
58
|
+
releasedAt?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface ParsedGuardianError {
|
|
62
|
+
/** Typed code, or `null` when the wire code is outside the known vocabulary. */
|
|
63
|
+
code: GuardianErrorCode | null;
|
|
64
|
+
/** Verbatim wire code, kept even when it does not narrow to the union. */
|
|
65
|
+
rawCode: string;
|
|
66
|
+
message: string;
|
|
67
|
+
meta: GuardianErrorMeta;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse a GUARDIAN error body `{ code, message, meta }` (feature 009),
|
|
72
|
+
* mapping the server's snake_case `meta` fields to camelCase. Returns
|
|
73
|
+
* `undefined` for non-JSON or non-conforming bodies — including a missing
|
|
74
|
+
* `meta` or a non-boolean `meta.retryable`, which the contract requires;
|
|
75
|
+
* treating those as conforming would silently misclassify retryability for
|
|
76
|
+
* bodies from older servers or intermediary proxies.
|
|
77
|
+
*/
|
|
78
|
+
function parseGuardianErrorBody(body: string): ParsedGuardianError | undefined {
|
|
79
|
+
let json: unknown;
|
|
80
|
+
try {
|
|
81
|
+
json = JSON.parse(body);
|
|
82
|
+
} catch {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
if (typeof json !== 'object' || json === null) return undefined;
|
|
86
|
+
const obj = json as Record<string, unknown>;
|
|
87
|
+
if (typeof obj.code !== 'string' || typeof obj.message !== 'string') return undefined;
|
|
88
|
+
|
|
89
|
+
if (typeof obj.meta !== 'object' || obj.meta === null || Array.isArray(obj.meta)) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const rawMeta = obj.meta as Record<string, unknown>;
|
|
93
|
+
if (typeof rawMeta.retryable !== 'boolean') return undefined;
|
|
94
|
+
const code = normalizeGuardianErrorCode(obj.code);
|
|
95
|
+
const meta: GuardianErrorMeta = { retryable: rawMeta.retryable };
|
|
96
|
+
if (
|
|
97
|
+
typeof rawMeta.retry_after_secs === 'number' &&
|
|
98
|
+
Number.isInteger(rawMeta.retry_after_secs) &&
|
|
99
|
+
rawMeta.retry_after_secs >= 0
|
|
100
|
+
) {
|
|
101
|
+
meta.retryAfterSecs = rawMeta.retry_after_secs;
|
|
102
|
+
}
|
|
103
|
+
if (Array.isArray(rawMeta.missing_permissions)) {
|
|
104
|
+
meta.missingPermissions = rawMeta.missing_permissions.filter(
|
|
105
|
+
(x): x is string => typeof x === 'string'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (typeof rawMeta.paused_at === 'string') meta.pausedAt = rawMeta.paused_at;
|
|
109
|
+
if (typeof rawMeta.released_at === 'string') meta.releasedAt = rawMeta.released_at;
|
|
110
|
+
if (typeof rawMeta.paused_reason === 'string' || rawMeta.paused_reason === null) {
|
|
111
|
+
meta.pausedReason = rawMeta.paused_reason as string | null;
|
|
112
|
+
}
|
|
113
|
+
return { code, rawCode: obj.code, message: obj.message, meta };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Error thrown by the GUARDIAN HTTP client. Parses the `{ code, message, meta }`
|
|
118
|
+
* error body (feature 009): branch on {@link code}, display {@link userMessage}.
|
|
42
119
|
*/
|
|
43
120
|
export class GuardianHttpError extends Error {
|
|
44
121
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* `commitment_mismatch`)
|
|
48
|
-
* envelope
|
|
49
|
-
*
|
|
122
|
+
* Typed, compiler-checked Guardian error code (issue #318), normalized to
|
|
123
|
+
* snake_case (e.g. `account_paused`, `account_released`,
|
|
124
|
+
* `commitment_mismatch`). `null` when the body is not a conforming JSON
|
|
125
|
+
* envelope OR the server emitted a code outside this client's known
|
|
126
|
+
* vocabulary — in the latter case {@link rawCode} still carries the
|
|
127
|
+
* verbatim wire string. Branch on this rather than on `body` text or the
|
|
128
|
+
* HTTP status alone; comparing against a non-member literal is a type
|
|
129
|
+
* error, so typos are caught at compile time.
|
|
50
130
|
*/
|
|
51
|
-
public readonly code:
|
|
131
|
+
public readonly code: GuardianErrorCode | null;
|
|
132
|
+
/**
|
|
133
|
+
* Verbatim wire code as the server sent it (e.g.
|
|
134
|
+
* `GUARDIAN_ACCOUNT_PAUSED`), including codes a newer server may emit
|
|
135
|
+
* that this client does not know. `null` only when the body carried no
|
|
136
|
+
* conforming envelope. For logging/telemetry; branch on {@link code}.
|
|
137
|
+
*/
|
|
138
|
+
public readonly rawCode: string | null;
|
|
139
|
+
/** Short, user-safe message — safe to display verbatim in a wallet UI. */
|
|
140
|
+
readonly userMessage?: string;
|
|
141
|
+
/** Structured side-data (`retryable`, `retryAfterSecs`, …). */
|
|
142
|
+
readonly meta?: GuardianErrorMeta;
|
|
52
143
|
/**
|
|
53
144
|
* RFC 3339 UTC timestamp at which the guardian released the account
|
|
54
|
-
* after it switched to a different guardian.
|
|
55
|
-
* `code === '
|
|
145
|
+
* after it switched to a different guardian. Convenience accessor for
|
|
146
|
+
* `meta.releasedAt`; present only when `code === 'account_released'`
|
|
147
|
+
* (wire form `GUARDIAN_ACCOUNT_RELEASED`, HTTP 409); the account is
|
|
56
148
|
* terminal on this server until re-onboarded via `configure`.
|
|
57
149
|
*/
|
|
58
150
|
public readonly releasedAt: string | null;
|
|
@@ -62,26 +154,17 @@ export class GuardianHttpError extends Error {
|
|
|
62
154
|
public readonly statusText: string,
|
|
63
155
|
public readonly body: string
|
|
64
156
|
) {
|
|
65
|
-
|
|
157
|
+
// Only the parsed, user-safe message is folded into Error.message; the
|
|
158
|
+
// raw body (which may carry backend/proxy internals) stays on the `body`
|
|
159
|
+
// field for diagnostics only.
|
|
160
|
+
const parsed = parseGuardianErrorBody(body);
|
|
161
|
+
super(`GUARDIAN HTTP error ${status}: ${statusText}${parsed ? ` - ${parsed.message}` : ''}`);
|
|
66
162
|
this.name = 'GuardianHttpError';
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const record = parsed as Record<string, unknown>;
|
|
73
|
-
if (typeof record['code'] === 'string') {
|
|
74
|
-
code = record['code'];
|
|
75
|
-
}
|
|
76
|
-
if (typeof record['released_at'] === 'string') {
|
|
77
|
-
releasedAt = record['released_at'];
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
} catch {
|
|
81
|
-
// Non-JSON body (e.g. proxy HTML error page): keep raw `body` only.
|
|
82
|
-
}
|
|
83
|
-
this.code = code;
|
|
84
|
-
this.releasedAt = releasedAt;
|
|
163
|
+
this.code = parsed?.code ?? null;
|
|
164
|
+
this.rawCode = parsed?.rawCode ?? null;
|
|
165
|
+
this.userMessage = parsed?.message;
|
|
166
|
+
this.meta = parsed?.meta;
|
|
167
|
+
this.releasedAt = parsed?.meta.releasedAt ?? null;
|
|
85
168
|
}
|
|
86
169
|
}
|
|
87
170
|
|
|
@@ -206,6 +289,65 @@ export class GuardianHttpClient {
|
|
|
206
289
|
};
|
|
207
290
|
}
|
|
208
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Request abandonment of a pending canonicalization candidate whose
|
|
294
|
+
* transaction will never land on-chain (issue #319).
|
|
295
|
+
*
|
|
296
|
+
* Records an abandon *intent* (`202 Accepted`): the delta stays a
|
|
297
|
+
* candidate — the account stays locked — until the guardian's worker
|
|
298
|
+
* confirms over the abandon quarantine that the transaction did not
|
|
299
|
+
* land, then discards the delta as `client_abandoned` and releases the
|
|
300
|
+
* account (typically well under a minute). Poll {@link abandonStatus}
|
|
301
|
+
* for the resolution. Refused with `GUARDIAN_CANDIDATE_LANDED` (409)
|
|
302
|
+
* when the transaction actually landed. Retries are idempotent and
|
|
303
|
+
* preserve the original request timestamp.
|
|
304
|
+
*/
|
|
305
|
+
async abandonCandidate(accountId: string, nonce: number): Promise<AbandonCandidateResponse> {
|
|
306
|
+
const serverRequest: ServerAbandonCandidateRequest = { account_id: accountId, nonce };
|
|
307
|
+
const response = await this.fetchAuthenticated('/delta/candidate/abandon', {
|
|
308
|
+
method: 'POST',
|
|
309
|
+
body: JSON.stringify(serverRequest),
|
|
310
|
+
}, accountId, serverRequest);
|
|
311
|
+
const server = (await response.json()) as ServerAbandonCandidateResponse;
|
|
312
|
+
return {
|
|
313
|
+
accountId: server.account_id,
|
|
314
|
+
nonce: server.nonce,
|
|
315
|
+
state: server.state,
|
|
316
|
+
abandonRequestedAt: server.abandon_requested_at,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Poll the resolution of an abandon request made with
|
|
322
|
+
* {@link abandonCandidate}: `'waiting'` while the quarantine runs,
|
|
323
|
+
* `'landed'` if the transaction landed after all (the delta
|
|
324
|
+
* canonicalized), `'abandoned'` once the delta is discarded as
|
|
325
|
+
* client-abandoned and the account released, `'unexpected'` for any
|
|
326
|
+
* state no abandon flow produces (including a missing delta).
|
|
327
|
+
*/
|
|
328
|
+
async abandonStatus(accountId: string, nonce: number): Promise<AbandonStatus> {
|
|
329
|
+
let delta: DeltaObject;
|
|
330
|
+
try {
|
|
331
|
+
delta = await this.getDelta(accountId, nonce);
|
|
332
|
+
} catch (e) {
|
|
333
|
+
if (e instanceof GuardianHttpError && e.code === 'delta_not_found') {
|
|
334
|
+
return 'unexpected';
|
|
335
|
+
}
|
|
336
|
+
throw e;
|
|
337
|
+
}
|
|
338
|
+
switch (delta.status.status) {
|
|
339
|
+
case 'candidate':
|
|
340
|
+
return 'waiting';
|
|
341
|
+
case 'canonical':
|
|
342
|
+
return 'landed';
|
|
343
|
+
case 'discarded':
|
|
344
|
+
return delta.status.reason === 'client_abandoned' ? 'abandoned' : 'unexpected';
|
|
345
|
+
default:
|
|
346
|
+
return 'unexpected';
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
|
|
209
351
|
async signDeltaProposal(request: SignProposalRequest): Promise<DeltaObject> {
|
|
210
352
|
const serverRequest = toServerSignProposalRequest(request);
|
|
211
353
|
const response = await this.fetchAuthenticated('/delta/proposal', {
|
|
@@ -352,7 +494,16 @@ export class GuardianHttpClient {
|
|
|
352
494
|
},
|
|
353
495
|
});
|
|
354
496
|
} catch (err) {
|
|
355
|
-
|
|
497
|
+
// Replay rejections (stale timestamp) are transient: retry once with a
|
|
498
|
+
// fresh timestamp. The specific "Replay attack" detail is now sanitized
|
|
499
|
+
// off the wire (feature 009), so branch on the stable auth code instead.
|
|
500
|
+
// Retrying a genuine auth failure is harmless — one extra attempt that
|
|
501
|
+
// also fails — and only happens when a retry budget remains.
|
|
502
|
+
if (
|
|
503
|
+
retries > 0 &&
|
|
504
|
+
err instanceof GuardianHttpError &&
|
|
505
|
+
err.code === 'authentication_failed'
|
|
506
|
+
) {
|
|
356
507
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
357
508
|
return this.fetchAuthenticated(path, init, accountId, requestPayload, retries - 1);
|
|
358
509
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
export { GuardianHttpClient, GuardianHttpError } from './http.js';
|
|
2
|
+
export type { GuardianErrorMeta } from './http.js';
|
|
3
|
+
export {
|
|
4
|
+
GUARDIAN_ERROR_CODES,
|
|
5
|
+
isGuardianErrorCode,
|
|
6
|
+
normalizeGuardianErrorCode,
|
|
7
|
+
} from './error-codes.js';
|
|
8
|
+
export type { GuardianErrorCode } from './error-codes.js';
|
|
2
9
|
export { RequestAuthPayload } from './auth-request.js';
|
|
3
10
|
|
|
4
11
|
export type {
|
|
12
|
+
AbandonCandidateResponse,
|
|
13
|
+
AbandonStatus,
|
|
5
14
|
Signer,
|
|
6
15
|
FalconSignature,
|
|
7
16
|
EcdsaSignature,
|
package/src/server-types.ts
CHANGED
|
@@ -21,7 +21,7 @@ export type ServerDeltaStatus =
|
|
|
21
21
|
| { status: 'pending'; timestamp: string; proposer_id: string; cosigner_sigs: ServerCosignerSignature[] }
|
|
22
22
|
| { status: 'candidate'; timestamp: string }
|
|
23
23
|
| { status: 'canonical'; timestamp: string }
|
|
24
|
-
| { status: 'discarded'; timestamp: string };
|
|
24
|
+
| { status: 'discarded'; timestamp: string; reason?: string };
|
|
25
25
|
|
|
26
26
|
export type ServerProposalType =
|
|
27
27
|
| 'add_signer'
|
|
@@ -54,6 +54,8 @@ export interface ServerProposalMetadata {
|
|
|
54
54
|
recipient_id?: string;
|
|
55
55
|
faucet_id?: string;
|
|
56
56
|
amount?: string;
|
|
57
|
+
/** P2ID note visibility, "public" or "private" (issue #322). Absent => public. */
|
|
58
|
+
note_type?: string;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
export interface ServerDeltaObject {
|
|
@@ -137,6 +139,18 @@ export interface ServerProposalsResponse {
|
|
|
137
139
|
proposals: ServerDeltaObject[];
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
export interface ServerAbandonCandidateRequest {
|
|
143
|
+
account_id: string;
|
|
144
|
+
nonce: number;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface ServerAbandonCandidateResponse {
|
|
148
|
+
account_id: string;
|
|
149
|
+
nonce: number;
|
|
150
|
+
state: 'pending' | 'abandoned';
|
|
151
|
+
abandon_requested_at?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
140
154
|
export interface ServerSignProposalRequest {
|
|
141
155
|
account_id: string;
|
|
142
156
|
commitment: string;
|
package/src/types.ts
CHANGED
|
@@ -65,7 +65,7 @@ export type DeltaStatus =
|
|
|
65
65
|
| { status: 'pending'; timestamp: string; proposerId: string; cosignerSigs: CosignerSignature[] }
|
|
66
66
|
| { status: 'candidate'; timestamp: string }
|
|
67
67
|
| { status: 'canonical'; timestamp: string }
|
|
68
|
-
| { status: 'discarded'; timestamp: string };
|
|
68
|
+
| { status: 'discarded'; timestamp: string; reason?: string };
|
|
69
69
|
|
|
70
70
|
export type ProposalType =
|
|
71
71
|
| 'add_signer'
|
|
@@ -98,6 +98,8 @@ export interface ProposalMetadata {
|
|
|
98
98
|
recipientId?: string;
|
|
99
99
|
faucetId?: string;
|
|
100
100
|
amount?: string;
|
|
101
|
+
/** P2ID note visibility, "public" or "private" (issue #322). Absent => public. */
|
|
102
|
+
noteType?: string;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
export interface DeltaObject {
|
|
@@ -187,6 +189,26 @@ export interface SignProposalRequest {
|
|
|
187
189
|
signature: ProposalSignature;
|
|
188
190
|
}
|
|
189
191
|
|
|
192
|
+
export interface AbandonCandidateResponse {
|
|
193
|
+
accountId: string;
|
|
194
|
+
nonce: number;
|
|
195
|
+
/**
|
|
196
|
+
* `'pending'` while the guardian's worker still has to resolve the
|
|
197
|
+
* abandon intent (the account stays locked until then); `'abandoned'`
|
|
198
|
+
* once the delta is discarded as client-abandoned and the account
|
|
199
|
+
* released.
|
|
200
|
+
*/
|
|
201
|
+
state: 'pending' | 'abandoned';
|
|
202
|
+
/**
|
|
203
|
+
* RFC 3339 UTC timestamp of the recorded abandon request. Retries
|
|
204
|
+
* return the original timestamp; absent once resolved.
|
|
205
|
+
*/
|
|
206
|
+
abandonRequestedAt?: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Resolution of an abandon request, as observed via the delta feed. */
|
|
210
|
+
export type AbandonStatus = 'waiting' | 'landed' | 'abandoned' | 'unexpected';
|
|
211
|
+
|
|
190
212
|
export interface PushDeltaResponse {
|
|
191
213
|
accountId: string;
|
|
192
214
|
nonce: number;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-request.test.d.ts","sourceRoot":"","sources":["../src/auth-request.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { RequestAuthPayload } from './auth-request.js';
|
|
3
|
-
describe('RequestAuthPayload', () => {
|
|
4
|
-
it('canonicalizes object keys recursively', () => {
|
|
5
|
-
const left = RequestAuthPayload.fromRequest({ b: 2, a: { y: 2, x: 1 } });
|
|
6
|
-
const right = RequestAuthPayload.fromRequest({ a: { x: 1, y: 2 }, b: 2 });
|
|
7
|
-
expect(left.toCanonicalJson()).toEqual(right.toCanonicalJson());
|
|
8
|
-
});
|
|
9
|
-
});
|
|
10
|
-
//# sourceMappingURL=auth-request.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-request.test.js","sourceRoot":"","sources":["../src/auth-request.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACzE,MAAM,KAAK,GAAG,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAE1E,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conversion.test.d.ts","sourceRoot":"","sources":["../src/conversion.test.ts"],"names":[],"mappings":""}
|