ldap-rest 1.2.1 → 2.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 (358) hide show
  1. package/bin/sync-james.mjs +13 -2
  2. package/dist/abstract/ldapFlat.js +1 -1
  3. package/dist/abstract/plugin.js +1 -1
  4. package/dist/abstract/twakePlugin.js +1 -1
  5. package/dist/bin/index.js +1 -1
  6. package/dist/config/args.js +1 -1
  7. package/dist/config/options.js +1 -0
  8. package/dist/config/schema.js +1 -1
  9. package/dist/lib/auth/base.js +1 -1
  10. package/dist/lib/authz/base.js +1 -1
  11. package/dist/lib/entitySchema/validator.js +1 -1
  12. package/dist/lib/errors.js +1 -1
  13. package/dist/lib/expressFormatedResponses.js +1 -1
  14. package/dist/lib/health.js +1 -1
  15. package/dist/lib/hookBus.js +1 -0
  16. package/dist/lib/instanceProviders.js +1 -1
  17. package/dist/lib/ldap/callerView.js +1 -0
  18. package/dist/lib/ldap/pool.js +1 -0
  19. package/dist/lib/ldap/schema.js +1 -0
  20. package/dist/lib/ldap/searchCache.js +1 -0
  21. package/dist/lib/ldapActions.js +1 -1
  22. package/dist/lib/ldapSchema.js +1 -1
  23. package/dist/lib/parseConfig.js +1 -1
  24. package/dist/lib/route.js +1 -0
  25. package/dist/lib/schemaFile.js +1 -1
  26. package/dist/lib/services/index.js +1 -0
  27. package/dist/lib/storage/store.js +1 -1
  28. package/dist/lib/utils.js +1 -1
  29. package/dist/lib/version.js +1 -0
  30. package/dist/logger/winston.js +1 -1
  31. package/dist/plugins/auth/auth.options.js +1 -0
  32. package/dist/plugins/auth/authzDynamic.js +1 -1
  33. package/dist/plugins/auth/authzLinid1.js +1 -1
  34. package/dist/plugins/auth/authzPerBranch.js +1 -1
  35. package/dist/plugins/auth/authzPerRoute.js +1 -1
  36. package/dist/plugins/auth/authzScope.js +1 -1
  37. package/dist/plugins/auth/crowdsec.js +1 -1
  38. package/dist/plugins/auth/fake.js +1 -1
  39. package/dist/plugins/auth/hmac.js +1 -1
  40. package/dist/plugins/auth/llng.js +1 -1
  41. package/dist/plugins/auth/openidconnect.js +1 -1
  42. package/dist/plugins/auth/rateLimit.js +1 -1
  43. package/dist/plugins/auth/token.js +1 -1
  44. package/dist/plugins/auth/totp.js +1 -1
  45. package/dist/plugins/auth/trustedProxy.js +1 -1
  46. package/dist/plugins/bcl.js +1 -1
  47. package/dist/plugins/bcl.options.js +1 -0
  48. package/dist/plugins/configApi.js +1 -1
  49. package/dist/plugins/demo/helloworld.js +1 -1
  50. package/dist/plugins/ldap/accountLifecycle.js +1 -1
  51. package/dist/plugins/ldap/bulkImport.js +1 -1
  52. package/dist/plugins/ldap/departmentSync.js +1 -1
  53. package/dist/plugins/ldap/enterpriseRules.js +1 -1
  54. package/dist/plugins/ldap/externalUsersInGroups.js +1 -1
  55. package/dist/plugins/ldap/flatGeneric.js +1 -1
  56. package/dist/plugins/ldap/groups.js +1 -1
  57. package/dist/plugins/ldap/ldap.options.js +1 -0
  58. package/dist/plugins/ldap/onChange.js +1 -1
  59. package/dist/plugins/ldap/organizations.js +1 -1
  60. package/dist/plugins/ldap/passwordPolicy.js +1 -1
  61. package/dist/plugins/ldap/raw.js +1 -1
  62. package/dist/plugins/ldap/trash.js +1 -1
  63. package/dist/plugins/metrics.js +1 -1
  64. package/dist/plugins/metrics.options.js +1 -0
  65. package/dist/plugins/rabbitmq.js +1 -1
  66. package/dist/plugins/rabbitmq.options.js +1 -0
  67. package/dist/plugins/scim/baseResolver.js +1 -1
  68. package/dist/plugins/scim/bulk.js +1 -1
  69. package/dist/plugins/scim/discovery.js +1 -1
  70. package/dist/plugins/scim/errors.js +1 -1
  71. package/dist/plugins/scim/filter.js +1 -1
  72. package/dist/plugins/scim/groups.js +1 -1
  73. package/dist/plugins/scim/mapping.js +1 -1
  74. package/dist/plugins/scim/patch.js +1 -1
  75. package/dist/plugins/scim/scim.js +1 -1
  76. package/dist/plugins/scim/scim.options.js +1 -0
  77. package/dist/plugins/scim/users.js +1 -1
  78. package/dist/plugins/static.js +1 -1
  79. package/dist/plugins/static.options.js +1 -0
  80. package/dist/plugins/storage/file.js +1 -1
  81. package/dist/plugins/storage/ldap.js +1 -1
  82. package/dist/plugins/storage/postgres.js +1 -1
  83. package/dist/plugins/storage/valkey.js +1 -1
  84. package/dist/plugins/storage.js +1 -1
  85. package/dist/plugins/storage.options.js +1 -0
  86. package/dist/plugins/twake/appAccounts.options.js +1 -0
  87. package/dist/plugins/twake/appAccountsApi.js +1 -1
  88. package/dist/plugins/twake/appAccountsConsistency.js +1 -1
  89. package/dist/plugins/twake/calendar.js +1 -1
  90. package/dist/plugins/twake/calendar.options.js +1 -0
  91. package/dist/plugins/twake/calendarResources.js +1 -1
  92. package/dist/plugins/twake/cloudery.options.js +1 -0
  93. package/dist/plugins/twake/clouderyProvision.js +1 -1
  94. package/dist/plugins/twake/cozy.options.js +1 -0
  95. package/dist/plugins/twake/cozyProvision.js +1 -1
  96. package/dist/plugins/twake/drive.js +1 -1
  97. package/dist/plugins/twake/drive.options.js +1 -0
  98. package/dist/plugins/twake/groups.js +1 -1
  99. package/dist/plugins/twake/instances.js +1 -1
  100. package/dist/plugins/twake/james.js +1 -1
  101. package/dist/plugins/twake/james.options.js +1 -0
  102. package/dist/plugins/twake/lifecycleAttributes.js +1 -1
  103. package/dist/plugins/twake/lifecycleEvents.js +1 -1
  104. package/dist/plugins/twake/migrationProxy.js +1 -1
  105. package/dist/plugins/twake/migrationProxy.options.js +1 -0
  106. package/dist/plugins/twake/spaces.js +1 -1
  107. package/dist/plugins/twake/tombstone.js +1 -1
  108. package/dist/plugins/twake/twakeGroups.options.js +1 -0
  109. package/dist/plugins/twake/twakeInstances.options.js +1 -0
  110. package/dist/plugins/twake/twakeLifecycle.options.js +1 -0
  111. package/dist/plugins/twake/twakeSpaces.options.js +1 -0
  112. package/dist/plugins/twake/twakeTombstone.options.js +1 -0
  113. package/dist/plugins/weblogs.js +1 -1
  114. package/dist/src/abstract/ldapFlat.d.ts +41 -10
  115. package/dist/src/abstract/plugin.d.ts +31 -5
  116. package/dist/src/abstract/twakePlugin.d.ts +14 -4
  117. package/dist/src/bin/index.d.ts +35 -4
  118. package/dist/src/config/args.d.ts +2 -4
  119. package/dist/src/config/options.d.ts +104 -0
  120. package/dist/src/config/schema.d.ts +50 -1
  121. package/dist/src/hooks.d.ts +60 -22
  122. package/dist/src/lib/authz/base.d.ts +4 -51
  123. package/dist/src/lib/authz/composition.d.ts +2 -2
  124. package/dist/src/lib/entitySchema/validator.d.ts +63 -6
  125. package/dist/src/lib/errors.d.ts +57 -1
  126. package/dist/src/lib/expressFormatedResponses.d.ts +49 -7
  127. package/dist/src/lib/hookBus.d.ts +226 -0
  128. package/dist/src/lib/instanceProviders.d.ts +0 -8
  129. package/dist/src/lib/ldap/callerView.d.ts +45 -0
  130. package/dist/src/lib/ldap/pool.d.ts +108 -0
  131. package/dist/src/lib/ldap/schema.d.ts +157 -0
  132. package/dist/src/lib/ldap/searchCache.d.ts +112 -0
  133. package/dist/src/lib/ldapActions.d.ts +26 -256
  134. package/dist/src/lib/parseConfig.d.ts +31 -2
  135. package/dist/src/lib/route.d.ts +99 -0
  136. package/dist/src/lib/services/index.d.ts +56 -0
  137. package/dist/src/lib/utils.d.ts +100 -36
  138. package/dist/src/lib/version.d.ts +12 -0
  139. package/dist/src/plugins/auth/auth.options.d.ts +88 -0
  140. package/dist/src/plugins/auth/authzDynamic.d.ts +10 -1
  141. package/dist/src/plugins/auth/authzLinid1.d.ts +6 -0
  142. package/dist/src/plugins/auth/authzPerBranch.d.ts +9 -0
  143. package/dist/src/plugins/auth/authzPerRoute.d.ts +3 -0
  144. package/dist/src/plugins/auth/authzScope.d.ts +23 -14
  145. package/dist/src/plugins/auth/crowdsec.d.ts +5 -0
  146. package/dist/src/plugins/auth/fake.d.ts +3 -0
  147. package/dist/src/plugins/auth/hmac.d.ts +4 -0
  148. package/dist/src/plugins/auth/llng.d.ts +4 -0
  149. package/dist/src/plugins/auth/openidconnect.d.ts +7 -0
  150. package/dist/src/plugins/auth/rateLimit.d.ts +4 -0
  151. package/dist/src/plugins/auth/token.d.ts +3 -0
  152. package/dist/src/plugins/auth/totp.d.ts +5 -0
  153. package/dist/src/plugins/auth/trustedProxy.d.ts +4 -0
  154. package/dist/src/plugins/bcl.d.ts +3 -0
  155. package/dist/src/plugins/bcl.options.d.ts +8 -0
  156. package/dist/src/plugins/configApi.d.ts +24 -5
  157. package/dist/src/plugins/demo/helloworld.d.ts +8 -19
  158. package/dist/src/plugins/ldap/accountLifecycle.d.ts +17 -0
  159. package/dist/src/plugins/ldap/bulkImport.d.ts +6 -76
  160. package/dist/src/plugins/ldap/departmentSync.d.ts +1 -0
  161. package/dist/src/plugins/ldap/enterpriseRules.d.ts +8 -15
  162. package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +4 -0
  163. package/dist/src/plugins/ldap/flatGeneric.d.ts +4 -0
  164. package/dist/src/plugins/ldap/groups.d.ts +27 -11
  165. package/dist/src/plugins/ldap/ldap.options.d.ts +75 -0
  166. package/dist/src/plugins/ldap/onChange.d.ts +10 -2
  167. package/dist/src/plugins/ldap/organizations.d.ts +16 -0
  168. package/dist/src/plugins/ldap/passwordPolicy.d.ts +35 -3
  169. package/dist/src/plugins/ldap/raw.d.ts +50 -19
  170. package/dist/src/plugins/ldap/trash.d.ts +6 -0
  171. package/dist/src/plugins/metrics.d.ts +3 -0
  172. package/dist/src/plugins/metrics.options.d.ts +8 -0
  173. package/dist/src/plugins/rabbitmq.d.ts +3 -0
  174. package/dist/src/plugins/rabbitmq.options.d.ts +8 -0
  175. package/dist/src/plugins/scim/baseResolver.d.ts +2 -1
  176. package/dist/src/plugins/scim/bulk.d.ts +6 -7
  177. package/dist/src/plugins/scim/discovery.d.ts +3 -0
  178. package/dist/src/plugins/scim/errors.d.ts +2 -1
  179. package/dist/src/plugins/scim/groups.d.ts +6 -4
  180. package/dist/src/plugins/scim/mapping.d.ts +16 -3
  181. package/dist/src/plugins/scim/scim.d.ts +37 -3
  182. package/dist/src/plugins/scim/scim.options.d.ts +35 -0
  183. package/dist/src/plugins/scim/types.d.ts +2 -0
  184. package/dist/src/plugins/scim/users.d.ts +6 -7
  185. package/dist/src/plugins/static.d.ts +9 -0
  186. package/dist/src/plugins/static.options.d.ts +13 -0
  187. package/dist/src/plugins/storage.d.ts +12 -1
  188. package/dist/src/plugins/storage.options.d.ts +16 -0
  189. package/dist/src/plugins/twake/appAccounts.options.d.ts +13 -0
  190. package/dist/src/plugins/twake/appAccountsApi.d.ts +18 -0
  191. package/dist/src/plugins/twake/appAccountsConsistency.d.ts +8 -0
  192. package/dist/src/plugins/twake/calendar.d.ts +25 -2
  193. package/dist/src/plugins/twake/calendar.options.d.ts +19 -0
  194. package/dist/src/plugins/twake/cloudery.options.d.ts +26 -0
  195. package/dist/src/plugins/twake/clouderyProvision.d.ts +18 -0
  196. package/dist/src/plugins/twake/cozy.options.d.ts +23 -0
  197. package/dist/src/plugins/twake/cozyProvision.d.ts +14 -0
  198. package/dist/src/plugins/twake/drive.d.ts +7 -0
  199. package/dist/src/plugins/twake/drive.options.d.ts +15 -0
  200. package/dist/src/plugins/twake/groups.d.ts +29 -1
  201. package/dist/src/plugins/twake/instances.d.ts +74 -10
  202. package/dist/src/plugins/twake/james.d.ts +17 -0
  203. package/dist/src/plugins/twake/james.options.d.ts +21 -0
  204. package/dist/src/plugins/twake/lifecycleAttributes.d.ts +13 -2
  205. package/dist/src/plugins/twake/lifecycleEvents.d.ts +12 -0
  206. package/dist/src/plugins/twake/migrationProxy.d.ts +11 -11
  207. package/dist/src/plugins/twake/migrationProxy.options.d.ts +16 -0
  208. package/dist/src/plugins/twake/spaces.d.ts +30 -0
  209. package/dist/src/plugins/twake/tombstone.d.ts +25 -16
  210. package/dist/src/plugins/twake/twakeGroups.options.d.ts +20 -0
  211. package/dist/src/plugins/twake/twakeInstances.options.d.ts +43 -0
  212. package/dist/src/plugins/twake/twakeLifecycle.options.d.ts +21 -0
  213. package/dist/src/plugins/twake/twakeSpaces.options.d.ts +19 -0
  214. package/dist/src/plugins/twake/twakeTombstone.options.d.ts +17 -0
  215. package/docs/README.md +17 -2
  216. package/docs/client-development/api/rest-api.md +13 -3
  217. package/docs/client-development/schemas/README.md +96 -32
  218. package/docs/development/contracts.md +109 -0
  219. package/docs/plugin-development/README.md +53 -95
  220. package/docs/plugin-development/dependencies.md +24 -8
  221. package/docs/plugin-development/hooks.md +143 -23
  222. package/docs/plugin-development/openapi.md +14 -1
  223. package/docs/plugin-development/options.md +136 -0
  224. package/docs/plugin-development/routes.md +152 -0
  225. package/docs/plugin-development/services.md +95 -0
  226. package/docs/plugin-development/testing.md +3 -3
  227. package/docs/usage/configuration.md +18 -11
  228. package/docs/usage/operations.md +28 -4
  229. package/docs/usage/plugins/integrations/instances.md +11 -3
  230. package/docs/usage/plugins/integrations/lifecycle-events.md +4 -1
  231. package/docs/usage/plugins/integrations/scim.md +20 -18
  232. package/docs/usage/plugins/integrations/tombstone.md +10 -6
  233. package/docs/usage/plugins/ldap/enterprise-rules.md +10 -1
  234. package/docs/usage/plugins/ldap/flat-generic.md +11 -0
  235. package/docs/usage/plugins/ldap/groups.md +4 -1
  236. package/docs/usage/plugins/ldap/password-policy.md +7 -0
  237. package/docs/usage/plugins/ldap/raw.md +17 -1
  238. package/docs/usage/upgrading.md +253 -0
  239. package/package.json +372 -286
  240. package/static/browser/ldap-browser/LdapBrowser.js +8 -6
  241. package/static/browser/ldap-browser/LdapBrowser.js.map +1 -1
  242. package/static/browser/ldap-browser/api/RawApiClient.js +2 -0
  243. package/static/browser/ldap-browser/api/RawApiClient.js.map +1 -1
  244. package/static/browser/ldap-browser/components/EntryTree.js +9 -5
  245. package/static/browser/ldap-browser/components/EntryTree.js.map +1 -1
  246. package/static/browser/ldap-browser/components/EntryView.js +2 -0
  247. package/static/browser/ldap-browser/components/EntryView.js.map +1 -1
  248. package/static/browser/ldap-browser/schema.js +3 -2
  249. package/static/browser/ldap-browser/schema.js.map +1 -1
  250. package/static/browser/ldap-browser.esm.js +27 -18
  251. package/static/browser/ldap-browser.esm.js.map +1 -1
  252. package/static/browser/ldap-browser.js +27 -18
  253. package/static/browser/ldap-browser.js.map +1 -1
  254. package/static/browser/ldap-browser.min.js +1 -1
  255. package/static/browser/ldap-browser.min.js.map +1 -1
  256. package/static/browser/ldap-group-editor/LdapGroupEditor.js +8 -6
  257. package/static/browser/ldap-group-editor/LdapGroupEditor.js.map +1 -1
  258. package/static/browser/ldap-group-editor/api/GroupApiClient.js +1 -0
  259. package/static/browser/ldap-group-editor/api/GroupApiClient.js.map +1 -1
  260. package/static/browser/ldap-group-editor/components/CreateGroupModal.js +5 -1
  261. package/static/browser/ldap-group-editor/components/CreateGroupModal.js.map +1 -1
  262. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js +6 -3
  263. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js.map +1 -1
  264. package/static/browser/ldap-group-editor/components/GroupTree.js +6 -3
  265. package/static/browser/ldap-group-editor/components/GroupTree.js.map +1 -1
  266. package/static/browser/ldap-group-editor/components/MoveGroupModal.js +6 -3
  267. package/static/browser/ldap-group-editor/components/MoveGroupModal.js.map +1 -1
  268. package/static/browser/ldap-group-editor.esm.js +32 -16
  269. package/static/browser/ldap-group-editor.esm.js.map +1 -1
  270. package/static/browser/ldap-group-editor.js +32 -16
  271. package/static/browser/ldap-group-editor.js.map +1 -1
  272. package/static/browser/ldap-group-editor.min.js +1 -1
  273. package/static/browser/ldap-group-editor.min.js.map +1 -1
  274. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js +3 -0
  275. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js.map +1 -1
  276. package/static/browser/ldap-resource-editor/cache/CacheManager.js +3 -1
  277. package/static/browser/ldap-resource-editor/cache/CacheManager.js.map +1 -1
  278. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js +6 -1
  279. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -1
  280. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js +2 -0
  281. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js.map +1 -1
  282. package/static/browser/ldap-tree-viewer/components/TreeNode.js +4 -0
  283. package/static/browser/ldap-tree-viewer/components/TreeNode.js.map +1 -1
  284. package/static/browser/ldap-tree-viewer/components/TreeRoot.js +3 -0
  285. package/static/browser/ldap-tree-viewer/components/TreeRoot.js.map +1 -1
  286. package/static/browser/ldap-tree-viewer/store/Store.js +3 -1
  287. package/static/browser/ldap-tree-viewer/store/Store.js.map +1 -1
  288. package/static/browser/ldap-tree-viewer.esm.js +18 -2
  289. package/static/browser/ldap-tree-viewer.esm.js.map +1 -1
  290. package/static/browser/ldap-tree-viewer.js +18 -2
  291. package/static/browser/ldap-tree-viewer.js.map +1 -1
  292. package/static/browser/ldap-tree-viewer.min.js +1 -1
  293. package/static/browser/ldap-tree-viewer.min.js.map +1 -1
  294. package/static/browser/ldap-unit-editor/LdapUnitEditor.js +7 -5
  295. package/static/browser/ldap-unit-editor/LdapUnitEditor.js.map +1 -1
  296. package/static/browser/ldap-unit-editor/api/UnitApiClient.js +1 -0
  297. package/static/browser/ldap-unit-editor/api/UnitApiClient.js.map +1 -1
  298. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js +7 -4
  299. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js.map +1 -1
  300. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js +7 -4
  301. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js.map +1 -1
  302. package/static/browser/ldap-unit-editor/components/UnitTree.js +6 -3
  303. package/static/browser/ldap-unit-editor/components/UnitTree.js.map +1 -1
  304. package/static/browser/ldap-unit-editor.esm.js +28 -16
  305. package/static/browser/ldap-unit-editor.esm.js.map +1 -1
  306. package/static/browser/ldap-unit-editor.js +28 -16
  307. package/static/browser/ldap-unit-editor.js.map +1 -1
  308. package/static/browser/ldap-unit-editor.min.js +1 -1
  309. package/static/browser/ldap-unit-editor.min.js.map +1 -1
  310. package/static/browser/ldap-user-editor/LdapUserEditor.js +9 -7
  311. package/static/browser/ldap-user-editor/LdapUserEditor.js.map +1 -1
  312. package/static/browser/ldap-user-editor/api/UserApiClient.js +2 -0
  313. package/static/browser/ldap-user-editor/api/UserApiClient.js.map +1 -1
  314. package/static/browser/ldap-user-editor/cache/CacheManager.js +3 -1
  315. package/static/browser/ldap-user-editor/cache/CacheManager.js.map +1 -1
  316. package/static/browser/ldap-user-editor/components/MoveUserModal.js +7 -4
  317. package/static/browser/ldap-user-editor/components/MoveUserModal.js.map +1 -1
  318. package/static/browser/ldap-user-editor/components/PointerField.js +6 -1
  319. package/static/browser/ldap-user-editor/components/PointerField.js.map +1 -1
  320. package/static/browser/ldap-user-editor/components/UserEditor.js +10 -5
  321. package/static/browser/ldap-user-editor/components/UserEditor.js.map +1 -1
  322. package/static/browser/ldap-user-editor/components/UserList.js +8 -4
  323. package/static/browser/ldap-user-editor/components/UserList.js.map +1 -1
  324. package/static/browser/ldap-user-editor/components/UserTree.js +7 -3
  325. package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -1
  326. package/static/browser/ldap-user-editor.esm.js +55 -30
  327. package/static/browser/ldap-user-editor.esm.js.map +1 -1
  328. package/static/browser/ldap-user-editor.js +55 -30
  329. package/static/browser/ldap-user-editor.js.map +1 -1
  330. package/static/browser/ldap-user-editor.min.js +1 -1
  331. package/static/browser/ldap-user-editor.min.js.map +1 -1
  332. package/static/browser/shared/components/DisposableComponent.js +3 -5
  333. package/static/browser/shared/components/DisposableComponent.js.map +1 -1
  334. package/static/browser/shared/components/Modal.js +2 -1
  335. package/static/browser/shared/components/Modal.js.map +1 -1
  336. package/static/browser/shared/components/StatusMessage.js +3 -2
  337. package/static/browser/shared/components/StatusMessage.js.map +1 -1
  338. package/static/browser/shared/utils/form.js.map +1 -1
  339. package/static/browser/shared/utils/hmac.js +1 -0
  340. package/static/browser/shared/utils/hmac.js.map +1 -1
  341. package/static/browser/shared/utils/totp.js +1 -0
  342. package/static/browser/shared/utils/totp.js.map +1 -1
  343. package/static/browser/shared.esm.js +9 -8
  344. package/static/browser/shared.esm.js.map +1 -1
  345. package/static/browser/shared.js +9 -8
  346. package/static/browser/shared.js.map +1 -1
  347. package/static/browser/shared.min.js +1 -1
  348. package/static/browser/shared.min.js.map +1 -1
  349. package/static/schemas/ad/groups.json +1 -1
  350. package/static/schemas/ad/users.json +3 -3
  351. package/static/schemas/example/organizations.json +2 -2
  352. package/static/schemas/example/users.json +2 -2
  353. package/static/schemas/obm/users.json +2 -2
  354. package/static/schemas/standard/groups.json +1 -1
  355. package/static/schemas/standard/users.json +2 -2
  356. package/static/schemas/twake/groups.json +4 -4
  357. package/static/schemas/twake/teamMailboxes.json +3 -3
  358. package/static/schemas/twake/users.json +7 -7
@@ -66,7 +66,7 @@ A complete schema consists of two main sections: entity metadata and attribute d
66
66
  },
67
67
  "mail": {
68
68
  "type": "string",
69
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
69
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
70
70
  "required": false,
71
71
  "role": "primaryEmail"
72
72
  },
@@ -74,7 +74,7 @@ A complete schema consists of two main sections: entity metadata and attribute d
74
74
  "type": "array",
75
75
  "items": {
76
76
  "type": "string",
77
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
77
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
78
78
  },
79
79
  "required": false,
80
80
  "role": "emailAliases"
@@ -197,7 +197,7 @@ Simple text values.
197
197
  },
198
198
  "mail": {
199
199
  "type": "string",
200
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
200
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
201
201
  "required": false,
202
202
  "role": "primaryEmail"
203
203
  },
@@ -237,7 +237,7 @@ Multi-valued attributes containing multiple items of the same type.
237
237
  "type": "array",
238
238
  "items": {
239
239
  "type": "string",
240
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
240
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
241
241
  },
242
242
  "required": false,
243
243
  "role": "emailAliases"
@@ -338,19 +338,20 @@ Complete reference for all attribute properties.
338
338
 
339
339
  ### Attribute Properties Table
340
340
 
341
- | Property | Type | Applies To | Description | Example |
342
- | ---------- | --------------- | ------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- |
343
- | `type` | string | All | `"string"`, `"array"`, `"pointer"`, `"number"`, `"integer"`, `"boolean"`, `"date"` | `"string"` |
344
- | `required` | boolean | All | Whether the attribute must be present | `true` |
345
- | `fixed` | boolean | All | Whether the attribute value cannot be changed after creation | `true` |
346
- | `role` | string/string[] | String | Semantic role, or roles, for special handling (see [Semantic Roles](#semantic-roles)) | `"identifier"` |
347
- | `test` | string/regex | String, Array items | Regular expression for validation | `"^[a-zA-Z0-9._-]{1,255}$"` |
348
- | `branch` | string[] | Pointer | LDAP branches where referenced entries exist | `["ou=users,dc=example,dc=com"]` |
349
- | `ui` | object | Pointer | UI configuration for pointer selection (future feature) | `{}` |
350
- | `hint` | text | String, Array items | What a valid value looks like, shown under the field and quoted in refusals | `"Expected pattern 999 9999"` |
351
- | `group` | text | All | Form section the attribute is shown in | `"Mailbox Settings"` |
352
- | `default` | any | All | Default value for new entries | `["top", "person"]` |
353
- | `items` | object | Array | Schema for array element validation | `{"type": "string", "test": "..."}` |
341
+ | Property | Type | Applies To | Description | Example |
342
+ | ----------- | --------------- | ------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- |
343
+ | `type` | string | All | `"string"`, `"array"`, `"pointer"`, `"number"`, `"integer"`, `"boolean"`, `"date"` | `"string"` |
344
+ | `required` | boolean | All | Whether the attribute must be present | `true` |
345
+ | `fixed` | boolean | All | Whether the attribute value cannot be changed after creation | `true` |
346
+ | `role` | string/string[] | String | Semantic role, or roles, for special handling (see [Semantic Roles](#semantic-roles)) | `"identifier"` |
347
+ | `test` | string/regex | String, Array items | Regular expression for validation | `"^[a-zA-Z0-9._-]{1,255}$"` |
348
+ | `maxLength` | number | String, Array items | Longest value accepted, in characters; a longer one is refused before `test` (4096) | `255` |
349
+ | `branch` | string[] | Pointer | LDAP branches where referenced entries exist | `["ou=users,dc=example,dc=com"]` |
350
+ | `ui` | object | Pointer | UI configuration for pointer selection (future feature) | `{}` |
351
+ | `hint` | text | String, Array items | What a valid value looks like, shown under the field and quoted in refusals | `"Expected pattern 999 9999"` |
352
+ | `group` | text | All | Form section the attribute is shown in | `"Mailbox Settings"` |
353
+ | `default` | any | All | Default value for new entries | `["top", "person"]` |
354
+ | `items` | object | Array | Schema for array element validation | `{"type": "string", "test": "..."}` |
354
355
 
355
356
  A _text_ is a plain string or a map keyed by language tag,
356
357
  `{ "en": "Mailbox Settings", "fr": "Paramètres de messagerie" }`, picked as
@@ -378,16 +379,40 @@ answers, one value where the attribute holds one, can be sent back as is;
378
379
  two values or more for a single-valued attribute are refused. Each element
379
380
  of an `array` follows `items.type`, text without `items`. A `number` or
380
381
  `integer` is a JSON number or its text (`42`, `"42"`), a `boolean` a JSON
381
- boolean or the directory's `TRUE` / `FALSE`; anything else for them, and
382
+ boolean or the directory's `TRUE` / `FALSE`. A JSON number is judged on the
383
+ text written for it, what the directory is sent: `1e21` and `1e-7` are
384
+ written `1e+21` and `1e-7`, so they are refused like that text, and a whole
385
+ number beyond 2⁵³ is refused whatever the type, as the JSON parser has
386
+ already lost its digits. Anything else for them, and
382
387
  anything but text for a `string`, `date` or `pointer`, is refused; an
383
388
  attribute with `normalize` takes any text, read after validation. In a
384
389
  `replace`, `null` or `[]` clears an attribute; in an `add` or on a creation
385
- they are refused as no value. A `delete` naming the attribute, or naming it
386
- with no value, removes it, unless it is `required`; naming values removes
387
- those, every value it holds included, as the server does not read what
388
- remains, and `''` names no value. An empty string sent as a value is
389
- checked against `test`, refused for a pointer, and missing for a `required`
390
- attribute. Every refusal answers 400 with the attribute's name.
390
+ they are refused as no value when the schema declares the attribute. A
391
+ `null` under a name the schema does not declare — and, for an entity without
392
+ a schema, under any name — is dropped from a creation and from an `add`, as
393
+ if the key were left out, and clears the attribute in a `replace`. A
394
+ `delete` naming the attribute, or naming it with no value, removes it,
395
+ unless it is `required`; naming values removes those, every value it holds
396
+ included, as the server does not read what remains, and `''` names no
397
+ value. An empty string sent as a value is checked against `test`, refused
398
+ for a pointer, and missing for a `required` attribute. Every refusal answers
399
+ 400 with the attribute's name.
400
+
401
+ #### What an attribute may be called
402
+
403
+ A name is read as the directory reads it: case and the `;binary` option do
404
+ not change the attribute it names, so `Cn`, `CN` and `cn;binary` all name
405
+ `cn`. Each key the schema declares is written under the schema's spelling —
406
+ `Cn` becomes `cn`, `L;binary` becomes `l;binary` — before any check, so that
407
+ a `required` attribute counts as present wherever it was sent, and a `fixed`
408
+ one is written once, at its default. The attribute's type is read the same
409
+ way when a value is converted, so a boolean sent as `pwdReset;binary` is
410
+ written `TRUE`.
411
+
412
+ Two keys of an entry or of one part of a change naming one attribute are
413
+ refused; the option does not make them two, so `l` and `l;binary` are
414
+ refused together. A name the schema does not declare is left as sent, and a
415
+ strict schema refuses it.
391
416
 
392
417
  Every entity route applies these rules; `test/lib/entitySchema.contract.test.ts`
393
418
  asserts them on a flat entity, a group and an organization alike. In the
@@ -451,7 +476,10 @@ Assigns semantic meaning to attributes for special handling by the application.
451
476
 
452
477
  #### `test` (Optional)
453
478
 
454
- Regular expression pattern for validating attribute values.
479
+ Regular expression pattern for validating attribute values. The value is
480
+ judged only up to its attribute's length: see
481
+ [`maxLength`](#maxlength-optional) below, which a deployment writing its own
482
+ pattern should read.
455
483
 
456
484
  ```json
457
485
  {
@@ -461,7 +489,7 @@ Regular expression pattern for validating attribute values.
461
489
  },
462
490
  "mail": {
463
491
  "type": "string",
464
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
492
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
465
493
  }
466
494
  }
467
495
  ```
@@ -474,7 +502,38 @@ Regular expression pattern for validating attribute values.
474
502
  "type": "array",
475
503
  "items": {
476
504
  "type": "string",
477
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
505
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
506
+ }
507
+ }
508
+ }
509
+ ```
510
+
511
+ #### `maxLength` (Optional)
512
+
513
+ Longest value, in UTF-16 code units (what `.length` counts, so an emoji or an
514
+ accented letter outside the basic plane counts as two), accepted before `test`
515
+ runs on it. A value past
516
+ it is refused with 400, and the pattern never reads it — whether or not it
517
+ would have matched. On an array, `maxLength` on the attribute governs every
518
+ item; one declared under `items` is read only when the attribute declares
519
+ none. It is a positive whole number of characters: the server refuses to load
520
+ a schema where it is not.
521
+
522
+ A pattern is written for values of its attribute, not for a payload of any
523
+ size: handed several kilobytes, a pattern whose parts overlap walks the value
524
+ in quadratic time, on the event loop that answers every other request
525
+ meanwhile. The limit keeps the pattern within what it was written for. It is
526
+ **4096** unless the schema says otherwise; an attribute holding longer values
527
+ — a description, a public key — declares its own.
528
+
529
+ ```json
530
+ {
531
+ "sshPublicKey": {
532
+ "type": "array",
533
+ "items": {
534
+ "type": "string",
535
+ "maxLength": 16384,
536
+ "test": "^[^\\r\\n]*$"
478
537
  }
479
538
  }
480
539
  }
@@ -594,7 +653,7 @@ Email-related roles help applications handle email addresses consistently across
594
653
  {
595
654
  "mail": {
596
655
  "type": "string",
597
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
656
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
598
657
  "required": false,
599
658
  "role": "primaryEmail"
600
659
  }
@@ -609,7 +668,7 @@ Email-related roles help applications handle email addresses consistently across
609
668
  "type": "array",
610
669
  "items": {
611
670
  "type": "string",
612
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
671
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
613
672
  },
614
673
  "required": false,
615
674
  "role": "emailAliases"
@@ -686,9 +745,14 @@ Common validation patterns:
686
745
  **Email:**
687
746
 
688
747
  ```regex
689
- ^[^@\s]+@[^@\s]+\.[^@\s]+$
748
+ ^[^@\s]+@[^@\s.]+(\.[^@\s.]+)+$
690
749
  ```
691
750
 
751
+ The domain part holds no dot of its own: where it did, the pattern tried every
752
+ dot as the last separator and read the value again each time — seconds for a
753
+ value of a few kilobytes. The result is that a trailing dot (`a@b.c.`) is
754
+ refused, where the older form accepted it.
755
+
692
756
  **DN (Distinguished Name):**
693
757
 
694
758
  ```regex
@@ -1045,7 +1109,7 @@ Start with the schema that most closely matches your directory type:
1045
1109
  "attributes": {
1046
1110
  "mail": {
1047
1111
  "type": "string",
1048
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$",
1112
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$",
1049
1113
  "required": false,
1050
1114
  "role": "primaryEmail"
1051
1115
  },
@@ -1238,7 +1302,7 @@ Use `test` patterns to enforce data quality:
1238
1302
  },
1239
1303
  "mail": {
1240
1304
  "type": "string",
1241
- "test": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
1305
+ "test": "^[^@\\s]+@[^@\\s.]+(\\.[^@\\s.]+)+$"
1242
1306
  }
1243
1307
  }
1244
1308
  ```
@@ -0,0 +1,109 @@
1
+ # Published contracts
2
+
3
+ Other code depends on three things ldap-rest publishes. A change to one that
4
+ is not additive breaks that code, so it needs a major release and the pull
5
+ request says so before it merges.
6
+
7
+ - **Configuration**: the CLI options and `DM_*` variables of
8
+ [Configuration](../usage/configuration.md) and of each plugin's page.
9
+ - **REST API**: the routes, request bodies and answers of the
10
+ [REST API](../client-development/api/rest-api.md), their parameters,
11
+ responses (references resolved), statuses and response headers. The error
12
+ `code` values are reviewed by hand: the document types them as strings.
13
+ - **Plugin API**: what the npm sub-path exports declare — `ldap-rest`,
14
+ `./hooks`, `./plugin`, `./ldapactions`, `./expressformatedresponses`, the
15
+ `./plugin-*`, `./abstract-*` and `./browser-*` entries — and the JSON
16
+ schema format of the `./schema-*` entries.
17
+
18
+ [API stability](../README.md#api-stability) says what a minor or patch
19
+ release promises. This page says how a change is detected, and what to do
20
+ when it is not additive.
21
+
22
+ ## Committed snapshots
23
+
24
+ Two snapshots are generated locally and checked in CI, so a change to a
25
+ contract shows up as a diff instead of a surprise after the release:
26
+
27
+ - `docs/usage/options.snapshot.json` lists every option's CLI name, plural
28
+ form, environment variable, type, default, kind, required flag and aliases.
29
+ Regenerate it with `npm run snapshot:options`; `npm run check:options`, part
30
+ of `npm run check`, fails when it is stale.
31
+ - `openapi.json` is the REST API document the published reference is built
32
+ from. Regenerate it with `npm run generate:openapi`; `npm run check:openapi`
33
+ fails when it is stale.
34
+
35
+ The plugin types have no snapshot yet: one api-extractor report per sub-path
36
+ export is a change of its own. Until it lands, a change to an exported type is
37
+ reviewed by hand.
38
+
39
+ The snapshot records what the parser reads. A narrowed `choices` list, a
40
+ raised `min` or a lowered `max` is checked when a plugin reads its options,
41
+ not by the snapshot, and is reviewed by hand, as is a custom error `code`.
42
+
43
+ ## The pull request gate
44
+
45
+ The `contracts` job of `.github/workflows/contracts.yml` generates the
46
+ snapshots from the pull request and compares them with the base branch's
47
+ committed ones. It reports:
48
+
49
+ - additive changes — a new option, route or field — and lets them through;
50
+ - non-additive changes, and fails unless the pull request carries the
51
+ `breaking` label.
52
+
53
+ A change is non-additive when it removes or narrows what was published:
54
+ a removed option or environment variable, a changed type, default, bound,
55
+ `const`, enum or required flag, a request value that becomes an enum, a
56
+ request object that stops accepting additional properties, a removed route,
57
+ method, parameter, response status, response header or field, a request field
58
+ that becomes required, a response that may now be null, changed security
59
+ requirements, a removed union variant, a value an alternative of the type
60
+ closes with an enum or a `const` where the type used to accept any, an
61
+ untyped request value that becomes a union, an `allOf` that adds a
62
+ constraint, or — once the plugin snapshot exists — a removed export or a
63
+ narrowed signature. Renaming an option is a deprecation, not a removal, as
64
+ long as the old CLI name and environment variable stay in its `aliases` and
65
+ `envAliases`. Adding a union variant, a field or an optional parameter is
66
+ additive, and so is a request union with an alternative that still accepts
67
+ everything the base did — in a response the client reads every alternative,
68
+ so a field the others drop is still a removal. A `oneOf` is the exception:
69
+ it refuses a value two alternatives match, so an alternative as wide as the
70
+ base narrows the others away, and the alternatives are not told apart from
71
+ one another — a disjoint `oneOf` therefore asks for the label too.
72
+ Documentation-only edits, such as a summary or description, are ignored.
73
+
74
+ The job runs the scripts from the pull request, so a change to them is
75
+ reviewed like any other code, as `.github/CODEOWNERS` requires.
76
+
77
+ The `breaking` label records that the maintainers accepted the break, and the
78
+ `CHANGELOG.md` section must say so: the release gate below reads the section,
79
+ not the label, and refuses to ship the break as a minor or patch release. The
80
+ workflow listens to label events, so adding the label runs the job again; it
81
+ has its own concurrency group, so that event does not cancel the test matrix.
82
+
83
+ To try the comparison locally against your base branch:
84
+
85
+ ```bash
86
+ npx tsx scripts/contracts.ts --base origin/master
87
+ ```
88
+
89
+ ## The release gate
90
+
91
+ `.github/workflows/release.yml` runs `scripts/check-release.ts` on the tag. A
92
+ non-major tag whose `CHANGELOG.md` section lists `### Breaking Changes` is
93
+ refused. For a 0.x version, a minor release counts as major, as semantic
94
+ versioning lets it break. When the tag has no section of its own, the gate
95
+ reads `## Unreleased`, so a tag cut before the section was renamed is still
96
+ checked; with neither section it fails. When the CHANGELOG lists no earlier
97
+ release, only a first `X.0.0` counts as major, so a trimmed file cannot open
98
+ the gate.
99
+
100
+ ## Deprecating instead of breaking
101
+
102
+ The window and the rules are in
103
+ [CONTRIBUTING.md](../../CONTRIBUTING.md#deprecating-a-published-contract).
104
+ The mechanics of an option alias are in
105
+ [plugin options](../plugin-development/options.md#deprecating-an-option): the
106
+ old CLI name and environment variable go in `aliases` and `envAliases`, and
107
+ the server warns at startup while it still reads them. An array option's
108
+ plural form has no alias slot: renaming it is a breaking change, so it waits
109
+ for a major release.
@@ -7,7 +7,9 @@ This guide will help you create powerful, production-ready plugins for LDAP-Rest
7
7
  - [Why Create a Plugin](#why-create-a-plugin)
8
8
  - [Plugin Structure](#plugin-structure)
9
9
  - [Creating Your Plugin](#creating-your-plugin)
10
+ - [Plugin services](./services.md)
10
11
  - [Custom Configuration Options](#custom-configuration-options)
12
+ - [Plugin options](./options.md)
11
13
  - [Loading Your Plugin](#loading-your-plugin)
12
14
  - [Plugin Features](#plugin-features)
13
15
  - [Configuration Best Practices](#configuration-best-practices)
@@ -53,14 +55,21 @@ export default class MyPlugin extends DmPlugin {
53
55
  - **roles** (optional): Array categorizing plugin purpose
54
56
  - **dependencies** (optional): Required plugins that must load first
55
57
  - **hooks** (optional): Callbacks for LDAP operations
58
+ - **provides** (optional): Typed services this plugin offers, see
59
+ [plugin services](services.md)
56
60
  - **api()** (optional): Method to register REST endpoints
57
61
 
58
62
  ### Available from Base Class
59
63
 
60
64
  - **this.server**: DM server instance
61
65
  - **this.config**: Complete configuration object
66
+ - **this.options(declarations)**: Values of the options the plugin declares,
67
+ typed and checked, see [plugin options](options.md)
62
68
  - **this.logger**: Winston logger instance
63
- - **this.registeredHooks**: Access to global hook registry
69
+ - **this.hookBus**: The server's hooks: chain, notify and wait on them, see
70
+ [hooks](hooks.md#the-bus)
71
+ - **this.service(token)**: The instance another plugin provides under a
72
+ service token, or `null`, see [plugin services](services.md)
64
73
  - **this.fetch**: `fetch` timed into the
65
74
  [outbound metrics](../usage/operations.md#metrics) under the plugin's
66
75
  name; the lint refuses a bare `fetch` or `node-fetch` under `src`, and a
@@ -272,99 +281,15 @@ npx ldap-rest
272
281
 
273
282
  ## Custom Configuration Options
274
283
 
275
- LDAP-Rest automatically accepts and stores any unknown command-line options in the config object. This allows your plugin to define custom configuration without modifying core files.
284
+ A core plugin declares its options next to its code, in a
285
+ `<plugin>.options.ts` file, and reads them through `this.options()` with their
286
+ types, defaults and checks. See [plugin options](options.md).
276
287
 
277
- ### How It Works
278
-
279
- 1. **Command-line option format**: `--option-name value`
280
- 2. **Converted to config key**: `option_name` (dashes become underscores)
281
- 3. **Environment variable format**: `DM_OPTION_NAME` (uppercase with underscores)
282
- 4. **Access in plugin**: `this.config.option_name`
283
-
284
- ### Single Value Options
285
-
286
- ```bash
287
- # Command line
288
- npx ldap-rest \
289
- --webhook-url https://example.com/webhook \
290
- --webhook-timeout 5000 \
291
- --webhook-retry true
292
-
293
- # Environment variables
294
- export DM_WEBHOOK_URL=https://example.com/webhook
295
- export DM_WEBHOOK_TIMEOUT=5000
296
- export DM_WEBHOOK_RETRY=true
297
- ```
298
-
299
- In your plugin:
300
-
301
- ```typescript
302
- constructor(server: DM) {
303
- super(server);
304
-
305
- // Access custom options (converted to snake_case)
306
- const url = this.config.webhook_url; // string
307
- const timeout = this.config.webhook_timeout; // string (convert if needed)
308
- const retry = this.config.webhook_retry; // string 'true' (convert if needed)
309
-
310
- // Type conversion
311
- const timeoutNum = parseInt(timeout as string || '3000');
312
- const retryBool = (retry as string) === 'true';
313
- }
314
- ```
315
-
316
- ### Multiple Value Options
317
-
318
- Repeat the same option multiple times to create an array:
319
-
320
- ```bash
321
- # Command line
322
- npx ldap-rest \
323
- --allowed-domain example.com \
324
- --allowed-domain test.com \
325
- --allowed-domain dev.com
326
-
327
- # Environment variable (comma or semicolon separated)
328
- export DM_ALLOWED_DOMAINS=example.com,test.com,dev.com
329
- ```
330
-
331
- In your plugin:
332
-
333
- ```typescript
334
- constructor(server: DM) {
335
- super(server);
336
-
337
- // Access as array (note: singular becomes plural in config key)
338
- const domains = this.config.allowed_domain as string[];
339
- // or sometimes stored as: this.config.allowed_domains
340
-
341
- this.logger.info(`Allowed domains: ${domains.join(', ')}`);
342
- }
343
- ```
344
-
345
- ### Configuration Priority
346
-
347
- Values are resolved in this order (later overrides earlier):
348
-
349
- 1. **Default value** in your plugin
350
- 2. **Environment variable** (`DM_OPTION_NAME`)
351
- 3. **Command-line argument** (`--option-name`)
352
-
353
- ```typescript
354
- constructor(server: DM) {
355
- super(server);
356
-
357
- // Provide defaults for optional configuration
358
- const webhookUrl = this.config.webhook_url || 'http://localhost:8080/webhook';
359
- const webhookTimeout = parseInt(this.config.webhook_timeout as string || '3000');
360
- const maxRetries = parseInt(this.config.max_retries as string || '3');
361
-
362
- // Validate required configuration
363
- if (!this.config.webhook_secret) {
364
- throw new Error('--webhook-secret is required for authentication');
365
- }
366
- }
367
- ```
288
+ A plugin outside the repository cannot add to `src/config/args.ts` yet. It keeps
289
+ reading `this.config.<name>`: LDAP-Rest accepts and stores unknown
290
+ `--option-name value` pairs under `option_name`, with `--option-name` and
291
+ `DM_OPTION_NAME` overriding in that order. See
292
+ [configuration](../usage/configuration.md#environment-variables).
368
293
 
369
294
  ---
370
295
 
@@ -532,7 +457,8 @@ const results = await this.server.ldap.search(
532
457
  'ou=users,dc=example,dc=com'
533
458
  );
534
459
 
535
- // Search with pagination (returns AsyncGenerator)
460
+ // Search with pagination (returns AsyncGenerator; the connection is taken
461
+ // when the first page is read)
536
462
  const pagedResults = await this.server.ldap.search(
537
463
  {
538
464
  paged: true,
@@ -684,7 +610,15 @@ hooks: Hooks = {
684
610
 
685
611
  ### Exposing API Endpoints
686
612
 
687
- Create REST API endpoints for your plugin. **Important:** Always use the `asyncHandler` wrapper for async routes to ensure errors are properly handled and don't crash the server.
613
+ Declare routes with [`defineRoute`](routes.md): it types and validates the
614
+ request (`params`, `query`, `body`), answers a failing request with a 400
615
+ naming the paths at fault, and records the route for the OpenAPI document.
616
+ The `asyncHandler` form below remains for routes that have not been migrated
617
+ yet.
618
+
619
+ Create REST API endpoints for your plugin. Always use the `asyncHandler`
620
+ wrapper for async routes, so that errors are properly handled and do not
621
+ crash the server.
688
622
 
689
623
  ```typescript
690
624
  import DmPlugin, { asyncHandler } from '../../abstract/plugin';
@@ -791,6 +725,29 @@ app.get(
791
725
  );
792
726
  ```
793
727
 
728
+ **Wrapping an error keeps its answer:**
729
+
730
+ Wrap with `cause` rather than in the message alone. The status and the class
731
+ of what was thrown are read through the chain, so a refusal stays a 403 and
732
+ a timeout a 504 wherever it surfaces:
733
+
734
+ ```typescript
735
+ // ✅ GOOD: the refusal keeps its 403 when it reaches the client
736
+ throw new Error(`Failed to add group ${dn}: ${String(err)}`, { cause: err });
737
+
738
+ // ❌ BAD: a 403 becomes a 500, and its message is all that is left
739
+ throw new Error(`Failed to add group ${dn}: ${String(err)}`);
740
+ ```
741
+
742
+ **Refusing a caller:**
743
+
744
+ An authorization plugin throws `AuthzRefusedError`, whose message is for the
745
+ operator: it names the model and the branch it judged. The client is told
746
+ `AUTHZ_REFUSED` with `code: "forbidden"` instead, whatever a plugin wrapped
747
+ it in on the way. A refusal that is not an authorization plugin's is any
748
+ `ForbiddenError`, answered with its own message; `isAuthzRefusal(err)` tells
749
+ the two apart.
750
+
794
751
  **Custom error handling:**
795
752
 
796
753
  ```typescript
@@ -1369,6 +1326,7 @@ See [Tests and checks](../../CONTRIBUTING.md#tests-and-checks).
1369
1326
 
1370
1327
  - [Contributing Guide](../../CONTRIBUTING.md) - Git workflow and PR guidelines
1371
1328
  - [Hooks Reference](./hooks.md) - Complete list of available hooks
1329
+ - [Plugin services](./services.md) - Typed interfaces between plugins
1372
1330
  - [Core Plugin Examples](../../src/plugins/) - Browse existing plugins
1373
1331
  - [Test Examples](../../test/plugins/) - See how core plugins are tested
1374
1332
 
@@ -1,9 +1,10 @@
1
1
  # Plugin Dependency Graph
2
2
 
3
- This document describes the dependencies between LDAP-Rest plugins. There are two types of dependencies:
3
+ This document describes the dependencies between LDAP-Rest plugins. There are three types of dependencies:
4
4
 
5
5
  1. **Explicit Dependencies**: Defined in the `dependencies` property of a plugin class
6
- 2. **Hook Dependencies**: Implicit dependencies through hooks (one plugin provides a hook, another consumes it)
6
+ 2. **Service Dependencies**: A plugin reaches a typed interface another one provides, see [plugin services](services.md)
7
+ 3. **Hook Dependencies**: Implicit dependencies through hooks (one plugin provides a hook, another consumes it)
7
8
 
8
9
  ## Dependency Types
9
10
 
@@ -24,10 +25,20 @@ overrides — a dependency also given to `--plugin`, or an entry listed twice
24
25
  is loaded once: the second request waits for the first. Only an entry with
25
26
  overrides builds a second instance, and it needs a name of its own.
26
27
 
28
+ ### Service Dependencies
29
+
30
+ A plugin provides a typed interface under a token, and a consumer reaches it
31
+ with `this.service(token)`. The token replaces the registry name as the way to
32
+ find another plugin's code; `dependencies` still decides what loads first. A
33
+ consumer whose provider is absent gets `null` and a single warning.
34
+
35
+ See [plugin services](services.md) for the token, the provided interface and
36
+ the registration rules.
37
+
27
38
  ### Hook Dependencies
28
39
 
29
40
  **Hook Providers**: Plugins that implement hooks (via `hooks: Hooks = { ... }`)
30
- **Hook Consumers**: Plugins that call hooks (via `runHook()` or `launchHooks()`)
41
+ **Hook Consumers**: Plugins that raise hooks (via `this.hookBus`)
31
42
 
32
43
  ## Complete Dependency Graph
33
44
 
@@ -601,10 +612,10 @@ refused before a plugin judges its content, or replaces it.
601
612
  2. Returns modified data to next plugin
602
613
  3. Last plugin's return value is used
603
614
 
604
- For non-chained hooks:
615
+ For notified hooks, see [hooks](hooks.md#the-bus):
605
616
 
606
- - All hooks execute in parallel
607
- - Order doesn't matter
617
+ - All hooks run one after the other, in load order
618
+ - A failing one is logged and the next still runs
608
619
 
609
620
  **Example**: `ldapdeleterequest` chain
610
621
 
@@ -642,11 +653,16 @@ export default class MyPlugin extends DmPlugin {
642
653
 
643
654
  ### Providing Hooks
644
655
 
656
+ A hook is declared in `src/hooks.ts` before anything raises it, and raised
657
+ through the bus of the kind it is, see [hooks](hooks.md#the-bus):
658
+
645
659
  ```typescript
646
660
  export default class MyPlugin extends DmPlugin {
647
661
  async someMethod() {
648
- // Fire a hook for other plugins to consume
649
- await this.server.launchHooks('myCustomHook', [param1, param2]);
662
+ // Thread a value through the subscribers of a chained hook
663
+ value = await this.hookBus.chain('ldapsearchopts', value);
664
+ // Tell the subscribers of a notified one, without waiting for them
665
+ this.hookBus.notify('ldapadddone', dn, entry, changeContext(req));
650
666
  }
651
667
  }
652
668
  ```