@mj-biz-apps/orders-core-entities-server 0.0.1 → 5.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.
Files changed (267) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +141 -2
  3. package/dist/AccountingBridge.d.ts +65 -0
  4. package/dist/AccountingBridge.d.ts.map +1 -0
  5. package/dist/AccountingBridge.js +102 -0
  6. package/dist/AccountingBridge.js.map +1 -0
  7. package/dist/AdvanceOrderStateOperation.d.ts +80 -0
  8. package/dist/AdvanceOrderStateOperation.d.ts.map +1 -0
  9. package/dist/AdvanceOrderStateOperation.js +267 -0
  10. package/dist/AdvanceOrderStateOperation.js.map +1 -0
  11. package/dist/ApplyAccountCreditOperation.d.ts +97 -0
  12. package/dist/ApplyAccountCreditOperation.d.ts.map +1 -0
  13. package/dist/ApplyAccountCreditOperation.js +275 -0
  14. package/dist/ApplyAccountCreditOperation.js.map +1 -0
  15. package/dist/BaseDeliveryChannel.d.ts +106 -0
  16. package/dist/BaseDeliveryChannel.d.ts.map +1 -0
  17. package/dist/BaseDeliveryChannel.js +36 -0
  18. package/dist/BaseDeliveryChannel.js.map +1 -0
  19. package/dist/BasePaymentProvider.d.ts +239 -0
  20. package/dist/BasePaymentProvider.d.ts.map +1 -0
  21. package/dist/BasePaymentProvider.js +91 -0
  22. package/dist/BasePaymentProvider.js.map +1 -0
  23. package/dist/BundleBehavior.d.ts +139 -0
  24. package/dist/BundleBehavior.d.ts.map +1 -0
  25. package/dist/BundleBehavior.js +173 -0
  26. package/dist/BundleBehavior.js.map +1 -0
  27. package/dist/BundleEngine.d.ts +60 -0
  28. package/dist/BundleEngine.d.ts.map +1 -0
  29. package/dist/BundleEngine.js +197 -0
  30. package/dist/BundleEngine.js.map +1 -0
  31. package/dist/CancelSubscriptionOperation.d.ts +98 -0
  32. package/dist/CancelSubscriptionOperation.d.ts.map +1 -0
  33. package/dist/CancelSubscriptionOperation.js +317 -0
  34. package/dist/CancelSubscriptionOperation.js.map +1 -0
  35. package/dist/CapturePaymentOperation.d.ts +62 -0
  36. package/dist/CapturePaymentOperation.d.ts.map +1 -0
  37. package/dist/CapturePaymentOperation.js +463 -0
  38. package/dist/CapturePaymentOperation.js.map +1 -0
  39. package/dist/CheckEntitlementOperation.d.ts +27 -0
  40. package/dist/CheckEntitlementOperation.d.ts.map +1 -0
  41. package/dist/CheckEntitlementOperation.js +45 -0
  42. package/dist/CheckEntitlementOperation.js.map +1 -0
  43. package/dist/CheckoutSessionService.d.ts +258 -0
  44. package/dist/CheckoutSessionService.d.ts.map +1 -0
  45. package/dist/CheckoutSessionService.js +1557 -0
  46. package/dist/CheckoutSessionService.js.map +1 -0
  47. package/dist/DeliveryBehavior.d.ts +121 -0
  48. package/dist/DeliveryBehavior.d.ts.map +1 -0
  49. package/dist/DeliveryBehavior.js +145 -0
  50. package/dist/DeliveryBehavior.js.map +1 -0
  51. package/dist/DeliveryRecipientResolver.d.ts +38 -0
  52. package/dist/DeliveryRecipientResolver.d.ts.map +1 -0
  53. package/dist/DeliveryRecipientResolver.js +93 -0
  54. package/dist/DeliveryRecipientResolver.js.map +1 -0
  55. package/dist/DeliveryResolver.d.ts +14 -0
  56. package/dist/DeliveryResolver.d.ts.map +1 -0
  57. package/dist/DeliveryResolver.js +45 -0
  58. package/dist/DeliveryResolver.js.map +1 -0
  59. package/dist/EmailDeliveryChannel.d.ts +31 -0
  60. package/dist/EmailDeliveryChannel.d.ts.map +1 -0
  61. package/dist/EmailDeliveryChannel.js +158 -0
  62. package/dist/EmailDeliveryChannel.js.map +1 -0
  63. package/dist/EntitlementBehavior.d.ts +235 -0
  64. package/dist/EntitlementBehavior.d.ts.map +1 -0
  65. package/dist/EntitlementBehavior.js +330 -0
  66. package/dist/EntitlementBehavior.js.map +1 -0
  67. package/dist/EntitlementEngine.d.ts +97 -0
  68. package/dist/EntitlementEngine.d.ts.map +1 -0
  69. package/dist/EntitlementEngine.js +338 -0
  70. package/dist/EntitlementEngine.js.map +1 -0
  71. package/dist/EntitlementGrantClaimDriver.d.ts +42 -0
  72. package/dist/EntitlementGrantClaimDriver.d.ts.map +1 -0
  73. package/dist/EntitlementGrantClaimDriver.js +158 -0
  74. package/dist/EntitlementGrantClaimDriver.js.map +1 -0
  75. package/dist/EntitlementRead.d.ts +82 -0
  76. package/dist/EntitlementRead.d.ts.map +1 -0
  77. package/dist/EntitlementRead.js +368 -0
  78. package/dist/EntitlementRead.js.map +1 -0
  79. package/dist/FulfillOrderLinesOperation.d.ts +34 -0
  80. package/dist/FulfillOrderLinesOperation.d.ts.map +1 -0
  81. package/dist/FulfillOrderLinesOperation.js +208 -0
  82. package/dist/FulfillOrderLinesOperation.js.map +1 -0
  83. package/dist/FulfillmentBehavior.d.ts +101 -0
  84. package/dist/FulfillmentBehavior.d.ts.map +1 -0
  85. package/dist/FulfillmentBehavior.js +145 -0
  86. package/dist/FulfillmentBehavior.js.map +1 -0
  87. package/dist/GLAccountResolver.d.ts +123 -0
  88. package/dist/GLAccountResolver.d.ts.map +1 -0
  89. package/dist/GLAccountResolver.js +175 -0
  90. package/dist/GLAccountResolver.js.map +1 -0
  91. package/dist/GetFulfillmentQueueOperation.d.ts +35 -0
  92. package/dist/GetFulfillmentQueueOperation.d.ts.map +1 -0
  93. package/dist/GetFulfillmentQueueOperation.js +221 -0
  94. package/dist/GetFulfillmentQueueOperation.js.map +1 -0
  95. package/dist/GetOverdueWorklistOperation.d.ts +41 -0
  96. package/dist/GetOverdueWorklistOperation.d.ts.map +1 -0
  97. package/dist/GetOverdueWorklistOperation.js +210 -0
  98. package/dist/GetOverdueWorklistOperation.js.map +1 -0
  99. package/dist/GiftCardBehavior.d.ts +97 -0
  100. package/dist/GiftCardBehavior.d.ts.map +1 -0
  101. package/dist/GiftCardBehavior.js +121 -0
  102. package/dist/GiftCardBehavior.js.map +1 -0
  103. package/dist/GiftCardEngine.d.ts +59 -0
  104. package/dist/GiftCardEngine.d.ts.map +1 -0
  105. package/dist/GiftCardEngine.js +197 -0
  106. package/dist/GiftCardEngine.js.map +1 -0
  107. package/dist/GuestOrderClaimDriver.d.ts +36 -0
  108. package/dist/GuestOrderClaimDriver.d.ts.map +1 -0
  109. package/dist/GuestOrderClaimDriver.js +161 -0
  110. package/dist/GuestOrderClaimDriver.js.map +1 -0
  111. package/dist/InvoiceBehavior.d.ts +394 -0
  112. package/dist/InvoiceBehavior.d.ts.map +1 -0
  113. package/dist/InvoiceBehavior.js +496 -0
  114. package/dist/InvoiceBehavior.js.map +1 -0
  115. package/dist/InvoiceBuilder.d.ts +48 -0
  116. package/dist/InvoiceBuilder.d.ts.map +1 -0
  117. package/dist/InvoiceBuilder.js +352 -0
  118. package/dist/InvoiceBuilder.js.map +1 -0
  119. package/dist/InvoiceDisplay.d.ts +96 -0
  120. package/dist/InvoiceDisplay.d.ts.map +1 -0
  121. package/dist/InvoiceDisplay.js +121 -0
  122. package/dist/InvoiceDisplay.js.map +1 -0
  123. package/dist/ListEntitlementsOperation.d.ts +21 -0
  124. package/dist/ListEntitlementsOperation.d.ts.map +1 -0
  125. package/dist/ListEntitlementsOperation.js +39 -0
  126. package/dist/ListEntitlementsOperation.js.map +1 -0
  127. package/dist/ManualPaymentProvider.d.ts +19 -0
  128. package/dist/ManualPaymentProvider.d.ts.map +1 -0
  129. package/dist/ManualPaymentProvider.js +92 -0
  130. package/dist/ManualPaymentProvider.js.map +1 -0
  131. package/dist/OrderEntityServer.d.ts +542 -0
  132. package/dist/OrderEntityServer.d.ts.map +1 -0
  133. package/dist/OrderEntityServer.js +2100 -0
  134. package/dist/OrderEntityServer.js.map +1 -0
  135. package/dist/OrderJournalEntryFactory.d.ts +140 -0
  136. package/dist/OrderJournalEntryFactory.d.ts.map +1 -0
  137. package/dist/OrderJournalEntryFactory.js +466 -0
  138. package/dist/OrderJournalEntryFactory.js.map +1 -0
  139. package/dist/OrderLineEntityServer.d.ts +90 -0
  140. package/dist/OrderLineEntityServer.d.ts.map +1 -0
  141. package/dist/OrderLineEntityServer.js +260 -0
  142. package/dist/OrderLineEntityServer.js.map +1 -0
  143. package/dist/OrdersSettings.d.ts +36 -0
  144. package/dist/OrdersSettings.d.ts.map +1 -0
  145. package/dist/OrdersSettings.js +147 -0
  146. package/dist/OrdersSettings.js.map +1 -0
  147. package/dist/PaymentAllocationFactory.d.ts +128 -0
  148. package/dist/PaymentAllocationFactory.d.ts.map +1 -0
  149. package/dist/PaymentAllocationFactory.js +235 -0
  150. package/dist/PaymentAllocationFactory.js.map +1 -0
  151. package/dist/PaymentHeaderEntityServer.d.ts +180 -0
  152. package/dist/PaymentHeaderEntityServer.d.ts.map +1 -0
  153. package/dist/PaymentHeaderEntityServer.js +656 -0
  154. package/dist/PaymentHeaderEntityServer.js.map +1 -0
  155. package/dist/PaymentIntentService.d.ts +101 -0
  156. package/dist/PaymentIntentService.d.ts.map +1 -0
  157. package/dist/PaymentIntentService.js +150 -0
  158. package/dist/PaymentIntentService.js.map +1 -0
  159. package/dist/PaymentJournalEntryFactory.d.ts +99 -0
  160. package/dist/PaymentJournalEntryFactory.d.ts.map +1 -0
  161. package/dist/PaymentJournalEntryFactory.js +127 -0
  162. package/dist/PaymentJournalEntryFactory.js.map +1 -0
  163. package/dist/PaymentLineEntityServer.d.ts +73 -0
  164. package/dist/PaymentLineEntityServer.d.ts.map +1 -0
  165. package/dist/PaymentLineEntityServer.js +303 -0
  166. package/dist/PaymentLineEntityServer.js.map +1 -0
  167. package/dist/PaymentProviderBehavior.d.ts +222 -0
  168. package/dist/PaymentProviderBehavior.d.ts.map +1 -0
  169. package/dist/PaymentProviderBehavior.js +368 -0
  170. package/dist/PaymentProviderBehavior.js.map +1 -0
  171. package/dist/PaymentProviderResolver.d.ts +84 -0
  172. package/dist/PaymentProviderResolver.d.ts.map +1 -0
  173. package/dist/PaymentProviderResolver.js +220 -0
  174. package/dist/PaymentProviderResolver.js.map +1 -0
  175. package/dist/PaymentReversalFactory.d.ts +107 -0
  176. package/dist/PaymentReversalFactory.d.ts.map +1 -0
  177. package/dist/PaymentReversalFactory.js +174 -0
  178. package/dist/PaymentReversalFactory.js.map +1 -0
  179. package/dist/PaymentSettlement.d.ts +56 -0
  180. package/dist/PaymentSettlement.d.ts.map +1 -0
  181. package/dist/PaymentSettlement.js +243 -0
  182. package/dist/PaymentSettlement.js.map +1 -0
  183. package/dist/PaymentTermsBehavior.d.ts +109 -0
  184. package/dist/PaymentTermsBehavior.d.ts.map +1 -0
  185. package/dist/PaymentTermsBehavior.js +172 -0
  186. package/dist/PaymentTermsBehavior.js.map +1 -0
  187. package/dist/PaymentWebhookHandler.d.ts +103 -0
  188. package/dist/PaymentWebhookHandler.d.ts.map +1 -0
  189. package/dist/PaymentWebhookHandler.js +246 -0
  190. package/dist/PaymentWebhookHandler.js.map +1 -0
  191. package/dist/PreviewPriceOperation.d.ts +62 -0
  192. package/dist/PreviewPriceOperation.d.ts.map +1 -0
  193. package/dist/PreviewPriceOperation.js +161 -0
  194. package/dist/PreviewPriceOperation.js.map +1 -0
  195. package/dist/PriceOrderOperation.d.ts +93 -0
  196. package/dist/PriceOrderOperation.d.ts.map +1 -0
  197. package/dist/PriceOrderOperation.js +146 -0
  198. package/dist/PriceOrderOperation.js.map +1 -0
  199. package/dist/ProductPriceEntityServer.d.ts +34 -0
  200. package/dist/ProductPriceEntityServer.d.ts.map +1 -0
  201. package/dist/ProductPriceEntityServer.js +97 -0
  202. package/dist/ProductPriceEntityServer.js.map +1 -0
  203. package/dist/RefundPaymentOperation.d.ts +61 -0
  204. package/dist/RefundPaymentOperation.d.ts.map +1 -0
  205. package/dist/RefundPaymentOperation.js +177 -0
  206. package/dist/RefundPaymentOperation.js.map +1 -0
  207. package/dist/RevenueRecognition.d.ts +76 -0
  208. package/dist/RevenueRecognition.d.ts.map +1 -0
  209. package/dist/RevenueRecognition.js +133 -0
  210. package/dist/RevenueRecognition.js.map +1 -0
  211. package/dist/ReversalBehavior.d.ts +82 -0
  212. package/dist/ReversalBehavior.d.ts.map +1 -0
  213. package/dist/ReversalBehavior.js +100 -0
  214. package/dist/ReversalBehavior.js.map +1 -0
  215. package/dist/ReversalResolver.d.ts +37 -0
  216. package/dist/ReversalResolver.d.ts.map +1 -0
  217. package/dist/ReversalResolver.js +96 -0
  218. package/dist/ReversalResolver.js.map +1 -0
  219. package/dist/SpawnRenewalsOperation.d.ts +109 -0
  220. package/dist/SpawnRenewalsOperation.d.ts.map +1 -0
  221. package/dist/SpawnRenewalsOperation.js +295 -0
  222. package/dist/SpawnRenewalsOperation.js.map +1 -0
  223. package/dist/StoredValuePaymentProvider.d.ts +52 -0
  224. package/dist/StoredValuePaymentProvider.d.ts.map +1 -0
  225. package/dist/StoredValuePaymentProvider.js +205 -0
  226. package/dist/StoredValuePaymentProvider.js.map +1 -0
  227. package/dist/StripeACHPaymentProvider.d.ts +50 -0
  228. package/dist/StripeACHPaymentProvider.d.ts.map +1 -0
  229. package/dist/StripeACHPaymentProvider.js +211 -0
  230. package/dist/StripeACHPaymentProvider.js.map +1 -0
  231. package/dist/StripePaymentProvider.d.ts +83 -0
  232. package/dist/StripePaymentProvider.d.ts.map +1 -0
  233. package/dist/StripePaymentProvider.js +442 -0
  234. package/dist/StripePaymentProvider.js.map +1 -0
  235. package/dist/SubscriptionBehavior.d.ts +197 -0
  236. package/dist/SubscriptionBehavior.d.ts.map +1 -0
  237. package/dist/SubscriptionBehavior.js +415 -0
  238. package/dist/SubscriptionBehavior.js.map +1 -0
  239. package/dist/checkoutCaptureAlert.d.ts +16 -0
  240. package/dist/checkoutCaptureAlert.d.ts.map +1 -0
  241. package/dist/checkoutCaptureAlert.js +55 -0
  242. package/dist/checkoutCaptureAlert.js.map +1 -0
  243. package/dist/checkoutCaptureRetry.d.ts +27 -0
  244. package/dist/checkoutCaptureRetry.d.ts.map +1 -0
  245. package/dist/checkoutCaptureRetry.js +50 -0
  246. package/dist/checkoutCaptureRetry.js.map +1 -0
  247. package/dist/claimDriverHelpers.d.ts +15 -0
  248. package/dist/claimDriverHelpers.d.ts.map +1 -0
  249. package/dist/claimDriverHelpers.js +34 -0
  250. package/dist/claimDriverHelpers.js.map +1 -0
  251. package/dist/entity-names.d.ts +15 -0
  252. package/dist/entity-names.d.ts.map +1 -0
  253. package/dist/entity-names.js +15 -0
  254. package/dist/entity-names.js.map +1 -0
  255. package/dist/identityClaimContracts.d.ts +118 -0
  256. package/dist/identityClaimContracts.d.ts.map +1 -0
  257. package/dist/identityClaimContracts.js +58 -0
  258. package/dist/identityClaimContracts.js.map +1 -0
  259. package/dist/index.d.ts +126 -0
  260. package/dist/index.d.ts.map +1 -0
  261. package/dist/index.js +115 -0
  262. package/dist/index.js.map +1 -0
  263. package/dist/sql-guards.d.ts +78 -0
  264. package/dist/sql-guards.d.ts.map +1 -0
  265. package/dist/sql-guards.js +115 -0
  266. package/dist/sql-guards.js.map +1 -0
  267. package/package.json +50 -5
@@ -0,0 +1,31 @@
1
+ import { BaseDeliveryChannel, type DeliverableDocument, type DeliveryAddressing, type DeliveryContext, type DeliveryResult } from './BaseDeliveryChannel.js';
2
+ export declare class EmailDeliveryChannel extends BaseDeliveryChannel {
3
+ /**
4
+ * Which MJ communication provider to send through.
5
+ *
6
+ * A GETTER, NOT AN ENV READ. This package's tsconfig sets `"types": []` — it has no Node globals
7
+ * by design, which is also why the signature code uses Web Crypto rather than `node:crypto`. So a
8
+ * deployment that sends through Microsoft Graph subclasses this and overrides the getter, and its
9
+ * `@RegisterClass(BaseDeliveryChannel, 'Email')` wins by load-order priority. That is the same
10
+ * escape hatch the payment drivers offer, and it keeps the choice in code somebody can read rather
11
+ * than in an environment variable nobody can find.
12
+ */
13
+ protected get ProviderName(): string;
14
+ Deliver(document: DeliverableDocument, addressing: DeliveryAddressing, context: DeliveryContext): Promise<DeliveryResult>;
15
+ /**
16
+ * Our vocabulary, translated into MJ's `Message`.
17
+ *
18
+ * `From` IS LEFT UNSET WHEN THE CALLER DID NOT SUPPLY ONE, rather than defaulted to something
19
+ * plausible. MJ's provider resolves its own configured sender, and a guess here — a no-reply
20
+ * address this app invented — would either be rejected by the provider's domain authentication or,
21
+ * worse, accepted and send the company's invoices from an address nobody monitors for replies.
22
+ *
23
+ * BOTH BODIES ARE SET when the caller supplied a plain-text alternative. An HTML-only message is
24
+ * one of the strongest spam signals there is, and an invoice landing in a junk folder is
25
+ * indistinguishable from one that was never sent.
26
+ */
27
+ private buildMessage;
28
+ }
29
+ /** Tree-shaking anchor — call from the server bootstrap so @RegisterClass is retained. */
30
+ export declare function LoadEmailDeliveryChannel(): void;
31
+ //# sourceMappingURL=EmailDeliveryChannel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailDeliveryChannel.d.ts","sourceRoot":"","sources":["../src/EmailDeliveryChannel.ts"],"names":[],"mappings":"AAqCA,OAAO,EACH,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,cAAc,EACtB,MAAM,0BAA0B,CAAC;AAclC,qBACa,oBAAqB,SAAQ,mBAAmB;IACzD;;;;;;;;;OASG;IACH,SAAS,KAAK,YAAY,IAAI,MAAM,CAEnC;IAEqB,OAAO,CACzB,QAAQ,EAAE,mBAAmB,EAC7B,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,eAAe,GACzB,OAAO,CAAC,cAAc,CAAC;IAiE1B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,YAAY;CAkBvB;AAED,0FAA0F;AAC1F,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
@@ -0,0 +1,158 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ /**
8
+ * EmailDeliveryChannel — sending a rendered document, over MJ's communication framework.
9
+ *
10
+ * THIS CLASS IS DELIBERATELY THIN, and the thinness is the design. MJ's `CommunicationEngine` already
11
+ * owns provider registration, credential resolution, template processing, dry-run preview, and — the
12
+ * one that matters most here — a persisted log row per message with its status and any error. Every
13
+ * one of those is a thing a hand-rolled mail path in this app would have had to grow, badly, over the
14
+ * following year. So this channel translates our vocabulary into MJ's and gets out of the way.
15
+ *
16
+ * THE LOG IS WHY THERE IS NO ORDERS-SIDE DELIVERY TABLE YET. `CommunicationEngine.SendSingleMessage`
17
+ * writes a `MJ: Communication Logs` row for every send, carrying the recipient, the body as sent, the
18
+ * status and the failure reason. That answers "was it sent, to whom, and did it work" today, without
19
+ * a schema change. What it does NOT answer is "has THIS ORDER been invoiced, and when" — that needs a
20
+ * row keyed by the order, and it is the deliberate next step rather than something to fake here by
21
+ * stuffing an order id into a log message.
22
+ *
23
+ * ONE MESSAGE PER RECIPIENT, NOT ONE MESSAGE WITH MANY RECIPIENTS. Two reasons, and the second is the
24
+ * one that decided it. A single message would expose every billing contact's address to all the
25
+ * others — a real disclosure when a customer's contacts span two organisations. And a partial failure
26
+ * on a multi-recipient send is unattributable: the provider reports one status for the message, so
27
+ * "delivered to two of three" is indistinguishable from "delivered", and nobody learns which contact
28
+ * did not get the bill.
29
+ *
30
+ * A PARTIAL SEND IS A FAILURE, reported with the addresses that DID work. The alternative — reporting
31
+ * success because at least one landed — is how an invoice quietly stops reaching the person who
32
+ * actually pays it while the send log stays green.
33
+ *
34
+ * CONNECTS TO:
35
+ * BASE: ./BaseDeliveryChannel.ts
36
+ * PURE: ./DeliveryBehavior.ts
37
+ * MJ: @memberjunction/communication-engine
38
+ * DOC: plans/archive/bizapps-orders-master.md §4.4
39
+ */
40
+ import { LogError, LogStatus } from '@memberjunction/core';
41
+ import { RegisterClass } from '@memberjunction/global';
42
+ import { CommunicationEngine } from '@memberjunction/communication-engine';
43
+ import { Message } from '@memberjunction/communication-types';
44
+ import { BaseDeliveryChannel, } from './BaseDeliveryChannel.js';
45
+ /**
46
+ * The MJ communication provider used when the app has not been told otherwise.
47
+ *
48
+ * SendGrid because it is the provider MJ ships an email implementation for and the one most
49
+ * deployments already configure. It is a constant rather than a hard-coded string in five places so
50
+ * a deployment that uses Microsoft Graph changes it here, and so this comment has somewhere to live.
51
+ */
52
+ const DEFAULT_PROVIDER = 'SendGrid';
53
+ /** MJ's own name for the message type. Not ours to invent — it is looked up in MJ's metadata. */
54
+ const MESSAGE_TYPE = 'Email';
55
+ let EmailDeliveryChannel = class EmailDeliveryChannel extends BaseDeliveryChannel {
56
+ /**
57
+ * Which MJ communication provider to send through.
58
+ *
59
+ * A GETTER, NOT AN ENV READ. This package's tsconfig sets `"types": []` — it has no Node globals
60
+ * by design, which is also why the signature code uses Web Crypto rather than `node:crypto`. So a
61
+ * deployment that sends through Microsoft Graph subclasses this and overrides the getter, and its
62
+ * `@RegisterClass(BaseDeliveryChannel, 'Email')` wins by load-order priority. That is the same
63
+ * escape hatch the payment drivers offer, and it keeps the choice in code somebody can read rather
64
+ * than in an environment variable nobody can find.
65
+ */
66
+ get ProviderName() {
67
+ return DEFAULT_PROVIDER;
68
+ }
69
+ async Deliver(document, addressing, context) {
70
+ const recipients = (addressing.To ?? []).filter((r) => (r.Address ?? '').trim());
71
+ if (!recipients.length) {
72
+ // A REFUSAL, not a throw: nothing is broken, there is simply nobody to send to. The caller
73
+ // should have asked `DecideDelivery` first, and this is the backstop for the one who did not.
74
+ return { Success: false, Delivered: [], Reason: 'no recipient address was supplied' };
75
+ }
76
+ const engine = CommunicationEngine.Instance;
77
+ await engine.Config(false, context.User, context.Provider);
78
+ const delivered = [];
79
+ const failures = [];
80
+ let providerMessageID;
81
+ for (const recipient of recipients) {
82
+ const address = recipient.Address.trim();
83
+ const message = this.buildMessage(document, addressing, address, recipient.FullName ?? undefined);
84
+ try {
85
+ const result = await engine.SendSingleMessage(this.ProviderName, MESSAGE_TYPE, message, undefined, context.PreviewOnly === true);
86
+ if (result?.Success) {
87
+ delivered.push(address);
88
+ providerMessageID ??= result.Message?.MessageType?.ID;
89
+ }
90
+ else {
91
+ failures.push(`${address}: ${result?.Error || 'the provider reported a failure with no reason'}`);
92
+ }
93
+ }
94
+ catch (err) {
95
+ // A FAULT — the provider was unreachable or threw. It is caught rather than propagated
96
+ // so one unreachable recipient does not abandon the rest, and it is recorded as a
97
+ // failure so the overall result cannot come back green.
98
+ const reason = err instanceof Error ? err.message : String(err);
99
+ LogError(`Delivering ${document.DocumentNumber} to ${address} threw: ${reason}`);
100
+ failures.push(`${address}: ${reason}`);
101
+ }
102
+ }
103
+ if (failures.length) {
104
+ // Partial success is reported as failure WITH the addresses that worked, so a retry can
105
+ // skip them. Reporting success because one landed is how a bill quietly stops reaching the
106
+ // person who pays it.
107
+ return {
108
+ Success: false,
109
+ Delivered: delivered,
110
+ ProviderMessageID: providerMessageID,
111
+ Reason: `${document.DocumentNumber} reached ${delivered.length} of ${recipients.length} recipients. ` +
112
+ `Failures: ${failures.join('; ')}`,
113
+ };
114
+ }
115
+ LogStatus(`${document.Kind} ${document.DocumentNumber} ${context.PreviewOnly ? 'previewed for' : 'sent to'} ` +
116
+ `${delivered.join(', ')}.`);
117
+ return { Success: true, Delivered: delivered, ProviderMessageID: providerMessageID };
118
+ }
119
+ /**
120
+ * Our vocabulary, translated into MJ's `Message`.
121
+ *
122
+ * `From` IS LEFT UNSET WHEN THE CALLER DID NOT SUPPLY ONE, rather than defaulted to something
123
+ * plausible. MJ's provider resolves its own configured sender, and a guess here — a no-reply
124
+ * address this app invented — would either be rejected by the provider's domain authentication or,
125
+ * worse, accepted and send the company's invoices from an address nobody monitors for replies.
126
+ *
127
+ * BOTH BODIES ARE SET when the caller supplied a plain-text alternative. An HTML-only message is
128
+ * one of the strongest spam signals there is, and an invoice landing in a junk folder is
129
+ * indistinguishable from one that was never sent.
130
+ */
131
+ buildMessage(document, addressing, address, fullName) {
132
+ const message = new Message();
133
+ message.To = address;
134
+ message.Subject = document.Subject;
135
+ message.HTMLBody = document.BodyHtml;
136
+ if (document.BodyPlain)
137
+ message.Body = document.BodyPlain;
138
+ if (addressing.From)
139
+ message.From = addressing.From;
140
+ if (addressing.FromName)
141
+ message.FromName = addressing.FromName;
142
+ if (addressing.CC?.length)
143
+ message.CCRecipients = [...addressing.CC];
144
+ if (addressing.BCC?.length)
145
+ message.BCCRecipients = [...addressing.BCC];
146
+ void fullName; // MJ's Message carries no per-recipient display name; kept for the seam's shape.
147
+ return message;
148
+ }
149
+ };
150
+ EmailDeliveryChannel = __decorate([
151
+ RegisterClass(BaseDeliveryChannel, 'Email')
152
+ ], EmailDeliveryChannel);
153
+ export { EmailDeliveryChannel };
154
+ /** Tree-shaking anchor — call from the server bootstrap so @RegisterClass is retained. */
155
+ export function LoadEmailDeliveryChannel() {
156
+ // intentionally empty
157
+ }
158
+ //# sourceMappingURL=EmailDeliveryChannel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailDeliveryChannel.js","sourceRoot":"","sources":["../src/EmailDeliveryChannel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * What a purchase grants, and for how long — the arithmetic, with no database in sight.
3
+ *
4
+ * A `ProductEntitlement` is a TEMPLATE: this product confers this feature, access level, or resource
5
+ * quantity. An `EntitlementGrant` is the INSTANCE created when somebody buys it, carrying a
6
+ * beneficiary and a validity window. This module decides what that instance should look like.
7
+ *
8
+ * THREE SETTINGS, ONE WALK (D76). Timing, quantity and validity are all configurable per deployment,
9
+ * and all three resolve down the SAME chain taxability uses:
10
+ *
11
+ * product → its category → that category's ancestors → the product type
12
+ *
13
+ * Nullable at product and category so "ask my parent" is sayable; NOT NULL at the type so the walk
14
+ * always terminates with a real answer. Sharing the chain is deliberate rather than tidy: anyone who
15
+ * understands how taxability resolves already understands this, and the two cannot drift into
16
+ * disagreeing about what "the product's category tree" means.
17
+ *
18
+ * WHY VALIDITY IS DIFFERENT FROM THE OTHER TWO. It is read from the `ProductEntitlement` FIRST and
19
+ * from the walk only when the template is silent, because one product can grant two entitlements with
20
+ * different windows. A course might give perpetual access to the downloadable materials and ninety
21
+ * days in the discussion forum. A policy resolved purely from the product could not say that.
22
+ *
23
+ * THE NON-SUBSCRIPTION CASES ARE THE INTERESTING ONES. It is tempting to assume access follows a
24
+ * subscription term, because that is the case with the most moving parts. But the commonest grants
25
+ * have nothing to do with subscriptions at all: a digital download is good forever, and a ticket to
26
+ * an online event is good for the length of the event and not a minute longer.
27
+ *
28
+ * CONNECTS TO:
29
+ * SERVER: ./EntitlementEngine.ts (the lookups and the writes)
30
+ * CALLER: OrderEntityServer (grants are created inside the booking transaction)
31
+ * MIRROR: ./TaxResolver.ts — the same walk, for the same reason
32
+ * DOC: plans/archive/bizapps-orders-master.md D27, D76
33
+ */
34
+ /** When access begins. */
35
+ export type GrantTiming = 'OnConfirm' | 'OnPaidInFull' | 'OnActivation';
36
+ /** How the template quantity relates to the line quantity. */
37
+ export type QuantityMode = 'PerUnit' | 'Flat';
38
+ /** How long access lasts. */
39
+ export type ValidityMode = 'Perpetual' | 'EventWindow' | 'FixedDuration' | 'SubscriptionTerm';
40
+ /** Where in the chain an answer came from. Recorded because "nobody said" differs from "it said no". */
41
+ export type PolicyLevel = 'ProductEntitlement' | 'Product' | 'ProductCategory' | 'ProductType';
42
+ export interface EntitlementPolicySource {
43
+ EntitlementGrantTiming: GrantTiming | null;
44
+ EntitlementQuantityMode: QuantityMode | null;
45
+ EntitlementValidityMode: ValidityMode | null;
46
+ }
47
+ /** One level of the category tree. Callers build these NEAREST FIRST. */
48
+ export interface PolicyCategoryLevel extends EntitlementPolicySource {
49
+ ID: string;
50
+ }
51
+ /** The type's answers are NOT NULL — this is what makes the walk terminate. */
52
+ export interface PolicyTypeDefaults {
53
+ DefaultEntitlementGrantTiming: GrantTiming;
54
+ DefaultEntitlementQuantityMode: QuantityMode;
55
+ DefaultEntitlementValidityMode: ValidityMode;
56
+ }
57
+ export interface ResolvedEntitlementPolicy {
58
+ GrantTiming: GrantTiming;
59
+ QuantityMode: QuantityMode;
60
+ ValidityMode: ValidityMode;
61
+ /** Which level decided each one, for the support question "why did this happen?". */
62
+ DecidedAt: {
63
+ GrantTiming: PolicyLevel;
64
+ QuantityMode: PolicyLevel;
65
+ ValidityMode: PolicyLevel;
66
+ };
67
+ }
68
+ /**
69
+ * Walk the chain for all three settings at once.
70
+ *
71
+ * ONE walk rather than three, because they resolve identically and a caller that walked separately
72
+ * would do the same lookups three times and could be given three different category chains.
73
+ *
74
+ * `entitlement` is consulted for VALIDITY ONLY — see the module header. Timing and quantity are
75
+ * properties of the purchase, not of the individual thing granted: it would make no sense for one
76
+ * entitlement on a line to appear at confirm and another to wait for payment.
77
+ */
78
+ export declare function ResolveEntitlementPolicy(product: EntitlementPolicySource,
79
+ /** NEAREST FIRST: the product's own category, then its parent, up to the root. */
80
+ categoryChain: PolicyCategoryLevel[], type: PolicyTypeDefaults, entitlement?: {
81
+ ValidityMode: ValidityMode | null;
82
+ } | null): ResolvedEntitlementPolicy;
83
+ /**
84
+ * How much of the entitlement this line grants.
85
+ *
86
+ * `PerUnit` multiplies, so three 5-seat packs give fifteen seats — which is what somebody buying
87
+ * three packs expects. `Flat` ignores the line quantity entirely, for entitlements that are not
88
+ * countable: "access to the member portal" does not become three portals.
89
+ *
90
+ * ROUNDED UP, and only ever up. A prorated subscription line carries a fractional quantity (0.5833
91
+ * for a short first period), and 0.5833 × 5 seats is 2.9165. Rounding down hands the customer two
92
+ * seats for a five-seat product and generates a support ticket; rounding up hands them three and
93
+ * generates nothing. Under-granting is the expensive direction, exactly as under-collecting is with
94
+ * tax, and for the same reason: the customer notices.
95
+ *
96
+ * A null template quantity means the entitlement is not a countable thing (a Feature or AccessLevel
97
+ * rather than a ResourceQuantity), and stays null rather than becoming zero — zero would read as
98
+ * "granted none of it".
99
+ */
100
+ export declare function ResolveGrantQuantity(templateQuantity: number | null, lineQuantity: number, mode: QuantityMode): number | null;
101
+ /** Everything a validity window might need to know, so the pure function needs no lookups. */
102
+ export interface ValidityContext {
103
+ /** When the grant takes effect — normally the order date. */
104
+ GrantedOn: Date;
105
+ /** FixedDuration only. */
106
+ DurationDays?: number | null;
107
+ /** EventWindow only: the event's own dates, and how far either side access opens. */
108
+ EventStartsAt?: Date | null;
109
+ EventEndsAt?: Date | null;
110
+ AccessLeadHours?: number | null;
111
+ AccessLagHours?: number | null;
112
+ /** SubscriptionTerm only. */
113
+ TermStartDate?: Date | null;
114
+ TermEndDate?: Date | null;
115
+ }
116
+ export interface ResolvedValidity {
117
+ ValidFrom: Date;
118
+ /** NULL means perpetual. A grant with no end is a real thing, not a missing value. */
119
+ ValidTo: Date | null;
120
+ ModeApplied: ValidityMode;
121
+ }
122
+ /**
123
+ * Turn a validity mode plus its context into an actual window.
124
+ *
125
+ * FALLS BACK RATHER THAN THROWING when a mode's own inputs are missing, and records what it actually
126
+ * applied. A ticket whose product has no event dates should not fail the customer's order — but the
127
+ * grant must not silently claim to be an event window either, so `ModeApplied` says `Perpetual` and
128
+ * an auditor can see the mode was requested and could not be honoured.
129
+ *
130
+ * The one thing it will not do is produce a window that ends before it starts. An event whose end
131
+ * date precedes its start, or a lag that somehow lands behind the lead, yields a window ending at its
132
+ * own start — an instantaneous grant, which reads as "nothing was granted" and is at least honest,
133
+ * rather than a negative window the database CHECK would reject at insert time with an error naming
134
+ * neither the product nor the event.
135
+ */
136
+ export declare function ResolveValidityWindow(mode: ValidityMode, ctx: ValidityContext): ResolvedValidity;
137
+ /**
138
+ * Should the grant be ACTIVE yet, given when access is supposed to begin?
139
+ *
140
+ * The grant row is written at confirm either way — downstream apps poll grants (D27), and a grant
141
+ * that does not exist until payment clears cannot be seen coming. What timing changes is the STATUS:
142
+ *
143
+ * OnConfirm Active immediately. A confirmed order is a claim on the seller.
144
+ * OnPaidInFull Suspended until the order's balance reaches zero. Access follows cash.
145
+ * OnActivation Suspended until something explicitly activates it. Note that nothing does yet —
146
+ * this exists because it was asked for, and a deployment choosing it is choosing to
147
+ * grant nothing until it builds the caller.
148
+ *
149
+ * `Suspended` rather than a missing row, and rather than `Revoked`: revoked means somebody took it
150
+ * away, which is a different fact that an access dispute turns on.
151
+ */
152
+ export declare function InitialGrantStatus(timing: GrantTiming, order: {
153
+ Balance: number | null;
154
+ TotalGross: number | null;
155
+ }): 'Active' | 'Suspended';
156
+ /**
157
+ * How much of a grant survives a partial return.
158
+ *
159
+ * A customer who bought five seats and sent two back keeps three. Returning everything revokes the
160
+ * grant outright rather than leaving a zero-quantity row, because zero is indistinguishable from a
161
+ * Feature grant that never had a quantity.
162
+ */
163
+ export declare function ReduceGrantForReturn(grantQuantity: number | null, originalLineQuantity: number, returnedQuantity: number): {
164
+ Quantity: number | null;
165
+ Revoke: boolean;
166
+ };
167
+ /** Why a check granted or refused. Collapsing these to a boolean loses the support conversation. */
168
+ export type EntitlementDecision = 'Granted' | 'NoGrant' | 'NotYetValid' | 'Expired' | 'Revoked' | 'Suspended' | 'SubscriptionInactive';
169
+ /** The facts a grant row carries that the evaluator needs — no database. */
170
+ export interface GrantAccessFacts {
171
+ Status: string;
172
+ ValidFrom: Date | null;
173
+ ValidTo: Date | null;
174
+ /** True when `EntitlementGrant.SubscriptionID` is set. Missing subscription row → fail closed. */
175
+ LinkedToSubscription?: boolean;
176
+ /** True when `EntitlementGrant.SubscriptionTermID` is set. Missing term row → fail closed. */
177
+ LinkedToTerm?: boolean;
178
+ }
179
+ /** Subscription facts that can cut access short or extend it through grace. */
180
+ export interface SubscriptionAccessFacts {
181
+ Status: string;
182
+ /**
183
+ * When the subscription is `Canceled`, this is access-through (`EndDate` stamped from
184
+ * `CancellationDecision.AccessThroughDate`). Ignored while the sub is Active/Trialing.
185
+ */
186
+ EndDate: Date | null;
187
+ }
188
+ export interface TermAccessFacts {
189
+ Status: string;
190
+ StartDate: Date;
191
+ EndDate: Date;
192
+ }
193
+ export interface GrantAccessEvaluation {
194
+ HasAccess: boolean;
195
+ Decision: EntitlementDecision;
196
+ ValidFrom: Date | null;
197
+ ValidTo: Date | null;
198
+ }
199
+ /**
200
+ * Is this grant in force at `asOf`?
201
+ *
202
+ * ONE function, because a second “is this in force” will drift from the first — that failure
203
+ * has already bitten this codebase twice. Bounds are inclusive: access holds at the exact
204
+ * `ValidFrom` / `ValidTo` / cancel `EndDate` instant.
205
+ *
206
+ * Cancelled subscriptions are the interesting case. The grant's `ValidTo` is the original term
207
+ * end, which is a LIE after an immediate cancel: access-through is `subscription.EndDate`
208
+ * (grace included). Grace can also EXTEND past `ValidTo`. Either way, `EndDate` is the bound
209
+ * once the sub is `Canceled`. Paused/Migrated refuse immediately — they have no grace story.
210
+ */
211
+ export declare function EvaluateGrantAccess(grant: GrantAccessFacts, asOf: Date, subscription?: SubscriptionAccessFacts | null, term?: TermAccessFacts | null): GrantAccessEvaluation;
212
+ export interface RankableAccess {
213
+ HasAccess: boolean;
214
+ Decision: EntitlementDecision;
215
+ ValidTo: Date | null;
216
+ }
217
+ /**
218
+ * One answer from many grants for the same Code. A Granted perpetual beats a Granted with an
219
+ * end; among denials the most useful reason wins, not the most recent row.
220
+ */
221
+ export declare function PickWinningAccess<T extends RankableAccess>(results: T[]): T | null;
222
+ /** Default CacheUntil cap for a check answer. Short so a revoke is felt quickly. */
223
+ export declare const ENTITLEMENT_CHECK_TTL_MS = 60000;
224
+ /**
225
+ * When the caller may reuse this answer. Never past `ValidTo` on a grant that is in force —
226
+ * caching a Granted past its own end is how paid content stays open after the window closed.
227
+ */
228
+ export declare function CacheUntilFor(evaluatedAt: Date, validTo: Date | null | undefined, hasAccess: boolean): Date;
229
+ /**
230
+ * After `Orders.CancelSubscription`, stored grant Status is a lie unless we revoke when
231
+ * access has already ended. Grace (`AccessThroughDate` in the future) leaves the rows
232
+ * standing — the evaluator honours `subscription.EndDate`.
233
+ */
234
+ export declare function ShouldRevokeGrantsOnCancel(accessThroughDate: Date, now: Date): boolean;
235
+ //# sourceMappingURL=EntitlementBehavior.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntitlementBehavior.d.ts","sourceRoot":"","sources":["../src/EntitlementBehavior.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,0BAA0B;AAC1B,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAExE,8DAA8D;AAC9D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9C,6BAA6B;AAC7B,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,aAAa,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAE9F,wGAAwG;AACxG,MAAM,MAAM,WAAW,GAAG,oBAAoB,GAAG,SAAS,GAAG,iBAAiB,GAAG,aAAa,CAAC;AAE/F,MAAM,WAAW,uBAAuB;IACpC,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C,uBAAuB,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7C,uBAAuB,EAAE,YAAY,GAAG,IAAI,CAAC;CAChD;AAED,yEAAyE;AACzE,MAAM,WAAW,mBAAoB,SAAQ,uBAAuB;IAChE,EAAE,EAAE,MAAM,CAAC;CACd;AAED,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IAC/B,6BAA6B,EAAE,WAAW,CAAC;IAC3C,8BAA8B,EAAE,YAAY,CAAC;IAC7C,8BAA8B,EAAE,YAAY,CAAC;CAChD;AAED,MAAM,WAAW,yBAAyB;IACtC,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,qFAAqF;IACrF,SAAS,EAAE;QAAE,WAAW,EAAE,WAAW,CAAC;QAAC,YAAY,EAAE,WAAW,CAAC;QAAC,YAAY,EAAE,WAAW,CAAA;KAAE,CAAC;CACjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,uBAAuB;AAChC,kFAAkF;AAClF,aAAa,EAAE,mBAAmB,EAAE,EACpC,IAAI,EAAE,kBAAkB,EACxB,WAAW,CAAC,EAAE;IAAE,YAAY,EAAE,YAAY,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,GAC3D,yBAAyB,CAiC3B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAC/B,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,YAAY,GACnB,MAAM,GAAG,IAAI,CAKf;AAED,8FAA8F;AAC9F,MAAM,WAAW,eAAe;IAC5B,6DAA6D;IAC7D,SAAS,EAAE,IAAI,CAAC;IAChB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qFAAqF;IACrF,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,6BAA6B;IAC7B,aAAa,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,IAAI,CAAC;IAChB,sFAAsF;IACtF,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,YAAY,CAAC;CAC7B;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,eAAe,GAAG,gBAAgB,CA4ChG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7D,QAAQ,GAAG,WAAW,CASxB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAChC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,oBAAoB,EAAE,MAAM,EAC5B,gBAAgB,EAAE,MAAM,GACzB;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAe9C;AASD,oGAAoG;AACpG,MAAM,MAAM,mBAAmB,GACzB,SAAS,GACT,SAAS,GACT,aAAa,GACb,SAAS,GACT,SAAS,GACT,WAAW,GACX,sBAAsB,CAAC;AAE7B,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,kGAAkG;IAClG,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,8FAA8F;IAC9F,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,IAAI,EACV,YAAY,CAAC,EAAE,uBAAuB,GAAG,IAAI,EAC7C,IAAI,CAAC,EAAE,eAAe,GAAG,IAAI,GAC9B,qBAAqB,CA4DvB;AAaD,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,CAalF;AAED,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,aAAa,CACzB,WAAW,EAAE,IAAI,EACjB,OAAO,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAChC,SAAS,EAAE,OAAO,GACnB,IAAI,CAMN;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,iBAAiB,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAEtF"}