@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@dejadex/web`. Versions follow semver; before 1.0 a
|
|
4
|
+
minor version may break the client contract (`src/types.ts`), and says so here.
|
|
5
|
+
|
|
6
|
+
## 0.1.1
|
|
7
|
+
|
|
8
|
+
- **Fix:** the usage database (`dejadex-usage`) no longer blocks a host's
|
|
9
|
+
`indexedDB.deleteDatabase`. The IndexedDB store closes its connection on
|
|
10
|
+
`versionchange`; before, a host deleting it on sign-out stayed blocked for
|
|
11
|
+
the life of the page, and the previous account's queued usage could be
|
|
12
|
+
drained under the next account's session.
|
|
13
|
+
- `UsageStore.close()` (optional for stores a host writes; a no-op on the
|
|
14
|
+
memory store). After a close or a `versionchange`, every call rejects with
|
|
15
|
+
`ScannerError` `INVALID_OPTIONS` ("the usage store is closed"); nothing is
|
|
16
|
+
dropped in silence.
|
|
17
|
+
- `destroy()` also closes the usage store the identifier opened itself. A store
|
|
18
|
+
passed as `options.usageStore` belongs to the host and is left open.
|
|
19
|
+
|
|
20
|
+
## 0.1.0 — first published release
|
|
21
|
+
|
|
22
|
+
- `createIdentifier`: card crops in, catalogue ids out, on the device
|
|
23
|
+
(onnxruntime-web: WebGPU, else WASM SIMD, threaded when the page is
|
|
24
|
+
cross-origin isolated) or through `POST /v1/identify` with the lease.
|
|
25
|
+
- Leases verified on the device with pinned Ed25519 keys (`leasePublicKeys`).
|
|
26
|
+
- Model and index downloads carry the lease's signature: the manifest URL's
|
|
27
|
+
Cloud CDN prefix signature (`URLPrefix`, `Expires`, `KeyName`, `Signature`)
|
|
28
|
+
is copied onto every file URL under that prefix, and onto no other.
|
|
29
|
+
- Usage counted on the device per distinct card per scan session, queued in
|
|
30
|
+
IndexedDB and handed to the host by `drainUsage`.
|
|
31
|
+
- **Contract:** usage is `UsageReport` (`reportId`, `leaseId`, `seq`,
|
|
32
|
+
`sessionId`, `distinctCards`, `cardIds?`, `calls?`, `at?`) in
|
|
33
|
+
`UsageBatch.reports`; it replaced the earlier per-identification
|
|
34
|
+
`UsageEvent`, before any release.
|
|
35
|
+
- `createScanner` and the live pipeline keep their final shape but are still stubs.
|
package/README.md
CHANGED
|
@@ -1,3 +1,223 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @dejadex/web
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Framework-free browser SDK for trading-card identification. Real today:
|
|
4
|
+
|
|
5
|
+
- **`createIdentifier`**: card crops in, catalogue ids out. Identification runs
|
|
6
|
+
on the device (onnxruntime-web: WebGPU, else WASM with SIMD, threaded when the
|
|
7
|
+
page is cross-origin isolated) or through `POST /v1/identify`. The answer has
|
|
8
|
+
the same shape either way. Leases are verified on the device. Usage is counted
|
|
9
|
+
on the device, one per distinct card per session.
|
|
10
|
+
- **The homography maths** (`geometry/`).
|
|
11
|
+
|
|
12
|
+
Still a stub, with its final shape: `createScanner`, the live pipeline
|
|
13
|
+
(detection, tracking, AR). See `openspec/changes/web-sdk-pipeline`.
|
|
14
|
+
|
|
15
|
+
ESM only. Published on the public npm registry, so it installs with no token:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npm install @dejadex/web
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
What protects the platform is the lease and the signed asset URLs, not the
|
|
22
|
+
SDK's code, which runs in browsers anyway. Releases are cut from a
|
|
23
|
+
`web-v<version>` tag by `.github/workflows/publish-web-sdk.yml` (npm trusted
|
|
24
|
+
publishing, no stored token), staged and then approved by a maintainer with
|
|
25
|
+
2FA; see `CHANGELOG.md`.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { createIdentifier } from '@dejadex/web'
|
|
31
|
+
|
|
32
|
+
const identifier = createIdentifier({
|
|
33
|
+
domain: 'tcg01_en',
|
|
34
|
+
// The service's lease-signing keys (its JWKS), pinned in the host's build.
|
|
35
|
+
leasePublicKeys: [{ kty: 'OKP', crv: 'Ed25519', x: '…', kid: '…' }],
|
|
36
|
+
// The identify route's base URL; omit for device-only.
|
|
37
|
+
serverUrl: 'https://api.dejadex.com',
|
|
38
|
+
identification: 'auto', // 'local' never sends an image; 'server' prefers the server
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// The host's backend mints the lease (POST /v1/leases with its secret key)
|
|
42
|
+
// and passes { token, manifestUrl } through unchanged.
|
|
43
|
+
await identifier.setLease(await myBackend.getLease())
|
|
44
|
+
|
|
45
|
+
const outcome = await identifier.identify(crops /* ≤ 9 JPEG Blobs, one card each */, {
|
|
46
|
+
session: scanSessionId, // one per visit to the scanner; usage counts each card once in it
|
|
47
|
+
purpose: 'live', // 'live' | 'still' | 'retry' (retries are never charged)
|
|
48
|
+
top: 5,
|
|
49
|
+
signal,
|
|
50
|
+
})
|
|
51
|
+
if (outcome.ok) {
|
|
52
|
+
outcome.modelVersion // the model the thresholds belong to
|
|
53
|
+
for (const r of outcome.results) {
|
|
54
|
+
r.index // the crop's position
|
|
55
|
+
r.kind // 'card' | 'not_a_card'
|
|
56
|
+
r.confident // top1 − top2 ≥ the manifest's confidentMargin; never for not_a_card
|
|
57
|
+
r.candidates // [{ cardId, score }], best first
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
outcome.reason // 'unavailable' | 'timeout' | 'rejected'
|
|
61
|
+
outcome.detail // starts with a stable code where there is one: LEASE_EXPIRED, ALLOWANCE_EXHAUSTED, …
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// While online, forward usage through the host's backend (it calls POST /v1/usage).
|
|
65
|
+
// A batch leaves the IndexedDB queue only when `deliver` resolves.
|
|
66
|
+
await identifier.drainUsage((batch) => myBackend.postUsage(batch))
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`identify` never throws. Other members: `prepare()` downloads and warms the
|
|
70
|
+
device engine and resolves its `DeviceEngineInfo` (backend, precision, threads,
|
|
71
|
+
measured `perCropMs`). `status()` reports the lease, the device engine and why
|
|
72
|
+
the server route is paused. `leaseRequest()` is the renewal body (see Usage).
|
|
73
|
+
`newSession()` and `pendingUsage()` are what their names say. `destroy()`
|
|
74
|
+
releases the device engine and closes the usage store the identifier opened;
|
|
75
|
+
a store passed as `usageStore` is the host's, and stays open.
|
|
76
|
+
|
|
77
|
+
**Sign-out.** Drain usage, `destroy()` the identifier, then
|
|
78
|
+
`indexedDB.deleteDatabase('dejadex-usage')`. An SDK store still open lets go
|
|
79
|
+
of the database when the delete arrives (and rejects later calls), so the
|
|
80
|
+
delete is never blocked and no queued usage carries into the next account.
|
|
81
|
+
|
|
82
|
+
### Where a call runs
|
|
83
|
+
|
|
84
|
+
| `identification` | Device fast (WebGPU, or ≤ `slowDeviceBudgetMs` = 300 ms per crop) | Device slow, or model still downloading |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `auto` | device | server, then device |
|
|
87
|
+
| `server` | server, then device | server, then device |
|
|
88
|
+
| `local` | device | device (waits for the model) |
|
|
89
|
+
|
|
90
|
+
The server is skipped, going straight to the device:
|
|
91
|
+
|
|
92
|
+
- while `navigator.onLine` is false;
|
|
93
|
+
- for the rest of a lease the server refused (401/403/409);
|
|
94
|
+
- until a 429's `Retry-After`;
|
|
95
|
+
- for 30 s after three failures in a row.
|
|
96
|
+
|
|
97
|
+
A server attempt waits at most the device's own time for the same crops, kept
|
|
98
|
+
between 300 ms and 3 s. While the device engine is still loading, it waits up
|
|
99
|
+
to `serverTimeoutMs` (8 s). After that the same crops are identified on the
|
|
100
|
+
device, and a late answer is ignored. A server answer from another model
|
|
101
|
+
version than the lease's is not used.
|
|
102
|
+
|
|
103
|
+
### What the device engine does
|
|
104
|
+
|
|
105
|
+
It reads the manifest from the lease's `manifestUrl`, whose query is a Cloud CDN
|
|
106
|
+
prefix signature over the lease's model/index directory. The engine copies that
|
|
107
|
+
query onto each file the manifest lists under the signed prefix, on the same
|
|
108
|
+
origin, and onto no other URL. The manifest must match
|
|
109
|
+
the lease's model version, index version and shard. The engine then downloads
|
|
110
|
+
the index shard, its ids and the model file for this device: fp16 on WebGPU
|
|
111
|
+
with `shader-f16`, fp32 on WebGPU without it, and on WASM `runtime.wasmPrecision`,
|
|
112
|
+
fp16 by default. Every file is checked against its SHA-256 and size and cached
|
|
113
|
+
by digest in the Cache API (`dejadex-assets-v1`). Each crop is then:
|
|
114
|
+
|
|
115
|
+
1. decoded without colour management;
|
|
116
|
+
2. resized to 224 × 308 with a bit-exact port of Pillow's bilinear filter (the
|
|
117
|
+
gallery's preprocessing), and normalised with ImageNet mean and std;
|
|
118
|
+
3. embedded at 0° and 180°, taking the L2-normalised CLS token;
|
|
119
|
+
4. scored against every row by brute-force cosine, keeping the better of the
|
|
120
|
+
two orientations.
|
|
121
|
+
|
|
122
|
+
`kind` and `confident` come from the manifest's `notACardBelow` and
|
|
123
|
+
`confidentMargin`.
|
|
124
|
+
|
|
125
|
+
By default the engine runs in a dedicated module Worker:
|
|
126
|
+
`new Worker(new URL('./worker.js', import.meta.url), { type: 'module' })`.
|
|
127
|
+
Vite, webpack 5 and Rollup bundle that pattern. With Vite, exclude the package
|
|
128
|
+
from pre-bundling (`optimizeDeps.exclude: ['@dejadex/web', 'onnxruntime-web']`),
|
|
129
|
+
or onnxruntime-web loses track of its WASM files. To build the Worker yourself,
|
|
130
|
+
point it at `@dejadex/web/identify-worker` and pass `engine: () => worker`.
|
|
131
|
+
`engine: 'inline'` runs the engine on the calling thread.
|
|
132
|
+
|
|
133
|
+
`runtime.wasmPaths` tells onnxruntime-web where its `.wasm` and `.mjs` files
|
|
134
|
+
are served, e.g. the dev server's `/ort/`.
|
|
135
|
+
|
|
136
|
+
### Leases
|
|
137
|
+
|
|
138
|
+
`verifyLease(token, { publicKeys, origin, domains })` mirrors the service's
|
|
139
|
+
`service/src/leases/jws.ts`. It accepts an Ed25519 compact JWS with header
|
|
140
|
+
`{ alg: 'EdDSA', typ: 'dejadex-lease+jwt', kid? }` and refuses everything
|
|
141
|
+
else, `alg: none` included. It checks, in order, the signature, the claims'
|
|
142
|
+
shape and 24 h lifetime, expiry (with 60 s of clock skew for `iat`), the page
|
|
143
|
+
origin and the shard. Failures are `LeaseVerificationError`s, which are
|
|
144
|
+
`ScannerError`s whose `reason` is the service's own code: `LEASE_BAD_SIGNATURE`,
|
|
145
|
+
`LEASE_EXPIRED`, `LEASE_ORIGIN_NOT_ALLOWED`, `LEASE_DOMAIN_NOT_ALLOWED`, ….
|
|
146
|
+
|
|
147
|
+
Signatures are checked with WebCrypto Ed25519 where the browser has it
|
|
148
|
+
(Chrome 137+, Safari 17+, Firefox 129+). Elsewhere they fall back to
|
|
149
|
+
`@noble/ed25519` 3.2.0, which is MIT-licensed, audited and has no
|
|
150
|
+
dependencies. It is loaded only when needed.
|
|
151
|
+
|
|
152
|
+
### Usage
|
|
153
|
+
|
|
154
|
+
Charged: a `kind: 'card'`, `confident` answer whose top `cardId` is new to the
|
|
155
|
+
session. Free: the same card again in the session, `not_a_card`, unsure
|
|
156
|
+
answers and `purpose: 'retry'`. An answer carrying an `identificationId` (the
|
|
157
|
+
service charged it itself) is left out of the report, but still counts toward
|
|
158
|
+
the lease's `allowance`. Once the allowance is used up, `identify` answers
|
|
159
|
+
`rejected` with `ALLOWANCE_EXHAUSTED`.
|
|
160
|
+
|
|
161
|
+
Each call is tallied per (lease, session) in IndexedDB (`dejadex-usage`) before
|
|
162
|
+
it returns. `drainUsage` turns the tallies into `UsageReport`s
|
|
163
|
+
(`{ reportId, leaseId, seq, sessionId, distinctCards, cardIds, calls, at }`,
|
|
164
|
+
with `seq` running per lease from 1). It hands them over in batches of at most
|
|
165
|
+
500 as `{ deviceId, reports }`, and removes a batch only when `deliver` resolves.
|
|
166
|
+
|
|
167
|
+
Every lease gets at least one report, an empty one if nothing was identified
|
|
168
|
+
under it, because the service renews a lease only once the previous one has
|
|
169
|
+
reported. To renew, drain first, then send `await identifier.leaseRequest()`
|
|
170
|
+
(`{ previousLeaseId, usageReportedThrough }`) to the host's backend.
|
|
171
|
+
`leaseRequest(id)` names a lease the host stored but has not set (it expired,
|
|
172
|
+
or this page never identified); `leaseRequest(null)` asks as a new device.
|
|
173
|
+
|
|
174
|
+
### Host requirements
|
|
175
|
+
|
|
176
|
+
- **CSP:** `script-src 'wasm-unsafe-eval'`, a `worker-src` for the SDK's
|
|
177
|
+
Worker, and a `connect-src` for the model CDN and the identify route.
|
|
178
|
+
- **Cross-origin isolation:** COOP `same-origin` plus COEP, needed for WASM
|
|
179
|
+
threads. Without it, WASM runs on one thread, which is several times slower.
|
|
180
|
+
Every cross-origin file must then carry CORP or CORS; the dev server sends both.
|
|
181
|
+
- **Privacy notice:** "The camera feed never leaves the device; on devices that
|
|
182
|
+
need it, the cropped image of a single card is sent for identification and
|
|
183
|
+
is not stored."
|
|
184
|
+
|
|
185
|
+
## Development
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
npm run build # tsc → dist/
|
|
189
|
+
npm test # vitest
|
|
190
|
+
npm run lint && npm run typecheck
|
|
191
|
+
npm run assets:dev # model + shard + ORT + dev leases on http://127.0.0.1:5350
|
|
192
|
+
npm run measure:identify # time identification on this machine (Node + headless Chromium)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`assets:dev` serves the folder written by `ml/scripts/phase0_export_shard.py`
|
|
196
|
+
(default `ml/artifacts/phase0/shard`). Run that script first. The server provides:
|
|
197
|
+
|
|
198
|
+
- `GET /manifests/<model>/<index>/manifest.json`, plus the shard, ids and `.onnx`
|
|
199
|
+
files it lists, **only with a valid prefix signature**, checked as Cloud CDN
|
|
200
|
+
checks it: an unsigned, expired or wrongly signed request, or one outside the
|
|
201
|
+
lease's pair directory, gets 403 `SIGNATURE_REFUSED`. A URL that lost its
|
|
202
|
+
signature fails here as it would in production;
|
|
203
|
+
- `GET /ort/<file>`, onnxruntime-web's dist, for `runtime.wasmPaths`;
|
|
204
|
+
- `POST /dev/lease { origin }`, which answers `{ token, manifestUrl }`: a lease
|
|
205
|
+
signed with a development key, kept in `.secrets/dev-lease-ed25519.pem`
|
|
206
|
+
(gitignored), and the manifest URL prefix-signed until the lease's `exp`;
|
|
207
|
+
- `GET /dev/lease-key`, the development public key as a JWKS, which a host pins
|
|
208
|
+
in its development configuration.
|
|
209
|
+
|
|
210
|
+
The URL key is the development service's (`service/.secrets/manifest-url-dev.key`,
|
|
211
|
+
name `dev-manifest-key`, from `npm run dev:setup` there; created on first
|
|
212
|
+
start if missing, never printed), so leases from the development service with
|
|
213
|
+
`MODEL_CDN_BASE_URL=http://127.0.0.1:5350` load here as well. `--url-key` and
|
|
214
|
+
`--url-key-name` point elsewhere.
|
|
215
|
+
|
|
216
|
+
Every response carries CORS and `Cross-Origin-Resource-Policy: cross-origin`.
|
|
217
|
+
Private Network Access preflights are answered. To reach it from a phone, use
|
|
218
|
+
`adb reverse tcp:5350 tcp:5350`.
|
|
219
|
+
|
|
220
|
+
The preprocessing parity fixtures in `src/identify/__fixtures__/` come from
|
|
221
|
+
`ml/scripts/phase0_sdk_fixtures.py`: synthetic crops, never card images. The
|
|
222
|
+
embedding half of that test runs only where the gitignored model files are on
|
|
223
|
+
disk. `DEJADEX_REQUIRE_MODELS=1` turns their absence into a failure.
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Machine-readable reasons the scanner refuses or stops. Stable; hosts branch on them. */
|
|
2
|
+
export type ScannerErrorCode = 'INVALID_OPTIONS' | 'NOT_IMPLEMENTED' | 'LEASE_INVALID' | 'LEASE_EXPIRED' | 'ALLOWANCE_EXHAUSTED' | 'ORIGIN_NOT_ALLOWED' | 'DOMAIN_NOT_ALLOWED' | 'UNSUPPORTED_DEVICE' | 'MODEL_INTEGRITY' | 'NETWORK';
|
|
3
|
+
export declare class ScannerError extends Error {
|
|
4
|
+
readonly code: ScannerErrorCode;
|
|
5
|
+
readonly name = "ScannerError";
|
|
6
|
+
constructor(code: ScannerErrorCode, message: string, options?: ErrorOptions);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,GACf,eAAe,GACf,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,SAAS,CAAA;AAEb,qBAAa,YAAa,SAAQ,KAAK;IAInC,QAAQ,CAAC,IAAI,EAAE,gBAAgB;IAHjC,SAAkB,IAAI,kBAAiB;gBAG5B,IAAI,EAAE,gBAAgB,EAC/B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY;CAIzB"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAaA,MAAM,OAAO,YAAa,SAAQ,KAAK;IAI1B;IAHO,IAAI,GAAG,cAAc,CAAA;IAEvC,YACW,IAAsB,EAC/B,OAAe,EACf,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAJd,SAAI,GAAJ,IAAI,CAAkB;IAKjC,CAAC;CACF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planar homographies for AR labels: the projective map from a card's own
|
|
3
|
+
* unit rectangle to the four corners the detector found on screen, and its
|
|
4
|
+
* CSS `matrix3d` form, so a host page can lay a DOM element on the card's
|
|
5
|
+
* plane with nothing but `transform`.
|
|
6
|
+
*
|
|
7
|
+
* Conventions, fixed for every consumer:
|
|
8
|
+
*
|
|
9
|
+
* - Card space is the unit square: (0,0) top-left, (1,0) top-right, (1,1)
|
|
10
|
+
* bottom-right, (0,1) bottom-left of the card as printed (portrait).
|
|
11
|
+
* - Screen space is CSS pixels of the element the corners were measured in,
|
|
12
|
+
* y pointing down.
|
|
13
|
+
* - A `Quad` lists the screen corners in that same order: TL, TR, BR, BL.
|
|
14
|
+
* - A `Homography` is a 3×3 matrix in ROW-MAJOR order, normalised so the
|
|
15
|
+
* bottom-right entry is 1. It maps (u, v, 1) to (x·w, y·w, w).
|
|
16
|
+
*
|
|
17
|
+
* The square-to-quad solution is the closed form from P. Heckbert,
|
|
18
|
+
* "Fundamentals of Texture Mapping and Image Warping" (1989), §2.2.3: no
|
|
19
|
+
* linear solve, exact for any convex quad, and cheap enough to run per track
|
|
20
|
+
* per frame.
|
|
21
|
+
*/
|
|
22
|
+
export interface Point {
|
|
23
|
+
readonly x: number;
|
|
24
|
+
readonly y: number;
|
|
25
|
+
}
|
|
26
|
+
/** Screen corners in card order: top-left, top-right, bottom-right, bottom-left. */
|
|
27
|
+
export type Quad = readonly [Point, Point, Point, Point];
|
|
28
|
+
/** Row-major 3×3 matrix, `[a, b, c, d, e, f, g, h, i]` with i = 1 once normalised. */
|
|
29
|
+
export type Homography = readonly [number, number, number, number, number, number, number, number, number];
|
|
30
|
+
export declare const IDENTITY: Homography;
|
|
31
|
+
/** The unit card rectangle's corners, in `Quad` order. */
|
|
32
|
+
export declare const UNIT_QUAD: Quad;
|
|
33
|
+
/**
|
|
34
|
+
* The homography that maps the unit card rectangle onto `quad`.
|
|
35
|
+
*
|
|
36
|
+
* Throws `RangeError` when no proper homography exists: a non-finite corner,
|
|
37
|
+
* three collinear corners (zero area), or a quad that is not convex — a
|
|
38
|
+
* concave or self-crossing quad would need the projective plane to fold, and
|
|
39
|
+
* the label would be drawn through the horizon.
|
|
40
|
+
*/
|
|
41
|
+
export declare function unitSquareToQuad(quad: Quad): Homography;
|
|
42
|
+
/** Maps a point through `m`. Throws `RangeError` for a point on the vanishing line. */
|
|
43
|
+
export declare function applyHomography(m: Homography, p: Point): Point;
|
|
44
|
+
/** `a · b` (apply `b` first, then `a`), normalised. */
|
|
45
|
+
export declare function multiplyHomography(a: Homography, b: Homography): Homography;
|
|
46
|
+
/** The inverse map, normalised. Throws `RangeError` for a singular matrix. */
|
|
47
|
+
export declare function invertHomography(m: Homography): Homography;
|
|
48
|
+
/** Scales `m` so its bottom-right entry is 1. */
|
|
49
|
+
export declare function normalise(m: readonly number[]): Homography;
|
|
50
|
+
export interface ElementSize {
|
|
51
|
+
/** The label element's layout width in CSS pixels (its `offsetWidth`). */
|
|
52
|
+
readonly width: number;
|
|
53
|
+
/** The label element's layout height in CSS pixels (its `offsetHeight`). */
|
|
54
|
+
readonly height: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The CSS `matrix3d(...)` that draws an element of `size` onto the quad that
|
|
58
|
+
* `m` maps the unit card to.
|
|
59
|
+
*
|
|
60
|
+
* The element must be absolutely positioned at the origin of the coordinate
|
|
61
|
+
* space the corners were measured in, with `transform-origin: 0 0`. Its own
|
|
62
|
+
* box (0..width, 0..height) is first scaled to the unit square, then sent
|
|
63
|
+
* through `m`. The 3×3 homography becomes the 4×4
|
|
64
|
+
*
|
|
65
|
+
* | a b 0 c |
|
|
66
|
+
* | d e 0 f |
|
|
67
|
+
* | 0 0 1 0 |
|
|
68
|
+
* | g h 0 i |
|
|
69
|
+
*
|
|
70
|
+
* which leaves z alone, and CSS takes it in COLUMN-major order.
|
|
71
|
+
*/
|
|
72
|
+
export declare function toCssMatrix3d(m: Homography, size?: ElementSize): string;
|
|
73
|
+
/** Convenience for the common case: corners in, CSS transform out. */
|
|
74
|
+
export declare function quadToCssMatrix3d(quad: Quad, size?: ElementSize): string;
|
|
75
|
+
//# sourceMappingURL=homography.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"homography.d.ts","sourceRoot":"","sources":["../../src/geometry/homography.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,oFAAoF;AACpF,MAAM,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;AAExD,sFAAsF;AACtF,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAE1G,eAAO,MAAM,QAAQ,EAAE,UAAwC,CAAA;AAE/D,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,IAKvB,CAAA;AAOD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,UAAU,CAwCvD;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,GAAG,KAAK,CAK9D;AAED,uDAAuD;AACvD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,UAAU,CAI3E;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,GAAG,UAAU,CAY1D;AAED,iDAAiD;AACjD,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAI1D;AAED,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,GAAE,WAAqC,GAAG,MAAM,CAMhG;AAUD,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAExE"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planar homographies for AR labels: the projective map from a card's own
|
|
3
|
+
* unit rectangle to the four corners the detector found on screen, and its
|
|
4
|
+
* CSS `matrix3d` form, so a host page can lay a DOM element on the card's
|
|
5
|
+
* plane with nothing but `transform`.
|
|
6
|
+
*
|
|
7
|
+
* Conventions, fixed for every consumer:
|
|
8
|
+
*
|
|
9
|
+
* - Card space is the unit square: (0,0) top-left, (1,0) top-right, (1,1)
|
|
10
|
+
* bottom-right, (0,1) bottom-left of the card as printed (portrait).
|
|
11
|
+
* - Screen space is CSS pixels of the element the corners were measured in,
|
|
12
|
+
* y pointing down.
|
|
13
|
+
* - A `Quad` lists the screen corners in that same order: TL, TR, BR, BL.
|
|
14
|
+
* - A `Homography` is a 3×3 matrix in ROW-MAJOR order, normalised so the
|
|
15
|
+
* bottom-right entry is 1. It maps (u, v, 1) to (x·w, y·w, w).
|
|
16
|
+
*
|
|
17
|
+
* The square-to-quad solution is the closed form from P. Heckbert,
|
|
18
|
+
* "Fundamentals of Texture Mapping and Image Warping" (1989), §2.2.3: no
|
|
19
|
+
* linear solve, exact for any convex quad, and cheap enough to run per track
|
|
20
|
+
* per frame.
|
|
21
|
+
*/
|
|
22
|
+
export const IDENTITY = [1, 0, 0, 0, 1, 0, 0, 0, 1];
|
|
23
|
+
/** The unit card rectangle's corners, in `Quad` order. */
|
|
24
|
+
export const UNIT_QUAD = [
|
|
25
|
+
{ x: 0, y: 0 },
|
|
26
|
+
{ x: 1, y: 0 },
|
|
27
|
+
{ x: 1, y: 1 },
|
|
28
|
+
{ x: 0, y: 1 },
|
|
29
|
+
];
|
|
30
|
+
/** Relative tolerance below which a quad or a matrix counts as degenerate. */
|
|
31
|
+
const DEGENERATE_EPS = 1e-12;
|
|
32
|
+
const isFiniteQuad = (q) => q.every((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
|
|
33
|
+
/**
|
|
34
|
+
* The homography that maps the unit card rectangle onto `quad`.
|
|
35
|
+
*
|
|
36
|
+
* Throws `RangeError` when no proper homography exists: a non-finite corner,
|
|
37
|
+
* three collinear corners (zero area), or a quad that is not convex — a
|
|
38
|
+
* concave or self-crossing quad would need the projective plane to fold, and
|
|
39
|
+
* the label would be drawn through the horizon.
|
|
40
|
+
*/
|
|
41
|
+
export function unitSquareToQuad(quad) {
|
|
42
|
+
if (!isFiniteQuad(quad))
|
|
43
|
+
throw new RangeError('quad has a non-finite corner');
|
|
44
|
+
const [p0, p1, p2, p3] = quad;
|
|
45
|
+
const dx1 = p1.x - p2.x;
|
|
46
|
+
const dx2 = p3.x - p2.x;
|
|
47
|
+
const dy1 = p1.y - p2.y;
|
|
48
|
+
const dy2 = p3.y - p2.y;
|
|
49
|
+
const sx = p0.x - p1.x + p2.x - p3.x;
|
|
50
|
+
const sy = p0.y - p1.y + p2.y - p3.y;
|
|
51
|
+
const span = Math.max(...quad.map((p) => Math.abs(p.x - p0.x)), ...quad.map((p) => Math.abs(p.y - p0.y)));
|
|
52
|
+
const den = dx1 * dy2 - dx2 * dy1;
|
|
53
|
+
if (span === 0 || Math.abs(den) <= DEGENERATE_EPS * span * span) {
|
|
54
|
+
throw new RangeError('quad is degenerate (zero area or collinear corners)');
|
|
55
|
+
}
|
|
56
|
+
const g = (sx * dy2 - dx2 * sy) / den;
|
|
57
|
+
const h = (dx1 * sy - sx * dy1) / den;
|
|
58
|
+
// w at each corner: (0,0) → 1, (1,0) → 1+g, (1,1) → 1+g+h, (0,1) → 1+h.
|
|
59
|
+
// All must share a sign, or the quad is not the image of a convex square.
|
|
60
|
+
if (1 + g <= DEGENERATE_EPS || 1 + h <= DEGENERATE_EPS || 1 + g + h <= DEGENERATE_EPS) {
|
|
61
|
+
throw new RangeError('quad is not convex');
|
|
62
|
+
}
|
|
63
|
+
return [
|
|
64
|
+
p1.x - p0.x + g * p1.x,
|
|
65
|
+
p3.x - p0.x + h * p3.x,
|
|
66
|
+
p0.x,
|
|
67
|
+
p1.y - p0.y + g * p1.y,
|
|
68
|
+
p3.y - p0.y + h * p3.y,
|
|
69
|
+
p0.y,
|
|
70
|
+
g,
|
|
71
|
+
h,
|
|
72
|
+
1,
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
/** Maps a point through `m`. Throws `RangeError` for a point on the vanishing line. */
|
|
76
|
+
export function applyHomography(m, p) {
|
|
77
|
+
const [a, b, c, d, e, f, g, h, i] = m;
|
|
78
|
+
const w = g * p.x + h * p.y + i;
|
|
79
|
+
if (Math.abs(w) <= DEGENERATE_EPS)
|
|
80
|
+
throw new RangeError('point maps to infinity');
|
|
81
|
+
return { x: (a * p.x + b * p.y + c) / w, y: (d * p.x + e * p.y + f) / w };
|
|
82
|
+
}
|
|
83
|
+
/** `a · b` (apply `b` first, then `a`), normalised. */
|
|
84
|
+
export function multiplyHomography(a, b) {
|
|
85
|
+
const r = (row, col) => a[row * 3] * b[col] + a[row * 3 + 1] * b[3 + col] + a[row * 3 + 2] * b[6 + col];
|
|
86
|
+
return normalise([r(0, 0), r(0, 1), r(0, 2), r(1, 0), r(1, 1), r(1, 2), r(2, 0), r(2, 1), r(2, 2)]);
|
|
87
|
+
}
|
|
88
|
+
/** The inverse map, normalised. Throws `RangeError` for a singular matrix. */
|
|
89
|
+
export function invertHomography(m) {
|
|
90
|
+
const [a, b, c, d, e, f, g, h, i] = m;
|
|
91
|
+
const A = e * i - f * h;
|
|
92
|
+
const B = -(d * i - f * g);
|
|
93
|
+
const C = d * h - e * g;
|
|
94
|
+
const det = a * A + b * B + c * C;
|
|
95
|
+
const scale = Math.max(...m.map(Math.abs));
|
|
96
|
+
if (!Number.isFinite(det) || Math.abs(det) <= DEGENERATE_EPS * scale * scale * scale) {
|
|
97
|
+
throw new RangeError('homography is singular');
|
|
98
|
+
}
|
|
99
|
+
// Adjugate (transposed cofactors); the 1/det factor cancels in normalise().
|
|
100
|
+
return normalise([A, -(b * i - c * h), b * f - c * e, B, a * i - c * g, -(a * f - c * d), C, -(a * h - b * g), a * e - b * d]);
|
|
101
|
+
}
|
|
102
|
+
/** Scales `m` so its bottom-right entry is 1. */
|
|
103
|
+
export function normalise(m) {
|
|
104
|
+
const k = m[8];
|
|
105
|
+
if (Math.abs(k) <= DEGENERATE_EPS)
|
|
106
|
+
throw new RangeError('homography cannot be normalised (h33 = 0)');
|
|
107
|
+
return m.map((v) => v / k);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The CSS `matrix3d(...)` that draws an element of `size` onto the quad that
|
|
111
|
+
* `m` maps the unit card to.
|
|
112
|
+
*
|
|
113
|
+
* The element must be absolutely positioned at the origin of the coordinate
|
|
114
|
+
* space the corners were measured in, with `transform-origin: 0 0`. Its own
|
|
115
|
+
* box (0..width, 0..height) is first scaled to the unit square, then sent
|
|
116
|
+
* through `m`. The 3×3 homography becomes the 4×4
|
|
117
|
+
*
|
|
118
|
+
* | a b 0 c |
|
|
119
|
+
* | d e 0 f |
|
|
120
|
+
* | 0 0 1 0 |
|
|
121
|
+
* | g h 0 i |
|
|
122
|
+
*
|
|
123
|
+
* which leaves z alone, and CSS takes it in COLUMN-major order.
|
|
124
|
+
*/
|
|
125
|
+
export function toCssMatrix3d(m, size = { width: 1, height: 1 }) {
|
|
126
|
+
if (!(size.width > 0) || !(size.height > 0))
|
|
127
|
+
throw new RangeError('element size must be positive');
|
|
128
|
+
const scaled = multiplyHomography(m, [1 / size.width, 0, 0, 0, 1 / size.height, 0, 0, 0, 1]);
|
|
129
|
+
const [a, b, c, d, e, f, g, h, i] = scaled;
|
|
130
|
+
const columnMajor = [a, d, 0, g, b, e, 0, h, 0, 0, 1, 0, c, f, 0, i];
|
|
131
|
+
return `matrix3d(${columnMajor.map(formatCssNumber).join(', ')})`;
|
|
132
|
+
}
|
|
133
|
+
/** Fixed precision, no `-0`, no exponent: what every engine parses identically. */
|
|
134
|
+
function formatCssNumber(v) {
|
|
135
|
+
if (!Number.isFinite(v))
|
|
136
|
+
throw new RangeError('non-finite matrix entry');
|
|
137
|
+
const fixed = Number(v.toFixed(12));
|
|
138
|
+
if (fixed === 0)
|
|
139
|
+
return '0';
|
|
140
|
+
return fixed.toFixed(12).replace(/\.?0+$/, '');
|
|
141
|
+
}
|
|
142
|
+
/** Convenience for the common case: corners in, CSS transform out. */
|
|
143
|
+
export function quadToCssMatrix3d(quad, size) {
|
|
144
|
+
return toCssMatrix3d(unitSquareToQuad(quad), size);
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=homography.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"homography.js","sourceRoot":"","sources":["../../src/geometry/homography.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ManifestFile } from './manifest.js';
|
|
2
|
+
/** The Cache API cache the SDK owns. Bump the suffix if the key scheme changes. */
|
|
3
|
+
export declare const CACHE_NAME = "dejadex-assets-v1";
|
|
4
|
+
export declare const MANIFEST_TIMEOUT_MS = 10000;
|
|
5
|
+
export type FetchLike = (input: string, init: {
|
|
6
|
+
signal: AbortSignal;
|
|
7
|
+
cache?: RequestCache;
|
|
8
|
+
}) => Promise<Response>;
|
|
9
|
+
/** The subset of `CacheStorage` used, so tests and hosts without one can pass their own (or none). */
|
|
10
|
+
export interface CacheLike {
|
|
11
|
+
match(key: string): Promise<Response | undefined>;
|
|
12
|
+
put(key: string, response: Response): Promise<void>;
|
|
13
|
+
delete(key: string): Promise<boolean>;
|
|
14
|
+
keys(): Promise<readonly {
|
|
15
|
+
readonly url: string;
|
|
16
|
+
}[]>;
|
|
17
|
+
}
|
|
18
|
+
export interface AssetStoreOptions {
|
|
19
|
+
readonly fetch: FetchLike;
|
|
20
|
+
/** null: no cache (every start downloads). */
|
|
21
|
+
readonly cache: CacheLike | null;
|
|
22
|
+
/** Overrides the per-file bound (ms); default from the size, at ≥ 100 kB/s. */
|
|
23
|
+
readonly fileTimeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface AssetStore {
|
|
26
|
+
/** The file's bytes, from the cache or the network, verified. */
|
|
27
|
+
get(file: ManifestFile, signal?: AbortSignal): Promise<ArrayBuffer>;
|
|
28
|
+
/** The manifest document from `url`; offline, the last one that loaded from it. */
|
|
29
|
+
manifest(url: string, signal?: AbortSignal): Promise<unknown>;
|
|
30
|
+
/** Drops cached files whose digest is not in `keep` (after a new manifest verified). */
|
|
31
|
+
evictExcept(keep: readonly string[]): Promise<number>;
|
|
32
|
+
}
|
|
33
|
+
export declare function sha256Hex(data: ArrayBuffer): Promise<string>;
|
|
34
|
+
/** `AbortSignal.any` where the runtime has it, else a controller following both. */
|
|
35
|
+
export declare function anySignal(signals: readonly (AbortSignal | undefined)[]): AbortSignal;
|
|
36
|
+
export declare function createAssetStore(options: AssetStoreOptions): AssetStore;
|
|
37
|
+
/** The page's Cache API cache, or null where there is none (or it is blocked). */
|
|
38
|
+
export declare function openDefaultCache(): Promise<CacheLike | null>;
|
|
39
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/identify/assets.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,mFAAmF;AACnF,eAAO,MAAM,UAAU,sBAAsB,CAAA;AAO7C,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,CAAC,EAAE,YAAY,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEjH,sGAAsG;AACtG,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAA;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACrC,IAAI,IAAI,OAAO,CAAC,SAAS;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAA;CACrD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAChC,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,iEAAiE;IACjE,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACnE,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7D,wFAAwF;IACxF,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACtD;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAKlE;AASD,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,SAAS,CAAC,EAAE,GAAG,WAAW,CAapF;AAID,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CA0FvE;AAED,kFAAkF;AAClF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAalE"}
|