@openora/core 0.4.1-canary.93 → 0.4.1-canary.95

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.
Files changed (78) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/audit/plugin.d.ts.map +1 -1
  3. package/dist/audit/plugin.js +13 -0
  4. package/dist/audit/plugin.js.map +1 -1
  5. package/dist/contracts/adapters/audit.d.ts +1 -1
  6. package/dist/contracts/adapters/audit.d.ts.map +1 -1
  7. package/dist/contracts/adapters/audit.js.map +1 -1
  8. package/dist/contracts/schemas/events.d.ts +5 -0
  9. package/dist/contracts/schemas/events.d.ts.map +1 -1
  10. package/dist/contracts/schemas/events.js +9 -0
  11. package/dist/contracts/schemas/events.js.map +1 -1
  12. package/dist/testing/mock.d.ts +6 -1
  13. package/dist/testing/mock.d.ts.map +1 -1
  14. package/dist/testing/mock.js +9 -0
  15. package/dist/testing/mock.js.map +1 -1
  16. package/dist/wallet/__tests__/custody-sweep-gate.test.d.ts +2 -0
  17. package/dist/wallet/__tests__/custody-sweep-gate.test.d.ts.map +1 -0
  18. package/dist/wallet/__tests__/custody-sweep-gate.test.js +70 -0
  19. package/dist/wallet/__tests__/custody-sweep-gate.test.js.map +1 -0
  20. package/dist/wallet/__tests__/custody-sweep.service.int.test.d.ts +2 -0
  21. package/dist/wallet/__tests__/custody-sweep.service.int.test.d.ts.map +1 -0
  22. package/dist/wallet/__tests__/custody-sweep.service.int.test.js +246 -0
  23. package/dist/wallet/__tests__/custody-sweep.service.int.test.js.map +1 -0
  24. package/dist/wallet/__tests__/reconciliation-diff.test.d.ts +2 -0
  25. package/dist/wallet/__tests__/reconciliation-diff.test.d.ts.map +1 -0
  26. package/dist/wallet/__tests__/reconciliation-diff.test.js +49 -0
  27. package/dist/wallet/__tests__/reconciliation-diff.test.js.map +1 -0
  28. package/dist/wallet/__tests__/reconciliation.service.int.test.d.ts +2 -0
  29. package/dist/wallet/__tests__/reconciliation.service.int.test.d.ts.map +1 -0
  30. package/dist/wallet/__tests__/reconciliation.service.int.test.js +395 -0
  31. package/dist/wallet/__tests__/reconciliation.service.int.test.js.map +1 -0
  32. package/dist/wallet/__tests__/wallet-asset.router.int.test.js +12 -2
  33. package/dist/wallet/__tests__/wallet-asset.router.int.test.js.map +1 -1
  34. package/dist/wallet/__tests__/wallet-auto-rule.router.int.test.js +12 -2
  35. package/dist/wallet/__tests__/wallet-auto-rule.router.int.test.js.map +1 -1
  36. package/dist/wallet/__tests__/wallet-auto-withdrawal-config.router.int.test.js +12 -2
  37. package/dist/wallet/__tests__/wallet-auto-withdrawal-config.router.int.test.js.map +1 -1
  38. package/dist/wallet/__tests__/wallet-custody-routes.router.test.d.ts +2 -0
  39. package/dist/wallet/__tests__/wallet-custody-routes.router.test.d.ts.map +1 -0
  40. package/dist/wallet/__tests__/wallet-custody-routes.router.test.js +128 -0
  41. package/dist/wallet/__tests__/wallet-custody-routes.router.test.js.map +1 -0
  42. package/dist/wallet/__tests__/wallet-webhook.router.int.test.js +32 -4
  43. package/dist/wallet/__tests__/wallet-webhook.router.int.test.js.map +1 -1
  44. package/dist/wallet/__tests__/wallet.router.int.test.js +12 -2
  45. package/dist/wallet/__tests__/wallet.router.int.test.js.map +1 -1
  46. package/dist/wallet/__tests__/wallet.service.int.test.js +34 -11
  47. package/dist/wallet/__tests__/wallet.service.int.test.js.map +1 -1
  48. package/dist/wallet/contract/index.d.ts.map +1 -1
  49. package/dist/wallet/contract/index.js +8 -8
  50. package/dist/wallet/contract/index.js.map +1 -1
  51. package/dist/wallet/plugin.d.ts.map +1 -1
  52. package/dist/wallet/plugin.js +91 -17
  53. package/dist/wallet/plugin.js.map +1 -1
  54. package/dist/wallet/router/index.d.ts +19 -2
  55. package/dist/wallet/router/index.d.ts.map +1 -1
  56. package/dist/wallet/router/index.js +32 -17
  57. package/dist/wallet/router/index.js.map +1 -1
  58. package/dist/wallet/service/custody-sweep.service.d.ts +77 -0
  59. package/dist/wallet/service/custody-sweep.service.d.ts.map +1 -0
  60. package/dist/wallet/service/custody-sweep.service.js +354 -0
  61. package/dist/wallet/service/custody-sweep.service.js.map +1 -0
  62. package/dist/wallet/service/reconciliation-finding.service.d.ts +39 -0
  63. package/dist/wallet/service/reconciliation-finding.service.d.ts.map +1 -0
  64. package/dist/wallet/service/reconciliation-finding.service.js +36 -0
  65. package/dist/wallet/service/reconciliation-finding.service.js.map +1 -0
  66. package/dist/wallet/service/reconciliation.service.d.ts +112 -0
  67. package/dist/wallet/service/reconciliation.service.d.ts.map +1 -0
  68. package/dist/wallet/service/reconciliation.service.js +509 -0
  69. package/dist/wallet/service/reconciliation.service.js.map +1 -0
  70. package/dist/wallet/service/wallet.service.d.ts +7 -2
  71. package/dist/wallet/service/wallet.service.d.ts.map +1 -1
  72. package/dist/wallet/service/wallet.service.js +45 -3
  73. package/dist/wallet/service/wallet.service.js.map +1 -1
  74. package/package.json +1 -1
  75. package/dist/wallet/__tests__/wallet-custody-stubs.router.test.d.ts +0 -2
  76. package/dist/wallet/__tests__/wallet-custody-stubs.router.test.d.ts.map +0 -1
  77. package/dist/wallet/__tests__/wallet-custody-stubs.router.test.js +0 -80
  78. package/dist/wallet/__tests__/wallet-custody-stubs.router.test.js.map +0 -1
@@ -0,0 +1,39 @@
1
+ import type { DrizzleDb, DrizzleTx } from '@openora/core/server';
2
+ import type { WalletReconciliationFindingKind } from '@openora/core/contracts';
3
+ /**
4
+ * Sentinel `runId` for a finding produced OUTSIDE any reconciliation job cycle - the
5
+ * live webhook path (`WalletService.creditDepositByAddress`) hits an unattributable
6
+ * deposit in real time, not on a poll. `runId` is NOT NULL on the table, so this fixed
7
+ * zero-uuid documents "no job run owns this row", mirroring the audit plugin's
8
+ * SYSTEM_ACTOR sentinel for "no admin acted here".
9
+ */
10
+ export declare const LIVE_WEBHOOK_RUN_ID = "00000000-0000-0000-0000-000000000000";
11
+ export type ReconciliationFindingInput = {
12
+ runId: string;
13
+ providerName: string;
14
+ kind: WalletReconciliationFindingKind;
15
+ currency?: string | null;
16
+ network?: string | null;
17
+ amount?: string | null;
18
+ address?: string | null;
19
+ tag?: string | null;
20
+ txHash?: string | null;
21
+ /**
22
+ * The dedup key: a partial unique index on (kind, externalId) makes a duplicate
23
+ * insert for the same underlying vendor event or job re-run a silent no-op. Pass a
24
+ * stable stand-in (eg the source row's own id) when no vendor externalId exists, so
25
+ * a finding with no natural external reference still dedupes across re-runs.
26
+ */
27
+ externalId?: string | null;
28
+ transactionId?: string | null;
29
+ detail?: string | null;
30
+ };
31
+ /**
32
+ * The single write path for a reconciliation finding - never a raw `insert` scattered
33
+ * across WalletService and ReconciliationService (both import this, neither imports the
34
+ * other, so there is no cycle). A finding is a report, never a credit instruction (see
35
+ * the schema's own comment): this never touches a balance or a transaction row, only
36
+ * records that something needs a human look.
37
+ */
38
+ export declare function recordReconciliationFinding(db: DrizzleDb | DrizzleTx, input: ReconciliationFindingInput): Promise<void>;
39
+ //# sourceMappingURL=reconciliation-finding.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciliation-finding.service.d.ts","sourceRoot":"","sources":["../../../src/wallet/service/reconciliation-finding.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAC;AAG/E;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,yCAAyC,CAAC;AAE1E,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,+BAA+B,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,EAAE,EAAE,SAAS,GAAG,SAAS,EACzB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CAkBf"}
@@ -0,0 +1,36 @@
1
+ import { walletReconciliationFinding } from '../schema/index.js';
2
+ /**
3
+ * Sentinel `runId` for a finding produced OUTSIDE any reconciliation job cycle - the
4
+ * live webhook path (`WalletService.creditDepositByAddress`) hits an unattributable
5
+ * deposit in real time, not on a poll. `runId` is NOT NULL on the table, so this fixed
6
+ * zero-uuid documents "no job run owns this row", mirroring the audit plugin's
7
+ * SYSTEM_ACTOR sentinel for "no admin acted here".
8
+ */
9
+ export const LIVE_WEBHOOK_RUN_ID = '00000000-0000-0000-0000-000000000000';
10
+ /**
11
+ * The single write path for a reconciliation finding - never a raw `insert` scattered
12
+ * across WalletService and ReconciliationService (both import this, neither imports the
13
+ * other, so there is no cycle). A finding is a report, never a credit instruction (see
14
+ * the schema's own comment): this never touches a balance or a transaction row, only
15
+ * records that something needs a human look.
16
+ */
17
+ export async function recordReconciliationFinding(db, input) {
18
+ await db
19
+ .insert(walletReconciliationFinding)
20
+ .values({
21
+ runId: input.runId,
22
+ providerName: input.providerName,
23
+ kind: input.kind,
24
+ currency: input.currency ?? null,
25
+ network: input.network ?? null,
26
+ amount: input.amount ?? null,
27
+ address: input.address ?? null,
28
+ tag: input.tag ?? null,
29
+ txHash: input.txHash ?? null,
30
+ externalId: input.externalId ?? null,
31
+ transactionId: input.transactionId ?? null,
32
+ detail: input.detail ?? null,
33
+ })
34
+ .onConflictDoNothing();
35
+ }
36
+ //# sourceMappingURL=reconciliation-finding.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciliation-finding.service.js","sourceRoot":"","sources":["../../../src/wallet/service/reconciliation-finding.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,sCAAsC,CAAC;AAuB1E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,EAAyB,EACzB,KAAiC;IAEjC,MAAM,EAAE;SACL,MAAM,CAAC,2BAA2B,CAAC;SACnC,MAAM,CAAC;QACN,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;QAChC,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI;QAC9B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;QAC5B,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI;QAC9B,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;QAC5B,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;QAC1C,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;KAC7B,CAAC;SACD,mBAAmB,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,112 @@
1
+ import { type DrizzleService, type EventBus } from '@openora/core/server';
2
+ import { type AuditWritePort, type ClientMeta, type PaymentProviderRegistry, type PaymentWebhookEvent, type PlatformConfig, type User } from '@openora/core/contracts';
3
+ import { type WalletJobRun, type WalletReconciliationFinding as WalletReconciliationFindingRow, type WalletTransaction } from '../schema/index.js';
4
+ import type { ListReconciliationFindingsInput, ReconciliationResolution, WalletReconciliationFinding as WalletReconciliationFindingDto } from '../contract/index.js';
5
+ import type { WalletService } from './wallet.service.js';
6
+ export declare const ReconciliationFindingNotFoundError: new (id: string) => Error;
7
+ export declare const ReconciliationCreditTransactionNotFoundError: new (id: string) => Error;
8
+ export declare const ReconciliationCreditMismatchError: new () => Error;
9
+ type LedgerMatch = Pick<WalletTransaction, 'id' | 'currency' | 'amount' | 'network'>;
10
+ /**
11
+ * Pure diff of one vendor deposit event against its (maybe absent) ledger row - the
12
+ * whole decision reconciliation makes for a deposit, extracted so it is unit-testable
13
+ * without a database. Never routes through moneyToNumber (see money.md): an exact
14
+ * decimal compare, not a float one.
15
+ */
16
+ export declare function diffDeposit(event: Extract<PaymentWebhookEvent, {
17
+ kind: 'deposit';
18
+ }>, tx: LedgerMatch | undefined): 'missing_deposit' | 'currency_mismatch' | 'amount_mismatch' | null;
19
+ export type ReconciliationServiceDeps = {
20
+ drizzle: DrizzleService;
21
+ events: EventBus;
22
+ wallet: WalletService;
23
+ paymentProviders: PaymentProviderRegistry;
24
+ audit: AuditWritePort;
25
+ platformConfig?: PlatformConfig;
26
+ };
27
+ /**
28
+ * Owns the reconciliation job cycle (claim -> per-provider diff -> stuck-withdrawal /
29
+ * stuck-sweep sweep -> finish + audit + alert) and the admin surface over its findings
30
+ * (list/resolve). Never credits or moves money itself - every finding is a report; the
31
+ * only paths that touch a balance are `WalletService.reconcileWithdrawalStatus` (already
32
+ * idempotent) and the pre-existing manual-adjustment path a `credited` resolution merely
33
+ * references.
34
+ */
35
+ export declare class ReconciliationService {
36
+ private readonly drizzle;
37
+ private readonly events;
38
+ private readonly wallet;
39
+ private readonly paymentProviders;
40
+ private readonly audit;
41
+ private readonly platformConfig?;
42
+ constructor({ drizzle, events, wallet, paymentProviders, audit, platformConfig, }: ReconciliationServiceDeps);
43
+ listFindings(filter: ListReconciliationFindingsInput): Promise<{
44
+ items: {
45
+ id: string;
46
+ runId: string;
47
+ providerName: string;
48
+ kind: "missing_deposit" | "unattributed_deposit" | "amount_mismatch" | "currency_mismatch" | "status_mismatch" | "unknown_at_provider" | "unconfigured_asset" | "stuck_sweep";
49
+ currency: string | null;
50
+ network: string | null;
51
+ amount: string | null;
52
+ address: string | null;
53
+ tag: string | null;
54
+ txHash: string | null;
55
+ externalId: string | null;
56
+ transactionId: string | null;
57
+ detail: string | null;
58
+ status: "open" | "resolved";
59
+ resolvedBy: string | null;
60
+ resolvedAt: string | null;
61
+ resolutionNote: string | null;
62
+ createdAt: string;
63
+ }[];
64
+ total: number;
65
+ page: number;
66
+ limit: number;
67
+ }>;
68
+ /**
69
+ * Conditional `UPDATE ... WHERE status = 'open'` is the whole double-resolve guard -
70
+ * a second resolve of an already-resolved finding matches zero rows and returns the
71
+ * row unchanged, with no second audit entry. `credited` requires the referenced
72
+ * transaction to already exist (the manual-adjustment path creates it - this never
73
+ * credits anything itself) and match the finding's currency/amount exactly.
74
+ */
75
+ resolveFinding(adminId: User['id'], id: WalletReconciliationFindingRow['id'], resolution: ReconciliationResolution, meta?: ClientMeta): Promise<WalletReconciliationFindingDto>;
76
+ /**
77
+ * The window is anchored to the last completed run, not to the wall clock. BullMQ
78
+ * repeatable jobs are delayed jobs: a tick missed while no worker was running is
79
+ * skipped, never backfilled. Against a fixed `now - lookbackHours` window that means
80
+ * any outage longer than `lookbackHours` leaves a span nothing ever reconciles and
81
+ * nothing ever reports - the worst possible failure mode for a compensating control.
82
+ * Catching up is capped, and a capped window is reported rather than passed over.
83
+ */
84
+ private resolveWindow;
85
+ /**
86
+ * Claim -> per-provider deposit/withdrawal diff -> stuck-withdrawal / stuck-sweep
87
+ * sweep -> finish + one audit entry + an alert past threshold. Returns null without
88
+ * doing any work when another cycle already owns the claim - the caller (cron tick or
89
+ * the on-demand route's worker) treats that as a normal, expected outcome.
90
+ */
91
+ runCycle(runId?: WalletJobRun['runId']): Promise<{
92
+ runId: WalletJobRun['runId'];
93
+ } | null>;
94
+ private reconcileProvider;
95
+ private sweptExternalIds;
96
+ private reconcileDeposits;
97
+ private batchLookupByProviderRefId;
98
+ private reconcileStuckWithdrawals;
99
+ private reconcileStuckSweeps;
100
+ private countOpenFindings;
101
+ /**
102
+ * The claim IS the insert - a partial unique index on (jobName) WHERE finishedAt IS
103
+ * NULL makes a conflicting insert the concurrency guard. A stale in-flight run (older
104
+ * than the configured staleness window - presumed crashed) is marked `abandoned` and its slot freed
105
+ * for this call to retry the claim once.
106
+ */
107
+ private claimRun;
108
+ private retryClaim;
109
+ private finishRun;
110
+ }
111
+ export {};
112
+ //# sourceMappingURL=reconciliation.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciliation.service.d.ts","sourceRoot":"","sources":["../../../src/wallet/service/reconciliation.service.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EAQd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,IAAI,EAEV,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,2BAA2B,IAAI,8BAA8B,EAClE,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,+BAA+B,EAC/B,wBAAwB,EACxB,2BAA2B,IAAI,8BAA8B,EAC9D,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD,eAAO,MAAM,kCAAkC,2BAA6C,CAAC;AAE7F,eAAO,MAAM,4CAA4C,2BAExD,CAAC;AAEF,eAAO,MAAM,iCAAiC,iBAG7C,CAAC;AAEF,KAAK,WAAW,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC;AAErF;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,EACxD,EAAE,EAAE,WAAW,GAAG,SAAS,GAC1B,iBAAiB,GAAG,mBAAmB,GAAG,iBAAiB,GAAG,IAAI,CAWpE;AAmCD,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,KAAK,EAAE,cAAc,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0B;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiB;gBAErC,EACV,OAAO,EACP,MAAM,EACN,MAAM,EACN,gBAAgB,EAChB,KAAK,EACL,cAAc,GACf,EAAE,yBAAyB;IAStB,YAAY,CAAC,MAAM,EAAE,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;IAiC1D;;;;;;OAMG;IACG,cAAc,CAClB,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EACnB,EAAE,EAAE,8BAA8B,CAAC,IAAI,CAAC,EACxC,UAAU,EAAE,wBAAwB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,8BAA8B,CAAC;IAkF1C;;;;;;;OAOG;YACW,aAAa;IAyB3B;;;;;OAKG;IACG,QAAQ,CACZ,KAAK,GAAE,YAAY,CAAC,OAAO,CAAgB,GAC1C,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;YAqFrC,iBAAiB;YA2CjB,gBAAgB;YAahB,iBAAiB;YAgDjB,0BAA0B;YAoB1B,yBAAyB;YAmEzB,oBAAoB;YAsCpB,iBAAiB;IAQ/B;;;;;OAKG;YACW,QAAQ;YAkCR,UAAU;YAaV,SAAS;CAUxB"}