@antelopejs/interface-dms 0.7.3 → 0.8.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 (237) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/dist/auth/index.d.ts +3 -3
  3. package/dist/auth/index.js +11 -9
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/auth/internal/jwt-auth-decorator.d.ts +20 -0
  6. package/dist/auth/internal/jwt-auth-decorator.js +51 -0
  7. package/dist/auth/internal/jwt-auth-decorator.js.map +1 -0
  8. package/dist/auth/internal/request-authenticators.d.ts +12 -0
  9. package/dist/auth/internal/request-authenticators.js +22 -0
  10. package/dist/auth/internal/request-authenticators.js.map +1 -1
  11. package/dist/base/activity-feed.d.ts +6 -6
  12. package/dist/base/block-registry/types.d.ts +16 -0
  13. package/dist/base/block-types.d.ts +1 -0
  14. package/dist/base/block-types.js +1 -0
  15. package/dist/base/block-types.js.map +1 -1
  16. package/dist/base/chart-card.d.ts +15 -0
  17. package/dist/base/chart-card.js +4 -0
  18. package/dist/base/chart-card.js.map +1 -1
  19. package/dist/base/chart-schemas.d.ts +26 -26
  20. package/dist/base/code-block.d.ts +3 -3
  21. package/dist/base/data-types/compare-types.js +19 -13
  22. package/dist/base/data-types/compare-types.js.map +1 -1
  23. package/dist/base/data-types/core.d.ts +11 -0
  24. package/dist/base/data-types/core.js +14 -1
  25. package/dist/base/data-types/core.js.map +1 -1
  26. package/dist/base/data-types/default-types.d.ts +112 -2
  27. package/dist/base/data-types/default-types.js +90 -19
  28. package/dist/base/data-types/default-types.js.map +1 -1
  29. package/dist/base/data-types/field-types.d.ts +10 -4
  30. package/dist/base/data-types/field-types.js +12 -2
  31. package/dist/base/data-types/field-types.js.map +1 -1
  32. package/dist/base/data-types/internal/compare-types.d.ts +3 -0
  33. package/dist/base/data-types/internal/compare-types.js +7 -0
  34. package/dist/base/data-types/internal/compare-types.js.map +1 -1
  35. package/dist/base/empty-state.d.ts +4 -4
  36. package/dist/base/form-block-schema.d.ts +59 -3
  37. package/dist/base/form-block-schema.js +12 -2
  38. package/dist/base/form-block-schema.js.map +1 -1
  39. package/dist/base/form-schema.d.ts +46 -1
  40. package/dist/base/form-schema.js.map +1 -1
  41. package/dist/base/form-types.d.ts +56 -13
  42. package/dist/base/form-types.js +12 -4
  43. package/dist/base/form-types.js.map +1 -1
  44. package/dist/base/guard.d.ts +6 -6
  45. package/dist/base/helpers/archive-mode-helpers.js +6 -2
  46. package/dist/base/helpers/archive-mode-helpers.js.map +1 -1
  47. package/dist/base/index.d.ts +1 -0
  48. package/dist/base/index.js +1 -0
  49. package/dist/base/index.js.map +1 -1
  50. package/dist/base/internal/form-schema.js +2 -0
  51. package/dist/base/internal/form-schema.js.map +1 -1
  52. package/dist/base/internal/layouts.d.ts +4 -0
  53. package/dist/base/key-value-list.d.ts +12 -9
  54. package/dist/base/key-value-list.js +1 -1
  55. package/dist/base/key-value-list.js.map +1 -1
  56. package/dist/base/kpi-card.d.ts +39 -5
  57. package/dist/base/kpi-card.js +18 -6
  58. package/dist/base/kpi-card.js.map +1 -1
  59. package/dist/base/layouts.d.ts +24 -0
  60. package/dist/base/layouts.js.map +1 -1
  61. package/dist/base/live-toggle.d.ts +11 -0
  62. package/dist/base/live-toggle.js +8 -1
  63. package/dist/base/live-toggle.js.map +1 -1
  64. package/dist/base/page-toolbar.d.ts +158 -0
  65. package/dist/base/page-toolbar.js +198 -0
  66. package/dist/base/page-toolbar.js.map +1 -0
  67. package/dist/base/period-selector.d.ts +9 -2
  68. package/dist/base/period-selector.js.map +1 -1
  69. package/dist/base/placeholder.d.ts +2 -2
  70. package/dist/base/section.d.ts +2 -2
  71. package/dist/base/stack.d.ts +2 -2
  72. package/dist/base/stat-group.d.ts +18 -6
  73. package/dist/base/stat-group.js +9 -0
  74. package/dist/base/stat-group.js.map +1 -1
  75. package/dist/base/table-view/column-display.d.ts +9 -5
  76. package/dist/base/table-view/column-display.js +1 -1
  77. package/dist/base/table-view/column-display.js.map +1 -1
  78. package/dist/base/table-view/data-functions.d.ts +26 -7
  79. package/dist/base/table-view/data-functions.js +10 -6
  80. package/dist/base/table-view/data-functions.js.map +1 -1
  81. package/dist/base/table-view/factory.js +24 -13
  82. package/dist/base/table-view/factory.js.map +1 -1
  83. package/dist/base/table-view/internal/auth.d.ts +2 -0
  84. package/dist/base/table-view/internal/auth.js +3 -1
  85. package/dist/base/table-view/internal/auth.js.map +1 -1
  86. package/dist/base/table-view/internal/factory-helpers.d.ts +5 -0
  87. package/dist/base/table-view/internal/factory-helpers.js +55 -11
  88. package/dist/base/table-view/internal/factory-helpers.js.map +1 -1
  89. package/dist/base/table-view/internal/guards.js +19 -2
  90. package/dist/base/table-view/internal/guards.js.map +1 -1
  91. package/dist/base/table-view/internal/realtime.js +17 -0
  92. package/dist/base/table-view/internal/realtime.js.map +1 -1
  93. package/dist/base/table-view/internal/resource-form.js +1 -0
  94. package/dist/base/table-view/internal/resource-form.js.map +1 -1
  95. package/dist/base/table-view/internal/row-actions-schema.d.ts +296 -0
  96. package/dist/base/table-view/internal/row-actions-schema.js +62 -0
  97. package/dist/base/table-view/internal/row-actions-schema.js.map +1 -0
  98. package/dist/base/table-view/internal/validation.d.ts +15 -3
  99. package/dist/base/table-view/internal/validation.js +70 -2
  100. package/dist/base/table-view/internal/validation.js.map +1 -1
  101. package/dist/base/table-view/meta.d.ts +8 -0
  102. package/dist/base/table-view/meta.js +25 -3
  103. package/dist/base/table-view/meta.js.map +1 -1
  104. package/dist/base/table-view/options.d.ts +90 -16
  105. package/dist/base/table-view/options.js.map +1 -1
  106. package/dist/base/table-view/realtime.d.ts +8 -0
  107. package/dist/base/table-view/realtime.js.map +1 -1
  108. package/dist/base/table-view/routes.js +5 -2
  109. package/dist/base/table-view/routes.js.map +1 -1
  110. package/dist/base/table-view/schema.d.ts +36 -30
  111. package/dist/base/table-view/schema.js +11 -57
  112. package/dist/base/table-view/schema.js.map +1 -1
  113. package/dist/base/table-view/source.d.ts +1 -1
  114. package/dist/base/table-view/source.js +4 -1
  115. package/dist/base/table-view/source.js.map +1 -1
  116. package/dist/base/table-view.d.ts +2 -2
  117. package/dist/base/table-view.js.map +1 -1
  118. package/dist/base/top-list-card.d.ts +5 -5
  119. package/dist/base/types/action-target.d.ts +77 -20
  120. package/dist/base/types/confirm-dialog.d.ts +5 -1
  121. package/dist/base/types/custom-button.d.ts +5 -3
  122. package/dist/base/types/field-config.d.ts +4 -4
  123. package/dist/base/types/guards.d.ts +10 -0
  124. package/dist/base/types/row-action.d.ts +6 -1
  125. package/dist/base/types/size.d.ts +3 -1
  126. package/dist/client-ip.d.ts +6 -2
  127. package/dist/client-ip.js +4 -2
  128. package/dist/client-ip.js.map +1 -1
  129. package/dist/component.d.ts +8 -0
  130. package/dist/component.js.map +1 -1
  131. package/dist/data-controllers/members.d.ts +2 -5
  132. package/dist/data-controllers/members.js +38 -9
  133. package/dist/data-controllers/members.js.map +1 -1
  134. package/dist/db/internal/tenantLifecycle.model.d.ts +4 -0
  135. package/dist/db/internal/tenantLifecycle.model.js +26 -0
  136. package/dist/db/internal/tenantLifecycle.model.js.map +1 -1
  137. package/dist/db/internal/tenantLifecycle.table.d.ts +1 -0
  138. package/dist/db/internal/tenantLifecycle.table.js +4 -0
  139. package/dist/db/internal/tenantLifecycle.table.js.map +1 -1
  140. package/dist/guards.js +2 -3
  141. package/dist/guards.js.map +1 -1
  142. package/dist/hooks.d.ts +47 -0
  143. package/dist/hooks.js +6 -0
  144. package/dist/hooks.js.map +1 -1
  145. package/dist/index.d.ts +3 -3
  146. package/dist/index.js +3 -2
  147. package/dist/index.js.map +1 -1
  148. package/dist/internal/invite-membership.d.ts +9 -0
  149. package/dist/internal/invite-membership.js +25 -0
  150. package/dist/internal/invite-membership.js.map +1 -1
  151. package/dist/internal/member-changes.d.ts +24 -0
  152. package/dist/internal/member-changes.js +66 -0
  153. package/dist/internal/member-changes.js.map +1 -0
  154. package/dist/internal/tenant-ownership.d.ts +4 -1
  155. package/dist/internal/tenant-ownership.js +4 -1
  156. package/dist/internal/tenant-ownership.js.map +1 -1
  157. package/dist/invite-extensions.d.ts +0 -9
  158. package/dist/invite-extensions.js +1 -36
  159. package/dist/invite-extensions.js.map +1 -1
  160. package/dist/invite-resolution.js +6 -2
  161. package/dist/invite-resolution.js.map +1 -1
  162. package/dist/invites.js +36 -22
  163. package/dist/invites.js.map +1 -1
  164. package/dist/layout-banners.d.ts +40 -4
  165. package/dist/layout-banners.js +25 -3
  166. package/dist/layout-banners.js.map +1 -1
  167. package/dist/notifications/internal.d.ts +2 -2
  168. package/dist/notifications/internal.js.map +1 -1
  169. package/dist/notifications/sendable.d.ts +3 -2
  170. package/dist/notifications/sendable.js +2 -1
  171. package/dist/notifications/sendable.js.map +1 -1
  172. package/dist/notifications/types.d.ts +9 -0
  173. package/dist/page/controllers.d.ts +44 -0
  174. package/dist/page/controllers.js +14 -1
  175. package/dist/page/controllers.js.map +1 -1
  176. package/dist/page/internal/layout-filter.d.ts +2 -1
  177. package/dist/page/internal/layout-filter.js +3 -1
  178. package/dist/page/internal/layout-filter.js.map +1 -1
  179. package/dist/page/internal/metadata-helpers.d.ts +7 -0
  180. package/dist/page/internal/metadata-helpers.js +13 -2
  181. package/dist/page/internal/metadata-helpers.js.map +1 -1
  182. package/dist/page/internal/page-registration.d.ts +2 -0
  183. package/dist/page/internal/page-registration.js.map +1 -1
  184. package/dist/page/layout-filter.js +31 -10
  185. package/dist/page/layout-filter.js.map +1 -1
  186. package/dist/page/metadata.js +29 -24
  187. package/dist/page/metadata.js.map +1 -1
  188. package/dist/page/types.d.ts +39 -4
  189. package/dist/page/types.js.map +1 -1
  190. package/dist/page.d.ts +1 -1
  191. package/dist/page.js +2 -1
  192. package/dist/page.js.map +1 -1
  193. package/dist/permissions-resolver.d.ts +26 -3
  194. package/dist/permissions-resolver.js +2 -1
  195. package/dist/permissions-resolver.js.map +1 -1
  196. package/dist/permissions.d.ts +7 -3
  197. package/dist/permissions.js +8 -6
  198. package/dist/permissions.js.map +1 -1
  199. package/dist/realtime.d.ts +24 -1
  200. package/dist/realtime.js +7 -1
  201. package/dist/realtime.js.map +1 -1
  202. package/dist/request-tenant.d.ts +17 -13
  203. package/dist/request-tenant.js +54 -42
  204. package/dist/request-tenant.js.map +1 -1
  205. package/dist/tenant-access.d.ts +15 -0
  206. package/dist/tenant-access.js +11 -1
  207. package/dist/tenant-access.js.map +1 -1
  208. package/dist/tenant-lifecycle.d.ts +27 -0
  209. package/dist/tenant-lifecycle.js +35 -0
  210. package/dist/tenant-lifecycle.js.map +1 -1
  211. package/dist/tenant-members.d.ts +26 -0
  212. package/dist/tenant-members.js +29 -0
  213. package/dist/tenant-members.js.map +1 -0
  214. package/dist/tenant-ownership.d.ts +4 -1
  215. package/dist/tenant-ownership.js +15 -3
  216. package/dist/tenant-ownership.js.map +1 -1
  217. package/dist/tenant-scoped-model.d.ts +2 -1
  218. package/dist/tenant-scoped-model.js +4 -3
  219. package/dist/tenant-scoped-model.js.map +1 -1
  220. package/docs/1.core/10.layout-banners.md +57 -2
  221. package/docs/1.core/2.pages-and-modules.md +20 -11
  222. package/docs/1.core/4.permissions.md +34 -1
  223. package/docs/1.core/5.tenancy.md +51 -5
  224. package/docs/1.core/7.hooks.md +13 -0
  225. package/docs/1.core/8.realtime.md +24 -3
  226. package/docs/2.auth/2.authentication.md +3 -3
  227. package/docs/3.base/1.introduction.md +1 -0
  228. package/docs/3.base/10.display-blocks.md +20 -2
  229. package/docs/3.base/2.types.md +7 -5
  230. package/docs/3.base/3.forms.md +21 -6
  231. package/docs/3.base/4.tables.md +14 -9
  232. package/docs/3.base/5.charts-and-widgets.md +8 -4
  233. package/docs/3.base/6.layout.md +38 -1
  234. package/docs/3.base/8.data-types.md +14 -7
  235. package/docs/4.notifications/3.sending.md +4 -3
  236. package/docs/5.html-render/1.introduction.md +5 -7
  237. package/package.json +3 -7
@@ -1,58 +1,70 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getRequestTenantId = getRequestTenantId;
4
- const jsonwebtoken_1 = require("jsonwebtoken");
4
+ exports.resolveRequestTenantId = resolveRequestTenantId;
5
+ const auth_1 = require("./auth");
5
6
  const request_authenticators_1 = require("./auth/internal/request-authenticators");
6
7
  const constants_1 = require("./constants");
7
- const BEARER_PREFIX = "Bearer ";
8
- function extractBearerToken(authHeader) {
9
- if (typeof authHeader !== "string")
10
- return undefined;
11
- if (!authHeader.startsWith(BEARER_PREFIX))
12
- return undefined;
13
- return authHeader.slice(BEARER_PREFIX.length);
8
+ const BEARER_SCHEME = /^Bearer\s+(\S+)$/i;
9
+ const pendingResolutions = new WeakMap();
10
+ /**
11
+ * The tenant of the request's verified credential: the one bound by the DMS
12
+ * auth decorators (`AuthUser`, `AuthUserWithPermission`, `IfAuthUser` with a
13
+ * valid token…), the tenant guards or `authenticateRequestPrincipal`.
14
+ * Without a verified credential, `DEFAULT_TENANT_ID`: a token is never read
15
+ * for its tenant before its signature, user and session are checked.
16
+ *
17
+ * Call it once the route's auth decorator has run (in the handler). A
18
+ * parameter or property provider resolved alongside the auth decorator uses
19
+ * {@link resolveRequestTenantId} instead.
20
+ *
21
+ * @throws HTTPResult 401 when the request's authentication failed or its
22
+ * credential changed since
23
+ */
24
+ function getRequestTenantId(ctx) {
25
+ return (0, request_authenticators_1.getAuthenticatedRequestTenantId)(ctx) ?? constants_1.DEFAULT_TENANT_ID;
14
26
  }
15
- function readTenantIdFromToken(token) {
27
+ async function verifyRequestTenantId(ctx, token) {
16
28
  try {
17
- const payload = (0, jsonwebtoken_1.decode)(token);
18
- if (payload &&
19
- typeof payload.tenantId === "string" &&
20
- payload.tenantId.length > 0) {
21
- return payload.tenantId;
22
- }
29
+ const data = await auth_1.internal.IfAuthUserAuthenticator(token);
30
+ if (!data)
31
+ return constants_1.DEFAULT_TENANT_ID;
32
+ const user = await auth_1.internal.AuthRawUserValidator(data);
33
+ if (!user || typeof user === "boolean")
34
+ return constants_1.DEFAULT_TENANT_ID;
35
+ const tenantId = data.tenantId || constants_1.DEFAULT_TENANT_ID;
36
+ (0, request_authenticators_1.bindVerifiedRequestPrincipal)(ctx, { userId: user._id, tenantId });
37
+ return tenantId;
23
38
  }
24
39
  catch {
25
- return undefined;
40
+ return constants_1.DEFAULT_TENANT_ID;
26
41
  }
27
- return undefined;
28
42
  }
29
43
  /**
30
- * Prefer the tenant bound by authenticateRequestPrincipal or the tenant guards.
31
- * An authentication failure or changed credential makes this accessor throw.
32
- * Without a bound principal, preserve legacy JWT decoding / DEFAULT_TENANT_ID.
33
- *
34
- * The legacy value is only *decoded*: this function does NOT verify the
35
- * JWT signature. It is safe when the request has already been authenticated
36
- * upstream (auth decorators such as `@AuthUser`, `@AuthTenantMember`, or the
37
- * gating performed by `PageController`) — those validators verify the signature
38
- * before this function runs, so the decoded `tenantId` is authentic.
39
- *
40
- * For routes that legitimately accept anonymous traffic (e.g. `@IfAuthUser`),
41
- * the fallback resolves to `DEFAULT_TENANT_ID` so public layouts can be served.
42
- *
43
- * Do NOT introduce new routes that rely on `tenantId` for authorization without
44
- * upstream auth verification — without signature checks, the claim is forgeable.
44
+ * {@link getRequestTenantId} for a provider that may run before the route's
45
+ * auth decorator (`TenantScopedModel`, a tenant-scoped `Model`): a bearer
46
+ * token not yet verified is verified here, and its tenant bound to the
47
+ * request. An invalid token reads as anonymous: `DEFAULT_TENANT_ID`.
45
48
  */
46
- function getRequestTenantId(ctx) {
47
- const authenticatedTenantId = (0, request_authenticators_1.getAuthenticatedRequestTenantId)(ctx);
48
- if (authenticatedTenantId)
49
- return authenticatedTenantId;
50
- const token = extractBearerToken(ctx.rawRequest?.headers?.authorization);
51
- if (token) {
52
- const tenantIdFromJwt = readTenantIdFromToken(token);
53
- if (tenantIdFromJwt)
54
- return tenantIdFromJwt;
49
+ async function resolveRequestTenantId(ctx) {
50
+ const bound = (0, request_authenticators_1.getAuthenticatedRequestTenantId)(ctx);
51
+ if (bound)
52
+ return bound;
53
+ const header = ctx.rawRequest?.headers?.authorization;
54
+ const token = typeof header === "string" ? header.match(BEARER_SCHEME)?.[1] : undefined;
55
+ if (!token)
56
+ return constants_1.DEFAULT_TENANT_ID;
57
+ const request = ctx.rawRequest;
58
+ const pending = pendingResolutions.get(request);
59
+ if (pending)
60
+ return pending;
61
+ const resolution = verifyRequestTenantId(ctx, token);
62
+ pendingResolutions.set(request, resolution);
63
+ try {
64
+ return await resolution;
65
+ }
66
+ finally {
67
+ pendingResolutions.delete(request);
55
68
  }
56
- return constants_1.DEFAULT_TENANT_ID;
57
69
  }
58
70
  //# sourceMappingURL=request-tenant.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"request-tenant.js","sourceRoot":"","sources":["../src/request-tenant.ts"],"names":[],"mappings":";;AAkDA,gDASC;AA1DD,+CAAsC;AACtC,mFAAyF;AACzF,2CAAgD;AAEhD,MAAM,aAAa,GAAG,SAAS,CAAC;AAMhC,SAAS,kBAAkB,CAAC,UAAmB;IAC7C,IAAI,OAAO,UAAU,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5D,OAAO,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa;IAC1C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,qBAAM,EAAC,KAAK,CAAgC,CAAC;QAC7D,IACE,OAAO;YACP,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ;YACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAC3B,CAAC;YACD,OAAO,OAAO,CAAC,QAAQ,CAAC;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,kBAAkB,CAAC,GAAmB;IACpD,MAAM,qBAAqB,GAAG,IAAA,wDAA+B,EAAC,GAAG,CAAC,CAAC;IACnE,IAAI,qBAAqB;QAAE,OAAO,qBAAqB,CAAC;IACxD,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;IACzE,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,eAAe,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,eAAe;YAAE,OAAO,eAAe,CAAC;IAC9C,CAAC;IACD,OAAO,6BAAiB,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"request-tenant.js","sourceRoot":"","sources":["../src/request-tenant.ts"],"names":[],"mappings":";;AA2BA,gDAEC;AAyBD,wDAmBC;AAvED,iCAAkC;AAClC,mFAGgD;AAChD,2CAAgD;AAEhD,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAE1C,MAAM,kBAAkB,GAAG,IAAI,OAAO,EAAoC,CAAC;AAE3E;;;;;;;;;;;;;GAaG;AACH,SAAgB,kBAAkB,CAAC,GAAmB;IACpD,OAAO,IAAA,wDAA+B,EAAC,GAAG,CAAC,IAAI,6BAAiB,CAAC;AACnE,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,GAAmB,EACnB,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,eAAQ,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI;YAAE,OAAO,6BAAiB,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,eAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,SAAS;YAAE,OAAO,6BAAiB,CAAC;QACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,6BAAiB,CAAC;QACpD,IAAA,qDAA4B,EAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,6BAAiB,CAAC;IAC3B,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,sBAAsB,CAC1C,GAAmB;IAEnB,MAAM,KAAK,GAAG,IAAA,wDAA+B,EAAC,GAAG,CAAC,CAAC;IACnD,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC;IACtD,MAAM,KAAK,GACT,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,IAAI,CAAC,KAAK;QAAE,OAAO,6BAAiB,CAAC;IACrC,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC;IAC/B,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,UAAU,GAAG,qBAAqB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrD,kBAAkB,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,OAAO,MAAM,UAAU,CAAC;IAC1B,CAAC;YAAS,CAAC;QACT,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;AACH,CAAC"}
@@ -50,6 +50,21 @@ export declare function RegisterTenantAccessGate(info: TenantAccessGateInfo): vo
50
50
  * undefined for unauthenticated requests.
51
51
  */
52
52
  export declare const CheckTenantAccess: (userId: string | undefined, tenantId: string) => Promise<TenantAccessResult>;
53
+ /** One denying gate, identified independently of its user-facing error code. */
54
+ export interface TenantAccessDenial {
55
+ gateId: string;
56
+ code: string;
57
+ }
58
+ /**
59
+ * Server-side evaluation of every registered gate, in gate order. Returns all
60
+ * denials (empty when allowed); any exception rejects the whole evaluation.
61
+ * Unlike CheckTenantAccess, an early denial does not hide later blockers.
62
+ * This is an observation, not an authorization receipt or an atomic snapshot
63
+ * of external state. Callers must preserve other holds and revalidate their
64
+ * operation's authorization before effects. Never infer ownership from code.
65
+ * Does not change the first-denial response of user-facing access checks.
66
+ */
67
+ export declare const EvaluateTenantAccess: (userId: string | undefined, tenantId: string) => Promise<TenantAccessDenial[]>;
53
68
  /**
54
69
  * Assert that the tenant is accessible; throws a 403 whose body is the
55
70
  * denying gate's `code` (a machine-readable i18n key) otherwise.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CheckTenantAccess = exports.internal = void 0;
3
+ exports.EvaluateTenantAccess = exports.CheckTenantAccess = exports.internal = void 0;
4
4
  exports.RegisterTenantAccessGate = RegisterTenantAccessGate;
5
5
  exports.AssertTenantAccess = AssertTenantAccess;
6
6
  const interface_api_1 = require("@antelopejs/interface-api");
@@ -48,6 +48,16 @@ function RegisterTenantAccessGate(info) {
48
48
  * undefined for unauthenticated requests.
49
49
  */
50
50
  exports.CheckTenantAccess = (0, interface_core_1.InterfaceFunction)();
51
+ /**
52
+ * Server-side evaluation of every registered gate, in gate order. Returns all
53
+ * denials (empty when allowed); any exception rejects the whole evaluation.
54
+ * Unlike CheckTenantAccess, an early denial does not hide later blockers.
55
+ * This is an observation, not an authorization receipt or an atomic snapshot
56
+ * of external state. Callers must preserve other holds and revalidate their
57
+ * operation's authorization before effects. Never infer ownership from code.
58
+ * Does not change the first-denial response of user-facing access checks.
59
+ */
60
+ exports.EvaluateTenantAccess = (0, interface_core_1.InterfaceFunction)();
51
61
  /**
52
62
  * Assert that the tenant is accessible; throws a 403 whose body is the
53
63
  * denying gate's `code` (a machine-readable i18n key) otherwise.
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-access.js","sourceRoot":"","sources":["../src/tenant-access.ts"],"names":[],"mappings":";;;AA0DA,4DAEC;AAmBD,gDAQC;AAvFD,6DAAuD;AACvD,+DAGoC;AAEpC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAiBlC;;GAEG;AACH,IAAiB,QAAQ,CAIxB;AAJD,WAAiB,QAAQ;IACV,iCAAwB,GAAG,IAAI,iCAAgB,EAEzD,CAAC;AACN,CAAC,EAJgB,QAAQ,wBAAR,QAAQ,QAIxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,wBAAwB,CAAC,IAA0B;IACjE,QAAQ,CAAC,wBAAwB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACU,QAAA,iBAAiB,GAC5B,IAAA,kCAAiB,GAKd,CAAC;AAEN;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CACtC,MAA0B,EAC1B,QAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAiB,EAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,0BAAU,CAAC,qBAAqB,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"tenant-access.js","sourceRoot":"","sources":["../src/tenant-access.ts"],"names":[],"mappings":";;;AA0DA,4DAEC;AA0CD,gDAQC;AA9GD,6DAAuD;AACvD,+DAGoC;AAEpC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAiBlC;;GAEG;AACH,IAAiB,QAAQ,CAIxB;AAJD,WAAiB,QAAQ;IACV,iCAAwB,GAAG,IAAI,iCAAgB,EAEzD,CAAC;AACN,CAAC,EAJgB,QAAQ,wBAAR,QAAQ,QAIxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,wBAAwB,CAAC,IAA0B;IACjE,QAAQ,CAAC,wBAAwB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACU,QAAA,iBAAiB,GAC5B,IAAA,kCAAiB,GAKd,CAAC;AAQN;;;;;;;;GAQG;AACU,QAAA,oBAAoB,GAC/B,IAAA,kCAAiB,GAKd,CAAC;AAEN;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CACtC,MAA0B,EAC1B,QAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAiB,EAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,0BAAU,CAAC,qBAAqB,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}
@@ -13,3 +13,30 @@ export declare function runTenantLifecycleOperation<T>(tenantId: string, work: (
13
13
  * rejection requires preserving data, never timeout-based takeover or reopening.
14
14
  */
15
15
  export declare function closeTenantLifecycleAdmission(tenantId: string): Promise<void>;
16
+ /** A non-destructive snapshot; no attempt expires or is retired by observation. */
17
+ export interface TenantLifecycleObservation {
18
+ revision: string;
19
+ barrierId: string | null;
20
+ activeAttemptIds: string[];
21
+ closed: boolean;
22
+ }
23
+ /** Observe outside a producer callback: an initiator must not drain itself. */
24
+ export declare function observeTenantLifecycle(tenantId: string): Promise<TenantLifecycleObservation>;
25
+ /**
26
+ * Publish a temporary barrier on the same CAS revision as producer admission.
27
+ * Use a unique identity per phase, never reuse it. Observe the revision BEFORE
28
+ * checking the durable business intent, then attempt this single CAS. After
29
+ * acquisition recheck that intent; release if abandoned. False requires a new
30
+ * observation and intent check, not a blind retry. Unknown outcomes throw.
31
+ * Existing attempts keep running; only an empty observation while this barrier
32
+ * remains owned permits inventory validation. No Stripe calls under a barrier.
33
+ */
34
+ export declare function pauseTenantLifecycleAdmission(tenantId: string, barrierId: string, expectedRevision: string): Promise<boolean>;
35
+ /**
36
+ * Release this owner's barrier, or invalidate the revision when none is held.
37
+ * The latter revokes a delayed acquisition after its business intent is aborted.
38
+ * Never reopens permanent deletion closure or another owner's barrier (false).
39
+ * Retains every active/uncertain attempt; timeout is not evidence of completion.
40
+ * Publish pending policy before release so newly admitted producers read it.
41
+ */
42
+ export declare function resumeTenantLifecycleAdmission(tenantId: string, barrierId: string): Promise<boolean>;
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runTenantLifecycleOperation = runTenantLifecycleOperation;
4
4
  exports.closeTenantLifecycleAdmission = closeTenantLifecycleAdmission;
5
+ exports.observeTenantLifecycle = observeTenantLifecycle;
6
+ exports.pauseTenantLifecycleAdmission = pauseTenantLifecycleAdmission;
7
+ exports.resumeTenantLifecycleAdmission = resumeTenantLifecycleAdmission;
5
8
  const node_crypto_1 = require("node:crypto");
6
9
  const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
7
10
  const tenantLifecycle_model_1 = require("./db/internal/tenantLifecycle.model");
@@ -55,4 +58,36 @@ async function runTenantLifecycleOperation(tenantId, work) {
55
58
  async function closeTenantLifecycleAdmission(tenantId) {
56
59
  await (0, interface_database_decorators_1.GetModel)(tenantLifecycle_model_1.TenantLifecycleModel, tenantId).close(tenantId);
57
60
  }
61
+ /** Observe outside a producer callback: an initiator must not drain itself. */
62
+ async function observeTenantLifecycle(tenantId) {
63
+ const state = await (0, interface_database_decorators_1.GetModel)(tenantLifecycle_model_1.TenantLifecycleModel, tenantId).state(tenantId);
64
+ return {
65
+ revision: state.revision,
66
+ barrierId: state.barrierId ?? null,
67
+ activeAttemptIds: [...state.activeAttemptIds],
68
+ closed: state.closed,
69
+ };
70
+ }
71
+ /**
72
+ * Publish a temporary barrier on the same CAS revision as producer admission.
73
+ * Use a unique identity per phase, never reuse it. Observe the revision BEFORE
74
+ * checking the durable business intent, then attempt this single CAS. After
75
+ * acquisition recheck that intent; release if abandoned. False requires a new
76
+ * observation and intent check, not a blind retry. Unknown outcomes throw.
77
+ * Existing attempts keep running; only an empty observation while this barrier
78
+ * remains owned permits inventory validation. No Stripe calls under a barrier.
79
+ */
80
+ async function pauseTenantLifecycleAdmission(tenantId, barrierId, expectedRevision) {
81
+ return (0, interface_database_decorators_1.GetModel)(tenantLifecycle_model_1.TenantLifecycleModel, tenantId).pause(tenantId, barrierId, expectedRevision);
82
+ }
83
+ /**
84
+ * Release this owner's barrier, or invalidate the revision when none is held.
85
+ * The latter revokes a delayed acquisition after its business intent is aborted.
86
+ * Never reopens permanent deletion closure or another owner's barrier (false).
87
+ * Retains every active/uncertain attempt; timeout is not evidence of completion.
88
+ * Publish pending policy before release so newly admitted producers read it.
89
+ */
90
+ async function resumeTenantLifecycleAdmission(tenantId, barrierId) {
91
+ return (0, interface_database_decorators_1.GetModel)(tenantLifecycle_model_1.TenantLifecycleModel, tenantId).resume(tenantId, barrierId);
92
+ }
58
93
  //# sourceMappingURL=tenant-lifecycle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-lifecycle.js","sourceRoot":"","sources":["../src/tenant-lifecycle.ts"],"names":[],"mappings":";;AAkCA,kEAoBC;AAMD,sEAIC;AAhED,6CAAyC;AACzC,6FAAqE;AACrE,+EAA2E;AAE3E,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAOlC;;;;;GAKG;AACH,SAAS,mBAAmB,CAAC,KAAc;IACzC,MAAM,SAAS,GAAI,KAAuC,EAAE,SAAS,CAAC;IACtE,IAAI,OAAO,SAAS,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,MAAM,IAAI,qBAAqB,IAAI,MAAM,GAAG,qBAAqB,CAAC;AAC3E,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,UAAU,2BAA2B,CAC/C,QAAgB,EAChB,IAAsB;IAEtB,MAAM,KAAK,GAAG,IAAA,wCAAQ,EAAC,4CAAoB,EAAE,QAAQ,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,IAAA,wBAAU,GAAE,CAAC;IAC/B,MAAM,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvC,IAAI,MAAS,CAAC;IACd,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,wEAAwE;YACxE,mDAAmD;YACnD,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACxC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,6BAA6B,CACjD,QAAgB;IAEhB,MAAM,IAAA,wCAAQ,EAAC,4CAAoB,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACjE,CAAC"}
1
+ {"version":3,"file":"tenant-lifecycle.js","sourceRoot":"","sources":["../src/tenant-lifecycle.ts"],"names":[],"mappings":";;AAkCA,kEAoBC;AAMD,sEAIC;AAWD,wDAUC;AAWD,sEAUC;AASD,wEAKC;AAxHD,6CAAyC;AACzC,6FAAqE;AACrE,+EAA2E;AAE3E,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAOlC;;;;;GAKG;AACH,SAAS,mBAAmB,CAAC,KAAc;IACzC,MAAM,SAAS,GAAI,KAAuC,EAAE,SAAS,CAAC;IACtE,IAAI,OAAO,SAAS,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,MAAM,IAAI,qBAAqB,IAAI,MAAM,GAAG,qBAAqB,CAAC;AAC3E,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,UAAU,2BAA2B,CAC/C,QAAgB,EAChB,IAAsB;IAEtB,MAAM,KAAK,GAAG,IAAA,wCAAQ,EAAC,4CAAoB,EAAE,QAAQ,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,IAAA,wBAAU,GAAE,CAAC;IAC/B,MAAM,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvC,IAAI,MAAS,CAAC;IACd,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,wEAAwE;YACxE,mDAAmD;YACnD,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACxC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,6BAA6B,CACjD,QAAgB;IAEhB,MAAM,IAAA,wCAAQ,EAAC,4CAAoB,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACjE,CAAC;AAUD,+EAA+E;AACxE,KAAK,UAAU,sBAAsB,CAC1C,QAAgB;IAEhB,MAAM,KAAK,GAAG,MAAM,IAAA,wCAAQ,EAAC,4CAAoB,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7E,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;QAClC,gBAAgB,EAAE,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC;QAC7C,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACI,KAAK,UAAU,6BAA6B,CACjD,QAAgB,EAChB,SAAiB,EACjB,gBAAwB;IAExB,OAAO,IAAA,wCAAQ,EAAC,4CAAoB,EAAE,QAAQ,CAAC,CAAC,KAAK,CACnD,QAAQ,EACR,SAAS,EACT,gBAAgB,CACjB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,8BAA8B,CAClD,QAAgB,EAChB,SAAiB;IAEjB,OAAO,IAAA,wCAAQ,EAAC,4CAAoB,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type MemberChange } from "./hooks";
2
+ /** What one membership or invitation write changes, and the write itself. */
3
+ export interface TenantMembersWrite<T> {
4
+ /**
5
+ * Announced through `Hook.MEMBER_BEING_CHANGED`, in order, before `write`
6
+ * runs. Empty for a write that changes no membership, such as an invitation
7
+ * created, edited or retired.
8
+ */
9
+ changes: MemberChange[];
10
+ write: () => Promise<T>;
11
+ }
12
+ /**
13
+ * The one entry point for writing tenant members and invitations: every
14
+ * insert, update or delete of a `TenantMemberModel` or `UserInviteModel` row
15
+ * goes through it. It admits the write under the tenant's lifecycle (refused
16
+ * while a barrier is held or once the tenant is closed), calls `plan` to read
17
+ * the current state and describe the change, fires `Hook.MEMBER_BEING_CHANGED`
18
+ * for each change (a throwing handler refuses the write, replays included),
19
+ * then runs the write and releases the admission.
20
+ *
21
+ * `plan` runs inside the admission so the change it describes is read from
22
+ * state no paused initiator is validating. Never call this from inside
23
+ * another `changeTenantMembers` or `runTenantLifecycleOperation` callback:
24
+ * each call takes its own admission.
25
+ */
26
+ export declare function changeTenantMembers<T>(tenantId: string, plan: () => Promise<TenantMembersWrite<T>>): Promise<T>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.changeTenantMembers = changeTenantMembers;
4
+ const hooks_1 = require("./hooks");
5
+ const tenant_lifecycle_1 = require("./tenant-lifecycle");
6
+ /**
7
+ * The one entry point for writing tenant members and invitations: every
8
+ * insert, update or delete of a `TenantMemberModel` or `UserInviteModel` row
9
+ * goes through it. It admits the write under the tenant's lifecycle (refused
10
+ * while a barrier is held or once the tenant is closed), calls `plan` to read
11
+ * the current state and describe the change, fires `Hook.MEMBER_BEING_CHANGED`
12
+ * for each change (a throwing handler refuses the write, replays included),
13
+ * then runs the write and releases the admission.
14
+ *
15
+ * `plan` runs inside the admission so the change it describes is read from
16
+ * state no paused initiator is validating. Never call this from inside
17
+ * another `changeTenantMembers` or `runTenantLifecycleOperation` callback:
18
+ * each call takes its own admission.
19
+ */
20
+ async function changeTenantMembers(tenantId, plan) {
21
+ return (0, tenant_lifecycle_1.runTenantLifecycleOperation)(tenantId, async () => {
22
+ const { changes, write } = await plan();
23
+ for (const change of changes) {
24
+ await (0, hooks_1.ExecuteHooks)(hooks_1.Hook.MEMBER_BEING_CHANGED, { tenantId, ...change });
25
+ }
26
+ return write();
27
+ });
28
+ }
29
+ //# sourceMappingURL=tenant-members.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-members.js","sourceRoot":"","sources":["../src/tenant-members.ts"],"names":[],"mappings":";;AA4BA,kDAWC;AAvCD,mCAAgE;AAChE,yDAAiE;AAajE;;;;;;;;;;;;;GAaG;AACI,KAAK,UAAU,mBAAmB,CACvC,QAAgB,EAChB,IAA0C;IAE1C,OAAO,IAAA,8CAA2B,EAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;QACxC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,IAAA,oBAAY,EAAC,YAAI,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -6,7 +6,10 @@ export interface TenantMembershipPatch {
6
6
  /** Durable invite delivery; grants membership without replacing later edits. */
7
7
  deliveryId?: string;
8
8
  }
9
- /** Admits membership writes before a tenant can begin destructive deletion. */
9
+ /**
10
+ * Adds the user to the tenant, or sets the roles and ownership of an existing
11
+ * member, through `changeTenantMembers`.
12
+ */
10
13
  export declare function applyTenantOwnership(userModel: UserModel, userId: string, tenantId: string, patch: TenantMembershipPatch): Promise<void>;
11
14
  export declare function syncPlatformOwnerOnTenantOwnerChange(userModel: UserModel, userId: string, tenantId: string, nextIsTenantOwner: boolean): Promise<void>;
12
15
  export declare function clearPlatformOwnerOnMemberRemoval(userModel: UserModel, userId: string, tenantId: string, removedMemberWasTenantOwner: boolean): Promise<void>;
@@ -3,11 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.applyTenantOwnership = applyTenantOwnership;
4
4
  exports.syncPlatformOwnerOnTenantOwnerChange = syncPlatformOwnerOnTenantOwnerChange;
5
5
  exports.clearPlatformOwnerOnMemberRemoval = clearPlatformOwnerOnMemberRemoval;
6
- const tenant_lifecycle_1 = require("./tenant-lifecycle");
6
+ const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
7
+ const tenantMembers_model_1 = require("./db/models/tenantMembers.model");
8
+ const tenant_members_1 = require("./tenant-members");
9
+ const member_changes_1 = require("./internal/member-changes");
7
10
  const tenant_ownership_1 = require("./internal/tenant-ownership");
8
- /** Admits membership writes before a tenant can begin destructive deletion. */
11
+ /**
12
+ * Adds the user to the tenant, or sets the roles and ownership of an existing
13
+ * member, through `changeTenantMembers`.
14
+ */
9
15
  async function applyTenantOwnership(userModel, userId, tenantId, patch) {
10
- await (0, tenant_lifecycle_1.runTenantLifecycleOperation)(tenantId, () => (0, tenant_ownership_1.applyAdmittedTenantOwnership)(userModel, userId, tenantId, patch));
16
+ await (0, tenant_members_1.changeTenantMembers)(tenantId, async () => {
17
+ const existing = await (0, interface_database_decorators_1.GetModel)(tenantMembers_model_1.TenantMemberModel, tenantId).getByUser(userId);
18
+ return {
19
+ changes: (0, member_changes_1.describeMembershipPatch)(existing, userId, patch),
20
+ write: () => (0, tenant_ownership_1.applyAdmittedTenantOwnership)(userModel, userId, tenantId, patch),
21
+ };
22
+ });
11
23
  }
12
24
  async function syncPlatformOwnerOnTenantOwnerChange(userModel, userId, tenantId, nextIsTenantOwner) {
13
25
  await (0, tenant_ownership_1.mirrorPlatformOwner)(userModel, userId, tenantId, nextIsTenantOwner);
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-ownership.js","sourceRoot":"","sources":["../src/tenant-ownership.ts"],"names":[],"mappings":";;AAgBA,oDASC;AAED,oFAOC;AAED,8EAQC;AA3CD,yDAAiE;AACjE,kEAGqC;AAUrC,+EAA+E;AACxE,KAAK,UAAU,oBAAoB,CACxC,SAAoB,EACpB,MAAc,EACd,QAAgB,EAChB,KAA4B;IAE5B,MAAM,IAAA,8CAA2B,EAAC,QAAQ,EAAE,GAAG,EAAE,CAC/C,IAAA,+CAA4B,EAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CACjE,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,oCAAoC,CACxD,SAAoB,EACpB,MAAc,EACd,QAAgB,EAChB,iBAA0B;IAE1B,MAAM,IAAA,sCAAmB,EAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAC5E,CAAC;AAEM,KAAK,UAAU,iCAAiC,CACrD,SAAoB,EACpB,MAAc,EACd,QAAgB,EAChB,2BAAoC;IAEpC,IAAI,CAAC,2BAA2B;QAAE,OAAO;IACzC,MAAM,IAAA,sCAAmB,EAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAChE,CAAC"}
1
+ {"version":3,"file":"tenant-ownership.js","sourceRoot":"","sources":["../src/tenant-ownership.ts"],"names":[],"mappings":";;AAsBA,oDAgBC;AAED,oFAOC;AAED,8EAQC;AAxDD,6FAAqE;AACrE,yEAAoE;AACpE,qDAAuD;AACvD,8DAAoE;AACpE,kEAGqC;AAUrC;;;GAGG;AACI,KAAK,UAAU,oBAAoB,CACxC,SAAoB,EACpB,MAAc,EACd,QAAgB,EAChB,KAA4B;IAE5B,MAAM,IAAA,oCAAmB,EAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,QAAQ,GAAG,MAAM,IAAA,wCAAQ,EAAC,uCAAiB,EAAE,QAAQ,CAAC,CAAC,SAAS,CACpE,MAAM,CACP,CAAC;QACF,OAAO;YACL,OAAO,EAAE,IAAA,wCAAuB,EAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC;YACzD,KAAK,EAAE,GAAG,EAAE,CACV,IAAA,+CAA4B,EAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC;SACnE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAEM,KAAK,UAAU,oCAAoC,CACxD,SAAoB,EACpB,MAAc,EACd,QAAgB,EAChB,iBAA0B;IAE1B,MAAM,IAAA,sCAAmB,EAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAC5E,CAAC;AAEM,KAAK,UAAU,iCAAiC,CACrD,SAAoB,EACpB,MAAc,EACd,QAAgB,EAChB,2BAAoC;IAEpC,IAAI,CAAC,2BAA2B;QAAE,OAAO;IACzC,MAAM,IAAA,sCAAmB,EAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAChE,CAAC"}
@@ -2,7 +2,8 @@ import { type Class } from "@antelopejs/interface-core/decorators";
2
2
  import { type DataModel } from "@antelopejs/interface-database-decorators";
3
3
  /**
4
4
  * Parameter/property decorator that injects `GetModel(cl, currentTenantId)`,
5
- * where `currentTenantId` is resolved per-request via {@link getRequestTenantId}.
5
+ * where `currentTenantId` is the tenant of the request's verified credential
6
+ * ({@link resolveRequestTenantId}), `DEFAULT_TENANT_ID` without one.
6
7
  *
7
8
  * Use on a `Model`-typed parameter of a route handler or DataAPI field. Only
8
9
  * applicable when the target table is registered in the `dms-tenant` schema
@@ -7,15 +7,16 @@ const interface_database_decorators_1 = require("@antelopejs/interface-database-
7
7
  const request_tenant_1 = require("./request-tenant");
8
8
  /**
9
9
  * Parameter/property decorator that injects `GetModel(cl, currentTenantId)`,
10
- * where `currentTenantId` is resolved per-request via {@link getRequestTenantId}.
10
+ * where `currentTenantId` is the tenant of the request's verified credential
11
+ * ({@link resolveRequestTenantId}), `DEFAULT_TENANT_ID` without one.
11
12
  *
12
13
  * Use on a `Model`-typed parameter of a route handler or DataAPI field. Only
13
14
  * applicable when the target table is registered in the `dms-tenant` schema
14
15
  * (or any schema with one instance per tenant).
15
16
  */
16
17
  exports.TenantScopedModel = (0, decorators_1.MakeParameterAndPropertyDecorator)((target, key, index, cl) => {
17
- (0, interface_api_1.SetParameterProvider)(target, key, index, (ctx) => {
18
- return (0, interface_database_decorators_1.GetModel)(cl, (0, request_tenant_1.getRequestTenantId)(ctx));
18
+ (0, interface_api_1.SetParameterProvider)(target, key, index, async (ctx) => {
19
+ return (0, interface_database_decorators_1.GetModel)(cl, await (0, request_tenant_1.resolveRequestTenantId)(ctx));
19
20
  });
20
21
  });
21
22
  //# sourceMappingURL=tenant-scoped-model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-scoped-model.js","sourceRoot":"","sources":["../src/tenant-scoped-model.ts"],"names":[],"mappings":";;;AAAA,6DAGmC;AACnC,sEAG+C;AAC/C,6FAGmD;AACnD,qDAAsD;AAEtD;;;;;;;GAOG;AACU,QAAA,iBAAiB,GAAG,IAAA,8CAAiC,EAChE,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAA8C,EAAE,EAAE;IACrE,IAAA,oCAAoB,EAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,GAAmB,EAAE,EAAE;QAC/D,OAAO,IAAA,wCAAQ,EAAC,EAAE,EAAE,IAAA,mCAAkB,EAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CACF,CAAC"}
1
+ {"version":3,"file":"tenant-scoped-model.js","sourceRoot":"","sources":["../src/tenant-scoped-model.ts"],"names":[],"mappings":";;;AAAA,6DAGmC;AACnC,sEAG+C;AAC/C,6FAGmD;AACnD,qDAA0D;AAE1D;;;;;;;;GAQG;AACU,QAAA,iBAAiB,GAAG,IAAA,8CAAiC,EAChE,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAA8C,EAAE,EAAE;IACrE,IAAA,oCAAoB,EAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE;QACrE,OAAO,IAAA,wCAAQ,EAAC,EAAE,EAAE,MAAM,IAAA,uCAAsB,EAAC,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CACF,CAAC"}
@@ -7,7 +7,7 @@ description: "A layout banner is a global message the backend adds under the das
7
7
 
8
8
  ## Overview
9
9
 
10
- A layout banner is a global message the backend adds under the dashboard header: a colored strip carrying a line of text and its link buttons, or a component of your frontend layer. It renders on every page of the dashboard layout — tenant pages and the module back-office pages under `/modules` alike — and banners stack by `order`.
10
+ A layout banner is a global message the backend adds under the dashboard header: a colored strip carrying a line of text and its link buttons, or a component of your frontend layer. It renders on the pages its [`scope`](#scope) selects — by default the application's pages, leaving the module area under `/modules` out — and banners stack by `order`.
11
11
 
12
12
  Whether a banner shows is decided per request, on the server, while the site layout is built. The answer is part of the server-rendered page, so a banner never flashes in or out after hydration.
13
13
 
@@ -92,6 +92,60 @@ RegisterLayoutBanner({
92
92
 
93
93
  Re-registering a `key` replaces the banner.
94
94
 
95
+ ## Scope
96
+
97
+ A banner is about the application being built — eighteen overdue invoices, a stock alert — so by default it shows on the application's pages only, and stays out of the **module area**: every page under `MODULE_URL_PREFIX` (`/modules`), that is the modules page, the module store and each module's own back-office pages. A banner about the workspace itself, which must follow the user into a module — billing past due, a suspended workspace, a maintenance window — sets `scope: LayoutBannerScope.EVERYWHERE`.
98
+
99
+ `scope` is a plain option of the banner declaration, the same on the three kinds of banner — a text banner, a resolved banner and a component banner — with the same values and the same `"app"` default:
100
+
101
+ ```typescript
102
+ import {
103
+ LayoutBannerScope,
104
+ LayoutBannerVariant,
105
+ RegisterLayoutBanner,
106
+ } from "@antelopejs/interface-dms/layout-banners";
107
+
108
+ // A text banner about the application: the default scope, written out.
109
+ RegisterLayoutBanner({
110
+ key: "my-app:overdue-invoices",
111
+ variant: LayoutBannerVariant.WARNING,
112
+ scope: LayoutBannerScope.APP,
113
+ text: "$my_app.banners.overdue_invoices",
114
+ actions: [{ label: "$my_app.banners.review", to: "/finance/invoices" }],
115
+ visible: async ({ tenantId }) => hasOverdueInvoices(tenantId),
116
+ });
117
+
118
+ // A resolved banner about the workspace: it follows the user into /modules.
119
+ RegisterLayoutBanner({
120
+ key: "saas:past-due",
121
+ variant: LayoutBannerVariant.ERROR,
122
+ scope: LayoutBannerScope.EVERYWHERE,
123
+ resolve: pastDueContent,
124
+ });
125
+
126
+ // A component banner about the workspace, also shown inside /modules.
127
+ RegisterLayoutBanner({
128
+ key: "my-app:maintenance",
129
+ variant: LayoutBannerVariant.INFO,
130
+ scope: LayoutBannerScope.EVERYWHERE,
131
+ component: "MyAppMaintenanceBanner",
132
+ props: { statusUrl: "https://status.example.com" },
133
+ visible: async () => isMaintenanceScheduled(),
134
+ });
135
+ ```
136
+
137
+ | `scope` | Shows on |
138
+ | ------- | -------- |
139
+ | `"app"` (default) | Every page outside the module area. |
140
+ | `"everywhere"` | Every page of the dashboard layout, module area included. |
141
+ | `"modules"` | The module area only. |
142
+
143
+ The scope is decided by the page, not by the request: the site layout is fetched once and shared by every page, so each banner carries its scope and the dashboard applies it against the current route — in the server-rendered page, so a banner never flashes in or out after hydration, and again on each client-side navigation, so moving in or out of `/modules` adds or removes it without re-fetching anything. A banner's `visible` or `resolve` therefore does not see the route; the scope is the only per-page filter. A dismissal is per key, whatever page the banner was closed on.
144
+
145
+ ::warning
146
+ **Behaviour change.** Before `scope` existed every banner showed on every page, module area included. A banner registered without `scope` now stays out of `/modules`: give `scope: LayoutBannerScope.EVERYWHERE` to the ones reporting a workspace-wide state.
147
+ ::
148
+
95
149
  ## `LayoutBannerInfo`
96
150
 
97
151
  | Field | Type | Description |
@@ -105,6 +159,7 @@ Re-registering a `key` replaces the banner.
105
159
  | `order` | `number` | Stack order, ascending from the top. Defaults to `0`; ties keep the registration order. |
106
160
  | `icon` | `string` | Leading icon. Defaults to one matching the variant. |
107
161
  | `dismissible` | `boolean` | Whether the user can close the banner. Defaults to `false`. |
162
+ | `scope` | `LayoutBannerScope` | `"app"`, `"everywhere"` or `"modules"`: the pages the banner shows on. Defaults to `"app"`, every page outside `/modules`. See [Scope](#scope). |
108
163
  | `visible` | `LayoutBannerVisibility` | Decides per request whether a `text` or `component` banner shows. Absent means always. |
109
164
  | `resolve` | `LayoutBannerResolver` | Builds `{ text, actions? }` per request, or `undefined` to hide the banner. Exclusive with `text`, `component` and `visible`. |
110
165
 
@@ -128,6 +183,6 @@ The resolvers run whenever a client fetches its site layout — on a full page l
128
183
 
129
184
  ## Rendering
130
185
 
131
- Banners render under the header of the dashboard layout, full width, in `order`, a text banner's `actions` on its right. Each one is its own live region: `error` banners carry `role="alert"`, `info` and `warning` banners `role="status"`.
186
+ Banners render under the header of the dashboard layout, on the pages their scope covers, full width, in `order`, a text banner's `actions` on its right. Each one is its own live region: `error` banners carry `role="alert"`, `info` and `warning` banners `role="status"`.
132
187
 
133
188
  A dismissible banner shows a close button. The dismissal lasts for the browser session and is kept in the `dms-dismissed-banners` cookie, which the server reads too, so a dismissed banner is already absent from the server-rendered page. A banner that is not dismissible ignores any dismissal recorded under its key.
@@ -62,6 +62,7 @@ The third argument is the page layout, a `ComponentInfo` produced by a layout bu
62
62
  | `order` | `number` | Sort order among siblings. |
63
63
  | `icon` | `string` | Icon name. |
64
64
  | `hidden` | `boolean` | Keep the entry out of the menu. Inherited from the resolved category when that category is hidden. |
65
+ | `parent` | `ControllerClass` | The page this one is a detail of: the breadcrumb shows it before this page ("Operations / Projects / <record>"), its route parameters filled from this page's own. Resolved at registration, served as `parentSlug`. Pages only. |
65
66
  | `permission` | `Partial<Permission> \| Action` | Override the auto-derived page permission, or bind the page to an existing component `Action`. See [Permissions](/docs/interfaces/core/permissions). |
66
67
  | `publicAccess` | `boolean` | Serve the page without authentication. Inherited from the resolved category. |
67
68
  | `authOnly` | `boolean` | Require a logged-in user but no permission check. Inherited from the resolved category. |
@@ -212,7 +213,7 @@ Call `RegisterModule` before any page that references the module id: a page reso
212
213
  | `description` | `string` | Description carried on the module root. |
213
214
  | `icon` | `string` | Icon name. |
214
215
  | `landingPage` | `string` | Id of the page the module opens on; `/modules/<id>` redirects to it, query and hash kept. Defaults to the first page by `order`. |
215
- | `catalogCategory` | `string` | Category the module is filed under in the module catalog (`"Content"`, `"Developer"`…), may be a `$` i18n key; without one it goes under "Other". Unrelated to menu categories (`MenuOptions.category`, `defaultCategory`). Formerly `category`. |
216
+ | `catalogCategory` | `string` | Category the module is filed under in the module catalog (`"Content"`, `"Developer"`…), may be a `$` i18n key. The Modules page and the store both file the module under it; without one, a store module takes the category of the store catalog, and any other goes under "Other". Unrelated to menu categories (`MenuOptions.category`, `defaultCategory`). Formerly `category`. |
216
217
  | `defaultCategory` | `{ displayName?, icon?, order?, urlSlug? }` | Groups the module's pages that declare `module` without an explicit `category`. Without it, those pages attach directly to the module root. |
217
218
 
218
219
  The generated default category uses `urlSlug` `"pages"` unless you override it, and falls back to the label `"$menu.section.pages"` when `displayName` is empty or omitted.
@@ -388,12 +389,20 @@ interface PageLayout<T = unknown> {
388
389
  Registering a page installs a `PageLayoutHandler` for its `fullSlug`:
389
390
 
390
391
  ```typescript
391
- type PageLayoutHandler = (
392
- user: User | undefined,
393
- memberModel: TenantMemberModel,
394
- roleModel: RoleModel,
395
- tenantId: string,
396
- ) => Promise<PageLayout>;
392
+ interface PageLayoutRequest {
393
+ user: User | undefined;
394
+ memberModel: TenantMemberModel;
395
+ roleModel: RoleModel;
396
+ tenantId: string;
397
+ /** The page URL's query, for header-button availability. */
398
+ query?: Readonly<Record<string, string>>;
399
+ /** The page URL's route parameters (`:id`), likewise. */
400
+ params?: Readonly<Record<string, string>>;
401
+ /** Passed to the permissions resolvers: `{ preview }` when a role preview reads the layout. */
402
+ resolverContext?: PermissionsResolverContext;
403
+ }
404
+
405
+ type PageLayoutHandler = (request: PageLayoutRequest) => Promise<PageLayout>;
397
406
  ```
398
407
 
399
408
  The handler drops the components the caller has no permission for and applies each component's own filter. Native upload tokens are bound to the exact page/component mount during page registration, not stamped by this handler; upload and metadata requests still check live authorization ([File storage](/docs/components/file-storage)). A page with `publicAccess`, `authOnly` or `noComponentPermissions` skips the filtering step and returns every component. Look a handler up with `GetPageLayoutBySlug(slug)`, which returns `undefined` for an unknown slug:
@@ -416,12 +425,12 @@ async function renderLayout(
416
425
  tenantId: string,
417
426
  ): Promise<PageLayout | undefined> {
418
427
  const handler = GetPageLayoutBySlug(slug);
419
- return handler?.(
428
+ return handler?.({
420
429
  user,
421
- GetModel(TenantMemberModel, tenantId),
422
- GetModel(RoleModel, tenantId),
430
+ memberModel: GetModel(TenantMemberModel, tenantId),
431
+ roleModel: GetModel(RoleModel, tenantId),
423
432
  tenantId,
424
- );
433
+ });
425
434
  }
426
435
  ```
427
436