@apex-inc/capacitor-plugin 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/ApexCapacitorPlugin.podspec +17 -0
- package/LICENSE +17 -0
- package/README.md +136 -0
- package/android/build.gradle +68 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/inc/apex/capacitor/ApexCapacitorPlugin.kt +325 -0
- package/android/src/main/java/inc/apex/capacitor/DeepLinkManager.kt +47 -0
- package/android/src/main/java/inc/apex/capacitor/InstallReferrerParser.kt +123 -0
- package/android/src/main/java/inc/apex/capacitor/OfflineQueue.kt +150 -0
- package/android/src/main/java/inc/apex/capacitor/SessionManager.kt +108 -0
- package/dist/batch-sender.d.ts +60 -0
- package/dist/batch-sender.d.ts.map +1 -0
- package/dist/batch-sender.js +115 -0
- package/dist/batch-sender.js.map +1 -0
- package/dist/definitions.d.ts +224 -0
- package/dist/definitions.d.ts.map +1 -0
- package/dist/definitions.js +14 -0
- package/dist/definitions.js.map +1 -0
- package/dist/esm/batch-sender.d.ts +60 -0
- package/dist/esm/batch-sender.d.ts.map +1 -0
- package/dist/esm/batch-sender.js +111 -0
- package/dist/esm/batch-sender.js.map +1 -0
- package/dist/esm/definitions.d.ts +224 -0
- package/dist/esm/definitions.d.ts.map +1 -0
- package/dist/esm/definitions.js +13 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/event-id.d.ts +17 -0
- package/dist/esm/event-id.d.ts.map +1 -0
- package/dist/esm/event-id.js +57 -0
- package/dist/esm/event-id.js.map +1 -0
- package/dist/esm/index.d.ts +29 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +30 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/offline-queue.d.ts +111 -0
- package/dist/esm/offline-queue.d.ts.map +1 -0
- package/dist/esm/offline-queue.js +240 -0
- package/dist/esm/offline-queue.js.map +1 -0
- package/dist/esm/session-manager.d.ts +63 -0
- package/dist/esm/session-manager.d.ts.map +1 -0
- package/dist/esm/session-manager.js +100 -0
- package/dist/esm/session-manager.js.map +1 -0
- package/dist/esm/web.d.ts +65 -0
- package/dist/esm/web.d.ts.map +1 -0
- package/dist/esm/web.js +203 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/event-id.d.ts +17 -0
- package/dist/event-id.d.ts.map +1 -0
- package/dist/event-id.js +61 -0
- package/dist/event-id.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/offline-queue.d.ts +111 -0
- package/dist/offline-queue.d.ts.map +1 -0
- package/dist/offline-queue.js +246 -0
- package/dist/offline-queue.js.map +1 -0
- package/dist/session-manager.d.ts +63 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +104 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/web.d.ts +65 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +207 -0
- package/dist/web.js.map +1 -0
- package/ios/Package.swift +34 -0
- package/ios/Sources/ApexCapacitorPlugin/AdvertisingIdProvider.swift +66 -0
- package/ios/Sources/ApexCapacitorPlugin/AttManager.swift +82 -0
- package/ios/Sources/ApexCapacitorPlugin/DeepLinkManager.swift +64 -0
- package/ios/Sources/ApexCapacitorPlugin/DeviceInfo.swift +107 -0
- package/ios/Sources/ApexCapacitorPlugin/OfflineQueue.swift +191 -0
- package/ios/Sources/ApexCapacitorPlugin/SessionManager.swift +113 -0
- package/ios/Sources/ApexCapacitorPlugin/SkanManager.swift +95 -0
- package/ios/Sources/ApexCapacitorPluginBridge/ApexCapacitorPlugin.swift +269 -0
- package/ios/Tests/ApexCapacitorPluginTests/AdvertisingIdProviderTests.swift +74 -0
- package/ios/Tests/ApexCapacitorPluginTests/AttManagerTests.swift +82 -0
- package/ios/Tests/ApexCapacitorPluginTests/DeepLinkManagerTests.swift +69 -0
- package/ios/Tests/ApexCapacitorPluginTests/DeviceInfoTests.swift +52 -0
- package/ios/Tests/ApexCapacitorPluginTests/OfflineQueueTests.swift +134 -0
- package/ios/Tests/ApexCapacitorPluginTests/SessionManagerTests.swift +98 -0
- package/ios/Tests/ApexCapacitorPluginTests/SkanManagerTests.swift +91 -0
- package/package.json +82 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public TypeScript interface for `@apex-inc/capacitor-plugin`.
|
|
3
|
+
*
|
|
4
|
+
* These types define the contract between the JS layer (this package) and
|
|
5
|
+
* the native iOS (Swift, MMP-004) and Android (Kotlin, MMP-005) implementations.
|
|
6
|
+
* Shape changes are breaking after 1.0.0.
|
|
7
|
+
*
|
|
8
|
+
* Pairs with `apex.js` (the web snippet) when loaded inside a Capacitor
|
|
9
|
+
* WebView — shared visitor ID, shared session, shared events via one backend.
|
|
10
|
+
* See `plans/mobile-measurement-platform.md` Phase 1b for scope and rationale.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=definitions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UUIDv4 generator for client-assigned event IDs.
|
|
3
|
+
*
|
|
4
|
+
* Used by the Apex Capacitor plugin to stamp every event with an idempotency
|
|
5
|
+
* key before it hits the network. The server dedupes on this ID within a
|
|
6
|
+
* 24-hour window — without it, mobile retry storms would double-count installs
|
|
7
|
+
* and waste client ad budget.
|
|
8
|
+
*
|
|
9
|
+
* Prefers `crypto.randomUUID()` when available (modern browsers, Node 14.17+,
|
|
10
|
+
* Capacitor WebViews on iOS 14+/Android 10+). Falls back to a manual
|
|
11
|
+
* implementation using `crypto.getRandomValues` for older environments.
|
|
12
|
+
*/
|
|
13
|
+
/** Generates a RFC 4122 v4 UUID. */
|
|
14
|
+
export declare function generateEventId(): string;
|
|
15
|
+
/** Validates a string looks like a UUIDv4. Useful in tests and sanity checks. */
|
|
16
|
+
export declare function isValidEventId(value: string): boolean;
|
|
17
|
+
//# sourceMappingURL=event-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-id.d.ts","sourceRoot":"","sources":["../../src/event-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oCAAoC;AACpC,wBAAgB,eAAe,IAAI,MAAM,CAcxC;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UUIDv4 generator for client-assigned event IDs.
|
|
3
|
+
*
|
|
4
|
+
* Used by the Apex Capacitor plugin to stamp every event with an idempotency
|
|
5
|
+
* key before it hits the network. The server dedupes on this ID within a
|
|
6
|
+
* 24-hour window — without it, mobile retry storms would double-count installs
|
|
7
|
+
* and waste client ad budget.
|
|
8
|
+
*
|
|
9
|
+
* Prefers `crypto.randomUUID()` when available (modern browsers, Node 14.17+,
|
|
10
|
+
* Capacitor WebViews on iOS 14+/Android 10+). Falls back to a manual
|
|
11
|
+
* implementation using `crypto.getRandomValues` for older environments.
|
|
12
|
+
*/
|
|
13
|
+
/** Generates a RFC 4122 v4 UUID. */
|
|
14
|
+
export function generateEventId() {
|
|
15
|
+
const g = globalThis;
|
|
16
|
+
if (g.crypto && typeof g.crypto.randomUUID === "function") {
|
|
17
|
+
return g.crypto.randomUUID();
|
|
18
|
+
}
|
|
19
|
+
if (g.crypto && typeof g.crypto.getRandomValues === "function") {
|
|
20
|
+
return generateUuidFromRandomBytes(g.crypto);
|
|
21
|
+
}
|
|
22
|
+
// Last-resort fallback: Math.random() (not cryptographically secure, but
|
|
23
|
+
// still satisfies the uniqueness requirement at our scale).
|
|
24
|
+
return generateUuidFromMath();
|
|
25
|
+
}
|
|
26
|
+
/** Validates a string looks like a UUIDv4. Useful in tests and sanity checks. */
|
|
27
|
+
export function isValidEventId(value) {
|
|
28
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
29
|
+
}
|
|
30
|
+
function generateUuidFromRandomBytes(crypto) {
|
|
31
|
+
const bytes = new Uint8Array(16);
|
|
32
|
+
crypto.getRandomValues(bytes);
|
|
33
|
+
// Set version (4) and variant (RFC 4122) bits.
|
|
34
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
35
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
36
|
+
const hex = [];
|
|
37
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
38
|
+
hex.push(bytes[i].toString(16).padStart(2, "0"));
|
|
39
|
+
}
|
|
40
|
+
return (hex.slice(0, 4).join("") +
|
|
41
|
+
"-" +
|
|
42
|
+
hex.slice(4, 6).join("") +
|
|
43
|
+
"-" +
|
|
44
|
+
hex.slice(6, 8).join("") +
|
|
45
|
+
"-" +
|
|
46
|
+
hex.slice(8, 10).join("") +
|
|
47
|
+
"-" +
|
|
48
|
+
hex.slice(10, 16).join(""));
|
|
49
|
+
}
|
|
50
|
+
function generateUuidFromMath() {
|
|
51
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
52
|
+
const r = (Math.random() * 16) | 0;
|
|
53
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
54
|
+
return v.toString(16);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=event-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-id.js","sourceRoot":"","sources":["../../src/event-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oCAAoC;AACpC,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,GAAG,UAAiC,CAAC;IAE5C,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAC1D,OAAO,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QAC/D,OAAO,2BAA2B,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED,yEAAyE;IACzE,4DAA4D;IAC5D,OAAO,oBAAoB,EAAE,CAAC;AAChC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,wEAAwE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC9F,CAAC;AAED,SAAS,2BAA2B,CAAC,MAAc;IACjD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,+CAA+C;IAC/C,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACpC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAEpC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,CACL,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB;IAC3B,OAAO,sCAAsC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;QACnE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QAC1C,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entry point for `@apex-inc/capacitor-plugin`.
|
|
3
|
+
*
|
|
4
|
+
* Registers the plugin with Capacitor's plugin registry and exports the
|
|
5
|
+
* proxy. Consumers import like:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Apex } from "@apex-inc/capacitor-plugin";
|
|
9
|
+
*
|
|
10
|
+
* await Apex.initialize({ projectKey: "prj_..." });
|
|
11
|
+
* await Apex.track({ type: "app_open" });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
import type { ApexCapacitorPlugin } from "./definitions";
|
|
15
|
+
/**
|
|
16
|
+
* Capacitor plugin proxy. In a native app, calls route through the bridge to
|
|
17
|
+
* the iOS (Swift) or Android (Kotlin) implementation. In a web context
|
|
18
|
+
* (browser or Capacitor PWA), the web fallback in `./web.ts` takes over.
|
|
19
|
+
*/
|
|
20
|
+
export declare const Apex: ApexCapacitorPlugin;
|
|
21
|
+
export * from "./definitions";
|
|
22
|
+
export { OfflineQueue, InMemoryStorage, IndexedDBStorage } from "./offline-queue";
|
|
23
|
+
export type { OfflineQueueStorage, OfflineQueueOptions, QueuedEvent } from "./offline-queue";
|
|
24
|
+
export { SessionManager } from "./session-manager";
|
|
25
|
+
export type { SessionManagerOptions, SessionSnapshot } from "./session-manager";
|
|
26
|
+
export { BatchSender } from "./batch-sender";
|
|
27
|
+
export type { BatchSenderOptions, FlushResult as BatchFlushResult } from "./batch-sender";
|
|
28
|
+
export { generateEventId, isValidEventId } from "./event-id";
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,mBAKlB,CAAC;AAGF,cAAc,eAAe,CAAC;AAG9B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,kBAAkB,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entry point for `@apex-inc/capacitor-plugin`.
|
|
3
|
+
*
|
|
4
|
+
* Registers the plugin with Capacitor's plugin registry and exports the
|
|
5
|
+
* proxy. Consumers import like:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Apex } from "@apex-inc/capacitor-plugin";
|
|
9
|
+
*
|
|
10
|
+
* await Apex.initialize({ projectKey: "prj_..." });
|
|
11
|
+
* await Apex.track({ type: "app_open" });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
import { registerPlugin } from "@capacitor/core";
|
|
15
|
+
/**
|
|
16
|
+
* Capacitor plugin proxy. In a native app, calls route through the bridge to
|
|
17
|
+
* the iOS (Swift) or Android (Kotlin) implementation. In a web context
|
|
18
|
+
* (browser or Capacitor PWA), the web fallback in `./web.ts` takes over.
|
|
19
|
+
*/
|
|
20
|
+
export const Apex = registerPlugin("ApexCapacitorPlugin", {
|
|
21
|
+
web: () => import("./web").then((m) => new m.ApexCapacitorWeb()),
|
|
22
|
+
});
|
|
23
|
+
// Re-export types so consumers can import them cleanly.
|
|
24
|
+
export * from "./definitions";
|
|
25
|
+
// Re-export core modules for advanced/test usage.
|
|
26
|
+
export { OfflineQueue, InMemoryStorage, IndexedDBStorage } from "./offline-queue";
|
|
27
|
+
export { SessionManager } from "./session-manager";
|
|
28
|
+
export { BatchSender } from "./batch-sender";
|
|
29
|
+
export { generateEventId, isValidEventId } from "./event-id";
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD;;;;GAIG;AACH,MAAM,CAAC,MAAM,IAAI,GAAwB,cAAc,CACrD,qBAAqB,EACrB;IACE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;CACjE,CACF,CAAC;AAEF,wDAAwD;AACxD,cAAc,eAAe,CAAC;AAE9B,kDAAkD;AAClD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAElF,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline event queue with durable storage and FIFO eviction.
|
|
3
|
+
*
|
|
4
|
+
* Every event tracked through the plugin gets enqueued here first. A batch
|
|
5
|
+
* sender drains the queue in the background (`batch-sender.ts`). When the
|
|
6
|
+
* user is offline (flights, subways, dead zones), events accumulate safely
|
|
7
|
+
* up to `maxSize`; the oldest events are dropped rather than crashing the
|
|
8
|
+
* app or silently failing.
|
|
9
|
+
*
|
|
10
|
+
* Pluggable `Storage` interface supports three implementations:
|
|
11
|
+
* - `InMemoryStorage` — used in tests and as the default for SSR/Node
|
|
12
|
+
* - `IndexedDBStorage` — used in browser + Capacitor WebView contexts
|
|
13
|
+
* - `NativeBridgeStorage` — used when Capacitor native plugin is active
|
|
14
|
+
* (persists through the native OfflineQueue on iOS Core Data / Android Room)
|
|
15
|
+
*
|
|
16
|
+
* This keeps the queue logic pure (no DOM/Native API dependencies in the
|
|
17
|
+
* algorithmic core), which makes testing trivial.
|
|
18
|
+
*/
|
|
19
|
+
import type { ApexEvent } from "./definitions";
|
|
20
|
+
/** A single queued event ready for delivery to the server. */
|
|
21
|
+
export interface QueuedEvent {
|
|
22
|
+
event: ApexEvent;
|
|
23
|
+
/** Attempt count so the batch sender can apply exponential backoff. */
|
|
24
|
+
attempts: number;
|
|
25
|
+
/** ISO timestamp when enqueued. Used for queue-age metrics + eviction. */
|
|
26
|
+
enqueuedAt: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Storage backend for the offline queue. Implementations persist events
|
|
30
|
+
* across app restarts and may be synchronous (in-memory) or asynchronous
|
|
31
|
+
* (IndexedDB, native bridge).
|
|
32
|
+
*/
|
|
33
|
+
export interface OfflineQueueStorage {
|
|
34
|
+
/** Read all queued events in insertion order. */
|
|
35
|
+
readAll(): Promise<QueuedEvent[]>;
|
|
36
|
+
/** Append one event to the queue. */
|
|
37
|
+
append(event: QueuedEvent): Promise<void>;
|
|
38
|
+
/** Replace the entire queue contents (used for flush + eviction). */
|
|
39
|
+
replaceAll(events: QueuedEvent[]): Promise<void>;
|
|
40
|
+
/** Remove all events matching the given event IDs. */
|
|
41
|
+
removeByIds(eventIds: string[]): Promise<void>;
|
|
42
|
+
/** Number of events currently persisted. */
|
|
43
|
+
size(): Promise<number>;
|
|
44
|
+
/** Best-effort clear — used on test mode toggle + uninstall cleanup. */
|
|
45
|
+
clear(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/** Options for {@link OfflineQueue}. */
|
|
48
|
+
export interface OfflineQueueOptions {
|
|
49
|
+
storage: OfflineQueueStorage;
|
|
50
|
+
/** Maximum events to hold before FIFO eviction. Default 1000. */
|
|
51
|
+
maxSize?: number;
|
|
52
|
+
/** Called when an event is evicted due to queue overflow. */
|
|
53
|
+
onEvicted?: (event: QueuedEvent) => void;
|
|
54
|
+
}
|
|
55
|
+
export declare class OfflineQueue {
|
|
56
|
+
private readonly storage;
|
|
57
|
+
private readonly maxSize;
|
|
58
|
+
private readonly onEvicted?;
|
|
59
|
+
constructor(options: OfflineQueueOptions);
|
|
60
|
+
/**
|
|
61
|
+
* Enqueue an event. If the queue is at `maxSize`, the oldest event is
|
|
62
|
+
* evicted first (FIFO) so the new event can be recorded.
|
|
63
|
+
*/
|
|
64
|
+
enqueue(event: ApexEvent): Promise<void>;
|
|
65
|
+
/** Enqueue many events in a single storage write. */
|
|
66
|
+
enqueueMany(events: ApexEvent[]): Promise<void>;
|
|
67
|
+
/** Read the next batch of events without removing them from the queue. */
|
|
68
|
+
peek(batchSize: number): Promise<QueuedEvent[]>;
|
|
69
|
+
/** Mark events as successfully delivered and remove them from the queue. */
|
|
70
|
+
markSent(eventIds: string[]): Promise<void>;
|
|
71
|
+
/** Increment attempt count for events that failed to send (for backoff). */
|
|
72
|
+
markFailed(eventIds: string[]): Promise<void>;
|
|
73
|
+
/** Current queue size. */
|
|
74
|
+
size(): Promise<number>;
|
|
75
|
+
/** ISO timestamp of the oldest queued event, or null if queue is empty. */
|
|
76
|
+
oldestEventAt(): Promise<string | null>;
|
|
77
|
+
/** Empty the queue. Typically called when toggling test mode. */
|
|
78
|
+
clear(): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* In-memory storage. Used in tests and as a fallback where neither IndexedDB
|
|
82
|
+
* nor the native bridge is available (e.g. SSR contexts).
|
|
83
|
+
*/
|
|
84
|
+
export declare class InMemoryStorage implements OfflineQueueStorage {
|
|
85
|
+
private events;
|
|
86
|
+
readAll(): Promise<QueuedEvent[]>;
|
|
87
|
+
append(event: QueuedEvent): Promise<void>;
|
|
88
|
+
replaceAll(events: QueuedEvent[]): Promise<void>;
|
|
89
|
+
removeByIds(eventIds: string[]): Promise<void>;
|
|
90
|
+
size(): Promise<number>;
|
|
91
|
+
clear(): Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* IndexedDB-backed storage for browser + Capacitor WebView contexts.
|
|
95
|
+
* Persists events across app restarts using a single object store keyed by
|
|
96
|
+
* event ID.
|
|
97
|
+
*/
|
|
98
|
+
export declare class IndexedDBStorage implements OfflineQueueStorage {
|
|
99
|
+
private readonly dbName;
|
|
100
|
+
private readonly storeName;
|
|
101
|
+
private dbPromise?;
|
|
102
|
+
constructor(dbName?: string, storeName?: string);
|
|
103
|
+
private openDb;
|
|
104
|
+
readAll(): Promise<QueuedEvent[]>;
|
|
105
|
+
append(event: QueuedEvent): Promise<void>;
|
|
106
|
+
replaceAll(events: QueuedEvent[]): Promise<void>;
|
|
107
|
+
removeByIds(eventIds: string[]): Promise<void>;
|
|
108
|
+
size(): Promise<number>;
|
|
109
|
+
clear(): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=offline-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-queue.d.ts","sourceRoot":"","sources":["../../src/offline-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline event queue with durable storage and FIFO eviction.
|
|
3
|
+
*
|
|
4
|
+
* Every event tracked through the plugin gets enqueued here first. A batch
|
|
5
|
+
* sender drains the queue in the background (`batch-sender.ts`). When the
|
|
6
|
+
* user is offline (flights, subways, dead zones), events accumulate safely
|
|
7
|
+
* up to `maxSize`; the oldest events are dropped rather than crashing the
|
|
8
|
+
* app or silently failing.
|
|
9
|
+
*
|
|
10
|
+
* Pluggable `Storage` interface supports three implementations:
|
|
11
|
+
* - `InMemoryStorage` — used in tests and as the default for SSR/Node
|
|
12
|
+
* - `IndexedDBStorage` — used in browser + Capacitor WebView contexts
|
|
13
|
+
* - `NativeBridgeStorage` — used when Capacitor native plugin is active
|
|
14
|
+
* (persists through the native OfflineQueue on iOS Core Data / Android Room)
|
|
15
|
+
*
|
|
16
|
+
* This keeps the queue logic pure (no DOM/Native API dependencies in the
|
|
17
|
+
* algorithmic core), which makes testing trivial.
|
|
18
|
+
*/
|
|
19
|
+
export class OfflineQueue {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.storage = options.storage;
|
|
22
|
+
this.maxSize = options.maxSize ?? 1000;
|
|
23
|
+
this.onEvicted = options.onEvicted;
|
|
24
|
+
if (this.maxSize < 1) {
|
|
25
|
+
throw new Error(`OfflineQueue maxSize must be >= 1, got ${this.maxSize}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Enqueue an event. If the queue is at `maxSize`, the oldest event is
|
|
30
|
+
* evicted first (FIFO) so the new event can be recorded.
|
|
31
|
+
*/
|
|
32
|
+
async enqueue(event) {
|
|
33
|
+
if (!event.id) {
|
|
34
|
+
throw new Error("OfflineQueue.enqueue: event.id is required (set via event-id.generateEventId)");
|
|
35
|
+
}
|
|
36
|
+
const existing = await this.storage.readAll();
|
|
37
|
+
const queued = {
|
|
38
|
+
event,
|
|
39
|
+
attempts: 0,
|
|
40
|
+
enqueuedAt: new Date().toISOString(),
|
|
41
|
+
};
|
|
42
|
+
const next = [...existing, queued];
|
|
43
|
+
if (next.length > this.maxSize) {
|
|
44
|
+
const overflow = next.splice(0, next.length - this.maxSize);
|
|
45
|
+
if (this.onEvicted) {
|
|
46
|
+
for (const ev of overflow) {
|
|
47
|
+
this.onEvicted(ev);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
await this.storage.replaceAll(next);
|
|
52
|
+
}
|
|
53
|
+
/** Enqueue many events in a single storage write. */
|
|
54
|
+
async enqueueMany(events) {
|
|
55
|
+
if (events.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
const existing = await this.storage.readAll();
|
|
58
|
+
const now = new Date().toISOString();
|
|
59
|
+
const queued = events.map((event) => {
|
|
60
|
+
if (!event.id) {
|
|
61
|
+
throw new Error("OfflineQueue.enqueueMany: every event must have an id");
|
|
62
|
+
}
|
|
63
|
+
return { event, attempts: 0, enqueuedAt: now };
|
|
64
|
+
});
|
|
65
|
+
const next = [...existing, ...queued];
|
|
66
|
+
if (next.length > this.maxSize) {
|
|
67
|
+
const overflow = next.splice(0, next.length - this.maxSize);
|
|
68
|
+
if (this.onEvicted) {
|
|
69
|
+
for (const ev of overflow) {
|
|
70
|
+
this.onEvicted(ev);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
await this.storage.replaceAll(next);
|
|
75
|
+
}
|
|
76
|
+
/** Read the next batch of events without removing them from the queue. */
|
|
77
|
+
async peek(batchSize) {
|
|
78
|
+
if (batchSize < 1)
|
|
79
|
+
return [];
|
|
80
|
+
const all = await this.storage.readAll();
|
|
81
|
+
return all.slice(0, batchSize);
|
|
82
|
+
}
|
|
83
|
+
/** Mark events as successfully delivered and remove them from the queue. */
|
|
84
|
+
async markSent(eventIds) {
|
|
85
|
+
if (eventIds.length === 0)
|
|
86
|
+
return;
|
|
87
|
+
await this.storage.removeByIds(eventIds);
|
|
88
|
+
}
|
|
89
|
+
/** Increment attempt count for events that failed to send (for backoff). */
|
|
90
|
+
async markFailed(eventIds) {
|
|
91
|
+
if (eventIds.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
const idSet = new Set(eventIds);
|
|
94
|
+
const all = await this.storage.readAll();
|
|
95
|
+
const next = all.map((q) => q.event.id && idSet.has(q.event.id) ? { ...q, attempts: q.attempts + 1 } : q);
|
|
96
|
+
await this.storage.replaceAll(next);
|
|
97
|
+
}
|
|
98
|
+
/** Current queue size. */
|
|
99
|
+
async size() {
|
|
100
|
+
return this.storage.size();
|
|
101
|
+
}
|
|
102
|
+
/** ISO timestamp of the oldest queued event, or null if queue is empty. */
|
|
103
|
+
async oldestEventAt() {
|
|
104
|
+
const all = await this.storage.readAll();
|
|
105
|
+
return all.length > 0 ? all[0].enqueuedAt : null;
|
|
106
|
+
}
|
|
107
|
+
/** Empty the queue. Typically called when toggling test mode. */
|
|
108
|
+
async clear() {
|
|
109
|
+
await this.storage.clear();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ── Storage implementations ────────────────────────────────────────────────
|
|
113
|
+
/**
|
|
114
|
+
* In-memory storage. Used in tests and as a fallback where neither IndexedDB
|
|
115
|
+
* nor the native bridge is available (e.g. SSR contexts).
|
|
116
|
+
*/
|
|
117
|
+
export class InMemoryStorage {
|
|
118
|
+
constructor() {
|
|
119
|
+
this.events = [];
|
|
120
|
+
}
|
|
121
|
+
async readAll() {
|
|
122
|
+
return [...this.events];
|
|
123
|
+
}
|
|
124
|
+
async append(event) {
|
|
125
|
+
this.events.push(event);
|
|
126
|
+
}
|
|
127
|
+
async replaceAll(events) {
|
|
128
|
+
this.events = [...events];
|
|
129
|
+
}
|
|
130
|
+
async removeByIds(eventIds) {
|
|
131
|
+
const idSet = new Set(eventIds);
|
|
132
|
+
this.events = this.events.filter((q) => !q.event.id || !idSet.has(q.event.id));
|
|
133
|
+
}
|
|
134
|
+
async size() {
|
|
135
|
+
return this.events.length;
|
|
136
|
+
}
|
|
137
|
+
async clear() {
|
|
138
|
+
this.events = [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* IndexedDB-backed storage for browser + Capacitor WebView contexts.
|
|
143
|
+
* Persists events across app restarts using a single object store keyed by
|
|
144
|
+
* event ID.
|
|
145
|
+
*/
|
|
146
|
+
export class IndexedDBStorage {
|
|
147
|
+
constructor(dbName = "apex-capacitor-queue", storeName = "events") {
|
|
148
|
+
this.dbName = dbName;
|
|
149
|
+
this.storeName = storeName;
|
|
150
|
+
}
|
|
151
|
+
async openDb() {
|
|
152
|
+
if (this.dbPromise)
|
|
153
|
+
return this.dbPromise;
|
|
154
|
+
const db = await new Promise((resolve, reject) => {
|
|
155
|
+
const idb = globalThis.indexedDB;
|
|
156
|
+
if (!idb) {
|
|
157
|
+
reject(new Error("IndexedDB not available in this environment"));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const req = idb.open(this.dbName, 1);
|
|
161
|
+
req.onupgradeneeded = () => {
|
|
162
|
+
const database = req.result;
|
|
163
|
+
if (!database.objectStoreNames.contains(this.storeName)) {
|
|
164
|
+
database.createObjectStore(this.storeName, { keyPath: "key", autoIncrement: true });
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
req.onsuccess = () => resolve(req.result);
|
|
168
|
+
req.onerror = () => reject(req.error);
|
|
169
|
+
});
|
|
170
|
+
this.dbPromise = Promise.resolve(db);
|
|
171
|
+
return db;
|
|
172
|
+
}
|
|
173
|
+
async readAll() {
|
|
174
|
+
const db = await this.openDb();
|
|
175
|
+
return new Promise((resolve, reject) => {
|
|
176
|
+
const tx = db.transaction(this.storeName, "readonly");
|
|
177
|
+
const store = tx.objectStore(this.storeName);
|
|
178
|
+
const req = store.getAll();
|
|
179
|
+
req.onsuccess = () => {
|
|
180
|
+
const rows = req.result ?? [];
|
|
181
|
+
// Strip internal autoIncrement key for caller.
|
|
182
|
+
resolve(rows
|
|
183
|
+
.sort((a, b) => (a.key ?? 0) - (b.key ?? 0))
|
|
184
|
+
.map(({ key: _k, ...rest }) => rest));
|
|
185
|
+
};
|
|
186
|
+
req.onerror = () => reject(req.error);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
async append(event) {
|
|
190
|
+
const db = await this.openDb();
|
|
191
|
+
await new Promise((resolve, reject) => {
|
|
192
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
193
|
+
const store = tx.objectStore(this.storeName);
|
|
194
|
+
store.add(event);
|
|
195
|
+
tx.oncomplete = () => resolve();
|
|
196
|
+
tx.onerror = () => reject(tx.error);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async replaceAll(events) {
|
|
200
|
+
const db = await this.openDb();
|
|
201
|
+
await new Promise((resolve, reject) => {
|
|
202
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
203
|
+
const store = tx.objectStore(this.storeName);
|
|
204
|
+
store.clear();
|
|
205
|
+
for (const ev of events) {
|
|
206
|
+
store.add(ev);
|
|
207
|
+
}
|
|
208
|
+
tx.oncomplete = () => resolve();
|
|
209
|
+
tx.onerror = () => reject(tx.error);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async removeByIds(eventIds) {
|
|
213
|
+
if (eventIds.length === 0)
|
|
214
|
+
return;
|
|
215
|
+
const idSet = new Set(eventIds);
|
|
216
|
+
const all = await this.readAll();
|
|
217
|
+
const kept = all.filter((q) => !q.event.id || !idSet.has(q.event.id));
|
|
218
|
+
await this.replaceAll(kept);
|
|
219
|
+
}
|
|
220
|
+
async size() {
|
|
221
|
+
const db = await this.openDb();
|
|
222
|
+
return new Promise((resolve, reject) => {
|
|
223
|
+
const tx = db.transaction(this.storeName, "readonly");
|
|
224
|
+
const store = tx.objectStore(this.storeName);
|
|
225
|
+
const req = store.count();
|
|
226
|
+
req.onsuccess = () => resolve(req.result ?? 0);
|
|
227
|
+
req.onerror = () => reject(req.error);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
async clear() {
|
|
231
|
+
const db = await this.openDb();
|
|
232
|
+
await new Promise((resolve, reject) => {
|
|
233
|
+
const tx = db.transaction(this.storeName, "readwrite");
|
|
234
|
+
tx.objectStore(this.storeName).clear();
|
|
235
|
+
tx.oncomplete = () => resolve();
|
|
236
|
+
tx.onerror = () => reject(tx.error);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=offline-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offline-queue.js","sourceRoot":"","sources":["../../src/offline-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mobile session manager.
|
|
3
|
+
*
|
|
4
|
+
* Defines the boundaries of a user's interaction with the app. A session
|
|
5
|
+
* starts on the first event after inactivity and ends when either
|
|
6
|
+
* (a) no events arrive for `timeoutMinutes` (default 30), or
|
|
7
|
+
* (b) the app is force-backgrounded and the OS reclaims memory.
|
|
8
|
+
*
|
|
9
|
+
* Emits `session_start` and `session_end` events that plug directly into the
|
|
10
|
+
* server's MSESSION# rollup (Phase 1c). The timeout is configurable and the
|
|
11
|
+
* clock is injectable for deterministic testing.
|
|
12
|
+
*/
|
|
13
|
+
export interface SessionSnapshot {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
startedAt: string;
|
|
16
|
+
lastActivityAt: string;
|
|
17
|
+
eventCount: number;
|
|
18
|
+
endedAt?: string;
|
|
19
|
+
durationSeconds?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface SessionManagerOptions {
|
|
22
|
+
/** Inactivity window after which a session ends. Default 30 minutes. */
|
|
23
|
+
timeoutMinutes?: number;
|
|
24
|
+
/** Called when a new session starts (emits `session_start` server-side). */
|
|
25
|
+
onSessionStart?: (session: SessionSnapshot) => void;
|
|
26
|
+
/** Called when a session ends (emits `session_end` server-side). */
|
|
27
|
+
onSessionEnd?: (session: SessionSnapshot) => void;
|
|
28
|
+
/** Injectable clock for deterministic testing. Defaults to `Date.now()`. */
|
|
29
|
+
now?: () => number;
|
|
30
|
+
/** Injectable `setTimeout` / `clearTimeout` for deterministic testing. */
|
|
31
|
+
scheduler?: {
|
|
32
|
+
setTimeout: (callback: () => void, ms: number) => number;
|
|
33
|
+
clearTimeout: (handle: number) => void;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare class SessionManager {
|
|
37
|
+
private readonly timeoutMs;
|
|
38
|
+
private readonly onSessionStart?;
|
|
39
|
+
private readonly onSessionEnd?;
|
|
40
|
+
private readonly now;
|
|
41
|
+
private readonly setTimeoutFn;
|
|
42
|
+
private readonly clearTimeoutFn;
|
|
43
|
+
private current;
|
|
44
|
+
private timeoutHandle;
|
|
45
|
+
constructor(options?: SessionManagerOptions);
|
|
46
|
+
/**
|
|
47
|
+
* Records user activity. Starts a new session if none is active, otherwise
|
|
48
|
+
* resets the inactivity timeout. Returns the current session snapshot.
|
|
49
|
+
*/
|
|
50
|
+
recordActivity(): SessionSnapshot;
|
|
51
|
+
/**
|
|
52
|
+
* Explicitly ends the current session immediately. Fires `onSessionEnd`.
|
|
53
|
+
* No-op if no session is active.
|
|
54
|
+
*/
|
|
55
|
+
endSession(): void;
|
|
56
|
+
/** Returns the current session snapshot, or null. */
|
|
57
|
+
getCurrent(): SessionSnapshot | null;
|
|
58
|
+
/** Force-starts a new session, ending any current one. */
|
|
59
|
+
forceStart(): SessionSnapshot;
|
|
60
|
+
private scheduleTimeout;
|
|
61
|
+
private clearTimeout;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/session-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,0EAA0E;IAC1E,SAAS,CAAC,EAAE;QACV,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;QACzD,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;KACxC,CAAC;CACH;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAqC;IACrE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAqC;IACnE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyC;IACtE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2B;IAC1D,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,aAAa,CAAuB;gBAEhC,OAAO,GAAE,qBAA0B;IAgB/C;;;OAGG;IACH,cAAc,IAAI,eAAe;IAwBjC;;;OAGG;IACH,UAAU,IAAI,IAAI;IAkBlB,qDAAqD;IACrD,UAAU,IAAI,eAAe,GAAG,IAAI;IAIpC,0DAA0D;IAC1D,UAAU,IAAI,eAAe;IAK7B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,YAAY;CAMrB"}
|