@visutry/tryon-core 0.2.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 +58 -0
- package/dist/__fixtures__/faceFixtures.d.ts +13 -0
- package/dist/__fixtures__/faceFixtures.d.ts.map +1 -0
- package/dist/__fixtures__/faceFixtures.js +206 -0
- package/dist/__fixtures__/faceFixtures.js.map +1 -0
- package/dist/adapter/visutry-site.d.ts +76 -0
- package/dist/adapter/visutry-site.d.ts.map +1 -0
- package/dist/adapter/visutry-site.js +172 -0
- package/dist/adapter/visutry-site.js.map +1 -0
- package/dist/analytics.d.ts +89 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +49 -0
- package/dist/analytics.js.map +1 -0
- package/dist/coordinate/CoordinateSystem.d.ts +46 -0
- package/dist/coordinate/CoordinateSystem.d.ts.map +1 -0
- package/dist/coordinate/CoordinateSystem.js +88 -0
- package/dist/coordinate/CoordinateSystem.js.map +1 -0
- package/dist/face/FaceMetricsCalculator.d.ts +52 -0
- package/dist/face/FaceMetricsCalculator.d.ts.map +1 -0
- package/dist/face/FaceMetricsCalculator.js +375 -0
- package/dist/face/FaceMetricsCalculator.js.map +1 -0
- package/dist/face/FaceSemanticMapper.d.ts +54 -0
- package/dist/face/FaceSemanticMapper.d.ts.map +1 -0
- package/dist/face/FaceSemanticMapper.js +129 -0
- package/dist/face/FaceSemanticMapper.js.map +1 -0
- package/dist/face/FaceShapeScorer.d.ts +35 -0
- package/dist/face/FaceShapeScorer.d.ts.map +1 -0
- package/dist/face/FaceShapeScorer.js +265 -0
- package/dist/face/FaceShapeScorer.js.map +1 -0
- package/dist/face/feature-extractor.d.ts +46 -0
- package/dist/face/feature-extractor.d.ts.map +1 -0
- package/dist/face/feature-extractor.js +106 -0
- package/dist/face/feature-extractor.js.map +1 -0
- package/dist/face/logreg-classifier.d.ts +117 -0
- package/dist/face/logreg-classifier.d.ts.map +1 -0
- package/dist/face/logreg-classifier.js +304 -0
- package/dist/face/logreg-classifier.js.map +1 -0
- package/dist/feature-flag.d.ts +43 -0
- package/dist/feature-flag.d.ts.map +1 -0
- package/dist/feature-flag.js +58 -0
- package/dist/feature-flag.js.map +1 -0
- package/dist/i18n/index.d.ts +15 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +91 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest/ManifestValidator.d.ts +23 -0
- package/dist/manifest/ManifestValidator.d.ts.map +1 -0
- package/dist/manifest/ManifestValidator.js +129 -0
- package/dist/manifest/ManifestValidator.js.map +1 -0
- package/dist/pose/GlassesPoseSolver.d.ts +64 -0
- package/dist/pose/GlassesPoseSolver.d.ts.map +1 -0
- package/dist/pose/GlassesPoseSolver.js +299 -0
- package/dist/pose/GlassesPoseSolver.js.map +1 -0
- package/dist/privacy/PrivacyGuard.d.ts +44 -0
- package/dist/privacy/PrivacyGuard.d.ts.map +1 -0
- package/dist/privacy/PrivacyGuard.js +105 -0
- package/dist/privacy/PrivacyGuard.js.map +1 -0
- package/dist/quality/QualityGate.d.ts +22 -0
- package/dist/quality/QualityGate.d.ts.map +1 -0
- package/dist/quality/QualityGate.js +161 -0
- package/dist/quality/QualityGate.js.map +1 -0
- package/dist/smoothing/PoseSmoothing.d.ts +37 -0
- package/dist/smoothing/PoseSmoothing.d.ts.map +1 -0
- package/dist/smoothing/PoseSmoothing.js +146 -0
- package/dist/smoothing/PoseSmoothing.js.map +1 -0
- package/dist/types/index.d.ts +444 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/errors.d.ts +7 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +20 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/math.d.ts +40 -0
- package/dist/utils/math.d.ts.map +1 -0
- package/dist/utils/math.js +149 -0
- package/dist/utils/math.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @visutry/tryon-core
|
|
2
|
+
|
|
3
|
+
Platform-agnostic core of the [VisuTry](https://github.com/franksunye/visutry-tryon-sdk) AR glasses try-on SDK.
|
|
4
|
+
|
|
5
|
+
It contains no platform (browser / WeChat) code — only the shared foundation:
|
|
6
|
+
shared types, coordinate-system transforms, face semantic mapping, face metrics,
|
|
7
|
+
face-shape scoring, the glasses pose solver, pose smoothing, the quality gate,
|
|
8
|
+
the asset-manifest validator, error helpers, and the i18n message catalogue.
|
|
9
|
+
|
|
10
|
+
This package is consumed by the platform adapters (`@visutry/tryon-web`,
|
|
11
|
+
`@visutry/tryon-wechat`) and the `@visutry/recommender`. Application code
|
|
12
|
+
usually depends on a platform adapter rather than the core directly.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @visutry/tryon-core
|
|
18
|
+
# or
|
|
19
|
+
npm install @visutry/tryon-core
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
> Requires Node.js >= 18.
|
|
23
|
+
|
|
24
|
+
## Basic usage
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import {
|
|
28
|
+
createSDKError,
|
|
29
|
+
GlassesPoseSolver,
|
|
30
|
+
QualityGate,
|
|
31
|
+
setLocale,
|
|
32
|
+
t,
|
|
33
|
+
type FaceShapeResult,
|
|
34
|
+
} from "@visutry/tryon-core";
|
|
35
|
+
|
|
36
|
+
// Localise user-facing SDK error messages (default locale: "en").
|
|
37
|
+
setLocale("zh-CN");
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// ...core pipeline helpers run here...
|
|
41
|
+
} catch (err) {
|
|
42
|
+
// err is a normalised SDKError whose message comes from the i18n catalogue.
|
|
43
|
+
console.error(t("error.sdk_destroyed"));
|
|
44
|
+
throw createSDKError("UNKNOWN", t("error.sdk_destroyed"), err);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API surface
|
|
49
|
+
|
|
50
|
+
- Types: `VisuTrySDK`, `VisuTrySDKConfig`, `FaceShapeResult`, `FaceMetrics`,
|
|
51
|
+
`GlassesAssetManifest`, `GlassesItem`, `RecommendationInput`, ...
|
|
52
|
+
- Pipeline: `GlassesPoseSolver`, `PoseSmoother`, `QualityGate`,
|
|
53
|
+
`FaceShapeScorer`, `PrivacyGuard`.
|
|
54
|
+
- Helpers: `createSDKError`, `ManifestValidator`, `setLocale` / `getLocale` / `t`.
|
|
55
|
+
|
|
56
|
+
## Full documentation
|
|
57
|
+
|
|
58
|
+
See the monorepo docs: <https://github.com/franksunye/visutry-tryon-sdk#readme>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FaceSemanticPoints, NormalizedFaceResult, GlassesAssetManifest } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a synthetic `FaceSemanticPoints` representing a specific face shape.
|
|
4
|
+
* Coordinates are in normalized image space (origin top-left, y down).
|
|
5
|
+
*
|
|
6
|
+
* The geometry is constructed so each shape exhibits the discriminating
|
|
7
|
+
* feature ratios the scorer relies on (width/height, jaw/cheek,
|
|
8
|
+
* jaw/eye-outer, eye-outer/cheek, chin type).
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildSemanticPoints(shape: "oval" | "round" | "square" | "heart" | "diamond" | "oblong"): FaceSemanticPoints;
|
|
11
|
+
export declare function buildFaceResult(semantic: FaceSemanticPoints, overrides?: Partial<NormalizedFaceResult>): NormalizedFaceResult;
|
|
12
|
+
export declare function buildManifest(overrides?: Partial<GlassesAssetManifest>): GlassesAssetManifest;
|
|
13
|
+
//# sourceMappingURL=faceFixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faceFixtures.d.ts","sourceRoot":"","sources":["../../src/__fixtures__/faceFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EAEpB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAG3B;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAClE,kBAAkB,CAyHpB;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,kBAAkB,EAC5B,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,oBAAoB,CAqCtB;AAED,wBAAgB,aAAa,CAAC,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAAG,oBAAoB,CA4CjG"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { MEDIAPIPE_SEMANTIC_INDEX_MAP } from "../face/FaceSemanticMapper.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a synthetic `FaceSemanticPoints` representing a specific face shape.
|
|
4
|
+
* Coordinates are in normalized image space (origin top-left, y down).
|
|
5
|
+
*
|
|
6
|
+
* The geometry is constructed so each shape exhibits the discriminating
|
|
7
|
+
* feature ratios the scorer relies on (width/height, jaw/cheek,
|
|
8
|
+
* jaw/eye-outer, eye-outer/cheek, chin type).
|
|
9
|
+
*/
|
|
10
|
+
export function buildSemanticPoints(shape) {
|
|
11
|
+
const cx = 0.5;
|
|
12
|
+
const cy = 0.5;
|
|
13
|
+
// Eye geometry (symmetric, level by default).
|
|
14
|
+
const eyeHalfSpan = 0.12; // outer corner half-span → eyeOuterDistance = 0.24
|
|
15
|
+
const eyeInnerOffset = 0.04;
|
|
16
|
+
const eyeY = cy - 0.08; // 0.42
|
|
17
|
+
const leftEyeOuter = { x: cx - eyeHalfSpan, y: eyeY, z: -0.02 };
|
|
18
|
+
const leftEyeInner = { x: cx - eyeInnerOffset, y: eyeY, z: -0.02 };
|
|
19
|
+
const rightEyeInner = { x: cx + eyeInnerOffset, y: eyeY, z: -0.02 };
|
|
20
|
+
const rightEyeOuter = { x: cx + eyeHalfSpan, y: eyeY, z: -0.02 };
|
|
21
|
+
const leftEyeCenter = { x: (leftEyeOuter.x + leftEyeInner.x) / 2, y: eyeY, z: -0.02 };
|
|
22
|
+
const rightEyeCenter = { x: (rightEyeInner.x + rightEyeOuter.x) / 2, y: eyeY, z: -0.02 };
|
|
23
|
+
const eyesCenter = { x: cx, y: eyeY, z: -0.02 };
|
|
24
|
+
const noseBridge = { x: cx, y: cy - 0.02, z: -0.03 };
|
|
25
|
+
const noseTip = { x: cx, y: cy + 0.04, z: -0.08 };
|
|
26
|
+
const foreheadCenter = { x: cx, y: cy - 0.18, z: -0.02 }; // y = 0.32
|
|
27
|
+
// Per-shape jaw / cheek geometry, tuned to hit target feature ratios.
|
|
28
|
+
// faceWidth = cheekboneWidth = 2 * cheekHalf
|
|
29
|
+
// jawWidth = 2 * jawHalf
|
|
30
|
+
// faceHeight = chinY - 0.32
|
|
31
|
+
// whr = cheekboneWidth / faceHeight
|
|
32
|
+
// jcr = jawWidth / cheekboneWidth
|
|
33
|
+
let cheekHalf = 0.15;
|
|
34
|
+
let jawHalf = 0.12;
|
|
35
|
+
let chinY = cy + 0.195; // 0.695
|
|
36
|
+
let browHalf = 0.07;
|
|
37
|
+
// visutry additions: face outline, forehead, nose wing half-widths
|
|
38
|
+
let faceHalf = 0.16; // leftFace/rightFace
|
|
39
|
+
let foreheadHalf = 0.14; // leftForehead/rightForehead
|
|
40
|
+
let noseHalf = 0.05; // noseLeft/noseRight
|
|
41
|
+
switch (shape) {
|
|
42
|
+
case "oval": // whr~0.80, jcr~0.80, rounded chin
|
|
43
|
+
cheekHalf = 0.15;
|
|
44
|
+
jawHalf = 0.12;
|
|
45
|
+
chinY = 0.695;
|
|
46
|
+
faceHalf = 0.155;
|
|
47
|
+
foreheadHalf = 0.135;
|
|
48
|
+
noseHalf = 0.045;
|
|
49
|
+
break;
|
|
50
|
+
case "round": // whr~0.96, jcr~0.91, rounded chin
|
|
51
|
+
cheekHalf = 0.16;
|
|
52
|
+
jawHalf = 0.1455;
|
|
53
|
+
chinY = 0.653;
|
|
54
|
+
faceHalf = 0.165;
|
|
55
|
+
foreheadHalf = 0.15;
|
|
56
|
+
noseHalf = 0.05;
|
|
57
|
+
break;
|
|
58
|
+
case "square": // whr~0.85, jcr~0.93, square chin
|
|
59
|
+
cheekHalf = 0.156;
|
|
60
|
+
jawHalf = 0.145;
|
|
61
|
+
chinY = 0.687;
|
|
62
|
+
faceHalf = 0.158;
|
|
63
|
+
foreheadHalf = 0.148; // broad forehead (fcr ~0.95, not triangle)
|
|
64
|
+
noseHalf = 0.048;
|
|
65
|
+
break;
|
|
66
|
+
case "heart": // whr~0.80, jcr~0.58, pointed chin, upper wide
|
|
67
|
+
cheekHalf = 0.12;
|
|
68
|
+
jawHalf = 0.0695;
|
|
69
|
+
chinY = 0.62;
|
|
70
|
+
browHalf = 0.09;
|
|
71
|
+
faceHalf = 0.125;
|
|
72
|
+
foreheadHalf = 0.13; // broad forehead (fcr > 0.9)
|
|
73
|
+
noseHalf = 0.04;
|
|
74
|
+
break;
|
|
75
|
+
case "diamond": // whr~0.75, jcr~0.64, pointed chin, cheek dominant
|
|
76
|
+
cheekHalf = 0.171;
|
|
77
|
+
jawHalf = 0.11;
|
|
78
|
+
chinY = 0.777;
|
|
79
|
+
faceHalf = 0.175;
|
|
80
|
+
foreheadHalf = 0.12; // narrow forehead (fcr < 0.8)
|
|
81
|
+
noseHalf = 0.042;
|
|
82
|
+
break;
|
|
83
|
+
case "oblong": // whr~0.66, jcr~0.82, rounded chin
|
|
84
|
+
cheekHalf = 0.1395;
|
|
85
|
+
jawHalf = 0.115;
|
|
86
|
+
chinY = 0.743;
|
|
87
|
+
faceHalf = 0.142;
|
|
88
|
+
foreheadHalf = 0.13;
|
|
89
|
+
noseHalf = 0.044;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
const leftCheek = { x: cx - cheekHalf, y: cy + 0.02, z: -0.03 };
|
|
93
|
+
const rightCheek = { x: cx + cheekHalf, y: cy + 0.02, z: -0.03 };
|
|
94
|
+
const leftJaw = { x: cx - jawHalf, y: cy + 0.13, z: -0.02 };
|
|
95
|
+
const rightJaw = { x: cx + jawHalf, y: cy + 0.13, z: -0.02 };
|
|
96
|
+
const chin = { x: cx, y: chinY, z: -0.04 };
|
|
97
|
+
return {
|
|
98
|
+
leftEyeOuter,
|
|
99
|
+
leftEyeInner,
|
|
100
|
+
rightEyeInner,
|
|
101
|
+
rightEyeOuter,
|
|
102
|
+
leftEyeCenter,
|
|
103
|
+
rightEyeCenter,
|
|
104
|
+
eyesCenter,
|
|
105
|
+
noseBridge,
|
|
106
|
+
noseTip,
|
|
107
|
+
leftBrowCenter: { x: cx - browHalf, y: eyeY - 0.04, z: -0.02 },
|
|
108
|
+
rightBrowCenter: { x: cx + browHalf, y: eyeY - 0.04, z: -0.02 },
|
|
109
|
+
foreheadCenter,
|
|
110
|
+
chin,
|
|
111
|
+
leftCheek,
|
|
112
|
+
rightCheek,
|
|
113
|
+
leftJaw,
|
|
114
|
+
rightJaw,
|
|
115
|
+
// visutry additions
|
|
116
|
+
leftFace: { x: cx - faceHalf, y: cy, z: -0.01 },
|
|
117
|
+
rightFace: { x: cx + faceHalf, y: cy, z: -0.01 },
|
|
118
|
+
leftForehead: { x: cx - foreheadHalf, y: cy - 0.14, z: -0.02 },
|
|
119
|
+
rightForehead: { x: cx + foreheadHalf, y: cy - 0.14, z: -0.02 },
|
|
120
|
+
noseLeft: { x: cx - noseHalf, y: cy, z: -0.03 },
|
|
121
|
+
noseRight: { x: cx + noseHalf, y: cy, z: -0.03 },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function buildFaceResult(semantic, overrides = {}) {
|
|
125
|
+
const raw = [];
|
|
126
|
+
const maxIndex = Math.max(...Object.values(MEDIAPIPE_SEMANTIC_INDEX_MAP).filter((v) => v !== undefined));
|
|
127
|
+
for (let i = 0; i <= maxIndex; i++)
|
|
128
|
+
raw.push({ x: 0.5, y: 0.5, z: 0 });
|
|
129
|
+
for (const [key, idx] of Object.entries(MEDIAPIPE_SEMANTIC_INDEX_MAP)) {
|
|
130
|
+
const pt = semantic[key];
|
|
131
|
+
if (pt && idx !== undefined)
|
|
132
|
+
raw[idx] = { ...pt };
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
source: "mediapipe",
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
landmarks: {
|
|
138
|
+
raw,
|
|
139
|
+
normalized: raw,
|
|
140
|
+
semantic,
|
|
141
|
+
},
|
|
142
|
+
pose: {
|
|
143
|
+
yaw: 0,
|
|
144
|
+
pitch: 0,
|
|
145
|
+
roll: 0,
|
|
146
|
+
confidence: 0.95,
|
|
147
|
+
...overrides.pose,
|
|
148
|
+
},
|
|
149
|
+
bbox: { x: 0.2, y: 0.15, width: 0.6, height: 0.7, ...overrides.bbox },
|
|
150
|
+
quality: {
|
|
151
|
+
confidence: 0.92,
|
|
152
|
+
faceVisible: true,
|
|
153
|
+
frontalScore: 0.9,
|
|
154
|
+
stabilityScore: 0.85,
|
|
155
|
+
warnings: [],
|
|
156
|
+
...overrides.quality,
|
|
157
|
+
},
|
|
158
|
+
...overrides,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
export function buildManifest(overrides = {}) {
|
|
162
|
+
return {
|
|
163
|
+
id: "test-glasses",
|
|
164
|
+
name: "Test Glasses",
|
|
165
|
+
modelUrl: "https://example.com/glasses.glb",
|
|
166
|
+
format: "glb",
|
|
167
|
+
coordinateSystem: {
|
|
168
|
+
unit: "millimeter",
|
|
169
|
+
forwardAxis: "+z",
|
|
170
|
+
upAxis: "+y",
|
|
171
|
+
},
|
|
172
|
+
dimensions: {
|
|
173
|
+
frameWidthMm: 140,
|
|
174
|
+
lensWidthMm: 50,
|
|
175
|
+
lensHeightMm: 40,
|
|
176
|
+
bridgeWidthMm: 20,
|
|
177
|
+
templeLengthMm: 140,
|
|
178
|
+
},
|
|
179
|
+
anchors: {
|
|
180
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
181
|
+
noseBridge: { x: 0, y: 0, z: 0 },
|
|
182
|
+
leftHinge: { x: -70, y: 0, z: 0 },
|
|
183
|
+
rightHinge: { x: 70, y: 0, z: 0 },
|
|
184
|
+
},
|
|
185
|
+
fitting: {
|
|
186
|
+
defaultScale: 1,
|
|
187
|
+
defaultOffset: { x: 0, y: 0, z: 0 },
|
|
188
|
+
defaultRotation: { x: 0, y: 0, z: 0 },
|
|
189
|
+
minScale: 0.2,
|
|
190
|
+
maxScale: 3,
|
|
191
|
+
},
|
|
192
|
+
material: {
|
|
193
|
+
lensOpacity: 0.5,
|
|
194
|
+
frameRoughness: 0.4,
|
|
195
|
+
supportsTransparency: true,
|
|
196
|
+
},
|
|
197
|
+
metadata: {
|
|
198
|
+
brand: "VisuTry",
|
|
199
|
+
shapeCategory: "rectangle",
|
|
200
|
+
colors: ["black"],
|
|
201
|
+
tags: ["demo"],
|
|
202
|
+
},
|
|
203
|
+
...overrides,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=faceFixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faceFixtures.js","sourceRoot":"","sources":["../../src/__fixtures__/faceFixtures.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VisuTry main-site adapter.
|
|
3
|
+
*
|
|
4
|
+
* Converts the SDK's honest `FaceShapeResult` into the `landmark-v1`
|
|
5
|
+
* `FaceGeometryAnalysis` contract consumed by the VisuTry main site
|
|
6
|
+
* (`src/types/face-analysis.ts`). This is the SINGLE adapter the spec
|
|
7
|
+
* (§12) requires — the main site must not run a parallel classification
|
|
8
|
+
* path.
|
|
9
|
+
*
|
|
10
|
+
* Key conversions (spec §4.3):
|
|
11
|
+
* - SDK `faceWidthToHeight` (W/H) → site `faceAspectRatio` (H/W), i.e. reciprocal.
|
|
12
|
+
* - SDK `analysisQuality` (0..1) → site `qualityScore` (0..100 integer).
|
|
13
|
+
* - SDK `matchStrength` (0..1) → site `measuredConfidence` (kept as-is;
|
|
14
|
+
* the field name is legacy but the value is match strength, NOT a probability).
|
|
15
|
+
* - SDK `classification: "unknown"` → site `status: "unavailable"`.
|
|
16
|
+
*/
|
|
17
|
+
import type { Classification, FaceMetrics, FaceShapeResult } from "../types/index.js";
|
|
18
|
+
/** The 7 canonical face shapes used by the VisuTry main site. */
|
|
19
|
+
export type SiteFaceShape = "round" | "square" | "oval" | "heart" | "diamond" | "oblong" | "triangle";
|
|
20
|
+
/** Ratio block consumed by the main site's report builder and VLM prompt. */
|
|
21
|
+
export interface SiteFaceGeometryRatios {
|
|
22
|
+
/** faceHeight / faceWidth — reciprocal of SDK `faceWidthToHeight`. */
|
|
23
|
+
faceAspectRatio: number;
|
|
24
|
+
/** cheekboneWidth / faceWidth. */
|
|
25
|
+
cheekToFaceWidth: number;
|
|
26
|
+
/** jawWidth / cheekboneWidth — same as SDK `jawCheekRatio`. */
|
|
27
|
+
jawToCheekWidth: number;
|
|
28
|
+
/** foreheadWidth / cheekboneWidth — same as SDK `foreheadCheekRatio`. */
|
|
29
|
+
foreheadToCheekWidth: number;
|
|
30
|
+
eyeLineTiltDeg: number;
|
|
31
|
+
symmetryOffset: number;
|
|
32
|
+
/** noseBridgeWidth / faceWidth — same as SDK `noseBridgeRatio`. */
|
|
33
|
+
noseBridgeToFaceWidth: number;
|
|
34
|
+
}
|
|
35
|
+
/** The `landmark-v1` geometry analysis contract consumed by the main site. */
|
|
36
|
+
export interface SiteFaceGeometryAnalysis {
|
|
37
|
+
version: "landmark-v1";
|
|
38
|
+
status: "measured" | "unavailable";
|
|
39
|
+
source: "mediapipe-face-landmarker" | "ai-fallback";
|
|
40
|
+
faceDetected: boolean;
|
|
41
|
+
faceCount: number;
|
|
42
|
+
qualityScore: number;
|
|
43
|
+
measuredShape?: SiteFaceShape;
|
|
44
|
+
measuredConfidence?: number;
|
|
45
|
+
ratios?: SiteFaceGeometryRatios;
|
|
46
|
+
signals: string[];
|
|
47
|
+
warnings: string[];
|
|
48
|
+
}
|
|
49
|
+
/** Bumped when the mapping logic or mirrored contract changes. */
|
|
50
|
+
export declare const VISUTRY_SITE_ADAPTER_VERSION = "1.0.0";
|
|
51
|
+
/**
|
|
52
|
+
* Convert an SDK `FaceShapeResult` into the main-site `landmark-v1`
|
|
53
|
+
* `FaceGeometryAnalysis` contract.
|
|
54
|
+
*
|
|
55
|
+
* - `classification: "unknown"` → `status: "unavailable"` (no shape, no ratios).
|
|
56
|
+
* - `classification: "single" | "mixed"` → `status: "measured"` with ratios.
|
|
57
|
+
* - `analysisQuality` (0..1) is scaled to `qualityScore` (0..100).
|
|
58
|
+
* - `faceWidthToHeight` (W/H) is converted to `faceAspectRatio` (H/W).
|
|
59
|
+
*
|
|
60
|
+
* The result is fully serialisable and carries no MediaPipe references.
|
|
61
|
+
*/
|
|
62
|
+
export declare function toFaceGeometryAnalysis(result: FaceShapeResult): SiteFaceGeometryAnalysis;
|
|
63
|
+
/**
|
|
64
|
+
* Build the ratio block from SDK metrics, applying the W/H → H/W conversion.
|
|
65
|
+
*
|
|
66
|
+
* Returns `undefined` when the essential raw measurements are missing or
|
|
67
|
+
* non-finite, so the main site's `normalizeGeometryAnalysis()` will treat
|
|
68
|
+
* the result as `unavailable`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildRatios(metrics: FaceMetrics): SiteFaceGeometryRatios | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Lightweight classification-to-status helper for callers that only need the
|
|
73
|
+
* status flag without building the full analysis object.
|
|
74
|
+
*/
|
|
75
|
+
export declare function classificationToStatus(classification: Classification): "measured" | "unavailable";
|
|
76
|
+
//# sourceMappingURL=visutry-site.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visutry-site.d.ts","sourceRoot":"","sources":["../../src/adapter/visutry-site.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAEV,cAAc,EACd,WAAW,EAEX,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAU3B,iEAAiE;AACjE,MAAM,MAAM,aAAa,GACrB,OAAO,GACP,QAAQ,GACR,MAAM,GACN,OAAO,GACP,SAAS,GACT,QAAQ,GACR,UAAU,CAAC;AAEf,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,eAAe,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,8EAA8E;AAC9E,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,UAAU,GAAG,aAAa,CAAC;IACnC,MAAM,EAAE,2BAA2B,GAAG,aAAa,CAAC;IACpD,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAMD,kEAAkE;AAClE,eAAO,MAAM,4BAA4B,UAAU,CAAC;AAqFpD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,eAAe,GACtB,wBAAwB,CAsC1B;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,WAAW,GACnB,sBAAsB,GAAG,SAAS,CAqCpC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,cAAc,GAC7B,UAAU,GAAG,aAAa,CAE5B"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VisuTry main-site adapter.
|
|
3
|
+
*
|
|
4
|
+
* Converts the SDK's honest `FaceShapeResult` into the `landmark-v1`
|
|
5
|
+
* `FaceGeometryAnalysis` contract consumed by the VisuTry main site
|
|
6
|
+
* (`src/types/face-analysis.ts`). This is the SINGLE adapter the spec
|
|
7
|
+
* (§12) requires — the main site must not run a parallel classification
|
|
8
|
+
* path.
|
|
9
|
+
*
|
|
10
|
+
* Key conversions (spec §4.3):
|
|
11
|
+
* - SDK `faceWidthToHeight` (W/H) → site `faceAspectRatio` (H/W), i.e. reciprocal.
|
|
12
|
+
* - SDK `analysisQuality` (0..1) → site `qualityScore` (0..100 integer).
|
|
13
|
+
* - SDK `matchStrength` (0..1) → site `measuredConfidence` (kept as-is;
|
|
14
|
+
* the field name is legacy but the value is match strength, NOT a probability).
|
|
15
|
+
* - SDK `classification: "unknown"` → site `status: "unavailable"`.
|
|
16
|
+
*/
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Adapter version
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/** Bumped when the mapping logic or mirrored contract changes. */
|
|
21
|
+
export const VISUTRY_SITE_ADAPTER_VERSION = "1.0.0";
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
function round(value, digits = 3) {
|
|
26
|
+
if (!Number.isFinite(value))
|
|
27
|
+
return 0;
|
|
28
|
+
const factor = 10 ** digits;
|
|
29
|
+
return Math.round(value * factor) / factor;
|
|
30
|
+
}
|
|
31
|
+
function clamp(value, min, max) {
|
|
32
|
+
return Math.min(max, Math.max(min, value));
|
|
33
|
+
}
|
|
34
|
+
/** Map SDK FaceShape (7 values) to site SiteFaceShape (same 7 values). */
|
|
35
|
+
function toSiteShape(shape) {
|
|
36
|
+
if (!shape)
|
|
37
|
+
return undefined;
|
|
38
|
+
return shape;
|
|
39
|
+
}
|
|
40
|
+
/** Human-readable warning strings matching the main-site style. */
|
|
41
|
+
const WARNING_MESSAGES = {
|
|
42
|
+
LOW_QUALITY: "Photo quality is too low for reliable face-shape measurement.",
|
|
43
|
+
AMBIGUOUS_MATCH: "The face shape is ambiguous — features match multiple shapes similarly.",
|
|
44
|
+
UNSTABLE_FRAMES: "Frame-to-frame measurements were unstable; try holding still.",
|
|
45
|
+
EXTREME_EXPRESSION: "An extreme expression was detected; use a neutral face for best results.",
|
|
46
|
+
MISSING_KEY_POINTS: "Some key facial reference points were missing or unreliable.",
|
|
47
|
+
NON_FRONTAL: "The face appears turned; use a straight-on photo.",
|
|
48
|
+
FACE_TOO_SMALL: "The face is too small in the photo. Move closer and keep the full face visible.",
|
|
49
|
+
MULTIPLE_FACES: "Multiple faces were detected. Use a photo with exactly one face.",
|
|
50
|
+
};
|
|
51
|
+
function mapWarnings(warnings) {
|
|
52
|
+
return warnings.map((w) => WARNING_MESSAGES[w] ?? w);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build human-readable geometry signals from metrics, mirroring the main
|
|
56
|
+
* site's `buildGeometrySignals()` so the report text stays consistent.
|
|
57
|
+
*/
|
|
58
|
+
function buildSignals(shape, metrics) {
|
|
59
|
+
const signals = [];
|
|
60
|
+
if (shape) {
|
|
61
|
+
const label = shape[0].toUpperCase() + shape.slice(1);
|
|
62
|
+
signals.push(`${label} shape supported by measured proportions`);
|
|
63
|
+
}
|
|
64
|
+
const wth = metrics.faceWidthToHeight;
|
|
65
|
+
signals.push(wth < 0.83
|
|
66
|
+
? "Longer vertical face proportion"
|
|
67
|
+
: wth > 0.93
|
|
68
|
+
? "Compact face length relative to width"
|
|
69
|
+
: "Balanced face length-to-width ratio");
|
|
70
|
+
const jcr = metrics.jawCheekRatio;
|
|
71
|
+
signals.push(jcr >= 0.94
|
|
72
|
+
? "Jaw width is close to cheekbone width"
|
|
73
|
+
: jcr <= 0.78
|
|
74
|
+
? "Jawline tapers below the cheekbones"
|
|
75
|
+
: "Jawline has moderate taper");
|
|
76
|
+
const fcr = metrics.foreheadCheekRatio ?? 0;
|
|
77
|
+
signals.push(fcr >= 0.9
|
|
78
|
+
? "Forehead width is close to cheekbone width"
|
|
79
|
+
: "Cheekbones read wider than the upper face");
|
|
80
|
+
return signals;
|
|
81
|
+
}
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Public API
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
/**
|
|
86
|
+
* Convert an SDK `FaceShapeResult` into the main-site `landmark-v1`
|
|
87
|
+
* `FaceGeometryAnalysis` contract.
|
|
88
|
+
*
|
|
89
|
+
* - `classification: "unknown"` → `status: "unavailable"` (no shape, no ratios).
|
|
90
|
+
* - `classification: "single" | "mixed"` → `status: "measured"` with ratios.
|
|
91
|
+
* - `analysisQuality` (0..1) is scaled to `qualityScore` (0..100).
|
|
92
|
+
* - `faceWidthToHeight` (W/H) is converted to `faceAspectRatio` (H/W).
|
|
93
|
+
*
|
|
94
|
+
* The result is fully serialisable and carries no MediaPipe references.
|
|
95
|
+
*/
|
|
96
|
+
export function toFaceGeometryAnalysis(result) {
|
|
97
|
+
const { classification, metrics, warnings, analysisQuality, matchStrength } = result;
|
|
98
|
+
const isUnavailable = classification === "unknown";
|
|
99
|
+
// qualityScore: 0..1 → 0..100 integer, clamped.
|
|
100
|
+
const qualityScore = Math.round(clamp(analysisQuality * 100, 0, 100));
|
|
101
|
+
if (isUnavailable) {
|
|
102
|
+
return {
|
|
103
|
+
version: "landmark-v1",
|
|
104
|
+
status: "unavailable",
|
|
105
|
+
source: "mediapipe-face-landmarker",
|
|
106
|
+
faceDetected: true,
|
|
107
|
+
faceCount: 1,
|
|
108
|
+
qualityScore,
|
|
109
|
+
signals: [],
|
|
110
|
+
warnings: mapWarnings(warnings),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const ratios = buildRatios(metrics);
|
|
114
|
+
const measuredShape = toSiteShape(result.primary);
|
|
115
|
+
return {
|
|
116
|
+
version: "landmark-v1",
|
|
117
|
+
status: "measured",
|
|
118
|
+
source: "mediapipe-face-landmarker",
|
|
119
|
+
faceDetected: true,
|
|
120
|
+
faceCount: 1,
|
|
121
|
+
qualityScore,
|
|
122
|
+
measuredShape,
|
|
123
|
+
measuredConfidence: round(matchStrength, 2),
|
|
124
|
+
ratios,
|
|
125
|
+
signals: buildSignals(measuredShape, metrics),
|
|
126
|
+
warnings: mapWarnings(warnings),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build the ratio block from SDK metrics, applying the W/H → H/W conversion.
|
|
131
|
+
*
|
|
132
|
+
* Returns `undefined` when the essential raw measurements are missing or
|
|
133
|
+
* non-finite, so the main site's `normalizeGeometryAnalysis()` will treat
|
|
134
|
+
* the result as `unavailable`.
|
|
135
|
+
*/
|
|
136
|
+
export function buildRatios(metrics) {
|
|
137
|
+
const { faceWidth, faceHeight, cheekboneWidth } = metrics;
|
|
138
|
+
if (!Number.isFinite(faceWidth) ||
|
|
139
|
+
!Number.isFinite(faceHeight) ||
|
|
140
|
+
!Number.isFinite(cheekboneWidth) ||
|
|
141
|
+
faceWidth <= 0 ||
|
|
142
|
+
faceHeight <= 0 ||
|
|
143
|
+
cheekboneWidth <= 0) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
const faceAspectRatio = round(faceHeight / faceWidth); // H/W — reciprocal
|
|
147
|
+
const cheekToFaceWidth = round(cheekboneWidth / faceWidth);
|
|
148
|
+
const jawToCheekWidth = round(metrics.jawCheekRatio);
|
|
149
|
+
const foreheadToCheekWidth = metrics.foreheadCheekRatio != null && Number.isFinite(metrics.foreheadCheekRatio)
|
|
150
|
+
? round(metrics.foreheadCheekRatio)
|
|
151
|
+
: round((metrics.foreheadWidth ?? 0) / cheekboneWidth);
|
|
152
|
+
const noseBridgeToFaceWidth = metrics.noseBridgeRatio != null && Number.isFinite(metrics.noseBridgeRatio)
|
|
153
|
+
? round(metrics.noseBridgeRatio)
|
|
154
|
+
: 0;
|
|
155
|
+
return {
|
|
156
|
+
faceAspectRatio,
|
|
157
|
+
cheekToFaceWidth,
|
|
158
|
+
jawToCheekWidth,
|
|
159
|
+
foreheadToCheekWidth,
|
|
160
|
+
eyeLineTiltDeg: round(metrics.eyeLineTiltDeg ?? 0, 1),
|
|
161
|
+
symmetryOffset: round(metrics.symmetryOffset ?? 0),
|
|
162
|
+
noseBridgeToFaceWidth,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Lightweight classification-to-status helper for callers that only need the
|
|
167
|
+
* status flag without building the full analysis object.
|
|
168
|
+
*/
|
|
169
|
+
export function classificationToStatus(classification) {
|
|
170
|
+
return classification === "unknown" ? "unavailable" : "measured";
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=visutry-site.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visutry-site.js","sourceRoot":"","sources":["../../src/adapter/visutry-site.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics event types for face-shape analysis (spec §12).
|
|
3
|
+
*
|
|
4
|
+
* The host application wires these events into its analytics pipeline
|
|
5
|
+
* (e.g. GA4). Events carry algorithm metadata and outcome signals only —
|
|
6
|
+
* NEVER photos, base64, landmarks, or any reversible biometric data.
|
|
7
|
+
*/
|
|
8
|
+
import type { AnalysisWarning, Classification, FaceShape } from "./types/index.js";
|
|
9
|
+
export type FaceAnalysisEventType = "start" | "complete" | "failed" | "abstained" | "retry";
|
|
10
|
+
/** Common fields present on every analytics event. */
|
|
11
|
+
interface AnalyticsEventBase {
|
|
12
|
+
/** Event type. */
|
|
13
|
+
type: FaceAnalysisEventType;
|
|
14
|
+
/** UTC epoch milliseconds. */
|
|
15
|
+
timestamp: number;
|
|
16
|
+
/** Input method. */
|
|
17
|
+
inputKind: "image" | "video";
|
|
18
|
+
/** Classification engine that produced (or attempted) the result. */
|
|
19
|
+
engine: "rules" | "logreg";
|
|
20
|
+
/** Algorithm version from `FaceShapeResult.model.version`. */
|
|
21
|
+
algorithmVersion: string;
|
|
22
|
+
/** SDK package version for audit / rollback tracking. */
|
|
23
|
+
sdkVersion?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Emitted when an analysis session begins. */
|
|
26
|
+
export interface FaceAnalysisStartEvent extends AnalyticsEventBase {
|
|
27
|
+
type: "start";
|
|
28
|
+
}
|
|
29
|
+
/** Emitted when analysis completes with a usable result. */
|
|
30
|
+
export interface FaceAnalysisCompleteEvent extends AnalyticsEventBase {
|
|
31
|
+
type: "complete";
|
|
32
|
+
classification: Classification;
|
|
33
|
+
primary?: FaceShape;
|
|
34
|
+
secondary?: FaceShape;
|
|
35
|
+
/** 0..1 — analysis quality (NOT a probability). */
|
|
36
|
+
analysisQuality: number;
|
|
37
|
+
/** 0..1 — match strength of the primary shape. */
|
|
38
|
+
matchStrength: number;
|
|
39
|
+
/** Wall-clock duration from start to complete, in milliseconds. */
|
|
40
|
+
durationMs: number;
|
|
41
|
+
warnings: AnalysisWarning[];
|
|
42
|
+
}
|
|
43
|
+
/** Emitted when analysis fails with an error (no result produced). */
|
|
44
|
+
export interface FaceAnalysisFailedEvent extends AnalyticsEventBase {
|
|
45
|
+
type: "failed";
|
|
46
|
+
/** Wall-clock duration from start to failure. */
|
|
47
|
+
durationMs: number;
|
|
48
|
+
/** SDK error code. */
|
|
49
|
+
errorCode: string;
|
|
50
|
+
/** Short, non-PII error message. */
|
|
51
|
+
errorMessage: string;
|
|
52
|
+
}
|
|
53
|
+
/** Emitted when the SDK abstains (`classification: "unknown"`). */
|
|
54
|
+
export interface FaceAnalysisAbstainedEvent extends AnalyticsEventBase {
|
|
55
|
+
type: "abstained";
|
|
56
|
+
/** 0..1 — analysis quality at the point of abstention. */
|
|
57
|
+
analysisQuality: number;
|
|
58
|
+
/** Wall-clock duration from start to abstention. */
|
|
59
|
+
durationMs: number;
|
|
60
|
+
warnings: AnalysisWarning[];
|
|
61
|
+
}
|
|
62
|
+
/** Emitted when the user retries after a failure or abstention. */
|
|
63
|
+
export interface FaceAnalysisRetryEvent extends AnalyticsEventBase {
|
|
64
|
+
type: "retry";
|
|
65
|
+
/** The event type that preceded this retry. */
|
|
66
|
+
previousOutcome: "failed" | "abstained";
|
|
67
|
+
}
|
|
68
|
+
export type FaceAnalysisAnalyticsEvent = FaceAnalysisStartEvent | FaceAnalysisCompleteEvent | FaceAnalysisFailedEvent | FaceAnalysisAbstainedEvent | FaceAnalysisRetryEvent;
|
|
69
|
+
/**
|
|
70
|
+
* Callback that receives analytics events. The host application implements
|
|
71
|
+
* this to forward events to GA4, Axiom, etc.
|
|
72
|
+
*/
|
|
73
|
+
export type AnalyticsReporter = (event: FaceAnalysisAnalyticsEvent) => void;
|
|
74
|
+
/**
|
|
75
|
+
* Create a no-op reporter (default when analytics is disabled).
|
|
76
|
+
* Useful as a safe default in tests and when `privacy.allowAnalytics` is false.
|
|
77
|
+
*/
|
|
78
|
+
export declare function noopReporter(): AnalyticsReporter;
|
|
79
|
+
/**
|
|
80
|
+
* Create a reporter that buffers events and flushes them via the provided
|
|
81
|
+
* sink. Useful for batching in production.
|
|
82
|
+
*/
|
|
83
|
+
export declare function createBufferedReporter(sink: (events: FaceAnalysisAnalyticsEvent[]) => void, flushIntervalMs?: number): {
|
|
84
|
+
reporter: AnalyticsReporter;
|
|
85
|
+
flush: () => void;
|
|
86
|
+
destroy: () => void;
|
|
87
|
+
};
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAMnF,MAAM,MAAM,qBAAqB,GAC7B,OAAO,GACP,UAAU,GACV,QAAQ,GACR,WAAW,GACX,OAAO,CAAC;AAEZ,sDAAsD;AACtD,UAAU,kBAAkB;IAC1B,kBAAkB;IAClB,IAAI,EAAE,qBAAqB,CAAC;IAC5B,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7B,qEAAqE;IACrE,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC3B,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,+CAA+C;AAC/C,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,IAAI,EAAE,OAAO,CAAC;CACf;AAED,4DAA4D;AAC5D,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE,IAAI,EAAE,UAAU,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,mDAAmD;IACnD,eAAe,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,sEAAsE;AACtE,MAAM,WAAW,uBAAwB,SAAQ,kBAAkB;IACjE,IAAI,EAAE,QAAQ,CAAC;IACf,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,mEAAmE;AACnE,MAAM,WAAW,0BAA2B,SAAQ,kBAAkB;IACpE,IAAI,EAAE,WAAW,CAAC;IAClB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;IACxB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,mEAAmE;AACnE,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,IAAI,EAAE,OAAO,CAAC;IACd,+CAA+C;IAC/C,eAAe,EAAE,QAAQ,GAAG,WAAW,CAAC;CACzC;AAED,MAAM,MAAM,0BAA0B,GAClC,sBAAsB,GACtB,yBAAyB,GACzB,uBAAuB,GACvB,0BAA0B,GAC1B,sBAAsB,CAAC;AAM3B;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,YAAY,IAAI,iBAAiB,CAIhD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,CAAC,MAAM,EAAE,0BAA0B,EAAE,KAAK,IAAI,EACpD,eAAe,SAAO,GACrB;IAAE,QAAQ,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA4BzE"}
|