@goplusvn/core 0.1.0 → 0.1.2

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 (591) hide show
  1. package/package.json +31 -175
  2. package/src/assets/erp_wallpaper.png +0 -0
  3. package/src/assets/goeat_logo.png +0 -0
  4. package/src/audit/audit-manager.ts +139 -0
  5. package/src/audit/index.ts +11 -0
  6. package/src/audit/memory-audit-logger.ts +86 -0
  7. package/src/audit/types.ts +50 -0
  8. package/src/auth/auth-service.ts +97 -0
  9. package/src/auth/index.ts +266 -0
  10. package/src/code-generation/index.ts +69 -0
  11. package/src/configs/auth-routes.ts +17 -0
  12. package/src/configs/crud.ts +136 -0
  13. package/src/configs/data/navigations.ts +781 -0
  14. package/src/configs/data/oauth-links.ts +10 -0
  15. package/src/configs/entities/material-categories.config.ts +125 -0
  16. package/src/configs/i18n.ts +12 -0
  17. package/src/configs/index.ts +26 -0
  18. package/src/configs/status.ts +25 -0
  19. package/src/configs/themes.ts +100 -0
  20. package/src/crud/components/crud-bulk-actions.tsx +91 -0
  21. package/src/crud/components/crud-card-view.tsx +241 -0
  22. package/src/crud/components/crud-context.tsx +122 -0
  23. package/src/crud/components/crud-delete-dialog.tsx +145 -0
  24. package/src/crud/components/crud-dialog.tsx +406 -0
  25. package/src/crud/components/crud-empty-state.tsx +104 -0
  26. package/src/crud/components/crud-export-button.tsx +170 -0
  27. package/src/crud/components/crud-field-renderer.tsx +653 -0
  28. package/src/crud/components/crud-filter-chips.tsx +102 -0
  29. package/src/crud/components/crud-filters/checkbox-filter.tsx +97 -0
  30. package/src/crud/components/crud-filters/datetime-filter.tsx +83 -0
  31. package/src/crud/components/crud-filters/filter-builder.tsx +66 -0
  32. package/src/crud/components/crud-filters/index.tsx +76 -0
  33. package/src/crud/components/crud-filters/radio-filter.tsx +86 -0
  34. package/src/crud/components/crud-filters/select-filter.tsx +141 -0
  35. package/src/crud/components/crud-filters/text-filter.tsx +86 -0
  36. package/src/crud/components/crud-form.tsx +642 -0
  37. package/src/crud/components/crud-import-dialog.tsx +440 -0
  38. package/src/crud/components/crud-infinite-scroll.tsx +116 -0
  39. package/src/crud/components/crud-page.tsx +1017 -0
  40. package/src/crud/components/crud-provider.tsx +277 -0
  41. package/src/crud/components/crud-row-actions.tsx +189 -0
  42. package/src/crud/components/crud-search.tsx +82 -0
  43. package/src/crud/components/crud-sheet.tsx +336 -0
  44. package/src/crud/components/crud-table-skeleton.tsx +26 -0
  45. package/src/crud/components/crud-table-toolbar.tsx +91 -0
  46. package/src/crud/components/crud-table.tsx +352 -0
  47. package/src/crud/components/crud-virtual-table.tsx +55 -0
  48. package/src/crud/components/index.tsx +20 -0
  49. package/src/crud/crud-filters/checkbox-filter.tsx +87 -0
  50. package/src/crud/crud-filters/datetime-filter.tsx +82 -0
  51. package/src/crud/crud-filters/filter-builder.tsx +64 -0
  52. package/src/crud/crud-filters/index.tsx +78 -0
  53. package/src/crud/crud-filters/radio-filter.tsx +79 -0
  54. package/src/crud/crud-filters/select-filter.tsx +148 -0
  55. package/src/crud/crud-filters/text-filter.tsx +81 -0
  56. package/src/crud/index.ts +43 -0
  57. package/src/crud/lib/crud-service.test.ts +334 -0
  58. package/src/crud/lib/crud-service.ts +358 -0
  59. package/src/crud/lib/crud-utils.test.ts +354 -0
  60. package/src/crud/lib/crud-utils.ts +299 -0
  61. package/src/crud/lib/crud-validator.ts +247 -0
  62. package/src/crud/lib/data-loader.ts +234 -0
  63. package/src/crud/lib/field-calculator.ts +241 -0
  64. package/src/crud/lib/field-formatter.ts +240 -0
  65. package/src/crud/lib/import-export-service.test.ts +290 -0
  66. package/src/crud/lib/import-export-service.ts +352 -0
  67. package/src/crud/lib/import-server-utils.ts +109 -0
  68. package/src/crud/lib/lazy-loader.ts +241 -0
  69. package/src/crud/lib/parse-filters.ts +85 -0
  70. package/src/crud/lib/permissions.ts +52 -0
  71. package/src/crud/lib/serialize-config.ts +60 -0
  72. package/src/crud/lib/stream-loader.ts +145 -0
  73. package/src/crud/lib/translate-config.ts +335 -0
  74. package/src/crud/lib/types.ts +11 -0
  75. package/src/crud/pages/entity-crud-page.tsx +144 -0
  76. package/src/crud/server.ts +8 -0
  77. package/src/home/constants.tsx +142 -0
  78. package/src/home/feature-showcase.tsx +171 -0
  79. package/src/home/home-page.tsx +191 -0
  80. package/src/home/hooks/index.ts +1 -0
  81. package/src/home/hooks/useWidgetPreferences.ts +167 -0
  82. package/src/home/index.ts +33 -0
  83. package/src/home/quick-access-dialog.tsx +271 -0
  84. package/src/home/quick-access-menu.tsx +267 -0
  85. package/src/home/types.ts +140 -0
  86. package/src/home/welcome-card.tsx +92 -0
  87. package/src/home/widget-container.tsx +258 -0
  88. package/src/home/widgets/base-widget.tsx +200 -0
  89. package/src/home/widgets/customers-widget.tsx +74 -0
  90. package/src/home/widgets/index.ts +6 -0
  91. package/src/home/widgets/orders-widget.tsx +87 -0
  92. package/src/home/widgets/revenue-widget.tsx +71 -0
  93. package/src/home/widgets/stock-widget.tsx +109 -0
  94. package/src/hooks/index.tsx +598 -0
  95. package/src/hooks/use-tenant.test.tsx +30 -0
  96. package/src/hooks/use-tenant.ts +5 -0
  97. package/src/index.ts +17 -0
  98. package/src/infrastructure/__tests__/architecture-verification.spec.ts +103 -0
  99. package/src/infrastructure/api-service.ts +317 -0
  100. package/src/infrastructure/cache/cache-manager.ts +107 -0
  101. package/src/infrastructure/cache/cache.ts +120 -0
  102. package/src/infrastructure/cache/index.ts +8 -0
  103. package/src/infrastructure/cache/types.ts +48 -0
  104. package/src/infrastructure/cron/cron-manager.ts +239 -0
  105. package/src/infrastructure/cron/index.ts +6 -0
  106. package/src/infrastructure/cron/types.ts +41 -0
  107. package/src/infrastructure/event-bus/event-bus.ts +145 -0
  108. package/src/infrastructure/event-bus/index.ts +2 -0
  109. package/src/infrastructure/event-bus/types.ts +22 -0
  110. package/src/infrastructure/index.ts +32 -0
  111. package/src/infrastructure/lock/decorators.ts +67 -0
  112. package/src/infrastructure/lock/index.ts +2 -0
  113. package/src/infrastructure/lock/lock-manager.ts +33 -0
  114. package/src/infrastructure/logger/index.ts +2 -0
  115. package/src/infrastructure/logger/logger.ts +96 -0
  116. package/src/infrastructure/logger/types.ts +25 -0
  117. package/src/layout/index.tsx +185 -0
  118. package/src/navigation/index.ts +91 -0
  119. package/src/notification/index.ts +14 -0
  120. package/src/notification/notification-service.ts +120 -0
  121. package/src/notification/storage/in-memory.ts +56 -0
  122. package/src/notification/storage/index.ts +1 -0
  123. package/src/notification/types.ts +51 -0
  124. package/src/organization/branch-service.ts +299 -0
  125. package/src/organization/branches.config.ts +154 -0
  126. package/src/organization/index.ts +5 -0
  127. package/src/plugin/apps-registry.ts +97 -0
  128. package/src/plugin/index.ts +5 -0
  129. package/src/plugin/types.ts +41 -0
  130. package/src/providers/index.tsx +109 -0
  131. package/src/providers/tenant-provider.tsx +45 -0
  132. package/src/rbac/components/roles/role-card.tsx +158 -0
  133. package/src/rbac/components/roles/role-stats-cards.tsx +29 -0
  134. package/src/rbac/components/roles/role-toolbar.tsx +123 -0
  135. package/src/rbac/hooks/use-role-operations.ts +159 -0
  136. package/src/rbac/hooks/use-roles-data.ts +59 -0
  137. package/src/rbac/index.ts +297 -0
  138. package/src/rbac/lib/permission-helpers.ts +63 -0
  139. package/src/rbac/pages/action-list-page.tsx +25 -0
  140. package/src/rbac/pages/resource-list-page.tsx +25 -0
  141. package/src/rbac/pages/role-list-page.tsx +378 -0
  142. package/src/rbac/permission-service.ts +140 -0
  143. package/src/rbac/permissions.ts +135 -0
  144. package/src/rbac/resource-service.ts +115 -0
  145. package/src/rbac/resource-validator.ts +119 -0
  146. package/src/rbac/role-service.ts +165 -0
  147. package/src/rbac/server.ts +16 -0
  148. package/src/rbac/types.ts +38 -0
  149. package/src/schemas/action.schema.ts +66 -0
  150. package/src/schemas/branch.schema.ts +52 -0
  151. package/src/schemas/coming-soon-schema.ts +9 -0
  152. package/src/schemas/company.schema.ts +44 -0
  153. package/src/schemas/forgot-passward-schema.ts +9 -0
  154. package/src/schemas/index.ts +30 -0
  155. package/src/schemas/material-category.schema.ts +43 -0
  156. package/src/schemas/material-pricing.schema.ts +74 -0
  157. package/src/schemas/material.schema.ts +76 -0
  158. package/src/schemas/materials.ts +52 -0
  159. package/src/schemas/new-passward-schema.ts +15 -0
  160. package/src/schemas/partner-company.schema.ts +149 -0
  161. package/src/schemas/register-schema.ts +36 -0
  162. package/src/schemas/resource.schema.ts +133 -0
  163. package/src/schemas/role.schema.ts +11 -0
  164. package/src/schemas/sign-in-schema.ts +24 -0
  165. package/src/schemas/supplier-pricing.schema.ts +15 -0
  166. package/src/schemas/supplier.schema.ts +120 -0
  167. package/src/schemas/system-category-group.schema.ts +67 -0
  168. package/src/schemas/system-category.schema.ts +77 -0
  169. package/src/schemas/system-config.schema.ts +118 -0
  170. package/src/schemas/uom.schema.ts +75 -0
  171. package/src/schemas/user-supplier.schema.ts +179 -0
  172. package/src/schemas/user.schema.ts +18 -0
  173. package/src/schemas/verify-email-schema.ts +9 -0
  174. package/src/schemas/warehouse.schema.ts +49 -0
  175. package/src/system/components/categories/category-list.tsx +529 -0
  176. package/src/system/components/categories/category-manager.tsx +89 -0
  177. package/src/system/components/categories/group-sidebar.tsx +308 -0
  178. package/src/system/components/settings/setting-dialogs.tsx +197 -0
  179. package/src/system/components/settings/setting-field.tsx +291 -0
  180. package/src/system/components/settings/setting-form-dialog.tsx +308 -0
  181. package/src/system/components/settings/settings-groups.ts +80 -0
  182. package/src/system/components/settings/settings-search.tsx +71 -0
  183. package/src/system/components/settings/settings-section.tsx +74 -0
  184. package/src/system/components/settings/settings-sidebar.tsx +81 -0
  185. package/src/system/constants.ts +3 -0
  186. package/src/system/index.ts +150 -0
  187. package/src/system/job-manager.ts +176 -0
  188. package/src/system/pages/components/categories/category-list.tsx +537 -0
  189. package/src/system/pages/components/categories/category-manager.tsx +90 -0
  190. package/src/system/pages/components/categories/group-sidebar.tsx +311 -0
  191. package/src/system/pages/components/settings/sales-rules-settings.tsx +222 -0
  192. package/src/system/pages/components/settings/setting-dialogs.tsx +197 -0
  193. package/src/system/pages/components/settings/setting-field.tsx +292 -0
  194. package/src/system/pages/components/settings/setting-form-dialog.tsx +308 -0
  195. package/src/system/pages/components/settings/settings-groups.ts +87 -0
  196. package/src/system/pages/components/settings/settings-page.tsx +372 -0
  197. package/src/system/pages/components/settings/settings-search.tsx +71 -0
  198. package/src/system/pages/components/settings/settings-section.tsx +74 -0
  199. package/src/system/pages/components/settings/settings-sidebar.tsx +81 -0
  200. package/src/system/pages/components/settings/system-settings.tsx +244 -0
  201. package/src/system/pages/system-category-page.tsx +15 -0
  202. package/src/system/pages/system-settings-page.tsx +380 -0
  203. package/src/system/schemas/system-category-group.schema.ts +46 -0
  204. package/src/system/schemas/system-category.schema.ts +56 -0
  205. package/src/system/services/settings-service.ts +127 -0
  206. package/src/system/services/system-category-service.ts +63 -0
  207. package/src/system/types.ts +45 -0
  208. package/src/types/index.ts +703 -0
  209. package/src/ui/auth/auth-layout.tsx +135 -0
  210. package/src/ui/auth/forgot-password-form.tsx +98 -0
  211. package/src/ui/auth/index.tsx +7 -0
  212. package/src/ui/auth/new-password-form.tsx +107 -0
  213. package/src/ui/auth/oauth-links.tsx +30 -0
  214. package/src/ui/auth/register-form.tsx +202 -0
  215. package/src/ui/auth/sign-in-form.tsx +238 -0
  216. package/src/ui/auth/verify-email-form.tsx +104 -0
  217. package/src/ui/crud/index.tsx +10 -0
  218. package/src/ui/data-display/accordion.tsx +65 -0
  219. package/src/ui/data-display/aspect-ratio.tsx +11 -0
  220. package/src/ui/data-display/avatar.tsx +163 -0
  221. package/src/ui/data-display/bento-grid.tsx +77 -0
  222. package/src/ui/data-display/carousel.tsx +249 -0
  223. package/src/ui/data-display/chart.tsx +363 -0
  224. package/src/ui/data-display/code-block-highlight.tsx +54 -0
  225. package/src/ui/data-display/collapsible.tsx +42 -0
  226. package/src/ui/data-display/compact-stat-bar.tsx +149 -0
  227. package/src/ui/data-display/data-table/data-table-context.tsx +255 -0
  228. package/src/ui/data-display/data-table/data-table-empty-state.tsx +133 -0
  229. package/src/ui/data-display/data-table/data-table-skeleton.tsx +145 -0
  230. package/src/ui/data-display/data-table/data-table-toolbar.tsx +353 -0
  231. package/src/ui/data-display/data-table/data-table.tsx +597 -0
  232. package/src/ui/data-display/data-table/index.ts +44 -0
  233. package/src/ui/data-display/data-table-column-header.tsx +75 -0
  234. package/src/ui/data-display/data-table-pagination.tsx +130 -0
  235. package/src/ui/data-display/data-table-view-options.tsx +59 -0
  236. package/src/ui/data-display/formatted-number-input.tsx +210 -0
  237. package/src/ui/data-display/highlight.tsx +20 -0
  238. package/src/ui/data-display/hover-card.tsx +48 -0
  239. package/src/ui/data-display/index.tsx +50 -0
  240. package/src/ui/data-display/iphone-15-pro.tsx +114 -0
  241. package/src/ui/data-display/kanban/index.ts +4 -0
  242. package/src/ui/data-display/kanban/kanban-board.tsx +192 -0
  243. package/src/ui/data-display/kanban/kanban-column.tsx +74 -0
  244. package/src/ui/data-display/kanban/kanban-item.tsx +50 -0
  245. package/src/ui/data-display/kanban/kanban-types.ts +21 -0
  246. package/src/ui/data-display/kpi-card.tsx +68 -0
  247. package/src/ui/data-display/media-grid.tsx +110 -0
  248. package/src/ui/data-display/safari.tsx +175 -0
  249. package/src/ui/data-display/show-more-text.tsx +55 -0
  250. package/src/ui/data-display/tabs.tsx +68 -0
  251. package/src/ui/data-display/timeline.tsx +256 -0
  252. package/src/ui/feedback/alert.tsx +60 -0
  253. package/src/ui/feedback/context-menu.tsx +245 -0
  254. package/src/ui/feedback/drawer.tsx +132 -0
  255. package/src/ui/feedback/error-dialog.tsx +273 -0
  256. package/src/ui/feedback/index.tsx +183 -0
  257. package/src/ui/feedback/progress.tsx +32 -0
  258. package/src/ui/feedback/sheet.tsx +148 -0
  259. package/src/ui/feedback/sonner.tsx +36 -0
  260. package/src/ui/forms/command.tsx +157 -0
  261. package/src/ui/forms/date-picker.tsx +73 -0
  262. package/src/ui/forms/date-range-picker.tsx +76 -0
  263. package/src/ui/forms/date-time-picker.tsx +109 -0
  264. package/src/ui/forms/editor/editor-menu-bar.tsx +394 -0
  265. package/src/ui/forms/editor/index.tsx +130 -0
  266. package/src/ui/forms/editor/multi-select-example.tsx +1234 -0
  267. package/src/ui/forms/emoji-picker.tsx +109 -0
  268. package/src/ui/forms/file-dropzone.tsx +169 -0
  269. package/src/ui/forms/file-thumbnail.tsx +29 -0
  270. package/src/ui/forms/index.tsx +201 -0
  271. package/src/ui/forms/input-file.tsx +99 -0
  272. package/src/ui/forms/input-group.tsx +46 -0
  273. package/src/ui/forms/input-otp.tsx +81 -0
  274. package/src/ui/forms/input-phone.tsx +172 -0
  275. package/src/ui/forms/input-spin.tsx +116 -0
  276. package/src/ui/forms/input-tags.tsx +219 -0
  277. package/src/ui/forms/input-time.tsx +42 -0
  278. package/src/ui/forms/multi-select.tsx +629 -0
  279. package/src/ui/forms/multiple-date-picker.tsx +74 -0
  280. package/src/ui/forms/radio-group.tsx +42 -0
  281. package/src/ui/forms/rating.tsx +158 -0
  282. package/src/ui/forms/time-picker.tsx +57 -0
  283. package/src/ui/index.tsx +17 -0
  284. package/src/ui/layout/animated-list.tsx +77 -0
  285. package/src/ui/layout/animated-sidebar.tsx +294 -0
  286. package/src/ui/layout/command-menu.tsx +355 -0
  287. package/src/ui/layout/customizer.tsx +324 -0
  288. package/src/ui/layout/footer.tsx +43 -0
  289. package/src/ui/layout/full-screen-toggle.tsx +52 -0
  290. package/src/ui/layout/header-breadcrumb.tsx +77 -0
  291. package/src/ui/layout/horizontal-layout-header.tsx +83 -0
  292. package/src/ui/layout/horizontal-layout.tsx +50 -0
  293. package/src/ui/layout/index.tsx +25 -0
  294. package/src/ui/layout/language-dropdown.tsx +103 -0
  295. package/src/ui/layout/logo.tsx +63 -0
  296. package/src/ui/layout/main-layout.tsx +57 -0
  297. package/src/ui/layout/mode-dropdown.tsx +58 -0
  298. package/src/ui/layout/notification-dropdown.tsx +127 -0
  299. package/src/ui/layout/page-tabs.tsx +306 -0
  300. package/src/ui/layout/route-cache.tsx +214 -0
  301. package/src/ui/layout/sidebar-group-icon-menu.tsx +195 -0
  302. package/src/ui/layout/sidebar.tsx +279 -0
  303. package/src/ui/layout/tab-content-cache.tsx +201 -0
  304. package/src/ui/layout/tab-navigation-provider.tsx +536 -0
  305. package/src/ui/layout/toggle-mobile-sidebar.tsx +33 -0
  306. package/src/ui/layout/top-bar-header-menubar.tsx +412 -0
  307. package/src/ui/layout/user-dropdown.tsx +188 -0
  308. package/src/ui/layout/vertical-layout-header.tsx +65 -0
  309. package/src/ui/layout/vertical-layout.tsx +47 -0
  310. package/src/ui/management/audit-log-page.tsx +209 -0
  311. package/src/ui/management/cache-management.tsx +349 -0
  312. package/src/ui/management/index.ts +3 -0
  313. package/src/ui/management/job-management.tsx +308 -0
  314. package/src/ui/pages/not-found.tsx +30 -0
  315. package/src/ui/primitives/badge.tsx +66 -0
  316. package/src/ui/primitives/breadcrumb.tsx +103 -0
  317. package/src/ui/primitives/button.tsx +129 -0
  318. package/src/ui/primitives/calendar.tsx +74 -0
  319. package/src/ui/primitives/card.tsx +86 -0
  320. package/src/ui/primitives/checkbox.tsx +31 -0
  321. package/src/ui/primitives/client.ts +30 -0
  322. package/src/ui/primitives/combobox.tsx +290 -0
  323. package/src/ui/primitives/dialog.tsx +121 -0
  324. package/src/ui/primitives/dropdown-menu.tsx +239 -0
  325. package/src/ui/primitives/dynamic-icon.tsx +24 -0
  326. package/src/ui/primitives/index.tsx +134 -0
  327. package/src/ui/primitives/input-number.tsx +131 -0
  328. package/src/ui/primitives/input.tsx +22 -0
  329. package/src/ui/primitives/keyboard.tsx +23 -0
  330. package/src/ui/primitives/label.tsx +24 -0
  331. package/src/ui/primitives/menubar.tsx +262 -0
  332. package/src/ui/primitives/navigation-menu.tsx +157 -0
  333. package/src/ui/primitives/pagination.tsx +118 -0
  334. package/src/ui/primitives/popover.tsx +56 -0
  335. package/src/ui/primitives/prefetch-link.tsx +60 -0
  336. package/src/ui/primitives/resizable.tsx +59 -0
  337. package/src/ui/primitives/scroll-area.tsx +63 -0
  338. package/src/ui/primitives/select.tsx +172 -0
  339. package/src/ui/primitives/separator.tsx +51 -0
  340. package/src/ui/primitives/sidebar.tsx +844 -0
  341. package/src/ui/primitives/slider.tsx +27 -0
  342. package/src/ui/primitives/status-badge.tsx +47 -0
  343. package/src/ui/primitives/sticky-layout.tsx +50 -0
  344. package/src/ui/primitives/switch.tsx +29 -0
  345. package/src/ui/primitives/table.tsx +116 -0
  346. package/src/ui/primitives/tabs.tsx +55 -0
  347. package/src/ui/primitives/toggle-group.tsx +70 -0
  348. package/src/ui/primitives/toggle.tsx +47 -0
  349. package/src/ui/primitives/tooltip.tsx +59 -0
  350. package/src/user/components/dangerous-zone.tsx +34 -0
  351. package/src/user/components/delete-account-form.tsx +40 -0
  352. package/src/user/components/index.ts +4 -0
  353. package/src/user/components/profile-info-form.tsx +390 -0
  354. package/src/user/components/profile-info.tsx +32 -0
  355. package/src/user/components/unified-profile-dialog.tsx +1019 -0
  356. package/src/user/components/user-stats.tsx +27 -0
  357. package/src/user/components/user-toolbar.tsx +137 -0
  358. package/src/user/components/users-card-view.tsx +253 -0
  359. package/src/user/index.ts +11 -0
  360. package/src/user/pages/user-list-page.tsx +234 -0
  361. package/src/user/pages/users-client-page.tsx +385 -0
  362. package/src/user/profile-page.tsx +19 -0
  363. package/src/user/schemas.ts +68 -0
  364. package/src/user/types.ts +34 -0
  365. package/src/user/user-service.ts +538 -0
  366. package/src/utils/index.ts +906 -0
  367. package/src/workflow/activity-timeline.tsx +412 -0
  368. package/src/workflow/approval-workflow.tsx +31 -0
  369. package/src/workflow/index.ts +2 -0
  370. package/dist/audit/index.d.mts +0 -115
  371. package/dist/audit/index.d.ts +0 -115
  372. package/dist/audit/index.js +0 -204
  373. package/dist/audit/index.js.map +0 -1
  374. package/dist/audit/index.mjs +0 -200
  375. package/dist/audit/index.mjs.map +0 -1
  376. package/dist/auth/index.d.mts +0 -86
  377. package/dist/auth/index.d.ts +0 -86
  378. package/dist/auth/index.js +0 -210
  379. package/dist/auth/index.js.map +0 -1
  380. package/dist/auth/index.mjs +0 -198
  381. package/dist/auth/index.mjs.map +0 -1
  382. package/dist/button-1dWvP9Ib.d.mts +0 -30
  383. package/dist/button-1dWvP9Ib.d.ts +0 -30
  384. package/dist/calendar-2QzdEo1z.d.mts +0 -20
  385. package/dist/calendar-2QzdEo1z.d.ts +0 -20
  386. package/dist/code-generation/index.d.mts +0 -30
  387. package/dist/code-generation/index.d.ts +0 -30
  388. package/dist/code-generation/index.js +0 -31
  389. package/dist/code-generation/index.js.map +0 -1
  390. package/dist/code-generation/index.mjs +0 -28
  391. package/dist/code-generation/index.mjs.map +0 -1
  392. package/dist/configs/index.d.mts +0 -175
  393. package/dist/configs/index.d.ts +0 -175
  394. package/dist/configs/index.js +0 -254
  395. package/dist/configs/index.js.map +0 -1
  396. package/dist/configs/index.mjs +0 -233
  397. package/dist/configs/index.mjs.map +0 -1
  398. package/dist/crud/index.d.mts +0 -646
  399. package/dist/crud/index.d.ts +0 -646
  400. package/dist/crud/index.js +0 -11772
  401. package/dist/crud/index.js.map +0 -1
  402. package/dist/crud/index.mjs +0 -11665
  403. package/dist/crud/index.mjs.map +0 -1
  404. package/dist/crud/server.d.mts +0 -20
  405. package/dist/crud/server.d.ts +0 -20
  406. package/dist/crud/server.js +0 -123
  407. package/dist/crud/server.js.map +0 -1
  408. package/dist/crud/server.mjs +0 -120
  409. package/dist/crud/server.mjs.map +0 -1
  410. package/dist/data-table-skeleton-12NA8Mjx.d.mts +0 -39
  411. package/dist/data-table-skeleton-12NA8Mjx.d.ts +0 -39
  412. package/dist/dialog-bKfjZMTd.d.mts +0 -22
  413. package/dist/dialog-bKfjZMTd.d.ts +0 -22
  414. package/dist/dynamic-icon-DrGIiu2N.d.mts +0 -10
  415. package/dist/dynamic-icon-DrGIiu2N.d.ts +0 -10
  416. package/dist/home/index.d.mts +0 -269
  417. package/dist/home/index.d.ts +0 -269
  418. package/dist/home/index.js +0 -1678
  419. package/dist/home/index.js.map +0 -1
  420. package/dist/home/index.mjs +0 -1635
  421. package/dist/home/index.mjs.map +0 -1
  422. package/dist/hooks/index.d.mts +0 -7
  423. package/dist/hooks/index.d.ts +0 -7
  424. package/dist/hooks/index.js +0 -8316
  425. package/dist/hooks/index.js.map +0 -1
  426. package/dist/hooks/index.mjs +0 -8255
  427. package/dist/hooks/index.mjs.map +0 -1
  428. package/dist/index-50hpiPrV.d.ts +0 -116
  429. package/dist/index-B9zQVEVi.d.mts +0 -116
  430. package/dist/index.d.mts +0 -5
  431. package/dist/index.d.ts +0 -5
  432. package/dist/index.js +0 -123
  433. package/dist/index.js.map +0 -1
  434. package/dist/index.mjs +0 -118
  435. package/dist/index.mjs.map +0 -1
  436. package/dist/infrastructure/index.d.mts +0 -423
  437. package/dist/infrastructure/index.d.ts +0 -423
  438. package/dist/infrastructure/index.js +0 -633
  439. package/dist/infrastructure/index.js.map +0 -1
  440. package/dist/infrastructure/index.mjs +0 -619
  441. package/dist/infrastructure/index.mjs.map +0 -1
  442. package/dist/label-DWTEkNPo.d.ts +0 -226
  443. package/dist/label-LPpdcoBx.d.mts +0 -226
  444. package/dist/layout/index.d.mts +0 -48
  445. package/dist/layout/index.d.ts +0 -48
  446. package/dist/layout/index.js +0 -117
  447. package/dist/layout/index.js.map +0 -1
  448. package/dist/layout/index.mjs +0 -90
  449. package/dist/layout/index.mjs.map +0 -1
  450. package/dist/navigation/index.d.mts +0 -16
  451. package/dist/navigation/index.d.ts +0 -16
  452. package/dist/navigation/index.js +0 -53
  453. package/dist/navigation/index.js.map +0 -1
  454. package/dist/navigation/index.mjs +0 -50
  455. package/dist/navigation/index.mjs.map +0 -1
  456. package/dist/notification/index.d.mts +0 -105
  457. package/dist/notification/index.d.ts +0 -105
  458. package/dist/notification/index.js +0 -278
  459. package/dist/notification/index.js.map +0 -1
  460. package/dist/notification/index.mjs +0 -274
  461. package/dist/notification/index.mjs.map +0 -1
  462. package/dist/organization/index.d.mts +0 -99
  463. package/dist/organization/index.d.ts +0 -99
  464. package/dist/organization/index.js +0 -360
  465. package/dist/organization/index.js.map +0 -1
  466. package/dist/organization/index.mjs +0 -352
  467. package/dist/organization/index.mjs.map +0 -1
  468. package/dist/plugin/index.d.mts +0 -83
  469. package/dist/plugin/index.d.ts +0 -83
  470. package/dist/plugin/index.js +0 -86
  471. package/dist/plugin/index.js.map +0 -1
  472. package/dist/plugin/index.mjs +0 -84
  473. package/dist/plugin/index.mjs.map +0 -1
  474. package/dist/providers/index.d.mts +0 -25
  475. package/dist/providers/index.d.ts +0 -25
  476. package/dist/providers/index.js +0 -84
  477. package/dist/providers/index.js.map +0 -1
  478. package/dist/providers/index.mjs +0 -77
  479. package/dist/providers/index.mjs.map +0 -1
  480. package/dist/rbac/index.d.mts +0 -226
  481. package/dist/rbac/index.d.ts +0 -226
  482. package/dist/rbac/index.js +0 -4784
  483. package/dist/rbac/index.js.map +0 -1
  484. package/dist/rbac/index.mjs +0 -4722
  485. package/dist/rbac/index.mjs.map +0 -1
  486. package/dist/rbac/permissions.d.mts +0 -26
  487. package/dist/rbac/permissions.d.ts +0 -26
  488. package/dist/rbac/permissions.js +0 -94
  489. package/dist/rbac/permissions.js.map +0 -1
  490. package/dist/rbac/permissions.mjs +0 -90
  491. package/dist/rbac/permissions.mjs.map +0 -1
  492. package/dist/rbac/server.d.mts +0 -1
  493. package/dist/rbac/server.d.ts +0 -1
  494. package/dist/rbac/server.js +0 -128
  495. package/dist/rbac/server.js.map +0 -1
  496. package/dist/rbac/server.mjs +0 -124
  497. package/dist/rbac/server.mjs.map +0 -1
  498. package/dist/schemas/index.d.mts +0 -1257
  499. package/dist/schemas/index.d.ts +0 -1257
  500. package/dist/schemas/index.js +0 -572
  501. package/dist/schemas/index.js.map +0 -1
  502. package/dist/schemas/index.mjs +0 -523
  503. package/dist/schemas/index.mjs.map +0 -1
  504. package/dist/server-QuYCTa89.d.mts +0 -83
  505. package/dist/server-QuYCTa89.d.ts +0 -83
  506. package/dist/sonner-C74GlRDQ.d.mts +0 -71
  507. package/dist/sonner-C74GlRDQ.d.ts +0 -71
  508. package/dist/status-BOXZgIqX.d.mts +0 -12
  509. package/dist/status-BOXZgIqX.d.ts +0 -12
  510. package/dist/system/index.d.mts +0 -77
  511. package/dist/system/index.d.ts +0 -77
  512. package/dist/system/index.js +0 -102
  513. package/dist/system/index.js.map +0 -1
  514. package/dist/system/index.mjs +0 -100
  515. package/dist/system/index.mjs.map +0 -1
  516. package/dist/tabs-C6FfBwPY.d.mts +0 -18
  517. package/dist/tabs-C6FfBwPY.d.ts +0 -18
  518. package/dist/tenant-provider-B8eC_Wpb.d.mts +0 -27
  519. package/dist/tenant-provider-B8eC_Wpb.d.ts +0 -27
  520. package/dist/types/index.d.mts +0 -469
  521. package/dist/types/index.d.ts +0 -469
  522. package/dist/types/index.js +0 -25
  523. package/dist/types/index.js.map +0 -1
  524. package/dist/types/index.mjs +0 -21
  525. package/dist/types/index.mjs.map +0 -1
  526. package/dist/ui/auth.d.mts +0 -39
  527. package/dist/ui/auth.d.ts +0 -39
  528. package/dist/ui/auth.js +0 -4941
  529. package/dist/ui/auth.js.map +0 -1
  530. package/dist/ui/auth.mjs +0 -4896
  531. package/dist/ui/auth.mjs.map +0 -1
  532. package/dist/ui/crud.d.mts +0 -2
  533. package/dist/ui/crud.d.ts +0 -2
  534. package/dist/ui/crud.js +0 -4
  535. package/dist/ui/crud.js.map +0 -1
  536. package/dist/ui/crud.mjs +0 -3
  537. package/dist/ui/crud.mjs.map +0 -1
  538. package/dist/ui/data-display.d.mts +0 -596
  539. package/dist/ui/data-display.d.ts +0 -596
  540. package/dist/ui/data-display.js +0 -5307
  541. package/dist/ui/data-display.js.map +0 -1
  542. package/dist/ui/data-display.mjs +0 -5212
  543. package/dist/ui/data-display.mjs.map +0 -1
  544. package/dist/ui/feedback.d.mts +0 -55
  545. package/dist/ui/feedback.d.ts +0 -55
  546. package/dist/ui/feedback.js +0 -2608
  547. package/dist/ui/feedback.js.map +0 -1
  548. package/dist/ui/feedback.mjs +0 -2526
  549. package/dist/ui/feedback.mjs.map +0 -1
  550. package/dist/ui/forms.d.mts +0 -309
  551. package/dist/ui/forms.d.ts +0 -309
  552. package/dist/ui/forms.js +0 -4656
  553. package/dist/ui/forms.js.map +0 -1
  554. package/dist/ui/forms.mjs +0 -4571
  555. package/dist/ui/forms.mjs.map +0 -1
  556. package/dist/ui/index.d.mts +0 -331
  557. package/dist/ui/index.d.ts +0 -331
  558. package/dist/ui/index.js +0 -16953
  559. package/dist/ui/index.js.map +0 -1
  560. package/dist/ui/index.mjs +0 -16598
  561. package/dist/ui/index.mjs.map +0 -1
  562. package/dist/ui/primitives/client.d.mts +0 -61
  563. package/dist/ui/primitives/client.d.ts +0 -61
  564. package/dist/ui/primitives/client.js +0 -3408
  565. package/dist/ui/primitives/client.js.map +0 -1
  566. package/dist/ui/primitives/client.mjs +0 -3256
  567. package/dist/ui/primitives/client.mjs.map +0 -1
  568. package/dist/ui/primitives.d.mts +0 -113
  569. package/dist/ui/primitives.d.ts +0 -113
  570. package/dist/ui/primitives.js +0 -3356
  571. package/dist/ui/primitives.js.map +0 -1
  572. package/dist/ui/primitives.mjs +0 -3227
  573. package/dist/ui/primitives.mjs.map +0 -1
  574. package/dist/user/index.d.mts +0 -228
  575. package/dist/user/index.d.ts +0 -228
  576. package/dist/user/index.js +0 -4306
  577. package/dist/user/index.js.map +0 -1
  578. package/dist/user/index.mjs +0 -4260
  579. package/dist/user/index.mjs.map +0 -1
  580. package/dist/utils/index.d.mts +0 -205
  581. package/dist/utils/index.d.ts +0 -205
  582. package/dist/utils/index.js +0 -574
  583. package/dist/utils/index.js.map +0 -1
  584. package/dist/utils/index.mjs +0 -514
  585. package/dist/utils/index.mjs.map +0 -1
  586. package/dist/workflow/index.d.mts +0 -40
  587. package/dist/workflow/index.d.ts +0 -40
  588. package/dist/workflow/index.js +0 -3710
  589. package/dist/workflow/index.js.map +0 -1
  590. package/dist/workflow/index.mjs +0 -3677
  591. package/dist/workflow/index.mjs.map +0 -1
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { LockManager } from "../lock/lock-manager";
3
+ import { WithLock } from "../lock/decorators";
4
+ import type { CacheOptions, Cache } from "../cache/types";
5
+ import { CacheNamespace } from "../cache/types";
6
+
7
+ // Mock Cache Implementation for Testing
8
+ class MockCache implements Cache {
9
+ private store = new Map<string, any>();
10
+ async get<T>(key: string): Promise<T | undefined> {
11
+ return this.store.get(key);
12
+ }
13
+ async set<T>(key: string, value: T): Promise<void> {
14
+ this.store.set(key, value);
15
+ }
16
+ async del(key: string): Promise<void> {
17
+ this.store.delete(key);
18
+ }
19
+ async has(key: string): Promise<boolean> {
20
+ return this.store.has(key);
21
+ }
22
+ async reset(): Promise<void> {
23
+ this.store.clear();
24
+ }
25
+ async keys(): Promise<string[]> {
26
+ return Array.from(this.store.keys());
27
+ }
28
+ }
29
+
30
+ describe("Architecture Verification: Phase 2 Enhancements", () => {
31
+ describe("2.2 Concurrency Control (LockManager)", () => {
32
+ it("should acquire and release lock successfully", async () => {
33
+ const cache = new MockCache();
34
+ const lockManager = new LockManager(cache);
35
+ const resourceId = "order-123";
36
+
37
+ const acquired = await lockManager.acquire(resourceId, { ttl: 1000 });
38
+ expect(acquired).toBe(true);
39
+
40
+ const isLocked = await cache.has(resourceId);
41
+ expect(isLocked).toBe(true);
42
+
43
+ await lockManager.release(resourceId);
44
+ const isLockedAfterRelease = await cache.has(resourceId);
45
+ expect(isLockedAfterRelease).toBe(false);
46
+ });
47
+
48
+ it("should fail to acquire lock if already locked", async () => {
49
+ const cache = new MockCache();
50
+ const lockManager = new LockManager(cache);
51
+ const resourceId = "order-456";
52
+
53
+ await lockManager.acquire(resourceId, { ttl: 5000 }); // Lock 1
54
+ const acquiredAgain = await lockManager.acquire(resourceId, {
55
+ ttl: 1000,
56
+ }); // Lock 2
57
+
58
+ expect(acquiredAgain).toBe(false);
59
+ });
60
+ });
61
+
62
+ describe("2.2 Decorator Usage (@WithLock)", () => {
63
+ it("should execute method with lock", async () => {
64
+ const cache = new MockCache();
65
+ const lockManager = new LockManager(cache);
66
+
67
+ class OrderService {
68
+ lockManager = lockManager;
69
+ executionCount = 0;
70
+
71
+ @WithLock("{0}")
72
+ async processOrder(orderId: string) {
73
+ this.executionCount++;
74
+ return `Processed ${orderId}`;
75
+ }
76
+ }
77
+
78
+ const service = new OrderService();
79
+ const result = await service.processOrder("100");
80
+
81
+ expect(result).toBe("Processed 100");
82
+ expect(service.executionCount).toBe(1);
83
+ // Lock should be released after execution
84
+ expect(await cache.has("lock:order-100")).toBe(false);
85
+ });
86
+ });
87
+
88
+ describe("2.3 Namespace Caching Strategy", () => {
89
+ it("should define CacheNamespace enum correctly", () => {
90
+ expect(CacheNamespace.Auth).toBe("auth");
91
+ expect(CacheNamespace.TenantConfig).toBe("tenant-config");
92
+ });
93
+
94
+ it("should allow configuring cache with namespace (Simulation)", () => {
95
+ // This tests the TYPE definition and intent, as actual implementation depends on the Factory we haven't fully refactored yet.
96
+ const options: CacheOptions = {
97
+ name: "redis",
98
+ prefix: CacheNamespace.Auth,
99
+ };
100
+ expect(options.prefix).toBe("auth");
101
+ });
102
+ });
103
+ });
@@ -0,0 +1,317 @@
1
+ /**
2
+ * @goerp/core - Abstract API Service Layer
3
+ * Inspired by Plane's APIService pattern (packages/services/src/api.service.ts)
4
+ *
5
+ * Provides a centralized HTTP client base class for all domain services.
6
+ * Features:
7
+ * - Centralized error handling & 401 redirect
8
+ * - Request/Response interceptors
9
+ * - Type-safe CRUD methods
10
+ * - Automatic retry logic (configurable)
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * class CustomerService extends APIService {
15
+ * constructor() { super('/api/sales/customers') }
16
+ *
17
+ * async list(filters?: CustomerFilters) {
18
+ * return this.get<PaginatedResponse<Customer>>('/', { params: filters })
19
+ * }
20
+ *
21
+ * async getById(id: string) {
22
+ * return this.get<Customer>(`/${id}`)
23
+ * }
24
+ *
25
+ * async create(data: CreateCustomerDTO) {
26
+ * return this.post<Customer>('/', data)
27
+ * }
28
+ * }
29
+ * ```
30
+ */
31
+
32
+ // ============================================================
33
+ // Types
34
+ // ============================================================
35
+
36
+ export interface APIRequestConfig {
37
+ params?: Record<string, unknown>
38
+ headers?: Record<string, string>
39
+ signal?: AbortSignal
40
+ /** Override default timeout (ms) */
41
+ timeout?: number
42
+ }
43
+
44
+ export interface APIResponse<T = unknown> {
45
+ data: T
46
+ status: number
47
+ ok: boolean
48
+ }
49
+
50
+ export interface APIError {
51
+ message: string
52
+ status: number
53
+ code?: string
54
+ details?: unknown
55
+ }
56
+
57
+ export interface APIServiceOptions {
58
+ /** Called when a 401 response is received */
59
+ onUnauthorized?: () => void
60
+ /** Called before each request (e.g., for logging) */
61
+ onRequest?: (method: string, url: string) => void
62
+ /** Called after each response (e.g., for logging) */
63
+ onResponse?: (method: string, url: string, status: number) => void
64
+ /** Called on any error */
65
+ onError?: (error: APIError) => void
66
+ /** Default timeout in ms (default: 30000) */
67
+ timeout?: number
68
+ /** Default headers to include in every request */
69
+ defaultHeaders?: Record<string, string>
70
+ }
71
+
72
+ // ============================================================
73
+ // APIService Base Class
74
+ // ============================================================
75
+
76
+ /**
77
+ * Abstract base class for making HTTP requests using fetch API.
78
+ * All domain services (CustomerService, OrderService, etc.) should extend this class.
79
+ *
80
+ * Unlike Plane's axios-based approach, we use native fetch() for Next.js compatibility
81
+ * (better SSR support, no extra dependency, works with Server Components).
82
+ */
83
+ export abstract class APIService {
84
+ protected readonly baseURL: string
85
+ private readonly options: APIServiceOptions
86
+
87
+ constructor(baseURL: string, options: APIServiceOptions = {}) {
88
+ this.baseURL = baseURL
89
+ this.options = options
90
+ }
91
+
92
+ // --------------------------------------------------------
93
+ // Core HTTP Methods
94
+ // --------------------------------------------------------
95
+
96
+ /**
97
+ * Makes a GET request to the specified endpoint
98
+ */
99
+ protected async get<T = unknown>(
100
+ endpoint: string = "",
101
+ config: APIRequestConfig = {}
102
+ ): Promise<T> {
103
+ const url = this.buildURL(endpoint, config.params)
104
+ return this.request<T>("GET", url, undefined, config)
105
+ }
106
+
107
+ /**
108
+ * Makes a POST request with JSON body
109
+ */
110
+ protected async post<T = unknown>(
111
+ endpoint: string = "",
112
+ data?: unknown,
113
+ config: APIRequestConfig = {}
114
+ ): Promise<T> {
115
+ const url = this.buildURL(endpoint)
116
+ return this.request<T>("POST", url, data, config)
117
+ }
118
+
119
+ /**
120
+ * Makes a PUT request with JSON body
121
+ */
122
+ protected async put<T = unknown>(
123
+ endpoint: string = "",
124
+ data?: unknown,
125
+ config: APIRequestConfig = {}
126
+ ): Promise<T> {
127
+ const url = this.buildURL(endpoint)
128
+ return this.request<T>("PUT", url, data, config)
129
+ }
130
+
131
+ /**
132
+ * Makes a PATCH request with JSON body
133
+ */
134
+ protected async patch<T = unknown>(
135
+ endpoint: string = "",
136
+ data?: unknown,
137
+ config: APIRequestConfig = {}
138
+ ): Promise<T> {
139
+ const url = this.buildURL(endpoint)
140
+ return this.request<T>("PATCH", url, data, config)
141
+ }
142
+
143
+ /**
144
+ * Makes a DELETE request
145
+ */
146
+ protected async delete<T = unknown>(
147
+ endpoint: string = "",
148
+ data?: unknown,
149
+ config: APIRequestConfig = {}
150
+ ): Promise<T> {
151
+ const url = this.buildURL(endpoint)
152
+ return this.request<T>("DELETE", url, data, config)
153
+ }
154
+
155
+ // --------------------------------------------------------
156
+ // Internal Helpers
157
+ // --------------------------------------------------------
158
+
159
+ /**
160
+ * Build full URL with query params
161
+ */
162
+ private buildURL(endpoint: string, params?: Record<string, unknown>): string {
163
+ const url = `${this.baseURL}${endpoint}`
164
+ if (!params || Object.keys(params).length === 0) return url
165
+
166
+ const searchParams = new URLSearchParams()
167
+ for (const [key, value] of Object.entries(params)) {
168
+ if (value !== undefined && value !== null && value !== "") {
169
+ searchParams.append(key, String(value))
170
+ }
171
+ }
172
+
173
+ const queryString = searchParams.toString()
174
+ return queryString ? `${url}?${queryString}` : url
175
+ }
176
+
177
+ /**
178
+ * Core request method — all HTTP methods funnel through here.
179
+ * Handles: headers, body, timeout, error handling, 401 redirect.
180
+ */
181
+ private async request<T>(
182
+ method: string,
183
+ url: string,
184
+ data?: unknown,
185
+ config: APIRequestConfig = {}
186
+ ): Promise<T> {
187
+ // Notify interceptor
188
+ this.options.onRequest?.(method, url)
189
+
190
+ const headers: Record<string, string> = {
191
+ "Content-Type": "application/json",
192
+ ...this.options.defaultHeaders,
193
+ ...config.headers,
194
+ }
195
+
196
+ const fetchOptions: RequestInit = {
197
+ method,
198
+ headers,
199
+ credentials: "include", // Send cookies (same as axios withCredentials)
200
+ signal: config.signal,
201
+ }
202
+
203
+ // Attach body for non-GET methods
204
+ if (data !== undefined && method !== "GET") {
205
+ fetchOptions.body = JSON.stringify(data)
206
+ }
207
+
208
+ try {
209
+ const response = await fetch(url, fetchOptions)
210
+
211
+ // Notify response interceptor
212
+ this.options.onResponse?.(method, url, response.status)
213
+
214
+ // Handle 401 — Centralized unauthorized redirect
215
+ if (response.status === 401) {
216
+ const error: APIError = {
217
+ message: "Unauthorized",
218
+ status: 401,
219
+ code: "UNAUTHORIZED",
220
+ }
221
+ this.options.onUnauthorized?.()
222
+ this.options.onError?.(error)
223
+ throw error
224
+ }
225
+
226
+ // Handle other error statuses
227
+ if (!response.ok) {
228
+ let errorBody: unknown
229
+ try {
230
+ errorBody = await response.json()
231
+ } catch {
232
+ errorBody = await response.text()
233
+ }
234
+
235
+ const error: APIError = {
236
+ message:
237
+ typeof errorBody === "object" && errorBody !== null && "message" in errorBody
238
+ ? String((errorBody as Record<string, unknown>).message)
239
+ : `HTTP Error ${response.status}`,
240
+ status: response.status,
241
+ code:
242
+ typeof errorBody === "object" && errorBody !== null && "code" in errorBody
243
+ ? String((errorBody as Record<string, unknown>).code)
244
+ : undefined,
245
+ details: errorBody,
246
+ }
247
+ this.options.onError?.(error)
248
+ throw error
249
+ }
250
+
251
+ // Handle empty responses (204 No Content)
252
+ if (response.status === 204) {
253
+ return undefined as T
254
+ }
255
+
256
+ // Parse JSON response
257
+ const responseData = await response.json()
258
+ return responseData as T
259
+ } catch (error) {
260
+ // Re-throw APIError as-is
261
+ if (this.isAPIError(error)) {
262
+ throw error
263
+ }
264
+
265
+ // Wrap network/timeout errors
266
+ const apiError: APIError = {
267
+ message: error instanceof Error ? error.message : "Network error",
268
+ status: 0,
269
+ code: "NETWORK_ERROR",
270
+ }
271
+ this.options.onError?.(apiError)
272
+ throw apiError
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Type guard for APIError
278
+ */
279
+ private isAPIError(error: unknown): error is APIError {
280
+ return (
281
+ typeof error === "object" &&
282
+ error !== null &&
283
+ "message" in error &&
284
+ "status" in error
285
+ )
286
+ }
287
+ }
288
+
289
+ // ============================================================
290
+ // Pre-configured Service Factory
291
+ // ============================================================
292
+
293
+ /**
294
+ * Default options for client-side services.
295
+ * Handles 401 redirect to login page automatically.
296
+ */
297
+ export const defaultClientOptions: APIServiceOptions = {
298
+ onUnauthorized: () => {
299
+ if (typeof window !== "undefined") {
300
+ const currentPath = window.location.pathname
301
+ window.location.replace(`/login?next_path=${encodeURIComponent(currentPath)}`)
302
+ }
303
+ },
304
+ }
305
+
306
+ /**
307
+ * Helper to check if an error is an APIError
308
+ */
309
+ export function isAPIError(error: unknown): error is APIError {
310
+ return (
311
+ typeof error === "object" &&
312
+ error !== null &&
313
+ "message" in error &&
314
+ "status" in error &&
315
+ typeof (error as APIError).status === "number"
316
+ )
317
+ }
@@ -0,0 +1,107 @@
1
+ import type {
2
+ Cache,
3
+ CacheManager as ICacheManager,
4
+ CacheManagerOptions,
5
+ CacheOptions,
6
+ } from "./types";
7
+ import { MemoryCache } from "./cache";
8
+
9
+ /**
10
+ * CacheManager - manages multiple cache instances
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { cacheManager } from '@goerp/core/infrastructure';
15
+ *
16
+ * const userCache = cacheManager.create({ name: 'users', ttl: 3600 });
17
+ * await userCache.set('user:123', userData);
18
+ * const user = await userCache.get('user:123');
19
+ * ```
20
+ */
21
+ class CacheManagerImpl implements ICacheManager {
22
+ private caches = new Map<string, Cache>();
23
+ private options: CacheManagerOptions;
24
+
25
+ constructor(options: CacheManagerOptions = {}) {
26
+ this.options = {
27
+ defaultTtl: options.defaultTtl || 3600,
28
+ prefix: options.prefix || "",
29
+ };
30
+ }
31
+
32
+ create(options: CacheOptions): Cache {
33
+ const existing = this.caches.get(options.name);
34
+ if (existing) {
35
+ return existing;
36
+ }
37
+
38
+ const cache = new MemoryCache({
39
+ ...options,
40
+ ttl: options.ttl || this.options.defaultTtl,
41
+ prefix: this.options.prefix,
42
+ });
43
+
44
+ this.caches.set(options.name, cache);
45
+ return cache;
46
+ }
47
+
48
+ get(name: string): Cache | undefined {
49
+ return this.caches.get(name);
50
+ }
51
+
52
+ async flushAll(): Promise<void> {
53
+ const promises: Promise<void>[] = [];
54
+ for (const cache of this.caches.values()) {
55
+ promises.push(cache.reset());
56
+ }
57
+ await Promise.all(promises);
58
+ }
59
+
60
+ /** Get all cache names */
61
+ getAllCaches(): string[] {
62
+ return Array.from(this.caches.keys());
63
+ }
64
+
65
+ /** Get stats for all caches (for admin dashboard) */
66
+ async getStats(): Promise<
67
+ Array<{ name: string; itemCount: number; ttl: number }>
68
+ > {
69
+ const stats: Array<{ name: string; itemCount: number; ttl: number }> = [];
70
+
71
+ for (const [name, cache] of this.caches.entries()) {
72
+ const keys = await cache.keys();
73
+ stats.push({
74
+ name,
75
+ itemCount: keys.length,
76
+ ttl: (cache as MemoryCache).getInfo().ttl,
77
+ });
78
+ }
79
+
80
+ return stats;
81
+ }
82
+
83
+ /** Clear a specific cache by name */
84
+ async clearCache(name: string): Promise<boolean> {
85
+ const cache = this.caches.get(name);
86
+ if (cache) {
87
+ await cache.reset();
88
+ return true;
89
+ }
90
+ return false;
91
+ }
92
+ }
93
+
94
+ // Singleton instance pattern stable across hot-reloads
95
+ const globalForCache = globalThis as unknown as {
96
+ coreCacheManager: CacheManagerImpl | undefined;
97
+ };
98
+
99
+ export const cacheManager =
100
+ globalForCache.coreCacheManager ?? new CacheManagerImpl();
101
+
102
+ if (process.env.NODE_ENV !== "production") {
103
+ globalForCache.coreCacheManager = cacheManager;
104
+ }
105
+
106
+ // Export class for testing
107
+ export { CacheManagerImpl };
@@ -0,0 +1,120 @@
1
+ import type { Cache, CacheOptions } from "./types";
2
+
3
+ interface CacheEntry<T> {
4
+ value: T;
5
+ expireAt: number | null;
6
+ }
7
+
8
+ /**
9
+ * Simple in-memory cache implementation
10
+ */
11
+ export class MemoryCache implements Cache {
12
+ private store = new Map<string, CacheEntry<unknown>>();
13
+ private name: string;
14
+ private defaultTtl: number;
15
+ private maxSize: number;
16
+ private prefix: string;
17
+
18
+ constructor(options: CacheOptions & { prefix?: string }) {
19
+ this.name = options.name;
20
+ this.defaultTtl = options.ttl || 3600;
21
+ this.maxSize = options.max || 1000;
22
+ this.prefix = options.prefix || "";
23
+ }
24
+
25
+ private getFullKey(key: string): string {
26
+ return this.prefix
27
+ ? `${this.prefix}:${this.name}:${key}`
28
+ : `${this.name}:${key}`;
29
+ }
30
+
31
+ private isExpired(entry: CacheEntry<unknown>): boolean {
32
+ if (entry.expireAt === null) return false;
33
+ return Date.now() > entry.expireAt;
34
+ }
35
+
36
+ private cleanup(): void {
37
+ for (const [key, entry] of this.store.entries()) {
38
+ if (this.isExpired(entry)) {
39
+ this.store.delete(key);
40
+ }
41
+ }
42
+ }
43
+
44
+ async get<T>(key: string): Promise<T | undefined> {
45
+ const fullKey = this.getFullKey(key);
46
+ const entry = this.store.get(fullKey);
47
+
48
+ if (!entry) return undefined;
49
+
50
+ if (this.isExpired(entry)) {
51
+ this.store.delete(fullKey);
52
+ return undefined;
53
+ }
54
+
55
+ return entry.value as T;
56
+ }
57
+
58
+ async set<T>(key: string, value: T, ttl?: number): Promise<void> {
59
+ const fullKey = this.getFullKey(key);
60
+ const ttlSeconds = ttl ?? this.defaultTtl;
61
+
62
+ // Enforce max size
63
+ if (this.store.size >= this.maxSize) {
64
+ this.cleanup();
65
+ // If still full, remove oldest
66
+ if (this.store.size >= this.maxSize) {
67
+ const firstKey = this.store.keys().next().value;
68
+ if (firstKey) this.store.delete(firstKey);
69
+ }
70
+ }
71
+
72
+ this.store.set(fullKey, {
73
+ value,
74
+ expireAt: ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : null,
75
+ });
76
+ }
77
+
78
+ async del(key: string): Promise<void> {
79
+ const fullKey = this.getFullKey(key);
80
+ this.store.delete(fullKey);
81
+ }
82
+
83
+ async has(key: string): Promise<boolean> {
84
+ const value = await this.get(key);
85
+ return value !== undefined;
86
+ }
87
+
88
+ async reset(): Promise<void> {
89
+ // Only clear keys for this cache (by prefix)
90
+ const prefix = this.getFullKey("");
91
+ for (const key of this.store.keys()) {
92
+ if (key.startsWith(prefix)) {
93
+ this.store.delete(key);
94
+ }
95
+ }
96
+ }
97
+
98
+ async keys(): Promise<string[]> {
99
+ this.cleanup();
100
+ const prefix = this.getFullKey("");
101
+ const result: string[] = [];
102
+
103
+ for (const key of this.store.keys()) {
104
+ if (key.startsWith(prefix)) {
105
+ result.push(key.slice(prefix.length));
106
+ }
107
+ }
108
+
109
+ return result;
110
+ }
111
+
112
+ /** Get cache info for admin dashboard */
113
+ getInfo(): { name: string; ttl: number; maxSize: number } {
114
+ return {
115
+ name: this.name,
116
+ ttl: this.defaultTtl,
117
+ maxSize: this.maxSize,
118
+ };
119
+ }
120
+ }
@@ -0,0 +1,8 @@
1
+ export { cacheManager, CacheManagerImpl } from "./cache-manager";
2
+ export { MemoryCache } from "./cache";
3
+ export type {
4
+ Cache,
5
+ CacheOptions,
6
+ CacheManager,
7
+ CacheManagerOptions,
8
+ } from "./types";
@@ -0,0 +1,48 @@
1
+ export enum CacheNamespace {
2
+ Auth = "auth",
3
+ Lock = "lock",
4
+ Metadata = "metadata",
5
+ TenantConfig = "tenant-config",
6
+ RateLimit = "rate-limit",
7
+ }
8
+
9
+ export interface CacheOptions {
10
+ /**
11
+ * Name of the cache store (e.g. 'redis', 'memory')
12
+ */
13
+ name: string;
14
+ /**
15
+ * Default TTL in seconds
16
+ */
17
+ ttl?: number;
18
+ /**
19
+ * Max number of items (for memory cache)
20
+ */
21
+ max?: number;
22
+ /**
23
+ * Namespace prefix for all keys in this cache instance
24
+ */
25
+ prefix?: CacheNamespace | string;
26
+ }
27
+
28
+ export interface Cache {
29
+ get<T>(key: string): Promise<T | undefined>;
30
+ set<T>(key: string, value: T, ttl?: number): Promise<void>;
31
+ del(key: string): Promise<void>;
32
+ has(key: string): Promise<boolean>;
33
+ reset(): Promise<void>;
34
+ keys(): Promise<string[]>;
35
+ }
36
+ export interface CacheManagerOptions {
37
+ defaultTtl?: number;
38
+ prefix?: string;
39
+ }
40
+
41
+ export interface CacheManager {
42
+ create(options: CacheOptions): Cache;
43
+ get(name: string): Cache | undefined;
44
+ flushAll(): Promise<void>;
45
+ getAllCaches(): string[];
46
+ getStats(): Promise<Array<{ name: string; itemCount: number; ttl: number }>>;
47
+ clearCache(name: string): Promise<boolean>;
48
+ }