@simple-auth-kit/cli 1.0.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 (820) hide show
  1. package/README.md +88 -0
  2. package/bin/simple-auth-kit.mjs +18 -0
  3. package/lib/copy.ts +190 -0
  4. package/lib/rename-native.ts +173 -0
  5. package/package.json +31 -0
  6. package/registry/README.md +312 -0
  7. package/registry/admin-apps/README.md +71 -0
  8. package/registry/admin-apps/nextjs/shared/components.json +25 -0
  9. package/registry/admin-apps/nextjs/shared/next.config.ts +9 -0
  10. package/registry/admin-apps/nextjs/shared/postcss.config.mjs +7 -0
  11. package/registry/admin-apps/nextjs/shared/src/app/globals.css +159 -0
  12. package/registry/admin-apps/nextjs/shared/src/app/signup/page.tsx +72 -0
  13. package/registry/admin-apps/nextjs/shared/src/components/alert-modal.tsx +32 -0
  14. package/registry/admin-apps/nextjs/shared/src/components/breadcrumb.tsx +48 -0
  15. package/registry/admin-apps/nextjs/shared/src/components/form-error-alert.tsx +24 -0
  16. package/registry/admin-apps/nextjs/shared/src/components/loader/skeleton.tsx +39 -0
  17. package/registry/admin-apps/nextjs/shared/src/components/loader/table-skeleton-loader.tsx +57 -0
  18. package/registry/admin-apps/nextjs/shared/src/components/permission-group-select.tsx +101 -0
  19. package/registry/admin-apps/nextjs/shared/src/components/permission-group-view.tsx +53 -0
  20. package/registry/admin-apps/nextjs/shared/src/components/role-multi-select.tsx +71 -0
  21. package/registry/admin-apps/nextjs/shared/src/components/theme-toggle.tsx +26 -0
  22. package/registry/admin-apps/nextjs/shared/src/components/ui/alert.tsx +78 -0
  23. package/registry/admin-apps/nextjs/shared/src/components/ui/avatar.tsx +53 -0
  24. package/registry/admin-apps/nextjs/shared/src/components/ui/badge.tsx +51 -0
  25. package/registry/admin-apps/nextjs/shared/src/components/ui/breadcrumb.tsx +109 -0
  26. package/registry/admin-apps/nextjs/shared/src/components/ui/button.tsx +67 -0
  27. package/registry/admin-apps/nextjs/shared/src/components/ui/card.tsx +103 -0
  28. package/registry/admin-apps/nextjs/shared/src/components/ui/checkbox.tsx +32 -0
  29. package/registry/admin-apps/nextjs/shared/src/components/ui/collapsible.tsx +33 -0
  30. package/registry/admin-apps/nextjs/shared/src/components/ui/data-table.tsx +107 -0
  31. package/registry/admin-apps/nextjs/shared/src/components/ui/dialog.tsx +143 -0
  32. package/registry/admin-apps/nextjs/shared/src/components/ui/dropdown-menu.tsx +234 -0
  33. package/registry/admin-apps/nextjs/shared/src/components/ui/heading.tsx +16 -0
  34. package/registry/admin-apps/nextjs/shared/src/components/ui/input.tsx +19 -0
  35. package/registry/admin-apps/nextjs/shared/src/components/ui/label.tsx +24 -0
  36. package/registry/admin-apps/nextjs/shared/src/components/ui/modal.tsx +29 -0
  37. package/registry/admin-apps/nextjs/shared/src/components/ui/select.tsx +192 -0
  38. package/registry/admin-apps/nextjs/shared/src/components/ui/separator.tsx +28 -0
  39. package/registry/admin-apps/nextjs/shared/src/components/ui/sheet.tsx +135 -0
  40. package/registry/admin-apps/nextjs/shared/src/components/ui/sidebar.tsx +567 -0
  41. package/registry/admin-apps/nextjs/shared/src/components/ui/skeleton.tsx +13 -0
  42. package/registry/admin-apps/nextjs/shared/src/components/ui/sonner.tsx +34 -0
  43. package/registry/admin-apps/nextjs/shared/src/components/ui/table.tsx +116 -0
  44. package/registry/admin-apps/nextjs/shared/src/components/ui/tooltip.tsx +61 -0
  45. package/registry/admin-apps/nextjs/shared/src/hooks/use-live-audit-feed.ts +51 -0
  46. package/registry/admin-apps/nextjs/shared/src/hooks/use-mobile.ts +17 -0
  47. package/registry/admin-apps/nextjs/shared/src/lib/error.ts +23 -0
  48. package/registry/admin-apps/nextjs/shared/src/lib/query-client.tsx +10 -0
  49. package/registry/admin-apps/nextjs/shared/src/lib/token-storage.ts +36 -0
  50. package/registry/admin-apps/nextjs/shared/src/lib/utils.ts +6 -0
  51. package/registry/admin-apps/nextjs/shared/tsconfig.json +41 -0
  52. package/registry/admin-apps/nextjs/variants/base/.env.example +9 -0
  53. package/registry/admin-apps/nextjs/variants/base/Dockerfile +32 -0
  54. package/registry/admin-apps/nextjs/variants/base/package.json +58 -0
  55. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/account/change-password/page.tsx +163 -0
  56. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/account/page.tsx +569 -0
  57. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/audit-log/page.tsx +180 -0
  58. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/[id]/edit/page.tsx +357 -0
  59. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/[id]/page.tsx +191 -0
  60. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/country-schema.ts +18 -0
  61. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/new/page.tsx +305 -0
  62. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/countries/page.tsx +367 -0
  63. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/edit/page.tsx +386 -0
  64. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/page.tsx +213 -0
  65. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/customer-schema.ts +35 -0
  66. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/new/page.tsx +334 -0
  67. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/page.tsx +400 -0
  68. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/dashboard/page.tsx +263 -0
  69. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/edit/page.tsx +275 -0
  70. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/page.tsx +183 -0
  71. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/language-schema.ts +20 -0
  72. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/new/page.tsx +226 -0
  73. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/page.tsx +342 -0
  74. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/layout.tsx +50 -0
  75. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/edit/page.tsx +309 -0
  76. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/page.tsx +119 -0
  77. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/new/page.tsx +275 -0
  78. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/page.tsx +311 -0
  79. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/permission-schema.ts +44 -0
  80. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/edit/page.tsx +276 -0
  81. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/page.tsx +179 -0
  82. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/new/page.tsx +241 -0
  83. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/page.tsx +325 -0
  84. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/role-schema.ts +20 -0
  85. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/edit/page.tsx +411 -0
  86. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/page.tsx +253 -0
  87. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/new/page.tsx +444 -0
  88. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/page.tsx +410 -0
  89. package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/user-schema.ts +37 -0
  90. package/registry/admin-apps/nextjs/variants/base/src/app/api/auth/[...nextauth]/route.ts +3 -0
  91. package/registry/admin-apps/nextjs/variants/base/src/app/icon.svg +5 -0
  92. package/registry/admin-apps/nextjs/variants/base/src/app/layout.tsx +31 -0
  93. package/registry/admin-apps/nextjs/variants/base/src/app/login/page.tsx +162 -0
  94. package/registry/admin-apps/nextjs/variants/base/src/app/page.tsx +22 -0
  95. package/registry/admin-apps/nextjs/variants/base/src/auth.ts +105 -0
  96. package/registry/admin-apps/nextjs/variants/base/src/components/app-sidebar.tsx +182 -0
  97. package/registry/admin-apps/nextjs/variants/base/src/components/nav-user.tsx +99 -0
  98. package/registry/admin-apps/nextjs/variants/base/src/components/permission-required.tsx +32 -0
  99. package/registry/admin-apps/nextjs/variants/base/src/components/photo-upload.tsx +203 -0
  100. package/registry/admin-apps/nextjs/variants/base/src/components/ui/accordion.tsx +66 -0
  101. package/registry/admin-apps/nextjs/variants/base/src/components/ui/alert-dialog.tsx +157 -0
  102. package/registry/admin-apps/nextjs/variants/base/src/components/ui/button-group.tsx +83 -0
  103. package/registry/admin-apps/nextjs/variants/base/src/components/ui/calendar.tsx +218 -0
  104. package/registry/admin-apps/nextjs/variants/base/src/components/ui/carousel.tsx +241 -0
  105. package/registry/admin-apps/nextjs/variants/base/src/components/ui/combobox.tsx +243 -0
  106. package/registry/admin-apps/nextjs/variants/base/src/components/ui/command.tsx +184 -0
  107. package/registry/admin-apps/nextjs/variants/base/src/components/ui/context-menu.tsx +252 -0
  108. package/registry/admin-apps/nextjs/variants/base/src/components/ui/date-picker.tsx +323 -0
  109. package/registry/admin-apps/nextjs/variants/base/src/components/ui/datetime-picker.tsx +896 -0
  110. package/registry/admin-apps/nextjs/variants/base/src/components/ui/drawer.tsx +135 -0
  111. package/registry/admin-apps/nextjs/variants/base/src/components/ui/empty.tsx +104 -0
  112. package/registry/admin-apps/nextjs/variants/base/src/components/ui/field.tsx +248 -0
  113. package/registry/admin-apps/nextjs/variants/base/src/components/ui/form.tsx +168 -0
  114. package/registry/admin-apps/nextjs/variants/base/src/components/ui/hover-card.tsx +44 -0
  115. package/registry/admin-apps/nextjs/variants/base/src/components/ui/input-group.tsx +170 -0
  116. package/registry/admin-apps/nextjs/variants/base/src/components/ui/input-otp.tsx +77 -0
  117. package/registry/admin-apps/nextjs/variants/base/src/components/ui/item.tsx +193 -0
  118. package/registry/admin-apps/nextjs/variants/base/src/components/ui/kbd.tsx +28 -0
  119. package/registry/admin-apps/nextjs/variants/base/src/components/ui/menubar.tsx +276 -0
  120. package/registry/admin-apps/nextjs/variants/base/src/components/ui/multi-select.tsx +381 -0
  121. package/registry/admin-apps/nextjs/variants/base/src/components/ui/multi-selector.tsx +641 -0
  122. package/registry/admin-apps/nextjs/variants/base/src/components/ui/native-select.tsx +48 -0
  123. package/registry/admin-apps/nextjs/variants/base/src/components/ui/navigation-menu.tsx +168 -0
  124. package/registry/admin-apps/nextjs/variants/base/src/components/ui/pagination.tsx +127 -0
  125. package/registry/admin-apps/nextjs/variants/base/src/components/ui/popover.tsx +48 -0
  126. package/registry/admin-apps/nextjs/variants/base/src/components/ui/progress.tsx +31 -0
  127. package/registry/admin-apps/nextjs/variants/base/src/components/ui/radio-group.tsx +45 -0
  128. package/registry/admin-apps/nextjs/variants/base/src/components/ui/resizable.tsx +55 -0
  129. package/registry/admin-apps/nextjs/variants/base/src/components/ui/scroll-area.tsx +58 -0
  130. package/registry/admin-apps/nextjs/variants/base/src/components/ui/spinner.tsx +27 -0
  131. package/registry/admin-apps/nextjs/variants/base/src/components/ui/switch.tsx +31 -0
  132. package/registry/admin-apps/nextjs/variants/base/src/components/ui/tabs.tsx +66 -0
  133. package/registry/admin-apps/nextjs/variants/base/src/components/ui/textarea.tsx +18 -0
  134. package/registry/admin-apps/nextjs/variants/base/src/components/ui/toggle-group.tsx +83 -0
  135. package/registry/admin-apps/nextjs/variants/base/src/components/ui/toggle.tsx +47 -0
  136. package/registry/admin-apps/nextjs/variants/base/src/components/user-menu.tsx +61 -0
  137. package/registry/admin-apps/nextjs/variants/base/src/lib/ability.ts +90 -0
  138. package/registry/admin-apps/nextjs/variants/base/src/lib/auth-client.ts +10 -0
  139. package/registry/admin-apps/nextjs/variants/base/src/lib/env.ts +10 -0
  140. package/registry/admin-apps/nextjs/variants/base/src/lib/server-token-storage.ts +33 -0
  141. package/registry/admin-apps/nextjs/variants/base/src/lib/stores/auth-store.ts +117 -0
  142. package/registry/admin-apps/nextjs/variants/base/src/lib/stores/store-context.tsx +50 -0
  143. package/registry/admin-apps/nextjs/variants/base/src/proxy.ts +81 -0
  144. package/registry/admin-apps/nextjs/variants/base/src/routes.ts +7 -0
  145. package/registry/admin-apps/nextjs/variants/workspaces/.env.example +7 -0
  146. package/registry/admin-apps/nextjs/variants/workspaces/Dockerfile +32 -0
  147. package/registry/admin-apps/nextjs/variants/workspaces/package.json +48 -0
  148. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/account/page.tsx +360 -0
  149. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/audit-log/page.tsx +185 -0
  150. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/[id]/edit/page.tsx +296 -0
  151. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/[id]/page.tsx +194 -0
  152. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/country-schema.ts +27 -0
  153. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/new/page.tsx +242 -0
  154. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/countries/page.tsx +373 -0
  155. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/[id]/edit/page.tsx +306 -0
  156. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/[id]/page.tsx +219 -0
  157. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/customer-schema.ts +36 -0
  158. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/new/page.tsx +251 -0
  159. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/customers/page.tsx +406 -0
  160. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/dashboard/page.tsx +146 -0
  161. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/[id]/edit/page.tsx +240 -0
  162. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/[id]/page.tsx +186 -0
  163. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/language-schema.ts +26 -0
  164. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/new/page.tsx +189 -0
  165. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/languages/page.tsx +348 -0
  166. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/layout.tsx +96 -0
  167. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/members/page.tsx +263 -0
  168. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/[id]/edit/page.tsx +264 -0
  169. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/[id]/page.tsx +126 -0
  170. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/new/page.tsx +238 -0
  171. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/page.tsx +294 -0
  172. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/permissions/permission-schema.ts +46 -0
  173. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/[id]/edit/page.tsx +248 -0
  174. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/[id]/page.tsx +186 -0
  175. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/new/page.tsx +205 -0
  176. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/page.tsx +485 -0
  177. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/roles/role-schema.ts +23 -0
  178. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/[id]/page.tsx +521 -0
  179. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/new/page.tsx +342 -0
  180. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/page.tsx +414 -0
  181. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/users/user-schema.ts +44 -0
  182. package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/workspaces/new/page.tsx +66 -0
  183. package/registry/admin-apps/nextjs/variants/workspaces/src/app/layout.tsx +31 -0
  184. package/registry/admin-apps/nextjs/variants/workspaces/src/app/login/page.tsx +110 -0
  185. package/registry/admin-apps/nextjs/variants/workspaces/src/app/page.tsx +33 -0
  186. package/registry/admin-apps/nextjs/variants/workspaces/src/components/app-sidebar.tsx +180 -0
  187. package/registry/admin-apps/nextjs/variants/workspaces/src/components/change-password-dialog.tsx +119 -0
  188. package/registry/admin-apps/nextjs/variants/workspaces/src/components/nav-user.tsx +105 -0
  189. package/registry/admin-apps/nextjs/variants/workspaces/src/components/permission-required.tsx +32 -0
  190. package/registry/admin-apps/nextjs/variants/workspaces/src/components/photo-upload.tsx +226 -0
  191. package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/calendar.tsx +218 -0
  192. package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/datetime-picker.tsx +896 -0
  193. package/registry/admin-apps/nextjs/variants/workspaces/src/components/ui/popover.tsx +48 -0
  194. package/registry/admin-apps/nextjs/variants/workspaces/src/components/user-menu.tsx +67 -0
  195. package/registry/admin-apps/nextjs/variants/workspaces/src/components/workspace-switcher.tsx +57 -0
  196. package/registry/admin-apps/nextjs/variants/workspaces/src/lib/ability.ts +101 -0
  197. package/registry/admin-apps/nextjs/variants/workspaces/src/lib/active-workspace.ts +38 -0
  198. package/registry/admin-apps/nextjs/variants/workspaces/src/lib/auth-client.ts +17 -0
  199. package/registry/admin-apps/nextjs/variants/workspaces/src/lib/env.ts +4 -0
  200. package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/auth-store.ts +139 -0
  201. package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/store-context.tsx +66 -0
  202. package/registry/admin-apps/nextjs/variants/workspaces/src/lib/stores/workspace-store.ts +89 -0
  203. package/registry/admin-apps/react/shared/components.json +25 -0
  204. package/registry/admin-apps/react/shared/postcss.config.js +5 -0
  205. package/registry/admin-apps/react/shared/src/components/alert-modal.tsx +30 -0
  206. package/registry/admin-apps/react/shared/src/components/loader/skeleton.tsx +39 -0
  207. package/registry/admin-apps/react/shared/src/components/loader/table-skeleton-loader.tsx +57 -0
  208. package/registry/admin-apps/react/shared/src/components/nav-user.tsx +103 -0
  209. package/registry/admin-apps/react/shared/src/components/role-multi-select.tsx +69 -0
  210. package/registry/admin-apps/react/shared/src/components/theme-toggle.tsx +24 -0
  211. package/registry/admin-apps/react/shared/src/components/ui/accordion.tsx +66 -0
  212. package/registry/admin-apps/react/shared/src/components/ui/alert-dialog.tsx +157 -0
  213. package/registry/admin-apps/react/shared/src/components/ui/alert.tsx +78 -0
  214. package/registry/admin-apps/react/shared/src/components/ui/avatar.tsx +53 -0
  215. package/registry/admin-apps/react/shared/src/components/ui/badge.tsx +51 -0
  216. package/registry/admin-apps/react/shared/src/components/ui/breadcrumb.tsx +109 -0
  217. package/registry/admin-apps/react/shared/src/components/ui/button-group.tsx +83 -0
  218. package/registry/admin-apps/react/shared/src/components/ui/button.tsx +67 -0
  219. package/registry/admin-apps/react/shared/src/components/ui/calendar.tsx +218 -0
  220. package/registry/admin-apps/react/shared/src/components/ui/card.tsx +103 -0
  221. package/registry/admin-apps/react/shared/src/components/ui/carousel.tsx +241 -0
  222. package/registry/admin-apps/react/shared/src/components/ui/checkbox.tsx +32 -0
  223. package/registry/admin-apps/react/shared/src/components/ui/collapsible.tsx +33 -0
  224. package/registry/admin-apps/react/shared/src/components/ui/context-menu.tsx +252 -0
  225. package/registry/admin-apps/react/shared/src/components/ui/data-table.tsx +107 -0
  226. package/registry/admin-apps/react/shared/src/components/ui/datetime-picker.tsx +896 -0
  227. package/registry/admin-apps/react/shared/src/components/ui/dialog.tsx +143 -0
  228. package/registry/admin-apps/react/shared/src/components/ui/drawer.tsx +135 -0
  229. package/registry/admin-apps/react/shared/src/components/ui/dropdown-menu.tsx +234 -0
  230. package/registry/admin-apps/react/shared/src/components/ui/empty.tsx +104 -0
  231. package/registry/admin-apps/react/shared/src/components/ui/field.tsx +248 -0
  232. package/registry/admin-apps/react/shared/src/components/ui/form.tsx +168 -0
  233. package/registry/admin-apps/react/shared/src/components/ui/heading.tsx +16 -0
  234. package/registry/admin-apps/react/shared/src/components/ui/hover-card.tsx +44 -0
  235. package/registry/admin-apps/react/shared/src/components/ui/input-group.tsx +170 -0
  236. package/registry/admin-apps/react/shared/src/components/ui/input-otp.tsx +77 -0
  237. package/registry/admin-apps/react/shared/src/components/ui/input.tsx +19 -0
  238. package/registry/admin-apps/react/shared/src/components/ui/item.tsx +193 -0
  239. package/registry/admin-apps/react/shared/src/components/ui/kbd.tsx +28 -0
  240. package/registry/admin-apps/react/shared/src/components/ui/label.tsx +24 -0
  241. package/registry/admin-apps/react/shared/src/components/ui/menubar.tsx +276 -0
  242. package/registry/admin-apps/react/shared/src/components/ui/modal.tsx +29 -0
  243. package/registry/admin-apps/react/shared/src/components/ui/multi-select.tsx +381 -0
  244. package/registry/admin-apps/react/shared/src/components/ui/multi-selector.tsx +641 -0
  245. package/registry/admin-apps/react/shared/src/components/ui/native-select.tsx +48 -0
  246. package/registry/admin-apps/react/shared/src/components/ui/navigation-menu.tsx +168 -0
  247. package/registry/admin-apps/react/shared/src/components/ui/pagination.tsx +127 -0
  248. package/registry/admin-apps/react/shared/src/components/ui/popover.tsx +48 -0
  249. package/registry/admin-apps/react/shared/src/components/ui/progress.tsx +31 -0
  250. package/registry/admin-apps/react/shared/src/components/ui/radio-group.tsx +45 -0
  251. package/registry/admin-apps/react/shared/src/components/ui/resizable.tsx +55 -0
  252. package/registry/admin-apps/react/shared/src/components/ui/scroll-area.tsx +58 -0
  253. package/registry/admin-apps/react/shared/src/components/ui/select.tsx +192 -0
  254. package/registry/admin-apps/react/shared/src/components/ui/separator.tsx +28 -0
  255. package/registry/admin-apps/react/shared/src/components/ui/sheet.tsx +135 -0
  256. package/registry/admin-apps/react/shared/src/components/ui/sidebar.tsx +567 -0
  257. package/registry/admin-apps/react/shared/src/components/ui/skeleton.tsx +13 -0
  258. package/registry/admin-apps/react/shared/src/components/ui/spinner.tsx +27 -0
  259. package/registry/admin-apps/react/shared/src/components/ui/switch.tsx +31 -0
  260. package/registry/admin-apps/react/shared/src/components/ui/table.tsx +116 -0
  261. package/registry/admin-apps/react/shared/src/components/ui/tabs.tsx +66 -0
  262. package/registry/admin-apps/react/shared/src/components/ui/textarea.tsx +18 -0
  263. package/registry/admin-apps/react/shared/src/components/ui/toggle-group.tsx +83 -0
  264. package/registry/admin-apps/react/shared/src/components/ui/toggle.tsx +47 -0
  265. package/registry/admin-apps/react/shared/src/components/ui/tooltip.tsx +61 -0
  266. package/registry/admin-apps/react/shared/src/hooks/use-mobile.ts +17 -0
  267. package/registry/admin-apps/react/shared/src/lib/cn.ts +6 -0
  268. package/registry/admin-apps/react/shared/src/lib/error.ts +23 -0
  269. package/registry/admin-apps/react/shared/src/lib/query-client.tsx +8 -0
  270. package/registry/admin-apps/react/shared/src/lib/utils.ts +1 -0
  271. package/registry/admin-apps/react/shared/src/vite-env.d.ts +9 -0
  272. package/registry/admin-apps/react/shared/tsconfig.json +24 -0
  273. package/registry/admin-apps/react/variants/base/.env.example +3 -0
  274. package/registry/admin-apps/react/variants/base/Dockerfile +32 -0
  275. package/registry/admin-apps/react/variants/base/index.html +12 -0
  276. package/registry/admin-apps/react/variants/base/package.json +61 -0
  277. package/registry/admin-apps/react/variants/base/src/App.tsx +103 -0
  278. package/registry/admin-apps/react/variants/base/src/components/app-sidebar.tsx +181 -0
  279. package/registry/admin-apps/react/variants/base/src/components/breadcrumb.tsx +47 -0
  280. package/registry/admin-apps/react/variants/base/src/components/change-password-dialog.tsx +102 -0
  281. package/registry/admin-apps/react/variants/base/src/components/form-error-alert.tsx +23 -0
  282. package/registry/admin-apps/react/variants/base/src/components/layout/AppShell.tsx +36 -0
  283. package/registry/admin-apps/react/variants/base/src/components/layout/RequireAuth.tsx +66 -0
  284. package/registry/admin-apps/react/variants/base/src/components/permission-group-select.tsx +99 -0
  285. package/registry/admin-apps/react/variants/base/src/components/permission-group-view.tsx +53 -0
  286. package/registry/admin-apps/react/variants/base/src/components/photo-upload.tsx +221 -0
  287. package/registry/admin-apps/react/variants/base/src/components/ui/combobox.tsx +243 -0
  288. package/registry/admin-apps/react/variants/base/src/components/ui/command.tsx +177 -0
  289. package/registry/admin-apps/react/variants/base/src/components/ui/date-picker.tsx +322 -0
  290. package/registry/admin-apps/react/variants/base/src/components/ui/sonner.tsx +34 -0
  291. package/registry/admin-apps/react/variants/base/src/components/user-menu.tsx +65 -0
  292. package/registry/admin-apps/react/variants/base/src/hooks/use-live-audit-feed.ts +50 -0
  293. package/registry/admin-apps/react/variants/base/src/index.css +161 -0
  294. package/registry/admin-apps/react/variants/base/src/lib/ability.ts +72 -0
  295. package/registry/admin-apps/react/variants/base/src/lib/auth-client.ts +9 -0
  296. package/registry/admin-apps/react/variants/base/src/lib/theme.tsx +64 -0
  297. package/registry/admin-apps/react/variants/base/src/lib/token-storage.ts +34 -0
  298. package/registry/admin-apps/react/variants/base/src/main.tsx +24 -0
  299. package/registry/admin-apps/react/variants/base/src/pages/AccountPage.tsx +368 -0
  300. package/registry/admin-apps/react/variants/base/src/pages/AddCountryPage.tsx +227 -0
  301. package/registry/admin-apps/react/variants/base/src/pages/AddCustomerPage.tsx +220 -0
  302. package/registry/admin-apps/react/variants/base/src/pages/AddLanguagePage.tsx +167 -0
  303. package/registry/admin-apps/react/variants/base/src/pages/AddRolePage.tsx +187 -0
  304. package/registry/admin-apps/react/variants/base/src/pages/AddUserPage.tsx +328 -0
  305. package/registry/admin-apps/react/variants/base/src/pages/AuditLogPage.tsx +180 -0
  306. package/registry/admin-apps/react/variants/base/src/pages/CountriesPage.tsx +371 -0
  307. package/registry/admin-apps/react/variants/base/src/pages/CountryDetailPage.tsx +209 -0
  308. package/registry/admin-apps/react/variants/base/src/pages/CustomerDetailPage.tsx +231 -0
  309. package/registry/admin-apps/react/variants/base/src/pages/CustomersPage.tsx +404 -0
  310. package/registry/admin-apps/react/variants/base/src/pages/DashboardPage.tsx +190 -0
  311. package/registry/admin-apps/react/variants/base/src/pages/EditCountryPage.tsx +279 -0
  312. package/registry/admin-apps/react/variants/base/src/pages/EditCustomerPage.tsx +272 -0
  313. package/registry/admin-apps/react/variants/base/src/pages/EditLanguagePage.tsx +216 -0
  314. package/registry/admin-apps/react/variants/base/src/pages/EditRolePage.tsx +232 -0
  315. package/registry/admin-apps/react/variants/base/src/pages/EditUserPage.tsx +308 -0
  316. package/registry/admin-apps/react/variants/base/src/pages/LanguageDetailPage.tsx +201 -0
  317. package/registry/admin-apps/react/variants/base/src/pages/LanguagesPage.tsx +346 -0
  318. package/registry/admin-apps/react/variants/base/src/pages/LoginPage.tsx +149 -0
  319. package/registry/admin-apps/react/variants/base/src/pages/PermissionDetailPage.tsx +135 -0
  320. package/registry/admin-apps/react/variants/base/src/pages/PermissionsPage.tsx +331 -0
  321. package/registry/admin-apps/react/variants/base/src/pages/RoleDetailPage.tsx +167 -0
  322. package/registry/admin-apps/react/variants/base/src/pages/RolesPage.tsx +613 -0
  323. package/registry/admin-apps/react/variants/base/src/pages/UserDetailPage.tsx +268 -0
  324. package/registry/admin-apps/react/variants/base/src/pages/UsersPage.tsx +423 -0
  325. package/registry/admin-apps/react/variants/base/src/pages/country-schema.ts +29 -0
  326. package/registry/admin-apps/react/variants/base/src/pages/customer-schema.ts +37 -0
  327. package/registry/admin-apps/react/variants/base/src/pages/language-schema.ts +28 -0
  328. package/registry/admin-apps/react/variants/base/src/pages/user-schema.ts +36 -0
  329. package/registry/admin-apps/react/variants/base/src/stores/auth-store.ts +144 -0
  330. package/registry/admin-apps/react/variants/base/src/stores/store-context.tsx +15 -0
  331. package/registry/admin-apps/react/variants/base/vite.config.ts +15 -0
  332. package/registry/admin-apps/react/variants/workspaces/.env.example +5 -0
  333. package/registry/admin-apps/react/variants/workspaces/Dockerfile +32 -0
  334. package/registry/admin-apps/react/variants/workspaces/index.html +12 -0
  335. package/registry/admin-apps/react/variants/workspaces/package.json +61 -0
  336. package/registry/admin-apps/react/variants/workspaces/src/App.tsx +118 -0
  337. package/registry/admin-apps/react/variants/workspaces/src/components/app-sidebar.tsx +186 -0
  338. package/registry/admin-apps/react/variants/workspaces/src/components/breadcrumb.tsx +39 -0
  339. package/registry/admin-apps/react/variants/workspaces/src/components/change-password-dialog.tsx +111 -0
  340. package/registry/admin-apps/react/variants/workspaces/src/components/form-error-alert.tsx +23 -0
  341. package/registry/admin-apps/react/variants/workspaces/src/components/layout/AppShell.tsx +37 -0
  342. package/registry/admin-apps/react/variants/workspaces/src/components/layout/CreateWorkspaceDialog.tsx +66 -0
  343. package/registry/admin-apps/react/variants/workspaces/src/components/layout/RequireAuth.tsx +107 -0
  344. package/registry/admin-apps/react/variants/workspaces/src/components/layout/WorkspaceSwitcher.tsx +52 -0
  345. package/registry/admin-apps/react/variants/workspaces/src/components/permission-group-select.tsx +99 -0
  346. package/registry/admin-apps/react/variants/workspaces/src/components/permission-group-view.tsx +53 -0
  347. package/registry/admin-apps/react/variants/workspaces/src/components/photo-upload.tsx +223 -0
  348. package/registry/admin-apps/react/variants/workspaces/src/components/ui/combobox.tsx +243 -0
  349. package/registry/admin-apps/react/variants/workspaces/src/components/ui/command.tsx +184 -0
  350. package/registry/admin-apps/react/variants/workspaces/src/components/ui/date-picker.tsx +323 -0
  351. package/registry/admin-apps/react/variants/workspaces/src/components/ui/sonner.tsx +34 -0
  352. package/registry/admin-apps/react/variants/workspaces/src/components/user-menu.tsx +66 -0
  353. package/registry/admin-apps/react/variants/workspaces/src/hooks/use-live-audit-feed.ts +50 -0
  354. package/registry/admin-apps/react/variants/workspaces/src/index.css +162 -0
  355. package/registry/admin-apps/react/variants/workspaces/src/lib/ability.ts +78 -0
  356. package/registry/admin-apps/react/variants/workspaces/src/lib/auth-client.ts +27 -0
  357. package/registry/admin-apps/react/variants/workspaces/src/lib/permission-groups.ts +22 -0
  358. package/registry/admin-apps/react/variants/workspaces/src/lib/theme.tsx +64 -0
  359. package/registry/admin-apps/react/variants/workspaces/src/lib/token-storage.ts +37 -0
  360. package/registry/admin-apps/react/variants/workspaces/src/lib/user-fields.ts +13 -0
  361. package/registry/admin-apps/react/variants/workspaces/src/main.tsx +24 -0
  362. package/registry/admin-apps/react/variants/workspaces/src/pages/AccountPage.tsx +351 -0
  363. package/registry/admin-apps/react/variants/workspaces/src/pages/AddCountryPage.tsx +230 -0
  364. package/registry/admin-apps/react/variants/workspaces/src/pages/AddCustomerPage.tsx +224 -0
  365. package/registry/admin-apps/react/variants/workspaces/src/pages/AddLanguagePage.tsx +170 -0
  366. package/registry/admin-apps/react/variants/workspaces/src/pages/AddUserPage.tsx +297 -0
  367. package/registry/admin-apps/react/variants/workspaces/src/pages/AuditLogPage.tsx +195 -0
  368. package/registry/admin-apps/react/variants/workspaces/src/pages/CountriesPage.tsx +378 -0
  369. package/registry/admin-apps/react/variants/workspaces/src/pages/CountryDetailPage.tsx +215 -0
  370. package/registry/admin-apps/react/variants/workspaces/src/pages/CustomerDetailPage.tsx +237 -0
  371. package/registry/admin-apps/react/variants/workspaces/src/pages/CustomersPage.tsx +411 -0
  372. package/registry/admin-apps/react/variants/workspaces/src/pages/DashboardPage.tsx +191 -0
  373. package/registry/admin-apps/react/variants/workspaces/src/pages/EditCountryPage.tsx +285 -0
  374. package/registry/admin-apps/react/variants/workspaces/src/pages/EditCustomerPage.tsx +279 -0
  375. package/registry/admin-apps/react/variants/workspaces/src/pages/EditLanguagePage.tsx +222 -0
  376. package/registry/admin-apps/react/variants/workspaces/src/pages/LanguageDetailPage.tsx +207 -0
  377. package/registry/admin-apps/react/variants/workspaces/src/pages/LanguagesPage.tsx +353 -0
  378. package/registry/admin-apps/react/variants/workspaces/src/pages/LoginPage.tsx +149 -0
  379. package/registry/admin-apps/react/variants/workspaces/src/pages/MembersPage.tsx +349 -0
  380. package/registry/admin-apps/react/variants/workspaces/src/pages/PermissionDetailPage.tsx +123 -0
  381. package/registry/admin-apps/react/variants/workspaces/src/pages/PermissionsPage.tsx +317 -0
  382. package/registry/admin-apps/react/variants/workspaces/src/pages/RoleDetailPage.tsx +207 -0
  383. package/registry/admin-apps/react/variants/workspaces/src/pages/RolesPage.tsx +617 -0
  384. package/registry/admin-apps/react/variants/workspaces/src/pages/UserDetailPage.tsx +405 -0
  385. package/registry/admin-apps/react/variants/workspaces/src/pages/UsersPage.tsx +425 -0
  386. package/registry/admin-apps/react/variants/workspaces/src/pages/country-schema.ts +29 -0
  387. package/registry/admin-apps/react/variants/workspaces/src/pages/customer-schema.ts +34 -0
  388. package/registry/admin-apps/react/variants/workspaces/src/pages/language-schema.ts +27 -0
  389. package/registry/admin-apps/react/variants/workspaces/src/stores/auth-store.ts +169 -0
  390. package/registry/admin-apps/react/variants/workspaces/src/stores/store-context.tsx +43 -0
  391. package/registry/admin-apps/react/variants/workspaces/src/stores/workspace-store.ts +120 -0
  392. package/registry/admin-apps/react/variants/workspaces/vite.config.ts +15 -0
  393. package/registry/combos/express-drizzle/package.json +32 -0
  394. package/registry/combos/express-drizzle/scripts/materialize.mjs +104 -0
  395. package/registry/combos/express-drizzle/scripts/migrate.mjs +41 -0
  396. package/registry/combos/express-drizzle/scripts/prove-cycle.mjs +29 -0
  397. package/registry/combos/express-drizzle/scripts/seed.mjs +25 -0
  398. package/registry/combos/express-drizzle/shared/drizzle.config.ts +67 -0
  399. package/registry/combos/express-drizzle/shared/src/ability.ts +44 -0
  400. package/registry/combos/express-drizzle/shared/src/auth-core-error.middleware.ts +31 -0
  401. package/registry/combos/express-drizzle/shared/src/auth.config.ts +63 -0
  402. package/registry/combos/express-drizzle/shared/src/auth.middleware.ts +38 -0
  403. package/registry/combos/express-drizzle/shared/src/auth.router.ts +266 -0
  404. package/registry/combos/express-drizzle/shared/src/db.ts +12 -0
  405. package/registry/combos/express-drizzle/shared/src/http-error.ts +18 -0
  406. package/registry/combos/express-drizzle/shared/src/id.helper.ts +22 -0
  407. package/registry/combos/express-drizzle/shared/src/key-provider.ts +52 -0
  408. package/registry/combos/express-drizzle/shared/src/oauth.repository.ts +47 -0
  409. package/registry/combos/express-drizzle/shared/src/openapi-fragment.ts +49 -0
  410. package/registry/combos/express-drizzle/shared/src/openapi-spec.ts +493 -0
  411. package/registry/combos/express-drizzle/shared/src/pagination.ts +30 -0
  412. package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +35 -0
  413. package/registry/combos/express-drizzle/shared/src/permission-cache.ts +191 -0
  414. package/registry/combos/express-drizzle/shared/src/rate-limit.store.ts +24 -0
  415. package/registry/combos/express-drizzle/shared/src/request-context.ts +35 -0
  416. package/registry/combos/express-drizzle/shared/src/response-envelope.middleware.ts +46 -0
  417. package/registry/combos/express-drizzle/shared/src/route-tiers.ts +148 -0
  418. package/registry/combos/express-drizzle/shared/src/session.repository.ts +130 -0
  419. package/registry/combos/express-drizzle/shared/src/two-factor.repository.ts +38 -0
  420. package/registry/combos/express-drizzle/test/bootstrap.ts +40 -0
  421. package/registry/combos/express-drizzle/test/harness.ts +187 -0
  422. package/registry/combos/express-drizzle/test/prove-cycle.ts +789 -0
  423. package/registry/combos/express-drizzle/tsconfig.json +20 -0
  424. package/registry/combos/express-drizzle/variants/base/.env.example +25 -0
  425. package/registry/combos/express-drizzle/variants/base/drizzle/0000_init.sql +219 -0
  426. package/registry/combos/express-drizzle/variants/base/drizzle/0001_add_is_active_field.sql +1 -0
  427. package/registry/combos/express-drizzle/variants/base/drizzle/meta/0000_snapshot.json +1861 -0
  428. package/registry/combos/express-drizzle/variants/base/drizzle/meta/0001_snapshot.json +1868 -0
  429. package/registry/combos/express-drizzle/variants/base/drizzle/meta/_journal.json +20 -0
  430. package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +307 -0
  431. package/registry/combos/express-drizzle/variants/base/src/audit-log.repository.ts +93 -0
  432. package/registry/combos/express-drizzle/variants/base/src/auth.service.ts +528 -0
  433. package/registry/combos/express-drizzle/variants/base/src/authz.middleware.ts +58 -0
  434. package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +78 -0
  435. package/registry/combos/express-drizzle/variants/base/src/openapi-admin.ts +565 -0
  436. package/registry/combos/express-drizzle/variants/base/src/rbac.defaults.ts +218 -0
  437. package/registry/combos/express-drizzle/variants/base/src/rbac.repository.ts +606 -0
  438. package/registry/combos/express-drizzle/variants/base/src/schema.ts +425 -0
  439. package/registry/combos/express-drizzle/variants/base/src/seed.ts +138 -0
  440. package/registry/combos/express-drizzle/variants/base/test/variant-hooks.ts +179 -0
  441. package/registry/combos/express-drizzle/variants/workspaces/.env.example +30 -0
  442. package/registry/combos/express-drizzle/variants/workspaces/drizzle/0000_init.sql +251 -0
  443. package/registry/combos/express-drizzle/variants/workspaces/drizzle/0001_add_is_active_field.sql +1 -0
  444. package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/0000_snapshot.json +2155 -0
  445. package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/0001_snapshot.json +2162 -0
  446. package/registry/combos/express-drizzle/variants/workspaces/drizzle/meta/_journal.json +20 -0
  447. package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +323 -0
  448. package/registry/combos/express-drizzle/variants/workspaces/src/audit-log.repository.ts +105 -0
  449. package/registry/combos/express-drizzle/variants/workspaces/src/auth.service.ts +545 -0
  450. package/registry/combos/express-drizzle/variants/workspaces/src/authz.middleware.ts +94 -0
  451. package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +87 -0
  452. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-admin.ts +596 -0
  453. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +183 -0
  454. package/registry/combos/express-drizzle/variants/workspaces/src/rbac.defaults.ts +248 -0
  455. package/registry/combos/express-drizzle/variants/workspaces/src/rbac.repository.ts +698 -0
  456. package/registry/combos/express-drizzle/variants/workspaces/src/schema.ts +487 -0
  457. package/registry/combos/express-drizzle/variants/workspaces/src/seed.ts +173 -0
  458. package/registry/combos/express-drizzle/variants/workspaces/src/workspace.repository.ts +254 -0
  459. package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +123 -0
  460. package/registry/combos/express-drizzle/variants/workspaces/test/variant-hooks.ts +370 -0
  461. package/registry/combos/express-prisma/package.json +32 -0
  462. package/registry/combos/express-prisma/scripts/materialize.mjs +84 -0
  463. package/registry/combos/express-prisma/scripts/migrate.mjs +32 -0
  464. package/registry/combos/express-prisma/scripts/prove-cycle.mjs +30 -0
  465. package/registry/combos/express-prisma/scripts/seed.mjs +24 -0
  466. package/registry/combos/express-prisma/shared/prisma.config.ts +38 -0
  467. package/registry/combos/express-prisma/shared/src/ability.ts +44 -0
  468. package/registry/combos/express-prisma/shared/src/auth-core-error.middleware.ts +31 -0
  469. package/registry/combos/express-prisma/shared/src/auth.config.ts +63 -0
  470. package/registry/combos/express-prisma/shared/src/auth.middleware.ts +44 -0
  471. package/registry/combos/express-prisma/shared/src/auth.router.ts +269 -0
  472. package/registry/combos/express-prisma/shared/src/http-error.ts +18 -0
  473. package/registry/combos/express-prisma/shared/src/id.helper.ts +22 -0
  474. package/registry/combos/express-prisma/shared/src/key-provider.ts +52 -0
  475. package/registry/combos/express-prisma/shared/src/oauth.repository.ts +33 -0
  476. package/registry/combos/express-prisma/shared/src/openapi-fragment.ts +49 -0
  477. package/registry/combos/express-prisma/shared/src/openapi-spec.ts +487 -0
  478. package/registry/combos/express-prisma/shared/src/pagination.ts +30 -0
  479. package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +29 -0
  480. package/registry/combos/express-prisma/shared/src/permission-cache.ts +191 -0
  481. package/registry/combos/express-prisma/shared/src/rate-limit.store.ts +21 -0
  482. package/registry/combos/express-prisma/shared/src/request-context.ts +35 -0
  483. package/registry/combos/express-prisma/shared/src/response-envelope.middleware.ts +46 -0
  484. package/registry/combos/express-prisma/shared/src/route-tiers.ts +148 -0
  485. package/registry/combos/express-prisma/shared/src/session.repository.ts +140 -0
  486. package/registry/combos/express-prisma/shared/src/two-factor.repository.ts +30 -0
  487. package/registry/combos/express-prisma/test/bootstrap.ts +45 -0
  488. package/registry/combos/express-prisma/test/harness.ts +187 -0
  489. package/registry/combos/express-prisma/test/prove-cycle.ts +793 -0
  490. package/registry/combos/express-prisma/tsconfig.json +21 -0
  491. package/registry/combos/express-prisma/variants/base/.env.example +25 -0
  492. package/registry/combos/express-prisma/variants/base/prisma/migrations/20260810165456_init/migration.sql +370 -0
  493. package/registry/combos/express-prisma/variants/base/prisma/migrations/20260811151048_add_is_active_field/migration.sql +2 -0
  494. package/registry/combos/express-prisma/variants/base/prisma/migrations/migration_lock.toml +3 -0
  495. package/registry/combos/express-prisma/variants/base/prisma/schema/simple-auth-kit.prisma +332 -0
  496. package/registry/combos/express-prisma/variants/base/src/admin.router.ts +314 -0
  497. package/registry/combos/express-prisma/variants/base/src/audit-log.repository.ts +91 -0
  498. package/registry/combos/express-prisma/variants/base/src/auth.service.ts +521 -0
  499. package/registry/combos/express-prisma/variants/base/src/authz.middleware.ts +58 -0
  500. package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +93 -0
  501. package/registry/combos/express-prisma/variants/base/src/openapi-admin.ts +569 -0
  502. package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +195 -0
  503. package/registry/combos/express-prisma/variants/base/src/rbac.repository.ts +484 -0
  504. package/registry/combos/express-prisma/variants/base/src/seed.ts +115 -0
  505. package/registry/combos/express-prisma/variants/base/test/variant-hooks.ts +159 -0
  506. package/registry/combos/express-prisma/variants/workspaces/.env.example +30 -0
  507. package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/20260810165502_init/migration.sql +443 -0
  508. package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/20260811151051_add_is_active_field/migration.sql +2 -0
  509. package/registry/combos/express-prisma/variants/workspaces/prisma/migrations/migration_lock.toml +3 -0
  510. package/registry/combos/express-prisma/variants/workspaces/prisma/schema/simple-auth-kit.prisma +394 -0
  511. package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +330 -0
  512. package/registry/combos/express-prisma/variants/workspaces/src/audit-log.repository.ts +102 -0
  513. package/registry/combos/express-prisma/variants/workspaces/src/auth.service.ts +542 -0
  514. package/registry/combos/express-prisma/variants/workspaces/src/authz.middleware.ts +94 -0
  515. package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +99 -0
  516. package/registry/combos/express-prisma/variants/workspaces/src/openapi-admin.ts +601 -0
  517. package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +183 -0
  518. package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +222 -0
  519. package/registry/combos/express-prisma/variants/workspaces/src/rbac.repository.ts +527 -0
  520. package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +147 -0
  521. package/registry/combos/express-prisma/variants/workspaces/src/workspace.repository.ts +188 -0
  522. package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +123 -0
  523. package/registry/combos/express-prisma/variants/workspaces/test/variant-hooks.ts +358 -0
  524. package/registry/combos/nestjs-drizzle/package.json +36 -0
  525. package/registry/combos/nestjs-drizzle/scripts/materialize.mjs +104 -0
  526. package/registry/combos/nestjs-drizzle/scripts/migrate.mjs +34 -0
  527. package/registry/combos/nestjs-drizzle/scripts/prove-cycle.mjs +29 -0
  528. package/registry/combos/nestjs-drizzle/scripts/seed.mjs +24 -0
  529. package/registry/combos/nestjs-drizzle/shared/drizzle.config.ts +67 -0
  530. package/registry/combos/nestjs-drizzle/shared/src/ability.guard.ts +46 -0
  531. package/registry/combos/nestjs-drizzle/shared/src/ability.ts +44 -0
  532. package/registry/combos/nestjs-drizzle/shared/src/auth-core-error.filter.ts +32 -0
  533. package/registry/combos/nestjs-drizzle/shared/src/auth.config.ts +65 -0
  534. package/registry/combos/nestjs-drizzle/shared/src/auth.controller.ts +299 -0
  535. package/registry/combos/nestjs-drizzle/shared/src/auth.guard.ts +34 -0
  536. package/registry/combos/nestjs-drizzle/shared/src/db.ts +12 -0
  537. package/registry/combos/nestjs-drizzle/shared/src/dto/auth.dto.ts +429 -0
  538. package/registry/combos/nestjs-drizzle/shared/src/id.helper.ts +22 -0
  539. package/registry/combos/nestjs-drizzle/shared/src/key-provider.ts +51 -0
  540. package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +36 -0
  541. package/registry/combos/nestjs-drizzle/shared/src/pagination.ts +30 -0
  542. package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +29 -0
  543. package/registry/combos/nestjs-drizzle/shared/src/permission-cache.ts +196 -0
  544. package/registry/combos/nestjs-drizzle/shared/src/rate-limit.store.ts +26 -0
  545. package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +25 -0
  546. package/registry/combos/nestjs-drizzle/shared/src/response.interceptor.ts +48 -0
  547. package/registry/combos/nestjs-drizzle/shared/src/route-tiers.ts +144 -0
  548. package/registry/combos/nestjs-drizzle/shared/src/session.repository.ts +132 -0
  549. package/registry/combos/nestjs-drizzle/shared/src/two-factor.repository.ts +35 -0
  550. package/registry/combos/nestjs-drizzle/test/bootstrap.ts +49 -0
  551. package/registry/combos/nestjs-drizzle/test/harness.ts +187 -0
  552. package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +795 -0
  553. package/registry/combos/nestjs-drizzle/tsconfig.json +20 -0
  554. package/registry/combos/nestjs-drizzle/variants/base/.env.example +25 -0
  555. package/registry/combos/nestjs-drizzle/variants/base/drizzle/0000_init.sql +219 -0
  556. package/registry/combos/nestjs-drizzle/variants/base/drizzle/0001_add_is_active_field.sql +1 -0
  557. package/registry/combos/nestjs-drizzle/variants/base/drizzle/0002_colossal_true_believers.sql +3 -0
  558. package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0000_snapshot.json +1861 -0
  559. package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0001_snapshot.json +1868 -0
  560. package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/0002_snapshot.json +1887 -0
  561. package/registry/combos/nestjs-drizzle/variants/base/drizzle/meta/_journal.json +27 -0
  562. package/registry/combos/nestjs-drizzle/variants/base/src/admin.controller.ts +361 -0
  563. package/registry/combos/nestjs-drizzle/variants/base/src/audit-log.repository.ts +90 -0
  564. package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +83 -0
  565. package/registry/combos/nestjs-drizzle/variants/base/src/auth.service.ts +544 -0
  566. package/registry/combos/nestjs-drizzle/variants/base/src/authz.guard.ts +53 -0
  567. package/registry/combos/nestjs-drizzle/variants/base/src/dto/admin.dto.ts +130 -0
  568. package/registry/combos/nestjs-drizzle/variants/base/src/rbac.defaults.ts +218 -0
  569. package/registry/combos/nestjs-drizzle/variants/base/src/rbac.repository.ts +633 -0
  570. package/registry/combos/nestjs-drizzle/variants/base/src/schema.ts +427 -0
  571. package/registry/combos/nestjs-drizzle/variants/base/src/seed.ts +138 -0
  572. package/registry/combos/nestjs-drizzle/variants/base/test/variant-hooks.ts +179 -0
  573. package/registry/combos/nestjs-drizzle/variants/workspaces/.env.example +30 -0
  574. package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0000_init.sql +251 -0
  575. package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0001_add_is_active_field.sql +1 -0
  576. package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/0002_calm_tyger_tiger.sql +3 -0
  577. package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0000_snapshot.json +2155 -0
  578. package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0001_snapshot.json +2162 -0
  579. package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/0002_snapshot.json +2181 -0
  580. package/registry/combos/nestjs-drizzle/variants/workspaces/drizzle/meta/_journal.json +27 -0
  581. package/registry/combos/nestjs-drizzle/variants/workspaces/src/admin.controller.ts +383 -0
  582. package/registry/combos/nestjs-drizzle/variants/workspaces/src/audit-log.repository.ts +102 -0
  583. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +87 -0
  584. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +553 -0
  585. package/registry/combos/nestjs-drizzle/variants/workspaces/src/authz.guard.ts +87 -0
  586. package/registry/combos/nestjs-drizzle/variants/workspaces/src/dto/admin.dto.ts +137 -0
  587. package/registry/combos/nestjs-drizzle/variants/workspaces/src/dto/workspace.dto.ts +60 -0
  588. package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.defaults.ts +248 -0
  589. package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.repository.ts +719 -0
  590. package/registry/combos/nestjs-drizzle/variants/workspaces/src/schema.ts +489 -0
  591. package/registry/combos/nestjs-drizzle/variants/workspaces/src/seed.ts +174 -0
  592. package/registry/combos/nestjs-drizzle/variants/workspaces/src/workspace.controller.ts +123 -0
  593. package/registry/combos/nestjs-drizzle/variants/workspaces/src/workspace.repository.ts +255 -0
  594. package/registry/combos/nestjs-drizzle/variants/workspaces/test/variant-hooks.ts +370 -0
  595. package/registry/combos/nestjs-prisma/package.json +41 -0
  596. package/registry/combos/nestjs-prisma/scripts/materialize.mjs +84 -0
  597. package/registry/combos/nestjs-prisma/scripts/migrate.mjs +32 -0
  598. package/registry/combos/nestjs-prisma/scripts/prove-cycle.mjs +30 -0
  599. package/registry/combos/nestjs-prisma/scripts/seed.mjs +24 -0
  600. package/registry/combos/nestjs-prisma/shared/prisma.config.ts +38 -0
  601. package/registry/combos/nestjs-prisma/shared/src/ability.guard.ts +29 -0
  602. package/registry/combos/nestjs-prisma/shared/src/ability.ts +23 -0
  603. package/registry/combos/nestjs-prisma/shared/src/auth-core-error.filter.ts +32 -0
  604. package/registry/combos/nestjs-prisma/shared/src/auth.config.ts +71 -0
  605. package/registry/combos/nestjs-prisma/shared/src/auth.controller.ts +303 -0
  606. package/registry/combos/nestjs-prisma/shared/src/auth.guard.ts +31 -0
  607. package/registry/combos/nestjs-prisma/shared/src/dto/auth.dto.ts +438 -0
  608. package/registry/combos/nestjs-prisma/shared/src/id.helper.ts +22 -0
  609. package/registry/combos/nestjs-prisma/shared/src/key-provider.ts +42 -0
  610. package/registry/combos/nestjs-prisma/shared/src/oauth.repository.ts +32 -0
  611. package/registry/combos/nestjs-prisma/shared/src/pagination.ts +30 -0
  612. package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +29 -0
  613. package/registry/combos/nestjs-prisma/shared/src/permission-cache.ts +140 -0
  614. package/registry/combos/nestjs-prisma/shared/src/rate-limit.store.ts +26 -0
  615. package/registry/combos/nestjs-prisma/shared/src/request-context.ts +19 -0
  616. package/registry/combos/nestjs-prisma/shared/src/response.interceptor.ts +48 -0
  617. package/registry/combos/nestjs-prisma/shared/src/route-tiers.ts +102 -0
  618. package/registry/combos/nestjs-prisma/shared/src/session.repository.ts +129 -0
  619. package/registry/combos/nestjs-prisma/shared/src/two-factor.repository.ts +29 -0
  620. package/registry/combos/nestjs-prisma/test/bootstrap.ts +54 -0
  621. package/registry/combos/nestjs-prisma/test/harness.ts +186 -0
  622. package/registry/combos/nestjs-prisma/test/prove-cycle.ts +795 -0
  623. package/registry/combos/nestjs-prisma/tsconfig.json +21 -0
  624. package/registry/combos/nestjs-prisma/variants/base/.env.example +31 -0
  625. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810084937_init/migration.sql +269 -0
  626. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810122546_add_profile_audit_soft_delete/migration.sql +96 -0
  627. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810123620_add_display_name/migration.sql +3 -0
  628. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810131421_drop_signing_keys/migration.sql +3 -0
  629. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810161515_bigint_ids/migration.sql +305 -0
  630. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260810162120_drop_name_unique/migration.sql +6 -0
  631. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260811145755_add_is_active_field/migration.sql +2 -0
  632. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260811183529_add_countries_languages_customers/migration.sql +146 -0
  633. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/20260812121128_add_user_profile_details/migration.sql +4 -0
  634. package/registry/combos/nestjs-prisma/variants/base/prisma/migrations/migration_lock.toml +3 -0
  635. package/registry/combos/nestjs-prisma/variants/base/prisma/schema/simple-auth-kit.prisma +437 -0
  636. package/registry/combos/nestjs-prisma/variants/base/src/admin.controller.ts +684 -0
  637. package/registry/combos/nestjs-prisma/variants/base/src/audit-log.gateway.ts +51 -0
  638. package/registry/combos/nestjs-prisma/variants/base/src/audit-log.repository.ts +111 -0
  639. package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +87 -0
  640. package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +618 -0
  641. package/registry/combos/nestjs-prisma/variants/base/src/authz.guard.ts +34 -0
  642. package/registry/combos/nestjs-prisma/variants/base/src/country.repository.ts +149 -0
  643. package/registry/combos/nestjs-prisma/variants/base/src/customer.repository.ts +169 -0
  644. package/registry/combos/nestjs-prisma/variants/base/src/docs.ts +60 -0
  645. package/registry/combos/nestjs-prisma/variants/base/src/dto/admin.dto.ts +130 -0
  646. package/registry/combos/nestjs-prisma/variants/base/src/dto/country.dto.ts +120 -0
  647. package/registry/combos/nestjs-prisma/variants/base/src/dto/customer.dto.ts +147 -0
  648. package/registry/combos/nestjs-prisma/variants/base/src/dto/language.dto.ts +93 -0
  649. package/registry/combos/nestjs-prisma/variants/base/src/language.repository.ts +135 -0
  650. package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +193 -0
  651. package/registry/combos/nestjs-prisma/variants/base/src/rbac.repository.ts +514 -0
  652. package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +89 -0
  653. package/registry/combos/nestjs-prisma/variants/base/test/variant-hooks.ts +135 -0
  654. package/registry/combos/nestjs-prisma/variants/workspaces/.env.example +30 -0
  655. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810084938_init/migration.sql +308 -0
  656. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810123450_add_profile_audit_soft_delete/migration.sql +96 -0
  657. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810123632_add_display_name/migration.sql +3 -0
  658. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810124243_workspace_audit_soft_delete/migration.sql +25 -0
  659. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810131432_drop_signing_keys/migration.sql +3 -0
  660. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810161551_bigint_ids/migration.sql +393 -0
  661. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260810162131_drop_name_unique/migration.sql +3 -0
  662. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260811145805_add_is_active_field/migration.sql +2 -0
  663. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/20260813120000_add_countries_languages_customers/migration.sql +158 -0
  664. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/migrations/migration_lock.toml +3 -0
  665. package/registry/combos/nestjs-prisma/variants/workspaces/prisma/schema/simple-auth-kit.prisma +521 -0
  666. package/registry/combos/nestjs-prisma/variants/workspaces/src/admin.controller.ts +733 -0
  667. package/registry/combos/nestjs-prisma/variants/workspaces/src/audit-log.repository.ts +98 -0
  668. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +83 -0
  669. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +509 -0
  670. package/registry/combos/nestjs-prisma/variants/workspaces/src/authz.guard.ts +69 -0
  671. package/registry/combos/nestjs-prisma/variants/workspaces/src/country.repository.ts +158 -0
  672. package/registry/combos/nestjs-prisma/variants/workspaces/src/customer.repository.ts +179 -0
  673. package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/admin.dto.ts +128 -0
  674. package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/country.dto.ts +120 -0
  675. package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/customer.dto.ts +147 -0
  676. package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/language.dto.ts +93 -0
  677. package/registry/combos/nestjs-prisma/variants/workspaces/src/dto/workspace.dto.ts +60 -0
  678. package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +143 -0
  679. package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +211 -0
  680. package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.repository.ts +505 -0
  681. package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +122 -0
  682. package/registry/combos/nestjs-prisma/variants/workspaces/src/workspace.controller.ts +111 -0
  683. package/registry/combos/nestjs-prisma/variants/workspaces/src/workspace.repository.ts +177 -0
  684. package/registry/combos/nestjs-prisma/variants/workspaces/test/variant-hooks.ts +333 -0
  685. package/registry/core/crypto.ts +43 -0
  686. package/registry/core/oauth.ts +159 -0
  687. package/registry/core/package.json +20 -0
  688. package/registry/core/password-reset.ts +42 -0
  689. package/registry/core/rate-limit.ts +33 -0
  690. package/registry/core/rbac.ts +4 -0
  691. package/registry/core/session-policy.ts +96 -0
  692. package/registry/core/test/crypto.test.ts +25 -0
  693. package/registry/core/test/oauth.test.ts +213 -0
  694. package/registry/core/test/password-reset.test.ts +77 -0
  695. package/registry/core/test/rate-limit.test.ts +41 -0
  696. package/registry/core/test/rbac.test.ts +20 -0
  697. package/registry/core/test/session-policy.test.ts +209 -0
  698. package/registry/core/test/test-keys.ts +6 -0
  699. package/registry/core/test/token-service.test.ts +81 -0
  700. package/registry/core/test/two-factor.test.ts +72 -0
  701. package/registry/core/token-service.ts +143 -0
  702. package/registry/core/tsconfig.json +5 -0
  703. package/registry/core/two-factor.ts +108 -0
  704. package/registry/core/types.ts +127 -0
  705. package/registry/mobile-apps/README.md +65 -0
  706. package/registry/mobile-apps/bare-rn/shared/.bundle/config +2 -0
  707. package/registry/mobile-apps/bare-rn/shared/.eslintrc.js +4 -0
  708. package/registry/mobile-apps/bare-rn/shared/.prettierrc.js +5 -0
  709. package/registry/mobile-apps/bare-rn/shared/.watchmanconfig +1 -0
  710. package/registry/mobile-apps/bare-rn/shared/App.tsx +28 -0
  711. package/registry/mobile-apps/bare-rn/shared/Gemfile +17 -0
  712. package/registry/mobile-apps/bare-rn/shared/README.md +97 -0
  713. package/registry/mobile-apps/bare-rn/shared/__mocks__/reactNativeConfigMock.js +3 -0
  714. package/registry/mobile-apps/bare-rn/shared/__mocks__/styleMock.js +1 -0
  715. package/registry/mobile-apps/bare-rn/shared/__tests__/App.test.tsx +13 -0
  716. package/registry/mobile-apps/bare-rn/shared/android/app/build.gradle +121 -0
  717. package/registry/mobile-apps/bare-rn/shared/android/app/debug.keystore +0 -0
  718. package/registry/mobile-apps/bare-rn/shared/android/app/proguard-rules.pro +10 -0
  719. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/AndroidManifest.xml +27 -0
  720. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/java/com/mobilebarern/MainActivity.kt +22 -0
  721. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/java/com/mobilebarern/MainApplication.kt +27 -0
  722. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  723. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  724. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  725. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  726. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  727. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  728. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  729. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  730. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  731. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  732. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  733. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/values/strings.xml +3 -0
  734. package/registry/mobile-apps/bare-rn/shared/android/app/src/main/res/values/styles.xml +9 -0
  735. package/registry/mobile-apps/bare-rn/shared/android/build.gradle +21 -0
  736. package/registry/mobile-apps/bare-rn/shared/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  737. package/registry/mobile-apps/bare-rn/shared/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  738. package/registry/mobile-apps/bare-rn/shared/android/gradle.properties +44 -0
  739. package/registry/mobile-apps/bare-rn/shared/android/gradlew +248 -0
  740. package/registry/mobile-apps/bare-rn/shared/android/gradlew.bat +98 -0
  741. package/registry/mobile-apps/bare-rn/shared/android/settings.gradle +6 -0
  742. package/registry/mobile-apps/bare-rn/shared/app.json +4 -0
  743. package/registry/mobile-apps/bare-rn/shared/babel.config.js +6 -0
  744. package/registry/mobile-apps/bare-rn/shared/global.css +2 -0
  745. package/registry/mobile-apps/bare-rn/shared/index.js +10 -0
  746. package/registry/mobile-apps/bare-rn/shared/ios/.xcode.env +11 -0
  747. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/AppDelegate.swift +48 -0
  748. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  749. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Images.xcassets/Contents.json +6 -0
  750. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/Info.plist +59 -0
  751. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/LaunchScreen.storyboard +47 -0
  752. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn/PrivacyInfo.xcprivacy +37 -0
  753. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn.xcodeproj/project.pbxproj +475 -0
  754. package/registry/mobile-apps/bare-rn/shared/ios/MobileBareRn.xcodeproj/xcshareddata/xcschemes/MobileBareRn.xcscheme +88 -0
  755. package/registry/mobile-apps/bare-rn/shared/ios/Podfile +34 -0
  756. package/registry/mobile-apps/bare-rn/shared/jest.config.js +17 -0
  757. package/registry/mobile-apps/bare-rn/shared/metro.config.js +55 -0
  758. package/registry/mobile-apps/bare-rn/shared/nativewind-env.d.ts +1 -0
  759. package/registry/mobile-apps/bare-rn/shared/src/api/tokenStorage.ts +29 -0
  760. package/registry/mobile-apps/bare-rn/shared/src/components/FormInput.tsx +21 -0
  761. package/registry/mobile-apps/bare-rn/shared/src/components/PrimaryButton.tsx +47 -0
  762. package/registry/mobile-apps/bare-rn/shared/src/screens/LoginScreen.tsx +58 -0
  763. package/registry/mobile-apps/bare-rn/shared/src/screens/SessionsScreen.tsx +77 -0
  764. package/registry/mobile-apps/bare-rn/shared/src/screens/SignupScreen.tsx +54 -0
  765. package/registry/mobile-apps/bare-rn/shared/src/screens/TwoFactorScreen.tsx +59 -0
  766. package/registry/mobile-apps/bare-rn/shared/tailwind.config.js +9 -0
  767. package/registry/mobile-apps/bare-rn/shared/tsconfig.json +17 -0
  768. package/registry/mobile-apps/bare-rn/variants/base/package.json +58 -0
  769. package/registry/mobile-apps/bare-rn/variants/base/src/api/authClient.ts +14 -0
  770. package/registry/mobile-apps/bare-rn/variants/base/src/navigation/RootNavigator.tsx +58 -0
  771. package/registry/mobile-apps/bare-rn/variants/base/src/navigation/types.ts +15 -0
  772. package/registry/mobile-apps/bare-rn/variants/base/src/screens/HomeScreen.tsx +64 -0
  773. package/registry/mobile-apps/bare-rn/variants/base/src/store/authStore.ts +143 -0
  774. package/registry/mobile-apps/bare-rn/variants/workspaces/package.json +58 -0
  775. package/registry/mobile-apps/bare-rn/variants/workspaces/src/api/authClient.ts +22 -0
  776. package/registry/mobile-apps/bare-rn/variants/workspaces/src/navigation/RootNavigator.tsx +72 -0
  777. package/registry/mobile-apps/bare-rn/variants/workspaces/src/navigation/types.ts +18 -0
  778. package/registry/mobile-apps/bare-rn/variants/workspaces/src/screens/HomeScreen.tsx +85 -0
  779. package/registry/mobile-apps/bare-rn/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +165 -0
  780. package/registry/mobile-apps/bare-rn/variants/workspaces/src/store/authStore.ts +177 -0
  781. package/registry/mobile-apps/bare-rn/variants/workspaces/src/store/workspaceStore.ts +90 -0
  782. package/registry/mobile-apps/bare-rn/variants/workspaces/src/workspace/activeWorkspace.ts +55 -0
  783. package/registry/mobile-apps/expo/shared/App.tsx +15 -0
  784. package/registry/mobile-apps/expo/shared/assets/android-icon-background.png +0 -0
  785. package/registry/mobile-apps/expo/shared/assets/android-icon-foreground.png +0 -0
  786. package/registry/mobile-apps/expo/shared/assets/android-icon-monochrome.png +0 -0
  787. package/registry/mobile-apps/expo/shared/assets/favicon.png +0 -0
  788. package/registry/mobile-apps/expo/shared/assets/icon.png +0 -0
  789. package/registry/mobile-apps/expo/shared/assets/splash-icon.png +0 -0
  790. package/registry/mobile-apps/expo/shared/babel.config.js +6 -0
  791. package/registry/mobile-apps/expo/shared/global.css +2 -0
  792. package/registry/mobile-apps/expo/shared/index.ts +8 -0
  793. package/registry/mobile-apps/expo/shared/metro.config.js +6 -0
  794. package/registry/mobile-apps/expo/shared/nativewind-env.d.ts +3 -0
  795. package/registry/mobile-apps/expo/shared/postcss.config.mjs +5 -0
  796. package/registry/mobile-apps/expo/shared/src/screens/LoginScreen.tsx +81 -0
  797. package/registry/mobile-apps/expo/shared/src/screens/SessionsScreen.tsx +77 -0
  798. package/registry/mobile-apps/expo/shared/src/screens/SignupScreen.tsx +77 -0
  799. package/registry/mobile-apps/expo/shared/src/screens/TwoFactorScreen.tsx +59 -0
  800. package/registry/mobile-apps/expo/shared/src/storage/asyncStorageTokenStorage.ts +32 -0
  801. package/registry/mobile-apps/expo/shared/tsconfig.json +6 -0
  802. package/registry/mobile-apps/expo/variants/base/app.json +25 -0
  803. package/registry/mobile-apps/expo/variants/base/package.json +35 -0
  804. package/registry/mobile-apps/expo/variants/base/src/api/authClient.ts +17 -0
  805. package/registry/mobile-apps/expo/variants/base/src/navigation/RootNavigator.tsx +58 -0
  806. package/registry/mobile-apps/expo/variants/base/src/navigation/types.ts +10 -0
  807. package/registry/mobile-apps/expo/variants/base/src/screens/HomeScreen.tsx +75 -0
  808. package/registry/mobile-apps/expo/variants/base/src/store/authStore.ts +146 -0
  809. package/registry/mobile-apps/expo/variants/workspaces/app.json +25 -0
  810. package/registry/mobile-apps/expo/variants/workspaces/package.json +35 -0
  811. package/registry/mobile-apps/expo/variants/workspaces/src/api/authClient.ts +21 -0
  812. package/registry/mobile-apps/expo/variants/workspaces/src/navigation/RootNavigator.tsx +82 -0
  813. package/registry/mobile-apps/expo/variants/workspaces/src/navigation/types.ts +20 -0
  814. package/registry/mobile-apps/expo/variants/workspaces/src/screens/HomeScreen.tsx +94 -0
  815. package/registry/mobile-apps/expo/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +175 -0
  816. package/registry/mobile-apps/expo/variants/workspaces/src/store/authStore.ts +179 -0
  817. package/registry/mobile-apps/expo/variants/workspaces/src/store/workspaceStore.ts +90 -0
  818. package/registry/mobile-apps/expo/variants/workspaces/src/workspace/activeWorkspace.ts +55 -0
  819. package/registry.json +263 -0
  820. package/simple-auth-kit.ts +715 -0
@@ -0,0 +1,358 @@
1
+ // Variant hooks for the workspace variant. See test/harness.ts for the contract.
2
+ //
3
+ // Admin authority here is per workspace, so making someone an admin means giving them a
4
+ // workspace to be admin *of*, and every admin call afterwards names it. `proveVariantProperties`
5
+ // carries the security property this whole model rests on: a role held in one workspace grants
6
+ // nothing in another.
7
+ import "dotenv/config";
8
+ import { PrismaPg } from "@prisma/adapter-pg";
9
+ import { PrismaClient } from "../generated/prisma/client.js";
10
+ import { POLICY_VERSION_KEY } from "../src/permission-cache.js";
11
+ import { permissionCacheStore } from "./bootstrap.js";
12
+ import { renewingToken, type AdminSession, type Principal, type ProofContext, type VariantHooks } from "./harness.js";
13
+
14
+ const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env["DATABASE_URL"] }) });
15
+
16
+ interface Workspace {
17
+ id: string;
18
+ name: string;
19
+ roles: string[];
20
+ }
21
+
22
+ /** Signs up a fresh user, creates a workspace they administer, and returns both. */
23
+ async function newAdminWithWorkspace(ctx: ProofContext, label: string) {
24
+ const email = ctx.uniqueEmail(label);
25
+ const password = `${label}-pw-12345`;
26
+ const tokens = await ctx.signup(email, password);
27
+ const userId = (await ctx.call("GET", "/auth/me", { token: tokens.accessToken })).body.sub as string;
28
+ const created = await ctx.call("POST", "/workspaces", { token: tokens.accessToken, body: { name: `${label}-workspace` } });
29
+ return { email, password, userId, token: tokens.accessToken, workspace: created.body as Workspace };
30
+ }
31
+
32
+ export const hooks: VariantHooks = {
33
+ variant: "workspaces",
34
+
35
+ async makeAdmin(ctx: ProofContext, principal: Principal): Promise<AdminSession> {
36
+ const created = await ctx.call("POST", "/workspaces", { token: principal.tokens.accessToken, body: { name: "proof-workspace" } });
37
+ ctx.assert(created.status === 201 || created.status === 200, `creating a workspace succeeds (got ${created.status})`);
38
+ const workspace = created.body as Workspace;
39
+ ctx.assert(workspace.roles.includes("admin"), "the creator of a workspace is an admin member of it");
40
+ // No re-login, and no fresh token needed: the access token is workspace-agnostic and carries
41
+ // no authorization, so the new membership is in effect immediately and the token never goes
42
+ // stale in any way that matters to authorization. `freshToken` re-logs in anyway, because the
43
+ // 2-second access-token TTL in test/bootstrap.ts can expire a token mid-section.
44
+ return {
45
+ token: principal.tokens.accessToken,
46
+ workspaceId: workspace.id,
47
+ freshToken: renewingToken(async () => {
48
+ const res = await ctx.call("POST", "/auth/login", { body: { identifier: principal.email, password: principal.password } });
49
+ return (res.body as { accessToken: string }).accessToken;
50
+ }),
51
+ };
52
+ },
53
+
54
+ async admitUser(ctx: ProofContext, admin: AdminSession, user: { userId: string; email: string }): Promise<void> {
55
+ const added = await ctx.call("POST", "/workspaces/members", { token: await admin.freshToken(), workspaceId: admin.workspaceId, body: { email: user.email } });
56
+ ctx.assert(added.status === 201 || added.status === 200, `adding a user to the workspace succeeds (got ${added.status})`);
57
+ },
58
+
59
+ async proveVariantProperties(ctx: ProofContext): Promise<void> {
60
+ // Two independent workspaces, each with its own admin.
61
+ const alpha = await newAdminWithWorkspace(ctx, "alpha");
62
+ const beta = await newAdminWithWorkspace(ctx, "beta");
63
+
64
+ ctx.assert(alpha.workspace.id !== beta.workspace.id, "two workspaces are distinct");
65
+
66
+ // --- a non-member cannot even see a workspace exists ---
67
+ const outsider = await ctx.call("GET", "/auth/admin/users", { token: alpha.token, workspaceId: beta.workspace.id });
68
+ ctx.assert(outsider.status === 403, `an admin of one workspace is refused in a workspace they are not a member of (got ${outsider.status})`);
69
+
70
+ const bogus = await ctx.call("GET", "/auth/admin/users", { token: alpha.token, workspaceId: "00000000-0000-0000-0000-000000000000" });
71
+ ctx.assert(bogus.status === 403, `an unknown workspace id is refused the same way as someone else's (got ${bogus.status})`);
72
+
73
+ const noHeader = await ctx.call("GET", "/auth/admin/users", { token: alpha.token });
74
+ ctx.assert(noHeader.status === 403, `a workspace-scoped route refuses a request that names no workspace (got ${noHeader.status})`);
75
+
76
+ // --- alpha joins beta as a plain member: their admin role in alpha must not follow them ---
77
+ const added = await ctx.call("POST", "/workspaces/members", { token: beta.token, workspaceId: beta.workspace.id, body: { email: alpha.email } });
78
+ ctx.assert(added.status === 201 || added.status === 200, `an admin can add an existing user to their workspace (got ${added.status})`);
79
+ const alphaInBeta = added.body as { memberId: string; roles: string[] };
80
+ ctx.assert(alphaInBeta.roles.join() === "member", `a new member defaults to the member role (got ${alphaInBeta.roles.join()})`);
81
+
82
+ const meInAlpha = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: alpha.workspace.id });
83
+ ctx.assert(meInAlpha.body.roles.includes("admin"), "the same token is admin in its own workspace");
84
+
85
+ const meInBeta = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: beta.workspace.id });
86
+ ctx.assert(!meInBeta.body.roles.includes("admin"), "THE core property: the admin role held in workspace A is absent in workspace B");
87
+ ctx.assert(meInBeta.body.roles.includes("member"), "…and only the roles granted by workspace B's membership apply there");
88
+
89
+ const adminAttemptInBeta = await ctx.call("GET", "/auth/admin/users", { token: alpha.token, workspaceId: beta.workspace.id });
90
+ ctx.assert(adminAttemptInBeta.status === 403, `being an admin elsewhere does not unlock workspace B's admin endpoints (got ${adminAttemptInBeta.status})`);
91
+
92
+ // --- roles are per workspace: the same role name can mean different things ---
93
+ const roleInBeta = await ctx.call("POST", "/auth/admin/roles", { token: beta.token, workspaceId: beta.workspace.id, body: { slug: "shared-name" } });
94
+ const roleInAlpha = await ctx.call("POST", "/auth/admin/roles", { token: alpha.token, workspaceId: alpha.workspace.id, body: { slug: "shared-name" } });
95
+ ctx.assert(roleInBeta.status === 201 && roleInAlpha.status === 201, "the same role name can exist independently in two workspaces");
96
+
97
+ await ctx.call("POST", `/auth/admin/roles/${(roleInBeta.body as { id: string }).id}/permissions`, {
98
+ token: beta.token,
99
+ workspaceId: beta.workspace.id,
100
+ body: { permission: "beta:only" },
101
+ });
102
+ await ctx.call("POST", `/auth/admin/users/${alpha.userId}/roles`, { token: beta.token, workspaceId: beta.workspace.id, body: { role: "shared-name" } });
103
+
104
+ const permsInBeta = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: beta.workspace.id });
105
+ ctx.assert(permsInBeta.body.permissions.includes("beta:only"), "a role assigned in workspace B grants its permissions in workspace B");
106
+ const permsInAlpha = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: alpha.workspace.id });
107
+ ctx.assert(!permsInAlpha.body.permissions.includes("beta:only"), "…and the identically-named role in workspace A does not carry them");
108
+
109
+ // --- direct grants attach to the membership, not the user ---
110
+ const directGrant = await ctx.call("POST", `/auth/admin/users/${alpha.userId}/permissions`, {
111
+ token: beta.token,
112
+ workspaceId: beta.workspace.id,
113
+ body: { permission: "beta:direct" },
114
+ });
115
+ ctx.assert(directGrant.status === 201 || directGrant.status === 200, `a direct grant inside workspace B succeeds (got ${directGrant.status})`);
116
+ const afterGrantBeta = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: beta.workspace.id });
117
+ ctx.assert(afterGrantBeta.body.permissions.includes("beta:direct"), "the direct grant applies in workspace B");
118
+ const afterGrantAlpha = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: alpha.workspace.id });
119
+ ctx.assert(!afterGrantAlpha.body.permissions.includes("beta:direct"), "the direct grant does not leak into workspace A");
120
+
121
+ // --- an admin cannot act on a user who is not in their workspace ---
122
+ const crossBlock = await ctx.call("POST", `/auth/admin/users/${beta.userId}/block`, { token: alpha.token, workspaceId: alpha.workspace.id });
123
+ ctx.assert(crossBlock.status === 404, `an admin cannot block a user who is not a member of their workspace (got ${crossBlock.status})`);
124
+
125
+ // --- audit log is scoped too ---
126
+ const betaAudit = await ctx.call("GET", "/auth/admin/audit-log", { token: beta.token, workspaceId: beta.workspace.id });
127
+ ctx.assert(betaAudit.status === 200, `an admin can read their workspace's audit log (got ${betaAudit.status})`);
128
+ ctx.assert(
129
+ (betaAudit.body.items as Array<{ workspaceId: string | null }>).every((e) => e.workspaceId === beta.workspace.id),
130
+ "the audit log never returns another workspace's entries",
131
+ );
132
+
133
+ // --- membership management ---
134
+ const myWorkspaces = await ctx.call("GET", "/workspaces", { token: alpha.token });
135
+ ctx.assert(myWorkspaces.status === 200, `a user can list the workspaces they belong to (got ${myWorkspaces.status})`);
136
+ const ids = (myWorkspaces.body as Workspace[]).map((w) => w.id);
137
+ ctx.assert(ids.includes(alpha.workspace.id) && ids.includes(beta.workspace.id), "the list covers every workspace the user is a member of");
138
+
139
+ const promote = await ctx.call("PUT", `/workspaces/members/${alphaInBeta.memberId}/roles`, {
140
+ token: beta.token,
141
+ workspaceId: beta.workspace.id,
142
+ body: { roles: ["member", "shared-name"] },
143
+ });
144
+ ctx.assert(promote.status === 200, `an admin can set a member's roles (got ${promote.status})`);
145
+
146
+ const memberList = await ctx.call("GET", "/workspaces/members", { token: alpha.token, workspaceId: beta.workspace.id });
147
+ ctx.assert(memberList.status === 200 && memberList.body.length === 2, `any member can list the workspace's members (got ${memberList.status})`);
148
+
149
+ const removed = await ctx.call("DELETE", `/workspaces/members/${alphaInBeta.memberId}`, { token: beta.token, workspaceId: beta.workspace.id });
150
+ ctx.assert(removed.status === 200, `an admin can remove a member (got ${removed.status})`);
151
+
152
+ const afterRemoval = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: beta.workspace.id });
153
+ ctx.assert(afterRemoval.status === 403, `a removed member loses access to the workspace immediately (got ${afterRemoval.status})`);
154
+
155
+ const stillInAlpha = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: alpha.workspace.id });
156
+ ctx.assert(stillInAlpha.status === 200 && stillInAlpha.body.roles.includes("admin"), "…while their own workspace is untouched — the same login still works");
157
+
158
+ // --- revocation is immediate here: the context is resolved per request, not per token ---
159
+ const immediate = await ctx.call("POST", `/auth/admin/users/${beta.userId}/permissions`, {
160
+ token: beta.token,
161
+ workspaceId: beta.workspace.id,
162
+ body: { permission: "probe:immediate" },
163
+ });
164
+ ctx.assert(immediate.status === 201 || immediate.status === 200, `a direct grant to oneself succeeds (got ${immediate.status})`);
165
+ const seen = await ctx.call("GET", "/auth/me", { token: beta.token, workspaceId: beta.workspace.id });
166
+ ctx.assert(seen.body.permissions.includes("probe:immediate"), "the grant is visible on the very next request, with no re-login");
167
+ await ctx.call("POST", `/auth/admin/users/${beta.userId}/permissions/${encodeURIComponent("probe:immediate")}/revoke`, {
168
+ token: beta.token,
169
+ workspaceId: beta.workspace.id,
170
+ });
171
+ const gone = await ctx.call("GET", "/auth/me", { token: beta.token, workspaceId: beta.workspace.id });
172
+ ctx.assert(!gone.body.permissions.includes("probe:immediate"), "…and the revocation lands on the very next request too, on the same token");
173
+
174
+ await proveNewWorkspaceIsProvisioned(ctx);
175
+ await proveWorkspaceHeaderCannotBeSkipped(ctx, alpha);
176
+ await proveTheCacheIsScopedAndReal(ctx, alpha, beta);
177
+ await proveARawDatabaseEditChangesEnforcement(ctx, beta);
178
+ },
179
+ };
180
+
181
+ /**
182
+ * THE lockout test. `Role` is unique per `[workspaceId, name]`, so a workspace created through
183
+ * POST /workspaces has no `Role` rows of its own — its creator's `["admin", "member"]` would be
184
+ * two names with nothing behind them. Before `WorkspaceRepository.create` provisioned the
185
+ * default roles in the same transaction, every assertion below returned 403: the creator was
186
+ * locked out of the workspace they had just made, with no way back in, because the seeder only
187
+ * ever provisions the workspace *it* creates.
188
+ *
189
+ * So this walks the entire administrative surface of a brand-new workspace, in order, as the
190
+ * person who created it thirty milliseconds earlier.
191
+ */
192
+ async function proveNewWorkspaceIsProvisioned(ctx: ProofContext): Promise<void> {
193
+ const creator = await newAdminWithWorkspace(ctx, "fresh");
194
+ const ws = creator.workspace.id;
195
+ const as = { token: creator.token, workspaceId: ws };
196
+ ctx.assert(creator.workspace.roles.includes("admin"), "the creator of a brand-new workspace holds the admin role in it");
197
+
198
+ const guestEmail = ctx.uniqueEmail("fresh-guest");
199
+ const guestTokens = await ctx.signup(guestEmail, "fresh-guest-pw-1");
200
+ const guestId = (await ctx.call("GET", "/auth/me", { token: guestTokens.accessToken })).body.sub as string;
201
+
202
+ const ok = (res: { status: number }) => res.status === 200 || res.status === 201;
203
+
204
+ const added = await ctx.call("POST", "/workspaces/members", { ...as, body: { email: guestEmail } });
205
+ ctx.assert(ok(added), `members:manage — the creator can add a member (got ${added.status})`);
206
+ const memberId = (added.body as { memberId: string }).memberId;
207
+
208
+ ctx.assert(ok(await ctx.call("GET", "/auth/admin/users", as)), "users:read — the creator can list the members");
209
+ ctx.assert(ok(await ctx.call("GET", "/auth/admin/audit-log", as)), "audit-log:read — the creator can read the audit log");
210
+
211
+ const role = await ctx.call("POST", "/auth/admin/roles", { ...as, body: { slug: "fresh-role" } });
212
+ ctx.assert(ok(role), `roles:manage — the creator can define a role (got ${role.status})`);
213
+ const attach = await ctx.call("POST", `/auth/admin/roles/${(role.body as { id: string }).id}/permissions`, { ...as, body: { permission: "fresh:probe" } });
214
+ ctx.assert(ok(attach), `roles:manage — the creator can say what that role carries (got ${attach.status})`);
215
+
216
+ ctx.assert(ok(await ctx.call("POST", `/auth/admin/users/${guestId}/roles`, { ...as, body: { role: "fresh-role" } })), "roles:assign — assign a role");
217
+ ctx.assert(ok(await ctx.call("POST", `/auth/admin/users/${guestId}/roles/fresh-role/revoke`, as)), "roles:assign — revoke a role");
218
+
219
+ ctx.assert(
220
+ ok(await ctx.call("POST", `/auth/admin/users/${guestId}/permissions`, { ...as, body: { permission: "fresh:direct" } })),
221
+ "permissions:grant — grant directly",
222
+ );
223
+ ctx.assert(
224
+ ok(await ctx.call("POST", `/auth/admin/users/${guestId}/permissions/${encodeURIComponent("fresh:direct")}/revoke`, as)),
225
+ "permissions:grant — revoke a direct grant",
226
+ );
227
+
228
+ ctx.assert(ok(await ctx.call("POST", `/auth/admin/users/${guestId}/block`, as)), "users:block — block a member");
229
+ ctx.assert(ok(await ctx.call("POST", `/auth/admin/users/${guestId}/unblock`, as)), "users:block — unblock a member");
230
+
231
+ ctx.assert(ok(await ctx.call("PUT", `/workspaces/members/${memberId}/roles`, { ...as, body: { roles: ["member"] } })), "roles:assign — set a member's roles");
232
+ ctx.assert(ok(await ctx.call("DELETE", `/workspaces/members/${memberId}`, as)), "members:manage — remove a member");
233
+
234
+ // The other half of the same property: the roles are provisioned, not inherited. A second
235
+ // workspace's "admin" is a different row that happens to share a name.
236
+ const other = await newAdminWithWorkspace(ctx, "fresh-other");
237
+ const trespass = await ctx.call("GET", "/auth/admin/users", { token: creator.token, workspaceId: other.workspace.id });
238
+ ctx.assert(trespass.status === 403, `being provisioned as admin of one new workspace grants nothing in another (got ${trespass.status})`);
239
+ }
240
+
241
+ /**
242
+ * `X-Workspace-Id` is the only thing that says which workspace a request acts in, so "the header
243
+ * is missing or junk" must never be a way to reach a handler with no workspace resolved. The
244
+ * failure mode being ruled out is a permissive empty context, a silent fall-back to some default
245
+ * workspace, or a handler running with `req.authz` undefined.
246
+ *
247
+ * All of them must deny, and deny the same way a workspace you are not a member of does — a
248
+ * caller must not be able to tell "no such workspace" from "not yours" from "you sent nothing".
249
+ */
250
+ async function proveWorkspaceHeaderCannotBeSkipped(ctx: ProofContext, member: { token: string; workspace: Workspace }): Promise<void> {
251
+ const cases: Array<[string, Record<string, string>]> = [
252
+ ["absent", {}],
253
+ ["empty", { "x-workspace-id": "" }],
254
+ ["whitespace", { "x-workspace-id": " " }],
255
+ ["not a uuid", { "x-workspace-id": "not-a-uuid" }],
256
+ ["a sql fragment", { "x-workspace-id": "' OR 1=1 --" }],
257
+ ["absurdly long", { "x-workspace-id": "x".repeat(4096) }],
258
+ ];
259
+
260
+ for (const [label, headers] of cases) {
261
+ const admin = await ctx.call("GET", "/auth/admin/users", { token: member.token, headers });
262
+ ctx.assert(admin.status === 403, `a permission-gated admin route denies a request whose workspace id is ${label} (got ${admin.status})`);
263
+
264
+ const members = await ctx.call("GET", "/workspaces/members", { token: member.token, headers });
265
+ ctx.assert(members.status === 403, `a member-gated workspace route denies a request whose workspace id is ${label} (got ${members.status})`);
266
+ }
267
+
268
+ // Indistinguishability, spelled out: an id that exists but is not yours and an id that exists
269
+ // nowhere must be the same answer, body included, or the 403 becomes a workspace oracle.
270
+ const stranger = await newAdminWithWorkspace(ctx, "stranger");
271
+ const notYours = await ctx.call("GET", "/auth/admin/users", { token: member.token, workspaceId: stranger.workspace.id });
272
+ const notReal = await ctx.call("GET", "/auth/admin/users", { token: member.token, workspaceId: "00000000-0000-0000-0000-000000000000" });
273
+ ctx.assert(
274
+ notYours.status === notReal.status && notYours.body?.message === notReal.body?.message,
275
+ `someone else's workspace and a nonexistent one answer identically (${notYours.status}/${notYours.body?.message} vs ${notReal.status}/${notReal.body?.message})`,
276
+ );
277
+
278
+ // /auth/me is the one route that works either way, because clients call it before they know
279
+ // which workspaces exist. It must answer with an empty authorization context, never with one
280
+ // borrowed from an arbitrary workspace the user happens to belong to.
281
+ const meNoHeader = await ctx.call("GET", "/auth/me", { token: member.token });
282
+ ctx.assert(meNoHeader.status === 200, `GET /auth/me answers without a workspace header (got ${meNoHeader.status})`);
283
+ ctx.assert(
284
+ Array.isArray(meNoHeader.body?.roles) && meNoHeader.body.roles.length === 0 && meNoHeader.body.permissions.length === 0,
285
+ `…with empty roles and permissions rather than some workspace's (got roles=${JSON.stringify(meNoHeader.body?.roles)})`,
286
+ );
287
+ ctx.assert(meNoHeader.body?.sub && meNoHeader.body?.sessionId, "…while still identifying the user, which is what makes it callable before any workspace is chosen");
288
+
289
+ const meEmptyHeader = await ctx.call("GET", "/auth/me", { token: member.token, headers: { "x-workspace-id": "" } });
290
+ ctx.assert(
291
+ meEmptyHeader.status === 200 && meEmptyHeader.body.roles.length === 0,
292
+ `an empty workspace header is treated as naming no workspace, not as naming one (got ${meEmptyHeader.status})`,
293
+ );
294
+
295
+ const meBogusHeader = await ctx.call("GET", "/auth/me", { token: member.token, workspaceId: "00000000-0000-0000-0000-000000000000" });
296
+ ctx.assert(meBogusHeader.status === 403, `but a workspace id that is named and not yours is refused outright, even here (got ${meBogusHeader.status})`);
297
+ }
298
+
299
+ /**
300
+ * The permission cache is keyed on the *membership*, not the user, so invalidating one workspace
301
+ * must not flush another — and a cache nobody can prove is working is a bug surface. These
302
+ * assertions read the very store the app resolves through (`test/bootstrap.ts` hands it to
303
+ * `AuthModule.forRoot`), so "the second request did not touch the database" is a measurement.
304
+ */
305
+ async function proveTheCacheIsScopedAndReal(
306
+ ctx: ProofContext,
307
+ alpha: { token: string; workspace: Workspace },
308
+ beta: { token: string; userId: string; workspace: Workspace },
309
+ ): Promise<void> {
310
+ const N = 8;
311
+ await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: alpha.workspace.id }); // warm
312
+ const before = { ...permissionCacheStore.stats };
313
+ for (let i = 0; i < N; i += 1) await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: alpha.workspace.id });
314
+ const after = permissionCacheStore.stats;
315
+ ctx.assert(
316
+ after.misses === before.misses && after.hits === before.hits + N,
317
+ `${N} identical workspace-scoped requests resolve the membership 0 further times — all ${N} were cache hits (misses +${after.misses - before.misses}, hits +${after.hits - before.hits})`,
318
+ );
319
+
320
+ // A grant inside beta must invalidate beta's entry and leave alpha's alone: the cache subject is
321
+ // the (user, workspace) pair, so one workspace's churn cannot cost another workspace its cache.
322
+ await ctx.call("POST", `/auth/admin/users/${beta.userId}/permissions`, { token: beta.token, workspaceId: beta.workspace.id, body: { permission: "probe:cache" } });
323
+ const beforeAlpha = { ...permissionCacheStore.stats };
324
+ const alphaAfter = await ctx.call("GET", "/auth/me", { token: alpha.token, workspaceId: alpha.workspace.id });
325
+ ctx.assert(
326
+ permissionCacheStore.stats.misses === beforeAlpha.misses && alphaAfter.status === 200,
327
+ "a grant made in workspace B does not invalidate the caller's cached membership of workspace A",
328
+ );
329
+ const betaAfter = await ctx.call("GET", "/auth/me", { token: beta.token, workspaceId: beta.workspace.id });
330
+ ctx.assert((betaAfter.body?.permissions ?? []).includes("probe:cache"), "…while workspace B sees the grant on its very next request");
331
+ await ctx.call("POST", `/auth/admin/users/${beta.userId}/permissions/${encodeURIComponent("probe:cache")}/revoke`, {
332
+ token: beta.token,
333
+ workspaceId: beta.workspace.id,
334
+ });
335
+ const betaRevoked = await ctx.call("GET", "/auth/me", { token: beta.token, workspaceId: beta.workspace.id });
336
+ ctx.assert(!(betaRevoked.body?.permissions ?? []).includes("probe:cache"), "…and loses it again on the request after the revoke");
337
+ }
338
+
339
+ /**
340
+ * The admin API already proves that editing the catalog changes enforcement, but it is the API
341
+ * doing the writing — this rules out any possibility that it is also doing something in memory.
342
+ * A `psql` session would look exactly like this, bump included: an out-of-band writer is outside
343
+ * the invalidation protocol and says so by bumping the policy counter (see permission-cache.ts).
344
+ */
345
+ async function proveARawDatabaseEditChangesEnforcement(ctx: ProofContext, beta: { token: string; workspace: Workspace }): Promise<void> {
346
+ const opened = await ctx.call("GET", "/auth/admin/audit-log", { token: beta.token, workspaceId: beta.workspace.id });
347
+ ctx.assert(opened.status === 200, `the workspace admin can read the audit log to begin with (got ${opened.status})`);
348
+
349
+ await prisma.permission.update({ where: { slug: "audit-log:read" }, data: { isActive: false } });
350
+ await permissionCacheStore.bump(POLICY_VERSION_KEY);
351
+ const denied = await ctx.call("GET", "/auth/admin/audit-log", { token: beta.token, workspaceId: beta.workspace.id });
352
+ ctx.assert(denied.status === 403, `a permission deactivated by a raw database write stops opening its route (got ${denied.status})`);
353
+
354
+ await prisma.permission.update({ where: { slug: "audit-log:read" }, data: { isActive: true } });
355
+ await permissionCacheStore.bump(POLICY_VERSION_KEY);
356
+ const restored = await ctx.call("GET", "/auth/admin/audit-log", { token: beta.token, workspaceId: beta.workspace.id });
357
+ ctx.assert(restored.status === 200, `…and reactivating it in the database opens the route again (got ${restored.status})`);
358
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@simple-auth-kit/combo-nestjs-drizzle",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "description": "Structural diff of nestjs-prisma: same NestJS controller/guard/module shape and the same two variants (base, workspaces) composed from shared/ + variants/<variant>/, with Drizzle instead of Prisma for schema + queries.",
6
+ "type": "module",
7
+ "scripts": {
8
+ "materialize": "node scripts/materialize.mjs",
9
+ "typecheck": "node scripts/materialize.mjs --all && tsc -p .variant/base && tsc -p .variant/workspaces",
10
+ "prove-cycle": "node scripts/prove-cycle.mjs",
11
+ "migrate": "node scripts/migrate.mjs",
12
+ "seed": "node scripts/seed.mjs"
13
+ },
14
+ "dependencies": {
15
+ "@casl/ability": "^7.0.1",
16
+ "@nestjs/common": "^11.1.28",
17
+ "@nestjs/core": "^11.1.28",
18
+ "@nestjs/platform-express": "^11.1.28",
19
+ "@nestjs/swagger": "^11.4.6",
20
+ "drizzle-orm": "^0.45.2",
21
+ "express": "^5.2.1",
22
+ "pg": "^8.23.0",
23
+ "reflect-metadata": "^0.2.2",
24
+ "rxjs": "^7.8.2"
25
+ },
26
+ "devDependencies": {
27
+ "@types/express": "^5.0.6",
28
+ "@types/node": "^26.2.0",
29
+ "@types/pg": "^8.21.0",
30
+ "dotenv": "^17.4.2",
31
+ "drizzle-kit": "^0.31.10",
32
+ "tsx": "^4.23.11",
33
+ "typescript": "^7.0.2",
34
+ "vitest": "^4.1.10"
35
+ }
36
+ }
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ // Composes a runnable copy of one variant of this combo into `.variant/<variant>/`.
3
+ //
4
+ // A variant is `shared/` + `variants/<variant>/` overlaid on top of each other — exactly what
5
+ // `simple-auth-kit add <combo> [--workspaces]` copies into a consumer's project. Composing it here
6
+ // too means the thing this package typechecks and runs `prove-cycle` against is byte-for-byte
7
+ // the thing the CLI emits, rather than a second wiring that can drift from it.
8
+ //
9
+ // Usage:
10
+ // node scripts/materialize.mjs <variant>|--all
11
+ //
12
+ // Unlike the Prisma combos there is nothing to code-generate afterwards: Drizzle's schema *is*
13
+ // the TypeScript in `src/schema.ts`, so a composed tree is immediately typecheckable.
14
+ import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
15
+ import { dirname, join, resolve } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const COMBO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
19
+ export const VARIANTS = ["base", "workspaces"];
20
+
21
+ /** Composed trees are throwaway — every entry a variant owns is replaced wholesale on each run. */
22
+ const REPLACED_ENTRIES = ["src", "drizzle", "test", "drizzle.config.ts", "tsconfig.json", ".env", ".env.example", ".gitignore"];
23
+
24
+ const TSCONFIG = {
25
+ // Depth is fixed by the layout: <repo>/registry/combos/<combo>/.variant/<variant>/
26
+ extends: "../../../../../tsconfig.base.json",
27
+ compilerOptions: { paths: { "@/lib/auth/core/*": ["../../../../core/*"] } },
28
+ include: ["src", "test", "drizzle.config.ts"],
29
+ exclude: ["node_modules"],
30
+ };
31
+
32
+ /** Each variant owns its own database — their schemas are different shapes, not different states of one. */
33
+ export function databaseUrlFor(variant, baseUrl) {
34
+ if (variant === "base") return baseUrl;
35
+ return baseUrl.replace(/^(.*\/)([^/?]+)(\?.*)?$/, (_m, prefix, db, query) => `${prefix}${db}_${variant}${query ?? ""}`);
36
+ }
37
+
38
+ async function rootEnv() {
39
+ return readFile(join(COMBO_ROOT, ".env"), "utf8");
40
+ }
41
+
42
+ export async function baseDatabaseUrl() {
43
+ const match = (await rootEnv()).match(/^\s*DATABASE_URL\s*=\s*"?([^"\n]+)"?/m);
44
+ if (!match) throw new Error(`no DATABASE_URL in ${join(COMBO_ROOT, ".env")}`);
45
+ return match[1];
46
+ }
47
+
48
+ /**
49
+ * `drizzle-kit migrate` applies migrations but will not create the database to apply them to
50
+ * (Prisma's `migrate dev` does, which is why the reference combo needs no equivalent). Both
51
+ * scripts that touch a database call this first so a clean checkout can prove itself.
52
+ */
53
+ export async function ensureDatabase(url) {
54
+ const { Client } = await import("pg");
55
+ const target = new URL(url);
56
+ const name = decodeURIComponent(target.pathname.replace(/^\//, ""));
57
+
58
+ const admin = new URL(url);
59
+ admin.pathname = "/postgres";
60
+ const client = new Client({ connectionString: admin.toString() });
61
+ await client.connect();
62
+ try {
63
+ const { rowCount } = await client.query("SELECT 1 FROM pg_database WHERE datname = $1", [name]);
64
+ // Identifier, not a value — pg has no placeholder for one, so it is quoted by hand.
65
+ if (!rowCount) await client.query(`CREATE DATABASE "${name.replace(/"/g, '""')}"`);
66
+ } finally {
67
+ await client.end();
68
+ }
69
+ }
70
+
71
+ export async function materialize(variant) {
72
+ if (!VARIANTS.includes(variant)) throw new Error(`unknown variant "${variant}" (expected one of: ${VARIANTS.join(", ")})`);
73
+ const dest = join(COMBO_ROOT, ".variant", variant);
74
+
75
+ await mkdir(dest, { recursive: true });
76
+ await Promise.all(REPLACED_ENTRIES.map((entry) => rm(join(dest, entry), { recursive: true, force: true })));
77
+
78
+ // Order matters: the variant overlay goes last, so `variants/<variant>/test/*` replaces the
79
+ // shared test scaffolding of the same name (that is how variant-hooks.ts is supplied).
80
+ await cp(join(COMBO_ROOT, "shared"), dest, { recursive: true });
81
+ await cp(join(COMBO_ROOT, "test"), join(dest, "test"), { recursive: true });
82
+ await cp(join(COMBO_ROOT, "variants", variant), dest, { recursive: true });
83
+
84
+ await writeFile(join(dest, "tsconfig.json"), JSON.stringify(TSCONFIG, null, 2) + "\n", "utf8");
85
+ // Forward the whole root `.env` (AUTH_JWT_SECRET and anything else a consumer would set) —
86
+ // only DATABASE_URL is rewritten, to point at this variant's own database.
87
+ const withVariantDatabase = (await rootEnv()).replace(
88
+ /^\s*DATABASE_URL\s*=.*$/m,
89
+ `DATABASE_URL="${databaseUrlFor(variant, await baseDatabaseUrl())}"`,
90
+ );
91
+ await writeFile(join(dest, ".env"), withVariantDatabase, "utf8");
92
+
93
+ return dest;
94
+ }
95
+
96
+ if (import.meta.url === `file://${process.argv[1]}`) {
97
+ const args = process.argv.slice(2);
98
+ const requested = args.filter((a) => !a.startsWith("--"));
99
+ const variants = args.includes("--all") || requested.length === 0 ? VARIANTS : requested;
100
+ for (const variant of variants) {
101
+ const dest = await materialize(variant);
102
+ console.log(`materialized ${variant} -> ${dest}`);
103
+ }
104
+ }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ // Authors a migration for one variant and copies it back into the registry.
3
+ //
4
+ // drizzle-kit resolves `drizzle.config.ts` — and the `./src/schema.ts` / `./drizzle` paths
5
+ // inside it — relative to the working directory, so migrations are generated inside the
6
+ // materialized variant and copied back into `variants/<variant>/drizzle`, which is what the CLI
7
+ // actually ships. Every emitted `.sql` is recorded in `drizzle/meta/_journal.json` by
8
+ // drizzle-kit itself; a `.sql` that is not journalled would never be applied, so the whole
9
+ // directory is copied back as one unit rather than file by file.
10
+ //
11
+ // Usage: node scripts/migrate.mjs <variant> [--name <migration-name>]
12
+ import { execFileSync } from "node:child_process";
13
+ import { cp, rm } from "node:fs/promises";
14
+ import { dirname, join, resolve } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ import { materialize, VARIANTS } from "./materialize.mjs";
17
+
18
+ const COMBO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
19
+
20
+ const args = process.argv.slice(2);
21
+ const nameIndex = args.indexOf("--name");
22
+ const name = nameIndex === -1 ? "init" : args[nameIndex + 1];
23
+ const variants = args.filter((a, i) => !a.startsWith("--") && i !== nameIndex + 1);
24
+
25
+ for (const variant of variants.length ? variants : VARIANTS) {
26
+ const dest = await materialize(variant);
27
+ execFileSync("npx", ["drizzle-kit", "generate", "--name", name], { cwd: dest, stdio: "inherit" });
28
+ execFileSync("npx", ["drizzle-kit", "migrate"], { cwd: dest, stdio: "inherit" });
29
+
30
+ const registryMigrations = join(COMBO_ROOT, "variants", variant, "drizzle");
31
+ await rm(registryMigrations, { recursive: true, force: true });
32
+ await cp(join(dest, "drizzle"), registryMigrations, { recursive: true });
33
+ console.log(`\nwrote migrations back to variants/${variant}/drizzle`);
34
+ }
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // Runs the black-box proof against one or both variants of this combo.
3
+ //
4
+ // Each variant is materialized, its migrations applied to its own database, seeded, and then
5
+ // `test/prove-cycle.ts` is run inside it with AUTH_VARIANT set — the test asserts the shared auth
6
+ // cycle for both, plus the workspace isolation properties for the workspace variant.
7
+ //
8
+ // The seed step is not a convenience. "The seeder is a prerequisite, not an extra": until it has
9
+ // run there is no role flagged `isDefault`, so a signup is given nothing and the very first
10
+ // assertion about database-resolved roles fails. Running it here means the proof states that
11
+ // dependency instead of inheriting it from whatever the developer's database happened to contain.
12
+ //
13
+ // Usage: node scripts/prove-cycle.mjs [variant ...] (default: every variant)
14
+ import { execFileSync } from "node:child_process";
15
+ import { baseDatabaseUrl, databaseUrlFor, ensureDatabase, materialize, VARIANTS } from "./materialize.mjs";
16
+
17
+ const requested = process.argv.slice(2).filter((a) => !a.startsWith("--"));
18
+ const variants = requested.length ? requested : VARIANTS;
19
+
20
+ for (const variant of variants) {
21
+ console.log(`\n=== prove-cycle: ${variant} ===\n`);
22
+ const dest = await materialize(variant);
23
+ await ensureDatabase(databaseUrlFor(variant, await baseDatabaseUrl()));
24
+ execFileSync("npx", ["drizzle-kit", "migrate"], { cwd: dest, stdio: "inherit" });
25
+ // The consumer-facing seeder, run exactly as a consumer runs it. Idempotent, so a database that
26
+ // has already been seeded is unaffected.
27
+ execFileSync("npx", ["tsx", "src/seed.ts"], { cwd: dest, stdio: "inherit" });
28
+ execFileSync("npx", ["tsx", "test/prove-cycle.ts"], { cwd: dest, stdio: "inherit", env: { ...process.env, AUTH_VARIANT: variant } });
29
+ }
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ // Runs a variant's seeder against this combo's own dev database.
3
+ //
4
+ // The seeder is consumer-facing source (`variants/<variant>/src/seed.ts`, copied into every
5
+ // project the CLI installs into), so it is run here the same way a consumer runs it: from the
6
+ // materialized variant, with that variant's `.env` supplying DATABASE_URL. Nothing about the
7
+ // seeder is test scaffolding, and there is no second copy of it living in `scripts/`.
8
+ //
9
+ // Usage:
10
+ // node scripts/seed.mjs [variant ...] (default: every variant)
11
+ // SEED_ADMIN_EMAIL=... SEED_ADMIN_PASSWORD=... node scripts/seed.mjs base
12
+ //
13
+ // Variables set in the environment win over the variant's `.env` — see seed.ts.
14
+ import { execFileSync } from "node:child_process";
15
+ import { materialize, VARIANTS } from "./materialize.mjs";
16
+
17
+ const requested = process.argv.slice(2).filter((a) => !a.startsWith("--"));
18
+ const variants = requested.length ? requested : VARIANTS;
19
+
20
+ for (const variant of variants) {
21
+ console.log(`\n=== seed: ${variant} ===\n`);
22
+ const dest = await materialize(variant);
23
+ execFileSync("npx", ["tsx", "src/seed.ts"], { cwd: dest, stdio: "inherit" });
24
+ }