@donotdev/crud 0.0.6 → 0.0.7

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 (304) hide show
  1. package/README.md +18 -2
  2. package/dist/CrudService.d.ts +91 -8
  3. package/dist/CrudService.d.ts.map +1 -1
  4. package/dist/CrudService.js +1 -1
  5. package/dist/CrudStore.d.ts +45 -2
  6. package/dist/CrudStore.d.ts.map +1 -1
  7. package/dist/CrudStore.js +1 -1
  8. package/dist/FieldRegistry.d.ts +56 -20
  9. package/dist/FieldRegistry.d.ts.map +1 -1
  10. package/dist/FieldRegistry.js +1 -1
  11. package/dist/adapters/FirestoreAdapter.d.ts +4 -4
  12. package/dist/adapters/FirestoreAdapter.d.ts.map +1 -1
  13. package/dist/adapters/FirestoreAdapter.js +1 -1
  14. package/dist/adapters/FunctionsAdapter.d.ts +3 -2
  15. package/dist/adapters/FunctionsAdapter.d.ts.map +1 -1
  16. package/dist/adapters/FunctionsAdapter.js +1 -1
  17. package/dist/builtinFieldTypes.d.ts +4 -0
  18. package/dist/builtinFieldTypes.d.ts.map +1 -1
  19. package/dist/builtinFieldTypes.js +1 -1
  20. package/dist/components/CrudButton.d.ts +1 -1
  21. package/dist/components/CrudButton.d.ts.map +1 -1
  22. package/dist/components/CrudButton.js +1 -1
  23. package/dist/components/DisplayFieldRenderer.d.ts +15 -11
  24. package/dist/components/DisplayFieldRenderer.d.ts.map +1 -1
  25. package/dist/components/DisplayFieldRenderer.js +1 -1
  26. package/dist/components/EntityCardList.d.ts +27 -0
  27. package/dist/components/EntityCardList.d.ts.map +1 -0
  28. package/dist/components/EntityCardList.js +1 -0
  29. package/dist/components/EntityDisplayRenderer.d.ts +43 -0
  30. package/dist/components/EntityDisplayRenderer.d.ts.map +1 -0
  31. package/dist/components/EntityDisplayRenderer.js +1 -0
  32. package/dist/components/EntityFormRenderer.d.ts +21 -26
  33. package/dist/components/EntityFormRenderer.d.ts.map +1 -1
  34. package/dist/components/EntityFormRenderer.js +1 -5
  35. package/dist/components/EntityList.d.ts +24 -0
  36. package/dist/components/EntityList.d.ts.map +1 -0
  37. package/dist/components/EntityList.js +1 -0
  38. package/dist/components/FormFieldRenderer.d.ts +3 -10
  39. package/dist/components/FormFieldRenderer.d.ts.map +1 -1
  40. package/dist/components/FormFieldRenderer.js +1 -1
  41. package/dist/components/FormLayout.d.ts +1 -9
  42. package/dist/components/FormLayout.d.ts.map +1 -1
  43. package/dist/components/controlled/complex/ControlledAddressField.d.ts +8 -0
  44. package/dist/components/controlled/complex/ControlledAddressField.d.ts.map +1 -0
  45. package/dist/components/controlled/complex/ControlledAddressField.js +1 -0
  46. package/dist/components/controlled/complex/ControlledDateField.d.ts +8 -0
  47. package/dist/components/controlled/complex/ControlledDateField.d.ts.map +1 -0
  48. package/dist/components/controlled/complex/ControlledDateField.js +1 -0
  49. package/dist/components/controlled/complex/ControlledGeoPointField.d.ts +8 -0
  50. package/dist/components/controlled/complex/ControlledGeoPointField.d.ts.map +1 -0
  51. package/dist/components/controlled/complex/ControlledGeoPointField.js +1 -0
  52. package/dist/components/controlled/complex/ControlledMapField.d.ts +8 -0
  53. package/dist/components/controlled/complex/ControlledMapField.d.ts.map +1 -0
  54. package/dist/components/controlled/complex/ControlledMapField.js +1 -0
  55. package/dist/components/controlled/complex/ControlledMultiInputField.d.ts +8 -0
  56. package/dist/components/controlled/complex/ControlledMultiInputField.d.ts.map +1 -0
  57. package/dist/components/controlled/complex/ControlledMultiInputField.js +1 -0
  58. package/dist/components/controlled/complex/ControlledRichTextField.d.ts +8 -0
  59. package/dist/components/controlled/complex/ControlledRichTextField.d.ts.map +1 -0
  60. package/dist/components/controlled/complex/ControlledRichTextField.js +1 -0
  61. package/dist/components/controlled/complex/ControlledTimestampField.d.ts +8 -0
  62. package/dist/components/controlled/complex/ControlledTimestampField.d.ts.map +1 -0
  63. package/dist/components/controlled/complex/ControlledTimestampField.js +1 -0
  64. package/dist/components/controlled/complex/index.d.ts +8 -0
  65. package/dist/components/controlled/complex/index.d.ts.map +1 -0
  66. package/dist/components/controlled/complex/index.js +1 -0
  67. package/dist/components/controlled/file/ControlledDocumentField.d.ts +8 -0
  68. package/dist/components/controlled/file/ControlledDocumentField.d.ts.map +1 -0
  69. package/dist/components/controlled/file/ControlledDocumentField.js +1 -0
  70. package/dist/components/controlled/file/ControlledFileField.d.ts +8 -0
  71. package/dist/components/controlled/file/ControlledFileField.d.ts.map +1 -0
  72. package/dist/components/controlled/file/ControlledFileField.js +1 -0
  73. package/dist/components/controlled/file/ControlledImageField.d.ts +8 -0
  74. package/dist/components/controlled/file/ControlledImageField.d.ts.map +1 -0
  75. package/dist/components/controlled/file/ControlledImageField.js +1 -0
  76. package/dist/components/controlled/file/ControlledMultiDocumentField.d.ts +8 -0
  77. package/dist/components/controlled/file/ControlledMultiDocumentField.d.ts.map +1 -0
  78. package/dist/components/controlled/file/ControlledMultiDocumentField.js +1 -0
  79. package/dist/components/controlled/file/ControlledMultiFileField.d.ts +8 -0
  80. package/dist/components/controlled/file/ControlledMultiFileField.d.ts.map +1 -0
  81. package/dist/components/controlled/file/ControlledMultiFileField.js +1 -0
  82. package/dist/components/controlled/file/ControlledMultiImageField.d.ts +8 -0
  83. package/dist/components/controlled/file/ControlledMultiImageField.d.ts.map +1 -0
  84. package/dist/components/controlled/file/ControlledMultiImageField.js +1 -0
  85. package/dist/components/controlled/file/index.d.ts +7 -0
  86. package/dist/components/controlled/file/index.d.ts.map +1 -0
  87. package/dist/components/controlled/file/index.js +1 -0
  88. package/dist/components/controlled/index.d.ts +12 -0
  89. package/dist/components/controlled/index.d.ts.map +1 -0
  90. package/dist/components/controlled/index.js +1 -0
  91. package/dist/components/controlled/input/ControlledCheckboxField.d.ts +8 -0
  92. package/dist/components/controlled/input/ControlledCheckboxField.d.ts.map +1 -0
  93. package/dist/components/controlled/input/ControlledCheckboxField.js +1 -0
  94. package/dist/components/controlled/input/ControlledNumberField.d.ts +8 -0
  95. package/dist/components/controlled/input/ControlledNumberField.d.ts.map +1 -0
  96. package/dist/components/controlled/input/ControlledNumberField.js +1 -0
  97. package/dist/components/controlled/input/ControlledPasswordField.d.ts +8 -0
  98. package/dist/components/controlled/input/ControlledPasswordField.d.ts.map +1 -0
  99. package/dist/components/controlled/input/ControlledPasswordField.js +1 -0
  100. package/dist/components/controlled/input/ControlledPhoneField.d.ts +8 -0
  101. package/dist/components/controlled/input/ControlledPhoneField.d.ts.map +1 -0
  102. package/dist/components/controlled/input/ControlledPhoneField.js +1 -0
  103. package/dist/components/controlled/input/ControlledRangeField.d.ts +8 -0
  104. package/dist/components/controlled/input/ControlledRangeField.d.ts.map +1 -0
  105. package/dist/components/controlled/input/ControlledRangeField.js +1 -0
  106. package/dist/components/controlled/input/ControlledSwitchField.d.ts +8 -0
  107. package/dist/components/controlled/input/ControlledSwitchField.d.ts.map +1 -0
  108. package/dist/components/controlled/input/ControlledSwitchField.js +1 -0
  109. package/dist/components/controlled/input/ControlledTextField.d.ts +8 -0
  110. package/dist/components/controlled/input/ControlledTextField.d.ts.map +1 -0
  111. package/dist/components/controlled/input/ControlledTextField.js +1 -0
  112. package/dist/components/controlled/input/ControlledTextareaField.d.ts +8 -0
  113. package/dist/components/controlled/input/ControlledTextareaField.d.ts.map +1 -0
  114. package/dist/components/controlled/input/ControlledTextareaField.js +1 -0
  115. package/dist/components/controlled/input/index.d.ts +9 -0
  116. package/dist/components/controlled/input/index.d.ts.map +1 -0
  117. package/dist/components/controlled/input/index.js +1 -0
  118. package/dist/components/controlled/select/ControlledComboboxField.d.ts +9 -0
  119. package/dist/components/controlled/select/ControlledComboboxField.d.ts.map +1 -0
  120. package/dist/components/controlled/select/ControlledComboboxField.js +1 -0
  121. package/dist/components/controlled/select/ControlledDropdownField.d.ts +9 -0
  122. package/dist/components/controlled/select/ControlledDropdownField.d.ts.map +1 -0
  123. package/dist/components/controlled/select/ControlledDropdownField.js +1 -0
  124. package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts +9 -0
  125. package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts.map +1 -0
  126. package/dist/components/controlled/select/ControlledMultiDropdownField.js +1 -0
  127. package/dist/components/controlled/select/ControlledRadioField.d.ts +9 -0
  128. package/dist/components/controlled/select/ControlledRadioField.d.ts.map +1 -0
  129. package/dist/components/controlled/select/ControlledRadioField.js +1 -0
  130. package/dist/components/controlled/select/index.d.ts +5 -0
  131. package/dist/components/controlled/select/index.d.ts.map +1 -0
  132. package/dist/components/controlled/select/index.js +1 -0
  133. package/dist/components/controlled/types.d.ts +23 -0
  134. package/dist/components/controlled/types.d.ts.map +1 -0
  135. package/dist/components/controlled/types.js +1 -0
  136. package/dist/components/form/fields/AddressFieldComponent.d.ts.map +1 -1
  137. package/dist/components/form/fields/AddressFieldComponent.js +1 -1
  138. package/dist/components/form/fields/AvatarFieldComponent.d.ts +2 -2
  139. package/dist/components/form/fields/BadgeFieldComponent.d.ts +2 -2
  140. package/dist/components/form/fields/ButtonFieldComponent.d.ts +1 -9
  141. package/dist/components/form/fields/ButtonFieldComponent.d.ts.map +1 -1
  142. package/dist/components/form/fields/ButtonFieldComponent.js +1 -1
  143. package/dist/components/form/fields/ComboboxComponent.d.ts.map +1 -1
  144. package/dist/components/form/fields/CurrencyFieldComponent.d.ts +1 -9
  145. package/dist/components/form/fields/CurrencyFieldComponent.d.ts.map +1 -1
  146. package/dist/components/form/fields/CurrencyFieldComponent.js +1 -1
  147. package/dist/components/form/fields/DateFieldComponent.d.ts.map +1 -1
  148. package/dist/components/form/fields/DateFieldComponent.js +1 -1
  149. package/dist/components/form/fields/DocumentFieldComponent.d.ts +47 -0
  150. package/dist/components/form/fields/DocumentFieldComponent.d.ts.map +1 -0
  151. package/dist/components/form/fields/DocumentFieldComponent.js +1 -0
  152. package/dist/components/form/fields/DropdownComponent.d.ts.map +1 -1
  153. package/dist/components/form/fields/DropdownComponent.js +1 -1
  154. package/dist/components/form/fields/FileFieldComponent.d.ts +31 -15
  155. package/dist/components/form/fields/FileFieldComponent.d.ts.map +1 -1
  156. package/dist/components/form/fields/FileFieldComponent.js +1 -1
  157. package/dist/components/form/fields/GeoPointFieldComponent.d.ts.map +1 -1
  158. package/dist/components/form/fields/GeoPointFieldComponent.js +1 -1
  159. package/dist/components/form/fields/HiddenFieldComponent.d.ts +1 -1
  160. package/dist/components/form/fields/HiddenFieldComponent.d.ts.map +1 -1
  161. package/dist/components/form/fields/HiddenFieldComponent.js +1 -1
  162. package/dist/components/form/fields/ImageFieldComponent.d.ts +8 -14
  163. package/dist/components/form/fields/ImageFieldComponent.d.ts.map +1 -1
  164. package/dist/components/form/fields/ImageFieldComponent.js +1 -1
  165. package/dist/components/form/fields/MapFieldComponent.d.ts.map +1 -1
  166. package/dist/components/form/fields/MapFieldComponent.js +1 -1
  167. package/dist/components/form/fields/MultiDropdownComponent.js +1 -1
  168. package/dist/components/form/fields/MultiInputTextFieldComponent.d.ts +1 -9
  169. package/dist/components/form/fields/MultiInputTextFieldComponent.d.ts.map +1 -1
  170. package/dist/components/form/fields/MultiInputTextFieldComponent.js +1 -1
  171. package/dist/components/form/fields/NumberFieldComponent.d.ts +2 -0
  172. package/dist/components/form/fields/NumberFieldComponent.d.ts.map +1 -1
  173. package/dist/components/form/fields/NumberFieldComponent.js +1 -1
  174. package/dist/components/form/fields/PasswordFieldComponent.d.ts +1 -9
  175. package/dist/components/form/fields/PasswordFieldComponent.d.ts.map +1 -1
  176. package/dist/components/form/fields/PhoneNumberComponent.d.ts +1 -9
  177. package/dist/components/form/fields/PhoneNumberComponent.d.ts.map +1 -1
  178. package/dist/components/form/fields/PhoneNumberComponent.js +1 -1
  179. package/dist/components/form/fields/RadioFieldComponent.d.ts +1 -9
  180. package/dist/components/form/fields/RadioFieldComponent.d.ts.map +1 -1
  181. package/dist/components/form/fields/ReferenceFieldComponent.d.ts +33 -12
  182. package/dist/components/form/fields/ReferenceFieldComponent.d.ts.map +1 -1
  183. package/dist/components/form/fields/ReferenceFieldComponent.js +1 -1
  184. package/dist/components/form/fields/RichTextComponent.d.ts +32 -0
  185. package/dist/components/form/fields/RichTextComponent.d.ts.map +1 -0
  186. package/dist/components/form/fields/RichTextComponent.js +1 -0
  187. package/dist/components/form/fields/SwitchFieldComponent.d.ts.map +1 -1
  188. package/dist/components/form/fields/TextAreaComponent.d.ts +1 -9
  189. package/dist/components/form/fields/TextAreaComponent.d.ts.map +1 -1
  190. package/dist/components/form/fields/TextAreaComponent.js +1 -1
  191. package/dist/components/form/fields/TextFieldComponent.d.ts +1 -9
  192. package/dist/components/form/fields/TextFieldComponent.d.ts.map +1 -1
  193. package/dist/components/form/fields/index.d.ts +4 -2
  194. package/dist/components/form/fields/index.d.ts.map +1 -1
  195. package/dist/components/form/fields/index.js +1 -1
  196. package/dist/components/form/fields/internal/TiptapEditor.d.ts +13 -0
  197. package/dist/components/form/fields/internal/TiptapEditor.d.ts.map +1 -0
  198. package/dist/components/form/fields/internal/TiptapEditor.js +52 -0
  199. package/dist/components/form/index.d.ts +10 -0
  200. package/dist/components/form/index.d.ts.map +1 -0
  201. package/dist/components/form/index.js +1 -0
  202. package/dist/components/form/internal/ImageViewerDialog.d.ts +1 -1
  203. package/dist/components/form/internal/ImageViewerDialog.d.ts.map +1 -1
  204. package/dist/components/index.d.ts +8 -2
  205. package/dist/components/index.d.ts.map +1 -1
  206. package/dist/components/index.js +1 -1
  207. package/dist/contexts/UploadContext.d.ts +16 -0
  208. package/dist/contexts/UploadContext.d.ts.map +1 -0
  209. package/dist/contexts/UploadContext.js +1 -0
  210. package/dist/contexts/index.d.ts +3 -0
  211. package/dist/contexts/index.d.ts.map +1 -0
  212. package/dist/contexts/index.js +1 -0
  213. package/dist/forms/hooks/index.d.ts +2 -0
  214. package/dist/forms/hooks/index.d.ts.map +1 -1
  215. package/dist/forms/hooks/index.js +1 -1
  216. package/dist/forms/hooks/useController.d.ts +29 -0
  217. package/dist/forms/hooks/useController.d.ts.map +1 -0
  218. package/dist/forms/hooks/useController.js +1 -0
  219. package/dist/forms/hooks/useEntityField.d.ts.map +1 -1
  220. package/dist/forms/hooks/useEntityField.js +1 -1
  221. package/dist/forms/hooks/useEntityForm.d.ts +8 -76
  222. package/dist/forms/hooks/useEntityForm.d.ts.map +1 -1
  223. package/dist/forms/hooks/useEntityForm.js +1 -1
  224. package/dist/forms/index.d.ts +6 -4
  225. package/dist/forms/index.d.ts.map +1 -1
  226. package/dist/forms/index.js +1 -1
  227. package/dist/forms/types.d.ts +31 -5
  228. package/dist/forms/types.d.ts.map +1 -1
  229. package/dist/forms/utils/getFieldsForOperation.d.ts +5 -5
  230. package/dist/forms/utils/getFieldsForOperation.d.ts.map +1 -1
  231. package/dist/forms/utils/getFieldsForOperation.js +1 -1
  232. package/dist/forms/utils/index.d.ts +9 -5
  233. package/dist/forms/utils/index.d.ts.map +1 -1
  234. package/dist/forms/utils/index.js +1 -1
  235. package/dist/forms/utils/isFieldEditable.d.ts +0 -8
  236. package/dist/forms/utils/isFieldEditable.d.ts.map +1 -1
  237. package/dist/forms/utils/optionHelpers.d.ts +54 -0
  238. package/dist/forms/utils/optionHelpers.d.ts.map +1 -0
  239. package/dist/forms/utils/optionHelpers.js +1 -0
  240. package/dist/forms/utils/translateFieldLabel.d.ts +70 -0
  241. package/dist/forms/utils/translateFieldLabel.d.ts.map +1 -0
  242. package/dist/forms/utils/translateFieldLabel.js +1 -0
  243. package/dist/forms/utils/validateEntity.d.ts +5 -2
  244. package/dist/forms/utils/validateEntity.d.ts.map +1 -1
  245. package/dist/forms/utils/validateEntity.js +1 -1
  246. package/dist/hooks/index.d.ts +3 -0
  247. package/dist/hooks/index.d.ts.map +1 -0
  248. package/dist/hooks/index.js +1 -0
  249. package/dist/hooks/useFileUpload.d.ts +67 -0
  250. package/dist/hooks/useFileUpload.d.ts.map +1 -0
  251. package/dist/hooks/useFileUpload.js +1 -0
  252. package/dist/index.d.ts +15 -5
  253. package/dist/index.d.ts.map +1 -1
  254. package/dist/index.js +1 -1
  255. package/dist/stores/FormStore.d.ts +78 -0
  256. package/dist/stores/FormStore.d.ts.map +1 -0
  257. package/dist/stores/FormStore.js +1 -0
  258. package/dist/stores/UploadStore.d.ts +105 -0
  259. package/dist/stores/UploadStore.d.ts.map +1 -0
  260. package/dist/stores/UploadStore.js +1 -0
  261. package/dist/stores/index.d.ts +11 -0
  262. package/dist/stores/index.d.ts.map +1 -0
  263. package/dist/stores/index.js +1 -0
  264. package/dist/tsconfig.tsbuildinfo +1 -1
  265. package/dist/useCrud.d.ts +32 -74
  266. package/dist/useCrud.d.ts.map +1 -1
  267. package/dist/useCrud.js +1 -1
  268. package/dist/useCrudCardList.d.ts +62 -0
  269. package/dist/useCrudCardList.d.ts.map +1 -0
  270. package/dist/useCrudCardList.js +1 -0
  271. package/dist/useCrudList.d.ts +61 -0
  272. package/dist/useCrudList.d.ts.map +1 -0
  273. package/dist/useCrudList.js +1 -0
  274. package/dist/utils/fileStorage.d.ts +58 -0
  275. package/dist/utils/fileStorage.d.ts.map +1 -0
  276. package/dist/utils/fileStorage.js +1 -0
  277. package/dist/utils/imageProcessing.d.ts +1 -1
  278. package/dist/utils/imageProcessing.d.ts.map +1 -1
  279. package/dist/utils/imageProcessing.js +1 -1
  280. package/dist/utils/imageStorage.d.ts +1 -10
  281. package/dist/utils/imageStorage.d.ts.map +1 -1
  282. package/dist/utils/imageStorage.js +1 -1
  283. package/dist/utils/mergeWithOptimistic.d.ts +27 -0
  284. package/dist/utils/mergeWithOptimistic.d.ts.map +1 -0
  285. package/dist/utils/mergeWithOptimistic.js +1 -0
  286. package/dist/utils/uploadValidation.d.ts +37 -0
  287. package/dist/utils/uploadValidation.d.ts.map +1 -0
  288. package/dist/utils/uploadValidation.js +1 -0
  289. package/package.json +22 -5
  290. package/dist/components/ControlledFields.d.ts +0 -49
  291. package/dist/components/ControlledFields.d.ts.map +0 -1
  292. package/dist/components/ControlledFields.js +0 -1
  293. package/dist/context/FormUploadContext.d.ts +0 -36
  294. package/dist/context/FormUploadContext.d.ts.map +0 -1
  295. package/dist/context/FormUploadContext.js +0 -1
  296. package/dist/context/index.d.ts +0 -2
  297. package/dist/context/index.d.ts.map +0 -1
  298. package/dist/context/index.js +0 -1
  299. package/dist/forms/utils/createEntitySchema.d.ts +0 -53
  300. package/dist/forms/utils/createEntitySchema.d.ts.map +0 -1
  301. package/dist/forms/utils/createEntitySchema.js +0 -1
  302. package/dist/forms/utils/normalizeToFieldConfig.d.ts +0 -47
  303. package/dist/forms/utils/normalizeToFieldConfig.d.ts.map +0 -1
  304. package/dist/forms/utils/normalizeToFieldConfig.js +0 -1
package/dist/useCrud.d.ts CHANGED
@@ -1,94 +1,52 @@
1
- import type { dndevSchema, Entity, FeatureStatus } from '@donotdev/core';
2
- import { type BackendType } from './CrudStore';
3
- import { type QueryOptions } from './CrudService';
1
+ import type { dndevSchema, Entity, FeatureStatus, OperationSchemas, CrudAPI } from '@donotdev/core';
2
+ import type { CrudServiceInterface, CacheOptions } from './CrudService';
3
+ import type { BackendType } from './CrudStore';
4
+ export declare const EMPTY_DATA: Record<string, unknown>;
5
+ export declare const EMPTY_OPTIMISTIC: Record<string, unknown>;
6
+ /**
7
+ * Get the current CrudService instance (for use in callbacks)
8
+ */
9
+ export declare function getCrudServiceInstance(): CrudServiceInterface | null;
4
10
  export interface UseCrudOptions<T> {
5
11
  backend?: BackendType;
6
12
  schema?: dndevSchema<T>;
7
13
  /** Entity definition - auto-generates schema if schema not provided */
8
14
  entity?: Entity;
9
- /** TanStack Query stale time in ms (default: 5 min) */
15
+ /** TanStack Query stale time in ms (default: Infinity) */
10
16
  staleTime?: number;
11
17
  /** Disable caching (bypass TanStack Query) */
12
18
  noCache?: boolean;
13
19
  }
14
20
  /**
15
- * CRUD API return type - complete interface for useCrud hook
16
- * Consistent with CrudAPI from @donotdev/types
21
+ * CRUD API return type - Single Document & Actions
22
+ */
23
+ /**
24
+ * CRUD API return type - Single Document & Actions
17
25
  */
18
- export interface UseCrudReturn<T> {
19
- /** Feature status: 'initializing' | 'ready' | 'degraded' | 'error' */
20
- status: FeatureStatus;
21
- /** Current data (from get/subscribe) */
22
- data: T | null;
23
- /** Loading state for async operations */
24
- loading: boolean;
25
- /** Last error encountered */
26
- error: Error | null;
27
- /** Fetch single document by ID */
28
- get: (id: string) => Promise<T | null>;
29
- /** Set/replace document by ID */
30
- set: (id: string, data: T) => Promise<void>;
31
- /** Partial update document by ID */
32
- update: (id: string, data: Partial<T>) => Promise<void>;
33
- /** Delete document by ID */
34
- delete: (id: string) => Promise<void>;
35
- /** Add new document (auto-generated ID) */
36
- add: (data: T) => Promise<string>;
37
- /** Query collection with filters */
38
- query: (options: QueryOptions) => Promise<T[]>;
39
- /** Subscribe to document changes */
40
- subscribe: (id: string, callback: (data: T | null, error?: Error) => void) => () => void;
41
- /** Subscribe to collection changes */
42
- subscribeToCollection: (options: QueryOptions, callback: (data: T[], error?: Error) => void) => () => void;
43
- /** Invalidate cache for this collection (TanStack Query) */
44
- invalidate: () => Promise<void>;
45
- /** Whether CRUD is available and operational (status === 'ready') */
46
- isAvailable: boolean;
26
+ export interface UseCrudReturn<T> extends CrudAPI<T> {
27
+ /** @internal Collection name */
28
+ _collection: string;
29
+ /** @internal Generated schemas */
30
+ _schemas: OperationSchemas | undefined;
31
+ /** @internal Cache options */
32
+ _cacheOptions: CacheOptions;
33
+ /** @internal Store data for collection */
34
+ _storeData: Record<string, unknown>;
47
35
  }
48
36
  /**
49
- * React hook for CRUD operations with lazy-loaded TanStack Query caching
50
- *
51
- * Collection-scoped hook that returns full CRUD API for a specific collection.
52
- * Uses unified FeatureStatus enum for lifecycle management.
53
- *
54
- * **Usage:**
55
- * ```typescript
56
- * // With entity (auto-generates schema, derives collection, defaults to 'firestore' backend)
57
- * const { status, data, get, set, query } = useCrud(carEntity);
37
+ * React hook for CRUD Actions & Single Documents
58
38
  *
59
- * // With entity and explicit backend
60
- * const { status, data, get, set, query } = useCrud(carEntity, { backend: 'functions' });
61
- *
62
- * // With collection string and entity
63
- * const { status, data, get, set, query } = useCrud('cars', { entity: carEntity });
64
- *
65
- * // With explicit schema
66
- * const { status, data, get, set, query } = useCrud('cars', { schema: CarSchema });
67
- *
68
- * // Check availability
69
- * if (!isAvailable) return <Degraded />;
70
- * if (status === 'initializing') return <Loader />;
71
- *
72
- * // Use CRUD operations
73
- * const car = await get('car-123');
74
- * await set('car-123', { make: 'Toyota', model: 'Camry' });
75
- * ```
76
- *
77
- * **Status values:**
78
- * - `initializing`: CRUD service loading
79
- * - `ready`: Fully operational
80
- * - `degraded`: Feature unavailable (no schema, no consent)
81
- * - `error`: Error occurred
39
+ * Use this hook for:
40
+ * - Checking feature status: `const status = useCrud('status');`
41
+ * - Adding, Updating, Deleting items
42
+ * - Fetching a single document
82
43
  *
83
44
  * @template T - Document type for this collection
84
- * @param entityOrCollection - Entity definition (auto-generates schema) or collection name
85
- * @param options - Configuration options (required if first param is collection string)
86
- * @returns Full CRUD API for the collection
87
- *
88
- * @version 0.0.5
89
- * @since 0.0.1
90
- * @author AMBROISE PARK Consulting
45
+ * @param entityOrCollection - Entity definition or collection name
46
+ * @param options - Configuration options
47
+ * @returns CRUD Actions API
91
48
  */
49
+ export declare function useCrud(key: 'status'): FeatureStatus;
92
50
  export declare function useCrud<T = unknown>(entity: Entity, options?: UseCrudOptions<T>): UseCrudReturn<T>;
93
51
  export declare function useCrud<T = unknown>(collection: string, options?: UseCrudOptions<T>): UseCrudReturn<T>;
94
52
  //# sourceMappingURL=useCrud.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCrud.d.ts","sourceRoot":"","sources":["../src/useCrud.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAQzE,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAoCvB,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACxB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,sEAAsE;IACtE,MAAM,EAAE,aAAa,CAAC;IACtB,wCAAwC;IACxC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,kCAAkC;IAClC,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,iCAAiC;IACjC,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,oCAAoC;IACpC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,4BAA4B;IAC5B,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,2CAA2C;IAC3C,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,oCAAoC;IACpC,KAAK,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/C,oCAAoC;IACpC,SAAS,EAAE,CACT,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,KAC9C,MAAM,IAAI,CAAC;IAChB,sCAAsC;IACtC,qBAAqB,EAAE,CACrB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,KACzC,MAAM,IAAI,CAAC;IAChB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,qEAAqE;IACrE,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,aAAa,CAAC,CAAC,CAAC,CAAC;AACpB,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,aAAa,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"useCrud.d.ts","sourceRoot":"","sources":["../src/useCrud.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,aAAa,EACb,gBAAgB,EAChB,OAAO,EACR,MAAM,gBAAgB,CAAC;AAKxB,OAAO,KAAK,EAAE,oBAAoB,EAAgB,YAAY,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,CAAC;AACtD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,CAAC;AAM5D;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,oBAAoB,GAAG,IAAI,CAEpE;AAMD,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACxB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC;IAElD,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACvC,8BAA8B;IAC9B,aAAa,EAAE,YAAY,CAAC;IAC5B,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,aAAa,CAAC;AACtD,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,aAAa,CAAC,CAAC,CAAC,CAAC;AACpB,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAC1B,aAAa,CAAC,CAAC,CAAC,CAAC"}
package/dist/useCrud.js CHANGED
@@ -1 +1 @@
1
- "use client";import{useEffect as U,useMemo as _}from"react";import{DEGRADED_CRUD_API as P,FEATURE_STATUS as y,useFeatureConsent as k}from"@donotdev/core";import{FRAMEWORK_FEATURES as z,handleError as M,isClient as G}from"@donotdev/core";import{useCrudStore as l}from"./CrudStore";import{getCrudService as L}from"./CrudService";import{createEntitySchema as x}from"./forms/utils/createEntitySchema";let t=null,n=null,q=null,C=!1;const F=1e3*60*5;async function K(){if(n)return n;const{QueryClient:a}=await import("@tanstack/react-query");return n=new a({defaultOptions:{queries:{staleTime:F,gcTime:1e3*60*30,retry:1,refetchOnWindowFocus:!1}}}),n}function Z(a,s={}){const f=k(z.CRUD),m=typeof a=="object"?a:s.entity,r=typeof a=="string"?a:a.collection,d=s.backend||"firestore",D=s.staleTime??F,c=s.noCache??!1,u=_(()=>{if(s.schema)return s.schema;if(m)return x(m,"full")},[s.schema,m]);U(()=>{if(!G()||!f||!u)return;const{backend:e}=l.getState();e===d&&t||q||(q=(async()=>{try{const i=L();t=i,i.setStore(l),await i.initialize(d),C=!1}catch(i){C||(M(i,{userMessage:"Failed to initialize CRUD service.",context:{backend:d,collection:r},severity:"error"}),C=!0)}finally{q=null}})())},[f,d,u,r]);const g=async e=>t?c?t.get(r,e,u):(await K()).fetchQuery({queryKey:["crud",r,"get",e],queryFn:()=>t.get(r,e,u),staleTime:D}):null,v=async(e,i)=>{t&&(await t.set(r,e,i,u),!c&&n&&n.invalidateQueries({queryKey:["crud",r]}))},b=async(e,i)=>{t&&(await t.update(r,e,i),!c&&n&&n.invalidateQueries({queryKey:["crud",r]}))},h=async e=>{t&&(await t.delete(r,e),!c&&n&&n.invalidateQueries({queryKey:["crud",r]}))},A=async e=>{if(!t)return"";const i=await t.add(r,e,u);return!c&&n&&n.invalidateQueries({queryKey:["crud",r]}),i},T=async e=>t?c?t.query(r,e,u):(await K()).fetchQuery({queryKey:["crud",r,"query",JSON.stringify(e)],queryFn:()=>t.query(r,e,u),staleTime:D}):[],w=(e,i)=>t?t.subscribe(r,e,(o,E)=>{!c&&n&&o&&n.setQueryData(["crud",r,"get",e],o),i(o,E)},u):()=>{},S=(e,i)=>t?t.subscribeToCollection(r,e,(o,E)=>{!c&&n&&o&&n.setQueryData(["crud",r,"query",JSON.stringify(e)],o),i(o,E)},u):()=>{},R=async()=>{n&&await n.invalidateQueries({queryKey:["crud",r]})},Q=l(e=>e.collections[r]?.data.current||null),I=l(e=>e.collections[r]?.loading||!1),p=l(e=>e.collections[r]?.error||null),N=f?t?y.READY:y.INITIALIZING:y.DEGRADED;return!f||!u||!t?{...P,status:N,data:Q,loading:I,error:p,get:g,set:v,update:b,delete:h,add:A,query:T,subscribe:w,subscribeToCollection:S,invalidate:R,isAvailable:!1}:{status:y.READY,data:Q,loading:I,error:p,get:g,set:v,update:b,delete:h,add:A,query:T,subscribe:w,subscribeToCollection:S,invalidate:R,isAvailable:!0}}export{Z as useCrud};
1
+ "use client";import{useEffect as B,useMemo as y,useCallback as n}from"react";import{useFeatureConsent as L,FRAMEWORK_FEATURES as j,FEATURE_STATUS as f,handleError as V,isClient as W,createSchemas as Z,DEGRADED_CRUD_API as $}from"@donotdev/core";import{getCrudService as H}from"./CrudService";import{useCrudStore as a}from"./CrudStore";const J={},ee={};let U=null,v=null,D=!1;function o(){return U}function te(u,i={}){const m=L(j.CRUD),P=a(e=>e.crudService),E=m&&!P,h=m?E?f.INITIALIZING:f.READY:f.DEGRADED;if(u==="status")return h;const S=typeof u=="object"?u:i.entity,t=typeof u=="string"?u:u.collection,d=i.backend||"functions",g=i.noCache??!1,T=i.staleTime,l=y(()=>({noCache:g,staleTime:T}),[g,T]),s=y(()=>{if(i.schema){const e=i.schema;return{create:e,draft:e,update:e,get:e,list:e,listCard:e,delete:e}}if(S)return Z(S)},[i.schema?"custom":S?.name,t]),b=a(e=>e.collections[t]?.data||J),w=a(e=>e.collections[t]?.loading||!1),A=a(e=>e.collections[t]?.error||null),F=a(e=>e.collections[t]?.data.current||null);B(()=>{if(!W()||!m||!s)return;const{crudService:e,backend:c}=a.getState();e&&c===d||v||(v=(async()=>{try{const r=H();U=r,r.setStore(a),await r.initialize(d),D=!1,a.getState().setBackend(d),a.getState().setCrudService(r)}catch(r){D||(V(r,{userMessage:"Failed to initialize CRUD service.",context:{backend:d,collection:t},severity:"error"}),D=!0)}finally{v=null}})())},[m,d,t,s?"hasSchema":"noSchema"]);const C=E||w,k=h===f.READY,x=n(async e=>{const c=o();return!c||!s?null:c.get(t,e,s.get,l)},[t,s?"hasSchema":"noSchema",l]),M=n(async(e,c)=>{const r=o();if(!r||!s)return;const G=c?.status==="draft"?s.draft:s.create;await r.set(t,e,c,G)},[t,s?"hasSchema":"noSchema"]),N=n(async(e,c)=>{const r=o();r&&await r.update(t,e,c)},[t]),z=n(async e=>{const c=o();c&&await c.delete(t,e)},[t]),Y=n(async e=>{const c=o();if(!c||!s)return"";const R=e?.status==="draft"?s.draft:s.create;return c.add(t,e,R)},[t,s?"hasSchema":"noSchema"]),q=n(async e=>{const c=o();return!c||!s?[]:c.query(t,e,s.list,l)},[t,s?"hasSchema":"noSchema",l]),p=n((e,c)=>{const r=o();return!r||!s?()=>{}:r.subscribe(t,e,c,s.get)},[t,s?"hasSchema":"noSchema"]),_=n((e,c)=>{const r=o();return!r||!s?()=>{}:r.subscribeToCollection(t,e,c,s.list)},[t,s?"hasSchema":"noSchema"]),I=n(async()=>{const e=o();e&&await e.invalidateCollection(t)},[t]);return k?{status:f.READY,data:F,loading:C,error:A,get:x,set:M,update:N,delete:z,add:Y,query:q,subscribe:p,subscribeToCollection:_,invalidate:I,isAvailable:!0,_collection:t,_schemas:s,_cacheOptions:l,_storeData:b}:{...$,status:h,loading:C,error:A,subscribe:p,subscribeToCollection:_,invalidate:I,_collection:t,_schemas:s,_cacheOptions:l,_storeData:b}}export{J as EMPTY_DATA,ee as EMPTY_OPTIMISTIC,o as getCrudServiceInstance,te as useCrud};
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @fileoverview useCrudCardList Hook - Optimized Card List Management
3
+ * @description Specialized hook for fetching lists of entities optimized for card views.
4
+ * Uses TanStack Query for reactive loading states.
5
+ * Fetches only the fields defined in `listCardFields` (or `listFields`) to reduce payload size.
6
+ *
7
+ * @version 0.1.0
8
+ * @since 0.0.1
9
+ * @author AMBROISE PARK Consulting
10
+ */
11
+ import type { Entity, FeatureStatus } from '@donotdev/core';
12
+ import type { UseCrudOptions } from './useCrud';
13
+ export interface UseCrudCardListOptions<T> extends UseCrudOptions<T> {
14
+ /** Automatically fetch data on mount (default: true) */
15
+ enabled?: boolean;
16
+ }
17
+ export interface UseCrudCardListReturn<T> {
18
+ status: FeatureStatus;
19
+ data: {
20
+ items: T[];
21
+ } | null;
22
+ /** True during initial fetch (show skeletons) */
23
+ loading: boolean;
24
+ /** True during background refetch (show subtle indicator) */
25
+ fetching: boolean;
26
+ error: Error | null;
27
+ /** Manual refresh function */
28
+ mutate: () => Promise<void>;
29
+ isAvailable: boolean;
30
+ }
31
+ /**
32
+ * React hook for fetching a list of entities optimized for Card views
33
+ *
34
+ * Use this hook for:
35
+ * - Public-facing grids
36
+ * - Dashboard widgets
37
+ * - Summary lists
38
+ *
39
+ * @template T - Document type
40
+ * @param entityOrCollection - Entity definition or collection name
41
+ * @param options - Configuration options
42
+ * @returns List data and control methods
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const { data, loading, fetching } = useCrudCardList(carEntity);
47
+ *
48
+ * if (loading) return <Skeletons />;
49
+ *
50
+ * return (
51
+ * <Grid>
52
+ * {data?.items.map(car => <Card key={car.id} title={car.make} />)}
53
+ * </Grid>
54
+ * );
55
+ * ```
56
+ */
57
+ export declare function useCrudCardList<T extends {
58
+ id: string;
59
+ } = {
60
+ id: string;
61
+ }>(entityOrCollection: Entity | string, options?: UseCrudCardListOptions<T>): UseCrudCardListReturn<T>;
62
+ //# sourceMappingURL=useCrudCardList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCrudCardList.d.ts","sourceRoot":"","sources":["../src/useCrudCardList.ts"],"names":[],"mappings":"AACA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAO5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,WAAW,sBAAsB,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IAClE,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACvE,kBAAkB,EAAE,MAAM,GAAG,MAAM,EACnC,OAAO,GAAE,sBAAsB,CAAC,CAAC,CAAM,GACtC,qBAAqB,CAAC,CAAC,CAAC,CAsF1B"}
@@ -0,0 +1 @@
1
+ import{useMemo as y,useCallback as T}from"react";import{useQuery as _}from"@donotdev/core";import{useCrudStore as I}from"./CrudStore";import{useCrud as h,getCrudServiceInstance as L,EMPTY_OPTIMISTIC as A}from"./useCrud";import{mergeWithOptimistic as D}from"./utils/mergeWithOptimistic";function K(p,n={}){const C=n.enabled??!0,i=h("status"),e=i==="ready",g=h(p,n),{error:c,invalidate:u,_collection:r,_schemas:a,_cacheOptions:l,_storeData:m}=g,d=I(s=>s.collections[r]?.optimistic||A),t=y(()=>{if(!e||!a)return null;const s=L();return s?s.getListQueryOptions(r,{},a.listCard,l,"listCard"):null},[e,r,l,a?"hasSchema":"noSchema"]),{data:o,isLoading:b,isFetching:q,error:v,refetch:f}=_({queryKey:t?.queryKey??["crud",r,"disabled"],queryFn:t?.queryFn??(()=>Promise.resolve([])),staleTime:t?.staleTime,enabled:C&&!!t}),O=y(()=>!o||!e?[]:D(o,d,m),[o,d,m,e]),S=T(async()=>{e&&(await u(),await f())},[u,f,e]);return e?{status:i,data:{items:O},loading:b,fetching:q,error:v??c,mutate:S,isAvailable:!0}:{status:i,data:{items:[]},loading:!1,fetching:!1,error:c,mutate:async()=>{},isAvailable:!1}}export{K as useCrudCardList};
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @fileoverview useCrudList Hook - Collection Management
3
+ * @description Specialized hook for fetching and managing lists of entities.
4
+ * Uses TanStack Query for reactive loading states.
5
+ *
6
+ * @version 0.2.0
7
+ * @since 0.0.1
8
+ * @author AMBROISE PARK Consulting
9
+ */
10
+ import type { Entity, FeatureStatus } from '@donotdev/core';
11
+ import type { UseCrudOptions } from './useCrud';
12
+ export interface UseCrudListOptions<T> extends UseCrudOptions<T> {
13
+ /** Automatically fetch data on mount (default: true) */
14
+ enabled?: boolean;
15
+ }
16
+ export interface UseCrudListReturn<T> {
17
+ status: FeatureStatus;
18
+ data: {
19
+ items: T[];
20
+ } | null;
21
+ /** True during initial fetch (show skeletons) */
22
+ loading: boolean;
23
+ /** True during background refetch (show subtle indicator) */
24
+ fetching: boolean;
25
+ error: Error | null;
26
+ /** Manual refresh function */
27
+ mutate: () => Promise<void>;
28
+ isAvailable: boolean;
29
+ }
30
+ /**
31
+ * React hook for fetching and managing a list of entities
32
+ *
33
+ * Use this hook for:
34
+ * - Admin tables
35
+ * - Data grids
36
+ * - Lists of items
37
+ *
38
+ * @template T - Document type
39
+ * @param entityOrCollection - Entity definition or collection name
40
+ * @param options - Configuration options
41
+ * @returns List data and control methods
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const { data, loading, mutate } = useCrudList(carEntity);
46
+ *
47
+ * if (loading) return <Loader />;
48
+ *
49
+ * return (
50
+ * <ul>
51
+ * {data?.items.map(car => <li key={car.id}>{car.make}</li>)}
52
+ * </ul>
53
+ * );
54
+ * ```
55
+ */
56
+ export declare function useCrudList<T extends {
57
+ id: string;
58
+ } = {
59
+ id: string;
60
+ }>(entityOrCollection: Entity | string, options?: UseCrudListOptions<T>): UseCrudListReturn<T>;
61
+ //# sourceMappingURL=useCrudList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCrudList.d.ts","sourceRoot":"","sources":["../src/useCrudList.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAO5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,WAAW,kBAAkB,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IAC9D,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACnE,kBAAkB,EAAE,MAAM,GAAG,MAAM,EACnC,OAAO,GAAE,kBAAkB,CAAC,CAAC,CAAM,GAClC,iBAAiB,CAAC,CAAC,CAAC,CAsFtB"}
@@ -0,0 +1 @@
1
+ import{useMemo as y,useCallback as T}from"react";import{useQuery as _}from"@donotdev/core";import{useCrudStore as I}from"./CrudStore";import{useCrud as h,getCrudServiceInstance as L,EMPTY_OPTIMISTIC as A}from"./useCrud";import{mergeWithOptimistic as D}from"./utils/mergeWithOptimistic";function K(p,n={}){const g=n.enabled??!0,i=h("status"),e=i==="ready",b=h(p,n),{error:c,invalidate:u,_collection:t,_schemas:a,_cacheOptions:l,_storeData:m}=b,d=I(s=>s.collections[t]?.optimistic||A),r=y(()=>{if(!e||!a)return null;const s=L();return s?s.getListQueryOptions(t,{},a.list,l,"list"):null},[e,t,l,a?"hasSchema":"noSchema"]),{data:o,isLoading:q,isFetching:v,error:C,refetch:f}=_({queryKey:r?.queryKey??["crud",t,"disabled"],queryFn:r?.queryFn??(()=>Promise.resolve([])),staleTime:r?.staleTime,enabled:g&&!!r}),O=y(()=>!o||!e?[]:D(o,d,m),[o,d,m,e]),S=T(async()=>{e&&(await u(),await f())},[u,f,e]);return e?{status:i,data:{items:O},loading:q,fetching:v,error:C??c,mutate:S,isAvailable:!0}:{status:i,data:{items:[]},loading:!1,fetching:!1,error:c,mutate:async()=>{},isAvailable:!1}}export{K as useCrudList};
@@ -0,0 +1,58 @@
1
+ import type { UploadProgressCallback } from '@donotdev/firebase';
2
+ import type { FileAsset } from '@donotdev/types';
3
+ export type { FileAsset };
4
+ export interface UploadProgress {
5
+ bytesTransferred: number;
6
+ totalBytes: number;
7
+ progress: number;
8
+ }
9
+ export interface UploadFileOptions {
10
+ /** Storage path (default: 'uploads/files') */
11
+ storagePath?: string;
12
+ /** Custom filename (default: timestamp_originalname) */
13
+ filename?: string;
14
+ /** Progress callback */
15
+ onProgress?: UploadProgressCallback;
16
+ }
17
+ /**
18
+ * Upload a file to Firebase Storage with progress tracking
19
+ * Generic version for any file type (documents, PDFs, etc.)
20
+ */
21
+ export declare function uploadFile(file: File, options?: UploadFileOptions): Promise<FileAsset>;
22
+ /**
23
+ * Upload multiple files to Firebase Storage
24
+ */
25
+ export declare function uploadFiles(files: File[], options?: UploadFileOptions): Promise<FileAsset[]>;
26
+ /**
27
+ * Delete a file from Firebase Storage
28
+ */
29
+ export declare function deleteFile(asset: FileAsset): Promise<void>;
30
+ /**
31
+ * Generate a unique file ID
32
+ */
33
+ export declare function generateFileId(): string;
34
+ /**
35
+ * Get file extension from filename
36
+ */
37
+ export declare function getFileExtension(filename: string): string;
38
+ /**
39
+ * Get file icon based on MIME type or extension
40
+ */
41
+ export declare function getFileIcon(mimeType?: string, filename?: string): string;
42
+ /**
43
+ * Format file size for display
44
+ */
45
+ export declare function formatFileSize(bytes: number): string;
46
+ /**
47
+ * Document MIME types
48
+ */
49
+ export declare const DOCUMENT_MIME_TYPES: string[];
50
+ /**
51
+ * Document file extensions
52
+ */
53
+ export declare const DOCUMENT_EXTENSIONS: string[];
54
+ /**
55
+ * Get accept string for document inputs
56
+ */
57
+ export declare function getDocumentAcceptString(): string;
58
+ //# sourceMappingURL=fileStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileStorage.d.ts","sourceRoot":"","sources":["../../src/utils/fileStorage.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,UAAU,CAAC,EAAE,sBAAsB,CAAC;CACrC;AAYD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,SAAS,CAAC,CAkCpB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,SAAS,EAAE,CAAC,CA4BtB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhE;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CA4CxE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAY/B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAY/B,CAAC;AAEF;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD"}
@@ -0,0 +1 @@
1
+ "use client";import{handleError as p}from"@donotdev/core";import{uploadFileResumable as u,deleteFileByUrl as d}from"@donotdev/firebase";function f(e){return e.replace(/[^a-zA-Z0-9.-]/g,"_").replace(/_{2,}/g,"_").toLowerCase()}async function x(e,t={}){const{storagePath:r="uploads/files",filename:n,onProgress:a}=t,o=Date.now(),i=f(e.name),c=n||`${o}_${i}`,l=`${r}/${c}`;try{const{promise:s}=u(e,{basePath:r,filename:c,onProgress:a,metadata:{contentType:e.type}});return{url:(await s).url,filename:e.name,size:e.size,mimeType:e.type,uploadedAt:new Date().toISOString()}}catch(s){throw p(s,{userMessage:"Failed to upload file",severity:"error",context:{filename:e.name,fileSize:e.size}})}}async function v(e,t={}){const r=[],n=e.reduce((o,i)=>o+i.size,0);let a=0;for(const o of e){const i=t.onProgress?l=>{const s=l.bytesTransferred/l.totalBytes*o.size;t.onProgress({bytesTransferred:a+s,totalBytes:n,progress:(a+s)/n*100})}:void 0,c=await x(o,{...t,onProgress:i});r.push(c),a+=o.size}return r}async function M(e){try{await d(e.url)}catch(t){throw p(t,{userMessage:"Failed to delete file",severity:"error",context:{url:e.url,filename:e.filename}}),t}}function S(){return`${Date.now()}-${Math.random().toString(36).slice(2,9)}`}function m(e){const t=e.split(".");return t.length>1?t.pop().toLowerCase():""}function P(e,t){const r=t?m(t):"";if(e){if(e.startsWith("image/"))return"image";if(e.startsWith("video/"))return"video";if(e.startsWith("audio/"))return"audio";if(e==="application/pdf")return"pdf";if(e.includes("word")||e.includes("document"))return"doc";if(e.includes("excel")||e.includes("spreadsheet"))return"xls";if(e.includes("powerpoint")||e.includes("presentation"))return"ppt"}switch(r){case"pdf":return"pdf";case"doc":case"docx":return"doc";case"xls":case"xlsx":case"csv":return"xls";case"ppt":case"pptx":return"ppt";case"md":case"txt":return"text";case"html":case"htm":return"html";case"zip":case"rar":case"7z":return"archive";default:return"file"}}function B(e){if(e===0)return"0 B";const t=1024,r=["B","KB","MB","GB"],n=Math.floor(Math.log(e)/Math.log(t));return`${parseFloat((e/Math.pow(t,n)).toFixed(1))} ${r[n]}`}const h=["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application/vnd.ms-powerpoint","application/vnd.openxmlformats-officedocument.presentationml.presentation","text/plain","text/markdown","text/html","text/csv"],g=[".pdf",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".txt",".md",".html",".csv"];function E(){return[...h,...g].join(",")}export{g as DOCUMENT_EXTENSIONS,h as DOCUMENT_MIME_TYPES,M as deleteFile,B as formatFileSize,S as generateFileId,E as getDocumentAcceptString,m as getFileExtension,P as getFileIcon,x as uploadFile,v as uploadFiles};
@@ -23,7 +23,7 @@ export interface ProcessImageOptions {
23
23
  thumbHeight?: number;
24
24
  /** Target aspect ratio (default: 4/3) */
25
25
  aspectRatio?: number;
26
- /** WebP quality (0-1, default: 0.9 for full, 0.8 for thumb) */
26
+ /** WebP quality (0-1, default: 0.85 for full, 0.65 for thumb) */
27
27
  quality?: number;
28
28
  /** Add transparent background for non-matching aspect ratios */
29
29
  transparentBackground?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"imageProcessing.d.ts","sourceRoot":"","sources":["../../src/utils/imageProcessing.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,IAAI,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iEAAiE;IACjE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAChE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,cAAc,CAAC,CAsEzB;AAwHD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,EACV,YAAY,CAAC,EAAE,MAAM,GACpB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBpC"}
1
+ {"version":3,"file":"imageProcessing.d.ts","sourceRoot":"","sources":["../../src/utils/imageProcessing.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,IAAI,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,iEAAiE;IACjE,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAChE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,cAAc,CAAC,CAuEzB;AAwHD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,EACV,YAAY,CAAC,EAAE,MAAM,GACpB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBpC"}
@@ -1 +1 @@
1
- async function x(t,r={}){const{maxWidth:o=1920,maxHeight:a=1440,thumbWidth:d=300,thumbHeight:i=225,aspectRatio:e=4/3,quality:h=.9,transparentBackground:c=!0,crop:l}=r;return new Promise((f,s)=>{const n=new Image,u=new FileReader;u.onload=w=>{n.src=w.target?.result},u.onerror=()=>s(new Error("Failed to read file")),n.onload=async()=>{try{const{width:w,height:F}=g(l?l.width:n.width,l?l.height:n.height,o,a,e),m=await b(n,w,F,e,h,c,l),v=await b(n,d,i,e,.8,c,l);f({fullBlob:m,thumbBlob:v,width:w,height:F})}catch(w){s(w)}},n.onerror=()=>s(new Error("Failed to load image")),u.readAsDataURL(t)})}function g(t,r,o,a,d){let i=t,e=r;i>o&&(e=o/i*e,i=o),e>a&&(i=a/e*i,e=a);const h=i/e;return Math.abs(h-d)>.01&&(h>d?e=i/d:i=e*d),{width:Math.round(i),height:Math.round(e)}}async function b(t,r,o,a,d,i,e){const h=document.createElement("canvas"),c=h.getContext("2d");if(!c)throw new Error("Failed to get canvas context");h.width=r,h.height=o,i?c.clearRect(0,0,r,o):(c.fillStyle="#FFFFFF",c.fillRect(0,0,r,o));let l,f,s,n;if(e)l=e.x,f=e.y,s=e.width,n=e.height;else{const u=t.width/t.height;s=t.width,n=t.height,l=0,f=0,u>a?(s=t.height*a,l=(t.width-s)/2):u<a&&(n=t.width/a,f=(t.height-n)/2)}return c.drawImage(t,l,f,s,n,0,0,r,o),new Promise((u,w)=>{h.toBlob(F=>{F?u(F):w(new Error("Failed to convert canvas to blob"))},"image/webp",d)})}function y(t,r){return t.type.startsWith("image/")?r&&t.size>r?{valid:!1,error:`File size exceeds ${(r/1048576).toFixed(1)}MB limit`}:{valid:!0}:{valid:!1,error:"File must be an image"}}export{x as processImage,y as validateImageFile};
1
+ async function x(t,r={}){const{maxWidth:o=1920,maxHeight:a=1440,thumbWidth:d=300,thumbHeight:i=225,aspectRatio:e=4/3,quality:h=.85,transparentBackground:c=!0,crop:l}=r;return new Promise((f,s)=>{const n=new Image,u=new FileReader;u.onload=w=>{n.src=w.target?.result},u.onerror=()=>s(new Error("Failed to read file")),n.onload=async()=>{try{const{width:w,height:F}=g(l?l.width:n.width,l?l.height:n.height,o,a,e),m=await b(n,w,F,e,h,c,l),v=await b(n,d,i,e,.65,c,l);f({fullBlob:m,thumbBlob:v,width:w,height:F})}catch(w){s(w)}},n.onerror=()=>s(new Error("Failed to load image")),u.readAsDataURL(t)})}function g(t,r,o,a,d){let i=t,e=r;i>o&&(e=o/i*e,i=o),e>a&&(i=a/e*i,e=a);const h=i/e;return Math.abs(h-d)>.01&&(h>d?e=i/d:i=e*d),{width:Math.round(i),height:Math.round(e)}}async function b(t,r,o,a,d,i,e){const h=document.createElement("canvas"),c=h.getContext("2d");if(!c)throw new Error("Failed to get canvas context");h.width=r,h.height=o,i?c.clearRect(0,0,r,o):(c.fillStyle="#FFFFFF",c.fillRect(0,0,r,o));let l,f,s,n;if(e)l=e.x,f=e.y,s=e.width,n=e.height;else{const u=t.width/t.height;s=t.width,n=t.height,l=0,f=0,u>a?(s=t.height*a,l=(t.width-s)/2):u<a&&(n=t.width/a,f=(t.height-n)/2)}return c.drawImage(t,l,f,s,n,0,0,r,o),new Promise((u,w)=>{h.toBlob(F=>{F?u(F):w(new Error("Failed to convert canvas to blob"))},"image/webp",d)})}function y(t,r){return t.type.startsWith("image/")?r&&t.size>r?{valid:!1,error:`File size exceeds ${(r/1048576).toFixed(1)}MB limit`}:{valid:!0}:{valid:!1,error:"File must be an image"}}export{x as processImage,y as validateImageFile};
@@ -1,13 +1,4 @@
1
- /**
2
- * @fileoverview Image Storage Utilities
3
- * @description Handle image uploads to Firebase Storage with progress tracking, WebP conversion, and thumbnails
4
- * Uses @donotdev/firebase for storage operations
5
- *
6
- * @version 0.0.2
7
- * @since 0.0.1
8
- * @author AMBROISE PARK Consulting
9
- */
10
- import { type UploadProgressCallback } from '@donotdev/firebase';
1
+ import type { UploadProgressCallback } from '@donotdev/firebase';
11
2
  import type { Picture } from '@donotdev/types';
12
3
  export type { Picture };
13
4
  export interface UploadProgress {
@@ -1 +1 @@
1
- {"version":3,"file":"imageStorage.d.ts","sourceRoot":"","sources":["../../src/utils/imageStorage.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,YAAY,EAAE,OAAO,EAAE,CAAC;AAExB,MAAM,WAAW,cAAc;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,UAAU,CAAC,EAAE,sBAAsB,CAAC;CACrC;AAYD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,IAAI,EACd,SAAS,EAAE,IAAI,EACf,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,OAAO,CAAC,CAmFlB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjE"}
1
+ {"version":3,"file":"imageStorage.d.ts","sourceRoot":"","sources":["../../src/utils/imageStorage.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,YAAY,EAAE,OAAO,EAAE,CAAC;AAExB,MAAM,WAAW,cAAc;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,UAAU,CAAC,EAAE,sBAAsB,CAAC;CACrC;AAYD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,IAAI,EACd,SAAS,EAAE,IAAI,EACf,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,OAAO,CAAC,CAmFlB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAcjE"}
@@ -1 +1 @@
1
- import{uploadFileResumable as c,deleteFileByUrl as i,getFileUrl as m}from"@donotdev/firebase";import{handleError as _}from"@donotdev/core";function F(e){return e.replace(/[^a-zA-Z0-9.-]/g,"_").replace(/_{2,}/g,"_").toLowerCase()}async function N(e,l,b,h={}){const{storagePath:r="uploads/images",filename:f,onProgress:s}=h,p=Date.now(),t=(f||`${p}_${F(b)}`).replace(/\.[^/.]+$/,""),w=`${r}/${t}_full.webp`,g=`${r}/${t}_thumb.webp`;try{const u=s?a=>{s({bytesTransferred:a.bytesTransferred,totalBytes:a.totalBytes*2,progress:a.progress*.5})}:void 0,o=new File([e],`${t}_full.webp`,{type:"image/webp"}),{promise:n}=c(o,{basePath:r,filename:`${t}_full.webp`,onProgress:u,metadata:{contentType:"image/webp"}}),d=(await n).url,y=s?a=>{s({bytesTransferred:a.bytesTransferred+e.size,totalBytes:e.size+l.size,progress:50+a.progress*.5})}:void 0,P=new File([l],`${t}_thumb.webp`,{type:"image/webp"}),{promise:U}=c(P,{basePath:r,filename:`${t}_thumb.webp`,onProgress:y,metadata:{contentType:"image/webp"}}),$=(await U).url;return{fullUrl:d,thumbUrl:$}}catch(u){try{const o=await m(w).catch(()=>null),n=await m(g).catch(()=>null);o&&await i(o).catch(()=>{}),n&&await i(n).catch(()=>{})}catch{}throw u}}async function k(e){try{await Promise.all([i(e.fullUrl),i(e.thumbUrl)])}catch(l){throw _(l,{userMessage:"Failed to delete image",severity:"error",context:{fullUrl:e.fullUrl,thumbUrl:e.thumbUrl}}),l}}export{k as deleteImage,N as uploadImage};
1
+ import{handleError as _}from"@donotdev/core";import{uploadFileResumable as c,deleteFileByUrl as i,getFileUrl as m}from"@donotdev/firebase";function F(e){return e.replace(/[^a-zA-Z0-9.-]/g,"_").replace(/_{2,}/g,"_").toLowerCase()}async function N(e,l,b,h={}){const{storagePath:r="uploads/images",filename:f,onProgress:s}=h,p=Date.now(),t=(f||`${p}_${F(b)}`).replace(/\.[^/.]+$/,""),w=`${r}/${t}_full.webp`,g=`${r}/${t}_thumb.webp`;try{const u=s?a=>{s({bytesTransferred:a.bytesTransferred,totalBytes:a.totalBytes*2,progress:a.progress*.5})}:void 0,o=new File([e],`${t}_full.webp`,{type:"image/webp"}),{promise:n}=c(o,{basePath:r,filename:`${t}_full.webp`,onProgress:u,metadata:{contentType:"image/webp"}}),d=(await n).url,y=s?a=>{s({bytesTransferred:a.bytesTransferred+e.size,totalBytes:e.size+l.size,progress:50+a.progress*.5})}:void 0,P=new File([l],`${t}_thumb.webp`,{type:"image/webp"}),{promise:U}=c(P,{basePath:r,filename:`${t}_thumb.webp`,onProgress:y,metadata:{contentType:"image/webp"}}),$=(await U).url;return{fullUrl:d,thumbUrl:$}}catch(u){try{const o=await m(w).catch(()=>null),n=await m(g).catch(()=>null);o&&await i(o).catch(()=>{}),n&&await i(n).catch(()=>{})}catch{}throw u}}async function k(e){try{await Promise.all([i(e.fullUrl),i(e.thumbUrl)])}catch(l){throw _(l,{userMessage:"Failed to delete image",severity:"error",context:{fullUrl:e.fullUrl,thumbUrl:e.thumbUrl}}),l}}export{k as deleteImage,N as uploadImage};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @fileoverview Optimistic merge utility
3
+ * @description Merges TanStack Query results with optimistic operations from store
4
+ *
5
+ * @version 0.0.1
6
+ * @since 0.0.1
7
+ * @author AMBROISE PARK Consulting
8
+ */
9
+ import type { OptimisticMeta } from '../CrudStore';
10
+ interface WithId {
11
+ id: string;
12
+ [key: string]: unknown;
13
+ }
14
+ /**
15
+ * Merge query results with optimistic operations.
16
+ * - Filter out items marked for deletion
17
+ * - Apply optimistic updates
18
+ * - Append optimistic adds (temp IDs)
19
+ *
20
+ * @param queryData - Data from TanStack Query cache
21
+ * @param optimistic - Optimistic operation metadata from store
22
+ * @param storeData - Current store data (for optimistic values)
23
+ * @returns Merged array with optimistic changes applied
24
+ */
25
+ export declare function mergeWithOptimistic<T extends WithId>(queryData: T[], optimistic: Record<string, OptimisticMeta>, storeData?: Record<string, unknown>): T[];
26
+ export {};
27
+ //# sourceMappingURL=mergeWithOptimistic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeWithOptimistic.d.ts","sourceRoot":"","sources":["../../src/utils/mergeWithOptimistic.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAClD,SAAS,EAAE,CAAC,EAAE,EACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC1C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,CAAC,EAAE,CA4BL"}
@@ -0,0 +1 @@
1
+ function p(s,t,i){const n=[],d=new Set;for(const e of s){const o=t[e.id];o?.operation!=="delete"&&(o?.operation==="update"&&i?.[e.id]?n.push(i[e.id]):n.push(e),d.add(e.id))}for(const[e,o]of Object.entries(t))o.operation==="add"&&!d.has(e)&&i?.[e]&&n.push(i[e]);return n}export{p as mergeWithOptimistic};
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @fileoverview Upload Validation Utilities
3
+ * @description Type-safe validation utilities to ensure no blob URLs reach the database.
4
+ * Provides security checks for Picture objects and form data validation.
5
+ *
6
+ * @version 0.0.1
7
+ * @since 0.0.1
8
+ * @author AMBROISE PARK Consulting
9
+ */
10
+ import type { Picture } from '@donotdev/types';
11
+ /**
12
+ * Check if a URL is a valid storage URL (not a blob URL)
13
+ * @param url - URL to check
14
+ * @returns true if URL is a storage URL (https://), false if blob URL or invalid
15
+ */
16
+ export declare function isStorageUrl(url: string): boolean;
17
+ /**
18
+ * Validate that a Picture object has valid storage URLs
19
+ * @param picture - Picture object to validate
20
+ * @returns true if both fullUrl and thumbUrl are valid storage URLs
21
+ */
22
+ export declare function validatePicture(picture: Picture): boolean;
23
+ /**
24
+ * Recursively check for blob URLs in form data
25
+ * Returns array of paths where blob URLs were found
26
+ * @param data - Data to check (can be nested object/array)
27
+ * @param path - Current path in the data structure (for error messages)
28
+ * @returns Array of paths where blob URLs were found
29
+ */
30
+ export declare function checkForBlobUrls(data: any, path?: string): string[];
31
+ /**
32
+ * Check if form data contains any blob URLs
33
+ * @param data - Form data to check
34
+ * @returns true if any blob URLs found, false otherwise
35
+ */
36
+ export declare function hasBlobUrls(data: any): boolean;
37
+ //# sourceMappingURL=uploadValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadValidation.d.ts","sourceRoot":"","sources":["../../src/utils/uploadValidation.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGjD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAKzD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,SAAK,GAAG,MAAM,EAAE,CA2C/D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAE9C"}
@@ -0,0 +1 @@
1
+ "use client";function l(r){return!r||typeof r!="string"?!1:r.startsWith("https://")&&!r.startsWith("blob:")}function i(r){return!r||typeof r!="object"?!1:l(r.fullUrl)&&l(r.thumbUrl)}function f(r,n=""){const e=[];if(r==null)return e;if(typeof r=="string")return r.startsWith("blob:")&&e.push(n||"root"),e;if(typeof r!="object")return e;if(Array.isArray(r))r.forEach((t,o)=>{const s=n?`${n}[${o}]`:`[${o}]`;e.push(...f(t,s))});else if(r.fullUrl!==void 0&&r.thumbUrl!==void 0){if(!l(r.fullUrl)||!l(r.thumbUrl)){const t=n||"root";e.push(`${t}.fullUrl or ${t}.thumbUrl contains blob URL`)}}else Object.entries(r).forEach(([t,o])=>{const s=n?`${n}.${t}`:t;e.push(...f(o,s))});return e}function u(r){return f(r).length>0}export{f as checkForBlobUrls,u as hasBlobUrls,l as isStorageUrl,i as validatePicture};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donotdev/crud",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -49,8 +49,8 @@
49
49
  "type-check": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@donotdev/components": "^0.0.12",
53
- "@donotdev/core": "^0.0.13",
52
+ "@donotdev/components": "^0.0.13",
53
+ "@donotdev/core": "^0.0.14",
54
54
  "@hookform/resolvers": "^5.2.2",
55
55
  "react-easy-crop": "^5.5.6"
56
56
  },
@@ -61,7 +61,24 @@
61
61
  "react": "^19.2.3",
62
62
  "react-dom": "^19.2.3",
63
63
  "react-hook-form": "^7.71.0",
64
- "valibot": "^1.2.0"
64
+ "valibot": "^1.2.0",
65
+ "@tiptap/react": "^3.16.0",
66
+ "@tiptap/pm": "^3.16.0",
67
+ "@tiptap/starter-kit": "^3.16.0",
68
+ "@tiptap/extension-placeholder": "^3.16.0"
65
69
  },
66
- "peerDependenciesMeta": {}
70
+ "peerDependenciesMeta": {
71
+ "@tiptap/react": {
72
+ "optional": true
73
+ },
74
+ "@tiptap/pm": {
75
+ "optional": true
76
+ },
77
+ "@tiptap/starter-kit": {
78
+ "optional": true
79
+ },
80
+ "@tiptap/extension-placeholder": {
81
+ "optional": true
82
+ }
83
+ }
67
84
  }