convex-cms 0.0.1

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 (379) hide show
  1. package/dist/cli/commands/admin.d.ts +16 -0
  2. package/dist/cli/commands/admin.d.ts.map +1 -0
  3. package/dist/cli/commands/admin.js +88 -0
  4. package/dist/cli/commands/admin.js.map +1 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +18 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/cli/utils/detectConvexUrl.d.ts +13 -0
  10. package/dist/cli/utils/detectConvexUrl.d.ts.map +1 -0
  11. package/dist/cli/utils/detectConvexUrl.js +48 -0
  12. package/dist/cli/utils/detectConvexUrl.js.map +1 -0
  13. package/dist/cli/utils/openBrowser.d.ts +7 -0
  14. package/dist/cli/utils/openBrowser.d.ts.map +1 -0
  15. package/dist/cli/utils/openBrowser.js +17 -0
  16. package/dist/cli/utils/openBrowser.js.map +1 -0
  17. package/dist/client/admin-config.d.ts +126 -0
  18. package/dist/client/admin-config.d.ts.map +1 -0
  19. package/dist/client/admin-config.js +117 -0
  20. package/dist/client/admin-config.js.map +1 -0
  21. package/dist/client/adminApi.d.ts +2273 -0
  22. package/dist/client/adminApi.d.ts.map +1 -0
  23. package/dist/client/adminApi.js +716 -0
  24. package/dist/client/adminApi.js.map +1 -0
  25. package/dist/client/agentTools.d.ts +933 -0
  26. package/dist/client/agentTools.d.ts.map +1 -0
  27. package/dist/client/agentTools.js +1004 -0
  28. package/dist/client/agentTools.js.map +1 -0
  29. package/dist/client/argTypes.d.ts +212 -0
  30. package/dist/client/argTypes.d.ts.map +1 -0
  31. package/dist/client/argTypes.js +5 -0
  32. package/dist/client/argTypes.js.map +1 -0
  33. package/dist/client/field-types.d.ts +55 -0
  34. package/dist/client/field-types.d.ts.map +1 -0
  35. package/dist/client/field-types.js +152 -0
  36. package/dist/client/field-types.js.map +1 -0
  37. package/dist/client/index.d.ts +189 -0
  38. package/dist/client/index.d.ts.map +1 -0
  39. package/dist/client/index.js +668 -0
  40. package/dist/client/index.js.map +1 -0
  41. package/dist/client/queryBuilder.d.ts +765 -0
  42. package/dist/client/queryBuilder.d.ts.map +1 -0
  43. package/dist/client/queryBuilder.js +970 -0
  44. package/dist/client/queryBuilder.js.map +1 -0
  45. package/dist/client/schema/codegen.d.ts +128 -0
  46. package/dist/client/schema/codegen.d.ts.map +1 -0
  47. package/dist/client/schema/codegen.js +318 -0
  48. package/dist/client/schema/codegen.js.map +1 -0
  49. package/dist/client/schema/defineContentType.d.ts +221 -0
  50. package/dist/client/schema/defineContentType.d.ts.map +1 -0
  51. package/dist/client/schema/defineContentType.js +380 -0
  52. package/dist/client/schema/defineContentType.js.map +1 -0
  53. package/dist/client/schema/index.d.ts +85 -0
  54. package/dist/client/schema/index.d.ts.map +1 -0
  55. package/dist/client/schema/index.js +92 -0
  56. package/dist/client/schema/index.js.map +1 -0
  57. package/dist/client/schema/schemaDrift.d.ts +199 -0
  58. package/dist/client/schema/schemaDrift.d.ts.map +1 -0
  59. package/dist/client/schema/schemaDrift.js +340 -0
  60. package/dist/client/schema/schemaDrift.js.map +1 -0
  61. package/dist/client/schema/typedClient.d.ts +401 -0
  62. package/dist/client/schema/typedClient.d.ts.map +1 -0
  63. package/dist/client/schema/typedClient.js +269 -0
  64. package/dist/client/schema/typedClient.js.map +1 -0
  65. package/dist/client/schema/types.d.ts +477 -0
  66. package/dist/client/schema/types.d.ts.map +1 -0
  67. package/dist/client/schema/types.js +39 -0
  68. package/dist/client/schema/types.js.map +1 -0
  69. package/dist/client/types.d.ts +449 -0
  70. package/dist/client/types.d.ts.map +1 -0
  71. package/dist/client/types.js +149 -0
  72. package/dist/client/types.js.map +1 -0
  73. package/dist/client/workflows.d.ts +51 -0
  74. package/dist/client/workflows.d.ts.map +1 -0
  75. package/dist/client/workflows.js +103 -0
  76. package/dist/client/workflows.js.map +1 -0
  77. package/dist/client/wrapper.d.ts +2198 -0
  78. package/dist/client/wrapper.d.ts.map +1 -0
  79. package/dist/client/wrapper.js +2651 -0
  80. package/dist/client/wrapper.js.map +1 -0
  81. package/dist/component/_generated/api.d.ts +124 -0
  82. package/dist/component/_generated/api.d.ts.map +1 -0
  83. package/dist/component/_generated/api.js +31 -0
  84. package/dist/component/_generated/api.js.map +1 -0
  85. package/dist/component/_generated/component.d.ts +4321 -0
  86. package/dist/component/_generated/component.d.ts.map +1 -0
  87. package/dist/component/_generated/component.js +11 -0
  88. package/dist/component/_generated/component.js.map +1 -0
  89. package/dist/component/_generated/dataModel.d.ts +46 -0
  90. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  91. package/dist/component/_generated/dataModel.js +11 -0
  92. package/dist/component/_generated/dataModel.js.map +1 -0
  93. package/dist/component/_generated/server.d.ts +121 -0
  94. package/dist/component/_generated/server.d.ts.map +1 -0
  95. package/dist/component/_generated/server.js +78 -0
  96. package/dist/component/_generated/server.js.map +1 -0
  97. package/dist/component/auditLog.d.ts +410 -0
  98. package/dist/component/auditLog.d.ts.map +1 -0
  99. package/dist/component/auditLog.js +607 -0
  100. package/dist/component/auditLog.js.map +1 -0
  101. package/dist/component/authorization.d.ts +323 -0
  102. package/dist/component/authorization.d.ts.map +1 -0
  103. package/dist/component/authorization.js +464 -0
  104. package/dist/component/authorization.js.map +1 -0
  105. package/dist/component/authorizationHooks.d.ts +184 -0
  106. package/dist/component/authorizationHooks.d.ts.map +1 -0
  107. package/dist/component/authorizationHooks.js +521 -0
  108. package/dist/component/authorizationHooks.js.map +1 -0
  109. package/dist/component/bulkOperations.d.ts +200 -0
  110. package/dist/component/bulkOperations.d.ts.map +1 -0
  111. package/dist/component/bulkOperations.js +568 -0
  112. package/dist/component/bulkOperations.js.map +1 -0
  113. package/dist/component/contentEntries.d.ts +719 -0
  114. package/dist/component/contentEntries.d.ts.map +1 -0
  115. package/dist/component/contentEntries.js +1617 -0
  116. package/dist/component/contentEntries.js.map +1 -0
  117. package/dist/component/contentEntryMutations.d.ts +505 -0
  118. package/dist/component/contentEntryMutations.d.ts.map +1 -0
  119. package/dist/component/contentEntryMutations.js +1009 -0
  120. package/dist/component/contentEntryMutations.js.map +1 -0
  121. package/dist/component/contentEntryValidation.d.ts +115 -0
  122. package/dist/component/contentEntryValidation.d.ts.map +1 -0
  123. package/dist/component/contentEntryValidation.js +546 -0
  124. package/dist/component/contentEntryValidation.js.map +1 -0
  125. package/dist/component/contentLock.d.ts +328 -0
  126. package/dist/component/contentLock.d.ts.map +1 -0
  127. package/dist/component/contentLock.js +471 -0
  128. package/dist/component/contentLock.js.map +1 -0
  129. package/dist/component/contentTypeMigration.d.ts +411 -0
  130. package/dist/component/contentTypeMigration.d.ts.map +1 -0
  131. package/dist/component/contentTypeMigration.js +805 -0
  132. package/dist/component/contentTypeMigration.js.map +1 -0
  133. package/dist/component/contentTypeMutations.d.ts +975 -0
  134. package/dist/component/contentTypeMutations.d.ts.map +1 -0
  135. package/dist/component/contentTypeMutations.js +768 -0
  136. package/dist/component/contentTypeMutations.js.map +1 -0
  137. package/dist/component/contentTypes.d.ts +538 -0
  138. package/dist/component/contentTypes.d.ts.map +1 -0
  139. package/dist/component/contentTypes.js +304 -0
  140. package/dist/component/contentTypes.js.map +1 -0
  141. package/dist/component/convex.config.d.ts +42 -0
  142. package/dist/component/convex.config.d.ts.map +1 -0
  143. package/dist/component/convex.config.js +43 -0
  144. package/dist/component/convex.config.js.map +1 -0
  145. package/dist/component/documentTypes.d.ts +186 -0
  146. package/dist/component/documentTypes.d.ts.map +1 -0
  147. package/dist/component/documentTypes.js +23 -0
  148. package/dist/component/documentTypes.js.map +1 -0
  149. package/dist/component/eventEmitter.d.ts +281 -0
  150. package/dist/component/eventEmitter.d.ts.map +1 -0
  151. package/dist/component/eventEmitter.js +300 -0
  152. package/dist/component/eventEmitter.js.map +1 -0
  153. package/dist/component/exportImport.d.ts +1120 -0
  154. package/dist/component/exportImport.d.ts.map +1 -0
  155. package/dist/component/exportImport.js +931 -0
  156. package/dist/component/exportImport.js.map +1 -0
  157. package/dist/component/index.d.ts +28 -0
  158. package/dist/component/index.d.ts.map +1 -0
  159. package/dist/component/index.js +142 -0
  160. package/dist/component/index.js.map +1 -0
  161. package/dist/component/lib/deepReferenceResolver.d.ts +252 -0
  162. package/dist/component/lib/deepReferenceResolver.d.ts.map +1 -0
  163. package/dist/component/lib/deepReferenceResolver.js +601 -0
  164. package/dist/component/lib/deepReferenceResolver.js.map +1 -0
  165. package/dist/component/lib/errors.d.ts +306 -0
  166. package/dist/component/lib/errors.d.ts.map +1 -0
  167. package/dist/component/lib/errors.js +407 -0
  168. package/dist/component/lib/errors.js.map +1 -0
  169. package/dist/component/lib/index.d.ts +10 -0
  170. package/dist/component/lib/index.d.ts.map +1 -0
  171. package/dist/component/lib/index.js +33 -0
  172. package/dist/component/lib/index.js.map +1 -0
  173. package/dist/component/lib/mediaReferenceResolver.d.ts +217 -0
  174. package/dist/component/lib/mediaReferenceResolver.d.ts.map +1 -0
  175. package/dist/component/lib/mediaReferenceResolver.js +326 -0
  176. package/dist/component/lib/mediaReferenceResolver.js.map +1 -0
  177. package/dist/component/lib/metadataExtractor.d.ts +245 -0
  178. package/dist/component/lib/metadataExtractor.d.ts.map +1 -0
  179. package/dist/component/lib/metadataExtractor.js +548 -0
  180. package/dist/component/lib/metadataExtractor.js.map +1 -0
  181. package/dist/component/lib/mutationAuth.d.ts +95 -0
  182. package/dist/component/lib/mutationAuth.d.ts.map +1 -0
  183. package/dist/component/lib/mutationAuth.js +146 -0
  184. package/dist/component/lib/mutationAuth.js.map +1 -0
  185. package/dist/component/lib/queries.d.ts +17 -0
  186. package/dist/component/lib/queries.d.ts.map +1 -0
  187. package/dist/component/lib/queries.js +49 -0
  188. package/dist/component/lib/queries.js.map +1 -0
  189. package/dist/component/lib/ragContentChunker.d.ts +423 -0
  190. package/dist/component/lib/ragContentChunker.d.ts.map +1 -0
  191. package/dist/component/lib/ragContentChunker.js +897 -0
  192. package/dist/component/lib/ragContentChunker.js.map +1 -0
  193. package/dist/component/lib/referenceResolver.d.ts +175 -0
  194. package/dist/component/lib/referenceResolver.d.ts.map +1 -0
  195. package/dist/component/lib/referenceResolver.js +293 -0
  196. package/dist/component/lib/referenceResolver.js.map +1 -0
  197. package/dist/component/lib/slugGenerator.d.ts +71 -0
  198. package/dist/component/lib/slugGenerator.d.ts.map +1 -0
  199. package/dist/component/lib/slugGenerator.js +207 -0
  200. package/dist/component/lib/slugGenerator.js.map +1 -0
  201. package/dist/component/lib/slugUniqueness.d.ts +131 -0
  202. package/dist/component/lib/slugUniqueness.d.ts.map +1 -0
  203. package/dist/component/lib/slugUniqueness.js +229 -0
  204. package/dist/component/lib/slugUniqueness.js.map +1 -0
  205. package/dist/component/lib/softDelete.d.ts +18 -0
  206. package/dist/component/lib/softDelete.d.ts.map +1 -0
  207. package/dist/component/lib/softDelete.js +29 -0
  208. package/dist/component/lib/softDelete.js.map +1 -0
  209. package/dist/component/localeFallbackChain.d.ts +410 -0
  210. package/dist/component/localeFallbackChain.d.ts.map +1 -0
  211. package/dist/component/localeFallbackChain.js +467 -0
  212. package/dist/component/localeFallbackChain.js.map +1 -0
  213. package/dist/component/localeFields.d.ts +508 -0
  214. package/dist/component/localeFields.d.ts.map +1 -0
  215. package/dist/component/localeFields.js +592 -0
  216. package/dist/component/localeFields.js.map +1 -0
  217. package/dist/component/mediaAssetMutations.d.ts +235 -0
  218. package/dist/component/mediaAssetMutations.d.ts.map +1 -0
  219. package/dist/component/mediaAssetMutations.js +558 -0
  220. package/dist/component/mediaAssetMutations.js.map +1 -0
  221. package/dist/component/mediaAssets.d.ts +168 -0
  222. package/dist/component/mediaAssets.d.ts.map +1 -0
  223. package/dist/component/mediaAssets.js +618 -0
  224. package/dist/component/mediaAssets.js.map +1 -0
  225. package/dist/component/mediaFolderMutations.d.ts +642 -0
  226. package/dist/component/mediaFolderMutations.d.ts.map +1 -0
  227. package/dist/component/mediaFolderMutations.js +849 -0
  228. package/dist/component/mediaFolderMutations.js.map +1 -0
  229. package/dist/component/mediaUploadMutations.d.ts +136 -0
  230. package/dist/component/mediaUploadMutations.d.ts.map +1 -0
  231. package/dist/component/mediaUploadMutations.js +205 -0
  232. package/dist/component/mediaUploadMutations.js.map +1 -0
  233. package/dist/component/mediaVariantMutations.d.ts +468 -0
  234. package/dist/component/mediaVariantMutations.d.ts.map +1 -0
  235. package/dist/component/mediaVariantMutations.js +737 -0
  236. package/dist/component/mediaVariantMutations.js.map +1 -0
  237. package/dist/component/mediaVariants.d.ts +525 -0
  238. package/dist/component/mediaVariants.d.ts.map +1 -0
  239. package/dist/component/mediaVariants.js +661 -0
  240. package/dist/component/mediaVariants.js.map +1 -0
  241. package/dist/component/ragContentIndexer.d.ts +595 -0
  242. package/dist/component/ragContentIndexer.d.ts.map +1 -0
  243. package/dist/component/ragContentIndexer.js +794 -0
  244. package/dist/component/ragContentIndexer.js.map +1 -0
  245. package/dist/component/rateLimitHooks.d.ts +266 -0
  246. package/dist/component/rateLimitHooks.d.ts.map +1 -0
  247. package/dist/component/rateLimitHooks.js +412 -0
  248. package/dist/component/rateLimitHooks.js.map +1 -0
  249. package/dist/component/roles.d.ts +649 -0
  250. package/dist/component/roles.d.ts.map +1 -0
  251. package/dist/component/roles.js +884 -0
  252. package/dist/component/roles.js.map +1 -0
  253. package/dist/component/scheduledPublish.d.ts +182 -0
  254. package/dist/component/scheduledPublish.d.ts.map +1 -0
  255. package/dist/component/scheduledPublish.js +304 -0
  256. package/dist/component/scheduledPublish.js.map +1 -0
  257. package/dist/component/schema.d.ts +4114 -0
  258. package/dist/component/schema.d.ts.map +1 -0
  259. package/dist/component/schema.js +469 -0
  260. package/dist/component/schema.js.map +1 -0
  261. package/dist/component/taxonomies.d.ts +476 -0
  262. package/dist/component/taxonomies.d.ts.map +1 -0
  263. package/dist/component/taxonomies.js +785 -0
  264. package/dist/component/taxonomies.js.map +1 -0
  265. package/dist/component/taxonomyMutations.d.ts +206 -0
  266. package/dist/component/taxonomyMutations.d.ts.map +1 -0
  267. package/dist/component/taxonomyMutations.js +1001 -0
  268. package/dist/component/taxonomyMutations.js.map +1 -0
  269. package/dist/component/trash.d.ts +265 -0
  270. package/dist/component/trash.d.ts.map +1 -0
  271. package/dist/component/trash.js +621 -0
  272. package/dist/component/trash.js.map +1 -0
  273. package/dist/component/types.d.ts +4 -0
  274. package/dist/component/types.d.ts.map +1 -0
  275. package/dist/component/types.js +2 -0
  276. package/dist/component/types.js.map +1 -0
  277. package/dist/component/userContext.d.ts +508 -0
  278. package/dist/component/userContext.d.ts.map +1 -0
  279. package/dist/component/userContext.js +615 -0
  280. package/dist/component/userContext.js.map +1 -0
  281. package/dist/component/validation.d.ts +387 -0
  282. package/dist/component/validation.d.ts.map +1 -0
  283. package/dist/component/validation.js +1052 -0
  284. package/dist/component/validation.js.map +1 -0
  285. package/dist/component/validators.d.ts +4645 -0
  286. package/dist/component/validators.d.ts.map +1 -0
  287. package/dist/component/validators.js +641 -0
  288. package/dist/component/validators.js.map +1 -0
  289. package/dist/component/versionMutations.d.ts +216 -0
  290. package/dist/component/versionMutations.d.ts.map +1 -0
  291. package/dist/component/versionMutations.js +321 -0
  292. package/dist/component/versionMutations.js.map +1 -0
  293. package/dist/component/webhookTrigger.d.ts +770 -0
  294. package/dist/component/webhookTrigger.d.ts.map +1 -0
  295. package/dist/component/webhookTrigger.js +1413 -0
  296. package/dist/component/webhookTrigger.js.map +1 -0
  297. package/dist/react/index.d.ts +316 -0
  298. package/dist/react/index.d.ts.map +1 -0
  299. package/dist/react/index.js +558 -0
  300. package/dist/react/index.js.map +1 -0
  301. package/dist/test.d.ts +2230 -0
  302. package/dist/test.d.ts.map +1 -0
  303. package/dist/test.js +1107 -0
  304. package/dist/test.js.map +1 -0
  305. package/package.json +95 -0
  306. package/src/cli/commands/admin.ts +104 -0
  307. package/src/cli/index.ts +21 -0
  308. package/src/cli/utils/detectConvexUrl.ts +54 -0
  309. package/src/cli/utils/openBrowser.ts +16 -0
  310. package/src/client/admin-config.ts +138 -0
  311. package/src/client/adminApi.ts +942 -0
  312. package/src/client/agentTools.ts +1311 -0
  313. package/src/client/argTypes.ts +316 -0
  314. package/src/client/field-types.ts +187 -0
  315. package/src/client/index.ts +1301 -0
  316. package/src/client/queryBuilder.ts +1100 -0
  317. package/src/client/schema/codegen.ts +500 -0
  318. package/src/client/schema/defineContentType.ts +501 -0
  319. package/src/client/schema/index.ts +169 -0
  320. package/src/client/schema/schemaDrift.ts +574 -0
  321. package/src/client/schema/typedClient.ts +688 -0
  322. package/src/client/schema/types.ts +666 -0
  323. package/src/client/types.ts +723 -0
  324. package/src/client/workflows.ts +141 -0
  325. package/src/client/wrapper.ts +4304 -0
  326. package/src/component/_generated/api.ts +140 -0
  327. package/src/component/_generated/component.ts +5029 -0
  328. package/src/component/_generated/dataModel.ts +60 -0
  329. package/src/component/_generated/server.ts +156 -0
  330. package/src/component/authorization.ts +647 -0
  331. package/src/component/authorizationHooks.ts +668 -0
  332. package/src/component/bulkOperations.ts +687 -0
  333. package/src/component/contentEntries.ts +1976 -0
  334. package/src/component/contentEntryMutations.ts +1223 -0
  335. package/src/component/contentEntryValidation.ts +707 -0
  336. package/src/component/contentLock.ts +550 -0
  337. package/src/component/contentTypeMigration.ts +1064 -0
  338. package/src/component/contentTypeMutations.ts +969 -0
  339. package/src/component/contentTypes.ts +346 -0
  340. package/src/component/convex.config.ts +44 -0
  341. package/src/component/documentTypes.ts +240 -0
  342. package/src/component/eventEmitter.ts +485 -0
  343. package/src/component/exportImport.ts +1169 -0
  344. package/src/component/index.ts +491 -0
  345. package/src/component/lib/deepReferenceResolver.ts +999 -0
  346. package/src/component/lib/errors.ts +816 -0
  347. package/src/component/lib/index.ts +145 -0
  348. package/src/component/lib/mediaReferenceResolver.ts +495 -0
  349. package/src/component/lib/metadataExtractor.ts +792 -0
  350. package/src/component/lib/mutationAuth.ts +199 -0
  351. package/src/component/lib/queries.ts +79 -0
  352. package/src/component/lib/ragContentChunker.ts +1371 -0
  353. package/src/component/lib/referenceResolver.ts +430 -0
  354. package/src/component/lib/slugGenerator.ts +262 -0
  355. package/src/component/lib/slugUniqueness.ts +333 -0
  356. package/src/component/lib/softDelete.ts +44 -0
  357. package/src/component/localeFallbackChain.ts +673 -0
  358. package/src/component/localeFields.ts +896 -0
  359. package/src/component/mediaAssetMutations.ts +725 -0
  360. package/src/component/mediaAssets.ts +932 -0
  361. package/src/component/mediaFolderMutations.ts +1046 -0
  362. package/src/component/mediaUploadMutations.ts +224 -0
  363. package/src/component/mediaVariantMutations.ts +900 -0
  364. package/src/component/mediaVariants.ts +793 -0
  365. package/src/component/ragContentIndexer.ts +1067 -0
  366. package/src/component/rateLimitHooks.ts +572 -0
  367. package/src/component/roles.ts +1360 -0
  368. package/src/component/scheduledPublish.ts +358 -0
  369. package/src/component/schema.ts +617 -0
  370. package/src/component/taxonomies.ts +949 -0
  371. package/src/component/taxonomyMutations.ts +1210 -0
  372. package/src/component/trash.ts +724 -0
  373. package/src/component/userContext.ts +898 -0
  374. package/src/component/validation.ts +1388 -0
  375. package/src/component/validators.ts +949 -0
  376. package/src/component/versionMutations.ts +392 -0
  377. package/src/component/webhookTrigger.ts +1922 -0
  378. package/src/react/index.ts +898 -0
  379. package/src/test.ts +1580 -0
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Slug Generator Utility
3
+ *
4
+ * Generates URL-friendly slugs from content titles.
5
+ * Handles special characters, unicode, and ensures slug format consistency.
6
+ */
7
+ /**
8
+ * Default character replacements for common special characters and unicode
9
+ */
10
+ const DEFAULT_REPLACEMENTS = {
11
+ // German
12
+ ä: "ae",
13
+ ö: "oe",
14
+ ü: "ue",
15
+ ß: "ss",
16
+ Ä: "ae",
17
+ Ö: "oe",
18
+ Ü: "ue",
19
+ // French
20
+ à: "a",
21
+ â: "a",
22
+ ç: "c",
23
+ é: "e",
24
+ è: "e",
25
+ ê: "e",
26
+ ë: "e",
27
+ î: "i",
28
+ ï: "i",
29
+ ô: "o",
30
+ ù: "u",
31
+ û: "u",
32
+ ÿ: "y",
33
+ œ: "oe",
34
+ æ: "ae",
35
+ // Spanish
36
+ ñ: "n",
37
+ Ñ: "n",
38
+ // Polish
39
+ ą: "a",
40
+ Ą: "a",
41
+ ć: "c",
42
+ Ć: "c",
43
+ ę: "e",
44
+ Ę: "e",
45
+ ł: "l",
46
+ Ł: "l",
47
+ ń: "n",
48
+ Ń: "n",
49
+ ó: "o",
50
+ Ó: "o",
51
+ ś: "s",
52
+ Ś: "s",
53
+ ź: "z",
54
+ Ź: "z",
55
+ ż: "z",
56
+ Ż: "z",
57
+ // Nordic
58
+ å: "a",
59
+ Å: "a",
60
+ ø: "o",
61
+ Ø: "o",
62
+ // Other common
63
+ ð: "d",
64
+ þ: "th",
65
+ // Symbols
66
+ "&": "and",
67
+ "@": "at",
68
+ "#": "hash",
69
+ "%": "percent",
70
+ "+": "plus",
71
+ "=": "equals",
72
+ // Punctuation to remove (replaced with empty string to prevent separator)
73
+ "'": "",
74
+ "\u2018": "", // left single quote '
75
+ "\u2019": "", // right single quote '
76
+ "\u201C": "", // left double quote "
77
+ "\u201D": "", // right double quote "
78
+ };
79
+ /**
80
+ * Generates a URL-friendly slug from a given title string.
81
+ *
82
+ * @param title - The input string to convert to a slug
83
+ * @param options - Optional configuration for slug generation
84
+ * @returns A URL-friendly slug string
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * generateSlug("Hello World!") // "hello-world"
89
+ * generateSlug("Café & Restaurant") // "cafe-and-restaurant"
90
+ * generateSlug("日本語タイトル") // "ri-ben-yu-taitoru"
91
+ * generateSlug(" Multiple Spaces ") // "multiple-spaces"
92
+ * ```
93
+ */
94
+ export function generateSlug(title, options = {}) {
95
+ const { maxLength = 100, separator = "-", lowercase = true, customReplacements = {}, } = options;
96
+ if (!title || typeof title !== "string") {
97
+ return "";
98
+ }
99
+ // Merge custom replacements with defaults (custom takes precedence)
100
+ const replacements = { ...DEFAULT_REPLACEMENTS, ...customReplacements };
101
+ let slug = title.trim();
102
+ // Apply character replacements
103
+ for (const [char, replacement] of Object.entries(replacements)) {
104
+ slug = slug.split(char).join(replacement);
105
+ }
106
+ // Normalize unicode to decomposed form, then remove combining diacritical marks
107
+ slug = slug.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
108
+ // Convert to lowercase if requested
109
+ if (lowercase) {
110
+ slug = slug.toLowerCase();
111
+ }
112
+ // Replace any non-alphanumeric characters (except separator) with separator
113
+ // This regex keeps letters (including unicode letters after normalization), numbers
114
+ const separatorRegex = new RegExp(`[^a-z0-9${escapeRegex(separator)}]`, "gi");
115
+ slug = slug.replace(separatorRegex, separator);
116
+ // Collapse multiple consecutive separators into one
117
+ const multipleSeparatorRegex = new RegExp(`${escapeRegex(separator)}+`, "g");
118
+ slug = slug.replace(multipleSeparatorRegex, separator);
119
+ // Remove leading and trailing separators
120
+ const trimSeparatorRegex = new RegExp(`^${escapeRegex(separator)}|${escapeRegex(separator)}$`, "g");
121
+ slug = slug.replace(trimSeparatorRegex, "");
122
+ // Truncate to max length, but don't cut words in the middle
123
+ if (slug.length > maxLength) {
124
+ slug = truncateSlug(slug, maxLength, separator);
125
+ }
126
+ return slug;
127
+ }
128
+ /**
129
+ * Escapes special regex characters in a string
130
+ */
131
+ function escapeRegex(str) {
132
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
133
+ }
134
+ /**
135
+ * Truncates a slug to a maximum length without cutting words
136
+ */
137
+ function truncateSlug(slug, maxLength, separator) {
138
+ if (slug.length <= maxLength) {
139
+ return slug;
140
+ }
141
+ // Find the last separator before the max length
142
+ const truncated = slug.substring(0, maxLength);
143
+ const lastSeparatorIndex = truncated.lastIndexOf(separator);
144
+ if (lastSeparatorIndex > 0) {
145
+ return truncated.substring(0, lastSeparatorIndex);
146
+ }
147
+ // If no separator found, just truncate at maxLength
148
+ return truncated;
149
+ }
150
+ /**
151
+ * Validates if a string is a valid slug format
152
+ *
153
+ * @param slug - The string to validate
154
+ * @param separator - The separator character (default: '-')
155
+ * @returns True if the string is a valid slug
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * isValidSlug("hello-world") // true
160
+ * isValidSlug("Hello World") // false
161
+ * isValidSlug("hello--world") // false
162
+ * isValidSlug("-hello-world") // false
163
+ * ```
164
+ */
165
+ export function isValidSlug(slug, separator = "-") {
166
+ if (!slug || typeof slug !== "string") {
167
+ return false;
168
+ }
169
+ // Must be lowercase alphanumeric with single separators
170
+ const validSlugRegex = new RegExp(`^[a-z0-9]+(?:${escapeRegex(separator)}[a-z0-9]+)*$`);
171
+ return validSlugRegex.test(slug);
172
+ }
173
+ /**
174
+ * Generates a unique slug by appending a numeric suffix if needed.
175
+ * This is a helper that can be used with a uniqueness check function.
176
+ *
177
+ * @param baseSlug - The base slug to make unique
178
+ * @param isUnique - Async function that checks if a slug is unique
179
+ * @param maxAttempts - Maximum number of suffix attempts (default: 100)
180
+ * @returns A unique slug
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * const checkUnique = async (slug: string) => {
185
+ * return !(await db.query("entries").withSlug(slug).first());
186
+ * };
187
+ * const uniqueSlug = await generateUniqueSlug("hello-world", checkUnique);
188
+ * // Returns "hello-world" if unique, or "hello-world-1", "hello-world-2", etc.
189
+ * ```
190
+ */
191
+ export async function generateUniqueSlug(baseSlug, isUnique, maxAttempts = 100) {
192
+ // Check if base slug is already unique
193
+ if (await isUnique(baseSlug)) {
194
+ return baseSlug;
195
+ }
196
+ // Try appending numeric suffixes
197
+ for (let i = 1; i <= maxAttempts; i++) {
198
+ const candidateSlug = `${baseSlug}-${i}`;
199
+ if (await isUnique(candidateSlug)) {
200
+ return candidateSlug;
201
+ }
202
+ }
203
+ // Fallback: append timestamp if all numeric suffixes are taken
204
+ const timestamp = Date.now().toString(36);
205
+ return `${baseSlug}-${timestamp}`;
206
+ }
207
+ //# sourceMappingURL=slugGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugGenerator.js","sourceRoot":"","sources":["../../../src/component/lib/slugGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Slug Uniqueness Utilities
3
+ *
4
+ * Provides functions for checking slug uniqueness within content type scope
5
+ * and generating unique slugs with incremental suffixes when conflicts exist.
6
+ */
7
+ /**
8
+ * Options for slug uniqueness checking
9
+ */
10
+ export interface SlugUniquenessOptions {
11
+ /** Maximum number of suffix attempts before falling back to timestamp (default: 100) */
12
+ maxAttempts?: number;
13
+ /** Separator character used in slugs (default: '-') */
14
+ separator?: string;
15
+ /** ID of the current entry to exclude from uniqueness check (for updates) */
16
+ excludeEntryId?: string;
17
+ }
18
+ /**
19
+ * Result of a slug uniqueness check
20
+ */
21
+ export interface SlugCheckResult {
22
+ /** Whether the slug is unique */
23
+ isUnique: boolean;
24
+ /** The existing entry ID that has this slug (if not unique) */
25
+ existingEntryId?: string;
26
+ /** Suggested alternative slug if not unique */
27
+ suggestedSlug?: string;
28
+ }
29
+ /**
30
+ * Entry data structure for uniqueness checking
31
+ */
32
+ export interface SlugEntry {
33
+ /** The entry's unique identifier */
34
+ _id: string;
35
+ /** The entry's slug */
36
+ slug: string;
37
+ /** Soft delete timestamp (null/undefined if not deleted) */
38
+ deletedAt?: number | null;
39
+ }
40
+ /**
41
+ * Function type for querying existing entries by slug within a content type
42
+ */
43
+ export type SlugQueryFn = (slug: string) => Promise<SlugEntry | null>;
44
+ /**
45
+ * Function type for querying all entries with a slug prefix within a content type
46
+ * Used for finding the next available suffix number
47
+ */
48
+ export type SlugPrefixQueryFn = (slugPrefix: string) => Promise<SlugEntry[]>;
49
+ /**
50
+ * Checks if a slug is unique within a content type scope.
51
+ *
52
+ * @param slug - The slug to check
53
+ * @param queryFn - Function that queries the database for entries with the given slug
54
+ * @param options - Configuration options
55
+ * @returns Result indicating uniqueness and suggestions
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * // In a Convex mutation
60
+ * const queryFn = async (slug: string) => {
61
+ * return await ctx.db
62
+ * .query("contentEntries")
63
+ * .withIndex("by_content_type_and_slug", (q) =>
64
+ * q.eq("contentTypeId", contentTypeId).eq("slug", slug)
65
+ * )
66
+ * .filter((q) => q.eq(q.field("deletedAt"), undefined))
67
+ * .first();
68
+ * };
69
+ *
70
+ * const result = await checkSlugUniqueness("my-post", queryFn);
71
+ * if (!result.isUnique) {
72
+ * console.log(`Slug conflict with entry ${result.existingEntryId}`);
73
+ * console.log(`Suggested alternative: ${result.suggestedSlug}`);
74
+ * }
75
+ * ```
76
+ */
77
+ export declare function checkSlugUniqueness(slug: string, queryFn: SlugQueryFn, options?: SlugUniquenessOptions): Promise<SlugCheckResult>;
78
+ /**
79
+ * Ensures a slug is unique by generating incremental suffixes if needed.
80
+ * This is the main function to use when creating or updating content entries.
81
+ *
82
+ * @param baseSlug - The desired slug (or title to generate slug from)
83
+ * @param queryFn - Function that queries the database for entries with a given slug
84
+ * @param options - Configuration options
85
+ * @returns A unique slug (either the original or with a numeric suffix)
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * // In a Convex mutation for creating a new entry
90
+ * const queryFn = async (slug: string) => {
91
+ * return await ctx.db
92
+ * .query("contentEntries")
93
+ * .withIndex("by_content_type_and_slug", (q) =>
94
+ * q.eq("contentTypeId", contentTypeId).eq("slug", slug)
95
+ * )
96
+ * .filter((q) => q.eq(q.field("deletedAt"), undefined))
97
+ * .first();
98
+ * };
99
+ *
100
+ * const uniqueSlug = await ensureUniqueSlug("my-post", queryFn);
101
+ * // Returns "my-post" if unique, or "my-post-1", "my-post-2", etc.
102
+ * ```
103
+ */
104
+ export declare function ensureUniqueSlug(baseSlug: string, queryFn: SlugQueryFn, options?: SlugUniquenessOptions): Promise<string>;
105
+ /**
106
+ * Finds the next available slug suffix by analyzing existing slugs.
107
+ * This is useful when you want to pre-compute the next suffix without
108
+ * iterating through each number.
109
+ *
110
+ * @param baseSlug - The base slug to find the next suffix for
111
+ * @param prefixQueryFn - Function that returns all entries with slugs starting with the base
112
+ * @param options - Configuration options
113
+ * @returns The next available slug with the appropriate suffix
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * // If entries exist with slugs: "post", "post-1", "post-2", "post-5"
118
+ * const nextSlug = await findNextAvailableSlug("post", queryPrefixFn);
119
+ * // Returns "post-3" (fills the gap)
120
+ * ```
121
+ */
122
+ export declare function findNextAvailableSlug(baseSlug: string, prefixQueryFn: SlugPrefixQueryFn, options?: SlugUniquenessOptions): Promise<string>;
123
+ /**
124
+ * Validates a slug and returns validation errors if any.
125
+ *
126
+ * @param slug - The slug to validate
127
+ * @param separator - The separator character (default: '-')
128
+ * @returns Array of validation error messages (empty if valid)
129
+ */
130
+ export declare function validateSlugFormat(slug: string, separator?: string): string[];
131
+ //# sourceMappingURL=slugUniqueness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugUniqueness.d.ts","sourceRoot":"","sources":["../../../src/component/lib/slugUniqueness.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,iCAAiC;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;AAEtE;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,qBAA0B,GACjC,OAAO,CAAC,eAAe,CAAC,CA2C1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,gBAAgB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,qBAA0B,GACjC,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CAC1C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,iBAAiB,EAChC,OAAO,GAAE,qBAA0B,GACjC,OAAO,CAAC,MAAM,CAAC,CA0DjB;AASD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAY,GACrB,MAAM,EAAE,CA0CV"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Slug Uniqueness Utilities
3
+ *
4
+ * Provides functions for checking slug uniqueness within content type scope
5
+ * and generating unique slugs with incremental suffixes when conflicts exist.
6
+ */
7
+ import { generateSlug, generateUniqueSlug, isValidSlug, } from "./slugGenerator.js";
8
+ /**
9
+ * Checks if a slug is unique within a content type scope.
10
+ *
11
+ * @param slug - The slug to check
12
+ * @param queryFn - Function that queries the database for entries with the given slug
13
+ * @param options - Configuration options
14
+ * @returns Result indicating uniqueness and suggestions
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // In a Convex mutation
19
+ * const queryFn = async (slug: string) => {
20
+ * return await ctx.db
21
+ * .query("contentEntries")
22
+ * .withIndex("by_content_type_and_slug", (q) =>
23
+ * q.eq("contentTypeId", contentTypeId).eq("slug", slug)
24
+ * )
25
+ * .filter((q) => q.eq(q.field("deletedAt"), undefined))
26
+ * .first();
27
+ * };
28
+ *
29
+ * const result = await checkSlugUniqueness("my-post", queryFn);
30
+ * if (!result.isUnique) {
31
+ * console.log(`Slug conflict with entry ${result.existingEntryId}`);
32
+ * console.log(`Suggested alternative: ${result.suggestedSlug}`);
33
+ * }
34
+ * ```
35
+ */
36
+ export async function checkSlugUniqueness(slug, queryFn, options = {}) {
37
+ const { excludeEntryId } = options;
38
+ // Validate the slug format
39
+ if (!isValidSlug(slug, options.separator)) {
40
+ return {
41
+ isUnique: false,
42
+ suggestedSlug: generateSlug(slug, { separator: options.separator }),
43
+ };
44
+ }
45
+ // Query for existing entry with this slug
46
+ const existingEntry = await queryFn(slug);
47
+ // Check if the slug is available
48
+ if (!existingEntry) {
49
+ return { isUnique: true };
50
+ }
51
+ // If we're updating an entry, exclude it from the check
52
+ if (excludeEntryId && existingEntry._id === excludeEntryId) {
53
+ return { isUnique: true };
54
+ }
55
+ // Slug is taken - generate a suggestion
56
+ const isUniqueFn = async (candidateSlug) => {
57
+ const entry = await queryFn(candidateSlug);
58
+ if (!entry)
59
+ return true;
60
+ if (excludeEntryId && entry._id === excludeEntryId)
61
+ return true;
62
+ return false;
63
+ };
64
+ const suggestedSlug = await generateUniqueSlug(slug, isUniqueFn, options.maxAttempts);
65
+ return {
66
+ isUnique: false,
67
+ existingEntryId: existingEntry._id,
68
+ suggestedSlug,
69
+ };
70
+ }
71
+ /**
72
+ * Ensures a slug is unique by generating incremental suffixes if needed.
73
+ * This is the main function to use when creating or updating content entries.
74
+ *
75
+ * @param baseSlug - The desired slug (or title to generate slug from)
76
+ * @param queryFn - Function that queries the database for entries with a given slug
77
+ * @param options - Configuration options
78
+ * @returns A unique slug (either the original or with a numeric suffix)
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // In a Convex mutation for creating a new entry
83
+ * const queryFn = async (slug: string) => {
84
+ * return await ctx.db
85
+ * .query("contentEntries")
86
+ * .withIndex("by_content_type_and_slug", (q) =>
87
+ * q.eq("contentTypeId", contentTypeId).eq("slug", slug)
88
+ * )
89
+ * .filter((q) => q.eq(q.field("deletedAt"), undefined))
90
+ * .first();
91
+ * };
92
+ *
93
+ * const uniqueSlug = await ensureUniqueSlug("my-post", queryFn);
94
+ * // Returns "my-post" if unique, or "my-post-1", "my-post-2", etc.
95
+ * ```
96
+ */
97
+ export async function ensureUniqueSlug(baseSlug, queryFn, options = {}) {
98
+ const { excludeEntryId, maxAttempts = 100 } = options;
99
+ // Validate and normalize the base slug
100
+ let slug = baseSlug;
101
+ if (!isValidSlug(slug, options.separator)) {
102
+ slug = generateSlug(slug, { separator: options.separator });
103
+ }
104
+ // If slug is empty after normalization, use a fallback
105
+ if (!slug) {
106
+ slug = "untitled";
107
+ }
108
+ // Check if the base slug is available
109
+ const isUniqueFn = async (candidateSlug) => {
110
+ const entry = await queryFn(candidateSlug);
111
+ if (!entry)
112
+ return true;
113
+ if (excludeEntryId && entry._id === excludeEntryId)
114
+ return true;
115
+ return false;
116
+ };
117
+ return generateUniqueSlug(slug, isUniqueFn, maxAttempts);
118
+ }
119
+ /**
120
+ * Finds the next available slug suffix by analyzing existing slugs.
121
+ * This is useful when you want to pre-compute the next suffix without
122
+ * iterating through each number.
123
+ *
124
+ * @param baseSlug - The base slug to find the next suffix for
125
+ * @param prefixQueryFn - Function that returns all entries with slugs starting with the base
126
+ * @param options - Configuration options
127
+ * @returns The next available slug with the appropriate suffix
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * // If entries exist with slugs: "post", "post-1", "post-2", "post-5"
132
+ * const nextSlug = await findNextAvailableSlug("post", queryPrefixFn);
133
+ * // Returns "post-3" (fills the gap)
134
+ * ```
135
+ */
136
+ export async function findNextAvailableSlug(baseSlug, prefixQueryFn, options = {}) {
137
+ const { excludeEntryId, separator = "-" } = options;
138
+ // Validate the base slug
139
+ let slug = baseSlug;
140
+ if (!isValidSlug(slug, separator)) {
141
+ slug = generateSlug(slug, { separator });
142
+ }
143
+ if (!slug) {
144
+ slug = "untitled";
145
+ }
146
+ // Get all entries with this prefix
147
+ const existingEntries = await prefixQueryFn(slug);
148
+ // Filter out the excluded entry and soft-deleted entries
149
+ const activeEntries = existingEntries.filter((entry) => {
150
+ if (excludeEntryId && entry._id === excludeEntryId)
151
+ return false;
152
+ if (entry.deletedAt)
153
+ return false;
154
+ return true;
155
+ });
156
+ // If no entries exist with this slug, the base is available
157
+ const hasSlugsToCheck = activeEntries.some((entry) => {
158
+ return entry.slug === slug || entry.slug.startsWith(`${slug}${separator}`);
159
+ });
160
+ if (!hasSlugsToCheck) {
161
+ return slug;
162
+ }
163
+ // Check if base slug itself is taken
164
+ const baseIsTaken = activeEntries.some((entry) => entry.slug === slug);
165
+ if (!baseIsTaken) {
166
+ return slug;
167
+ }
168
+ // Extract existing suffix numbers
169
+ const suffixPattern = new RegExp(`^${escapeRegex(slug)}${escapeRegex(separator)}(\\d+)$`);
170
+ const usedNumbers = new Set();
171
+ for (const entry of activeEntries) {
172
+ const match = entry.slug.match(suffixPattern);
173
+ if (match) {
174
+ usedNumbers.add(parseInt(match[1], 10));
175
+ }
176
+ }
177
+ // Find the smallest available number
178
+ let nextNumber = 1;
179
+ while (usedNumbers.has(nextNumber)) {
180
+ nextNumber++;
181
+ }
182
+ return `${slug}${separator}${nextNumber}`;
183
+ }
184
+ /**
185
+ * Escapes special regex characters in a string
186
+ */
187
+ function escapeRegex(str) {
188
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
189
+ }
190
+ /**
191
+ * Validates a slug and returns validation errors if any.
192
+ *
193
+ * @param slug - The slug to validate
194
+ * @param separator - The separator character (default: '-')
195
+ * @returns Array of validation error messages (empty if valid)
196
+ */
197
+ export function validateSlugFormat(slug, separator = "-") {
198
+ const errors = [];
199
+ if (!slug || typeof slug !== "string") {
200
+ errors.push("Slug is required");
201
+ return errors;
202
+ }
203
+ if (slug.length === 0) {
204
+ errors.push("Slug cannot be empty");
205
+ return errors;
206
+ }
207
+ if (slug.length > 100) {
208
+ errors.push("Slug must be 100 characters or less");
209
+ }
210
+ if (slug !== slug.toLowerCase()) {
211
+ errors.push("Slug must be lowercase");
212
+ }
213
+ if (slug.startsWith(separator)) {
214
+ errors.push(`Slug cannot start with '${separator}'`);
215
+ }
216
+ if (slug.endsWith(separator)) {
217
+ errors.push(`Slug cannot end with '${separator}'`);
218
+ }
219
+ const doubleSeparatorRegex = new RegExp(`${escapeRegex(separator)}{2,}`);
220
+ if (doubleSeparatorRegex.test(slug)) {
221
+ errors.push(`Slug cannot contain consecutive '${separator}' characters`);
222
+ }
223
+ const invalidCharsRegex = new RegExp(`[^a-z0-9${escapeRegex(separator)}]`);
224
+ if (invalidCharsRegex.test(slug)) {
225
+ errors.push("Slug can only contain lowercase letters, numbers, and hyphens");
226
+ }
227
+ return errors;
228
+ }
229
+ //# sourceMappingURL=slugUniqueness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugUniqueness.js","sourceRoot":"","sources":["../../../src/component/lib/slugUniqueness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Soft-delete utilities for CMS documents.
3
+ *
4
+ * Provides type-safe helpers for working with documents that use
5
+ * the soft-delete pattern (deletedAt timestamp).
6
+ */
7
+ export interface SoftDeletable {
8
+ deletedAt?: number;
9
+ }
10
+ export declare function isDeleted<T extends SoftDeletable>(doc: T): boolean;
11
+ export declare function isActive<T extends SoftDeletable>(doc: T): boolean;
12
+ export declare function filterActive<T extends SoftDeletable>(docs: T[]): T[];
13
+ export declare function filterDeleted<T extends SoftDeletable>(docs: T[]): T[];
14
+ export declare function requireNotDeleted<T extends SoftDeletable>(doc: T, errorFactory: () => Error): asserts doc is T & {
15
+ deletedAt: undefined;
16
+ };
17
+ export declare function requireDeleted<T extends SoftDeletable>(doc: T, errorFactory: () => Error): void;
18
+ //# sourceMappingURL=softDelete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"softDelete.d.ts","sourceRoot":"","sources":["../../../src/component/lib/softDelete.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,aAAa,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAElE;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,aAAa,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAEjE;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAEpE;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAErE;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,aAAa,EACvD,GAAG,EAAE,CAAC,EACN,YAAY,EAAE,MAAM,KAAK,GACxB,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,CAI7C;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EACpD,GAAG,EAAE,CAAC,EACN,YAAY,EAAE,MAAM,KAAK,GACxB,IAAI,CAIN"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Soft-delete utilities for CMS documents.
3
+ *
4
+ * Provides type-safe helpers for working with documents that use
5
+ * the soft-delete pattern (deletedAt timestamp).
6
+ */
7
+ export function isDeleted(doc) {
8
+ return doc.deletedAt !== undefined;
9
+ }
10
+ export function isActive(doc) {
11
+ return doc.deletedAt === undefined;
12
+ }
13
+ export function filterActive(docs) {
14
+ return docs.filter(isActive);
15
+ }
16
+ export function filterDeleted(docs) {
17
+ return docs.filter(isDeleted);
18
+ }
19
+ export function requireNotDeleted(doc, errorFactory) {
20
+ if (isDeleted(doc)) {
21
+ throw errorFactory();
22
+ }
23
+ }
24
+ export function requireDeleted(doc, errorFactory) {
25
+ if (!isDeleted(doc)) {
26
+ throw errorFactory();
27
+ }
28
+ }
29
+ //# sourceMappingURL=softDelete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"softDelete.js","sourceRoot":"","sources":["../../../src/component/lib/softDelete.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,UAAU,SAAS,CAA0B,GAAM;IACvD,OAAO,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,QAAQ,CAA0B,GAAM;IACtD,OAAO,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAA0B,IAAS;IAC7D,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa,CAA0B,IAAS;IAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,GAAM,EACN,YAAyB;IAEzB,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,GAAM,EACN,YAAyB;IAEzB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}