@aglyn/plugins-commerce 1.0.0-beta.277 → 1.0.0-beta.282

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 (212) hide show
  1. package/package.json +17 -14
  2. package/src/lib/components/commerce-console-page.js +33 -1
  3. package/src/lib/components/commerce-console-page.js.map +1 -1
  4. package/src/lib/components/console/payments-settings-card.component.js +1 -1
  5. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  6. package/src/lib/components/console/pos/pos-hardware-api.d.ts +122 -0
  7. package/src/lib/components/console/pos/pos-hardware-api.js +90 -0
  8. package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -0
  9. package/src/lib/components/console/pos/pos-reader-splash-api.d.ts +58 -0
  10. package/src/lib/components/console/pos/pos-reader-splash-api.js +101 -0
  11. package/src/lib/components/console/pos/pos-reader-splash-api.js.map +1 -0
  12. package/src/lib/components/console/pos-devices-card.component.js +5 -0
  13. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  14. package/src/lib/components/console/pos-hardware-card.component.d.ts +26 -0
  15. package/src/lib/components/console/pos-hardware-card.component.js +456 -0
  16. package/src/lib/components/console/pos-hardware-card.component.js.map +1 -0
  17. package/src/lib/components/console/pos-hardware-orders-staff-page.component.d.ts +14 -0
  18. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +519 -0
  19. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -0
  20. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.d.ts +27 -0
  21. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +444 -0
  22. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -0
  23. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.d.ts +9 -0
  24. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js +37 -0
  25. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js.map +1 -0
  26. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +24 -0
  27. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +346 -0
  28. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -0
  29. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +5 -1
  30. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +83 -4
  31. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -1
  32. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.d.ts +35 -0
  33. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js +546 -0
  34. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js.map +1 -0
  35. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +2 -1
  36. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +20 -1
  37. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
  38. package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +1 -1
  39. package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -1
  40. package/src/lib/components/console/pos-page.component.js +6 -3
  41. package/src/lib/components/console/pos-page.component.js.map +1 -1
  42. package/src/lib/components/console/pos-reader-splash-setting.component.d.ts +22 -0
  43. package/src/lib/components/console/pos-reader-splash-setting.component.js +275 -0
  44. package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -0
  45. package/src/lib/components/console/pos-reader-splash-staff-page.component.d.ts +14 -0
  46. package/src/lib/components/console/pos-reader-splash-staff-page.component.js +301 -0
  47. package/src/lib/components/console/pos-reader-splash-staff-page.component.js.map +1 -0
  48. package/src/lib/components/console/pos-zones.d.ts +37 -3
  49. package/src/lib/components/console/pos-zones.js +11 -1
  50. package/src/lib/components/console/pos-zones.js.map +1 -1
  51. package/src/lib/components/console/product-check-in-fields.component.d.ts +20 -0
  52. package/src/lib/components/console/product-check-in-fields.component.js +172 -0
  53. package/src/lib/components/console/product-check-in-fields.component.js.map +1 -0
  54. package/src/lib/components/console/product-editor-dialog.component.js +48 -9
  55. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  56. package/src/lib/components/console/product-register-fields.component.d.ts +12 -0
  57. package/src/lib/components/console/product-register-fields.component.js +39 -1
  58. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  59. package/src/lib/components/console/products-hub-card.component.js +21 -6
  60. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  61. package/src/lib/components/console/reservations-card.component.js +239 -70
  62. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  63. package/src/lib/components/reservation-widget.js +16 -0
  64. package/src/lib/components/reservation-widget.js.map +1 -1
  65. package/src/lib/constants/operator-alerts.d.ts +6 -0
  66. package/src/lib/constants/operator-alerts.js +20 -1
  67. package/src/lib/constants/operator-alerts.js.map +1 -1
  68. package/src/lib/declarations.console-server.js +7 -0
  69. package/src/lib/declarations.console-server.js.map +1 -1
  70. package/src/lib/declarations.d.ts +26 -0
  71. package/src/lib/declarations.js +34 -0
  72. package/src/lib/declarations.js.map +1 -0
  73. package/src/lib/model/commerce-check-in-product.d.ts +37 -0
  74. package/src/lib/model/commerce-check-in-product.js +49 -0
  75. package/src/lib/model/commerce-check-in-product.js.map +1 -0
  76. package/src/lib/model/commerce-check-in.d.ts +187 -0
  77. package/src/lib/model/commerce-check-in.js +468 -0
  78. package/src/lib/model/commerce-check-in.js.map +1 -0
  79. package/src/lib/model/commerce-pos-day-report.d.ts +209 -0
  80. package/src/lib/model/commerce-pos-day-report.js +572 -0
  81. package/src/lib/model/commerce-pos-day-report.js.map +1 -0
  82. package/src/lib/model/commerce-pos-offline.d.ts +65 -3
  83. package/src/lib/model/commerce-pos-offline.js +104 -7
  84. package/src/lib/model/commerce-pos-offline.js.map +1 -1
  85. package/src/lib/model/commerce-pos-ops.d.ts +3 -0
  86. package/src/lib/model/commerce-pos-ops.js +1 -0
  87. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  88. package/src/lib/model/commerce-pos.d.ts +43 -3
  89. package/src/lib/model/commerce-pos.js +73 -5
  90. package/src/lib/model/commerce-pos.js.map +1 -1
  91. package/src/lib/model/commerce-reservations.d.ts +73 -7
  92. package/src/lib/model/commerce-reservations.js +90 -10
  93. package/src/lib/model/commerce-reservations.js.map +1 -1
  94. package/src/lib/model/commerce.d.ts +40 -0
  95. package/src/lib/model/commerce.js +22 -4
  96. package/src/lib/model/commerce.js.map +1 -1
  97. package/src/lib/model/index.d.ts +1 -0
  98. package/src/lib/model/index.js +1 -0
  99. package/src/lib/model/index.js.map +1 -1
  100. package/src/lib/model/plugin-permissions.d.ts +1 -1
  101. package/src/lib/model/plugin-permissions.js +2 -1
  102. package/src/lib/model/plugin-permissions.js.map +1 -1
  103. package/src/lib/model/pos-hardware.d.ts +288 -0
  104. package/src/lib/model/pos-hardware.js +499 -0
  105. package/src/lib/model/pos-hardware.js.map +1 -0
  106. package/src/lib/model/pos-offline-card-limits.d.ts +20 -0
  107. package/src/lib/model/pos-offline-card-limits.js +28 -0
  108. package/src/lib/model/pos-offline-card-limits.js.map +1 -0
  109. package/src/lib/model/pos-reader-splash.d.ts +114 -0
  110. package/src/lib/model/pos-reader-splash.js +141 -0
  111. package/src/lib/model/pos-reader-splash.js.map +1 -0
  112. package/src/lib/model/product-write.d.ts +6 -0
  113. package/src/lib/plugin-config.d.ts +10 -0
  114. package/src/lib/plugin-config.js +40 -0
  115. package/src/lib/plugin-config.js.map +1 -1
  116. package/src/lib/plugin.js +28 -0
  117. package/src/lib/plugin.js.map +1 -1
  118. package/src/lib/server/cart-checkout.js +3 -1
  119. package/src/lib/server/cart-checkout.js.map +1 -1
  120. package/src/lib/server/cart.js +12 -1
  121. package/src/lib/server/cart.js.map +1 -1
  122. package/src/lib/server/catalog.js +49 -12
  123. package/src/lib/server/catalog.js.map +1 -1
  124. package/src/lib/server/checkout-extras.js +1 -1
  125. package/src/lib/server/checkout-extras.js.map +1 -1
  126. package/src/lib/server/checkout.js +7 -0
  127. package/src/lib/server/checkout.js.map +1 -1
  128. package/src/lib/server/person-eraser.d.ts +8 -1
  129. package/src/lib/server/person-eraser.js +23 -1
  130. package/src/lib/server/person-eraser.js.map +1 -1
  131. package/src/lib/server/pos-check-in.d.ts +49 -0
  132. package/src/lib/server/pos-check-in.js +652 -0
  133. package/src/lib/server/pos-check-in.js.map +1 -0
  134. package/src/lib/server/pos-day-report.d.ts +46 -0
  135. package/src/lib/server/pos-day-report.js +299 -0
  136. package/src/lib/server/pos-day-report.js.map +1 -0
  137. package/src/lib/server/pos-hardware-admin.d.ts +50 -0
  138. package/src/lib/server/pos-hardware-admin.js +366 -0
  139. package/src/lib/server/pos-hardware-admin.js.map +1 -0
  140. package/src/lib/server/pos-hardware-notify.d.ts +27 -0
  141. package/src/lib/server/pos-hardware-notify.js +142 -0
  142. package/src/lib/server/pos-hardware-notify.js.map +1 -0
  143. package/src/lib/server/pos-hardware-user-eraser.d.ts +26 -0
  144. package/src/lib/server/pos-hardware-user-eraser.js +67 -0
  145. package/src/lib/server/pos-hardware-user-eraser.js.map +1 -0
  146. package/src/lib/server/pos-hardware-webhook.d.ts +31 -0
  147. package/src/lib/server/pos-hardware-webhook.js +176 -0
  148. package/src/lib/server/pos-hardware-webhook.js.map +1 -0
  149. package/src/lib/server/pos-hardware.d.ts +58 -0
  150. package/src/lib/server/pos-hardware.js +234 -0
  151. package/src/lib/server/pos-hardware.js.map +1 -0
  152. package/src/lib/server/pos-offline-card-reconcile.d.ts +38 -0
  153. package/src/lib/server/pos-offline-card-reconcile.js +58 -0
  154. package/src/lib/server/pos-offline-card-reconcile.js.map +1 -0
  155. package/src/lib/server/pos-offline-sync.d.ts +32 -0
  156. package/src/lib/server/pos-offline-sync.js +191 -15
  157. package/src/lib/server/pos-offline-sync.js.map +1 -1
  158. package/src/lib/server/pos-ops-routes.d.ts +4 -2
  159. package/src/lib/server/pos-ops-routes.js +14 -2
  160. package/src/lib/server/pos-ops-routes.js.map +1 -1
  161. package/src/lib/server/pos-order.js +39 -51
  162. package/src/lib/server/pos-order.js.map +1 -1
  163. package/src/lib/server/pos-payment.js +8 -4
  164. package/src/lib/server/pos-payment.js.map +1 -1
  165. package/src/lib/server/pos-reader-splash-admin.d.ts +36 -0
  166. package/src/lib/server/pos-reader-splash-admin.js +295 -0
  167. package/src/lib/server/pos-reader-splash-admin.js.map +1 -0
  168. package/src/lib/server/pos-reader-splash-render.d.ts +40 -0
  169. package/src/lib/server/pos-reader-splash-render.js +138 -0
  170. package/src/lib/server/pos-reader-splash-render.js.map +1 -0
  171. package/src/lib/server/pos-reader-splash-server.d.ts +32 -0
  172. package/src/lib/server/pos-reader-splash-server.js +32 -0
  173. package/src/lib/server/pos-reader-splash-server.js.map +1 -0
  174. package/src/lib/server/pos-reader-splash.d.ts +105 -0
  175. package/src/lib/server/pos-reader-splash.js +548 -0
  176. package/src/lib/server/pos-reader-splash.js.map +1 -0
  177. package/src/lib/server/pos-sale.d.ts +2 -1
  178. package/src/lib/server/pos-sale.js +3 -1
  179. package/src/lib/server/pos-sale.js.map +1 -1
  180. package/src/lib/server/pos-terminal.d.ts +67 -4
  181. package/src/lib/server/pos-terminal.js +239 -32
  182. package/src/lib/server/pos-terminal.js.map +1 -1
  183. package/src/lib/server/process-restock.js +4 -0
  184. package/src/lib/server/process-restock.js.map +1 -1
  185. package/src/lib/server/product-card.js +2 -2
  186. package/src/lib/server/product-card.js.map +1 -1
  187. package/src/lib/server/product-catalog.d.ts +2 -0
  188. package/src/lib/server/product-catalog.js +11 -4
  189. package/src/lib/server/product-catalog.js.map +1 -1
  190. package/src/lib/server/product.js +3 -1
  191. package/src/lib/server/product.js.map +1 -1
  192. package/src/lib/server/related.js +8 -1
  193. package/src/lib/server/related.js.map +1 -1
  194. package/src/lib/server/reservation-availability-query.d.ts +35 -0
  195. package/src/lib/server/reservation-availability-query.js +34 -0
  196. package/src/lib/server/reservation-availability-query.js.map +1 -0
  197. package/src/lib/server/reservation-availability.d.ts +21 -0
  198. package/src/lib/server/reservation-availability.js +64 -10
  199. package/src/lib/server/reservation-availability.js.map +1 -1
  200. package/src/lib/server/reservation-walk-in.d.ts +28 -0
  201. package/src/lib/server/reservation-walk-in.js +166 -0
  202. package/src/lib/server/reservation-walk-in.js.map +1 -0
  203. package/src/lib/server/reserve.js +13 -10
  204. package/src/lib/server/reserve.js.map +1 -1
  205. package/src/lib/server/reviews.js +3 -1
  206. package/src/lib/server/reviews.js.map +1 -1
  207. package/src/lib/server/site-page-resolver.js +5 -2
  208. package/src/lib/server/site-page-resolver.js.map +1 -1
  209. package/src/lib/server.js +26 -0
  210. package/src/lib/server.js.map +1 -1
  211. package/src/lib/testing/fake-firestore.js +72 -0
  212. package/src/lib/testing/fake-firestore.js.map +1 -1
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type MediaCdnScope } from '@aglyn/aglyn/app-utils/media-cdn-scope';
18
+ import type { PluginApiHandler } from '@aglyn/aglyn/server';
19
+ import { type PosReaderSplashChoice, type PosReaderSplashFamilyId } from '../model/pos-reader-splash';
20
+ /** `platformSettings/posReaderSplash`: staff's default screen. */
21
+ export declare const POS_READER_SPLASH_SETTINGS_ID = "posReaderSplash";
22
+ /** One family's default screen, as staff uploaded it. */
23
+ export interface PlatformSplashImage {
24
+ storagePath: string;
25
+ sha256: string;
26
+ contentType: 'image/png' | 'image/jpeg';
27
+ bytes: number;
28
+ uploadedAtMs: number;
29
+ uploadedBy?: string;
30
+ }
31
+ export interface PlatformSplashDoc {
32
+ families?: Partial<Record<PosReaderSplashFamilyId, PlatformSplashImage>>;
33
+ updatedAtMs?: number;
34
+ }
35
+ export declare function platformSplashRef(): FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>;
36
+ export declare function posReaderSplashBucket(): import("@google-cloud/storage").Bucket;
37
+ export declare function readPlatformSplash(): Promise<PlatformSplashDoc>;
38
+ /** The default is usable once the S700/S710 screen is uploaded; the smaller one is drawn from it if absent. */
39
+ export declare function platformSplashReady(doc: PlatformSplashDoc): boolean;
40
+ interface LocatedAsset {
41
+ kind: 'asset';
42
+ scope: MediaCdnScope;
43
+ scopeSegment: string;
44
+ mediaId: string;
45
+ doc: Record<string, unknown>;
46
+ /** What changes when the asset's bytes do. */
47
+ version: string;
48
+ }
49
+ interface LocatedUrl {
50
+ kind: 'url';
51
+ url: string;
52
+ version: string;
53
+ }
54
+ type LocatedSource = LocatedAsset | LocatedUrl;
55
+ /**
56
+ * The workspace's own logo: a white-label workspace's brand logo first,
57
+ * then the logo on its organization profile. Never the platform's: the
58
+ * resolved brand profile falls back to the platform's own logo for a
59
+ * workspace without a brand, so its `logoUrl` is not read here.
60
+ */
61
+ export declare function workspaceLogoValue(org: Record<string, any> | null, whiteLabel: boolean): string | null;
62
+ type SplashSource = {
63
+ mode: 'platform';
64
+ doc: PlatformSplashDoc;
65
+ } | {
66
+ mode: 'logo' | 'artwork';
67
+ located: LocatedSource;
68
+ };
69
+ export interface PosReaderSplashPlan {
70
+ choice: PosReaderSplashChoice | null;
71
+ effectiveChoice: PosReaderSplashChoice;
72
+ whiteLabel: boolean;
73
+ productName: string;
74
+ platformReady: boolean;
75
+ logoValue: string | null;
76
+ image: {
77
+ mediaScope: string;
78
+ mediaId: string;
79
+ } | null;
80
+ /** `none` when the readers show Stripe's standard screen. */
81
+ key: string;
82
+ problem: string | null;
83
+ source: SplashSource | null;
84
+ }
85
+ export declare function planPosReaderSplash(hostId: string, org: Record<string, any> | null, config: Record<string, any> | undefined, platform?: PlatformSplashDoc): Promise<PosReaderSplashPlan>;
86
+ /** The org that owns the site, with its id on it (`planPosReaderSplash` reads it). */
87
+ export declare function ownerOrgOf(hostId: string): Promise<Record<string, any> | null>;
88
+ export interface PosReaderSplashApplyResult {
89
+ outcome: 'unchanged' | 'applied' | 'cleared' | 'skipped' | 'failed';
90
+ plan: PosReaderSplashPlan | null;
91
+ problem: string | null;
92
+ }
93
+ /**
94
+ * Puts the store's start-up screen on its readers' configuration when it
95
+ * changed since it was last applied. Never throws: a failure is recorded on
96
+ * the store's terminal document and returned, and the readers keep their
97
+ * previous screen.
98
+ */
99
+ export declare function applyPosReaderSplash(hostId: string, options?: {
100
+ org?: Record<string, any> | null;
101
+ platform?: PlatformSplashDoc;
102
+ force?: boolean;
103
+ }): Promise<PosReaderSplashApplyResult>;
104
+ export declare const posReaderSplashHandler: PluginApiHandler;
105
+ export {};
@@ -0,0 +1,548 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { createHash } from "node:crypto";
18
+ import { parseMediaCdnScope } from "@aglyn/aglyn/app-utils/media-cdn-scope";
19
+ import { formatMediaRef, isMediaCdnPath, MEDIA_CDN_ROUTE, mediaRefFromStorageUrl, parseMediaRef, resolveMediaSrc } from "@aglyn/aglyn/app-utils/media-ref";
20
+ import { checkEntitlement, resolveBrandingProfile } from "@aglyn/aglyn/app-utils/plan-entitlements";
21
+ import { visibleToHost } from "@aglyn/aglyn/app-utils/scope-tokens";
22
+ import { firebaseAdmin, getOrgForHost } from "@aglyn/tenant-data-admin";
23
+ import { isPosReaderSplashChoice, POS_READER_SPLASH_DEVICE_TYPES, POS_READER_SPLASH_FAMILIES, POS_READER_SPLASH_SOURCE_MAX_BYTES, POS_READER_SPLASH_SOURCE_TYPES, posReaderSplashArtworkProblem, posReaderSplashEffectiveChoice } from "../model/pos-reader-splash.js";
24
+ import { authorizePosStaff, posRequestBody } from "./pos-auth.js";
25
+ import { PosReaderSplashRenderError, posReaderSplashImageFacts, renderPosReaderSplash } from "./pos-reader-splash-render.js";
26
+ import { POS_STRIPE_VERSION, posTerminalAvailable } from "./pos-stripe.js";
27
+ import { savePosReaderConfiguration } from "./pos-terminal.js";
28
+ /*==========================================
29
+ * A STORE'S CARD READERS SHOW ITS START-UP SCREEN (AGL-4048).
30
+ *
31
+ * ## Where it lives
32
+ *
33
+ * Readers and Locations are the PLATFORM's (the destination-charge model,
34
+ * `pos-terminal.ts`), one Location per site. The screen is set on that
35
+ * site's one Terminal Configuration, the one reader tipping already uses,
36
+ * per device type (`stripe_s700`, `stripe_s710`, `bbpos_wisepos_e`), from a
37
+ * File uploaded to Stripe with purpose `terminal_reader_splashscreen`.
38
+ *
39
+ * The platform's account-default configuration is NEVER given a screen. A
40
+ * Location without an override inherits it, so an account default would put
41
+ * the platform's brand on a white-label store's readers, which is the one
42
+ * thing the white-label rule forbids. Staff's default screen is applied per
43
+ * Location instead, to the stores that use it.
44
+ *
45
+ * ## What is applied
46
+ *
47
+ * `hosts/{hostId}/terminal/config.splash` holds the store's choice
48
+ * (`model/pos-reader-splash.ts`); the screen applied is described by a KEY
49
+ * built from cheap metadata only: the choice, the platform default's
50
+ * version, the logo's address and its asset's content digest, the picked
51
+ * image's. `splashAppliedKey` records the last key that reached Stripe, so
52
+ * applying again with nothing changed makes no Stripe call at all, and a
53
+ * changed logo, a new staff default or a new choice makes exactly one round
54
+ * of uploads. Every upload and the first configuration write are keyed by
55
+ * content, so a retried apply never makes a second file or configuration.
56
+ *
57
+ * ## When it is applied
58
+ *
59
+ * - when the store's Location is created (its first reader);
60
+ * - when the POS devices card saves a choice, or opens and finds the key
61
+ * stale (a logo changed since);
62
+ * - when staff change the default, for every store with a Location
63
+ * (`pos-reader-splash-admin.ts`).
64
+ *
65
+ * Never on the payment path: drawing and uploading takes a second or two,
66
+ * and a reader with an older screen still takes the payment.
67
+ *
68
+ * ## Test mode first
69
+ *
70
+ * Gated by `posTerminalAvailable()`: always in test mode, in live mode only
71
+ * once `STRIPE_TERMINAL_LIVE_ENABLED` is on, like every reader control.
72
+ *=========================================*/ /** Raised when the drawing or the Location changes how keys are built. */ const RENDER_VERSION = 'v1';
73
+ /** `platformSettings/posReaderSplash`: staff's default screen. */ export const POS_READER_SPLASH_SETTINGS_ID = 'posReaderSplash';
74
+ const firestore = ()=>firebaseAdmin.app().firestore();
75
+ function terminalConfigRef(hostId) {
76
+ return firestore().collection('hosts').doc(hostId).collection('terminal').doc('config');
77
+ }
78
+ export function platformSplashRef() {
79
+ return firestore().collection('platformSettings').doc(POS_READER_SPLASH_SETTINGS_ID);
80
+ }
81
+ export function posReaderSplashBucket() {
82
+ return firebaseAdmin.app().storage().bucket(process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || undefined);
83
+ }
84
+ export async function readPlatformSplash() {
85
+ const snapshot = await platformSplashRef().get();
86
+ return snapshot.exists ? snapshot.data() : {};
87
+ }
88
+ /** The default is usable once the S700/S710 screen is uploaded; the smaller one is drawn from it if absent. */ export function platformSplashReady(doc) {
89
+ var _doc_families_portraitlarge, _doc_families;
90
+ return Boolean((_doc_families = doc.families) == null ? void 0 : (_doc_families_portraitlarge = _doc_families['portrait-large']) == null ? void 0 : _doc_families_portraitlarge.storagePath);
91
+ }
92
+ function platformSplashVersion(doc) {
93
+ return POS_READER_SPLASH_FAMILIES.map((family)=>{
94
+ var _ref;
95
+ var _doc_families_family_id, _doc_families;
96
+ return (_ref = (_doc_families = doc.families) == null ? void 0 : (_doc_families_family_id = _doc_families[family.id]) == null ? void 0 : _doc_families_family_id.sha256) != null ? _ref : '-';
97
+ }).join('.');
98
+ }
99
+ const digest = (value)=>createHash('sha256').update(value).digest('hex');
100
+ const cleanString = (value)=>typeof value === 'string' ? value.trim() : '';
101
+ async function locateMediaAsset(scopeSegment, mediaId, hostId, orgId) {
102
+ var _doc_contentType, _ref, _doc_updatedAtMs;
103
+ const scope = parseMediaCdnScope(scopeSegment);
104
+ if (!scope || !/^[A-Za-z0-9_-]{1,64}$/.test(mediaId)) return null;
105
+ const owned = scope.isOrg ? scope.scopeId === orgId : scope.scopeId === hostId;
106
+ if (!owned) return null;
107
+ const snapshot = await firestore().collection(scope.isOrg ? 'orgs' : 'hosts').doc(scope.scopeId).collection('media').doc(mediaId).get();
108
+ const doc = snapshot.exists ? snapshot.data() : null;
109
+ if (!doc || doc['deletedAt'] || doc['private']) return null;
110
+ if (scope.isOrg && !visibleToHost(doc['visibleTo'], hostId)) return null;
111
+ if (!/^image\//.test(String((_doc_contentType = doc['contentType']) != null ? _doc_contentType : ''))) return null;
112
+ if (typeof doc['sizeBytes'] === 'number' && doc['sizeBytes'] > POS_READER_SPLASH_SOURCE_MAX_BYTES) return null;
113
+ const version = cleanString(doc['contentSha256']) || cleanString(doc['contentHash']) || String((_ref = (_doc_updatedAtMs = doc['updatedAtMs']) != null ? _doc_updatedAtMs : doc['sizeBytes']) != null ? _ref : '');
114
+ return {
115
+ kind: 'asset',
116
+ scope,
117
+ scopeSegment,
118
+ mediaId,
119
+ doc,
120
+ version
121
+ };
122
+ }
123
+ /** Where a stored logo or image value lives, or null when it cannot be read. */ async function locateSource(value, hostId, orgId) {
124
+ const stored = parseMediaRef(value) ? value : mediaRefFromStorageUrl(value);
125
+ const ref = stored ? parseMediaRef(stored) : null;
126
+ if (ref) return locateMediaAsset(ref.scope, ref.mediaId, hostId, orgId);
127
+ if (isMediaCdnPath(value)) {
128
+ const rest = value.slice(`${MEDIA_CDN_ROUTE}/`.length).split('?')[0];
129
+ const slash = rest.indexOf('/');
130
+ if (slash <= 0) return null;
131
+ return locateMediaAsset(rest.slice(0, slash), rest.slice(slash + 1), hostId, orgId);
132
+ }
133
+ // `isMediaCdnPath` narrows a string it refuses to `never`; the value is still the string.
134
+ const address = value;
135
+ if (/^https:\/\//.test(address) && address.length <= 2000) return {
136
+ kind: 'url',
137
+ url: address,
138
+ version: address
139
+ };
140
+ return null;
141
+ }
142
+ async function readAssetBytes(asset) {
143
+ const { mediaCdnServeBlock, mediaStoragePathInScope } = await import("./pos-reader-splash-server.js");
144
+ const blocked = await mediaCdnServeBlock(asset.scope, {
145
+ contentSha256: typeof asset.doc['contentSha256'] === 'string' ? asset.doc['contentSha256'] : undefined,
146
+ contentHash: typeof asset.doc['contentHash'] === 'string' ? asset.doc['contentHash'] : undefined,
147
+ scopeSegment: asset.scopeSegment,
148
+ mediaId: asset.mediaId
149
+ });
150
+ if (blocked) return null;
151
+ const objectPath = mediaStoragePathInScope({
152
+ storagePath: asset.doc['storagePath'],
153
+ base: `${asset.scope.isOrg ? 'orgs' : 'hosts'}/${asset.scope.scopeId}`,
154
+ mediaId: asset.mediaId
155
+ });
156
+ const [buffer] = await posReaderSplashBucket().file(objectPath).download();
157
+ return buffer.length > POS_READER_SPLASH_SOURCE_MAX_BYTES ? null : buffer;
158
+ }
159
+ const FETCH_TIMEOUT_MS = 8000;
160
+ async function fetchPublicImage(url) {
161
+ const { createPinnedDispatcher, resolvePublicIp } = await import("./pos-reader-splash-server.js");
162
+ const pinnedIp = await resolvePublicIp(new URL(url).hostname);
163
+ if (!pinnedIp) return null;
164
+ const dispatcher = createPinnedDispatcher(pinnedIp);
165
+ const controller = new AbortController();
166
+ const timeout = setTimeout(()=>controller.abort(), FETCH_TIMEOUT_MS);
167
+ try {
168
+ var _response_headers_get, _response_headers_get1;
169
+ const init = {
170
+ redirect: 'error',
171
+ signal: controller.signal,
172
+ dispatcher
173
+ };
174
+ const response = await fetch(url, init);
175
+ if (!response.ok) return null;
176
+ if (!/^image\//.test((_response_headers_get = response.headers.get('content-type')) != null ? _response_headers_get : '')) return null;
177
+ const declared = Number((_response_headers_get1 = response.headers.get('content-length')) != null ? _response_headers_get1 : 0);
178
+ if (declared > POS_READER_SPLASH_SOURCE_MAX_BYTES) return null;
179
+ const buffer = Buffer.from(await response.arrayBuffer());
180
+ return buffer.length > POS_READER_SPLASH_SOURCE_MAX_BYTES ? null : buffer;
181
+ } catch (unused) {
182
+ return null;
183
+ } finally{
184
+ clearTimeout(timeout);
185
+ dispatcher.close == null ? void 0 : dispatcher.close.call(dispatcher).catch(()=>undefined);
186
+ }
187
+ }
188
+ async function readSourceBytes(source) {
189
+ return source.kind === 'asset' ? readAssetBytes(source) : fetchPublicImage(source.url);
190
+ }
191
+ /*==========================================
192
+ * WHAT A STORE SHOWS.
193
+ *=========================================*/ /**
194
+ * The workspace's own logo: a white-label workspace's brand logo first,
195
+ * then the logo on its organization profile. Never the platform's: the
196
+ * resolved brand profile falls back to the platform's own logo for a
197
+ * workspace without a brand, so its `logoUrl` is not read here.
198
+ */ export function workspaceLogoValue(org, whiteLabel) {
199
+ var _org_brandingProfile;
200
+ const brand = whiteLabel ? cleanString(org == null ? void 0 : (_org_brandingProfile = org['brandingProfile']) == null ? void 0 : _org_brandingProfile['logoUrl']) : '';
201
+ return brand || cleanString(org == null ? void 0 : org['logoUrl']) || null;
202
+ }
203
+ const NO_LOGO = 'Add a logo to your organization profile to show it on your card readers.';
204
+ const NO_IMAGE = 'The image picked for your card readers is no longer in your media library. Choose another.';
205
+ export async function planPosReaderSplash(hostId, org, config, platform) {
206
+ var _ref, _ref1;
207
+ const orgId = String((_ref = org == null ? void 0 : org['id']) != null ? _ref : '');
208
+ const whiteLabel = checkEntitlement(org, 'whiteLabel');
209
+ const productName = resolveBrandingProfile(org).productName;
210
+ const setting = (_ref1 = config == null ? void 0 : config['splash']) != null ? _ref1 : null;
211
+ const choice = isPosReaderSplashChoice(setting == null ? void 0 : setting.choice) ? setting.choice : null;
212
+ const effectiveChoice = posReaderSplashEffectiveChoice(choice, whiteLabel);
213
+ const doc = platform != null ? platform : await readPlatformSplash();
214
+ const logoValue = workspaceLogoValue(org, whiteLabel);
215
+ const image = (setting == null ? void 0 : setting.mediaScope) && (setting == null ? void 0 : setting.mediaId) ? {
216
+ mediaScope: setting.mediaScope,
217
+ mediaId: setting.mediaId
218
+ } : null;
219
+ const base = {
220
+ choice,
221
+ effectiveChoice,
222
+ whiteLabel,
223
+ productName,
224
+ platformReady: platformSplashReady(doc),
225
+ logoValue,
226
+ image
227
+ };
228
+ if (effectiveChoice === 'platform') {
229
+ // The white-label rule is in `posReaderSplashEffectiveChoice`; this is
230
+ // the guard behind it, because this branch is the platform's brand.
231
+ if (whiteLabel || !platformSplashReady(doc)) return _extends({}, base, {
232
+ key: 'none',
233
+ problem: null,
234
+ source: null
235
+ });
236
+ return _extends({}, base, {
237
+ key: `platform:${RENDER_VERSION}:${platformSplashVersion(doc)}`,
238
+ problem: null,
239
+ source: {
240
+ mode: 'platform',
241
+ doc
242
+ }
243
+ });
244
+ }
245
+ if (effectiveChoice === 'logo') {
246
+ const located = logoValue ? await locateSource(logoValue, hostId, orgId) : null;
247
+ if (!located) return _extends({}, base, {
248
+ key: 'none',
249
+ problem: NO_LOGO,
250
+ source: null
251
+ });
252
+ return _extends({}, base, {
253
+ key: `logo:${RENDER_VERSION}:${digest(`${logoValue}|${located.version}`).slice(0, 24)}`,
254
+ problem: null,
255
+ source: {
256
+ mode: 'logo',
257
+ located
258
+ }
259
+ });
260
+ }
261
+ const located = image ? await locateMediaAsset(image.mediaScope, image.mediaId, hostId, orgId) : null;
262
+ if (!located) return _extends({}, base, {
263
+ key: 'none',
264
+ problem: NO_IMAGE,
265
+ source: null
266
+ });
267
+ return _extends({}, base, {
268
+ key: `image:${RENDER_VERSION}:${digest(`${image == null ? void 0 : image.mediaScope}/${image == null ? void 0 : image.mediaId}|${located.version}`).slice(0, 24)}`,
269
+ problem: null,
270
+ source: {
271
+ mode: 'artwork',
272
+ located
273
+ }
274
+ });
275
+ }
276
+ async function loadSharpFactory() {
277
+ const { loadSharp } = await import("./pos-reader-splash-server.js");
278
+ return loadSharp();
279
+ }
280
+ /** Each family's screen for the plan, ready for Stripe. */ async function drawScreens(source) {
281
+ const sharp = await loadSharpFactory();
282
+ const screens = {};
283
+ if (source.mode === 'platform') {
284
+ var _source_doc_families;
285
+ const bucket = posReaderSplashBucket();
286
+ const read = async (image)=>(await bucket.file(image.storagePath).download())[0];
287
+ const large = (_source_doc_families = source.doc.families) == null ? void 0 : _source_doc_families['portrait-large'];
288
+ const largeBody = await read(large);
289
+ for (const family of POS_READER_SPLASH_FAMILIES){
290
+ var _source_doc_families1;
291
+ const own = (_source_doc_families1 = source.doc.families) == null ? void 0 : _source_doc_families1[family.id];
292
+ // Staff's own screen for the family is sent as uploaded: it was checked
293
+ // against Stripe's rules for exactly this family then. A family staff
294
+ // left empty is drawn from the S700/S710 screen, the same 9:16.
295
+ screens[family.id] = (own == null ? void 0 : own.storagePath) ? {
296
+ body: own === large ? largeBody : await read(own),
297
+ contentType: own.contentType
298
+ } : await renderPosReaderSplash({
299
+ source: largeBody,
300
+ family,
301
+ mode: 'artwork',
302
+ sharp
303
+ });
304
+ }
305
+ return screens;
306
+ }
307
+ const bytes = await readSourceBytes(source.located);
308
+ if (!bytes) throw new PosReaderSplashRenderError('That image could not be read.');
309
+ if (source.mode === 'artwork') {
310
+ var _ref, _ref1;
311
+ const facts = await posReaderSplashImageFacts(sharp, bytes);
312
+ const problem = posReaderSplashArtworkProblem({
313
+ contentType: (facts == null ? void 0 : facts.contentType) || null,
314
+ bytes: bytes.length,
315
+ width: (_ref = facts == null ? void 0 : facts.width) != null ? _ref : 0,
316
+ height: (_ref1 = facts == null ? void 0 : facts.height) != null ? _ref1 : 0
317
+ });
318
+ if (problem) throw new PosReaderSplashRenderError(problem);
319
+ }
320
+ for (const family of POS_READER_SPLASH_FAMILIES){
321
+ screens[family.id] = await renderPosReaderSplash({
322
+ source: bytes,
323
+ family,
324
+ mode: source.mode,
325
+ sharp
326
+ });
327
+ }
328
+ return screens;
329
+ }
330
+ /** One screen uploaded to Stripe, keyed by its content so a retry makes no second file. */ async function uploadSplashFile(hostId, family, screen) {
331
+ const form = new FormData();
332
+ form.append('purpose', 'terminal_reader_splashscreen');
333
+ const extension = screen.contentType === 'image/png' ? 'png' : 'jpg';
334
+ form.append('file', new Blob([
335
+ new Uint8Array(screen.body)
336
+ ], {
337
+ type: screen.contentType
338
+ }), `start-up-${family.id}.${extension}`);
339
+ const response = await fetch('https://files.stripe.com/v1/files', {
340
+ method: 'POST',
341
+ headers: {
342
+ Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,
343
+ 'Stripe-Version': POS_STRIPE_VERSION,
344
+ 'Idempotency-Key': `pos-splash-file:${hostId}:${family.id}:${digest(screen.body).slice(0, 32)}`
345
+ },
346
+ body: form
347
+ });
348
+ const body = await response.json().catch(()=>({}));
349
+ if (!response.ok || !body.id) {
350
+ var _body_error;
351
+ throw new PosReaderSplashRenderError(((_body_error = body.error) == null ? void 0 : _body_error.message) || 'Stripe did not accept the start-up screen.');
352
+ }
353
+ return String(body.id);
354
+ }
355
+ /** The org that owns the site, with its id on it (`planPosReaderSplash` reads it). */ export async function ownerOrgOf(hostId) {
356
+ var _ref, _owner_org_id;
357
+ const owner = await getOrgForHost(hostId).catch(()=>null);
358
+ if (!(owner == null ? void 0 : owner.org)) return null;
359
+ return _extends({}, owner.org, {
360
+ id: String((_ref = (_owner_org_id = owner.org.id) != null ? _owner_org_id : owner.orgId) != null ? _ref : '')
361
+ });
362
+ }
363
+ /**
364
+ * Puts the store's start-up screen on its readers' configuration when it
365
+ * changed since it was last applied. Never throws: a failure is recorded on
366
+ * the store's terminal document and returned, and the readers keep their
367
+ * previous screen.
368
+ */ export async function applyPosReaderSplash(hostId, options = {}) {
369
+ if (!posTerminalAvailable()) return {
370
+ outcome: 'skipped',
371
+ plan: null,
372
+ problem: null
373
+ };
374
+ const ref = terminalConfigRef(hostId);
375
+ const config = (await ref.get()).data();
376
+ if (!(config == null ? void 0 : config['stripeLocationId'])) return {
377
+ outcome: 'skipped',
378
+ plan: null,
379
+ problem: null
380
+ };
381
+ const org = options.org !== undefined ? options.org : await ownerOrgOf(hostId);
382
+ const plan = await planPosReaderSplash(hostId, org, config, options.platform);
383
+ const applied = typeof config['splashAppliedKey'] === 'string' ? config['splashAppliedKey'] : null;
384
+ // Nothing to show and nothing ever shown: the readers already show
385
+ // Stripe's standard screen, and no Stripe call is needed to keep it.
386
+ if (plan.key === 'none' && (applied === null || applied === 'none') && !options.force) {
387
+ return {
388
+ outcome: 'unchanged',
389
+ plan,
390
+ problem: plan.problem
391
+ };
392
+ }
393
+ if (plan.key === applied && !options.force) return {
394
+ outcome: 'unchanged',
395
+ plan,
396
+ problem: plan.problem
397
+ };
398
+ try {
399
+ const params = {};
400
+ if (plan.key === 'none' || !plan.source) {
401
+ // An empty value unsets the device's screen on this configuration.
402
+ for (const deviceType of POS_READER_SPLASH_DEVICE_TYPES)params[deviceType] = '';
403
+ } else {
404
+ const screens = await drawScreens(plan.source);
405
+ for (const family of POS_READER_SPLASH_FAMILIES){
406
+ const fileId = await uploadSplashFile(hostId, family, screens[family.id]);
407
+ for (const deviceType of family.deviceTypes)params[`${deviceType}[splashscreen]`] = fileId;
408
+ }
409
+ }
410
+ const saved = await savePosReaderConfiguration(hostId, params, `splash:${digest(plan.key).slice(0, 24)}`);
411
+ if (!saved.ok) throw new PosReaderSplashRenderError('Stripe did not accept the start-up screen. Try again.');
412
+ await ref.set({
413
+ splashAppliedKey: plan.key,
414
+ splashAppliedAtMs: Date.now(),
415
+ splashProblem: firebaseAdmin.firestore.FieldValue.delete()
416
+ }, {
417
+ merge: true
418
+ });
419
+ return {
420
+ outcome: plan.key === 'none' ? 'cleared' : 'applied',
421
+ plan,
422
+ problem: plan.problem
423
+ };
424
+ } catch (error) {
425
+ const problem = error instanceof PosReaderSplashRenderError ? error.message : 'The start-up screen could not be applied. Try again.';
426
+ if (!(error instanceof PosReaderSplashRenderError)) console.error('[pos-reader-splash]', hostId, error);
427
+ await ref.set({
428
+ splashProblem: problem,
429
+ splashFailedAtMs: Date.now()
430
+ }, {
431
+ merge: true
432
+ }).catch(()=>undefined);
433
+ return {
434
+ outcome: 'failed',
435
+ plan,
436
+ problem
437
+ };
438
+ }
439
+ }
440
+ /*==========================================
441
+ * `POST /api/commerce/pos-reader-splash`: the POS devices card's setting.
442
+ *
443
+ * status the store's choice and what it shows (re-applies a stale one)
444
+ * save { choice, mediaScope?, mediaId? } — store and apply
445
+ *
446
+ * Gated like a sale (`authorizePosStaff`), as every reader control is.
447
+ *=========================================*/ function statusOf(plan, config, hostId, problem) {
448
+ var _ref, _ref1, _ref2, _ref3, _ref4, _resolveMediaSrc, _resolveMediaSrc1;
449
+ return {
450
+ available: posTerminalAvailable(),
451
+ choice: (_ref = plan == null ? void 0 : plan.choice) != null ? _ref : null,
452
+ effectiveChoice: (_ref1 = plan == null ? void 0 : plan.effectiveChoice) != null ? _ref1 : 'platform',
453
+ whiteLabel: (_ref2 = plan == null ? void 0 : plan.whiteLabel) != null ? _ref2 : false,
454
+ productName: (_ref3 = plan == null ? void 0 : plan.productName) != null ? _ref3 : '',
455
+ platformReady: (_ref4 = plan == null ? void 0 : plan.platformReady) != null ? _ref4 : false,
456
+ logoUrl: (plan == null ? void 0 : plan.logoValue) ? (_resolveMediaSrc = resolveMediaSrc(plan.logoValue, {
457
+ hostId
458
+ })) != null ? _resolveMediaSrc : null : null,
459
+ image: (plan == null ? void 0 : plan.image) ? _extends({}, plan.image, {
460
+ url: (_resolveMediaSrc1 = resolveMediaSrc(formatMediaRef(plan.image.mediaScope, plan.image.mediaId), {
461
+ hostId
462
+ })) != null ? _resolveMediaSrc1 : null
463
+ }) : null,
464
+ locationReady: Boolean(config == null ? void 0 : config['stripeLocationId']),
465
+ appliedAtMs: typeof (config == null ? void 0 : config['splashAppliedAtMs']) === 'number' ? config['splashAppliedAtMs'] : null,
466
+ problem: problem != null ? problem : typeof (config == null ? void 0 : config['splashProblem']) === 'string' ? config['splashProblem'] : null
467
+ };
468
+ }
469
+ async function currentStatus(hostId, org) {
470
+ var _result_plan;
471
+ const result = await applyPosReaderSplash(hostId, {
472
+ org
473
+ });
474
+ const config = (await terminalConfigRef(hostId).get()).data();
475
+ const plan = (_result_plan = result.plan) != null ? _result_plan : await planPosReaderSplash(hostId, org, config);
476
+ return statusOf(plan, config, hostId, result.outcome === 'failed' ? result.problem : plan.problem);
477
+ }
478
+ export const posReaderSplashHandler = async (req, res)=>{
479
+ var _body_hostId, _body_action;
480
+ if (req.method !== 'POST') return res.status(405).json({
481
+ error: 'Method not allowed'
482
+ });
483
+ const body = posRequestBody(req);
484
+ const hostId = String((_body_hostId = body['hostId']) != null ? _body_hostId : '');
485
+ const gate = await authorizePosStaff(req, hostId);
486
+ if ('error' in gate) return res.status(gate.status).json({
487
+ error: gate.error
488
+ });
489
+ const staff = gate.staff;
490
+ const org = staff.org ? _extends({}, staff.org, {
491
+ id: staff.orgId
492
+ }) : null;
493
+ const action = String((_body_action = body['action']) != null ? _body_action : '');
494
+ try {
495
+ if (action === 'status') return res.status(200).json(await currentStatus(hostId, org));
496
+ if (action !== 'save') return res.status(400).json({
497
+ error: 'Unknown action'
498
+ });
499
+ if (!posTerminalAvailable()) return res.status(409).json({
500
+ error: 'Card readers are not available yet.'
501
+ });
502
+ const choice = body['choice'];
503
+ if (!isPosReaderSplashChoice(choice)) return res.status(400).json({
504
+ error: 'Choose what your readers show.'
505
+ });
506
+ const whiteLabel = checkEntitlement(staff.org, 'whiteLabel');
507
+ if (choice === 'platform' && whiteLabel) {
508
+ return res.status(400).json({
509
+ error: 'Choose your logo or your own image.'
510
+ });
511
+ }
512
+ const setting = {
513
+ choice,
514
+ updatedAtMs: Date.now(),
515
+ updatedBy: staff.uid
516
+ };
517
+ if (choice === 'image') {
518
+ var _body_mediaScope, _body_mediaId, _located_doc_contentType;
519
+ const mediaScope = String((_body_mediaScope = body['mediaScope']) != null ? _body_mediaScope : '');
520
+ const mediaId = String((_body_mediaId = body['mediaId']) != null ? _body_mediaId : '');
521
+ const located = await locateMediaAsset(mediaScope, mediaId, hostId, staff.orgId);
522
+ if (!located) return res.status(400).json({
523
+ error: 'Choose an image from your media library.'
524
+ });
525
+ const contentType = String((_located_doc_contentType = located.doc['contentType']) != null ? _located_doc_contentType : '');
526
+ if (!POS_READER_SPLASH_SOURCE_TYPES.includes(contentType)) {
527
+ return res.status(400).json({
528
+ error: 'Choose a PNG, JPEG or WebP image.'
529
+ });
530
+ }
531
+ setting.mediaScope = mediaScope;
532
+ setting.mediaId = mediaId;
533
+ }
534
+ await terminalConfigRef(hostId).set({
535
+ splash: setting
536
+ }, {
537
+ merge: true
538
+ });
539
+ return res.status(200).json(await currentStatus(hostId, org));
540
+ } catch (error) {
541
+ console.error('[pos-reader-splash]', action, error);
542
+ return res.status(500).json({
543
+ error: 'The start-up screen could not be updated'
544
+ });
545
+ }
546
+ };
547
+
548
+ //# sourceMappingURL=pos-reader-splash.js.map