@carlonicora/nextjs-jsonapi 1.0.3 → 1.0.4

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 (261) hide show
  1. package/package.json +2 -1
  2. package/src/atoms/index.ts +1 -0
  3. package/src/atoms/recentPagesAtom.ts +10 -0
  4. package/src/client/context/JsonApiContext.ts +61 -0
  5. package/src/client/context/JsonApiProvider.tsx +27 -0
  6. package/src/client/context/index.ts +2 -0
  7. package/src/client/hooks/index.ts +3 -0
  8. package/src/client/hooks/useJsonApiGet.ts +188 -0
  9. package/src/client/hooks/useJsonApiMutation.ts +193 -0
  10. package/src/client/hooks/useRehydration.ts +47 -0
  11. package/src/client/index.ts +11 -0
  12. package/src/client/request.ts +97 -0
  13. package/src/client/token.ts +10 -0
  14. package/src/components/containers/PageContainer.tsx +15 -0
  15. package/src/components/containers/ReactMarkdownContainer.tsx +119 -0
  16. package/src/components/containers/TabsContainer.tsx +93 -0
  17. package/src/components/containers/index.ts +3 -0
  18. package/src/components/contents/AttributeElement.tsx +20 -0
  19. package/src/components/contents/index.ts +1 -0
  20. package/src/components/details/AllowedUsersDetails.tsx +23 -0
  21. package/src/components/details/index.ts +1 -0
  22. package/src/components/editors/BlockNoteDiffInlineContent.tsx +152 -0
  23. package/src/components/editors/BlockNoteEditor.tsx +404 -0
  24. package/src/components/editors/BlockNoteEditorContainer.tsx +13 -0
  25. package/src/components/editors/BlockNoteEditorFormattingToolbar.tsx +38 -0
  26. package/src/components/editors/index.ts +1 -0
  27. package/src/components/errors/ErrorDetails.tsx +41 -0
  28. package/src/components/errors/errorToast.ts +9 -0
  29. package/src/components/errors/index.ts +2 -0
  30. package/src/components/forms/CommonAssociationForm.tsx +162 -0
  31. package/src/components/forms/CommonDeleter.tsx +94 -0
  32. package/src/components/forms/CommonEditorButtons.tsx +30 -0
  33. package/src/components/forms/CommonEditorHeader.tsx +35 -0
  34. package/src/components/forms/CommonEditorTrigger.tsx +26 -0
  35. package/src/components/forms/DatePickerPopover.tsx +219 -0
  36. package/src/components/forms/DateRangeSelector.tsx +110 -0
  37. package/src/components/forms/FileUploader.tsx +324 -0
  38. package/src/components/forms/FormCheckbox.tsx +66 -0
  39. package/src/components/forms/FormContainerGeneric.tsx +39 -0
  40. package/src/components/forms/FormDate.tsx +247 -0
  41. package/src/components/forms/FormDateTime.tsx +231 -0
  42. package/src/components/forms/FormInput.tsx +110 -0
  43. package/src/components/forms/FormPassword.tsx +54 -0
  44. package/src/components/forms/FormPlaceAutocomplete.tsx +286 -0
  45. package/src/components/forms/FormSelect.tsx +72 -0
  46. package/src/components/forms/FormSlider.tsx +51 -0
  47. package/src/components/forms/FormSwitch.tsx +25 -0
  48. package/src/components/forms/FormTextarea.tsx +44 -0
  49. package/src/components/forms/MultiFileUploader.tsx +107 -0
  50. package/src/components/forms/PasswordInput.tsx +47 -0
  51. package/src/components/forms/index.ts +21 -0
  52. package/src/components/index.ts +11 -0
  53. package/src/components/navigations/Breadcrumb.tsx +83 -0
  54. package/src/components/navigations/ContentTitle.tsx +39 -0
  55. package/src/components/navigations/Header.tsx +27 -0
  56. package/src/components/navigations/ModeToggleSwitch.tsx +25 -0
  57. package/src/components/navigations/PageSection.tsx +64 -0
  58. package/src/components/navigations/RecentPagesNavigator.tsx +52 -0
  59. package/src/components/navigations/index.ts +6 -0
  60. package/src/components/pages/PageContainerContentDetails.tsx +76 -0
  61. package/src/components/pages/PageContentContainer.tsx +31 -0
  62. package/src/components/pages/index.ts +2 -0
  63. package/src/components/tables/ContentListTable.tsx +165 -0
  64. package/src/components/tables/ContentTableSearch.tsx +105 -0
  65. package/src/components/tables/cells/cell.component.tsx +18 -0
  66. package/src/components/tables/cells/cell.date.tsx +16 -0
  67. package/src/components/tables/cells/cell.id.tsx +27 -0
  68. package/src/components/tables/cells/cell.link.tsx +18 -0
  69. package/src/components/tables/cells/cell.text.tsx +12 -0
  70. package/src/components/tables/cells/cell.url.tsx +13 -0
  71. package/src/components/tables/cells/index.ts +5 -0
  72. package/src/components/tables/index.ts +3 -0
  73. package/src/contexts/SharedContext.tsx +35 -0
  74. package/src/contexts/index.ts +2 -0
  75. package/src/core/abstracts/AbstractApiData.ts +138 -0
  76. package/src/core/abstracts/AbstractService.ts +263 -0
  77. package/src/core/abstracts/index.ts +2 -0
  78. package/src/core/endpoint/EndpointCreator.ts +97 -0
  79. package/src/core/endpoint/index.ts +1 -0
  80. package/src/core/factories/JsonApiDataFactory.ts +12 -0
  81. package/src/core/factories/RehydrationFactory.ts +30 -0
  82. package/src/core/factories/index.ts +2 -0
  83. package/src/core/fields/FieldSelector.ts +15 -0
  84. package/src/core/fields/index.ts +1 -0
  85. package/src/core/index.ts +20 -0
  86. package/src/core/interfaces/ApiData.ts +8 -0
  87. package/src/core/interfaces/ApiDataInterface.ts +15 -0
  88. package/src/core/interfaces/ApiRequestDataTypeInterface.ts +14 -0
  89. package/src/core/interfaces/ApiResponseInterface.ts +17 -0
  90. package/src/core/interfaces/JsonApiHydratedDataInterface.ts +5 -0
  91. package/src/core/interfaces/index.ts +5 -0
  92. package/src/core/registry/DataClassRegistry.ts +51 -0
  93. package/src/core/registry/ModuleRegistrar.ts +43 -0
  94. package/src/core/registry/ModuleRegistry.ts +64 -0
  95. package/src/core/registry/index.ts +3 -0
  96. package/src/core/utils/index.ts +2 -0
  97. package/src/core/utils/rehydrate.ts +24 -0
  98. package/src/core/utils/translateResponse.ts +125 -0
  99. package/src/features/auth/auth.module.ts +9 -0
  100. package/src/features/auth/config.ts +57 -0
  101. package/src/features/auth/data/auth.interface.ts +31 -0
  102. package/src/features/auth/data/auth.service.ts +159 -0
  103. package/src/features/auth/data/auth.ts +54 -0
  104. package/src/features/auth/data/index.ts +3 -0
  105. package/src/features/auth/index.ts +3 -0
  106. package/src/features/company/company.module.ts +10 -0
  107. package/src/features/company/data/company.fields.ts +6 -0
  108. package/src/features/company/data/company.interface.ts +28 -0
  109. package/src/features/company/data/company.service.ts +73 -0
  110. package/src/features/company/data/company.ts +104 -0
  111. package/src/features/company/data/index.ts +4 -0
  112. package/src/features/company/index.ts +2 -0
  113. package/src/features/content/content.module.ts +20 -0
  114. package/src/features/content/data/content.fields.ts +13 -0
  115. package/src/features/content/data/content.interface.ts +23 -0
  116. package/src/features/content/data/content.service.ts +75 -0
  117. package/src/features/content/data/content.ts +85 -0
  118. package/src/features/content/data/index.ts +4 -0
  119. package/src/features/content/index.ts +2 -0
  120. package/src/features/feature/components/forms/FormFeatures.tsx +149 -0
  121. package/src/features/feature/components/index.ts +1 -0
  122. package/src/features/feature/data/feature.interface.ts +9 -0
  123. package/src/features/feature/data/feature.service.ts +19 -0
  124. package/src/features/feature/data/feature.ts +33 -0
  125. package/src/features/feature/data/index.ts +3 -0
  126. package/src/features/feature/feature.module.ts +10 -0
  127. package/src/features/feature/index.ts +3 -0
  128. package/src/features/index.ts +12 -0
  129. package/src/features/module/data/index.ts +2 -0
  130. package/src/features/module/data/module.interface.ts +12 -0
  131. package/src/features/module/data/module.ts +42 -0
  132. package/src/features/module/index.ts +2 -0
  133. package/src/features/module/module.module.ts +10 -0
  134. package/src/features/notification/data/index.ts +4 -0
  135. package/src/features/notification/data/notification.fields.ts +8 -0
  136. package/src/features/notification/data/notification.interface.ts +14 -0
  137. package/src/features/notification/data/notification.service.ts +34 -0
  138. package/src/features/notification/data/notification.ts +51 -0
  139. package/src/features/notification/index.ts +2 -0
  140. package/src/features/notification/notification.module.ts +10 -0
  141. package/src/features/push/data/index.ts +3 -0
  142. package/src/features/push/data/push.interface.ts +8 -0
  143. package/src/features/push/data/push.service.ts +17 -0
  144. package/src/features/push/data/push.ts +18 -0
  145. package/src/features/push/index.ts +2 -0
  146. package/src/features/push/push.module.ts +10 -0
  147. package/src/features/role/data/index.ts +4 -0
  148. package/src/features/role/data/role.fields.ts +8 -0
  149. package/src/features/role/data/role.interface.ts +16 -0
  150. package/src/features/role/data/role.service.ts +117 -0
  151. package/src/features/role/data/role.ts +62 -0
  152. package/src/features/role/index.ts +2 -0
  153. package/src/features/role/role.module.ts +10 -0
  154. package/src/features/s3/data/index.ts +3 -0
  155. package/src/features/s3/data/s3.interface.ts +11 -0
  156. package/src/features/s3/data/s3.service.ts +30 -0
  157. package/src/features/s3/data/s3.ts +60 -0
  158. package/src/features/s3/index.ts +2 -0
  159. package/src/features/s3/s3.module.ts +10 -0
  160. package/src/features/search/index.ts +1 -0
  161. package/src/features/search/interfaces/index.ts +1 -0
  162. package/src/features/search/interfaces/search.result.interface.ts +3 -0
  163. package/src/features/user/author.module.ts +10 -0
  164. package/src/features/user/components/index.ts +2 -0
  165. package/src/features/user/components/lists/ContributorsList.tsx +41 -0
  166. package/src/features/user/components/lists/index.ts +1 -0
  167. package/src/features/user/components/widgets/UserAvatar.tsx +86 -0
  168. package/src/features/user/components/widgets/index.ts +1 -0
  169. package/src/features/user/contexts/CurrentUserContext.tsx +156 -0
  170. package/src/features/user/contexts/index.ts +1 -0
  171. package/src/features/user/data/index.ts +4 -0
  172. package/src/features/user/data/user.fields.ts +8 -0
  173. package/src/features/user/data/user.interface.ts +41 -0
  174. package/src/features/user/data/user.service.ts +246 -0
  175. package/src/features/user/data/user.ts +162 -0
  176. package/src/features/user/index.ts +4 -0
  177. package/src/features/user/user.module.ts +21 -0
  178. package/src/hooks/TableGeneratorRegistry.ts +53 -0
  179. package/src/hooks/index.ts +33 -0
  180. package/src/hooks/types.ts +35 -0
  181. package/src/hooks/url.rewriter.ts +22 -0
  182. package/src/hooks/useCustomD3Graph.tsx +705 -0
  183. package/src/hooks/useDataListRetriever.ts +349 -0
  184. package/src/hooks/useDebounce.ts +33 -0
  185. package/src/hooks/usePageUrlGenerator.ts +50 -0
  186. package/src/hooks/useTableGenerator.ts +16 -0
  187. package/src/i18n/config.ts +73 -0
  188. package/src/i18n/index.ts +18 -0
  189. package/src/index.ts +16 -0
  190. package/src/interfaces/breadcrumb.item.data.interface.ts +4 -0
  191. package/src/interfaces/d3.link.interface.ts +7 -0
  192. package/src/interfaces/d3.node.interface.ts +12 -0
  193. package/src/interfaces/index.ts +3 -0
  194. package/src/permissions/check.ts +127 -0
  195. package/src/permissions/index.ts +2 -0
  196. package/src/permissions/types.ts +109 -0
  197. package/src/roles/config.ts +46 -0
  198. package/src/roles/index.ts +1 -0
  199. package/src/server/cache.ts +28 -0
  200. package/src/server/index.ts +3 -0
  201. package/src/server/request.ts +113 -0
  202. package/src/server/token.ts +10 -0
  203. package/src/shadcnui/custom/kanban.tsx +1001 -0
  204. package/src/shadcnui/custom/link.tsx +18 -0
  205. package/src/shadcnui/custom/multi-select.tsx +382 -0
  206. package/src/shadcnui/index.ts +49 -0
  207. package/src/shadcnui/ui/accordion.tsx +52 -0
  208. package/src/shadcnui/ui/alert-dialog.tsx +141 -0
  209. package/src/shadcnui/ui/alert.tsx +43 -0
  210. package/src/shadcnui/ui/avatar.tsx +50 -0
  211. package/src/shadcnui/ui/badge.tsx +40 -0
  212. package/src/shadcnui/ui/breadcrumb.tsx +115 -0
  213. package/src/shadcnui/ui/button.tsx +51 -0
  214. package/src/shadcnui/ui/calendar.tsx +73 -0
  215. package/src/shadcnui/ui/card.tsx +43 -0
  216. package/src/shadcnui/ui/carousel.tsx +225 -0
  217. package/src/shadcnui/ui/chart.tsx +320 -0
  218. package/src/shadcnui/ui/checkbox.tsx +29 -0
  219. package/src/shadcnui/ui/collapsible.tsx +11 -0
  220. package/src/shadcnui/ui/command.tsx +155 -0
  221. package/src/shadcnui/ui/context-menu.tsx +179 -0
  222. package/src/shadcnui/ui/dialog.tsx +96 -0
  223. package/src/shadcnui/ui/drawer.tsx +89 -0
  224. package/src/shadcnui/ui/dropdown-menu.tsx +205 -0
  225. package/src/shadcnui/ui/form.tsx +138 -0
  226. package/src/shadcnui/ui/hover-card.tsx +29 -0
  227. package/src/shadcnui/ui/input.tsx +21 -0
  228. package/src/shadcnui/ui/label.tsx +26 -0
  229. package/src/shadcnui/ui/navigation-menu.tsx +168 -0
  230. package/src/shadcnui/ui/popover.tsx +33 -0
  231. package/src/shadcnui/ui/progress.tsx +25 -0
  232. package/src/shadcnui/ui/radio-group.tsx +37 -0
  233. package/src/shadcnui/ui/resizable.tsx +47 -0
  234. package/src/shadcnui/ui/scroll-area.tsx +40 -0
  235. package/src/shadcnui/ui/select.tsx +164 -0
  236. package/src/shadcnui/ui/separator.tsx +28 -0
  237. package/src/shadcnui/ui/sheet.tsx +139 -0
  238. package/src/shadcnui/ui/sidebar.tsx +677 -0
  239. package/src/shadcnui/ui/skeleton.tsx +13 -0
  240. package/src/shadcnui/ui/slider.tsx +25 -0
  241. package/src/shadcnui/ui/sonner.tsx +25 -0
  242. package/src/shadcnui/ui/switch.tsx +31 -0
  243. package/src/shadcnui/ui/table.tsx +120 -0
  244. package/src/shadcnui/ui/tabs.tsx +55 -0
  245. package/src/shadcnui/ui/textarea.tsx +24 -0
  246. package/src/shadcnui/ui/toggle.tsx +39 -0
  247. package/src/shadcnui/ui/tooltip.tsx +61 -0
  248. package/src/unified/JsonApiRequest.ts +325 -0
  249. package/src/unified/index.ts +1 -0
  250. package/src/utils/blocknote-diff.util.ts +815 -0
  251. package/src/utils/blocknote-word-diff-renderer.util.ts +413 -0
  252. package/src/utils/cn.ts +6 -0
  253. package/src/utils/compose-refs.ts +61 -0
  254. package/src/utils/date-formatter.ts +53 -0
  255. package/src/utils/exists.ts +7 -0
  256. package/src/utils/index.ts +15 -0
  257. package/src/utils/schemas/entity.object.schema.ts +8 -0
  258. package/src/utils/schemas/index.ts +2 -0
  259. package/src/utils/schemas/user.object.schema.ts +9 -0
  260. package/src/utils/table-options.ts +67 -0
  261. package/src/utils/use-mobile.tsx +21 -0
@@ -0,0 +1,127 @@
1
+ import { Action, ModuleWithPermissions, PermissionModule, PermissionUser } from "./types";
2
+
3
+ /**
4
+ * Check if a user has permission to perform an action on a module.
5
+ *
6
+ * @param module - The module to check permissions for
7
+ * @param action - The action to check (read, create, update, delete)
8
+ * @param user - The user with their modules and permissions
9
+ * @param data - Optional data object for path-based permission checks
10
+ */
11
+ export function checkPermissions<T extends PermissionUser>(params: {
12
+ module: ModuleWithPermissions;
13
+ action: Action;
14
+ user: T;
15
+ data?: any;
16
+ }): boolean {
17
+ const selectedModule = params.user.modules.find((module: PermissionModule) => module.id === params.module.moduleId);
18
+
19
+ if (!selectedModule) return false;
20
+ const permissionConfig = selectedModule.permissions[params.action];
21
+
22
+ if (!permissionConfig) return false;
23
+ if (typeof permissionConfig === "boolean") return permissionConfig as boolean;
24
+
25
+ if (!params.data) return true;
26
+
27
+ try {
28
+ const singlePermissionConfig = permissionConfig.split("|").map((p) => p.trim());
29
+
30
+ for (const path of singlePermissionConfig) {
31
+ if (getValueFromPath(params.data, path, params.user.id)) return true;
32
+ }
33
+ return false;
34
+ } catch {
35
+ if (typeof permissionConfig === "string") {
36
+ return getValueFromPath(params.data, permissionConfig, params.user.id);
37
+ }
38
+ }
39
+
40
+ return false;
41
+ }
42
+
43
+ /**
44
+ * Check permissions from server context where user object is not fully available.
45
+ *
46
+ * @param module - The module to check permissions for
47
+ * @param action - The action to check
48
+ * @param userId - The user's ID
49
+ * @param selectedModule - The selected module with its permissions
50
+ * @param data - Optional data object for path-based permission checks
51
+ */
52
+ export function checkPermissionsFromServer(params: {
53
+ module: ModuleWithPermissions;
54
+ action: Action;
55
+ userId: string;
56
+ selectedModule?: PermissionModule;
57
+ data?: any;
58
+ }): boolean {
59
+ if (!params.selectedModule) return false;
60
+ const permissionConfig = params.selectedModule.permissions[params.action];
61
+
62
+ if (!permissionConfig) return false;
63
+ if (typeof permissionConfig === "boolean") return permissionConfig as boolean;
64
+
65
+ if (!params.data) return true;
66
+
67
+ try {
68
+ const singlePermissionConfig = permissionConfig.split("|").map((p) => p.trim());
69
+
70
+ for (const path of singlePermissionConfig) {
71
+ if (getValueFromPath(params.data, path, params.userId)) return true;
72
+ }
73
+ return false;
74
+ } catch {
75
+ if (typeof permissionConfig === "string") {
76
+ return getValueFromPath(params.data, permissionConfig, params.userId);
77
+ }
78
+ }
79
+
80
+ return false;
81
+ }
82
+
83
+ /**
84
+ * Traverse an object path and check if the value matches the user ID.
85
+ * Handles nested objects, arrays, and various data structures.
86
+ */
87
+ export function getValueFromPath(obj: any, path: string, userId: string): any {
88
+ const parts = path.split(".");
89
+ let current = obj;
90
+
91
+ for (const part of parts) {
92
+ if (!current) return false;
93
+
94
+ if (Array.isArray(current)) {
95
+ let found = false;
96
+ for (const item of current) {
97
+ const result = getValueFromPath(item, parts.slice(parts.indexOf(part)).join("."), userId);
98
+ if (result === userId || result === true) {
99
+ found = true;
100
+ break;
101
+ }
102
+ }
103
+ return found;
104
+ } else if (current[part] !== undefined) {
105
+ current = current[part];
106
+ } else {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ if (Array.isArray(current)) {
112
+ // If final value is an array, check if any element has id matching userId
113
+ return current.some((item: any) => {
114
+ if (typeof item === "object" && item.id !== undefined) {
115
+ return item.id.toString() === userId;
116
+ }
117
+ return item.toString() === userId;
118
+ });
119
+ }
120
+
121
+ // Direct comparison for primitive values or objects with id
122
+ if (typeof current === "object" && current.id !== undefined) {
123
+ return current.id.toString() === userId;
124
+ }
125
+
126
+ return current.toString() === userId;
127
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./types";
2
+ export * from "./check";
@@ -0,0 +1,109 @@
1
+ import { ApiRequestDataTypeInterface } from "../core/interfaces/ApiRequestDataTypeInterface";
2
+ import { FieldSelector } from "../core/fields/FieldSelector";
3
+
4
+ /**
5
+ * Permission actions
6
+ */
7
+ export enum Action {
8
+ Read = "read",
9
+ Create = "create",
10
+ Update = "update",
11
+ Delete = "delete",
12
+ }
13
+
14
+ /**
15
+ * Generic permission check type.
16
+ * Can be a boolean or a function that checks permissions dynamically.
17
+ * @template T - The data type being checked
18
+ * @template U - The user type (defaults to PermissionUser)
19
+ */
20
+ export type PermissionCheck<T, U = PermissionUser> = boolean | ((user?: U | string, data?: T) => boolean);
21
+
22
+ /**
23
+ * Page URL configuration for modules
24
+ */
25
+ export type PageUrl = {
26
+ pageUrl?: string;
27
+ };
28
+
29
+ /**
30
+ * Module permission definition wrapper
31
+ */
32
+ export type ModulePermissionDefinition<T> = {
33
+ interface: T;
34
+ };
35
+
36
+ /**
37
+ * Base module definition
38
+ */
39
+ export type ModuleDefinition = {
40
+ pageUrl?: string;
41
+ name: string;
42
+ model: any;
43
+ feature?: string;
44
+ moduleId?: string;
45
+ };
46
+
47
+ /**
48
+ * Permission configuration for a module.
49
+ * Can be a boolean (allow/deny all) or a string path for dynamic checks.
50
+ */
51
+ export interface PermissionConfig {
52
+ create: boolean | string;
53
+ read: boolean | string;
54
+ update: boolean | string;
55
+ delete: boolean | string;
56
+ }
57
+
58
+ /**
59
+ * Generic interface for a module that has permissions.
60
+ * Apps should ensure their Module class implements this.
61
+ */
62
+ export interface PermissionModule {
63
+ id: string;
64
+ permissions: PermissionConfig;
65
+ }
66
+
67
+ /**
68
+ * Generic interface for a user that has modules with permissions.
69
+ * Apps should ensure their User class implements this.
70
+ */
71
+ export interface PermissionUser {
72
+ id: string;
73
+ modules: PermissionModule[];
74
+ }
75
+
76
+ /**
77
+ * Module definition with permissions - extends ApiRequestDataTypeInterface
78
+ */
79
+ export type ModuleWithPermissions = ApiRequestDataTypeInterface & {
80
+ pageUrl?: string;
81
+ feature?: string;
82
+ moduleId?: string;
83
+ inclusions?: Record<
84
+ string,
85
+ {
86
+ types?: string[];
87
+ fields?: FieldSelector<any>[];
88
+ }
89
+ >;
90
+ };
91
+
92
+ /**
93
+ * Factory type for creating module definitions
94
+ */
95
+ export type ModuleFactory = (params: {
96
+ pageUrl?: string;
97
+ name: string;
98
+ cache?: string | "days" | "default" | "hours" | "max" | "minutes" | "seconds" | "weeks";
99
+ model: any;
100
+ feature?: string;
101
+ moduleId?: string;
102
+ inclusions?: Record<
103
+ string,
104
+ {
105
+ types?: string[];
106
+ fields?: FieldSelector<any>[];
107
+ }
108
+ >;
109
+ }) => ModuleWithPermissions;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Role ID configuration interface
3
+ * Apps provide their role IDs via configureRoles()
4
+ */
5
+ export interface RoleIdConfig {
6
+ Administrator: string;
7
+ CompanyAdministrator: string;
8
+ [key: string]: string; // Allow additional roles
9
+ }
10
+
11
+ // Private storage for the injected role IDs
12
+ let _roleId: RoleIdConfig | null = null;
13
+
14
+ /**
15
+ * Configure role IDs for the library
16
+ * Call this at app startup to provide role ID constants
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { configureRoles } from "@carlonicora/nextjs-jsonapi";
21
+ * import { RoleId } from "@phlow/shared";
22
+ *
23
+ * configureRoles(RoleId);
24
+ * ```
25
+ */
26
+ export function configureRoles(roleId: RoleIdConfig): void {
27
+ _roleId = roleId;
28
+ }
29
+
30
+ /**
31
+ * Get configured role IDs
32
+ * @throws Error if roles not configured
33
+ */
34
+ export function getRoleId(): RoleIdConfig {
35
+ if (!_roleId) {
36
+ throw new Error("Roles not configured. Call configureRoles() at app startup.");
37
+ }
38
+ return _roleId;
39
+ }
40
+
41
+ /**
42
+ * Check if roles have been configured
43
+ */
44
+ export function isRolesConfigured(): boolean {
45
+ return _roleId !== null;
46
+ }
@@ -0,0 +1 @@
1
+ export { configureRoles, getRoleId, isRolesConfigured, type RoleIdConfig } from "./config";
@@ -0,0 +1,28 @@
1
+ "use server";
2
+
3
+ import { revalidateTag } from "next/cache";
4
+
5
+ export type CacheProfile = "seconds" | "minutes" | "hours" | "days" | "weeks" | "max" | "default";
6
+
7
+ /**
8
+ * Revalidate a cache tag to invalidate cached data.
9
+ * Next.js 16+ requires a profile parameter.
10
+ *
11
+ * @param tag - The cache tag to invalidate
12
+ * @param profile - The cache profile (defaults to "max" for immediate invalidation)
13
+ */
14
+ export async function invalidateCacheTag(tag: string, profile: CacheProfile = "max"): Promise<void> {
15
+ revalidateTag(tag, profile);
16
+ }
17
+
18
+ /**
19
+ * Revalidate multiple cache tags.
20
+ *
21
+ * @param tags - Array of cache tags to invalidate
22
+ * @param profile - The cache profile (defaults to "max" for immediate invalidation)
23
+ */
24
+ export async function invalidateCacheTags(tags: string[], profile: CacheProfile = "max"): Promise<void> {
25
+ for (const tag of tags) {
26
+ revalidateTag(tag, profile);
27
+ }
28
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./request";
2
+ export * from "./token";
3
+ export * from "./cache";
@@ -0,0 +1,113 @@
1
+ "use server";
2
+
3
+ import { ApiData } from "../core/interfaces/ApiData";
4
+ import { cacheLife } from "next/dist/server/use-cache/cache-life";
5
+ import { cacheTag } from "next/dist/server/use-cache/cache-tag";
6
+
7
+ export interface ServerRequestParams {
8
+ method: string;
9
+ url: string;
10
+ token?: string;
11
+ cache?: string;
12
+ body?: any;
13
+ files?: { [key: string]: File | Blob } | File | Blob;
14
+ companyId?: string;
15
+ language: string;
16
+ additionalHeaders?: Record<string, string>;
17
+ }
18
+
19
+ /**
20
+ * Server-side request with Next.js caching support.
21
+ * Uses "use cache" directive for automatic caching.
22
+ */
23
+ export async function serverRequest(params: ServerRequestParams): Promise<ApiData> {
24
+ "use cache";
25
+
26
+ const response: ApiData = {
27
+ data: undefined,
28
+ ok: false,
29
+ status: 0,
30
+ statusText: "",
31
+ };
32
+
33
+ // Apply caching configuration
34
+ if (params.cache) {
35
+ if (["days", "default", "hours", "max", "minutes", "seconds", "weeks"].includes(params.cache)) {
36
+ cacheLife(params.cache as any);
37
+ } else {
38
+ cacheTag(params.cache);
39
+ }
40
+ } else {
41
+ cacheLife("seconds");
42
+ }
43
+
44
+ const additionalHeaders: Record<string, string> = { ...params.additionalHeaders };
45
+
46
+ if (params.companyId) {
47
+ additionalHeaders["x-companyid"] = params.companyId;
48
+ }
49
+ additionalHeaders["x-language"] = params.language;
50
+
51
+ let requestBody: BodyInit | undefined = undefined;
52
+
53
+ if (params.files) {
54
+ const formData = new FormData();
55
+ if (params.body && typeof params.body === "object") {
56
+ for (const key in params.body) {
57
+ if (Object.prototype.hasOwnProperty.call(params.body, key)) {
58
+ formData.append(
59
+ key,
60
+ typeof params.body[key] === "object" ? JSON.stringify(params.body[key]) : params.body[key],
61
+ );
62
+ }
63
+ }
64
+ }
65
+
66
+ if (params.files instanceof Blob) {
67
+ formData.append("file", params.files);
68
+ } else if (typeof params.files === "object" && params.files !== null) {
69
+ for (const key in params.files) {
70
+ if (Object.prototype.hasOwnProperty.call(params.files, key)) {
71
+ formData.append(key, params.files[key]);
72
+ }
73
+ }
74
+ }
75
+
76
+ requestBody = formData;
77
+ } else if (params.body !== undefined) {
78
+ requestBody = JSON.stringify(params.body);
79
+ additionalHeaders["Content-Type"] = "application/json";
80
+ }
81
+
82
+ const options: RequestInit = {
83
+ method: params.method,
84
+ headers: { Accept: "application/json", ...additionalHeaders },
85
+ };
86
+
87
+ if (requestBody !== undefined) {
88
+ options.body = requestBody;
89
+ }
90
+
91
+ if (params.token) {
92
+ options.headers = { ...options.headers, Authorization: `Bearer ${params.token}` };
93
+ }
94
+
95
+ try {
96
+ const apiResponse = await fetch(params.url, options);
97
+
98
+ response.ok = apiResponse.ok;
99
+ response.status = apiResponse.status;
100
+ response.statusText = apiResponse.statusText;
101
+ try {
102
+ response.data = await apiResponse.json();
103
+ } catch {
104
+ response.data = undefined;
105
+ }
106
+ } catch {
107
+ response.ok = false;
108
+ response.status = 500;
109
+ response.data = undefined;
110
+ }
111
+
112
+ return response;
113
+ }
@@ -0,0 +1,10 @@
1
+ "use server";
2
+
3
+ /**
4
+ * Get the authentication token from cookies (server-side)
5
+ */
6
+ export async function getServerToken(): Promise<string | undefined> {
7
+ const { cookies } = await import("next/headers");
8
+ const cookieStore = await cookies();
9
+ return cookieStore.get("token")?.value;
10
+ }