@aglyn/plugins-commerce 1.0.0-beta.278 → 1.0.0-beta.283

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,295 @@
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 { pluginRequestFromWeb } from "@aglyn/aglyn/server";
19
+ import { emailUnverifiedResponse, firebaseAdmin, isImpersonationSession } from "@aglyn/tenant-data-admin";
20
+ import { addAdminAudit } from "@aglyn/tenant-data-admin/server/admin-audit-write";
21
+ import { invalidIdTokenResponse } from "@aglyn/tenant-data-admin/server/id-token-refusal";
22
+ import { POS_READER_SPLASH_FAMILIES, POS_READER_SPLASH_MAX_BYTES, posReaderSplashFamily, posReaderSplashImageProblem } from "../model/pos-reader-splash.js";
23
+ import { applyPosReaderSplash, ownerOrgOf, platformSplashReady, platformSplashRef, posReaderSplashBucket, readPlatformSplash } from "./pos-reader-splash.js";
24
+ import { posReaderSplashImageFacts } from "./pos-reader-splash-render.js";
25
+ /*==========================================
26
+ * `/api/commerce/admin/reader-splash` (AGL-4048): the platform's default
27
+ * card reader start-up screen, for Staff → Reader start-up screen.
28
+ *
29
+ * GET each family's screen, as metadata
30
+ * GET ?preview={family} that family's image itself
31
+ * POST upload { family, contentType, dataBase64 }
32
+ * POST remove { family }
33
+ * POST apply put the current default on every store that
34
+ * uses it (the page calls it after a change)
35
+ *
36
+ * The artwork is designed separately and uploaded here; none is kept in
37
+ * the repository. Each image is checked against Stripe's rules for its
38
+ * family before it is stored: PNG or JPEG, under 2 MB, exactly the reader's
39
+ * screen. The S700/S710 screen is the one that turns the default on; the
40
+ * WisePOS E screen is drawn from it when staff upload none.
41
+ *
42
+ * Staff only. Reading is open to every staff role; every write is `super`,
43
+ * because it changes what every non-white-label store's readers show. Every
44
+ * write is an `adminAudit` row. The images live in storage under
45
+ * `platform/commerce/pos-reader-splash/`, which no client rule opens.
46
+ *=========================================*/ const WRITE_ROLES = [
47
+ 'super'
48
+ ];
49
+ /** How many stores one `apply` reaches; staff press it again for the rest. */ const APPLY_WINDOW = 400;
50
+ const APPLY_CONCURRENCY = 4;
51
+ const json = (body, status = 200)=>Response.json(body, {
52
+ status,
53
+ headers: {
54
+ 'Cache-Control': 'no-store'
55
+ }
56
+ });
57
+ function view(doc) {
58
+ var _doc_updatedAtMs;
59
+ return {
60
+ ready: platformSplashReady(doc),
61
+ families: POS_READER_SPLASH_FAMILIES.map((family)=>{
62
+ var _doc_families;
63
+ const image = (_doc_families = doc.families) == null ? void 0 : _doc_families[family.id];
64
+ return {
65
+ id: family.id,
66
+ label: family.label,
67
+ width: family.width,
68
+ height: family.height,
69
+ image: image ? {
70
+ contentType: image.contentType,
71
+ bytes: image.bytes,
72
+ sha256: image.sha256,
73
+ uploadedAtMs: image.uploadedAtMs
74
+ } : null
75
+ };
76
+ }),
77
+ updatedAtMs: (_doc_updatedAtMs = doc.updatedAtMs) != null ? _doc_updatedAtMs : null
78
+ };
79
+ }
80
+ async function upload(doc, input, uid) {
81
+ var _input_dataBase64;
82
+ var _doc_families;
83
+ const family = posReaderSplashFamily(input['family']);
84
+ if (!family) return {
85
+ error: 'Unknown reader family',
86
+ status: 400
87
+ };
88
+ const raw = String((_input_dataBase64 = input['dataBase64']) != null ? _input_dataBase64 : '');
89
+ // A base64 body is 4/3 of the file; anything longer cannot be under 2 MB.
90
+ if (!raw || raw.length > Math.ceil(POS_READER_SPLASH_MAX_BYTES * 4 / 3) + 4) {
91
+ return {
92
+ error: 'Use an image under 2 MB.',
93
+ status: 400
94
+ };
95
+ }
96
+ const body = Buffer.from(raw, 'base64');
97
+ const { loadSharp } = await import("./pos-reader-splash-server.js");
98
+ const facts = await posReaderSplashImageFacts(await loadSharp(), body);
99
+ if (!facts) return {
100
+ error: 'That image could not be read.',
101
+ status: 400
102
+ };
103
+ // The bytes decide the type, never the name or the browser's claim.
104
+ const problem = posReaderSplashImageProblem(_extends({}, facts, {
105
+ bytes: body.length
106
+ }), family);
107
+ if (problem) return {
108
+ error: problem,
109
+ status: 400
110
+ };
111
+ const sha256 = createHash('sha256').update(body).digest('hex');
112
+ const extension = facts.contentType === 'image/png' ? 'png' : 'jpg';
113
+ const storagePath = `platform/commerce/pos-reader-splash/${family.id}-${sha256.slice(0, 16)}.${extension}`;
114
+ await posReaderSplashBucket().file(storagePath).save(body, {
115
+ contentType: facts.contentType,
116
+ resumable: false
117
+ });
118
+ const previous = (_doc_families = doc.families) == null ? void 0 : _doc_families[family.id];
119
+ if ((previous == null ? void 0 : previous.storagePath) && previous.storagePath !== storagePath) {
120
+ await posReaderSplashBucket().file(previous.storagePath).delete().catch(()=>undefined);
121
+ }
122
+ return {
123
+ familyId: family.id,
124
+ image: {
125
+ storagePath,
126
+ sha256,
127
+ contentType: facts.contentType,
128
+ bytes: body.length,
129
+ uploadedAtMs: Date.now(),
130
+ uploadedBy: uid
131
+ }
132
+ };
133
+ }
134
+ /** Every store with a Location, its screen brought up to the current default. */ async function applyToEveryStore() {
135
+ const firestore = firebaseAdmin.app().firestore();
136
+ const platform = await readPlatformSplash();
137
+ const snapshot = await firestore.collectionGroup('terminal').limit(APPLY_WINDOW + 1).get();
138
+ const hostIds = snapshot.docs.filter((doc)=>doc.id === 'config' && doc.get('stripeLocationId')).map((doc)=>doc.ref.path.split('/')[1]).filter(Boolean);
139
+ const counts = {
140
+ applied: 0,
141
+ unchanged: 0,
142
+ failed: 0
143
+ };
144
+ const queue = hostIds.slice(0, APPLY_WINDOW);
145
+ await Promise.all(Array.from({
146
+ length: APPLY_CONCURRENCY
147
+ }, async ()=>{
148
+ for(let hostId = queue.shift(); hostId; hostId = queue.shift()){
149
+ const org = await ownerOrgOf(hostId);
150
+ const result = await applyPosReaderSplash(hostId, {
151
+ org,
152
+ platform
153
+ });
154
+ if (result.outcome === 'applied' || result.outcome === 'cleared') counts.applied += 1;
155
+ else if (result.outcome === 'failed') counts.failed += 1;
156
+ else counts.unchanged += 1;
157
+ }
158
+ }));
159
+ return _extends({}, counts, {
160
+ more: snapshot.docs.length > APPLY_WINDOW
161
+ });
162
+ }
163
+ async function handler(request) {
164
+ var _headers_authorization;
165
+ const { method, query, body, headers: rawHeaders } = await pluginRequestFromWeb(request);
166
+ const headers = rawHeaders;
167
+ const authorization = (_headers_authorization = headers.authorization) != null ? _headers_authorization : '';
168
+ const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
169
+ if (!idToken) return json({
170
+ error: 'Unauthenticated'
171
+ }, 401);
172
+ try {
173
+ var _decoded_staffRole, _input_action;
174
+ const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
175
+ if (!decoded.email_verified && !isImpersonationSession(decoded)) return emailUnverifiedResponse();
176
+ if (!decoded['staff']) return json({
177
+ error: 'Staff only'
178
+ }, 403);
179
+ // A missing role claim reads as the least one, never as super.
180
+ const role = String((_decoded_staffRole = decoded['staffRole']) != null ? _decoded_staffRole : 'support');
181
+ const firestore = firebaseAdmin.app().firestore();
182
+ const doc = await readPlatformSplash();
183
+ if (method === 'GET') {
184
+ var _ref;
185
+ const preview = String((_ref = query == null ? void 0 : query['preview']) != null ? _ref : '');
186
+ if (preview) {
187
+ var _doc_families;
188
+ const family = posReaderSplashFamily(preview);
189
+ const image = family ? (_doc_families = doc.families) == null ? void 0 : _doc_families[family.id] : undefined;
190
+ if (!image) return json({
191
+ error: 'No image'
192
+ }, 404);
193
+ const [bytes] = await posReaderSplashBucket().file(image.storagePath).download();
194
+ return new Response(new Uint8Array(bytes), {
195
+ status: 200,
196
+ headers: {
197
+ 'Content-Type': image.contentType,
198
+ 'Cache-Control': 'no-store'
199
+ }
200
+ });
201
+ }
202
+ return json(_extends({}, view(doc), {
203
+ canWrite: WRITE_ROLES.includes(role)
204
+ }));
205
+ }
206
+ if (method !== 'POST') return json({
207
+ error: 'Method not allowed'
208
+ }, 405);
209
+ if (!WRITE_ROLES.includes(role)) {
210
+ return json({
211
+ error: 'Changing the reader start-up screen requires the super staff role.'
212
+ }, 403);
213
+ }
214
+ const input = body != null ? body : {};
215
+ const action = String((_input_action = input['action']) != null ? _input_action : '');
216
+ const actor = {
217
+ actorUid: decoded.uid,
218
+ actorEmail: decoded.email ? String(decoded.email) : null
219
+ };
220
+ if (action === 'apply') return json(_extends({
221
+ ok: true
222
+ }, await applyToEveryStore()));
223
+ if (action === 'upload' || action === 'remove') {
224
+ var _ref1, _ref2;
225
+ var _doc_families_familyId, _doc_families1, _next_families_familyId, _next_families;
226
+ let next;
227
+ let familyId;
228
+ if (action === 'upload') {
229
+ var _doc_families2;
230
+ const outcome = await upload(doc, input, decoded.uid);
231
+ if ('error' in outcome) return json({
232
+ error: outcome.error
233
+ }, outcome.status);
234
+ familyId = outcome.familyId;
235
+ next = {
236
+ families: _extends({}, (_doc_families2 = doc.families) != null ? _doc_families2 : {}, {
237
+ [familyId]: outcome.image
238
+ }),
239
+ updatedAtMs: Date.now()
240
+ };
241
+ } else {
242
+ var _doc_families3;
243
+ const family = posReaderSplashFamily(input['family']);
244
+ if (!family) return json({
245
+ error: 'Unknown reader family'
246
+ }, 400);
247
+ familyId = family.id;
248
+ const families = _extends({}, (_doc_families3 = doc.families) != null ? _doc_families3 : {});
249
+ const previous = families[family.id];
250
+ delete families[family.id];
251
+ if (previous == null ? void 0 : previous.storagePath) {
252
+ await posReaderSplashBucket().file(previous.storagePath).delete().catch(()=>undefined);
253
+ }
254
+ next = {
255
+ families,
256
+ updatedAtMs: Date.now()
257
+ };
258
+ }
259
+ await platformSplashRef().set(next);
260
+ await addAdminAudit(firestore, _extends({
261
+ action: `pos-reader-splash-${action}`
262
+ }, actor, {
263
+ scope: 'posReaderSplash',
264
+ target: `platformSettings/posReaderSplash`,
265
+ before: {
266
+ family: familyId,
267
+ sha256: (_ref1 = (_doc_families1 = doc.families) == null ? void 0 : (_doc_families_familyId = _doc_families1[familyId]) == null ? void 0 : _doc_families_familyId.sha256) != null ? _ref1 : null
268
+ },
269
+ after: {
270
+ family: familyId,
271
+ sha256: (_ref2 = (_next_families = next.families) == null ? void 0 : (_next_families_familyId = _next_families[familyId]) == null ? void 0 : _next_families_familyId.sha256) != null ? _ref2 : null
272
+ },
273
+ at: firebaseAdmin.firestore.FieldValue.serverTimestamp()
274
+ }));
275
+ return json(_extends({
276
+ ok: true
277
+ }, view(next), {
278
+ canWrite: true
279
+ }));
280
+ }
281
+ return json({
282
+ error: 'Unknown action'
283
+ }, 400);
284
+ } catch (error) {
285
+ const unauthenticated = invalidIdTokenResponse(error);
286
+ if (unauthenticated) return unauthenticated;
287
+ console.error('[pos-reader-splash-admin]', error);
288
+ return json({
289
+ error: 'The reader start-up screen could not be updated'
290
+ }, 500);
291
+ }
292
+ }
293
+ export { handler as posReaderSplashAdmin };
294
+
295
+ //# sourceMappingURL=pos-reader-splash-admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-reader-splash-admin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createHash } from 'node:crypto'\nimport { pluginRequestFromWeb } from '@aglyn/aglyn/server'\nimport { emailUnverifiedResponse, firebaseAdmin, isImpersonationSession } from '@aglyn/tenant-data-admin'\nimport { addAdminAudit } from '@aglyn/tenant-data-admin/server/admin-audit-write'\nimport { invalidIdTokenResponse } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport {\n POS_READER_SPLASH_FAMILIES,\n POS_READER_SPLASH_MAX_BYTES,\n posReaderSplashFamily,\n posReaderSplashImageProblem,\n} from '../model/pos-reader-splash'\nimport {\n applyPosReaderSplash,\n ownerOrgOf,\n platformSplashReady,\n platformSplashRef,\n posReaderSplashBucket,\n readPlatformSplash,\n type PlatformSplashDoc,\n type PlatformSplashImage,\n} from './pos-reader-splash'\nimport { posReaderSplashImageFacts } from './pos-reader-splash-render'\n\n/*==========================================\n * `/api/commerce/admin/reader-splash` (AGL-4048): the platform's default\n * card reader start-up screen, for Staff → Reader start-up screen.\n *\n * GET each family's screen, as metadata\n * GET ?preview={family} that family's image itself\n * POST upload { family, contentType, dataBase64 }\n * POST remove { family }\n * POST apply put the current default on every store that\n * uses it (the page calls it after a change)\n *\n * The artwork is designed separately and uploaded here; none is kept in\n * the repository. Each image is checked against Stripe's rules for its\n * family before it is stored: PNG or JPEG, under 2 MB, exactly the reader's\n * screen. The S700/S710 screen is the one that turns the default on; the\n * WisePOS E screen is drawn from it when staff upload none.\n *\n * Staff only. Reading is open to every staff role; every write is `super`,\n * because it changes what every non-white-label store's readers show. Every\n * write is an `adminAudit` row. The images live in storage under\n * `platform/commerce/pos-reader-splash/`, which no client rule opens.\n *=========================================*/\n\nconst WRITE_ROLES = ['super']\n\n/** How many stores one `apply` reaches; staff press it again for the rest. */\nconst APPLY_WINDOW = 400\nconst APPLY_CONCURRENCY = 4\n\nconst json = (body: unknown, status = 200) =>\n Response.json(body, { status, headers: { 'Cache-Control': 'no-store' } })\n\nfunction view(doc: PlatformSplashDoc) {\n return {\n ready: platformSplashReady(doc),\n families: POS_READER_SPLASH_FAMILIES.map((family) => {\n const image = doc.families?.[family.id]\n return {\n id: family.id,\n label: family.label,\n width: family.width,\n height: family.height,\n image: image\n ? {\n contentType: image.contentType,\n bytes: image.bytes,\n sha256: image.sha256,\n uploadedAtMs: image.uploadedAtMs,\n }\n : null,\n }\n }),\n updatedAtMs: doc.updatedAtMs ?? null,\n }\n}\n\nexport type StaffReaderSplashView = ReturnType<typeof view>\n\nasync function upload(\n doc: PlatformSplashDoc,\n input: Record<string, unknown>,\n uid: string,\n): Promise<{ error: string; status: number } | { image: PlatformSplashImage; familyId: string }> {\n const family = posReaderSplashFamily(input['family'])\n if (!family) return { error: 'Unknown reader family', status: 400 }\n const raw = String(input['dataBase64'] ?? '')\n // A base64 body is 4/3 of the file; anything longer cannot be under 2 MB.\n if (!raw || raw.length > Math.ceil((POS_READER_SPLASH_MAX_BYTES * 4) / 3) + 4) {\n return { error: 'Use an image under 2 MB.', status: 400 }\n }\n const body = Buffer.from(raw, 'base64')\n const { loadSharp } = await import('./pos-reader-splash-server')\n const facts = await posReaderSplashImageFacts((await loadSharp()) as any, body)\n if (!facts) return { error: 'That image could not be read.', status: 400 }\n // The bytes decide the type, never the name or the browser's claim.\n const problem = posReaderSplashImageProblem({ ...facts, bytes: body.length }, family)\n if (problem) return { error: problem, status: 400 }\n const sha256 = createHash('sha256').update(body).digest('hex')\n const extension = facts.contentType === 'image/png' ? 'png' : 'jpg'\n const storagePath = `platform/commerce/pos-reader-splash/${family.id}-${sha256.slice(0, 16)}.${extension}`\n await posReaderSplashBucket()\n .file(storagePath)\n .save(body, { contentType: facts.contentType, resumable: false })\n const previous = doc.families?.[family.id]\n if (previous?.storagePath && previous.storagePath !== storagePath) {\n await posReaderSplashBucket().file(previous.storagePath).delete().catch(() => undefined)\n }\n return {\n familyId: family.id,\n image: {\n storagePath,\n sha256,\n contentType: facts.contentType as PlatformSplashImage['contentType'],\n bytes: body.length,\n uploadedAtMs: Date.now(),\n uploadedBy: uid,\n },\n }\n}\n\n/** Every store with a Location, its screen brought up to the current default. */\nasync function applyToEveryStore() {\n const firestore = firebaseAdmin.app().firestore()\n const platform = await readPlatformSplash()\n const snapshot = await firestore.collectionGroup('terminal').limit(APPLY_WINDOW + 1).get()\n const hostIds = (snapshot.docs as Array<{ id: string; ref: { path: string }; get: (field: string) => unknown }>)\n .filter((doc) => doc.id === 'config' && doc.get('stripeLocationId'))\n .map((doc) => doc.ref.path.split('/')[1])\n .filter(Boolean)\n const counts = { applied: 0, unchanged: 0, failed: 0 }\n const queue = hostIds.slice(0, APPLY_WINDOW)\n await Promise.all(\n Array.from({ length: APPLY_CONCURRENCY }, async () => {\n for (let hostId = queue.shift(); hostId; hostId = queue.shift()) {\n const org = await ownerOrgOf(hostId)\n const result = await applyPosReaderSplash(hostId, { org, platform })\n if (result.outcome === 'applied' || result.outcome === 'cleared') counts.applied += 1\n else if (result.outcome === 'failed') counts.failed += 1\n else counts.unchanged += 1\n }\n }),\n )\n return { ...counts, more: snapshot.docs.length > APPLY_WINDOW }\n}\n\nasync function handler(request: Request): Promise<Response> {\n const { method, query, body, headers: rawHeaders } = await pluginRequestFromWeb(request)\n const headers = rawHeaders as Partial<Record<string, string>>\n const authorization = headers.authorization ?? ''\n const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined\n if (!idToken) return json({ error: 'Unauthenticated' }, 401)\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n if (!decoded.email_verified && !isImpersonationSession(decoded)) return emailUnverifiedResponse()\n if (!decoded['staff']) return json({ error: 'Staff only' }, 403)\n // A missing role claim reads as the least one, never as super.\n const role = String(decoded['staffRole'] ?? 'support')\n const firestore = firebaseAdmin.app().firestore()\n const doc = await readPlatformSplash()\n\n if (method === 'GET') {\n const preview = String(query?.['preview'] ?? '')\n if (preview) {\n const family = posReaderSplashFamily(preview)\n const image = family ? doc.families?.[family.id] : undefined\n if (!image) return json({ error: 'No image' }, 404)\n const [bytes] = (await posReaderSplashBucket().file(image.storagePath).download()) as [Buffer]\n return new Response(new Uint8Array(bytes), {\n status: 200,\n headers: { 'Content-Type': image.contentType, 'Cache-Control': 'no-store' },\n })\n }\n return json({ ...view(doc), canWrite: WRITE_ROLES.includes(role) })\n }\n\n if (method !== 'POST') return json({ error: 'Method not allowed' }, 405)\n if (!WRITE_ROLES.includes(role)) {\n return json({ error: 'Changing the reader start-up screen requires the super staff role.' }, 403)\n }\n const input = (body ?? {}) as Record<string, unknown>\n const action = String(input['action'] ?? '')\n const actor = { actorUid: decoded.uid, actorEmail: decoded.email ? String(decoded.email) : null }\n\n if (action === 'apply') return json({ ok: true, ...(await applyToEveryStore()) })\n\n if (action === 'upload' || action === 'remove') {\n let next: PlatformSplashDoc\n let familyId: string\n if (action === 'upload') {\n const outcome = await upload(doc, input, decoded.uid)\n if ('error' in outcome) return json({ error: outcome.error }, outcome.status)\n familyId = outcome.familyId\n next = { families: { ...(doc.families ?? {}), [familyId]: outcome.image }, updatedAtMs: Date.now() }\n } else {\n const family = posReaderSplashFamily(input['family'])\n if (!family) return json({ error: 'Unknown reader family' }, 400)\n familyId = family.id\n const families = { ...(doc.families ?? {}) }\n const previous = families[family.id]\n delete families[family.id]\n if (previous?.storagePath) {\n await posReaderSplashBucket().file(previous.storagePath).delete().catch(() => undefined)\n }\n next = { families, updatedAtMs: Date.now() }\n }\n await platformSplashRef().set(next)\n await addAdminAudit(firestore, {\n action: `pos-reader-splash-${action}`,\n ...actor,\n scope: 'posReaderSplash',\n target: `platformSettings/posReaderSplash`,\n before: { family: familyId, sha256: doc.families?.[familyId as keyof typeof doc.families]?.sha256 ?? null },\n after: { family: familyId, sha256: next.families?.[familyId as keyof typeof next.families]?.sha256 ?? null },\n at: firebaseAdmin.firestore.FieldValue.serverTimestamp(),\n })\n return json({ ok: true, ...view(next), canWrite: true })\n }\n return json({ error: 'Unknown action' }, 400)\n } catch (error) {\n const unauthenticated = invalidIdTokenResponse(error)\n if (unauthenticated) return unauthenticated\n console.error('[pos-reader-splash-admin]', error)\n return json({ error: 'The reader start-up screen could not be updated' }, 500)\n }\n}\n\nexport { handler as posReaderSplashAdmin }\n"],"names":["createHash","pluginRequestFromWeb","emailUnverifiedResponse","firebaseAdmin","isImpersonationSession","addAdminAudit","invalidIdTokenResponse","POS_READER_SPLASH_FAMILIES","POS_READER_SPLASH_MAX_BYTES","posReaderSplashFamily","posReaderSplashImageProblem","applyPosReaderSplash","ownerOrgOf","platformSplashReady","platformSplashRef","posReaderSplashBucket","readPlatformSplash","posReaderSplashImageFacts","WRITE_ROLES","APPLY_WINDOW","APPLY_CONCURRENCY","json","body","status","Response","headers","view","doc","ready","families","map","family","image","id","label","width","height","contentType","bytes","sha256","uploadedAtMs","updatedAtMs","upload","input","uid","error","raw","String","length","Math","ceil","Buffer","from","loadSharp","facts","problem","update","digest","extension","storagePath","slice","file","save","resumable","previous","delete","catch","undefined","familyId","Date","now","uploadedBy","applyToEveryStore","firestore","app","platform","snapshot","collectionGroup","limit","get","hostIds","docs","filter","ref","path","split","Boolean","counts","applied","unchanged","failed","queue","Promise","all","Array","hostId","shift","org","result","outcome","more","handler","request","method","query","rawHeaders","authorization","idToken","startsWith","decoded","auth","verifyIdToken","email_verified","role","preview","download","Uint8Array","canWrite","includes","action","actor","actorUid","actorEmail","email","ok","next","set","scope","target","before","after","at","FieldValue","serverTimestamp","unauthenticated","console","posReaderSplashAdmin"],"mappings":"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"}
@@ -0,0 +1,40 @@
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 { Sharp, SharpOptions } from 'sharp';
18
+ import { type PosReaderSplashFamily } from '../model/pos-reader-splash';
19
+ type SharpFactory = (input?: Buffer | SharpOptions, options?: SharpOptions) => Sharp;
20
+ export type PosReaderSplashRenderMode = 'artwork' | 'logo';
21
+ export interface RenderedPosReaderSplash {
22
+ body: Buffer;
23
+ contentType: 'image/png' | 'image/jpeg';
24
+ }
25
+ export declare class PosReaderSplashRenderError extends Error {
26
+ }
27
+ /** One reader family's start-up screen from `source`. */
28
+ export declare function renderPosReaderSplash(options: {
29
+ source: Buffer;
30
+ family: PosReaderSplashFamily;
31
+ mode: PosReaderSplashRenderMode;
32
+ sharp: SharpFactory;
33
+ }): Promise<RenderedPosReaderSplash>;
34
+ /** An image's format and size, read without decoding its pixels. */
35
+ export declare function posReaderSplashImageFacts(sharp: SharpFactory, body: Buffer): Promise<{
36
+ contentType: string;
37
+ width: number;
38
+ height: number;
39
+ } | null>;
40
+ export {};
@@ -0,0 +1,138 @@
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
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
+ import { POS_READER_SPLASH_MAX_BYTES, posReaderSplashImageProblem } from "../model/pos-reader-splash.js";
18
+ /** A source whose decoded pixels exceed this is refused rather than decoded. */ const MAX_INPUT_PIXELS = 50000000;
19
+ /** The logo's box, as a share of the screen. */ const LOGO_BOX = {
20
+ width: 0.6,
21
+ height: 0.3
22
+ };
23
+ /** The opaque ground a logo sits on. */ const LOGO_GROUND = {
24
+ r: 255,
25
+ g: 255,
26
+ b: 255,
27
+ alpha: 1
28
+ };
29
+ /** JPEG qualities tried, best first, when a PNG is too large for Stripe. */ const JPEG_QUALITIES = [
30
+ 90,
31
+ 82,
32
+ 74,
33
+ 66
34
+ ];
35
+ export class PosReaderSplashRenderError extends Error {
36
+ }
37
+ /** Decoding options; an SVG is rasterized at a density sharp at `target`. */ async function decodeOptions(sharp, source, target) {
38
+ var _meta_width, _meta_height;
39
+ const base = {
40
+ limitInputPixels: MAX_INPUT_PIXELS,
41
+ failOn: 'error'
42
+ };
43
+ const meta = await sharp(source, base).metadata();
44
+ if (meta.format !== 'svg') return base;
45
+ const longest = Math.max((_meta_width = meta.width) != null ? _meta_width : 0, (_meta_height = meta.height) != null ? _meta_height : 0);
46
+ if (!longest) return base;
47
+ return _extends({}, base, {
48
+ density: Math.min(2400, Math.max(1, Math.ceil(72 * target / longest)))
49
+ });
50
+ }
51
+ async function drawn(sharp, source, family, mode) {
52
+ if (mode === 'artwork') {
53
+ const options = await decodeOptions(sharp, source, family.height);
54
+ return sharp(source, options).rotate().resize(family.width, family.height, {
55
+ fit: 'cover'
56
+ }).flatten({
57
+ background: LOGO_GROUND
58
+ }).png().toBuffer();
59
+ }
60
+ const boxWidth = Math.round(family.width * LOGO_BOX.width);
61
+ const boxHeight = Math.round(family.height * LOGO_BOX.height);
62
+ const options = await decodeOptions(sharp, source, Math.max(boxWidth, boxHeight));
63
+ const mark = await sharp(source, options).rotate().resize(boxWidth, boxHeight, {
64
+ fit: 'inside'
65
+ }).png().toBuffer();
66
+ const composed = await sharp({
67
+ create: {
68
+ width: family.width,
69
+ height: family.height,
70
+ channels: 4,
71
+ background: LOGO_GROUND
72
+ }
73
+ }).composite([
74
+ {
75
+ input: mark,
76
+ gravity: 'center'
77
+ }
78
+ ]).png().toBuffer();
79
+ // A second pass: sharp flattens before it composites within one pipeline,
80
+ // and the screen has no use for an alpha channel.
81
+ return sharp(composed).flatten({
82
+ background: LOGO_GROUND
83
+ }).png().toBuffer();
84
+ }
85
+ /** One reader family's start-up screen from `source`. */ export async function renderPosReaderSplash(options) {
86
+ var _meta_width, _meta_height;
87
+ const { source, family, mode, sharp } = options;
88
+ let png;
89
+ try {
90
+ png = await drawn(sharp, source, family, mode);
91
+ } catch (unused) {
92
+ throw new PosReaderSplashRenderError('That image could not be read.');
93
+ }
94
+ let rendered = {
95
+ body: png,
96
+ contentType: 'image/png'
97
+ };
98
+ if (png.length >= POS_READER_SPLASH_MAX_BYTES) {
99
+ for (const quality of JPEG_QUALITIES){
100
+ const body = await sharp(png).jpeg({
101
+ quality,
102
+ mozjpeg: true
103
+ }).toBuffer();
104
+ rendered = {
105
+ body,
106
+ contentType: 'image/jpeg'
107
+ };
108
+ if (body.length < POS_READER_SPLASH_MAX_BYTES) break;
109
+ }
110
+ }
111
+ const meta = await sharp(rendered.body).metadata();
112
+ const problem = posReaderSplashImageProblem({
113
+ contentType: rendered.contentType,
114
+ bytes: rendered.body.length,
115
+ width: (_meta_width = meta.width) != null ? _meta_width : 0,
116
+ height: (_meta_height = meta.height) != null ? _meta_height : 0
117
+ }, family);
118
+ if (problem) throw new PosReaderSplashRenderError(problem);
119
+ return rendered;
120
+ }
121
+ /** An image's format and size, read without decoding its pixels. */ export async function posReaderSplashImageFacts(sharp, body) {
122
+ try {
123
+ var _meta_width, _meta_height;
124
+ const meta = await sharp(body, {
125
+ limitInputPixels: MAX_INPUT_PIXELS
126
+ }).metadata();
127
+ const contentType = meta.format === 'png' ? 'image/png' : meta.format === 'jpeg' ? 'image/jpeg' : meta.format === 'webp' ? 'image/webp' : '';
128
+ return {
129
+ contentType,
130
+ width: (_meta_width = meta.width) != null ? _meta_width : 0,
131
+ height: (_meta_height = meta.height) != null ? _meta_height : 0
132
+ };
133
+ } catch (unused) {
134
+ return null;
135
+ }
136
+ }
137
+
138
+ //# sourceMappingURL=pos-reader-splash-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-reader-splash-render.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Sharp, SharpOptions } from 'sharp'\nimport {\n POS_READER_SPLASH_MAX_BYTES,\n posReaderSplashImageProblem,\n type PosReaderSplashFamily,\n} from '../model/pos-reader-splash'\n\n/*==========================================\n * Draws a reader's start-up screen (AGL-4048) at the exact size Stripe asks\n * for, from either:\n *\n * - `artwork` a full-screen portrait image, scaled to cover the screen\n * (a 9:16 image covers both readers' screens whole);\n * - `logo` a logo, fitted inside the middle of the screen on an opaque\n * white ground, never stretched.\n *\n * The logo's box is 60% of the width and 30% of the height: on a 1080-wide\n * portrait screen a square logo comes out 648 px, and a wide wordmark keeps\n * a margin either side. White is the ground the approved workspace-logo\n * standard sets (a square logo on an opaque white plate), so the same logo\n * reads the same on a reader as in the apps.\n *\n * The result is PNG, the lossless choice for flat artwork; a photo that\n * comes out at 2 MB or more is re-encoded as JPEG until it fits, and every\n * screen is checked against Stripe's rules before it is handed back.\n *=========================================*/\n\ntype SharpFactory = (input?: Buffer | SharpOptions, options?: SharpOptions) => Sharp\n\nexport type PosReaderSplashRenderMode = 'artwork' | 'logo'\n\n/** A source whose decoded pixels exceed this is refused rather than decoded. */\nconst MAX_INPUT_PIXELS = 50_000_000\n\n/** The logo's box, as a share of the screen. */\nconst LOGO_BOX = { width: 0.6, height: 0.3 } as const\n\n/** The opaque ground a logo sits on. */\nconst LOGO_GROUND = { r: 255, g: 255, b: 255, alpha: 1 } as const\n\n/** JPEG qualities tried, best first, when a PNG is too large for Stripe. */\nconst JPEG_QUALITIES = [90, 82, 74, 66] as const\n\nexport interface RenderedPosReaderSplash {\n body: Buffer\n contentType: 'image/png' | 'image/jpeg'\n}\n\nexport class PosReaderSplashRenderError extends Error {}\n\n/** Decoding options; an SVG is rasterized at a density sharp at `target`. */\nasync function decodeOptions(sharp: SharpFactory, source: Buffer, target: number): Promise<SharpOptions> {\n const base: SharpOptions = { limitInputPixels: MAX_INPUT_PIXELS, failOn: 'error' }\n const meta = await sharp(source, base).metadata()\n if (meta.format !== 'svg') return base\n const longest = Math.max(meta.width ?? 0, meta.height ?? 0)\n if (!longest) return base\n return { ...base, density: Math.min(2400, Math.max(1, Math.ceil((72 * target) / longest))) }\n}\n\nasync function drawn(\n sharp: SharpFactory,\n source: Buffer,\n family: PosReaderSplashFamily,\n mode: PosReaderSplashRenderMode,\n): Promise<Buffer> {\n if (mode === 'artwork') {\n const options = await decodeOptions(sharp, source, family.height)\n return sharp(source, options)\n .rotate()\n .resize(family.width, family.height, { fit: 'cover' })\n .flatten({ background: LOGO_GROUND })\n .png()\n .toBuffer()\n }\n const boxWidth = Math.round(family.width * LOGO_BOX.width)\n const boxHeight = Math.round(family.height * LOGO_BOX.height)\n const options = await decodeOptions(sharp, source, Math.max(boxWidth, boxHeight))\n const mark = await sharp(source, options)\n .rotate()\n .resize(boxWidth, boxHeight, { fit: 'inside' })\n .png()\n .toBuffer()\n const composed = await sharp({\n create: { width: family.width, height: family.height, channels: 4, background: LOGO_GROUND },\n })\n .composite([{ input: mark, gravity: 'center' }])\n .png()\n .toBuffer()\n // A second pass: sharp flattens before it composites within one pipeline,\n // and the screen has no use for an alpha channel.\n return sharp(composed).flatten({ background: LOGO_GROUND }).png().toBuffer()\n}\n\n/** One reader family's start-up screen from `source`. */\nexport async function renderPosReaderSplash(options: {\n source: Buffer\n family: PosReaderSplashFamily\n mode: PosReaderSplashRenderMode\n sharp: SharpFactory\n}): Promise<RenderedPosReaderSplash> {\n const { source, family, mode, sharp } = options\n let png: Buffer\n try {\n png = await drawn(sharp, source, family, mode)\n } catch {\n throw new PosReaderSplashRenderError('That image could not be read.')\n }\n let rendered: RenderedPosReaderSplash = { body: png, contentType: 'image/png' }\n if (png.length >= POS_READER_SPLASH_MAX_BYTES) {\n for (const quality of JPEG_QUALITIES) {\n const body = await sharp(png).jpeg({ quality, mozjpeg: true }).toBuffer()\n rendered = { body, contentType: 'image/jpeg' }\n if (body.length < POS_READER_SPLASH_MAX_BYTES) break\n }\n }\n const meta = await sharp(rendered.body).metadata()\n const problem = posReaderSplashImageProblem(\n {\n contentType: rendered.contentType,\n bytes: rendered.body.length,\n width: meta.width ?? 0,\n height: meta.height ?? 0,\n },\n family,\n )\n if (problem) throw new PosReaderSplashRenderError(problem)\n return rendered\n}\n\n/** An image's format and size, read without decoding its pixels. */\nexport async function posReaderSplashImageFacts(\n sharp: SharpFactory,\n body: Buffer,\n): Promise<{ contentType: string; width: number; height: number } | null> {\n try {\n const meta = await sharp(body, { limitInputPixels: MAX_INPUT_PIXELS }).metadata()\n const contentType =\n meta.format === 'png' ? 'image/png' : meta.format === 'jpeg' ? 'image/jpeg' : meta.format === 'webp' ? 'image/webp' : ''\n return { contentType, width: meta.width ?? 0, height: meta.height ?? 0 }\n } catch {\n return null\n }\n}\n"],"names":["POS_READER_SPLASH_MAX_BYTES","posReaderSplashImageProblem","MAX_INPUT_PIXELS","LOGO_BOX","width","height","LOGO_GROUND","r","g","b","alpha","JPEG_QUALITIES","PosReaderSplashRenderError","Error","decodeOptions","sharp","source","target","meta","base","limitInputPixels","failOn","metadata","format","longest","Math","max","density","min","ceil","drawn","family","mode","options","rotate","resize","fit","flatten","background","png","toBuffer","boxWidth","round","boxHeight","mark","composed","create","channels","composite","input","gravity","renderPosReaderSplash","rendered","body","contentType","length","quality","jpeg","mozjpeg","problem","bytes","posReaderSplashImageFacts"],"mappings":"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"}
@@ -0,0 +1,32 @@
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
+ /**
18
+ * The server helpers the card reader start-up screen (AGL-4048) loads only
19
+ * when it draws or reads an image: the media CDN's block, a media asset's
20
+ * storage path, `sharp`, and the pinned fetch for an outside logo address.
21
+ *
22
+ * Loaded by RELATIVE path from `pos-reader-splash.ts` and
23
+ * `pos-reader-splash-admin.ts`, never by importing `@aglyn/tenant-data-admin`
24
+ * lazily: a workspace lib `import()`ed by its package specifier is lazy-loaded
25
+ * everywhere as far as nx is concerned, and every static import of it across
26
+ * this plugin would then fail the module boundary rule
27
+ * (`ai-media-asset-server.ts` in the AI plugin is the same arrangement).
28
+ */
29
+ export { mediaStoragePathInScope } from '@aglyn/tenant-data-admin/server/media-storage-path';
30
+ export { loadSharp } from '@aglyn/tenant-data-admin/server/media-variants';
31
+ export { mediaCdnServeBlock } from '@aglyn/tenant-data-admin/server/serve-media-cdn';
32
+ export { createPinnedDispatcher, resolvePublicIp } from '@aglyn/tenant-data-admin/server/serve-plugin-fetch';
@@ -0,0 +1,32 @@
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
+ * The server helpers the card reader start-up screen (AGL-4048) loads only
18
+ * when it draws or reads an image: the media CDN's block, a media asset's
19
+ * storage path, `sharp`, and the pinned fetch for an outside logo address.
20
+ *
21
+ * Loaded by RELATIVE path from `pos-reader-splash.ts` and
22
+ * `pos-reader-splash-admin.ts`, never by importing `@aglyn/tenant-data-admin`
23
+ * lazily: a workspace lib `import()`ed by its package specifier is lazy-loaded
24
+ * everywhere as far as nx is concerned, and every static import of it across
25
+ * this plugin would then fail the module boundary rule
26
+ * (`ai-media-asset-server.ts` in the AI plugin is the same arrangement).
27
+ */ export { mediaStoragePathInScope } from "@aglyn/tenant-data-admin/server/media-storage-path";
28
+ export { loadSharp } from "@aglyn/tenant-data-admin/server/media-variants";
29
+ export { mediaCdnServeBlock } from "@aglyn/tenant-data-admin/server/serve-media-cdn";
30
+ export { createPinnedDispatcher, resolvePublicIp } from "@aglyn/tenant-data-admin/server/serve-plugin-fetch";
31
+
32
+ //# sourceMappingURL=pos-reader-splash-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-reader-splash-server.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The server helpers the card reader start-up screen (AGL-4048) loads only\n * when it draws or reads an image: the media CDN's block, a media asset's\n * storage path, `sharp`, and the pinned fetch for an outside logo address.\n *\n * Loaded by RELATIVE path from `pos-reader-splash.ts` and\n * `pos-reader-splash-admin.ts`, never by importing `@aglyn/tenant-data-admin`\n * lazily: a workspace lib `import()`ed by its package specifier is lazy-loaded\n * everywhere as far as nx is concerned, and every static import of it across\n * this plugin would then fail the module boundary rule\n * (`ai-media-asset-server.ts` in the AI plugin is the same arrangement).\n */\nexport { mediaStoragePathInScope } from '@aglyn/tenant-data-admin/server/media-storage-path'\nexport { loadSharp } from '@aglyn/tenant-data-admin/server/media-variants'\nexport { mediaCdnServeBlock } from '@aglyn/tenant-data-admin/server/serve-media-cdn'\nexport { createPinnedDispatcher, resolvePublicIp } from '@aglyn/tenant-data-admin/server/serve-plugin-fetch'\n"],"names":["mediaStoragePathInScope","loadSharp","mediaCdnServeBlock","createPinnedDispatcher","resolvePublicIp"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;CAWC,GACD,SAASA,uBAAuB,QAAQ,qDAAoD;AAC5F,SAASC,SAAS,QAAQ,iDAAgD;AAC1E,SAASC,kBAAkB,QAAQ,kDAAiD;AACpF,SAASC,sBAAsB,EAAEC,eAAe,QAAQ,qDAAoD"}