@eide/uniformgen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +356 -0
  2. package/dist/auth/credentials.d.ts +58 -0
  3. package/dist/auth/credentials.d.ts.map +1 -0
  4. package/dist/auth/credentials.js +107 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +563 -0
  8. package/dist/commands/init.d.ts +11 -0
  9. package/dist/commands/init.d.ts.map +1 -0
  10. package/dist/commands/init.js +113 -0
  11. package/dist/commands/login.d.ts +9 -0
  12. package/dist/commands/login.d.ts.map +1 -0
  13. package/dist/commands/login.js +158 -0
  14. package/dist/commands/logout.d.ts +5 -0
  15. package/dist/commands/logout.d.ts.map +1 -0
  16. package/dist/commands/logout.js +13 -0
  17. package/dist/commands/push.d.ts +13 -0
  18. package/dist/commands/push.d.ts.map +1 -0
  19. package/dist/commands/push.js +328 -0
  20. package/dist/commands/scaffold.d.ts +19 -0
  21. package/dist/commands/scaffold.d.ts.map +1 -0
  22. package/dist/commands/scaffold.js +366 -0
  23. package/dist/commands/seed.d.ts +20 -0
  24. package/dist/commands/seed.d.ts.map +1 -0
  25. package/dist/commands/seed.js +380 -0
  26. package/dist/commands/select-project.d.ts +10 -0
  27. package/dist/commands/select-project.d.ts.map +1 -0
  28. package/dist/commands/select-project.js +277 -0
  29. package/dist/commands/setup.d.ts +5 -0
  30. package/dist/commands/setup.d.ts.map +1 -0
  31. package/dist/commands/setup.js +51 -0
  32. package/dist/commands/sync.d.ts +13 -0
  33. package/dist/commands/sync.d.ts.map +1 -0
  34. package/dist/commands/sync.js +318 -0
  35. package/dist/commands/whoami.d.ts +5 -0
  36. package/dist/commands/whoami.d.ts.map +1 -0
  37. package/dist/commands/whoami.js +31 -0
  38. package/dist/config/load-config.d.ts +6 -0
  39. package/dist/config/load-config.d.ts.map +1 -0
  40. package/dist/config/load-config.js +103 -0
  41. package/dist/config/settings.d.ts +20 -0
  42. package/dist/config/settings.d.ts.map +1 -0
  43. package/dist/config/settings.js +64 -0
  44. package/dist/config/types.d.ts +152 -0
  45. package/dist/config/types.d.ts.map +1 -0
  46. package/dist/config/types.js +94 -0
  47. package/dist/fetcher/fetch-schemas.d.ts +140 -0
  48. package/dist/fetcher/fetch-schemas.d.ts.map +1 -0
  49. package/dist/fetcher/fetch-schemas.js +223 -0
  50. package/dist/fetcher/fetch-workflows.d.ts +53 -0
  51. package/dist/fetcher/fetch-workflows.d.ts.map +1 -0
  52. package/dist/fetcher/fetch-workflows.js +164 -0
  53. package/dist/generated/hooks/customer-detail.d.ts +74 -0
  54. package/dist/generated/hooks/customer-detail.d.ts.map +1 -0
  55. package/dist/generated/hooks/customer-detail.js +113 -0
  56. package/dist/generated/hooks/design-system.d.ts +74 -0
  57. package/dist/generated/hooks/design-system.d.ts.map +1 -0
  58. package/dist/generated/hooks/design-system.js +109 -0
  59. package/dist/generated/hooks/index.d.ts +16 -0
  60. package/dist/generated/hooks/index.d.ts.map +1 -0
  61. package/dist/generated/hooks/index.js +14 -0
  62. package/dist/generated/hooks/shopify-collection.d.ts +74 -0
  63. package/dist/generated/hooks/shopify-collection.d.ts.map +1 -0
  64. package/dist/generated/hooks/shopify-collection.js +113 -0
  65. package/dist/generated/hooks/shopify-market.d.ts +74 -0
  66. package/dist/generated/hooks/shopify-market.d.ts.map +1 -0
  67. package/dist/generated/hooks/shopify-market.js +109 -0
  68. package/dist/generated/hooks/shopify-product.d.ts +74 -0
  69. package/dist/generated/hooks/shopify-product.d.ts.map +1 -0
  70. package/dist/generated/hooks/shopify-product.js +113 -0
  71. package/dist/generated/hooks/shopify-variant.d.ts +74 -0
  72. package/dist/generated/hooks/shopify-variant.d.ts.map +1 -0
  73. package/dist/generated/hooks/shopify-variant.js +113 -0
  74. package/dist/generated/hooks/template.d.ts +74 -0
  75. package/dist/generated/hooks/template.d.ts.map +1 -0
  76. package/dist/generated/hooks/template.js +107 -0
  77. package/dist/generated/types/config.d.ts +88 -0
  78. package/dist/generated/types/config.d.ts.map +1 -0
  79. package/dist/generated/types/config.js +14 -0
  80. package/dist/generated/types/data-models/index.d.ts +7 -0
  81. package/dist/generated/types/data-models/index.d.ts.map +1 -0
  82. package/dist/generated/types/data-models/index.js +6 -0
  83. package/dist/generated/types/data-models/test.d.ts +29 -0
  84. package/dist/generated/types/data-models/test.d.ts.map +1 -0
  85. package/dist/generated/types/data-models/test.js +1 -0
  86. package/dist/generated/types/data-models/watch.d.ts +26 -0
  87. package/dist/generated/types/data-models/watch.d.ts.map +1 -0
  88. package/dist/generated/types/data-models/watch.js +1 -0
  89. package/dist/generated/types/field-types.d.ts +255 -0
  90. package/dist/generated/types/field-types.d.ts.map +1 -0
  91. package/dist/generated/types/field-types.js +35 -0
  92. package/dist/generated/types/hooks.d.ts +106 -0
  93. package/dist/generated/types/hooks.d.ts.map +1 -0
  94. package/dist/generated/types/hooks.js +9 -0
  95. package/dist/generated/types/index.d.ts +10 -0
  96. package/dist/generated/types/index.d.ts.map +1 -0
  97. package/dist/generated/types/index.js +9 -0
  98. package/dist/generated/types/models/button-variant.d.ts +16 -0
  99. package/dist/generated/types/models/button-variant.d.ts.map +1 -0
  100. package/dist/generated/types/models/button-variant.js +1 -0
  101. package/dist/generated/types/models/color-palette.d.ts +27 -0
  102. package/dist/generated/types/models/color-palette.d.ts.map +1 -0
  103. package/dist/generated/types/models/color-palette.js +1 -0
  104. package/dist/generated/types/models/color-with-scale.d.ts +15 -0
  105. package/dist/generated/types/models/color-with-scale.d.ts.map +1 -0
  106. package/dist/generated/types/models/color-with-scale.js +1 -0
  107. package/dist/generated/types/models/context.d.ts +53 -0
  108. package/dist/generated/types/models/context.d.ts.map +1 -0
  109. package/dist/generated/types/models/context.js +51 -0
  110. package/dist/generated/types/models/customer-detail.d.ts +32 -0
  111. package/dist/generated/types/models/customer-detail.d.ts.map +1 -0
  112. package/dist/generated/types/models/customer-detail.js +26 -0
  113. package/dist/generated/types/models/design-system-badges.d.ts +16 -0
  114. package/dist/generated/types/models/design-system-badges.d.ts.map +1 -0
  115. package/dist/generated/types/models/design-system-badges.js +1 -0
  116. package/dist/generated/types/models/design-system-buttons.d.ts +17 -0
  117. package/dist/generated/types/models/design-system-buttons.d.ts.map +1 -0
  118. package/dist/generated/types/models/design-system-buttons.js +1 -0
  119. package/dist/generated/types/models/design-system-cards.d.ts +16 -0
  120. package/dist/generated/types/models/design-system-cards.d.ts.map +1 -0
  121. package/dist/generated/types/models/design-system-cards.js +1 -0
  122. package/dist/generated/types/models/design-system-colors.d.ts +12 -0
  123. package/dist/generated/types/models/design-system-colors.d.ts.map +1 -0
  124. package/dist/generated/types/models/design-system-colors.js +1 -0
  125. package/dist/generated/types/models/design-system-dark-mode.d.ts +13 -0
  126. package/dist/generated/types/models/design-system-dark-mode.d.ts.map +1 -0
  127. package/dist/generated/types/models/design-system-dark-mode.js +1 -0
  128. package/dist/generated/types/models/design-system-focus-ring.d.ts +14 -0
  129. package/dist/generated/types/models/design-system-focus-ring.d.ts.map +1 -0
  130. package/dist/generated/types/models/design-system-focus-ring.js +1 -0
  131. package/dist/generated/types/models/design-system-grid.d.ts +18 -0
  132. package/dist/generated/types/models/design-system-grid.d.ts.map +1 -0
  133. package/dist/generated/types/models/design-system-grid.js +1 -0
  134. package/dist/generated/types/models/design-system-inputs.d.ts +26 -0
  135. package/dist/generated/types/models/design-system-inputs.d.ts.map +1 -0
  136. package/dist/generated/types/models/design-system-inputs.js +1 -0
  137. package/dist/generated/types/models/design-system-links.d.ts +16 -0
  138. package/dist/generated/types/models/design-system-links.d.ts.map +1 -0
  139. package/dist/generated/types/models/design-system-links.js +1 -0
  140. package/dist/generated/types/models/design-system-shadows.d.ts +20 -0
  141. package/dist/generated/types/models/design-system-shadows.d.ts.map +1 -0
  142. package/dist/generated/types/models/design-system-shadows.js +1 -0
  143. package/dist/generated/types/models/design-system-spacing.d.ts +13 -0
  144. package/dist/generated/types/models/design-system-spacing.d.ts.map +1 -0
  145. package/dist/generated/types/models/design-system-spacing.js +1 -0
  146. package/dist/generated/types/models/design-system-transitions.d.ts +15 -0
  147. package/dist/generated/types/models/design-system-transitions.d.ts.map +1 -0
  148. package/dist/generated/types/models/design-system-transitions.js +1 -0
  149. package/dist/generated/types/models/design-system-typography.d.ts +21 -0
  150. package/dist/generated/types/models/design-system-typography.d.ts.map +1 -0
  151. package/dist/generated/types/models/design-system-typography.js +1 -0
  152. package/dist/generated/types/models/design-system.d.ts +138 -0
  153. package/dist/generated/types/models/design-system.d.ts.map +1 -0
  154. package/dist/generated/types/models/design-system.js +64 -0
  155. package/dist/generated/types/models/experiment.d.ts +57 -0
  156. package/dist/generated/types/models/experiment.d.ts.map +1 -0
  157. package/dist/generated/types/models/experiment.js +55 -0
  158. package/dist/generated/types/models/font-file.d.ts +16 -0
  159. package/dist/generated/types/models/font-file.d.ts.map +1 -0
  160. package/dist/generated/types/models/font-file.js +1 -0
  161. package/dist/generated/types/models/index.d.ts +38 -0
  162. package/dist/generated/types/models/index.d.ts.map +1 -0
  163. package/dist/generated/types/models/index.js +12 -0
  164. package/dist/generated/types/models/integration.d.ts +32 -0
  165. package/dist/generated/types/models/integration.d.ts.map +1 -0
  166. package/dist/generated/types/models/integration.js +26 -0
  167. package/dist/generated/types/models/route-tree.d.ts +43 -0
  168. package/dist/generated/types/models/route-tree.d.ts.map +1 -0
  169. package/dist/generated/types/models/route-tree.js +39 -0
  170. package/dist/generated/types/models/segment.d.ts +57 -0
  171. package/dist/generated/types/models/segment.d.ts.map +1 -0
  172. package/dist/generated/types/models/segment.js +55 -0
  173. package/dist/generated/types/models/shopify-collection.d.ts +32 -0
  174. package/dist/generated/types/models/shopify-collection.d.ts.map +1 -0
  175. package/dist/generated/types/models/shopify-collection.js +26 -0
  176. package/dist/generated/types/models/shopify-market.d.ts +32 -0
  177. package/dist/generated/types/models/shopify-market.d.ts.map +1 -0
  178. package/dist/generated/types/models/shopify-market.js +26 -0
  179. package/dist/generated/types/models/shopify-product.d.ts +32 -0
  180. package/dist/generated/types/models/shopify-product.d.ts.map +1 -0
  181. package/dist/generated/types/models/shopify-product.js +26 -0
  182. package/dist/generated/types/models/shopify-variant.d.ts +32 -0
  183. package/dist/generated/types/models/shopify-variant.d.ts.map +1 -0
  184. package/dist/generated/types/models/shopify-variant.js +26 -0
  185. package/dist/generated/types/models/template.d.ts +53 -0
  186. package/dist/generated/types/models/template.d.ts.map +1 -0
  187. package/dist/generated/types/models/template.js +40 -0
  188. package/dist/generated/types/models/typography-variant.d.ts +15 -0
  189. package/dist/generated/types/models/typography-variant.d.ts.map +1 -0
  190. package/dist/generated/types/models/typography-variant.js +1 -0
  191. package/dist/generated/types/scalars.d.ts +56 -0
  192. package/dist/generated/types/scalars.d.ts.map +1 -0
  193. package/dist/generated/types/scalars.js +6 -0
  194. package/dist/generators/admin/index.d.ts +32 -0
  195. package/dist/generators/admin/index.d.ts.map +1 -0
  196. package/dist/generators/admin/index.js +219 -0
  197. package/dist/generators/admin/mutations.d.ts +23 -0
  198. package/dist/generators/admin/mutations.d.ts.map +1 -0
  199. package/dist/generators/admin/mutations.js +424 -0
  200. package/dist/generators/admin/queries.d.ts +20 -0
  201. package/dist/generators/admin/queries.d.ts.map +1 -0
  202. package/dist/generators/admin/queries.js +476 -0
  203. package/dist/generators/admin/types.d.ts +28 -0
  204. package/dist/generators/admin/types.d.ts.map +1 -0
  205. package/dist/generators/admin/types.js +254 -0
  206. package/dist/generators/cms/index.d.ts +29 -0
  207. package/dist/generators/cms/index.d.ts.map +1 -0
  208. package/dist/generators/cms/index.js +126 -0
  209. package/dist/generators/cms/route.d.ts +27 -0
  210. package/dist/generators/cms/route.d.ts.map +1 -0
  211. package/dist/generators/cms/route.js +409 -0
  212. package/dist/generators/cms/types.d.ts +15 -0
  213. package/dist/generators/cms/types.d.ts.map +1 -0
  214. package/dist/generators/cms/types.js +137 -0
  215. package/dist/generators/contexts/index.d.ts +25 -0
  216. package/dist/generators/contexts/index.d.ts.map +1 -0
  217. package/dist/generators/contexts/index.js +591 -0
  218. package/dist/generators/documents/data-models.d.ts +6 -0
  219. package/dist/generators/documents/data-models.d.ts.map +1 -0
  220. package/dist/generators/documents/data-models.js +61 -0
  221. package/dist/generators/documents/entity-models.d.ts +7 -0
  222. package/dist/generators/documents/entity-models.d.ts.map +1 -0
  223. package/dist/generators/documents/entity-models.js +87 -0
  224. package/dist/generators/documents/workflows.d.ts +11 -0
  225. package/dist/generators/documents/workflows.d.ts.map +1 -0
  226. package/dist/generators/documents/workflows.js +101 -0
  227. package/dist/generators/filters/index.d.ts +16 -0
  228. package/dist/generators/filters/index.d.ts.map +1 -0
  229. package/dist/generators/filters/index.js +384 -0
  230. package/dist/generators/hooks/agnostic.d.ts +16 -0
  231. package/dist/generators/hooks/agnostic.d.ts.map +1 -0
  232. package/dist/generators/hooks/agnostic.js +248 -0
  233. package/dist/generators/hooks/index.d.ts +9 -0
  234. package/dist/generators/hooks/index.d.ts.map +1 -0
  235. package/dist/generators/hooks/index.js +8 -0
  236. package/dist/generators/hooks/react.d.ts +16 -0
  237. package/dist/generators/hooks/react.d.ts.map +1 -0
  238. package/dist/generators/hooks/react.js +394 -0
  239. package/dist/generators/hooks/remix.d.ts +16 -0
  240. package/dist/generators/hooks/remix.d.ts.map +1 -0
  241. package/dist/generators/hooks/remix.js +349 -0
  242. package/dist/generators/hooks/workflows.d.ts +23 -0
  243. package/dist/generators/hooks/workflows.d.ts.map +1 -0
  244. package/dist/generators/hooks/workflows.js +312 -0
  245. package/dist/generators/resolve/index.d.ts +13 -0
  246. package/dist/generators/resolve/index.d.ts.map +1 -0
  247. package/dist/generators/resolve/index.js +13 -0
  248. package/dist/generators/resolve/platform.d.ts +29 -0
  249. package/dist/generators/resolve/platform.d.ts.map +1 -0
  250. package/dist/generators/resolve/platform.js +479 -0
  251. package/dist/generators/types/config.d.ts +7 -0
  252. package/dist/generators/types/config.d.ts.map +1 -0
  253. package/dist/generators/types/config.js +113 -0
  254. package/dist/generators/types/data-models.d.ts +10 -0
  255. package/dist/generators/types/data-models.d.ts.map +1 -0
  256. package/dist/generators/types/data-models.js +100 -0
  257. package/dist/generators/types/entity-models.d.ts +13 -0
  258. package/dist/generators/types/entity-models.d.ts.map +1 -0
  259. package/dist/generators/types/entity-models.js +241 -0
  260. package/dist/generators/types/field-types.d.ts +9 -0
  261. package/dist/generators/types/field-types.d.ts.map +1 -0
  262. package/dist/generators/types/field-types.js +651 -0
  263. package/dist/generators/types/hooks.d.ts +7 -0
  264. package/dist/generators/types/hooks.d.ts.map +1 -0
  265. package/dist/generators/types/hooks.js +132 -0
  266. package/dist/generators/types/scalars.d.ts +6 -0
  267. package/dist/generators/types/scalars.d.ts.map +1 -0
  268. package/dist/generators/types/scalars.js +68 -0
  269. package/dist/generators/types/user-details.d.ts +6 -0
  270. package/dist/generators/types/user-details.d.ts.map +1 -0
  271. package/dist/generators/types/user-details.js +60 -0
  272. package/dist/generators/types/workflows.d.ts +15 -0
  273. package/dist/generators/types/workflows.d.ts.map +1 -0
  274. package/dist/generators/types/workflows.js +163 -0
  275. package/dist/graphql/generated/gql.d.ts +47 -0
  276. package/dist/graphql/generated/gql.d.ts.map +1 -0
  277. package/dist/graphql/generated/gql.js +10 -0
  278. package/dist/graphql/generated/graphql.d.ts +8455 -0
  279. package/dist/graphql/generated/graphql.d.ts.map +1 -0
  280. package/dist/graphql/generated/graphql.js +573 -0
  281. package/dist/graphql/generated/index.d.ts +2 -0
  282. package/dist/graphql/generated/index.d.ts.map +1 -0
  283. package/dist/graphql/generated/index.js +1 -0
  284. package/dist/index.d.ts +13 -0
  285. package/dist/index.d.ts.map +1 -0
  286. package/dist/index.js +9 -0
  287. package/dist/schema/define-entity-model.d.ts +122 -0
  288. package/dist/schema/define-entity-model.d.ts.map +1 -0
  289. package/dist/schema/define-entity-model.js +59 -0
  290. package/dist/schema/schema-loader.d.ts +10 -0
  291. package/dist/schema/schema-loader.d.ts.map +1 -0
  292. package/dist/schema/schema-loader.js +91 -0
  293. package/dist/utils/field-mapping.d.ts +83 -0
  294. package/dist/utils/field-mapping.d.ts.map +1 -0
  295. package/dist/utils/field-mapping.js +334 -0
  296. package/dist/writer/write-files.d.ts +12 -0
  297. package/dist/writer/write-files.d.ts.map +1 -0
  298. package/dist/writer/write-files.js +35 -0
  299. package/package.json +70 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Agnostic Hooks Generator
3
+ *
4
+ * Generates framework-agnostic typed helper functions that parse
5
+ * the generic entity API responses into strongly typed data.
6
+ */
7
+ import { toPascalCase, sanitizeFieldName } from '../../utils/field-mapping.js';
8
+ /**
9
+ * Map metadata field type to TypeScript type
10
+ */
11
+ function getMetadataValueType(fieldType) {
12
+ switch (fieldType) {
13
+ case 'string':
14
+ return 'string';
15
+ case 'number':
16
+ return 'number';
17
+ case 'boolean':
18
+ return 'boolean';
19
+ case 'date':
20
+ return 'string'; // ISO date string from JSON
21
+ case 'array':
22
+ return 'unknown[]';
23
+ case 'object':
24
+ return 'Record<string, unknown>';
25
+ default:
26
+ return 'unknown';
27
+ }
28
+ }
29
+ /**
30
+ * Build a nested TypeScript interface from dot-notation paths
31
+ */
32
+ function buildNestedMetadataType(fields, depth = 0) {
33
+ // Group fields by their first path segment
34
+ const groups = new Map();
35
+ const directFields = [];
36
+ for (const field of fields) {
37
+ const parts = field.key.split('.');
38
+ if (parts.length === 1) {
39
+ directFields.push(field);
40
+ }
41
+ else {
42
+ const firstPart = parts[0];
43
+ const remaining = {
44
+ ...field,
45
+ key: parts.slice(1).join('.'),
46
+ };
47
+ if (!groups.has(firstPart)) {
48
+ groups.set(firstPart, []);
49
+ }
50
+ groups.get(firstPart).push(remaining);
51
+ }
52
+ }
53
+ const indent = ' '.repeat(depth + 1);
54
+ const lines = [];
55
+ // Add direct fields
56
+ for (const field of directFields) {
57
+ const valueType = getMetadataValueType(field.type);
58
+ const fieldName = sanitizeFieldName(field.key);
59
+ const optional = field.required ? '' : '?';
60
+ lines.push(`${indent}${fieldName}${optional}: ${valueType};`);
61
+ }
62
+ // Add nested groups
63
+ for (const [groupName, groupFields] of groups) {
64
+ const nestedType = buildNestedMetadataType(groupFields, depth + 1);
65
+ if (nestedType !== '{}') {
66
+ const fieldName = sanitizeFieldName(groupName);
67
+ lines.push(`${indent}${fieldName}?: ${nestedType}`);
68
+ }
69
+ }
70
+ if (lines.length === 0) {
71
+ return '{}';
72
+ }
73
+ const closeIndent = ' '.repeat(depth);
74
+ return `{\n${lines.join('\n')}\n${closeIndent}}`;
75
+ }
76
+ /**
77
+ * Generate metadata type interface for a model
78
+ */
79
+ function generateMetadataType(model) {
80
+ const metadataFields = model.metadataSchema?.fields;
81
+ if (!metadataFields || metadataFields.length === 0) {
82
+ return null;
83
+ }
84
+ return buildNestedMetadataType(metadataFields, 0);
85
+ }
86
+ /**
87
+ * Check if model is inline-only (no records mode)
88
+ */
89
+ function isInlineOnlyModel(model) {
90
+ return model.modes.inline && !model.modes.records;
91
+ }
92
+ /**
93
+ * Generate agnostic typed helpers for an entity model
94
+ */
95
+ export function generateAgnosticHooks(model, _allModels) {
96
+ // Skip inline-only models - they don't have standalone records
97
+ if (isInlineOnlyModel(model)) {
98
+ return '';
99
+ }
100
+ const typeName = toPascalCase(model.key);
101
+ const dataTypeName = `${typeName}Data`;
102
+ const filtersTypeName = `${typeName}Filters`;
103
+ const lines = [];
104
+ // File header
105
+ lines.push(`/**`);
106
+ lines.push(` * ${model.name} - Typed Helpers`);
107
+ lines.push(` *`);
108
+ lines.push(` * @generated by UniformGen - DO NOT EDIT MANUALLY`);
109
+ lines.push(` */`);
110
+ lines.push('');
111
+ // Imports
112
+ lines.push(`import type { ${dataTypeName} } from '../types/models/${model.key}.js';`);
113
+ lines.push(`import type { FilterInput, SortInput } from '../types/field-types.js';`);
114
+ lines.push(`import { type ${filtersTypeName}, build${typeName}Filters } from '../filters/${model.key}.js';`);
115
+ lines.push('');
116
+ // Re-export filter types for convenience
117
+ lines.push(`// Re-export filter types for convenience`);
118
+ lines.push(`export type { ${filtersTypeName} } from '../filters/${model.key}.js';`);
119
+ lines.push(`export { build${typeName}Filters } from '../filters/${model.key}.js';`);
120
+ lines.push(`export type { FilterInput, SortInput } from '../types/field-types.js';`);
121
+ lines.push('');
122
+ // Generate metadata type if model has metadataSchema
123
+ const metadataType = generateMetadataType(model);
124
+ const metadataTypeName = `${typeName}Metadata`;
125
+ if (metadataType) {
126
+ lines.push(`/** Typed metadata for ${model.name} */`);
127
+ lines.push(`export interface ${metadataTypeName} ${metadataType}`);
128
+ lines.push('');
129
+ }
130
+ // EntityRecord type (simplified version of what the API returns)
131
+ lines.push(`/** Entity record from the API */`);
132
+ lines.push(`export interface ${typeName}Record {`);
133
+ lines.push(` id: string;`);
134
+ lines.push(` modelKey: '${model.key}';`);
135
+ lines.push(` naturalKey?: string;`);
136
+ lines.push(` data: ${dataTypeName};`);
137
+ if (metadataType) {
138
+ lines.push(` metadata?: ${metadataTypeName};`);
139
+ }
140
+ else {
141
+ lines.push(` metadata?: Record<string, unknown>;`);
142
+ }
143
+ lines.push(` versionNumber: number;`);
144
+ lines.push(` publishedVersionNumber?: number;`);
145
+ lines.push(` status: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';`);
146
+ lines.push(` createdAt: string;`);
147
+ lines.push(` updatedAt: string;`);
148
+ lines.push(`}`);
149
+ lines.push('');
150
+ // Parse single entity
151
+ lines.push(`/**`);
152
+ lines.push(` * Parse a generic entity record into a typed ${model.name} record`);
153
+ lines.push(` */`);
154
+ lines.push(`export function parse${typeName}(entity: {`);
155
+ lines.push(` id: string;`);
156
+ lines.push(` modelKey: string;`);
157
+ lines.push(` naturalKey?: string;`);
158
+ lines.push(` data: unknown;`);
159
+ lines.push(` metadata?: unknown;`);
160
+ lines.push(` versionNumber: number;`);
161
+ lines.push(` publishedVersionNumber?: number;`);
162
+ lines.push(` status: string;`);
163
+ lines.push(` createdAt: string;`);
164
+ lines.push(` updatedAt: string;`);
165
+ lines.push(`}): ${typeName}Record {`);
166
+ lines.push(` return {`);
167
+ lines.push(` ...entity,`);
168
+ lines.push(` modelKey: '${model.key}',`);
169
+ lines.push(` data: entity.data as ${dataTypeName},`);
170
+ if (metadataType) {
171
+ lines.push(` metadata: entity.metadata as ${metadataTypeName},`);
172
+ }
173
+ else {
174
+ lines.push(` metadata: entity.metadata as Record<string, unknown>,`);
175
+ }
176
+ lines.push(` status: entity.status as 'DRAFT' | 'PUBLISHED' | 'ARCHIVED',`);
177
+ lines.push(` };`);
178
+ lines.push(`}`);
179
+ lines.push('');
180
+ // Parse list of entities
181
+ lines.push(`/**`);
182
+ lines.push(` * Parse a list of generic entity records into typed ${model.name} records`);
183
+ lines.push(` */`);
184
+ lines.push(`export function parse${typeName}List(entities: Array<{`);
185
+ lines.push(` id: string;`);
186
+ lines.push(` modelKey: string;`);
187
+ lines.push(` naturalKey?: string;`);
188
+ lines.push(` data: unknown;`);
189
+ lines.push(` metadata?: unknown;`);
190
+ lines.push(` versionNumber: number;`);
191
+ lines.push(` publishedVersionNumber?: number;`);
192
+ lines.push(` status: string;`);
193
+ lines.push(` createdAt: string;`);
194
+ lines.push(` updatedAt: string;`);
195
+ lines.push(`}>): ${typeName}Record[] {`);
196
+ lines.push(` return entities.map(parse${typeName});`);
197
+ lines.push(`}`);
198
+ lines.push('');
199
+ // Extract data helper
200
+ lines.push(`/**`);
201
+ lines.push(` * Extract typed data from a ${model.name} record`);
202
+ lines.push(` */`);
203
+ lines.push(`export function get${typeName}Data(entity: { data: unknown }): ${dataTypeName} {`);
204
+ lines.push(` return entity.data as ${dataTypeName};`);
205
+ lines.push(`}`);
206
+ lines.push('');
207
+ // Type guard
208
+ lines.push(`/**`);
209
+ lines.push(` * Type guard to check if an entity is a ${model.name}`);
210
+ lines.push(` */`);
211
+ lines.push(`export function is${typeName}(entity: { modelKey: string }): entity is ${typeName}Record {`);
212
+ lines.push(` return entity.modelKey === '${model.key}';`);
213
+ lines.push(`}`);
214
+ return lines.join('\n') + '\n';
215
+ }
216
+ /**
217
+ * Generate index file for agnostic hooks
218
+ */
219
+ export function generateAgnosticHooksIndex(models) {
220
+ const recordModels = models.filter((m) => m.modes.records);
221
+ let indexCode = `/**
222
+ * Typed Entity Helpers
223
+ *
224
+ * Framework-agnostic typed helpers for parsing entity API responses.
225
+ *
226
+ * @generated by UniformGen - DO NOT EDIT MANUALLY
227
+ */\n\n`;
228
+ // Re-export base types
229
+ indexCode += `// Re-export base filter and sort types\n`;
230
+ indexCode += `export type { FilterInput, SortInput } from '../types/field-types.js';\n\n`;
231
+ for (const model of recordModels) {
232
+ const typeName = toPascalCase(model.key);
233
+ const hasMetadata = model.metadataSchema?.fields && model.metadataSchema.fields.length > 0;
234
+ indexCode += `export {\n`;
235
+ indexCode += ` parse${typeName},\n`;
236
+ indexCode += ` parse${typeName}List,\n`;
237
+ indexCode += ` get${typeName}Data,\n`;
238
+ indexCode += ` is${typeName},\n`;
239
+ indexCode += ` type ${typeName}Record,\n`;
240
+ if (hasMetadata) {
241
+ indexCode += ` type ${typeName}Metadata,\n`;
242
+ }
243
+ indexCode += ` type ${typeName}Filters,\n`;
244
+ indexCode += ` build${typeName}Filters,\n`;
245
+ indexCode += `} from './${model.key}.js';\n`;
246
+ }
247
+ return indexCode;
248
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Hooks Generators
3
+ *
4
+ * Generates typed hooks/helpers based on the configured framework.
5
+ */
6
+ export { generateAgnosticHooks, generateAgnosticHooksIndex } from './agnostic.js';
7
+ export { generateReactHooks, generateReactHooksIndex } from './react.js';
8
+ export { generateRemixHooks, generateRemixHooksIndex } from './remix.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/generators/hooks/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hooks Generators
3
+ *
4
+ * Generates typed hooks/helpers based on the configured framework.
5
+ */
6
+ export { generateAgnosticHooks, generateAgnosticHooksIndex } from './agnostic.js';
7
+ export { generateReactHooks, generateReactHooksIndex } from './react.js';
8
+ export { generateRemixHooks, generateRemixHooksIndex } from './remix.js';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * React/Apollo Hooks Generator
3
+ *
4
+ * Generates typed React hooks using Apollo Client that wrap
5
+ * the generic entity API with strongly typed responses.
6
+ */
7
+ import type { EntityModel } from '../../fetcher/fetch-schemas.js';
8
+ /**
9
+ * Generate React/Apollo hooks for an entity model
10
+ */
11
+ export declare function generateReactHooks(model: EntityModel, _allModels: EntityModel[]): string;
12
+ /**
13
+ * Generate index file for React hooks
14
+ */
15
+ export declare function generateReactHooksIndex(models: EntityModel[]): string;
16
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../../src/generators/hooks/react.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,gCAAgC,CAAC;AAoGvF;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,WAAW,EAAE,GACxB,MAAM,CAqQR;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAuDrE"}
@@ -0,0 +1,394 @@
1
+ /**
2
+ * React/Apollo Hooks Generator
3
+ *
4
+ * Generates typed React hooks using Apollo Client that wrap
5
+ * the generic entity API with strongly typed responses.
6
+ */
7
+ import { toPascalCase, toCamelCase, sanitizeFieldName } from '../../utils/field-mapping.js';
8
+ /**
9
+ * Map metadata field type to TypeScript type
10
+ */
11
+ function getMetadataValueType(fieldType) {
12
+ switch (fieldType) {
13
+ case 'string':
14
+ return 'string';
15
+ case 'number':
16
+ return 'number';
17
+ case 'boolean':
18
+ return 'boolean';
19
+ case 'date':
20
+ return 'string'; // ISO date string from JSON
21
+ case 'array':
22
+ return 'unknown[]';
23
+ case 'object':
24
+ return 'Record<string, unknown>';
25
+ default:
26
+ return 'unknown';
27
+ }
28
+ }
29
+ /**
30
+ * Build a nested TypeScript interface from dot-notation paths
31
+ */
32
+ function buildNestedMetadataType(fields, depth = 0) {
33
+ // Group fields by their first path segment
34
+ const groups = new Map();
35
+ const directFields = [];
36
+ for (const field of fields) {
37
+ const parts = field.key.split('.');
38
+ if (parts.length === 1) {
39
+ directFields.push(field);
40
+ }
41
+ else {
42
+ const firstPart = parts[0];
43
+ const remaining = {
44
+ ...field,
45
+ key: parts.slice(1).join('.'),
46
+ };
47
+ if (!groups.has(firstPart)) {
48
+ groups.set(firstPart, []);
49
+ }
50
+ groups.get(firstPart).push(remaining);
51
+ }
52
+ }
53
+ const indent = ' '.repeat(depth + 1);
54
+ const lines = [];
55
+ // Add direct fields
56
+ for (const field of directFields) {
57
+ const valueType = getMetadataValueType(field.type);
58
+ const fieldName = sanitizeFieldName(field.key);
59
+ const optional = field.required ? '' : '?';
60
+ lines.push(`${indent}${fieldName}${optional}: ${valueType};`);
61
+ }
62
+ // Add nested groups
63
+ for (const [groupName, groupFields] of groups) {
64
+ const nestedType = buildNestedMetadataType(groupFields, depth + 1);
65
+ if (nestedType !== '{}') {
66
+ const fieldName = sanitizeFieldName(groupName);
67
+ lines.push(`${indent}${fieldName}?: ${nestedType}`);
68
+ }
69
+ }
70
+ if (lines.length === 0) {
71
+ return '{}';
72
+ }
73
+ const closeIndent = ' '.repeat(depth);
74
+ return `{\n${lines.join('\n')}\n${closeIndent}}`;
75
+ }
76
+ /**
77
+ * Generate metadata type interface for a model
78
+ */
79
+ function generateMetadataType(model) {
80
+ const metadataFields = model.metadataSchema?.fields;
81
+ if (!metadataFields || metadataFields.length === 0) {
82
+ return null;
83
+ }
84
+ return buildNestedMetadataType(metadataFields, 0);
85
+ }
86
+ /**
87
+ * Check if model is inline-only (no records mode)
88
+ */
89
+ function isInlineOnlyModel(model) {
90
+ return model.modes.inline && !model.modes.records;
91
+ }
92
+ /**
93
+ * Generate React/Apollo hooks for an entity model
94
+ */
95
+ export function generateReactHooks(model, _allModels) {
96
+ // Skip inline-only models - they don't have standalone records
97
+ if (isInlineOnlyModel(model)) {
98
+ return '';
99
+ }
100
+ const typeName = toPascalCase(model.key);
101
+ const dataTypeName = `${typeName}Data`;
102
+ const camelName = toCamelCase(model.key);
103
+ const filtersTypeName = `${typeName}Filters`;
104
+ const lines = [];
105
+ // File header
106
+ lines.push(`/**`);
107
+ lines.push(` * ${model.name} - React Hooks`);
108
+ lines.push(` *`);
109
+ lines.push(` * @generated by UniformGen - DO NOT EDIT MANUALLY`);
110
+ lines.push(` */`);
111
+ lines.push('');
112
+ // Imports
113
+ lines.push(`import { useQuery, useMutation, type QueryHookOptions, type MutationHookOptions } from '@apollo/client';`);
114
+ lines.push(`import {`);
115
+ lines.push(` Get${typeName}Document,`);
116
+ lines.push(` List${typeName}sDocument,`);
117
+ lines.push(` Create${typeName}Document,`);
118
+ lines.push(` Update${typeName}Document,`);
119
+ lines.push(` Delete${typeName}Document,`);
120
+ lines.push(` Publish${typeName}Document,`);
121
+ lines.push(` Unpublish${typeName}Document,`);
122
+ lines.push(`} from '../documents/${model.key}.js';`);
123
+ lines.push(`import type { ${dataTypeName} } from '../types/models/${model.key}.js';`);
124
+ lines.push(`import type { FilterInput, SortInput } from '../types/field-types.js';`);
125
+ lines.push(`import { type ${filtersTypeName}, build${typeName}Filters } from '../filters/${model.key}.js';`);
126
+ lines.push('');
127
+ // Re-export filter types for convenience
128
+ lines.push(`// Re-export filter types for convenience`);
129
+ lines.push(`export type { ${filtersTypeName} } from '../filters/${model.key}.js';`);
130
+ lines.push(`export { build${typeName}Filters } from '../filters/${model.key}.js';`);
131
+ lines.push('');
132
+ // Generate metadata type if model has metadataSchema
133
+ const metadataType = generateMetadataType(model);
134
+ const metadataTypeName = `${typeName}Metadata`;
135
+ if (metadataType) {
136
+ lines.push(`/** Typed metadata for ${model.name} */`);
137
+ lines.push(`export interface ${metadataTypeName} ${metadataType}`);
138
+ lines.push('');
139
+ }
140
+ // Typed record interface
141
+ lines.push(`/** Typed ${model.name} record */`);
142
+ lines.push(`export interface ${typeName}Record {`);
143
+ lines.push(` id: string;`);
144
+ lines.push(` modelKey: '${model.key}';`);
145
+ lines.push(` naturalKey?: string;`);
146
+ lines.push(` data: ${dataTypeName};`);
147
+ if (metadataType) {
148
+ lines.push(` metadata?: ${metadataTypeName};`);
149
+ }
150
+ else {
151
+ lines.push(` metadata?: Record<string, unknown>;`);
152
+ }
153
+ lines.push(` versionNumber: number;`);
154
+ lines.push(` publishedVersionNumber?: number;`);
155
+ lines.push(` status: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';`);
156
+ lines.push(` createdAt: string;`);
157
+ lines.push(` updatedAt: string;`);
158
+ lines.push(`}`);
159
+ lines.push('');
160
+ // List result interface
161
+ lines.push(`/** Typed list result */`);
162
+ lines.push(`export interface ${typeName}ListResult {`);
163
+ lines.push(` items: ${typeName}Record[];`);
164
+ lines.push(` total: number;`);
165
+ lines.push(` hasMore: boolean;`);
166
+ lines.push(`}`);
167
+ lines.push('');
168
+ // Parse helper
169
+ lines.push(`/** Parse raw entity to typed record */`);
170
+ lines.push(`function parse${typeName}(entity: any): ${typeName}Record {`);
171
+ lines.push(` return {`);
172
+ lines.push(` ...entity,`);
173
+ lines.push(` modelKey: '${model.key}',`);
174
+ lines.push(` data: entity.data as ${dataTypeName},`);
175
+ if (metadataType) {
176
+ lines.push(` metadata: entity.metadata as ${metadataTypeName},`);
177
+ }
178
+ else {
179
+ lines.push(` metadata: entity.metadata as Record<string, unknown>,`);
180
+ }
181
+ lines.push(` };`);
182
+ lines.push(`}`);
183
+ lines.push('');
184
+ // useGet hook
185
+ lines.push(`/**`);
186
+ lines.push(` * Fetch a single ${model.name} by ID`);
187
+ lines.push(` */`);
188
+ lines.push(`export function use${typeName}(`);
189
+ lines.push(` id: string,`);
190
+ lines.push(` options?: Omit<QueryHookOptions, 'variables'> & { preview?: boolean }`);
191
+ lines.push(`) {`);
192
+ lines.push(` const { preview, ...queryOptions } = options ?? {};`);
193
+ lines.push(` const result = useQuery(Get${typeName}Document, {`);
194
+ lines.push(` ...queryOptions,`);
195
+ lines.push(` variables: { id, preview },`);
196
+ lines.push(` });`);
197
+ lines.push('');
198
+ lines.push(` return {`);
199
+ lines.push(` ...result,`);
200
+ lines.push(` ${camelName}: result.data?.entity ? parse${typeName}(result.data.entity) : undefined,`);
201
+ lines.push(` };`);
202
+ lines.push(`}`);
203
+ lines.push('');
204
+ // useList hook with typed filters
205
+ lines.push(`/**`);
206
+ lines.push(` * Fetch a list of ${model.name} records`);
207
+ lines.push(` * `);
208
+ lines.push(` * @example`);
209
+ lines.push(` * // Using typed filters (recommended)`);
210
+ lines.push(` * const { ${camelName}s } = use${typeName}s({`);
211
+ lines.push(` * filters: { title: { contains: 'example' } },`);
212
+ lines.push(` * });`);
213
+ lines.push(` * `);
214
+ lines.push(` * @example`);
215
+ lines.push(` * // Using raw FilterInput array`);
216
+ lines.push(` * const { ${camelName}s } = use${typeName}s({`);
217
+ lines.push(` * rawFilters: [{ field: 'data.title', operator: 'contains', value: 'example' }],`);
218
+ lines.push(` * });`);
219
+ lines.push(` */`);
220
+ lines.push(`export function use${typeName}s(options?: {`);
221
+ lines.push(` limit?: number;`);
222
+ lines.push(` offset?: number;`);
223
+ lines.push(` /** Typed filters with autocomplete support */`);
224
+ lines.push(` filters?: ${filtersTypeName};`);
225
+ lines.push(` /** Raw filter array (use filters for type safety) */`);
226
+ lines.push(` rawFilters?: FilterInput[];`);
227
+ lines.push(` sort?: SortInput;`);
228
+ lines.push(` preview?: boolean;`);
229
+ lines.push(` queryOptions?: Omit<QueryHookOptions, 'variables'>;`);
230
+ lines.push(`}) {`);
231
+ lines.push(` const { queryOptions, filters, rawFilters, ...rest } = options ?? {};`);
232
+ lines.push(` `);
233
+ lines.push(` // Convert typed filters to FilterInput array`);
234
+ lines.push(` const apiFilters = filters ? build${typeName}Filters(filters) : rawFilters;`);
235
+ lines.push(` `);
236
+ lines.push(` const result = useQuery(List${typeName}sDocument, {`);
237
+ lines.push(` ...queryOptions,`);
238
+ lines.push(` variables: { ...rest, filters: apiFilters },`);
239
+ lines.push(` });`);
240
+ lines.push('');
241
+ lines.push(` const typedResult: ${typeName}ListResult | undefined = result.data?.entities`);
242
+ lines.push(` ? {`);
243
+ lines.push(` items: result.data.entities.items.map(parse${typeName}),`);
244
+ lines.push(` total: result.data.entities.total,`);
245
+ lines.push(` hasMore: result.data.entities.hasMore,`);
246
+ lines.push(` }`);
247
+ lines.push(` : undefined;`);
248
+ lines.push('');
249
+ lines.push(` return {`);
250
+ lines.push(` ...result,`);
251
+ lines.push(` ${camelName}s: typedResult,`);
252
+ lines.push(` };`);
253
+ lines.push(`}`);
254
+ lines.push('');
255
+ // useCreate mutation
256
+ lines.push(`/**`);
257
+ lines.push(` * Create a new ${model.name}`);
258
+ lines.push(` */`);
259
+ lines.push(`export function useCreate${typeName}(`);
260
+ lines.push(` options?: MutationHookOptions`);
261
+ lines.push(`) {`);
262
+ lines.push(` const [mutate, result] = useMutation(Create${typeName}Document, options);`);
263
+ lines.push('');
264
+ lines.push(` const create${typeName} = async (data: ${dataTypeName}) => {`);
265
+ lines.push(` const response = await mutate({ variables: { data } });`);
266
+ lines.push(` return response.data?.createEntity ? parse${typeName}(response.data.createEntity) : undefined;`);
267
+ lines.push(` };`);
268
+ lines.push('');
269
+ lines.push(` return [create${typeName}, result] as const;`);
270
+ lines.push(`}`);
271
+ lines.push('');
272
+ // useUpdate mutation
273
+ lines.push(`/**`);
274
+ lines.push(` * Update an existing ${model.name}`);
275
+ lines.push(` */`);
276
+ lines.push(`export function useUpdate${typeName}(`);
277
+ lines.push(` options?: MutationHookOptions`);
278
+ lines.push(`) {`);
279
+ lines.push(` const [mutate, result] = useMutation(Update${typeName}Document, options);`);
280
+ lines.push('');
281
+ lines.push(` const update${typeName} = async (id: string, data: Partial<${dataTypeName}>) => {`);
282
+ lines.push(` const response = await mutate({ variables: { id, data } });`);
283
+ lines.push(` return response.data?.updateEntity ? parse${typeName}(response.data.updateEntity) : undefined;`);
284
+ lines.push(` };`);
285
+ lines.push('');
286
+ lines.push(` return [update${typeName}, result] as const;`);
287
+ lines.push(`}`);
288
+ lines.push('');
289
+ // useDelete mutation
290
+ lines.push(`/**`);
291
+ lines.push(` * Delete a ${model.name}`);
292
+ lines.push(` */`);
293
+ lines.push(`export function useDelete${typeName}(`);
294
+ lines.push(` options?: MutationHookOptions`);
295
+ lines.push(`) {`);
296
+ lines.push(` const [mutate, result] = useMutation(Delete${typeName}Document, options);`);
297
+ lines.push('');
298
+ lines.push(` const delete${typeName} = async (id: string) => {`);
299
+ lines.push(` const response = await mutate({ variables: { id } });`);
300
+ lines.push(` return response.data?.deleteEntity;`);
301
+ lines.push(` };`);
302
+ lines.push('');
303
+ lines.push(` return [delete${typeName}, result] as const;`);
304
+ lines.push(`}`);
305
+ lines.push('');
306
+ // usePublish mutation
307
+ lines.push(`/**`);
308
+ lines.push(` * Publish a ${model.name}`);
309
+ lines.push(` */`);
310
+ lines.push(`export function usePublish${typeName}(`);
311
+ lines.push(` options?: MutationHookOptions`);
312
+ lines.push(`) {`);
313
+ lines.push(` const [mutate, result] = useMutation(Publish${typeName}Document, options);`);
314
+ lines.push('');
315
+ lines.push(` const publish${typeName} = async (id: string, versionId?: string) => {`);
316
+ lines.push(` const response = await mutate({ variables: { id, versionId } });`);
317
+ lines.push(` return response.data?.publishEntity ? parse${typeName}(response.data.publishEntity) : undefined;`);
318
+ lines.push(` };`);
319
+ lines.push('');
320
+ lines.push(` return [publish${typeName}, result] as const;`);
321
+ lines.push(`}`);
322
+ lines.push('');
323
+ // useUnpublish mutation
324
+ lines.push(`/**`);
325
+ lines.push(` * Unpublish a ${model.name}`);
326
+ lines.push(` */`);
327
+ lines.push(`export function useUnpublish${typeName}(`);
328
+ lines.push(` options?: MutationHookOptions`);
329
+ lines.push(`) {`);
330
+ lines.push(` const [mutate, result] = useMutation(Unpublish${typeName}Document, options);`);
331
+ lines.push('');
332
+ lines.push(` const unpublish${typeName} = async (id: string) => {`);
333
+ lines.push(` const response = await mutate({ variables: { id } });`);
334
+ lines.push(` return response.data?.unpublishEntity ? parse${typeName}(response.data.unpublishEntity) : undefined;`);
335
+ lines.push(` };`);
336
+ lines.push('');
337
+ lines.push(` return [unpublish${typeName}, result] as const;`);
338
+ lines.push(`}`);
339
+ return lines.join('\n') + '\n';
340
+ }
341
+ /**
342
+ * Generate index file for React hooks
343
+ */
344
+ export function generateReactHooksIndex(models) {
345
+ const recordModels = models.filter((m) => m.modes.records);
346
+ let indexCode = `/**
347
+ * Typed React Hooks
348
+ *
349
+ * Apollo Client hooks with strongly typed entity responses.
350
+ *
351
+ * @generated by UniformGen - DO NOT EDIT MANUALLY
352
+ */\n\n`;
353
+ // Re-export base types
354
+ indexCode += `// Re-export base filter and sort types\n`;
355
+ indexCode += `export type { FilterInput, SortInput } from '../types/field-types.js';\n\n`;
356
+ // Re-export context hooks and helpers
357
+ indexCode += `// Context hooks and helpers\n`;
358
+ indexCode += `export {\n`;
359
+ indexCode += ` useAvailableContexts,\n`;
360
+ indexCode += ` fetchAvailableContexts,\n`;
361
+ indexCode += ` isValidContextValue,\n`;
362
+ indexCode += ` getDefaultContextValue,\n`;
363
+ indexCode += ` getContextValues,\n`;
364
+ indexCode += ` GET_AVAILABLE_CONTEXTS,\n`;
365
+ indexCode += ` GET_CONTEXT_VALUES,\n`;
366
+ indexCode += ` type ContextDefinition,\n`;
367
+ indexCode += ` type ContextValue,\n`;
368
+ indexCode += ` type ContextWithValues,\n`;
369
+ indexCode += ` type TypedContexts,\n`;
370
+ indexCode += ` type ContextKey,\n`;
371
+ indexCode += ` CONTEXT_KEYS,\n`;
372
+ indexCode += `} from './contexts.js';\n\n`;
373
+ for (const model of recordModels) {
374
+ const typeName = toPascalCase(model.key);
375
+ const hasMetadata = model.metadataSchema?.fields && model.metadataSchema.fields.length > 0;
376
+ indexCode += `export {\n`;
377
+ indexCode += ` use${typeName},\n`;
378
+ indexCode += ` use${typeName}s,\n`;
379
+ indexCode += ` useCreate${typeName},\n`;
380
+ indexCode += ` useUpdate${typeName},\n`;
381
+ indexCode += ` useDelete${typeName},\n`;
382
+ indexCode += ` usePublish${typeName},\n`;
383
+ indexCode += ` useUnpublish${typeName},\n`;
384
+ indexCode += ` type ${typeName}Record,\n`;
385
+ if (hasMetadata) {
386
+ indexCode += ` type ${typeName}Metadata,\n`;
387
+ }
388
+ indexCode += ` type ${typeName}ListResult,\n`;
389
+ indexCode += ` type ${typeName}Filters,\n`;
390
+ indexCode += ` build${typeName}Filters,\n`;
391
+ indexCode += `} from './${model.key}.js';\n`;
392
+ }
393
+ return indexCode;
394
+ }