persian-data-grid 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/OFL.txt +93 -0
  2. package/README.md +179 -22
  3. package/dist/ERPTableGroupRow-E5Q6FYDS.js +125 -0
  4. package/dist/ERPTableGroupRow-E5Q6FYDS.js.map +1 -0
  5. package/dist/ERPTableGroupRow-F2IPDYL5.cjs +131 -0
  6. package/dist/ERPTableGroupRow-F2IPDYL5.cjs.map +1 -0
  7. package/dist/GridPlugin-Bewn0X51.d.ts +57 -0
  8. package/dist/GridPlugin-DewWufGZ.d.cts +57 -0
  9. package/dist/GridPresenceService-GEH6535Q.js +3 -0
  10. package/dist/GridPresenceService-GEH6535Q.js.map +1 -0
  11. package/dist/GridPresenceService-RDN5FXHQ.cjs +12 -0
  12. package/dist/GridPresenceService-RDN5FXHQ.cjs.map +1 -0
  13. package/dist/GridSession-CeIfVFck.d.ts +2625 -0
  14. package/dist/GridSession-CmQn2RnL.d.cts +2625 -0
  15. package/dist/GridSyncService-3JQLEY65.cjs +12 -0
  16. package/dist/GridSyncService-3JQLEY65.cjs.map +1 -0
  17. package/dist/GridSyncService-GECXGEKL.js +3 -0
  18. package/dist/GridSyncService-GECXGEKL.js.map +1 -0
  19. package/dist/PdfRenderer-B9FJaCo_.d.cts +22 -0
  20. package/dist/PdfRenderer-bN7ca_Aj.d.ts +22 -0
  21. package/dist/RemoteApi-CCQxiSf1.d.cts +28 -0
  22. package/dist/RemoteApi-CCQxiSf1.d.ts +28 -0
  23. package/dist/SelectImpl-NM4O4IGW.js +447 -0
  24. package/dist/SelectImpl-NM4O4IGW.js.map +1 -0
  25. package/dist/SelectImpl-W2EI4T7O.cjs +453 -0
  26. package/dist/SelectImpl-W2EI4T7O.cjs.map +1 -0
  27. package/dist/UITableController-C71LXBiX.d.ts +678 -0
  28. package/dist/UITableController-CIwnHIw8.d.cts +678 -0
  29. package/dist/VazirmatnPdfRenderer-CpCqA-Wh.d.cts +96 -0
  30. package/dist/VazirmatnPdfRenderer-D46smncn.d.ts +96 -0
  31. package/dist/adapters/arian-erp.cjs +89 -0
  32. package/dist/adapters/arian-erp.cjs.map +1 -0
  33. package/dist/adapters/arian-erp.d.cts +62 -0
  34. package/dist/adapters/arian-erp.d.ts +62 -0
  35. package/dist/adapters/arian-erp.js +79 -0
  36. package/dist/adapters/arian-erp.js.map +1 -0
  37. package/dist/appendAtEnd-RHIQRG2W.cjs +35 -0
  38. package/dist/appendAtEnd-RHIQRG2W.cjs.map +1 -0
  39. package/dist/appendAtEnd-TJEABLGD.js +33 -0
  40. package/dist/appendAtEnd-TJEABLGD.js.map +1 -0
  41. package/dist/applyImportedRows-C2fWYoPy.d.cts +135 -0
  42. package/dist/applyImportedRows-Cqh_N_0k.d.ts +135 -0
  43. package/dist/applyRowVisibilityHandler-Q64ZFC7A.cjs +86 -0
  44. package/dist/applyRowVisibilityHandler-Q64ZFC7A.cjs.map +1 -0
  45. package/dist/applyRowVisibilityHandler-ZLZXQEGY.js +84 -0
  46. package/dist/applyRowVisibilityHandler-ZLZXQEGY.js.map +1 -0
  47. package/dist/broadcastChannelTransport-FHUMV76O.cjs +12 -0
  48. package/dist/broadcastChannelTransport-FHUMV76O.cjs.map +1 -0
  49. package/dist/broadcastChannelTransport-MIH53VNZ.js +3 -0
  50. package/dist/broadcastChannelTransport-MIH53VNZ.js.map +1 -0
  51. package/dist/callbackRemoteApi-BYLjmSI6.d.ts +51 -0
  52. package/dist/callbackRemoteApi-Cv26MmSD.d.cts +51 -0
  53. package/dist/cellRenderContext-BaNh4qn_.d.ts +42 -0
  54. package/dist/cellRenderContext-DvngkptV.d.cts +42 -0
  55. package/dist/chunk-27ALJSZP.js +12187 -0
  56. package/dist/chunk-27ALJSZP.js.map +1 -0
  57. package/dist/chunk-3HP3HQCW.cjs +688 -0
  58. package/dist/chunk-3HP3HQCW.cjs.map +1 -0
  59. package/dist/chunk-3Y3KKGEC.js +3 -0
  60. package/dist/chunk-3Y3KKGEC.js.map +1 -0
  61. package/dist/chunk-52LFOPXD.cjs +588 -0
  62. package/dist/chunk-52LFOPXD.cjs.map +1 -0
  63. package/dist/chunk-5ALMNLUR.cjs +1408 -0
  64. package/dist/chunk-5ALMNLUR.cjs.map +1 -0
  65. package/dist/chunk-5HKVMPBD.js +937 -0
  66. package/dist/chunk-5HKVMPBD.js.map +1 -0
  67. package/dist/chunk-72IVAOTK.cjs +38 -0
  68. package/dist/chunk-72IVAOTK.cjs.map +1 -0
  69. package/dist/chunk-77UXAJII.js +112 -0
  70. package/dist/chunk-77UXAJII.js.map +1 -0
  71. package/dist/chunk-7GWV5HGO.js +36 -0
  72. package/dist/chunk-7GWV5HGO.js.map +1 -0
  73. package/dist/chunk-7T6AWRPU.js +581 -0
  74. package/dist/chunk-7T6AWRPU.js.map +1 -0
  75. package/dist/chunk-AU3RDJYQ.js +243 -0
  76. package/dist/chunk-AU3RDJYQ.js.map +1 -0
  77. package/dist/chunk-CILNIWYW.cjs +100 -0
  78. package/dist/chunk-CILNIWYW.cjs.map +1 -0
  79. package/dist/chunk-CMQ4VLCW.cjs +840 -0
  80. package/dist/chunk-CMQ4VLCW.cjs.map +1 -0
  81. package/dist/chunk-DFFP7SJ6.cjs +257 -0
  82. package/dist/chunk-DFFP7SJ6.cjs.map +1 -0
  83. package/dist/chunk-E2J533UH.cjs +53 -0
  84. package/dist/chunk-E2J533UH.cjs.map +1 -0
  85. package/dist/chunk-EBV6HGZB.cjs +33 -0
  86. package/dist/chunk-EBV6HGZB.cjs.map +1 -0
  87. package/dist/chunk-F4PYOYHR.js +112 -0
  88. package/dist/chunk-F4PYOYHR.js.map +1 -0
  89. package/dist/chunk-F6EMC75A.cjs +34 -0
  90. package/dist/chunk-F6EMC75A.cjs.map +1 -0
  91. package/dist/chunk-GKE33FOJ.js +32 -0
  92. package/dist/chunk-GKE33FOJ.js.map +1 -0
  93. package/dist/chunk-GQEDLUMB.js +168 -0
  94. package/dist/chunk-GQEDLUMB.js.map +1 -0
  95. package/dist/chunk-HLOHWFOA.js +645 -0
  96. package/dist/chunk-HLOHWFOA.js.map +1 -0
  97. package/dist/chunk-IMPCRWYP.cjs +251 -0
  98. package/dist/chunk-IMPCRWYP.cjs.map +1 -0
  99. package/dist/chunk-IPPIXOVQ.js +317 -0
  100. package/dist/chunk-IPPIXOVQ.js.map +1 -0
  101. package/dist/chunk-J4XJ4UBC.js +1404 -0
  102. package/dist/chunk-J4XJ4UBC.js.map +1 -0
  103. package/dist/chunk-J7CPPFXT.cjs +89 -0
  104. package/dist/chunk-J7CPPFXT.cjs.map +1 -0
  105. package/dist/chunk-JOICD47Q.cjs +41 -0
  106. package/dist/chunk-JOICD47Q.cjs.map +1 -0
  107. package/dist/chunk-KCNTKYUY.js +206 -0
  108. package/dist/chunk-KCNTKYUY.js.map +1 -0
  109. package/dist/chunk-KHMG6EI2.js +13 -0
  110. package/dist/chunk-KHMG6EI2.js.map +1 -0
  111. package/dist/chunk-KMRURD5J.cjs +15 -0
  112. package/dist/chunk-KMRURD5J.cjs.map +1 -0
  113. package/dist/chunk-KRJG3KMP.cjs +174 -0
  114. package/dist/chunk-KRJG3KMP.cjs.map +1 -0
  115. package/dist/chunk-LY27YAXE.cjs +208 -0
  116. package/dist/chunk-LY27YAXE.cjs.map +1 -0
  117. package/dist/chunk-MT3KLB2Q.cjs +944 -0
  118. package/dist/chunk-MT3KLB2Q.cjs.map +1 -0
  119. package/dist/chunk-MUQQKPWR.js +84 -0
  120. package/dist/chunk-MUQQKPWR.js.map +1 -0
  121. package/dist/chunk-N7SJTINL.cjs +4 -0
  122. package/dist/chunk-N7SJTINL.cjs.map +1 -0
  123. package/dist/chunk-NB5GCGHL.js +833 -0
  124. package/dist/chunk-NB5GCGHL.js.map +1 -0
  125. package/dist/chunk-NQUVAJQA.js +298 -0
  126. package/dist/chunk-NQUVAJQA.js.map +1 -0
  127. package/dist/chunk-NTV2UUAJ.cjs +149 -0
  128. package/dist/chunk-NTV2UUAJ.cjs.map +1 -0
  129. package/dist/chunk-PJQWHF6T.js +31 -0
  130. package/dist/chunk-PJQWHF6T.js.map +1 -0
  131. package/dist/chunk-PQY54EL5.js +248 -0
  132. package/dist/chunk-PQY54EL5.js.map +1 -0
  133. package/dist/chunk-Q7TBBUTX.cjs +110 -0
  134. package/dist/chunk-Q7TBBUTX.cjs.map +1 -0
  135. package/dist/chunk-QZN7R2WP.cjs +114 -0
  136. package/dist/chunk-QZN7R2WP.cjs.map +1 -0
  137. package/dist/chunk-RKVSJU46.js +35 -0
  138. package/dist/chunk-RKVSJU46.js.map +1 -0
  139. package/dist/chunk-SVO62ZV2.js +50 -0
  140. package/dist/chunk-SVO62ZV2.js.map +1 -0
  141. package/dist/chunk-SX4VQXAA.cjs +114 -0
  142. package/dist/chunk-SX4VQXAA.cjs.map +1 -0
  143. package/dist/chunk-SZ7BLAY3.js +92 -0
  144. package/dist/chunk-SZ7BLAY3.js.map +1 -0
  145. package/dist/chunk-TN4MG6FY.js +112 -0
  146. package/dist/chunk-TN4MG6FY.js.map +1 -0
  147. package/dist/chunk-UC7PHDM6.cjs +331 -0
  148. package/dist/chunk-UC7PHDM6.cjs.map +1 -0
  149. package/dist/chunk-UXVLYUUW.js +108 -0
  150. package/dist/chunk-UXVLYUUW.js.map +1 -0
  151. package/dist/chunk-VBAZ77Q4.js +932 -0
  152. package/dist/chunk-VBAZ77Q4.js.map +1 -0
  153. package/dist/chunk-VSAFQTW5.cjs +319 -0
  154. package/dist/chunk-VSAFQTW5.cjs.map +1 -0
  155. package/dist/chunk-VYJQ2HTA.cjs +936 -0
  156. package/dist/chunk-VYJQ2HTA.cjs.map +1 -0
  157. package/dist/chunk-W5256BJ4.js +56 -0
  158. package/dist/chunk-W5256BJ4.js.map +1 -0
  159. package/dist/chunk-WTHTVKVI.cjs +12299 -0
  160. package/dist/chunk-WTHTVKVI.cjs.map +1 -0
  161. package/dist/chunk-WUMOSOEK.cjs +62 -0
  162. package/dist/chunk-WUMOSOEK.cjs.map +1 -0
  163. package/dist/chunk-XLRE2J25.js +140 -0
  164. package/dist/chunk-XLRE2J25.js.map +1 -0
  165. package/dist/chunk-Z4QPZ3Z3.cjs +114 -0
  166. package/dist/chunk-Z4QPZ3Z3.cjs.map +1 -0
  167. package/dist/createEngineGridQuery-BBkud5oE.d.ts +939 -0
  168. package/dist/createEngineGridQuery-D-fgO_jg.d.cts +939 -0
  169. package/dist/date-picker-jalali.cjs +263 -0
  170. package/dist/date-picker-jalali.cjs.map +1 -0
  171. package/dist/date-picker-jalali.d.cts +62 -0
  172. package/dist/date-picker-jalali.d.ts +62 -0
  173. package/dist/date-picker-jalali.js +256 -0
  174. package/dist/date-picker-jalali.js.map +1 -0
  175. package/dist/finance.cjs +205 -0
  176. package/dist/finance.cjs.map +1 -0
  177. package/dist/finance.d.cts +90 -0
  178. package/dist/finance.d.ts +90 -0
  179. package/dist/finance.js +157 -0
  180. package/dist/finance.js.map +1 -0
  181. package/dist/foldedColumn-IHDT4ARZ.cjs +66 -0
  182. package/dist/foldedColumn-IHDT4ARZ.cjs.map +1 -0
  183. package/dist/foldedColumn-TFURUKFG.js +64 -0
  184. package/dist/foldedColumn-TFURUKFG.js.map +1 -0
  185. package/dist/headless.cjs +729 -0
  186. package/dist/headless.cjs.map +1 -0
  187. package/dist/headless.d.cts +508 -0
  188. package/dist/headless.d.ts +508 -0
  189. package/dist/headless.js +226 -0
  190. package/dist/headless.js.map +1 -0
  191. package/dist/index-DN8M8Jkq.d.cts +14 -0
  192. package/dist/index-DmzL-F_o.d.ts +14 -0
  193. package/dist/index.cjs +8060 -11409
  194. package/dist/index.cjs.map +1 -1
  195. package/dist/index.d.cts +457 -2305
  196. package/dist/index.d.ts +457 -2305
  197. package/dist/index.js +7302 -11084
  198. package/dist/index.js.map +1 -1
  199. package/dist/locales/ar.cjs +12 -0
  200. package/dist/locales/ar.cjs.map +1 -0
  201. package/dist/locales/ar.d.cts +6 -0
  202. package/dist/locales/ar.d.ts +6 -0
  203. package/dist/locales/ar.js +3 -0
  204. package/dist/locales/ar.js.map +1 -0
  205. package/dist/locales/en.cjs +12 -0
  206. package/dist/locales/en.cjs.map +1 -0
  207. package/dist/locales/en.d.cts +6 -0
  208. package/dist/locales/en.d.ts +6 -0
  209. package/dist/locales/en.js +3 -0
  210. package/dist/locales/en.js.map +1 -0
  211. package/dist/locales/fa.cjs +12 -0
  212. package/dist/locales/fa.cjs.map +1 -0
  213. package/dist/locales/fa.d.cts +6 -0
  214. package/dist/locales/fa.d.ts +6 -0
  215. package/dist/locales/fa.js +3 -0
  216. package/dist/locales/fa.js.map +1 -0
  217. package/dist/locales/index.cjs +51 -0
  218. package/dist/locales/index.cjs.map +1 -0
  219. package/dist/locales/index.d.cts +5 -0
  220. package/dist/locales/index.d.ts +5 -0
  221. package/dist/locales/index.js +6 -0
  222. package/dist/locales/index.js.map +1 -0
  223. package/dist/memoryRecords-AWDKUJ2F.cjs +50 -0
  224. package/dist/memoryRecords-AWDKUJ2F.cjs.map +1 -0
  225. package/dist/memoryRecords-OJTGXLRH.js +47 -0
  226. package/dist/memoryRecords-OJTGXLRH.js.map +1 -0
  227. package/dist/pagedFooterTotals-DXCGDO6F.cjs +27 -0
  228. package/dist/pagedFooterTotals-DXCGDO6F.cjs.map +1 -0
  229. package/dist/pagedFooterTotals-ORWKUNE7.js +25 -0
  230. package/dist/pagedFooterTotals-ORWKUNE7.js.map +1 -0
  231. package/dist/pagedKit-MFGAJFSK.cjs +134 -0
  232. package/dist/pagedKit-MFGAJFSK.cjs.map +1 -0
  233. package/dist/pagedKit-WYWY4LRV.js +127 -0
  234. package/dist/pagedKit-WYWY4LRV.js.map +1 -0
  235. package/dist/pagedSearch-HCPJW7VM.js +38 -0
  236. package/dist/pagedSearch-HCPJW7VM.js.map +1 -0
  237. package/dist/pagedSearch-U3WCG4GN.cjs +42 -0
  238. package/dist/pagedSearch-U3WCG4GN.cjs.map +1 -0
  239. package/dist/plugins/csv.cjs +124 -0
  240. package/dist/plugins/csv.cjs.map +1 -0
  241. package/dist/plugins/csv.d.cts +44 -0
  242. package/dist/plugins/csv.d.ts +44 -0
  243. package/dist/plugins/csv.js +120 -0
  244. package/dist/plugins/csv.js.map +1 -0
  245. package/dist/plugins/excel.cjs +52 -0
  246. package/dist/plugins/excel.cjs.map +1 -0
  247. package/dist/plugins/excel.d.cts +14 -0
  248. package/dist/plugins/excel.d.ts +14 -0
  249. package/dist/plugins/excel.js +7 -0
  250. package/dist/plugins/excel.js.map +1 -0
  251. package/dist/plugins/pdf/fonts/vazirmatn.cjs +50 -0
  252. package/dist/plugins/pdf/fonts/vazirmatn.cjs.map +1 -0
  253. package/dist/plugins/pdf/fonts/vazirmatn.d.cts +24 -0
  254. package/dist/plugins/pdf/fonts/vazirmatn.d.ts +24 -0
  255. package/dist/plugins/pdf/fonts/vazirmatn.js +39 -0
  256. package/dist/plugins/pdf/fonts/vazirmatn.js.map +1 -0
  257. package/dist/plugins/pdf.cjs +24 -0
  258. package/dist/plugins/pdf.cjs.map +1 -0
  259. package/dist/plugins/pdf.d.cts +21 -0
  260. package/dist/plugins/pdf.d.ts +21 -0
  261. package/dist/plugins/pdf.js +7 -0
  262. package/dist/plugins/pdf.js.map +1 -0
  263. package/dist/presence.cjs +56 -0
  264. package/dist/presence.cjs.map +1 -0
  265. package/dist/presence.d.cts +17 -0
  266. package/dist/presence.d.ts +17 -0
  267. package/dist/presence.js +54 -0
  268. package/dist/presence.js.map +1 -0
  269. package/dist/rebuildRowVisibility-66EAZ356.cjs +21 -0
  270. package/dist/rebuildRowVisibility-66EAZ356.cjs.map +1 -0
  271. package/dist/rebuildRowVisibility-6TYM5BP2.js +19 -0
  272. package/dist/rebuildRowVisibility-6TYM5BP2.js.map +1 -0
  273. package/dist/styles.css +1440 -0
  274. package/dist/sync.cjs +148 -0
  275. package/dist/sync.cjs.map +1 -0
  276. package/dist/sync.d.cts +17 -0
  277. package/dist/sync.d.ts +17 -0
  278. package/dist/sync.js +146 -0
  279. package/dist/sync.js.map +1 -0
  280. package/dist/types-D__47BKv.d.cts +23 -0
  281. package/dist/types-D__47BKv.d.ts +23 -0
  282. package/package.json +185 -29
@@ -0,0 +1,127 @@
1
+ export { PagedCachedTableRepository } from './chunk-J4XJ4UBC.js';
2
+ import './chunk-5HKVMPBD.js';
3
+ import { DomainError } from './chunk-KHMG6EI2.js';
4
+ import { Pagination } from './chunk-XLRE2J25.js';
5
+
6
+ // application/handlers/deleteRowsCommandHandler.ts
7
+ var DeleteRowsCommandHandler = class {
8
+ constructor(repo, audit, permission) {
9
+ this.repo = repo;
10
+ this.audit = audit;
11
+ this.permission = permission;
12
+ }
13
+ /**
14
+ * Bulk delete — never toggles restore:
15
+ * - `Added` → hard-remove from IndexedDB
16
+ * - otherwise → `rowState = Deleted` (soft; قابل برگرداندن با حذف تکی)
17
+ * Already-`Deleted` rows stay deleted.
18
+ */
19
+ async execute(command, actor) {
20
+ try {
21
+ const rowIds = command.rowIds ?? [];
22
+ if (rowIds.length === 0) return null;
23
+ for (const rowId of rowIds) {
24
+ if (!this.permission.canDeleteRow?.({ actor, rowId })) {
25
+ throw new DomainError(
26
+ "FORBIDDEN",
27
+ "Actor is not allowed to delete selected rows",
28
+ { actor, rowId }
29
+ );
30
+ }
31
+ }
32
+ const remainingWanted = new Set(rowIds);
33
+ let foundAny = false;
34
+ await this.repo.iterateRowRecords(
35
+ command.store,
36
+ command.tableKey,
37
+ "active",
38
+ async (records) => {
39
+ for (const rec of records) {
40
+ if (remainingWanted.delete(rec.rowId)) foundAny = true;
41
+ if (remainingWanted.size === 0) return false;
42
+ }
43
+ }
44
+ );
45
+ if (!foundAny) {
46
+ throw new DomainError("ROW_NOT_FOUND", "No requested rows found", {
47
+ tableKey: command.tableKey
48
+ });
49
+ }
50
+ const { softDeleted, hardRemoved, remaining } = await this.repo.softDeleteRows(command.store, command.tableKey, rowIds);
51
+ this.audit.log({
52
+ type: "INFORMATION",
53
+ message: "ROWS_DELETED",
54
+ payload: {
55
+ requested: rowIds.length,
56
+ hardRemoved,
57
+ softDeleted,
58
+ remaining
59
+ }
60
+ });
61
+ return {
62
+ removed: hardRemoved,
63
+ softDeleted,
64
+ remaining
65
+ };
66
+ } catch (error) {
67
+ if (error instanceof DomainError) {
68
+ this.audit.log({
69
+ type: error.code,
70
+ message: error.message,
71
+ payload: error.details
72
+ });
73
+ } else {
74
+ this.audit.log({
75
+ type: "INVARIANT_VIOLATION",
76
+ message: "Unexpected error",
77
+ payload: { error }
78
+ });
79
+ }
80
+ return null;
81
+ }
82
+ }
83
+ };
84
+
85
+ // infrastructure/Repositories/paged/pagedKit.ts
86
+ async function seedWindowedTable(args) {
87
+ const { repo, store, tableKey, loadedTable, columns, columnMeta, page, pageSize } = args;
88
+ let table = args.table;
89
+ if (loadedTable) {
90
+ table.setColumns(Array.from(columns.values()));
91
+ const activeName = table.getActiveCollectionName() ?? "default";
92
+ const conditionByCollection = /* @__PURE__ */ new Map();
93
+ for (const row of table.getCollections().get(activeName)?.getRows().values() ?? []) {
94
+ const cond = row.getVisibilityCondition() ?? {};
95
+ for (const [fieldName, flag] of Object.entries(cond)) {
96
+ if (flag !== true) continue;
97
+ const colCfg = columnMeta.get(`${activeName}_${fieldName}`) ?? columnMeta.get(fieldName);
98
+ const coll = colCfg?.fieldCollection ?? activeName;
99
+ (conditionByCollection.get(coll) ?? conditionByCollection.set(coll, /* @__PURE__ */ new Map()).get(coll)).set(fieldName, true);
100
+ }
101
+ }
102
+ for (const cfg of columnMeta.values()) {
103
+ const coll = cfg.fieldCollection ?? activeName;
104
+ if (conditionByCollection.get(coll)?.get(cfg.fieldName) !== true) continue;
105
+ const target = table.getCollections().get(coll);
106
+ const cur = target?.getColumns().get(cfg.fieldName);
107
+ if (target && cur) {
108
+ target.getColumns().set(cfg.fieldName, { ...cur, hidden: false });
109
+ }
110
+ }
111
+ const loadedActiveCount = table.getCollections().get(table.getActiveCollectionName())?.getRows().size ?? 0;
112
+ const totalRows = loadedActiveCount > 0 ? Math.max(table.getPagination().totalRows, loadedActiveCount) : table.getPagination().totalRows;
113
+ table.setPagination(new Pagination(page, pageSize, totalRows));
114
+ await repo.save(store, table);
115
+ } else {
116
+ table.setColumns(Array.from(columns.values()));
117
+ table.setPagination(new Pagination(page, pageSize, args.activeRowCount));
118
+ await repo.replaceAll(store, table);
119
+ }
120
+ const pageWindow = await repo.loadPage(store, tableKey, page, pageSize);
121
+ if (pageWindow) table = pageWindow;
122
+ return table.setPageWindow(true);
123
+ }
124
+
125
+ export { DeleteRowsCommandHandler, seedWindowedTable };
126
+ //# sourceMappingURL=pagedKit-WYWY4LRV.js.map
127
+ //# sourceMappingURL=pagedKit-WYWY4LRV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../application/handlers/deleteRowsCommandHandler.ts","../infrastructure/Repositories/paged/pagedKit.ts"],"names":[],"mappings":"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","file":"pagedKit-WYWY4LRV.js","sourcesContent":["// application/handlers/deleteRowsCommandHandler.ts\r\n//\r\n// Page-window (persistence 'paged-idb') bulk delete. Kept in its own module so\r\n// the default bundle only pays for it when the paged mode is actually used.\r\n\r\nimport { DomainError } from '../../domain/errors/DomainError';\r\nimport {\r\n Actor,\r\n PermissionPolicy,\r\n} from '../../domain/policies';\r\nimport { DeleteRowsCommand } from '../commands';\r\nimport { AuditLogger } from '../ports/AuditLogger';\r\nimport { WindowedTableRepository } from '../ports/TableRepository';\r\n\r\n/* ======================================================\r\n Delete Selected Rows (full dataset / IndexedDB SoT)\r\n ====================================================== */\r\n\r\nexport class DeleteRowsCommandHandler {\r\n constructor(\r\n private readonly repo: WindowedTableRepository,\r\n private readonly audit: AuditLogger,\r\n private readonly permission: PermissionPolicy,\r\n ) { }\r\n\r\n /**\r\n * Bulk delete — never toggles restore:\r\n * - `Added` → hard-remove from IndexedDB\r\n * - otherwise → `rowState = Deleted` (soft; قابل برگرداندن با حذف تکی)\r\n * Already-`Deleted` rows stay deleted.\r\n */\r\n async execute(command: DeleteRowsCommand, actor: Actor) {\r\n try {\r\n const rowIds = command.rowIds ?? [];\r\n if (rowIds.length === 0) return null;\r\n\r\n for (const rowId of rowIds) {\r\n if (!this.permission.canDeleteRow?.({ actor, rowId })) {\r\n throw new DomainError(\r\n 'FORBIDDEN',\r\n 'Actor is not allowed to delete selected rows',\r\n { actor, rowId },\r\n );\r\n }\r\n }\r\n\r\n // Memory Iron Rule: streamed validity check (heap = one batch) — every\r\n // requested id must exist in the active collection's persisted records.\r\n const remainingWanted = new Set(rowIds);\r\n let foundAny = false;\r\n await this.repo.iterateRowRecords(\r\n command.store,\r\n command.tableKey,\r\n 'active',\r\n async records => {\r\n for (const rec of records) {\r\n if (remainingWanted.delete(rec.rowId)) foundAny = true;\r\n if (remainingWanted.size === 0) return false; // early stop\r\n }\r\n },\r\n );\r\n if (!foundAny) {\r\n throw new DomainError('ROW_NOT_FOUND', 'No requested rows found', {\r\n tableKey: command.tableKey,\r\n });\r\n }\r\n // Ids absent from persisted records (e.g. unsaved RAM-only additions)\r\n // are skipped silently — the persisted delete cannot act on them.\r\n\r\n // Soft/hard delete applied DIRECTLY on IndexedDB records in one\r\n // readwrite transaction (cursor-based) — never getAllRows + replaceAll.\r\n // Added → hard-removed; others → rowState=Deleted on the record;\r\n // already-Deleted stay deleted (no restore on bulk delete).\r\n const { softDeleted, hardRemoved, remaining } =\r\n await this.repo.softDeleteRows(command.store, command.tableKey, rowIds);\r\n\r\n this.audit.log({\r\n type: 'INFORMATION',\r\n message: 'ROWS_DELETED',\r\n payload: {\r\n requested: rowIds.length,\r\n hardRemoved,\r\n softDeleted,\r\n remaining,\r\n },\r\n });\r\n\r\n return {\r\n removed: hardRemoved,\r\n softDeleted,\r\n remaining,\r\n };\r\n } catch (error) {\r\n if (error instanceof DomainError) {\r\n this.audit.log({\r\n type: error.code,\r\n message: error.message,\r\n payload: error.details,\r\n });\r\n } else {\r\n this.audit.log({\r\n type: 'INVARIANT_VIOLATION',\r\n message: 'Unexpected error',\r\n payload: { error },\r\n });\r\n }\r\n return null;\r\n }\r\n }\r\n}\r\n","// Page-window ('paged-idb') kit — loaded lazily by createGrid so the default\r\n// entry only pays for it when the mode is used.\r\nimport { DeleteRowsCommandHandler } from '../../../application/handlers/deleteRowsCommandHandler';\r\nimport type { WindowedTableRepository } from '../../../application/ports/TableRepository';\r\nimport type { ColumnSchema } from '../../../domain/types/ColumnSchema';\r\nimport type { Table } from '../../../domain/entities/Table';\r\nimport { Pagination } from '../../../domain/value-objects';\r\n\r\nexport { DeleteRowsCommandHandler };\r\nexport { PagedCachedTableRepository } from './PagedCachedTableRepository';\r\n\r\n/**\r\n * Page-window seeding (persistence 'paged-idb'): first open pushes the full\r\n * seed to IndexedDB (replaceAll) and keeps ONE page in RAM; re-open reuses the\r\n * persisted window. Conditional (whenFieldTruthy / whenRemoteFlag) columns that\r\n * hold data are unhidden in their own collection.\r\n */\r\nexport async function seedWindowedTable(args: {\r\n repo: WindowedTableRepository;\r\n store: string;\r\n tableKey: string;\r\n table: Table;\r\n loadedTable?: Table;\r\n columns: Map<string, ColumnSchema>;\r\n columnMeta: Map<string, ColumnSchema>;\r\n page: number;\r\n pageSize: number;\r\n activeRowCount: number;\r\n}): Promise<Table> {\r\n const { repo, store, tableKey, loadedTable, columns, columnMeta, page, pageSize } =\r\n args;\r\n let table = args.table;\r\n\r\n if (loadedTable) {\r\n table.setColumns(Array.from(columns.values()));\r\n // ویرایش (باز شدن مجدد جدول از IndexedDB): ستون‌های شرطیِ محتوا دار باید\r\n // در کالکشن خودشان نمایان شوند — حتی کالکشن‌هایی که سطرشان در پنجرهٔ RAM\r\n // نیست. اسکن مقدار، per-collection روی متای ستون‌ها انجام می‌شود.\r\n const activeName = table.getActiveCollectionName() ?? 'default';\r\n const conditionByCollection = new Map<string, Map<string, boolean>>();\r\n for (const row of table.getCollections().get(activeName)?.getRows().values() ?? []) {\r\n const cond = row.getVisibilityCondition() ?? {};\r\n for (const [fieldName, flag] of Object.entries(cond)) {\r\n if (flag !== true) continue;\r\n const colCfg =\r\n columnMeta.get(`${activeName}_${fieldName}`) ?? columnMeta.get(fieldName);\r\n const coll = colCfg?.fieldCollection ?? activeName;\r\n (\r\n conditionByCollection.get(coll) ??\r\n conditionByCollection.set(coll, new Map()).get(coll)!\r\n ).set(fieldName, true);\r\n }\r\n }\r\n for (const cfg of columnMeta.values()) {\r\n const coll = cfg.fieldCollection ?? activeName;\r\n if (conditionByCollection.get(coll)?.get(cfg.fieldName) !== true) continue;\r\n const target = table.getCollections().get(coll);\r\n const cur = target?.getColumns().get(cfg.fieldName);\r\n if (target && cur) {\r\n target.getColumns().set(cfg.fieldName, { ...cur, hidden: false });\r\n }\r\n }\r\n const loadedActiveCount =\r\n table.getCollections().get(table.getActiveCollectionName())?.getRows().size ?? 0;\r\n const totalRows =\r\n loadedActiveCount > 0\r\n ? Math.max(table.getPagination().totalRows, loadedActiveCount)\r\n : table.getPagination().totalRows;\r\n table.setPagination(new Pagination(page, pageSize, totalRows));\r\n await repo.save(store, table);\r\n } else {\r\n table.setColumns(Array.from(columns.values()));\r\n table.setPagination(new Pagination(page, pageSize, args.activeRowCount));\r\n // Full dataset → IndexedDB source of truth; RAM kept to page 1 only\r\n await repo.replaceAll(store, table);\r\n }\r\n\r\n // Always bind the engine to the RAM page window (not the full dataset)\r\n const pageWindow = await repo.loadPage(store, tableKey, page, pageSize);\r\n if (pageWindow) table = pageWindow;\r\n return table.setPageWindow(true);\r\n}\r\n"]}
@@ -0,0 +1,38 @@
1
+ import './chunk-XLRE2J25.js';
2
+
3
+ // ui/gridSearch/pagedSearch.ts
4
+ function getRecordCellSearchText(cells, columnId) {
5
+ const cell = cells[columnId];
6
+ const display = cell?.display;
7
+ if (display != null && display !== "") return String(display);
8
+ return String(cell?.value ?? "");
9
+ }
10
+ async function findPagedColumnMatches(controller, columnId, query) {
11
+ const q = query.trim().toLowerCase();
12
+ if (!q) return [];
13
+ const matches = [];
14
+ await controller.iterateRowsFromDb("active", async (records) => {
15
+ for (const record of records) {
16
+ if (record.rowState === "Delete" /* Deleted */) continue;
17
+ const text = getRecordCellSearchText(record.cells, columnId).toLowerCase();
18
+ if (text.includes(q)) matches.push({ rowId: record.rowId, columnId });
19
+ }
20
+ });
21
+ return matches;
22
+ }
23
+ async function findPagedRowPage(controller, rowId, pageSize) {
24
+ let targetPage = null;
25
+ await controller.iterateRowsFromDb("active", async (records, ordinalBase) => {
26
+ for (let i = 0; i < records.length; i++) {
27
+ if (records[i].rowId === rowId) {
28
+ targetPage = Math.ceil((ordinalBase + i + 1) / pageSize);
29
+ return false;
30
+ }
31
+ }
32
+ });
33
+ return targetPage;
34
+ }
35
+
36
+ export { findPagedColumnMatches, findPagedRowPage, getRecordCellSearchText };
37
+ //# sourceMappingURL=pagedSearch-HCPJW7VM.js.map
38
+ //# sourceMappingURL=pagedSearch-HCPJW7VM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../ui/gridSearch/pagedSearch.ts"],"names":[],"mappings":";;;AAQO,SAAS,uBAAA,CACd,OACA,QAAA,EACQ;AACR,EAAA,MAAM,IAAA,GAAO,MAAM,QAAQ,CAAA;AAC3B,EAAA,MAAM,UAAU,IAAA,EAAM,OAAA;AACtB,EAAA,IAAI,WAAW,IAAA,IAAQ,OAAA,KAAY,EAAA,EAAI,OAAO,OAAO,OAAO,CAAA;AAC5D,EAAA,OAAO,MAAA,CAAO,IAAA,EAAM,KAAA,IAAS,EAAE,CAAA;AACjC;AAEA,eAAsB,sBAAA,CACpB,UAAA,EACA,QAAA,EACA,KAAA,EAC4B;AAC5B,EAAA,MAAM,CAAA,GAAI,KAAA,CAAM,IAAA,EAAK,CAAE,WAAA,EAAY;AACnC,EAAA,IAAI,CAAC,CAAA,EAAG,OAAO,EAAC;AAChB,EAAA,MAAM,UAA6B,EAAC;AACpC,EAAA,MAAM,UAAA,CAAW,iBAAA,CAAkB,QAAA,EAAU,OAAO,OAAA,KAAY;AAC9D,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,IAAI,OAAO,QAAA,KAAA,QAAA,gBAAmC;AAC9C,MAAA,MAAM,OAAO,uBAAA,CAAwB,MAAA,CAAO,KAAA,EAAO,QAAQ,EAAE,WAAA,EAAY;AACzE,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,CAAC,CAAA,EAAG,OAAA,CAAQ,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,IACtE;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,OAAA;AACT;AAGA,eAAsB,gBAAA,CACpB,UAAA,EACA,KAAA,EACA,QAAA,EACwB;AACxB,EAAA,IAAI,UAAA,GAA4B,IAAA;AAChC,EAAA,MAAM,UAAA,CAAW,iBAAA,CAAkB,QAAA,EAAU,OAAO,SAAS,WAAA,KAAgB;AAC3E,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AACvC,MAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,CAAE,KAAA,KAAU,KAAA,EAAO;AAC9B,QAAA,UAAA,GAAa,IAAA,CAAK,IAAA,CAAA,CAAM,WAAA,GAAc,CAAA,GAAI,KAAK,QAAQ,CAAA;AACvD,QAAA,OAAO,KAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,UAAA;AACT","file":"pagedSearch-HCPJW7VM.js","sourcesContent":["// Page-window ('paged-idb') grid search helpers — loaded lazily by `utils.ts`\r\n// so the default entry does not pay for them. Rows stream from IndexedDB in\r\n// bounded batches; only lightweight matches accumulate.\r\nimport { RowStateEnum } from '../../domain/value-objects';\r\nimport type { UITableController } from '../../infrastructure/ui/UITableController';\r\nimport type { GridSearchMatch } from './types';\r\n\r\n/** Search text for a streamed record cell (plain data, no domain objects). */\r\nexport function getRecordCellSearchText(\r\n cells: Record<string, { value: unknown; originalValue: unknown; display: unknown }>,\r\n columnId: string,\r\n): string {\r\n const cell = cells[columnId];\r\n const display = cell?.display;\r\n if (display != null && display !== '') return String(display);\r\n return String(cell?.value ?? '');\r\n}\r\n\r\nexport async function findPagedColumnMatches(\r\n controller: UITableController,\r\n columnId: string,\r\n query: string,\r\n): Promise<GridSearchMatch[]> {\r\n const q = query.trim().toLowerCase();\r\n if (!q) return [];\r\n const matches: GridSearchMatch[] = [];\r\n await controller.iterateRowsFromDb('active', async (records) => {\r\n for (const record of records) {\r\n if (record.rowState === RowStateEnum.Deleted) continue;\r\n const text = getRecordCellSearchText(record.cells, columnId).toLowerCase();\r\n if (text.includes(q)) matches.push({ rowId: record.rowId, columnId });\r\n }\r\n });\r\n return matches;\r\n}\r\n\r\n/** Page that holds `rowId` (from the record's sorted ordinal), or null. */\r\nexport async function findPagedRowPage(\r\n controller: UITableController,\r\n rowId: number,\r\n pageSize: number,\r\n): Promise<number | null> {\r\n let targetPage: number | null = null;\r\n await controller.iterateRowsFromDb('active', async (records, ordinalBase) => {\r\n for (let i = 0; i < records.length; i++) {\r\n if (records[i].rowId === rowId) {\r\n targetPage = Math.ceil((ordinalBase + i + 1) / pageSize);\r\n return false; // early stop\r\n }\r\n }\r\n });\r\n return targetPage;\r\n}\r\n"]}
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ require('./chunk-NTV2UUAJ.cjs');
4
+
5
+ // ui/gridSearch/pagedSearch.ts
6
+ function getRecordCellSearchText(cells, columnId) {
7
+ const cell = cells[columnId];
8
+ const display = cell?.display;
9
+ if (display != null && display !== "") return String(display);
10
+ return String(cell?.value ?? "");
11
+ }
12
+ async function findPagedColumnMatches(controller, columnId, query) {
13
+ const q = query.trim().toLowerCase();
14
+ if (!q) return [];
15
+ const matches = [];
16
+ await controller.iterateRowsFromDb("active", async (records) => {
17
+ for (const record of records) {
18
+ if (record.rowState === "Delete" /* Deleted */) continue;
19
+ const text = getRecordCellSearchText(record.cells, columnId).toLowerCase();
20
+ if (text.includes(q)) matches.push({ rowId: record.rowId, columnId });
21
+ }
22
+ });
23
+ return matches;
24
+ }
25
+ async function findPagedRowPage(controller, rowId, pageSize) {
26
+ let targetPage = null;
27
+ await controller.iterateRowsFromDb("active", async (records, ordinalBase) => {
28
+ for (let i = 0; i < records.length; i++) {
29
+ if (records[i].rowId === rowId) {
30
+ targetPage = Math.ceil((ordinalBase + i + 1) / pageSize);
31
+ return false;
32
+ }
33
+ }
34
+ });
35
+ return targetPage;
36
+ }
37
+
38
+ exports.findPagedColumnMatches = findPagedColumnMatches;
39
+ exports.findPagedRowPage = findPagedRowPage;
40
+ exports.getRecordCellSearchText = getRecordCellSearchText;
41
+ //# sourceMappingURL=pagedSearch-U3WCG4GN.cjs.map
42
+ //# sourceMappingURL=pagedSearch-U3WCG4GN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../ui/gridSearch/pagedSearch.ts"],"names":[],"mappings":";;;;;AAQO,SAAS,uBAAA,CACd,OACA,QAAA,EACQ;AACR,EAAA,MAAM,IAAA,GAAO,MAAM,QAAQ,CAAA;AAC3B,EAAA,MAAM,UAAU,IAAA,EAAM,OAAA;AACtB,EAAA,IAAI,WAAW,IAAA,IAAQ,OAAA,KAAY,EAAA,EAAI,OAAO,OAAO,OAAO,CAAA;AAC5D,EAAA,OAAO,MAAA,CAAO,IAAA,EAAM,KAAA,IAAS,EAAE,CAAA;AACjC;AAEA,eAAsB,sBAAA,CACpB,UAAA,EACA,QAAA,EACA,KAAA,EAC4B;AAC5B,EAAA,MAAM,CAAA,GAAI,KAAA,CAAM,IAAA,EAAK,CAAE,WAAA,EAAY;AACnC,EAAA,IAAI,CAAC,CAAA,EAAG,OAAO,EAAC;AAChB,EAAA,MAAM,UAA6B,EAAC;AACpC,EAAA,MAAM,UAAA,CAAW,iBAAA,CAAkB,QAAA,EAAU,OAAO,OAAA,KAAY;AAC9D,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,IAAI,OAAO,QAAA,KAAA,QAAA,gBAAmC;AAC9C,MAAA,MAAM,OAAO,uBAAA,CAAwB,MAAA,CAAO,KAAA,EAAO,QAAQ,EAAE,WAAA,EAAY;AACzE,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,CAAC,CAAA,EAAG,OAAA,CAAQ,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,IACtE;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,OAAA;AACT;AAGA,eAAsB,gBAAA,CACpB,UAAA,EACA,KAAA,EACA,QAAA,EACwB;AACxB,EAAA,IAAI,UAAA,GAA4B,IAAA;AAChC,EAAA,MAAM,UAAA,CAAW,iBAAA,CAAkB,QAAA,EAAU,OAAO,SAAS,WAAA,KAAgB;AAC3E,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AACvC,MAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,CAAE,KAAA,KAAU,KAAA,EAAO;AAC9B,QAAA,UAAA,GAAa,IAAA,CAAK,IAAA,CAAA,CAAM,WAAA,GAAc,CAAA,GAAI,KAAK,QAAQ,CAAA;AACvD,QAAA,OAAO,KAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,UAAA;AACT","file":"pagedSearch-U3WCG4GN.cjs","sourcesContent":["// Page-window ('paged-idb') grid search helpers — loaded lazily by `utils.ts`\r\n// so the default entry does not pay for them. Rows stream from IndexedDB in\r\n// bounded batches; only lightweight matches accumulate.\r\nimport { RowStateEnum } from '../../domain/value-objects';\r\nimport type { UITableController } from '../../infrastructure/ui/UITableController';\r\nimport type { GridSearchMatch } from './types';\r\n\r\n/** Search text for a streamed record cell (plain data, no domain objects). */\r\nexport function getRecordCellSearchText(\r\n cells: Record<string, { value: unknown; originalValue: unknown; display: unknown }>,\r\n columnId: string,\r\n): string {\r\n const cell = cells[columnId];\r\n const display = cell?.display;\r\n if (display != null && display !== '') return String(display);\r\n return String(cell?.value ?? '');\r\n}\r\n\r\nexport async function findPagedColumnMatches(\r\n controller: UITableController,\r\n columnId: string,\r\n query: string,\r\n): Promise<GridSearchMatch[]> {\r\n const q = query.trim().toLowerCase();\r\n if (!q) return [];\r\n const matches: GridSearchMatch[] = [];\r\n await controller.iterateRowsFromDb('active', async (records) => {\r\n for (const record of records) {\r\n if (record.rowState === RowStateEnum.Deleted) continue;\r\n const text = getRecordCellSearchText(record.cells, columnId).toLowerCase();\r\n if (text.includes(q)) matches.push({ rowId: record.rowId, columnId });\r\n }\r\n });\r\n return matches;\r\n}\r\n\r\n/** Page that holds `rowId` (from the record's sorted ordinal), or null. */\r\nexport async function findPagedRowPage(\r\n controller: UITableController,\r\n rowId: number,\r\n pageSize: number,\r\n): Promise<number | null> {\r\n let targetPage: number | null = null;\r\n await controller.iterateRowsFromDb('active', async (records, ordinalBase) => {\r\n for (let i = 0; i < records.length; i++) {\r\n if (records[i].rowId === rowId) {\r\n targetPage = Math.ceil((ordinalBase + i + 1) / pageSize);\r\n return false; // early stop\r\n }\r\n }\r\n });\r\n return targetPage;\r\n}\r\n"]}
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ var chunk72IVAOTK_cjs = require('../chunk-72IVAOTK.cjs');
4
+ require('../chunk-F6EMC75A.cjs');
5
+
6
+ // packages/plugins/csv/src/buildCsv.ts
7
+ function escapeCsvCell(value, delimiter) {
8
+ if (value.includes('"') || value.includes("\n") || value.includes("\r") || value.includes(delimiter)) {
9
+ return `"${value.replace(/"/g, '""')}"`;
10
+ }
11
+ return value;
12
+ }
13
+ function buildCsvText(source, options = {}) {
14
+ const delimiter = options.delimiter ?? ",";
15
+ const table = chunk72IVAOTK_cjs.buildExportTable(source);
16
+ const hasGroups = (table.rowKinds ?? []).some((k) => k === "group");
17
+ const includeGroup = options.includeGroupColumn ?? hasGroups;
18
+ const groupCaption = options.groupColumnCaption ?? "Group";
19
+ const header = [
20
+ ...includeGroup ? [groupCaption] : [],
21
+ ...table.columns.map((c) => c.caption)
22
+ ].map((h) => escapeCsvCell(h, delimiter)).join(delimiter);
23
+ const lines = table.rows.map((cells, i) => {
24
+ const kind = table.rowKinds?.[i] ?? "leaf";
25
+ const groupKey = kind === "group" ? source.rows[i]?.groupKey ?? cells.find((c) => c !== "") ?? "" : "";
26
+ const cols = [
27
+ ...includeGroup ? [kind === "group" ? groupKey : ""] : [],
28
+ ...cells
29
+ ];
30
+ return cols.map((c) => escapeCsvCell(c, delimiter)).join(delimiter);
31
+ });
32
+ return [header, ...lines].join("\n");
33
+ }
34
+ function buildCsvBlob(source, options = {}) {
35
+ const text = buildCsvText(source, options);
36
+ return new Blob([`\uFEFF${text}`], { type: "text/csv;charset=utf-8" });
37
+ }
38
+
39
+ // packages/plugins/csv/src/CsvExportPlugin.ts
40
+ function downloadBlob(blob, fileName) {
41
+ if (typeof document === "undefined") return;
42
+ const url = URL.createObjectURL(blob);
43
+ const a = document.createElement("a");
44
+ a.href = url;
45
+ a.download = fileName;
46
+ a.click();
47
+ URL.revokeObjectURL(url);
48
+ }
49
+ var CsvExportPlugin = class {
50
+ constructor(options = {}) {
51
+ this.options = options;
52
+ this.name = "csv-export";
53
+ this.unsub = null;
54
+ }
55
+ register(ctx) {
56
+ this.unsub = ctx.bus.subscribeAll((event) => {
57
+ void this.onEvent(ctx, event);
58
+ });
59
+ }
60
+ dispose() {
61
+ this.unsub?.();
62
+ this.unsub = null;
63
+ }
64
+ async onEvent(ctx, event) {
65
+ if (event.type !== "EXPORT_REQUESTED") return;
66
+ if (event.tableId !== ctx.tableId) return;
67
+ const payload = event.payload;
68
+ if (payload.format !== "csv") return;
69
+ try {
70
+ const source = ctx.query.getExportSource(
71
+ payload.collectionScope ?? "active"
72
+ );
73
+ if (!source) throw new Error("NO_EXPORT_SOURCE");
74
+ const blob = buildCsvBlob(source, this.options);
75
+ const fileName = payload.fileName ?? `export-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.csv`;
76
+ const rowCount = source.rows.length;
77
+ const columnCount = source.columns.length;
78
+ if (this.options.download !== false) {
79
+ downloadBlob(blob, fileName);
80
+ }
81
+ this.options.onBlob?.(blob, { fileName, rowCount, columnCount });
82
+ await ctx.bus.publish({
83
+ eventId: `${Date.now()}-csv-ok`,
84
+ type: "CSV_EXPORT_COMPLETED",
85
+ tableId: ctx.tableId,
86
+ streamId: `table/${ctx.tableId}/view`,
87
+ version: 0,
88
+ occurredAt: Date.now(),
89
+ actorId: event.actorId,
90
+ correlationId: event.correlationId,
91
+ causationId: event.eventId,
92
+ payload: {
93
+ format: "csv",
94
+ rowCount,
95
+ columnCount
96
+ }
97
+ });
98
+ } catch (err) {
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ await ctx.bus.publish({
101
+ eventId: `${Date.now()}-csv-fail`,
102
+ type: "CSV_EXPORT_FAILED",
103
+ tableId: ctx.tableId,
104
+ streamId: `table/${ctx.tableId}/view`,
105
+ version: 0,
106
+ occurredAt: Date.now(),
107
+ actorId: event.actorId,
108
+ correlationId: event.correlationId,
109
+ causationId: event.eventId,
110
+ payload: {
111
+ format: "csv",
112
+ code: message === "NO_EXPORT_SOURCE" ? message : "EXPORT_FAILED",
113
+ message
114
+ }
115
+ });
116
+ }
117
+ }
118
+ };
119
+
120
+ exports.CsvExportPlugin = CsvExportPlugin;
121
+ exports.buildCsvBlob = buildCsvBlob;
122
+ exports.buildCsvText = buildCsvText;
123
+ //# sourceMappingURL=csv.cjs.map
124
+ //# sourceMappingURL=csv.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../packages/plugins/csv/src/buildCsv.ts","../../packages/plugins/csv/src/CsvExportPlugin.ts"],"names":["buildExportTable"],"mappings":"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","file":"csv.cjs","sourcesContent":["/**\r\n * CSV export helpers (PR-122). Pure — no React/DOM.\r\n * Group rows emit a leading key column when `includeGroupColumn` is true,\r\n * otherwise the group label is written into the first data column.\r\n */\r\n\r\nimport type { ExportSource } from '../../../../application/plugins/GridPlugin';\r\nimport { buildExportTable } from '../../excel/src/buildExportTable';\r\n\r\nexport type BuildCsvOptions = {\r\n /** Default `,` */\r\n delimiter?: string;\r\n /** Include a leading \"Group\" column (default true when any group row). */\r\n includeGroupColumn?: boolean;\r\n groupColumnCaption?: string;\r\n};\r\n\r\nfunction escapeCsvCell(value: string, delimiter: string): string {\r\n if (\r\n value.includes('\"') ||\r\n value.includes('\\n') ||\r\n value.includes('\\r') ||\r\n value.includes(delimiter)\r\n ) {\r\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\r\n }\r\n return value;\r\n}\r\n\r\nexport function buildCsvText(\r\n source: ExportSource,\r\n options: BuildCsvOptions = {},\r\n): string {\r\n const delimiter = options.delimiter ?? ',';\r\n const table = buildExportTable(source);\r\n const hasGroups = (table.rowKinds ?? []).some((k) => k === 'group');\r\n const includeGroup =\r\n options.includeGroupColumn ?? hasGroups;\r\n const groupCaption = options.groupColumnCaption ?? 'Group';\r\n\r\n const header = [\r\n ...(includeGroup ? [groupCaption] : []),\r\n ...table.columns.map((c) => c.caption),\r\n ]\r\n .map((h) => escapeCsvCell(h, delimiter))\r\n .join(delimiter);\r\n\r\n const lines = table.rows.map((cells, i) => {\r\n const kind = table.rowKinds?.[i] ?? 'leaf';\r\n const groupKey =\r\n kind === 'group'\r\n ? (source.rows[i]?.groupKey ?? cells.find((c) => c !== '') ?? '')\r\n : '';\r\n const cols = [\r\n ...(includeGroup ? [kind === 'group' ? groupKey : ''] : []),\r\n ...cells,\r\n ];\r\n return cols.map((c) => escapeCsvCell(c, delimiter)).join(delimiter);\r\n });\r\n\r\n return [header, ...lines].join('\\n');\r\n}\r\n\r\nexport function buildCsvBlob(\r\n source: ExportSource,\r\n options: BuildCsvOptions = {},\r\n): Blob {\r\n const text = buildCsvText(source, options);\r\n // UTF-8 BOM helps Excel open Persian CSV correctly.\r\n return new Blob([`\\uFEFF${text}`], { type: 'text/csv;charset=utf-8' });\r\n}\r\n","import type {\r\n ExportRequestedPayload,\r\n GridEvent,\r\n} from '../../../../domain/types/GridEvent';\r\nimport type {\r\n GridPlugin,\r\n GridPluginContext,\r\n} from '../../../../application/plugins/GridPlugin';\r\nimport { buildCsvBlob, type BuildCsvOptions } from './buildCsv';\r\n\r\nfunction downloadBlob(blob: Blob, fileName: string): void {\r\n if (typeof document === 'undefined') return;\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = fileName;\r\n a.click();\r\n URL.revokeObjectURL(url);\r\n}\r\nexport type CsvExportPluginOptions = BuildCsvOptions & {\r\n /** Skip browser download (tests / Node). */\r\n download?: boolean;\r\n onBlob?: (\r\n blob: Blob,\r\n meta: { fileName: string; rowCount: number; columnCount: number },\r\n ) => void;\r\n};\r\n\r\n/**\r\n * Subscribes to EXPORT_REQUESTED (format=csv); never mutates Domain (PR-122).\r\n */\r\nexport class CsvExportPlugin implements GridPlugin {\r\n readonly name = 'csv-export';\r\n private unsub: (() => void) | null = null;\r\n\r\n constructor(private readonly options: CsvExportPluginOptions = {}) {}\r\n\r\n register(ctx: GridPluginContext): void {\r\n this.unsub = ctx.bus.subscribeAll((event) => {\r\n void this.onEvent(ctx, event);\r\n });\r\n }\r\n\r\n dispose(): void {\r\n this.unsub?.();\r\n this.unsub = null;\r\n }\r\n\r\n private async onEvent(\r\n ctx: GridPluginContext,\r\n event: GridEvent,\r\n ): Promise<void> {\r\n if (event.type !== 'EXPORT_REQUESTED') return;\r\n if (event.tableId !== ctx.tableId) return;\r\n\r\n const payload = event.payload as ExportRequestedPayload;\r\n if (payload.format !== 'csv') return;\r\n\r\n try {\r\n const source = ctx.query.getExportSource(\r\n payload.collectionScope ?? 'active',\r\n );\r\n if (!source) throw new Error('NO_EXPORT_SOURCE');\r\n\r\n const blob = buildCsvBlob(source, this.options);\r\n const fileName =\r\n payload.fileName ??\r\n `export-${new Date().toISOString().slice(0, 10)}.csv`;\r\n const rowCount = source.rows.length;\r\n const columnCount = source.columns.length;\r\n\r\n if (this.options.download !== false) {\r\n downloadBlob(blob, fileName);\r\n }\r\n this.options.onBlob?.(blob, { fileName, rowCount, columnCount });\r\n\r\n await ctx.bus.publish({\r\n eventId: `${Date.now()}-csv-ok`,\r\n type: 'CSV_EXPORT_COMPLETED',\r\n tableId: ctx.tableId,\r\n streamId: `table/${ctx.tableId}/view`,\r\n version: 0,\r\n occurredAt: Date.now(),\r\n actorId: event.actorId,\r\n correlationId: event.correlationId,\r\n causationId: event.eventId,\r\n payload: {\r\n format: 'csv' as const,\r\n rowCount,\r\n columnCount,\r\n },\r\n });\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n await ctx.bus.publish({\r\n eventId: `${Date.now()}-csv-fail`,\r\n type: 'CSV_EXPORT_FAILED',\r\n tableId: ctx.tableId,\r\n streamId: `table/${ctx.tableId}/view`,\r\n version: 0,\r\n occurredAt: Date.now(),\r\n actorId: event.actorId,\r\n correlationId: event.correlationId,\r\n causationId: event.eventId,\r\n payload: {\r\n format: 'csv' as const,\r\n code:\r\n message === 'NO_EXPORT_SOURCE' ? message : 'EXPORT_FAILED',\r\n message,\r\n },\r\n });\r\n }\r\n }\r\n}\r\n"]}
@@ -0,0 +1,44 @@
1
+ import { E as ExportSource, G as GridPlugin, a as GridPluginContext } from '../GridPlugin-DewWufGZ.cjs';
2
+ import '../GridSession-CmQn2RnL.cjs';
3
+ import '../RemoteApi-CCQxiSf1.cjs';
4
+ import 'react';
5
+
6
+ /**
7
+ * CSV export helpers (PR-122). Pure — no React/DOM.
8
+ * Group rows emit a leading key column when `includeGroupColumn` is true,
9
+ * otherwise the group label is written into the first data column.
10
+ */
11
+
12
+ type BuildCsvOptions = {
13
+ /** Default `,` */
14
+ delimiter?: string;
15
+ /** Include a leading "Group" column (default true when any group row). */
16
+ includeGroupColumn?: boolean;
17
+ groupColumnCaption?: string;
18
+ };
19
+ declare function buildCsvText(source: ExportSource, options?: BuildCsvOptions): string;
20
+ declare function buildCsvBlob(source: ExportSource, options?: BuildCsvOptions): Blob;
21
+
22
+ type CsvExportPluginOptions = BuildCsvOptions & {
23
+ /** Skip browser download (tests / Node). */
24
+ download?: boolean;
25
+ onBlob?: (blob: Blob, meta: {
26
+ fileName: string;
27
+ rowCount: number;
28
+ columnCount: number;
29
+ }) => void;
30
+ };
31
+ /**
32
+ * Subscribes to EXPORT_REQUESTED (format=csv); never mutates Domain (PR-122).
33
+ */
34
+ declare class CsvExportPlugin implements GridPlugin {
35
+ private readonly options;
36
+ readonly name = "csv-export";
37
+ private unsub;
38
+ constructor(options?: CsvExportPluginOptions);
39
+ register(ctx: GridPluginContext): void;
40
+ dispose(): void;
41
+ private onEvent;
42
+ }
43
+
44
+ export { type BuildCsvOptions, CsvExportPlugin, type CsvExportPluginOptions, buildCsvBlob, buildCsvText };
@@ -0,0 +1,44 @@
1
+ import { E as ExportSource, G as GridPlugin, a as GridPluginContext } from '../GridPlugin-Bewn0X51.js';
2
+ import '../GridSession-CeIfVFck.js';
3
+ import '../RemoteApi-CCQxiSf1.js';
4
+ import 'react';
5
+
6
+ /**
7
+ * CSV export helpers (PR-122). Pure — no React/DOM.
8
+ * Group rows emit a leading key column when `includeGroupColumn` is true,
9
+ * otherwise the group label is written into the first data column.
10
+ */
11
+
12
+ type BuildCsvOptions = {
13
+ /** Default `,` */
14
+ delimiter?: string;
15
+ /** Include a leading "Group" column (default true when any group row). */
16
+ includeGroupColumn?: boolean;
17
+ groupColumnCaption?: string;
18
+ };
19
+ declare function buildCsvText(source: ExportSource, options?: BuildCsvOptions): string;
20
+ declare function buildCsvBlob(source: ExportSource, options?: BuildCsvOptions): Blob;
21
+
22
+ type CsvExportPluginOptions = BuildCsvOptions & {
23
+ /** Skip browser download (tests / Node). */
24
+ download?: boolean;
25
+ onBlob?: (blob: Blob, meta: {
26
+ fileName: string;
27
+ rowCount: number;
28
+ columnCount: number;
29
+ }) => void;
30
+ };
31
+ /**
32
+ * Subscribes to EXPORT_REQUESTED (format=csv); never mutates Domain (PR-122).
33
+ */
34
+ declare class CsvExportPlugin implements GridPlugin {
35
+ private readonly options;
36
+ readonly name = "csv-export";
37
+ private unsub;
38
+ constructor(options?: CsvExportPluginOptions);
39
+ register(ctx: GridPluginContext): void;
40
+ dispose(): void;
41
+ private onEvent;
42
+ }
43
+
44
+ export { type BuildCsvOptions, CsvExportPlugin, type CsvExportPluginOptions, buildCsvBlob, buildCsvText };
@@ -0,0 +1,120 @@
1
+ import { buildExportTable } from '../chunk-7GWV5HGO.js';
2
+ import '../chunk-GKE33FOJ.js';
3
+
4
+ // packages/plugins/csv/src/buildCsv.ts
5
+ function escapeCsvCell(value, delimiter) {
6
+ if (value.includes('"') || value.includes("\n") || value.includes("\r") || value.includes(delimiter)) {
7
+ return `"${value.replace(/"/g, '""')}"`;
8
+ }
9
+ return value;
10
+ }
11
+ function buildCsvText(source, options = {}) {
12
+ const delimiter = options.delimiter ?? ",";
13
+ const table = buildExportTable(source);
14
+ const hasGroups = (table.rowKinds ?? []).some((k) => k === "group");
15
+ const includeGroup = options.includeGroupColumn ?? hasGroups;
16
+ const groupCaption = options.groupColumnCaption ?? "Group";
17
+ const header = [
18
+ ...includeGroup ? [groupCaption] : [],
19
+ ...table.columns.map((c) => c.caption)
20
+ ].map((h) => escapeCsvCell(h, delimiter)).join(delimiter);
21
+ const lines = table.rows.map((cells, i) => {
22
+ const kind = table.rowKinds?.[i] ?? "leaf";
23
+ const groupKey = kind === "group" ? source.rows[i]?.groupKey ?? cells.find((c) => c !== "") ?? "" : "";
24
+ const cols = [
25
+ ...includeGroup ? [kind === "group" ? groupKey : ""] : [],
26
+ ...cells
27
+ ];
28
+ return cols.map((c) => escapeCsvCell(c, delimiter)).join(delimiter);
29
+ });
30
+ return [header, ...lines].join("\n");
31
+ }
32
+ function buildCsvBlob(source, options = {}) {
33
+ const text = buildCsvText(source, options);
34
+ return new Blob([`\uFEFF${text}`], { type: "text/csv;charset=utf-8" });
35
+ }
36
+
37
+ // packages/plugins/csv/src/CsvExportPlugin.ts
38
+ function downloadBlob(blob, fileName) {
39
+ if (typeof document === "undefined") return;
40
+ const url = URL.createObjectURL(blob);
41
+ const a = document.createElement("a");
42
+ a.href = url;
43
+ a.download = fileName;
44
+ a.click();
45
+ URL.revokeObjectURL(url);
46
+ }
47
+ var CsvExportPlugin = class {
48
+ constructor(options = {}) {
49
+ this.options = options;
50
+ this.name = "csv-export";
51
+ this.unsub = null;
52
+ }
53
+ register(ctx) {
54
+ this.unsub = ctx.bus.subscribeAll((event) => {
55
+ void this.onEvent(ctx, event);
56
+ });
57
+ }
58
+ dispose() {
59
+ this.unsub?.();
60
+ this.unsub = null;
61
+ }
62
+ async onEvent(ctx, event) {
63
+ if (event.type !== "EXPORT_REQUESTED") return;
64
+ if (event.tableId !== ctx.tableId) return;
65
+ const payload = event.payload;
66
+ if (payload.format !== "csv") return;
67
+ try {
68
+ const source = ctx.query.getExportSource(
69
+ payload.collectionScope ?? "active"
70
+ );
71
+ if (!source) throw new Error("NO_EXPORT_SOURCE");
72
+ const blob = buildCsvBlob(source, this.options);
73
+ const fileName = payload.fileName ?? `export-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.csv`;
74
+ const rowCount = source.rows.length;
75
+ const columnCount = source.columns.length;
76
+ if (this.options.download !== false) {
77
+ downloadBlob(blob, fileName);
78
+ }
79
+ this.options.onBlob?.(blob, { fileName, rowCount, columnCount });
80
+ await ctx.bus.publish({
81
+ eventId: `${Date.now()}-csv-ok`,
82
+ type: "CSV_EXPORT_COMPLETED",
83
+ tableId: ctx.tableId,
84
+ streamId: `table/${ctx.tableId}/view`,
85
+ version: 0,
86
+ occurredAt: Date.now(),
87
+ actorId: event.actorId,
88
+ correlationId: event.correlationId,
89
+ causationId: event.eventId,
90
+ payload: {
91
+ format: "csv",
92
+ rowCount,
93
+ columnCount
94
+ }
95
+ });
96
+ } catch (err) {
97
+ const message = err instanceof Error ? err.message : String(err);
98
+ await ctx.bus.publish({
99
+ eventId: `${Date.now()}-csv-fail`,
100
+ type: "CSV_EXPORT_FAILED",
101
+ tableId: ctx.tableId,
102
+ streamId: `table/${ctx.tableId}/view`,
103
+ version: 0,
104
+ occurredAt: Date.now(),
105
+ actorId: event.actorId,
106
+ correlationId: event.correlationId,
107
+ causationId: event.eventId,
108
+ payload: {
109
+ format: "csv",
110
+ code: message === "NO_EXPORT_SOURCE" ? message : "EXPORT_FAILED",
111
+ message
112
+ }
113
+ });
114
+ }
115
+ }
116
+ };
117
+
118
+ export { CsvExportPlugin, buildCsvBlob, buildCsvText };
119
+ //# sourceMappingURL=csv.js.map
120
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../packages/plugins/csv/src/buildCsv.ts","../../packages/plugins/csv/src/CsvExportPlugin.ts"],"names":[],"mappings":"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","file":"csv.js","sourcesContent":["/**\r\n * CSV export helpers (PR-122). Pure — no React/DOM.\r\n * Group rows emit a leading key column when `includeGroupColumn` is true,\r\n * otherwise the group label is written into the first data column.\r\n */\r\n\r\nimport type { ExportSource } from '../../../../application/plugins/GridPlugin';\r\nimport { buildExportTable } from '../../excel/src/buildExportTable';\r\n\r\nexport type BuildCsvOptions = {\r\n /** Default `,` */\r\n delimiter?: string;\r\n /** Include a leading \"Group\" column (default true when any group row). */\r\n includeGroupColumn?: boolean;\r\n groupColumnCaption?: string;\r\n};\r\n\r\nfunction escapeCsvCell(value: string, delimiter: string): string {\r\n if (\r\n value.includes('\"') ||\r\n value.includes('\\n') ||\r\n value.includes('\\r') ||\r\n value.includes(delimiter)\r\n ) {\r\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\r\n }\r\n return value;\r\n}\r\n\r\nexport function buildCsvText(\r\n source: ExportSource,\r\n options: BuildCsvOptions = {},\r\n): string {\r\n const delimiter = options.delimiter ?? ',';\r\n const table = buildExportTable(source);\r\n const hasGroups = (table.rowKinds ?? []).some((k) => k === 'group');\r\n const includeGroup =\r\n options.includeGroupColumn ?? hasGroups;\r\n const groupCaption = options.groupColumnCaption ?? 'Group';\r\n\r\n const header = [\r\n ...(includeGroup ? [groupCaption] : []),\r\n ...table.columns.map((c) => c.caption),\r\n ]\r\n .map((h) => escapeCsvCell(h, delimiter))\r\n .join(delimiter);\r\n\r\n const lines = table.rows.map((cells, i) => {\r\n const kind = table.rowKinds?.[i] ?? 'leaf';\r\n const groupKey =\r\n kind === 'group'\r\n ? (source.rows[i]?.groupKey ?? cells.find((c) => c !== '') ?? '')\r\n : '';\r\n const cols = [\r\n ...(includeGroup ? [kind === 'group' ? groupKey : ''] : []),\r\n ...cells,\r\n ];\r\n return cols.map((c) => escapeCsvCell(c, delimiter)).join(delimiter);\r\n });\r\n\r\n return [header, ...lines].join('\\n');\r\n}\r\n\r\nexport function buildCsvBlob(\r\n source: ExportSource,\r\n options: BuildCsvOptions = {},\r\n): Blob {\r\n const text = buildCsvText(source, options);\r\n // UTF-8 BOM helps Excel open Persian CSV correctly.\r\n return new Blob([`\\uFEFF${text}`], { type: 'text/csv;charset=utf-8' });\r\n}\r\n","import type {\r\n ExportRequestedPayload,\r\n GridEvent,\r\n} from '../../../../domain/types/GridEvent';\r\nimport type {\r\n GridPlugin,\r\n GridPluginContext,\r\n} from '../../../../application/plugins/GridPlugin';\r\nimport { buildCsvBlob, type BuildCsvOptions } from './buildCsv';\r\n\r\nfunction downloadBlob(blob: Blob, fileName: string): void {\r\n if (typeof document === 'undefined') return;\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = fileName;\r\n a.click();\r\n URL.revokeObjectURL(url);\r\n}\r\nexport type CsvExportPluginOptions = BuildCsvOptions & {\r\n /** Skip browser download (tests / Node). */\r\n download?: boolean;\r\n onBlob?: (\r\n blob: Blob,\r\n meta: { fileName: string; rowCount: number; columnCount: number },\r\n ) => void;\r\n};\r\n\r\n/**\r\n * Subscribes to EXPORT_REQUESTED (format=csv); never mutates Domain (PR-122).\r\n */\r\nexport class CsvExportPlugin implements GridPlugin {\r\n readonly name = 'csv-export';\r\n private unsub: (() => void) | null = null;\r\n\r\n constructor(private readonly options: CsvExportPluginOptions = {}) {}\r\n\r\n register(ctx: GridPluginContext): void {\r\n this.unsub = ctx.bus.subscribeAll((event) => {\r\n void this.onEvent(ctx, event);\r\n });\r\n }\r\n\r\n dispose(): void {\r\n this.unsub?.();\r\n this.unsub = null;\r\n }\r\n\r\n private async onEvent(\r\n ctx: GridPluginContext,\r\n event: GridEvent,\r\n ): Promise<void> {\r\n if (event.type !== 'EXPORT_REQUESTED') return;\r\n if (event.tableId !== ctx.tableId) return;\r\n\r\n const payload = event.payload as ExportRequestedPayload;\r\n if (payload.format !== 'csv') return;\r\n\r\n try {\r\n const source = ctx.query.getExportSource(\r\n payload.collectionScope ?? 'active',\r\n );\r\n if (!source) throw new Error('NO_EXPORT_SOURCE');\r\n\r\n const blob = buildCsvBlob(source, this.options);\r\n const fileName =\r\n payload.fileName ??\r\n `export-${new Date().toISOString().slice(0, 10)}.csv`;\r\n const rowCount = source.rows.length;\r\n const columnCount = source.columns.length;\r\n\r\n if (this.options.download !== false) {\r\n downloadBlob(blob, fileName);\r\n }\r\n this.options.onBlob?.(blob, { fileName, rowCount, columnCount });\r\n\r\n await ctx.bus.publish({\r\n eventId: `${Date.now()}-csv-ok`,\r\n type: 'CSV_EXPORT_COMPLETED',\r\n tableId: ctx.tableId,\r\n streamId: `table/${ctx.tableId}/view`,\r\n version: 0,\r\n occurredAt: Date.now(),\r\n actorId: event.actorId,\r\n correlationId: event.correlationId,\r\n causationId: event.eventId,\r\n payload: {\r\n format: 'csv' as const,\r\n rowCount,\r\n columnCount,\r\n },\r\n });\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n await ctx.bus.publish({\r\n eventId: `${Date.now()}-csv-fail`,\r\n type: 'CSV_EXPORT_FAILED',\r\n tableId: ctx.tableId,\r\n streamId: `table/${ctx.tableId}/view`,\r\n version: 0,\r\n occurredAt: Date.now(),\r\n actorId: event.actorId,\r\n correlationId: event.correlationId,\r\n causationId: event.eventId,\r\n payload: {\r\n format: 'csv' as const,\r\n code:\r\n message === 'NO_EXPORT_SOURCE' ? message : 'EXPORT_FAILED',\r\n message,\r\n },\r\n });\r\n }\r\n }\r\n}\r\n"]}