@traqio/core 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Khufu FZCO
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,70 @@
1
- # Temporary Holding Version
1
+ # @traqio/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The platform-neutral JavaScript engine of the [Traqio](https://traqio.app) SDKs:
4
+ a persisted offline event queue with batching and retries, SKAdNetwork conversion
5
+ values, Apple Ads attribution and deep-link resolution. No advertising id (IDFA /
6
+ GAID) unless the host opts in (`collectAdvertisingId`, read through the native bridge,
7
+ never prompting), no fingerprinting, no pasteboard — the device id is a random
8
+ first-party id.
9
+
10
+ It has no React Native, Expo or browser dependency: everything platform-specific
11
+ (the Google Play Install Referrer, SKAdNetwork) arrives through a native bridge.
12
+ **Most apps should not install it directly** — install the package for your
13
+ platform, which includes it:
14
+
15
+ | Your app | Install |
16
+ | --- | --- |
17
+ | Expo | `@traqio/expo` |
18
+ | React Native | `@traqio/react-native` |
19
+ | Capacitor / Ionic | `@traqio/capacitor` |
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install @traqio/core
25
+ ```
26
+
27
+ ## Initialize
28
+
29
+ ```ts
30
+ import { Traqio } from "@traqio/core";
31
+
32
+ await Traqio.init({
33
+ sdkKey: "YOUR_SDK_KEY", // from the Traqio console — public by design
34
+ endpoint: "https://api.traqio.app", // origin only
35
+ storage, // { getItem, setItem } — makes the install fire once
36
+ native, // a NativeBridge; without one there is no attribution
37
+ });
38
+ ```
39
+
40
+ ## Track events
41
+
42
+ ```ts
43
+ Traqio.setUserId(user.id); // the same id you give RevenueCat (app_user_id)
44
+
45
+ Traqio.track("purchase", {
46
+ value: 9.99,
47
+ currency: "EUR",
48
+ transactionId: purchase.transactionId, // the store's id: counts the purchase once
49
+ });
50
+ ```
51
+
52
+ Use the event names Meta and TikTok can optimise on (`purchase`, `signup`,
53
+ `subscribe`, `trial`, `add_to_cart`, `checkout`…) — any other name is stored but
54
+ reaches the networks as a custom event.
55
+
56
+ ## Requirements
57
+
58
+ - A JavaScript runtime with `fetch`.
59
+ - Persistent storage passed as `storage` (React Native's AsyncStorage is picked up
60
+ automatically when installed).
61
+
62
+ ## Documentation
63
+
64
+ The full API reference — every `init` option, `trackAdRevenue`, `setConsent`,
65
+ `forgetMe`, deep links, the native bridge — is at
66
+ **https://traqio.app/docs/sdks/javascript**.
67
+
68
+ ## License
69
+
70
+ MIT
@@ -0,0 +1,31 @@
1
+ /** Native surface the Expo module exposes. Absent on Android and pre-iOS 14.3. */
2
+ export interface AdServicesNative {
3
+ /** The opaque 24h token, or null when the framework has none on this device. */
4
+ getAttributionToken(): Promise<string | null>;
5
+ }
6
+ export interface AdServicesOptions {
7
+ endpoint: string;
8
+ sdkKey: string;
9
+ deviceId?: string;
10
+ userId?: string;
11
+ native?: AdServicesNative;
12
+ /** Same store the event queue uses, so "already sent" survives a restart. */
13
+ storage?: {
14
+ getItem(key: string): Promise<string | null>;
15
+ setItem(key: string, value: string): Promise<void>;
16
+ };
17
+ /** Injectable for tests; defaults to the global fetch. */
18
+ fetchFn?: typeof fetch;
19
+ }
20
+ export declare const ADSERVICES_SENT_KEY = "traq_adservices_sent";
21
+ export declare const ADSERVICES_ATTEMPTS_KEY = "traq_adservices_attempts";
22
+ /**
23
+ * Hand the token over, at most once per install.
24
+ *
25
+ * Returns what happened, so a host app can debug the integration without reading our
26
+ * logs. Never throws: there is no user-visible consequence to any outcome here, and
27
+ * an exception in `init()` would take the whole SDK down for a signal most installs
28
+ * do not have.
29
+ */
30
+ export declare function reportAdServicesToken(opts: AdServicesOptions): Promise<"sent" | "already-sent" | "no-token" | "unsupported" | "post-failed">;
31
+ //# sourceMappingURL=adservices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adservices.d.ts","sourceRoot":"","sources":["../src/adservices.ts"],"names":[],"mappings":"AAmBA,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,6EAA6E;IAC7E,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAC/G,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAC1D,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAUlE;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC,CA2C/E"}
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ // Apple Ads (Search Ads) attribution — the JS half.
3
+ //
4
+ // What it buys: the one DETERMINISTIC attribution iOS still allows. Every other iOS
5
+ // signal is SKAdNetwork, which is aggregated, six bits wide and 24h+ late. Apple
6
+ // Search Ads self-attributes through the AdServices framework, and any developer may
7
+ // resolve their own app's tokens — no partner programme, no allowlist.
8
+ //
9
+ // What it is NOT: an identifier. `AAAttribution.attributionToken()` returns an
10
+ // opaque, single-purpose claim ticket valid for **24 hours**. No IDFA, no ATT prompt
11
+ // of our own, no fingerprinting. Apple decides how much of the answer to return from
12
+ // the ATT status the user already chose — detailed with consent, standard without.
13
+ //
14
+ // Why the SERVER resolves it: Apple answers 404 until the token is known on its side,
15
+ // which right after an install is the normal first answer. That retry has to outlive
16
+ // the app process, so the device's only job is to hand the token over before it
17
+ // expires. See apps/api/src/apple-ads/.
18
+ //
19
+ // https://developer.apple.com/documentation/adservices
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.ADSERVICES_ATTEMPTS_KEY = exports.ADSERVICES_SENT_KEY = void 0;
22
+ exports.reportAdServicesToken = reportAdServicesToken;
23
+ exports.ADSERVICES_SENT_KEY = "traq_adservices_sent";
24
+ exports.ADSERVICES_ATTEMPTS_KEY = "traq_adservices_attempts";
25
+ /**
26
+ * How many launches we keep asking before giving up on this install. The framework
27
+ * can answer later in an install's life, but on a device where it never will —
28
+ * a simulator, an install with no attribution record at all, which is MOST installs —
29
+ * asking forever is work with no answer to it.
30
+ */
31
+ const MAX_ATTEMPTS = 5;
32
+ /**
33
+ * Hand the token over, at most once per install.
34
+ *
35
+ * Returns what happened, so a host app can debug the integration without reading our
36
+ * logs. Never throws: there is no user-visible consequence to any outcome here, and
37
+ * an exception in `init()` would take the whole SDK down for a signal most installs
38
+ * do not have.
39
+ */
40
+ async function reportAdServicesToken(opts) {
41
+ if (!opts.native?.getAttributionToken)
42
+ return "unsupported";
43
+ if (await read(opts, exports.ADSERVICES_SENT_KEY))
44
+ return "already-sent";
45
+ let token = null;
46
+ try {
47
+ token = await opts.native.getAttributionToken();
48
+ }
49
+ catch {
50
+ // The framework throws on a device with nothing to give. Treated as "no token".
51
+ }
52
+ if (!token || !token.trim()) {
53
+ const attempts = Number(await read(opts, exports.ADSERVICES_ATTEMPTS_KEY)) || 0;
54
+ await write(opts, exports.ADSERVICES_ATTEMPTS_KEY, String(attempts + 1));
55
+ // Bounded: stop asking a device that will never answer.
56
+ if (attempts + 1 >= MAX_ATTEMPTS)
57
+ await write(opts, exports.ADSERVICES_SENT_KEY, "1");
58
+ return "no-token";
59
+ }
60
+ const doFetch = opts.fetchFn ?? (typeof fetch === "function" ? fetch : undefined);
61
+ if (!doFetch)
62
+ return "post-failed";
63
+ try {
64
+ const res = await doFetch(`${opts.endpoint}/ingest/adservices`, {
65
+ method: "POST",
66
+ headers: { "Content-Type": "application/json" },
67
+ body: JSON.stringify({
68
+ sdkKey: opts.sdkKey,
69
+ token: token.trim(),
70
+ deviceId: opts.deviceId,
71
+ appUserId: opts.userId,
72
+ }),
73
+ });
74
+ if (!res.ok)
75
+ return "post-failed";
76
+ }
77
+ catch {
78
+ return "post-failed";
79
+ }
80
+ // Only mark sent on success, so a failed post retries on the next launch — inside
81
+ // the 24h window, which is what makes the retry worth anything.
82
+ await write(opts, exports.ADSERVICES_SENT_KEY, "1");
83
+ return "sent";
84
+ }
85
+ async function read(opts, key) {
86
+ try {
87
+ return (await opts.storage?.getItem(key)) ?? null;
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ async function write(opts, key, value) {
94
+ try {
95
+ await opts.storage?.setItem(key, value);
96
+ }
97
+ catch {
98
+ /* storage full/unavailable */
99
+ }
100
+ }
101
+ //# sourceMappingURL=adservices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adservices.js","sourceRoot":"","sources":["../src/adservices.ts"],"names":[],"mappings":";AAAA,oDAAoD;AACpD,EAAE;AACF,oFAAoF;AACpF,iFAAiF;AACjF,qFAAqF;AACrF,uEAAuE;AACvE,EAAE;AACF,+EAA+E;AAC/E,qFAAqF;AACrF,qFAAqF;AACrF,mFAAmF;AACnF,EAAE;AACF,sFAAsF;AACtF,qFAAqF;AACrF,gFAAgF;AAChF,wCAAwC;AACxC,EAAE;AACF,uDAAuD;;;AAuCvD,sDA6CC;AAhEY,QAAA,mBAAmB,GAAG,sBAAsB,CAAC;AAC7C,QAAA,uBAAuB,GAAG,0BAA0B,CAAC;AAElE;;;;;GAKG;AACH,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB;;;;;;;GAOG;AACI,KAAK,UAAU,qBAAqB,CACzC,IAAuB;IAEvB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB;QAAE,OAAO,aAAa,CAAC;IAE5D,IAAI,MAAM,IAAI,CAAC,IAAI,EAAE,2BAAmB,CAAC;QAAE,OAAO,cAAc,CAAC;IAEjE,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,gFAAgF;IAClF,CAAC;IAED,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,+BAAuB,CAAC,CAAC,IAAI,CAAC,CAAC;QACxE,MAAM,KAAK,CAAC,IAAI,EAAE,+BAAuB,EAAE,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC;QACjE,wDAAwD;QACxD,IAAI,QAAQ,GAAG,CAAC,IAAI,YAAY;YAAE,MAAM,KAAK,CAAC,IAAI,EAAE,2BAAmB,EAAE,GAAG,CAAC,CAAC;QAC9E,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAClF,IAAI,CAAC,OAAO;QAAE,OAAO,aAAa,CAAC;IAEnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,oBAAoB,EAAE;YAC9D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,SAAS,EAAE,IAAI,CAAC,MAAM;aACvB,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,aAAa,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,kFAAkF;IAClF,gEAAgE;IAChE,MAAM,KAAK,CAAC,IAAI,EAAE,2BAAmB,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,IAAuB,EAAE,GAAW;IACtD,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,KAAK,CAAC,IAAuB,EAAE,GAAW,EAAE,KAAa;IACtE,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ /** Where a link wants the app to land. */
2
+ export interface DeepLink {
3
+ /**
4
+ * The destination set on the link in the console — an in-app path
5
+ * ("/product/42") or an app URL ("myapp://product/42"). Null when the link
6
+ * carries none: the app still learns WHICH link brought the user (`code`).
7
+ */
8
+ deepLink: string | null;
9
+ /** The link's slug (the `<code>` of `/c/<code>`). */
10
+ code: string | null;
11
+ /** True when the link was tapped BEFORE the install; false when it opened the app. */
12
+ deferred: boolean;
13
+ /** How it was resolved. `ip_match` is a probabilistic guess — see the docs. */
14
+ via: "referrer" | "ip_match" | "link";
15
+ /** The URL that opened the app (direct opens only). */
16
+ url?: string;
17
+ }
18
+ /** What `/deeplink/deferred` and `/deeplink/open` answer. */
19
+ export interface DeepLinkAnswer {
20
+ matched: boolean;
21
+ via: "referrer" | "ip_match" | "link" | null;
22
+ code: string | null;
23
+ deepLink: string | null;
24
+ }
25
+ /** True when `url` is one of our tracking links (`http(s)://<host>/c/<code>`). */
26
+ export declare function isTrackingUrl(url: string): boolean;
27
+ /** An API answer → the callback's shape, or null when nothing matched. */
28
+ export declare function toDeepLink(answer: DeepLinkAnswer | null, deferred: boolean, url?: string): DeepLink | null;
29
+ //# sourceMappingURL=deeplink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deeplink.d.ts","sourceRoot":"","sources":["../src/deeplink.ts"],"names":[],"mappings":"AAcA,0CAA0C;AAC1C,MAAM,WAAW,QAAQ;IACvB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qDAAqD;IACrD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,sFAAsF;IACtF,QAAQ,EAAE,OAAO,CAAC;IAClB,+EAA+E;IAC/E,GAAG,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;IACtC,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAMD,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGlD;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAG1G"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ // Deep linking — the SDK half (docs/DEEP_LINKING.md).
3
+ //
4
+ // Two moments, one callback:
5
+ // - DIRECT: a universal link / App Link (`https://<your host>/c/<code>`) opened
6
+ // the installed app. The OS never hit our server, so the SDK resolves the
7
+ // code — which also records the click the OS skipped.
8
+ // - DEFERRED: the app was installed FROM a link. On the very first launch the
9
+ // SDK asks where that link wanted the user to land. Android answers from the
10
+ // Play Install Referrer (deterministic); iOS only when the app opted into the
11
+ // IP match in the console (probabilistic, off by default).
12
+ //
13
+ // Privacy: nothing new leaves the device. The referrer is already sent with the
14
+ // install; the URL is the one that opened the app. No pasteboard, no IDFA.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.isTrackingUrl = isTrackingUrl;
17
+ exports.toDeepLink = toDeepLink;
18
+ // Mirrors `parseTrackingUrl` in apps/api/src/tracking/link-rules.ts. Checked on
19
+ // the device so the app's OWN URLs (most of what opens an app) never leave it.
20
+ const TRACKING_PATH = /^\/c\/[A-Za-z0-9_-]{1,50}\/?$/;
21
+ /** True when `url` is one of our tracking links (`http(s)://<host>/c/<code>`). */
22
+ function isTrackingUrl(url) {
23
+ const m = /^https?:\/\/[^/?#]+(\/[^?#]*)?/i.exec(url);
24
+ return Boolean(m && TRACKING_PATH.test(m[1] ?? ""));
25
+ }
26
+ /** An API answer → the callback's shape, or null when nothing matched. */
27
+ function toDeepLink(answer, deferred, url) {
28
+ if (!answer?.matched || !answer.via)
29
+ return null;
30
+ return { deepLink: answer.deepLink, code: answer.code, deferred, via: answer.via, ...(url ? { url } : {}) };
31
+ }
32
+ //# sourceMappingURL=deeplink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deeplink.js","sourceRoot":"","sources":["../src/deeplink.ts"],"names":[],"mappings":";AAAA,sDAAsD;AACtD,EAAE;AACF,6BAA6B;AAC7B,kFAAkF;AAClF,8EAA8E;AAC9E,0DAA0D;AAC1D,gFAAgF;AAChF,iFAAiF;AACjF,kFAAkF;AAClF,+DAA+D;AAC/D,EAAE;AACF,gFAAgF;AAChF,2EAA2E;;AAiC3E,sCAGC;AAGD,gCAGC;AAdD,gFAAgF;AAChF,+EAA+E;AAC/E,MAAM,aAAa,GAAG,+BAA+B,CAAC;AAEtD,kFAAkF;AAClF,SAAgB,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,GAAG,iCAAiC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,0EAA0E;AAC1E,SAAgB,UAAU,CAAC,MAA6B,EAAE,QAAiB,EAAE,GAAY;IACvF,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9G,CAAC"}