@dejadex/web 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +35 -0
- package/README.md +222 -2
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/geometry/homography.d.ts +75 -0
- package/dist/geometry/homography.d.ts.map +1 -0
- package/dist/geometry/homography.js +146 -0
- package/dist/geometry/homography.js.map +1 -0
- package/dist/identify/assets.d.ts +39 -0
- package/dist/identify/assets.d.ts.map +1 -0
- package/dist/identify/assets.js +167 -0
- package/dist/identify/assets.js.map +1 -0
- package/dist/identify/decode.d.ts +6 -0
- package/dist/identify/decode.d.ts.map +1 -0
- package/dist/identify/decode.js +53 -0
- package/dist/identify/decode.js.map +1 -0
- package/dist/identify/engine.d.ts +32 -0
- package/dist/identify/engine.d.ts.map +1 -0
- package/dist/identify/engine.js +127 -0
- package/dist/identify/engine.js.map +1 -0
- package/dist/identify/identifier.d.ts +91 -0
- package/dist/identify/identifier.d.ts.map +1 -0
- package/dist/identify/identifier.js +332 -0
- package/dist/identify/identifier.js.map +1 -0
- package/dist/identify/manifest.d.ts +60 -0
- package/dist/identify/manifest.d.ts.map +1 -0
- package/dist/identify/manifest.js +211 -0
- package/dist/identify/manifest.js.map +1 -0
- package/dist/identify/preprocess.d.ts +52 -0
- package/dist/identify/preprocess.d.ts.map +1 -0
- package/dist/identify/preprocess.js +195 -0
- package/dist/identify/preprocess.js.map +1 -0
- package/dist/identify/router.d.ts +60 -0
- package/dist/identify/router.d.ts.map +1 -0
- package/dist/identify/router.js +83 -0
- package/dist/identify/router.js.map +1 -0
- package/dist/identify/runtime.d.ts +69 -0
- package/dist/identify/runtime.d.ts.map +1 -0
- package/dist/identify/runtime.js +140 -0
- package/dist/identify/runtime.js.map +1 -0
- package/dist/identify/search.d.ts +35 -0
- package/dist/identify/search.d.ts.map +1 -0
- package/dist/identify/search.js +99 -0
- package/dist/identify/search.js.map +1 -0
- package/dist/identify/server.d.ts +60 -0
- package/dist/identify/server.d.ts.map +1 -0
- package/dist/identify/server.js +149 -0
- package/dist/identify/server.js.map +1 -0
- package/dist/identify/shard.d.ts +31 -0
- package/dist/identify/shard.d.ts.map +1 -0
- package/dist/identify/shard.js +146 -0
- package/dist/identify/shard.js.map +1 -0
- package/dist/identify/types.d.ts +107 -0
- package/dist/identify/types.d.ts.map +1 -0
- package/dist/identify/types.js +7 -0
- package/dist/identify/types.js.map +1 -0
- package/dist/identify/worker.d.ts +2 -0
- package/dist/identify/worker.d.ts.map +1 -0
- package/dist/identify/worker.js +13 -0
- package/dist/identify/worker.js.map +1 -0
- package/dist/identify/workerProtocol.d.ts +54 -0
- package/dist/identify/workerProtocol.d.ts.map +1 -0
- package/dist/identify/workerProtocol.js +113 -0
- package/dist/identify/workerProtocol.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/lease/verify.d.ts +69 -0
- package/dist/lease/verify.d.ts.map +1 -0
- package/dist/lease/verify.js +235 -0
- package/dist/lease/verify.js.map +1 -0
- package/dist/scanner.d.ts +10 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +58 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +180 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/usage/meter.d.ts +83 -0
- package/dist/usage/meter.d.ts.map +1 -0
- package/dist/usage/meter.js +111 -0
- package/dist/usage/meter.js.map +1 -0
- package/dist/usage/store.d.ts +40 -0
- package/dist/usage/store.d.ts.map +1 -0
- package/dist/usage/store.js +248 -0
- package/dist/usage/store.js.map +1 -0
- package/package.json +64 -4
- package/src/errors.ts +24 -0
- package/src/geometry/homography.ts +178 -0
- package/src/identify/assets.ts +189 -0
- package/src/identify/decode.ts +54 -0
- package/src/identify/engine.ts +167 -0
- package/src/identify/identifier.ts +431 -0
- package/src/identify/manifest.ts +240 -0
- package/src/identify/preprocess.ts +227 -0
- package/src/identify/router.ts +115 -0
- package/src/identify/runtime.ts +197 -0
- package/src/identify/search.ts +115 -0
- package/src/identify/server.ts +167 -0
- package/src/identify/shard.ts +154 -0
- package/src/identify/types.ts +122 -0
- package/src/identify/worker.ts +13 -0
- package/src/identify/workerProtocol.ts +136 -0
- package/src/index.ts +71 -0
- package/src/lease/verify.ts +252 -0
- package/src/scanner.ts +56 -0
- package/src/types.ts +196 -0
- package/src/usage/meter.ts +165 -0
- package/src/usage/store.ts +282 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lease verification on the device: an Ed25519 compact JWS (RFC 7515,
|
|
3
|
+
* `alg: EdDSA` per RFC 8037), `typ: dejadex-lease+jwt`, checked against the
|
|
4
|
+
* public keys the host embeds. Mirrors `service/src/leases/jws.ts` check for
|
|
5
|
+
* check, in the same order:
|
|
6
|
+
*
|
|
7
|
+
* structure → header → signature (over the raw bytes) → claims shape →
|
|
8
|
+
* time → origin → domains
|
|
9
|
+
*
|
|
10
|
+
* Nothing in an unsigned payload is looked at before the signature holds, and
|
|
11
|
+
* the algorithm comes from this code, never from the token.
|
|
12
|
+
*
|
|
13
|
+
* Signature: WebCrypto Ed25519 where the browser has it (Chrome 137+, Safari
|
|
14
|
+
* 17+, Firefox 129+, Node 22); elsewhere `@noble/ed25519` (MIT, audited,
|
|
15
|
+
* zero dependencies), loaded only then.
|
|
16
|
+
*
|
|
17
|
+
* The browser is untrusted: this check keeps an honest host from running on a
|
|
18
|
+
* lease it should not, and bounds what a dishonest one gets. The service
|
|
19
|
+
* checks every lease again.
|
|
20
|
+
*/
|
|
21
|
+
import { ScannerError } from '../errors.js';
|
|
22
|
+
export const LEASE_ALG = 'EdDSA';
|
|
23
|
+
export const LEASE_TYP = 'dejadex-lease+jwt';
|
|
24
|
+
export const MAX_LEASE_TTL_SECONDS = 86_400;
|
|
25
|
+
export const DEFAULT_CLOCK_SKEW_SECONDS = 60;
|
|
26
|
+
const SCANNER_CODE = {
|
|
27
|
+
LEASE_MALFORMED: 'LEASE_INVALID',
|
|
28
|
+
LEASE_BAD_SIGNATURE: 'LEASE_INVALID',
|
|
29
|
+
LEASE_NOT_YET_VALID: 'LEASE_INVALID',
|
|
30
|
+
LEASE_EXPIRED: 'LEASE_EXPIRED',
|
|
31
|
+
LEASE_ORIGIN_NOT_ALLOWED: 'ORIGIN_NOT_ALLOWED',
|
|
32
|
+
LEASE_DOMAIN_NOT_ALLOWED: 'DOMAIN_NOT_ALLOWED',
|
|
33
|
+
};
|
|
34
|
+
export class LeaseVerificationError extends ScannerError {
|
|
35
|
+
reason;
|
|
36
|
+
constructor(reason, message) {
|
|
37
|
+
super(SCANNER_CODE[reason], `${reason}: ${message}`);
|
|
38
|
+
this.reason = reason;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const fail = (reason, message) => {
|
|
42
|
+
throw new LeaseVerificationError(reason, message);
|
|
43
|
+
};
|
|
44
|
+
const malformed = (why) => fail('LEASE_MALFORMED', `malformed lease: ${why}`);
|
|
45
|
+
const B64URL = /^[A-Za-z0-9_-]*$/;
|
|
46
|
+
/** Strict unpadded base64url: one decoding and one encoding per string, or null. */
|
|
47
|
+
export function base64urlDecodeStrict(s) {
|
|
48
|
+
if (!B64URL.test(s) || s.length % 4 === 1)
|
|
49
|
+
return null;
|
|
50
|
+
const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4);
|
|
51
|
+
let bin;
|
|
52
|
+
try {
|
|
53
|
+
bin = atob(b64);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const out = new Uint8Array(bin.length);
|
|
59
|
+
for (let i = 0; i < bin.length; i++)
|
|
60
|
+
out[i] = bin.charCodeAt(i);
|
|
61
|
+
// Non-zero trailing bits decode like the canonical form; re-encoding exposes them.
|
|
62
|
+
if (base64urlEncode(out) !== s)
|
|
63
|
+
return null;
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
export function base64urlEncode(bytes) {
|
|
67
|
+
let bin = '';
|
|
68
|
+
for (const b of bytes)
|
|
69
|
+
bin += String.fromCharCode(b);
|
|
70
|
+
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
71
|
+
}
|
|
72
|
+
function jsonObject(bytes) {
|
|
73
|
+
let value;
|
|
74
|
+
try {
|
|
75
|
+
value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null;
|
|
81
|
+
}
|
|
82
|
+
function decodeHeader(segment) {
|
|
83
|
+
const bytes = base64urlDecodeStrict(segment);
|
|
84
|
+
if (bytes === null || bytes.length === 0)
|
|
85
|
+
return malformed('header is not base64url');
|
|
86
|
+
const header = jsonObject(bytes);
|
|
87
|
+
if (header === null)
|
|
88
|
+
return malformed('header is not a JSON object');
|
|
89
|
+
if (header.alg !== LEASE_ALG)
|
|
90
|
+
return malformed(`unsupported alg ${JSON.stringify(header.alg)}`);
|
|
91
|
+
if (header.typ !== LEASE_TYP)
|
|
92
|
+
return malformed(`unexpected typ ${JSON.stringify(header.typ)}`);
|
|
93
|
+
if ('crit' in header)
|
|
94
|
+
return malformed('crit header not supported');
|
|
95
|
+
for (const key of Object.keys(header)) {
|
|
96
|
+
if (key !== 'alg' && key !== 'typ' && key !== 'kid')
|
|
97
|
+
return malformed(`unexpected header parameter ${key}`);
|
|
98
|
+
}
|
|
99
|
+
if (header.kid !== undefined && (typeof header.kid !== 'string' || header.kid.length === 0))
|
|
100
|
+
return malformed('kid must be a non-empty string');
|
|
101
|
+
return header.kid === undefined ? {} : { kid: header.kid };
|
|
102
|
+
}
|
|
103
|
+
const nonEmpty = (v) => typeof v === 'string' && v.length > 0;
|
|
104
|
+
const stringArray = (v) => Array.isArray(v) && v.length > 0 && v.every(nonEmpty);
|
|
105
|
+
const unixSeconds = (v) => Number.isSafeInteger(v) && v >= 0;
|
|
106
|
+
function toClaims(raw) {
|
|
107
|
+
const { iss, sub, uid, jti, origins, domains, modelVersion, indexVersion, allowance, iat, exp } = raw;
|
|
108
|
+
if (!nonEmpty(iss))
|
|
109
|
+
return malformed('iss');
|
|
110
|
+
if (!nonEmpty(sub))
|
|
111
|
+
return malformed('sub');
|
|
112
|
+
if (!nonEmpty(uid))
|
|
113
|
+
return malformed('uid');
|
|
114
|
+
if (!nonEmpty(jti))
|
|
115
|
+
return malformed('jti');
|
|
116
|
+
if (!stringArray(origins))
|
|
117
|
+
return malformed('origins');
|
|
118
|
+
if (!stringArray(domains))
|
|
119
|
+
return malformed('domains');
|
|
120
|
+
if (!nonEmpty(modelVersion))
|
|
121
|
+
return malformed('modelVersion');
|
|
122
|
+
if (!nonEmpty(indexVersion))
|
|
123
|
+
return malformed('indexVersion');
|
|
124
|
+
if (!Number.isSafeInteger(allowance) || allowance <= 0)
|
|
125
|
+
return malformed('allowance');
|
|
126
|
+
if (!unixSeconds(iat))
|
|
127
|
+
return malformed('iat');
|
|
128
|
+
if (!unixSeconds(exp))
|
|
129
|
+
return malformed('exp');
|
|
130
|
+
if (exp <= iat)
|
|
131
|
+
return malformed('exp must be after iat');
|
|
132
|
+
if (exp - iat > MAX_LEASE_TTL_SECONDS)
|
|
133
|
+
return malformed('lifetime exceeds 24 h');
|
|
134
|
+
return { iss, sub, uid, jti, origins: [...origins], domains: [...domains], modelVersion, indexVersion, allowance: allowance, iat, exp };
|
|
135
|
+
}
|
|
136
|
+
/** The encoding of the Ed25519 base point: a key every implementation must accept. */
|
|
137
|
+
const BASE_POINT = new Uint8Array([0x58, ...new Array(31).fill(0x66)]);
|
|
138
|
+
let webCryptoEd25519 = null;
|
|
139
|
+
/** Whether this runtime's WebCrypto does Ed25519, probed once with a known-good key. */
|
|
140
|
+
export function hasWebCryptoEd25519() {
|
|
141
|
+
webCryptoEd25519 ??= (async () => {
|
|
142
|
+
const subtle = globalThis.crypto?.subtle;
|
|
143
|
+
if (!subtle)
|
|
144
|
+
return false;
|
|
145
|
+
try {
|
|
146
|
+
await subtle.importKey('raw', BASE_POINT, { name: 'Ed25519' }, false, ['verify']);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
})();
|
|
153
|
+
return webCryptoEd25519;
|
|
154
|
+
}
|
|
155
|
+
/** WebCrypto Ed25519 when the runtime has it, else `@noble/ed25519`. Any error is "does not verify". */
|
|
156
|
+
export const defaultEd25519Verify = async (publicKey, message, signature) => {
|
|
157
|
+
try {
|
|
158
|
+
if (await hasWebCryptoEd25519()) {
|
|
159
|
+
const subtle = globalThis.crypto.subtle;
|
|
160
|
+
const key = await subtle.importKey('raw', publicKey, { name: 'Ed25519' }, false, ['verify']);
|
|
161
|
+
return await subtle.verify({ name: 'Ed25519' }, key, signature, message);
|
|
162
|
+
}
|
|
163
|
+
return await nobleEd25519Verify(publicKey, message, signature);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/** The fallback on its own, for runtimes without WebCrypto Ed25519 (and its tests). */
|
|
170
|
+
export const nobleEd25519Verify = async (publicKey, message, signature) => {
|
|
171
|
+
const noble = await import('@noble/ed25519');
|
|
172
|
+
try {
|
|
173
|
+
return await noble.verifyAsync(signature, message, publicKey);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
/** Verifies `token` and returns the lease, or throws a `LeaseVerificationError`. */
|
|
180
|
+
export async function verifyLease(token, options) {
|
|
181
|
+
if (typeof token !== 'string')
|
|
182
|
+
return malformed('token is not a string');
|
|
183
|
+
const parts = token.split('.');
|
|
184
|
+
if (parts.length !== 3)
|
|
185
|
+
return malformed('expected three dot-separated segments');
|
|
186
|
+
const [headerB64 = '', payloadB64 = '', signatureB64 = ''] = parts;
|
|
187
|
+
const header = decodeHeader(headerB64);
|
|
188
|
+
const payload = base64urlDecodeStrict(payloadB64);
|
|
189
|
+
if (payload === null || payload.length === 0)
|
|
190
|
+
return malformed('payload is not base64url');
|
|
191
|
+
const signature = base64urlDecodeStrict(signatureB64);
|
|
192
|
+
if (signature === null)
|
|
193
|
+
return malformed('signature is not base64url');
|
|
194
|
+
if (signature.length !== 64)
|
|
195
|
+
return fail('LEASE_BAD_SIGNATURE', 'signature has the wrong length');
|
|
196
|
+
const keys = options.publicKeys.filter((k) => header.kid === undefined || k.kid === undefined || k.kid === header.kid);
|
|
197
|
+
if (keys.length === 0)
|
|
198
|
+
return fail('LEASE_BAD_SIGNATURE', header.kid ? `no public key with kid ${header.kid}` : 'no public key');
|
|
199
|
+
const message = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
|
|
200
|
+
const verify = options.verify ?? defaultEd25519Verify;
|
|
201
|
+
let valid = false;
|
|
202
|
+
for (const key of keys) {
|
|
203
|
+
if ((key.kty !== undefined && key.kty !== 'OKP') || (key.crv !== undefined && key.crv !== 'Ed25519'))
|
|
204
|
+
continue;
|
|
205
|
+
if ((key.alg !== undefined && key.alg !== LEASE_ALG) || (key.use !== undefined && key.use !== 'sig'))
|
|
206
|
+
continue;
|
|
207
|
+
const raw = base64urlDecodeStrict(key.x);
|
|
208
|
+
if (raw === null || raw.length !== 32)
|
|
209
|
+
continue;
|
|
210
|
+
if (await verify(raw, message, signature)) {
|
|
211
|
+
valid = true;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (!valid)
|
|
216
|
+
return fail('LEASE_BAD_SIGNATURE', 'signature does not verify');
|
|
217
|
+
const raw = jsonObject(payload);
|
|
218
|
+
if (raw === null)
|
|
219
|
+
return malformed('payload is not a JSON object');
|
|
220
|
+
const claims = toClaims(raw);
|
|
221
|
+
const now = options.now ?? Math.floor(Date.now() / 1000);
|
|
222
|
+
const skew = options.clockSkewSeconds ?? DEFAULT_CLOCK_SKEW_SECONDS;
|
|
223
|
+
if (now >= claims.exp)
|
|
224
|
+
return fail('LEASE_EXPIRED', `lease expired at ${claims.exp}`);
|
|
225
|
+
if (claims.iat > now + skew)
|
|
226
|
+
return fail('LEASE_NOT_YET_VALID', `lease issued at ${claims.iat}, in the future`);
|
|
227
|
+
if (options.origin !== null && !claims.origins.includes(options.origin)) {
|
|
228
|
+
return fail('LEASE_ORIGIN_NOT_ALLOWED', `origin ${options.origin} is not allowed by this lease`);
|
|
229
|
+
}
|
|
230
|
+
const missing = options.domains.filter((d) => !claims.domains.includes(d));
|
|
231
|
+
if (missing.length > 0)
|
|
232
|
+
return fail('LEASE_DOMAIN_NOT_ALLOWED', `domains not granted: ${missing.join(', ')}`);
|
|
233
|
+
return { token, claims };
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/lease/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Scanner, ScannerOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a scanner. Options are checked now, so a misconfigured host fails
|
|
4
|
+
* at the call site rather than at the first frame.
|
|
5
|
+
*
|
|
6
|
+
* STUB: the pipeline (openspec/changes/web-sdk-pipeline) is not built yet.
|
|
7
|
+
* `start` and `identify` reject with `NOT_IMPLEMENTED`; the shape is final.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createScanner(options: ScannerOptions): Scanner;
|
|
10
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,OAAO,EAAE,cAAc,EAA6B,MAAM,YAAY,CAAA;AAIhG;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CA4B9D"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ScannerError } from './errors.js';
|
|
2
|
+
const DOMAIN_CODE = /^[a-z][a-z0-9]*_[a-z]{2}$/;
|
|
3
|
+
/**
|
|
4
|
+
* Creates a scanner. Options are checked now, so a misconfigured host fails
|
|
5
|
+
* at the call site rather than at the first frame.
|
|
6
|
+
*
|
|
7
|
+
* STUB: the pipeline (openspec/changes/web-sdk-pipeline) is not built yet.
|
|
8
|
+
* `start` and `identify` reject with `NOT_IMPLEMENTED`; the shape is final.
|
|
9
|
+
*/
|
|
10
|
+
export function createScanner(options) {
|
|
11
|
+
validate(options);
|
|
12
|
+
let state = 'idle';
|
|
13
|
+
const notYet = (what) => Promise.reject(new ScannerError('NOT_IMPLEMENTED', `${what} is not implemented yet`));
|
|
14
|
+
return {
|
|
15
|
+
get state() {
|
|
16
|
+
return state;
|
|
17
|
+
},
|
|
18
|
+
get tier() {
|
|
19
|
+
return null;
|
|
20
|
+
},
|
|
21
|
+
start() {
|
|
22
|
+
if (state === 'destroyed')
|
|
23
|
+
return Promise.reject(new ScannerError('INVALID_OPTIONS', 'scanner was destroyed'));
|
|
24
|
+
return notYet('live scanning');
|
|
25
|
+
},
|
|
26
|
+
stop() {
|
|
27
|
+
if (state === 'running')
|
|
28
|
+
state = 'stopped';
|
|
29
|
+
},
|
|
30
|
+
identify() {
|
|
31
|
+
return notYet('snapshot identification');
|
|
32
|
+
},
|
|
33
|
+
async destroy() {
|
|
34
|
+
state = 'destroyed';
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function validate(options) {
|
|
39
|
+
const fail = (message) => {
|
|
40
|
+
throw new ScannerError('INVALID_OPTIONS', message);
|
|
41
|
+
};
|
|
42
|
+
if (typeof options?.getLease !== 'function')
|
|
43
|
+
fail('getLease must be a function');
|
|
44
|
+
if (typeof options.reportUsage !== 'function')
|
|
45
|
+
fail('reportUsage must be a function');
|
|
46
|
+
if (typeof options.onTracks !== 'function')
|
|
47
|
+
fail('onTracks must be a function');
|
|
48
|
+
if (!Array.isArray(options.domains) || options.domains.length === 0)
|
|
49
|
+
fail('domains must list at least one domain');
|
|
50
|
+
for (const d of options.domains) {
|
|
51
|
+
if (typeof d !== 'string' || !DOMAIN_CODE.test(d))
|
|
52
|
+
fail(`not a domain code: ${String(d)}`);
|
|
53
|
+
}
|
|
54
|
+
const tiers = ['auto', 'live_full', 'live_reduced', 'snapshot'];
|
|
55
|
+
if (options.tier !== undefined && !tiers.includes(options.tier))
|
|
56
|
+
fail(`unknown tier: ${String(options.tier)}`);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG1C,MAAM,WAAW,GAAG,2BAA2B,CAAA;AAE/C;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,OAAuB;IACnD,QAAQ,CAAC,OAAO,CAAC,CAAA;IAEjB,IAAI,KAAK,GAAiB,MAAM,CAAA;IAChC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAC9B,OAAO,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,iBAAiB,EAAE,GAAG,IAAI,yBAAyB,CAAC,CAAC,CAAA;IAEvF,OAAO;QACL,IAAI,KAAK;YACP,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,IAAI;YACN,OAAO,IAAI,CAAA;QACb,CAAC;QACD,KAAK;YACH,IAAI,KAAK,KAAK,WAAW;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC,CAAA;YAC9G,OAAO,MAAM,CAAC,eAAe,CAAC,CAAA;QAChC,CAAC;QACD,IAAI;YACF,IAAI,KAAK,KAAK,SAAS;gBAAE,KAAK,GAAG,SAAS,CAAA;QAC5C,CAAC;QACD,QAAQ;YACN,OAAO,MAAM,CAAC,yBAAyB,CAAC,CAAA;QAC1C,CAAC;QACD,KAAK,CAAC,OAAO;YACX,KAAK,GAAG,WAAW,CAAA;QACrB,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,OAAuB;IACvC,MAAM,IAAI,GAAG,CAAC,OAAe,EAAS,EAAE;QACtC,MAAM,IAAI,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAA;IACpD,CAAC,CAAA;IACD,IAAI,OAAO,OAAO,EAAE,QAAQ,KAAK,UAAU;QAAE,IAAI,CAAC,6BAA6B,CAAC,CAAA;IAChF,IAAI,OAAO,OAAO,CAAC,WAAW,KAAK,UAAU;QAAE,IAAI,CAAC,gCAAgC,CAAC,CAAA;IACrF,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,UAAU;QAAE,IAAI,CAAC,6BAA6B,CAAC,CAAA;IAC/E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,IAAI,CAAC,uCAAuC,CAAC,CAAA;IAClH,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAChC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,sBAAsB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC5F,CAAC;IACD,MAAM,KAAK,GAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,CAAC,CAAA;IACnF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,IAAI,CAAC,iBAAiB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAChH,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK's public types. These are the contract the first client tenant (and
|
|
3
|
+
* every later one) builds against, so they are settled before the pipeline
|
|
4
|
+
* behind them exists: see openspec/changes/web-sdk-pipeline and
|
|
5
|
+
* openspec/changes/lease-licensing for the behaviour each field promises.
|
|
6
|
+
*/
|
|
7
|
+
import type { Homography, Quad } from './geometry/homography.js';
|
|
8
|
+
/**
|
|
9
|
+
* A catalogue domain: one franchise in one language, and one index shard.
|
|
10
|
+
* Codes are neutral by rule (`tcg01_en`, `tcg01_ja`, `tcg02_en`, …); a
|
|
11
|
+
* franchise name never appears in one.
|
|
12
|
+
*/
|
|
13
|
+
export type DomainCode = string;
|
|
14
|
+
/** A print's platform-owned id. Opaque to clients; map through `externalIds`. */
|
|
15
|
+
export type PrintId = string;
|
|
16
|
+
/**
|
|
17
|
+
* Ids of the same print in third-party catalogues, keyed by source
|
|
18
|
+
* (e.g. `{ tcgdex: "<set>-<number>" }`). A client maps its own ids through
|
|
19
|
+
* the source it already uses.
|
|
20
|
+
*/
|
|
21
|
+
export type ExternalIds = Readonly<Record<string, string>>;
|
|
22
|
+
/** A finish of a print, e.g. `normal`, `holo`, `reverse_holo`, `first_edition`. Domain-defined. */
|
|
23
|
+
export type VariantCode = string;
|
|
24
|
+
/** What the device can sustain, chosen at start-up (`auto`) or forced by the host. */
|
|
25
|
+
export type DeviceTier = 'live_full' | 'live_reduced' | 'snapshot';
|
|
26
|
+
/** The claims of a lease, as the service signs them. Times are Unix seconds. */
|
|
27
|
+
export interface LeaseClaims {
|
|
28
|
+
/** The issuing service. */
|
|
29
|
+
readonly iss: string;
|
|
30
|
+
/** The tenant the lease bills to. */
|
|
31
|
+
readonly sub: string;
|
|
32
|
+
/** The tenant's end user, hashed by the service; never the client's raw id. */
|
|
33
|
+
readonly uid: string;
|
|
34
|
+
/** This lease's id; usage reports and renewals name it. */
|
|
35
|
+
readonly jti: string;
|
|
36
|
+
/** Page origins the lease runs on, exact `scheme://host[:port]` matches. */
|
|
37
|
+
readonly origins: readonly string[];
|
|
38
|
+
/** Index shards the lease unlocks. */
|
|
39
|
+
readonly domains: readonly DomainCode[];
|
|
40
|
+
/** The embedding model version; locked to `indexVersion`. */
|
|
41
|
+
readonly modelVersion: string;
|
|
42
|
+
readonly indexVersion: string;
|
|
43
|
+
/** Identifications this lease permits before it must be renewed. */
|
|
44
|
+
readonly allowance: number;
|
|
45
|
+
readonly iat: number;
|
|
46
|
+
readonly exp: number;
|
|
47
|
+
}
|
|
48
|
+
/** A verified lease: the compact JWS the client's backend handed over, and its claims. */
|
|
49
|
+
export interface Lease {
|
|
50
|
+
readonly token: string;
|
|
51
|
+
readonly claims: LeaseClaims;
|
|
52
|
+
}
|
|
53
|
+
/** What the SDK asks the host for when it needs a lease. */
|
|
54
|
+
export interface LeaseRequest {
|
|
55
|
+
/** The lease being renewed, or null for the first one on this device. */
|
|
56
|
+
readonly previousLeaseId: string | null;
|
|
57
|
+
/**
|
|
58
|
+
* The last usage sequence number already accepted by `reportUsage` for the
|
|
59
|
+
* previous lease. The service refuses a renewal whose previous lease has
|
|
60
|
+
* unreported usage.
|
|
61
|
+
*/
|
|
62
|
+
readonly usageReportedThrough: number | null;
|
|
63
|
+
}
|
|
64
|
+
/** What the host's backend returns from the service's `POST /v1/leases`, passed through as is. */
|
|
65
|
+
export interface LeaseGrant {
|
|
66
|
+
/** The signed lease, compact JWS. */
|
|
67
|
+
readonly token: string;
|
|
68
|
+
/** Signed URL of the manifest that lists model and index files for this lease. */
|
|
69
|
+
readonly manifestUrl: string;
|
|
70
|
+
}
|
|
71
|
+
/** One of the top-K guesses for a track. */
|
|
72
|
+
export interface Candidate {
|
|
73
|
+
readonly printId: PrintId;
|
|
74
|
+
readonly externalIds: ExternalIds;
|
|
75
|
+
/** Cosine similarity to the reference embedding, in [-1, 1]. */
|
|
76
|
+
readonly score: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* One physical card the SDK is following, as of the latest processed frame.
|
|
80
|
+
*
|
|
81
|
+
* `printId` is null until the track is confirmed, and stays null when the
|
|
82
|
+
* crop matched nothing above the threshold (`status: 'unknown'`): the
|
|
83
|
+
* catalogue is closed, so a miss is reported as a miss with its best guesses,
|
|
84
|
+
* never as a near neighbour.
|
|
85
|
+
*/
|
|
86
|
+
export interface TrackResult {
|
|
87
|
+
/** Stable for as long as the card stays tracked. */
|
|
88
|
+
readonly trackId: number;
|
|
89
|
+
readonly status: 'pending' | 'confirmed' | 'unknown';
|
|
90
|
+
/** Corners in the source frame's pixel space (y down): TL, TR, BR, BL of the card. The host maps them to its own layout (e.g. object-fit: cover) before drawing. */
|
|
91
|
+
readonly quad: Quad;
|
|
92
|
+
/** Unit card rectangle → `quad`; `toCssMatrix3d` turns it into a transform. */
|
|
93
|
+
readonly homography: Homography;
|
|
94
|
+
readonly domain: DomainCode | null;
|
|
95
|
+
readonly printId: PrintId | null;
|
|
96
|
+
readonly externalIds: ExternalIds;
|
|
97
|
+
/** Null when the variant is unresolved: the host shows a picker, never a guess. */
|
|
98
|
+
readonly variant: VariantCode | null;
|
|
99
|
+
/** In [0, 1]; the SDK's own calibrated belief in `printId`. */
|
|
100
|
+
readonly confidence: number;
|
|
101
|
+
/** The top-K candidates (K = 5), best first, including the chosen one. */
|
|
102
|
+
readonly alternates: readonly Candidate[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* What one scan session counted under one lease since its previous report.
|
|
106
|
+
*
|
|
107
|
+
* The billing unit is a distinct card per scan session: a card is charged the
|
|
108
|
+
* first time it is identified in a session, by whichever side identified it,
|
|
109
|
+
* and never again in that session however often it is seen. Misses, "not a
|
|
110
|
+
* card" answers and retries are never charged. See openspec/changes/
|
|
111
|
+
* lease-licensing, usage-metering.
|
|
112
|
+
*/
|
|
113
|
+
export interface UsageReport {
|
|
114
|
+
/** Unique per report, so a report sent twice is counted once. */
|
|
115
|
+
readonly reportId: string;
|
|
116
|
+
/** The lease's `jti`. */
|
|
117
|
+
readonly leaseId: string;
|
|
118
|
+
/** Per lease, from 1, up by exactly 1 per report; renewals name the last one accepted. */
|
|
119
|
+
readonly seq: number;
|
|
120
|
+
/** The scan session: random, at least 128 bits, one per scanner session. */
|
|
121
|
+
readonly sessionId: string;
|
|
122
|
+
/** Cards first identified in this session since its previous report, excluding any the service counted. */
|
|
123
|
+
readonly distinctCards: number;
|
|
124
|
+
/** Those cards' print ids, so the service can charge each at most once per session. */
|
|
125
|
+
readonly cardIds?: readonly PrintId[];
|
|
126
|
+
/** Identify attempts behind this report, retries and "not a card" answers included. Informational, never charged. */
|
|
127
|
+
readonly calls?: number;
|
|
128
|
+
/** Device clock, Unix milliseconds. Informational; the service orders by `seq`. */
|
|
129
|
+
readonly at?: number;
|
|
130
|
+
}
|
|
131
|
+
/** A batch the SDK hands to `reportUsage`, flushed from its IndexedDB queue. */
|
|
132
|
+
export interface UsageBatch {
|
|
133
|
+
/** A random per-install id (not a fingerprint), for monthly active devices. */
|
|
134
|
+
readonly deviceId: string;
|
|
135
|
+
/** May be empty: one batch per lease used is delivered even with nothing to report. */
|
|
136
|
+
readonly reports: readonly UsageReport[];
|
|
137
|
+
}
|
|
138
|
+
/** Where live frames come from. */
|
|
139
|
+
export type FrameSourceInput = MediaStream | HTMLVideoElement;
|
|
140
|
+
export interface ScannerOptions {
|
|
141
|
+
/**
|
|
142
|
+
* Obtain a lease. The host calls its own backend, which calls the service's
|
|
143
|
+
* `POST /v1/leases` with the tenant's secret key; the secret never reaches
|
|
144
|
+
* the browser. Called at start and before the current lease runs out.
|
|
145
|
+
*/
|
|
146
|
+
getLease(request: LeaseRequest): Promise<LeaseGrant>;
|
|
147
|
+
/**
|
|
148
|
+
* Deliver queued usage. The host's backend forwards it to `POST /v1/usage`.
|
|
149
|
+
* Resolve only once it is accepted; a rejection leaves the batch queued.
|
|
150
|
+
*/
|
|
151
|
+
reportUsage(batch: UsageBatch): Promise<void>;
|
|
152
|
+
/** The domains to search; each must be granted by the lease. */
|
|
153
|
+
readonly domains: readonly DomainCode[];
|
|
154
|
+
/** Every processed frame's tracks, on the main thread. */
|
|
155
|
+
onTracks(tracks: readonly TrackResult[]): void;
|
|
156
|
+
/** Errors the host should know about (lease, device, downloads). */
|
|
157
|
+
onError?(error: Error): void;
|
|
158
|
+
/** Force a tier, or let the SDK pick from the device's capabilities. Default `auto`. */
|
|
159
|
+
readonly tier?: DeviceTier | 'auto';
|
|
160
|
+
/**
|
|
161
|
+
* The scan session usage is charged by (each distinct card once per session).
|
|
162
|
+
* Give one per visit to the host's scanner; default: a new random id per scanner.
|
|
163
|
+
*/
|
|
164
|
+
readonly sessionId?: string;
|
|
165
|
+
}
|
|
166
|
+
export type ScannerState = 'idle' | 'starting' | 'running' | 'stopped' | 'failed' | 'destroyed';
|
|
167
|
+
export interface Scanner {
|
|
168
|
+
readonly state: ScannerState;
|
|
169
|
+
/** The tier in use, once started. */
|
|
170
|
+
readonly tier: DeviceTier | null;
|
|
171
|
+
/** Live mode: follow and identify every card in `source`. */
|
|
172
|
+
start(source: FrameSourceInput): Promise<void>;
|
|
173
|
+
/** Pause live mode; tracks are dropped, caches are kept. */
|
|
174
|
+
stop(): void;
|
|
175
|
+
/** Snapshot mode: identify every card in one image. */
|
|
176
|
+
identify(image: ImageBitmap | Blob): Promise<readonly TrackResult[]>;
|
|
177
|
+
/** Release the Worker, the sessions and the camera hooks. */
|
|
178
|
+
destroy(): Promise<void>;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an identification costs: one per distinct card per scan session
|
|
3
|
+
* (lease-licensing, usage-metering).
|
|
4
|
+
*
|
|
5
|
+
* A session is one visit to a host's scanner (the host's `session` id, or the
|
|
6
|
+
* identifier's own). Within it, the first confident `card` answer for a
|
|
7
|
+
* `cardId` is charged once; the same card again in the session is free, and
|
|
8
|
+
* so are `not_a_card` answers, answers that are not confident (the host asks
|
|
9
|
+
* the user instead of asserting), and `retry` calls.
|
|
10
|
+
*
|
|
11
|
+
* A server answer that carries an `identificationId` was charged by the
|
|
12
|
+
* service itself: it adds no card to the report, but still marks the card as
|
|
13
|
+
* seen in the session and still counts toward the lease's allowance.
|
|
14
|
+
*
|
|
15
|
+
* Every call is tallied in the `UsageStore` before its answer reaches the
|
|
16
|
+
* host. `drain` seals the open tallies into `UsageReport`s and hands them to
|
|
17
|
+
* the host in batches; a batch leaves the store only once the host's delivery
|
|
18
|
+
* resolves.
|
|
19
|
+
*/
|
|
20
|
+
import type { LeaseClaims, LeaseRequest, UsageBatch } from '../types.js';
|
|
21
|
+
import type { IdentifyPurpose, IdentifyResult } from '../identify/types.js';
|
|
22
|
+
import { type UsageStore } from './store.js';
|
|
23
|
+
/** Reports per batch handed to the host. */
|
|
24
|
+
export declare const MAX_BATCH_REPORTS = 500;
|
|
25
|
+
export interface MeterOptions {
|
|
26
|
+
readonly store: UsageStore;
|
|
27
|
+
readonly now?: () => number;
|
|
28
|
+
readonly newId?: () => string;
|
|
29
|
+
}
|
|
30
|
+
export interface MeterInput {
|
|
31
|
+
readonly lease: LeaseClaims;
|
|
32
|
+
readonly session: string;
|
|
33
|
+
readonly purpose: IdentifyPurpose | undefined;
|
|
34
|
+
readonly results: readonly IdentifyResult[];
|
|
35
|
+
}
|
|
36
|
+
export interface DrainResult {
|
|
37
|
+
/** Reports delivered by this drain. */
|
|
38
|
+
readonly delivered: number;
|
|
39
|
+
/** Reports and open tallies still queued. */
|
|
40
|
+
readonly pending: number;
|
|
41
|
+
}
|
|
42
|
+
export interface RecordResult {
|
|
43
|
+
/** Cards this call charged on the device (first seen in the session, not counted by the service). */
|
|
44
|
+
readonly charged: readonly string[];
|
|
45
|
+
/** Cards the service had already charged (`identificationId`). */
|
|
46
|
+
readonly serverCharged: number;
|
|
47
|
+
}
|
|
48
|
+
export declare class UsageMeter {
|
|
49
|
+
private readonly store;
|
|
50
|
+
private readonly now;
|
|
51
|
+
private readonly newId;
|
|
52
|
+
/** session → card ids already charged (by either side) in it. */
|
|
53
|
+
private readonly seen;
|
|
54
|
+
/** lease id → cards charged under it on this page (device + service). */
|
|
55
|
+
private readonly used;
|
|
56
|
+
private chain;
|
|
57
|
+
constructor(options: MeterOptions);
|
|
58
|
+
/** Cards charged under `leaseId` on this page so far. */
|
|
59
|
+
usedUnder(leaseId: string): number;
|
|
60
|
+
/** Whether the lease's allowance is used up. */
|
|
61
|
+
exhausted(lease: LeaseClaims): boolean;
|
|
62
|
+
/** Tallies one call's answers; resolves once the tally is stored. */
|
|
63
|
+
record(input: MeterInput): Promise<RecordResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Seals the open tallies and hands the queued reports to `deliver` in
|
|
66
|
+
* batches of at most 500, oldest first. Each batch is removed once `deliver`
|
|
67
|
+
* resolves; a rejection stops the drain, leaves that batch queued, and is
|
|
68
|
+
* rethrown.
|
|
69
|
+
*/
|
|
70
|
+
drain(deliver: (batch: UsageBatch) => Promise<void>): Promise<DrainResult>;
|
|
71
|
+
pending(): Promise<number>;
|
|
72
|
+
/**
|
|
73
|
+
* Makes sure the lease gets at least one report, even if nothing is ever
|
|
74
|
+
* identified under it: the service renews a lease only once the previous
|
|
75
|
+
* one has reported (an empty report counts).
|
|
76
|
+
*/
|
|
77
|
+
touch(leaseId: string, session: string): Promise<void>;
|
|
78
|
+
/** What the host's lease request carries about the lease being renewed. */
|
|
79
|
+
leaseRequest(previousLeaseId: string | null): Promise<LeaseRequest>;
|
|
80
|
+
private sessionSet;
|
|
81
|
+
private serial;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=meter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meter.d.ts","sourceRoot":"","sources":["../../src/usage/meter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAEtD,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAIpC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;IAC7C,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAA;CAC5C;AAED,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,qGAAqG;IACrG,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IACtD,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,KAAK,CAAsC;gBAEvC,OAAO,EAAE,YAAY;IAMjC,yDAAyD;IACzD,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAIlC,gDAAgD;IAChD,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAItC,qEAAqE;IACrE,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IAqBhD;;;;;OAKG;IACG,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBhF,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAI1B;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,2EAA2E;IACrE,YAAY,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC;IAOzE,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,MAAM;CAKf"}
|