@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 +21 -0
- package/README.md +69 -2
- package/build/adservices.d.ts +31 -0
- package/build/adservices.d.ts.map +1 -0
- package/build/adservices.js +101 -0
- package/build/adservices.js.map +1 -0
- package/build/deeplink.d.ts +29 -0
- package/build/deeplink.d.ts.map +1 -0
- package/build/deeplink.js +32 -0
- package/build/deeplink.js.map +1 -0
- package/build/index.d.ts +406 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +897 -0
- package/build/index.js.map +1 -0
- package/build/skan.d.ts +125 -0
- package/build/skan.d.ts.map +1 -0
- package/build/skan.js +243 -0
- package/build/skan.js.map +1 -0
- package/package.json +47 -3
- package/src/adservices.ts +118 -0
- package/src/deeplink.ts +55 -0
- package/src/index.ts +1149 -0
- package/src/skan.ts +308 -0
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
|
-
#
|
|
1
|
+
# @traqio/core
|
|
2
2
|
|
|
3
|
-
|
|
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":"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"}
|
|
@@ -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"}
|