@astrale-domains/shell 0.1.5 → 0.1.7

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 (301) hide show
  1. package/README.md +73 -173
  2. package/dist/domain.d.ts +477 -31
  3. package/dist/domain.js +87 -78
  4. package/dist/functions/application/index.js +2 -1
  5. package/dist/functions/application/legacy/index.js +1 -0
  6. package/dist/functions/application/legacy/list-entrypoints.js +8 -0
  7. package/dist/functions/authority/capabilities.js +1 -0
  8. package/dist/functions/authority/composition.js +47 -0
  9. package/dist/functions/authority/index.js +2 -0
  10. package/dist/functions/authority/roles.js +98 -0
  11. package/dist/functions/directory/disconnect-directory.js +35 -0
  12. package/dist/functions/directory/index.js +1 -0
  13. package/dist/functions/group/change-group-membership.js +43 -9
  14. package/dist/functions/group/create-group.js +23 -0
  15. package/dist/functions/group/delete-group.js +33 -0
  16. package/dist/functions/group/deprovision-group.js +27 -0
  17. package/dist/functions/group/group-errors.js +43 -0
  18. package/dist/functions/group/group-membership-errors.js +2 -0
  19. package/dist/functions/group/group.js +51 -0
  20. package/dist/functions/group/index.js +8 -2
  21. package/dist/functions/group/legacy/index.js +1 -0
  22. package/dist/functions/group/legacy/membership.js +60 -0
  23. package/dist/functions/group/membership-laws.js +40 -0
  24. package/dist/functions/group/provision-group.js +33 -0
  25. package/dist/functions/group/update-group.js +55 -0
  26. package/dist/functions/invitation/index.js +2 -0
  27. package/dist/functions/invitation/invitation-errors.js +13 -0
  28. package/dist/functions/invitation/invitation-methods.js +41 -0
  29. package/dist/functions/invitation/send-invitation.js +81 -0
  30. package/dist/functions/user/account.js +35 -0
  31. package/dist/functions/user/create-user.js +45 -0
  32. package/dist/functions/user/deactivate-user.js +19 -0
  33. package/dist/functions/user/delete-user.js +34 -0
  34. package/dist/functions/user/deprovision-user.js +21 -0
  35. package/dist/functions/user/index.js +15 -1
  36. package/dist/functions/user/invite-errors.js +18 -0
  37. package/dist/functions/user/invite-user.js +42 -34
  38. package/dist/functions/user/legacy/claimed-account.js +11 -0
  39. package/dist/functions/user/legacy/index.js +4 -0
  40. package/dist/functions/user/legacy/invite.js +20 -0
  41. package/dist/functions/user/legacy/migrate-accounts.js +98 -0
  42. package/dist/functions/user/legacy/record-profile.js +19 -0
  43. package/dist/functions/user/protect-administrators.js +16 -0
  44. package/dist/functions/user/provision-user.js +70 -0
  45. package/dist/functions/user/reactivate-user.js +19 -0
  46. package/dist/functions/user/read-account.js +12 -0
  47. package/dist/functions/user/request-invitation.js +17 -0
  48. package/dist/functions/user/update-profile.js +22 -0
  49. package/dist/functions/user/update-user.js +68 -0
  50. package/dist/functions/user/user-errors.js +69 -0
  51. package/dist/functions/user/write-account.js +57 -0
  52. package/dist/functions/view-access/index.js +2 -1
  53. package/dist/functions/view-access/{assign-views.js → legacy/assign-views.js} +11 -7
  54. package/dist/functions/view-access/legacy/index.js +1 -0
  55. package/dist/functions/view-access/{view-access-errors.js → legacy/view-access-errors.js} +5 -1
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.js +3 -1
  58. package/dist/integrations/user-invitation/invite-user-request.d.ts +6 -0
  59. package/dist/legacy/index.js +1 -0
  60. package/dist/legacy/release.js +33 -0
  61. package/dist/mutations/composition/grant-role.js +13 -0
  62. package/dist/mutations/composition/index.js +1 -0
  63. package/dist/mutations/group/legacy/index.js +1 -0
  64. package/dist/mutations/group/legacy/membership-class.js +7 -0
  65. package/dist/mutations/group/set-group-membership.js +23 -14
  66. package/dist/mutations/identity/create-profile.js +37 -0
  67. package/dist/mutations/identity/delete-identity.js +28 -0
  68. package/dist/mutations/identity/index.js +4 -0
  69. package/dist/mutations/identity/write-account.js +109 -0
  70. package/dist/mutations/identity/write-group.js +72 -0
  71. package/dist/mutations/invitation/index.js +1 -0
  72. package/dist/mutations/invitation/write-invitation.js +62 -0
  73. package/dist/mutations/user/legacy/index.js +1 -0
  74. package/dist/mutations/user/{record-user-profile.js → legacy/record-user-profile.js} +5 -2
  75. package/dist/providers/admin-enrollment/create-admin-enrollment-provider.js +6 -3
  76. package/dist/providers/admin-enrollment/legacy/index.js +1 -0
  77. package/dist/providers/admin-enrollment/legacy/send-without-profile.js +16 -0
  78. package/dist/queries/application/legacy/index.js +1 -0
  79. package/dist/queries/application/legacy/read-installed-entrypoints.js +47 -0
  80. package/dist/queries/composition/index.js +2 -0
  81. package/dist/queries/composition/read-extensions.js +34 -0
  82. package/dist/queries/composition/read-role-nodes.js +41 -0
  83. package/dist/queries/group/index.js +1 -1
  84. package/dist/queries/group/legacy/index.js +2 -0
  85. package/dist/queries/group/legacy/read-core-groups.js +46 -0
  86. package/dist/queries/group/legacy/read-member-of-group.js +24 -0
  87. package/dist/queries/group/read-group-membership-endpoints.js +11 -0
  88. package/dist/queries/group/read-group-membership.js +6 -21
  89. package/dist/queries/identity/index.js +1 -0
  90. package/dist/queries/identity/read-identities.js +29 -0
  91. package/dist/queries/invitation/index.js +1 -0
  92. package/dist/queries/invitation/read-invitation.js +25 -0
  93. package/dist/queries/profile/index.js +1 -0
  94. package/dist/queries/profile/read-profile.js +15 -0
  95. package/dist/queries/role/index.js +1 -0
  96. package/dist/queries/role/read-role-node.js +13 -0
  97. package/dist/queries/user/legacy/index.js +1 -0
  98. package/dist/queries/user/legacy/read-user-profile.js +27 -0
  99. package/dist/runtime.d.ts +18203 -630
  100. package/dist/runtime.js +32 -7
  101. package/dist/schema/index.d.ts +7 -4
  102. package/dist/schema/index.js +8 -4
  103. package/dist/schema/legacy/index.js +1 -0
  104. package/dist/schema/legacy/schema.js +32 -0
  105. package/dist/schema/modules/application/legacy/entrypoint-entry.js +11 -0
  106. package/dist/schema/modules/application/legacy/functions/index.js +1 -0
  107. package/dist/schema/modules/application/legacy/functions/list-entrypoints.js +21 -0
  108. package/dist/schema/modules/application/legacy/index.js +2 -0
  109. package/dist/schema/modules/directory/functions/disconnect-directory.d.ts +13 -0
  110. package/dist/schema/modules/directory/functions/disconnect-directory.js +15 -0
  111. package/dist/schema/modules/directory/functions/index.d.ts +1 -0
  112. package/dist/schema/modules/directory/functions/index.js +1 -0
  113. package/dist/schema/modules/directory/index.d.ts +1 -0
  114. package/dist/schema/modules/directory/index.js +1 -0
  115. package/dist/schema/modules/group/classes/group-profile-link.d.ts +34 -0
  116. package/dist/schema/modules/group/classes/group-profile-link.js +21 -0
  117. package/dist/schema/modules/group/classes/group-profile.d.ts +15 -0
  118. package/dist/schema/modules/group/classes/group-profile.js +20 -0
  119. package/dist/schema/modules/group/classes/group.d.ts +73 -1
  120. package/dist/schema/modules/group/classes/group.js +28 -7
  121. package/dist/schema/modules/group/classes/index.d.ts +2 -1
  122. package/dist/schema/modules/group/classes/index.js +3 -1
  123. package/dist/schema/modules/group/functions/assign-member.js +17 -0
  124. package/dist/schema/modules/group/functions/create-group.js +20 -0
  125. package/dist/schema/modules/group/functions/delete-group.js +12 -0
  126. package/dist/schema/modules/group/functions/deprovision-group.js +14 -0
  127. package/dist/schema/modules/group/functions/index.js +7 -2
  128. package/dist/schema/modules/group/functions/provision-group.js +14 -0
  129. package/dist/schema/modules/group/functions/unassign-member.js +17 -0
  130. package/dist/schema/modules/group/functions/update-group.js +20 -0
  131. package/dist/schema/modules/group/index.d.ts +3 -3
  132. package/dist/schema/modules/group/index.js +4 -3
  133. package/dist/schema/modules/group/legacy/admission.d.ts +9 -0
  134. package/dist/schema/modules/group/legacy/admission.js +9 -0
  135. package/dist/schema/modules/group/legacy/classes/index.d.ts +1 -0
  136. package/dist/schema/modules/group/legacy/classes/index.js +1 -0
  137. package/dist/schema/modules/group/{classes → legacy/classes}/member-of-group.d.ts +10 -1
  138. package/dist/schema/modules/group/legacy/classes/member-of-group.js +32 -0
  139. package/dist/schema/modules/group/legacy/core-groups.d.ts +22 -0
  140. package/dist/schema/modules/group/legacy/core-groups.js +28 -0
  141. package/dist/schema/modules/group/legacy/index.d.ts +5 -0
  142. package/dist/schema/modules/group/legacy/index.js +6 -0
  143. package/dist/schema/modules/group/legacy/policies/belongs-to-group.d.ts +5 -0
  144. package/dist/schema/modules/group/legacy/policies/belongs-to-group.js +9 -0
  145. package/dist/schema/modules/group/legacy/policies/index.d.ts +2 -0
  146. package/dist/schema/modules/group/legacy/policies/index.js +2 -0
  147. package/dist/schema/modules/group/legacy/policies/owns-membership-group.d.ts +6 -0
  148. package/dist/schema/modules/group/legacy/policies/owns-membership-group.js +12 -0
  149. package/dist/schema/modules/group/legacy/relaxations.d.ts +21 -0
  150. package/dist/schema/modules/group/legacy/relaxations.js +27 -0
  151. package/dist/schema/modules/group/policies/describes-group-of-subject.d.ts +2 -0
  152. package/dist/schema/modules/group/policies/describes-group-of-subject.js +7 -0
  153. package/dist/schema/modules/group/policies/index.d.ts +2 -1
  154. package/dist/schema/modules/group/policies/index.js +2 -1
  155. package/dist/schema/modules/group/policies/is-group-profile-source.d.ts +2 -0
  156. package/dist/schema/modules/group/policies/is-group-profile-source.js +6 -0
  157. package/dist/schema/modules/group/types/group.js +10 -0
  158. package/dist/schema/modules/group/types/index.js +1 -0
  159. package/dist/schema/modules/invitation/classes/index.js +2 -0
  160. package/dist/schema/modules/invitation/classes/invitation-user.js +17 -0
  161. package/dist/schema/modules/invitation/classes/invitation.js +30 -0
  162. package/dist/schema/modules/invitation/functions/accept.js +16 -0
  163. package/dist/schema/modules/invitation/functions/cancel.js +12 -0
  164. package/dist/schema/modules/invitation/functions/index.js +4 -0
  165. package/dist/schema/modules/invitation/functions/resend.js +12 -0
  166. package/dist/schema/modules/invitation/functions/send.js +20 -0
  167. package/dist/schema/modules/invitation/index.js +2 -0
  168. package/dist/schema/modules/invitation/types/index.js +1 -0
  169. package/dist/schema/modules/invitation/types/invitation-status.js +5 -0
  170. package/dist/schema/modules/namespace/classes/alias.d.ts +2 -1
  171. package/dist/schema/modules/namespace/classes/alias.js +11 -2
  172. package/dist/schema/modules/namespace/functions/link.js +3 -2
  173. package/dist/schema/modules/namespace/functions/rename.js +3 -2
  174. package/dist/schema/modules/namespace/functions/unlink.js +3 -2
  175. package/dist/schema/modules/role/classes/index.d.ts +1 -0
  176. package/dist/schema/modules/role/classes/index.js +1 -0
  177. package/dist/schema/modules/role/classes/role.d.ts +16 -0
  178. package/dist/schema/modules/role/classes/role.js +19 -0
  179. package/dist/schema/modules/role/core/index.d.ts +1 -0
  180. package/dist/schema/modules/role/core/index.js +1 -0
  181. package/dist/schema/modules/role/core/roles.d.ts +37 -0
  182. package/dist/schema/modules/role/core/roles.js +30 -0
  183. package/dist/schema/modules/role/index.d.ts +2 -0
  184. package/dist/schema/modules/role/index.js +2 -0
  185. package/dist/schema/modules/user/classes/index.d.ts +2 -0
  186. package/dist/schema/modules/user/classes/index.js +3 -0
  187. package/dist/schema/modules/user/classes/user-profile-link.d.ts +35 -0
  188. package/dist/schema/modules/user/classes/user-profile-link.js +22 -0
  189. package/dist/schema/modules/user/classes/user-profile.d.ts +41 -0
  190. package/dist/schema/modules/user/classes/user-profile.js +30 -0
  191. package/dist/schema/modules/user/classes/user.d.ts +133 -8
  192. package/dist/schema/modules/user/classes/user.js +46 -15
  193. package/dist/schema/modules/user/functions/create-user.js +21 -0
  194. package/dist/schema/modules/user/functions/deactivate-user.js +12 -0
  195. package/dist/schema/modules/user/functions/delete-user.js +12 -0
  196. package/dist/schema/modules/user/functions/deprovision-user.js +14 -0
  197. package/dist/schema/modules/user/functions/index.js +8 -1
  198. package/dist/schema/modules/user/functions/invite-user.js +23 -4
  199. package/dist/schema/modules/user/functions/provision-user.js +25 -0
  200. package/dist/schema/modules/user/functions/reactivate-user.js +12 -0
  201. package/dist/schema/modules/user/functions/update-profile.js +16 -0
  202. package/dist/schema/modules/user/functions/update-user.js +22 -0
  203. package/dist/schema/modules/user/index.d.ts +2 -1
  204. package/dist/schema/modules/user/index.js +2 -1
  205. package/dist/schema/modules/user/legacy/account-properties.js +24 -0
  206. package/dist/schema/modules/user/legacy/functions/index.js +2 -0
  207. package/dist/schema/modules/user/legacy/functions/migrate-accounts.js +23 -0
  208. package/dist/schema/modules/user/legacy/functions/record-profile.js +18 -0
  209. package/dist/schema/modules/user/legacy/index.js +5 -0
  210. package/dist/schema/modules/user/legacy/invite.js +15 -0
  211. package/dist/schema/modules/user/legacy/relaxations.js +37 -0
  212. package/dist/schema/modules/user/policies/describes-subject.d.ts +2 -0
  213. package/dist/schema/modules/user/policies/describes-subject.js +7 -0
  214. package/dist/schema/modules/user/policies/index.d.ts +2 -0
  215. package/dist/schema/modules/user/policies/index.js +2 -0
  216. package/dist/schema/modules/user/policies/is-profile-source.d.ts +2 -0
  217. package/dist/schema/modules/user/policies/is-profile-source.js +6 -0
  218. package/dist/schema/modules/user/types/account.d.ts +12 -0
  219. package/dist/schema/modules/user/types/account.js +12 -0
  220. package/dist/schema/modules/user/types/index.d.ts +1 -1
  221. package/dist/schema/modules/user/types/index.js +1 -1
  222. package/dist/schema/modules/view-access/classes/view-access.d.ts +7 -11
  223. package/dist/schema/modules/view-access/classes/view-access.js +17 -12
  224. package/dist/schema/modules/view-access/index.d.ts +1 -2
  225. package/dist/schema/modules/view-access/index.js +1 -2
  226. package/dist/schema/modules/view-access/legacy/functions/assign-views.js +22 -0
  227. package/dist/schema/modules/view-access/legacy/functions/index.js +1 -0
  228. package/dist/schema/modules/view-access/legacy/index.js +2 -0
  229. package/dist/schema/modules/view-access/legacy/view-assignment.js +24 -0
  230. package/dist/schema/modules/view-access/policies/holds-view-access.d.ts +2 -0
  231. package/dist/schema/modules/view-access/policies/holds-view-access.js +5 -0
  232. package/dist/schema/modules/view-access/policies/index.d.ts +1 -0
  233. package/dist/schema/modules/view-access/policies/index.js +1 -0
  234. package/dist/schema/policies/has-role.d.ts +6 -0
  235. package/dist/schema/policies/has-role.js +9 -0
  236. package/dist/schema/policies/index.d.ts +1 -0
  237. package/dist/schema/policies/index.js +1 -4
  238. package/dist/schema/policies/legacy/administers-shell-class.js +21 -0
  239. package/dist/schema/policies/legacy/index.js +4 -0
  240. package/dist/schema/policies/legacy/is-self.js +9 -0
  241. package/dist/schema/policies/legacy/owns-class.js +12 -0
  242. package/dist/schema/policies/legacy/uses-function.js +9 -0
  243. package/dist/schema/schema.d.ts +1849 -118
  244. package/dist/schema/schema.js +35 -15
  245. package/dist/schema/types/attributes.js +15 -0
  246. package/dist/schema/types/index.js +1 -0
  247. package/dist/utils/attributes/attributes.js +12 -0
  248. package/dist/utils/attributes/index.js +1 -0
  249. package/dist/utils/index.js +3 -0
  250. package/dist/utils/same-json/index.js +1 -0
  251. package/dist/utils/same-json/same-json.js +9 -0
  252. package/package.json +4 -4
  253. package/dist/functions/application/list-entrypoints.js +0 -6
  254. package/dist/functions/user/record-profile.js +0 -18
  255. package/dist/mutations/user/index.js +0 -1
  256. package/dist/queries/application/index.js +0 -1
  257. package/dist/queries/application/read-installed-entrypoints.js +0 -28
  258. package/dist/queries/user/index.js +0 -1
  259. package/dist/queries/user/read-user-profile.js +0 -9
  260. package/dist/schema/modules/application/functions/index.d.ts +0 -1
  261. package/dist/schema/modules/application/functions/index.js +0 -1
  262. package/dist/schema/modules/application/functions/list-entrypoints.d.ts +0 -15
  263. package/dist/schema/modules/application/functions/list-entrypoints.js +0 -16
  264. package/dist/schema/modules/application/index.d.ts +0 -2
  265. package/dist/schema/modules/application/index.js +0 -2
  266. package/dist/schema/modules/application/types/entrypoint-entry.d.ts +0 -8
  267. package/dist/schema/modules/application/types/entrypoint-entry.js +0 -7
  268. package/dist/schema/modules/application/types/index.d.ts +0 -1
  269. package/dist/schema/modules/application/types/index.js +0 -1
  270. package/dist/schema/modules/group/classes/member-of-group.js +0 -12
  271. package/dist/schema/modules/group/core/administrators-group.d.ts +0 -1
  272. package/dist/schema/modules/group/core/administrators-group.js +0 -6
  273. package/dist/schema/modules/group/core/applications-group.d.ts +0 -1
  274. package/dist/schema/modules/group/core/applications-group.js +0 -6
  275. package/dist/schema/modules/group/core/index.d.ts +0 -3
  276. package/dist/schema/modules/group/core/index.js +0 -3
  277. package/dist/schema/modules/group/core/members-group.d.ts +0 -1
  278. package/dist/schema/modules/group/core/members-group.js +0 -6
  279. package/dist/schema/modules/group/functions/assign-member-to-group.js +0 -11
  280. package/dist/schema/modules/group/functions/unassign-member-from-group.js +0 -11
  281. package/dist/schema/modules/group/policies/belongs-to-group.d.ts +0 -1
  282. package/dist/schema/modules/group/policies/belongs-to-group.js +0 -5
  283. package/dist/schema/modules/user/functions/record-profile.js +0 -14
  284. package/dist/schema/modules/user/types/user-profile.d.ts +0 -7
  285. package/dist/schema/modules/user/types/user-profile.js +0 -7
  286. package/dist/schema/modules/view-access/core/index.d.ts +0 -1
  287. package/dist/schema/modules/view-access/core/index.js +0 -1
  288. package/dist/schema/modules/view-access/core/view-only-group.d.ts +0 -5
  289. package/dist/schema/modules/view-access/core/view-only-group.js +0 -10
  290. package/dist/schema/modules/view-access/functions/assign-views.d.ts +0 -16
  291. package/dist/schema/modules/view-access/functions/assign-views.js +0 -17
  292. package/dist/schema/modules/view-access/functions/index.d.ts +0 -1
  293. package/dist/schema/modules/view-access/functions/index.js +0 -1
  294. package/dist/schema/modules/view-access/types/index.js +0 -1
  295. package/dist/schema/modules/view-access/types/view-assignment.js +0 -16
  296. package/dist/schema/policies/administers-shell-class.js +0 -11
  297. package/dist/schema/policies/is-self.js +0 -5
  298. package/dist/schema/policies/owns-class.js +0 -7
  299. package/dist/schema/policies/uses-function.js +0 -5
  300. /package/dist/functions/group/{assign-member-to-group.js → assign-member.js} +0 -0
  301. /package/dist/functions/group/{unassign-member-from-group.js → unassign-member.js} +0 -0
@@ -1,5 +1,9 @@
1
1
  import { z } from 'zod';
2
- export const viewAccessErrors = {
2
+ /**
3
+ * @deprecated The refusals of `assignViews` (L-SHELL-12). Replaced by the Kernel's refusals of a
4
+ * Mutation on `view_access`. Removal: Shell major (delivery step 5) after 1Pact step 4.
5
+ */
6
+ export const legacyViewAccessErrors = {
3
7
  INVALID_VIEW_ASSIGNMENT_REFERENCE: {
4
8
  family: 'VALIDATION_ERROR',
5
9
  message: 'Identities and Views must be exact canonical @NodeId paths.',
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { ShellSchema, S, type Shell, type ShellClasses, type ShellSchemaInput, type InvitationSummary, User, Group, member_of_group, adminGroup, memberGroup, Namespace, alias, viewsNamespace, } from './schema/index.js';
1
+ export { ShellSchema, S, type Shell, type ShellClasses, type ShellSchemaInput, type InvitationSummary, type ProfileName, User, Group, Role, hasRole, profileRoles, atomicRoles, shellRoles, PROFILE_NAMES, view_access, Namespace, alias, viewsNamespace, member_of_group, adminGroup, memberGroup, } from './schema/index.js';
package/dist/index.js CHANGED
@@ -1 +1,3 @@
1
- export { ShellSchema, S, User, Group, member_of_group, adminGroup, memberGroup, Namespace, alias, viewsNamespace, } from './schema/index.js';
1
+ export { ShellSchema, S, User, Group, Role, hasRole, profileRoles, atomicRoles, shellRoles, PROFILE_NAMES, view_access, Namespace, alias, viewsNamespace,
2
+ // L-SHELL-16: the former names of the legacy Edge Class and Core groups.
3
+ member_of_group, adminGroup, memberGroup, } from './schema/index.js';
@@ -1,6 +1,12 @@
1
+ import type { ProfileName } from '../../schema/modules/role/index.js';
2
+ /**
3
+ * What Shell asks Admin to send: the account, its email, the profile it was granted, and who asked.
4
+ * `profile` is optional on the wire, so an Admin that ignores it still sends the invitation.
5
+ */
1
6
  export interface InviteRequest {
2
7
  readonly requestId: string;
3
8
  readonly user: string;
4
9
  readonly email: string;
10
+ readonly profile?: ProfileName;
5
11
  readonly requestedBy: string;
6
12
  }
@@ -0,0 +1 @@
1
+ export { legacyRelease } from './release.js';
@@ -0,0 +1,33 @@
1
+ import { requirements, } from '@astrale-os/sdk/domain';
2
+ import { K } from '@astrale-os/sdk/schema';
3
+ import { S } from '../schema/index.js';
4
+ const key = ({ ref }) => `${ref.origin}:${ref.name}`;
5
+ /** The members each legacy binding adds to a target binding, by Role. */
6
+ const members = new Map([
7
+ // The legacy Core groups each become a member of the profile they stood for, so every stored
8
+ // member holds that profile with no data migration (AM3).
9
+ [key(S.core.nodes['workspace.admin']), [S.core.nodes.admin]],
10
+ [key(S.core.nodes['workspace.member']), [S.core.nodes.member]],
11
+ // View only and Applications open only what they are given: Kernel reader, with no profile (AM10).
12
+ [key(K.core.nodes.reader), [S.core.nodes['view-only'], S.core.nodes.applications]],
13
+ // listEntrypoints reads every entrypoint View as Shell.
14
+ [key(K.core.nodes['view.reader']), [requirements.domain]],
15
+ ]);
16
+ /**
17
+ * @deprecated The release bindings legacy Core groups and callers need (L-SHELL-7): each legacy
18
+ * group is a member of its profile (AM3); Shell reads installed entrypoints for `listEntrypoints`
19
+ * (row L-SHELL-11), and writes `extends_with` when it acts for the owner of a foreign Group
20
+ * subclass, bounded by its own scope (row L-SHELL-9, AM5). Replaced by the target bindings alone.
21
+ * Removal: Shell major (delivery step 5) after 1Pact step 4.
22
+ */
23
+ export function legacyRelease(target) {
24
+ return {
25
+ roles: target.roles.map((binding) => {
26
+ const added = members.get(key(binding.role));
27
+ return added === undefined
28
+ ? binding
29
+ : { ...binding, members: [...(binding.members ?? []), ...added] };
30
+ }),
31
+ scopes: [{ on: K.classes.extends_with, operations: ['create', 'delete'] }],
32
+ };
33
+ }
@@ -0,0 +1,13 @@
1
+ import { defineMutation } from '@astrale-os/sdk/mutation';
2
+ /** Grant one Role: `extends_with` from the holder to it (R1). Shell owns its Roles' Class. */
3
+ export const grantRole = defineMutation()((domain) => ({
4
+ id: 'shell.role.grant',
5
+ build({ holder, role }, mutation) {
6
+ mutation.createEdge({
7
+ class: domain.dependencies.kernel.classes.extends_with,
8
+ source: { id: holder },
9
+ target: role,
10
+ });
11
+ },
12
+ project: () => ({}),
13
+ }));
@@ -0,0 +1 @@
1
+ export { grantRole } from './grant-role.js';
@@ -0,0 +1 @@
1
+ export { legacyMembershipClass } from './membership-class.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @deprecated The legacy `member_of_group` Edge Class a membership write converges (L-SHELL-8).
3
+ * Replaced by `extends_with` alone. Removal: Shell major (delivery step 5) after 1Pact step 4.
4
+ */
5
+ export function legacyMembershipClass(domain) {
6
+ return domain.classes.member_of_group;
7
+ }
@@ -1,29 +1,38 @@
1
1
  import { defineMutation } from '@astrale-os/sdk/mutation';
2
+ // Seam L-SHELL-8: the legacy membership Edge a change may converge beside `extends_with`.
3
+ import { legacyMembershipClass } from './legacy/index.js';
4
+ /** Converge the membership Edges of one pair, guarded by what was observed of them. */
2
5
  export const setGroupMembership = defineMutation()((domain) => ({
3
6
  id: 'shell.group.membership.set',
4
7
  build(input, mutation) {
5
- mutation.expect.query(input.current.endpoints);
6
- mutation.expect.query(input.current.guard);
7
- const endpoints = { source: input.member, target: input.group };
8
- const changes = [
9
- { class: domain.classes.member_of_group, current: input.current.membership },
10
- { class: domain.dependencies.kernel.classes.extends_with, current: input.current.authority },
11
- ];
12
- for (const edge of changes) {
13
- if (edge.current === input.present)
8
+ mutation.expect.query(input.endpoints);
9
+ for (const edge of input.edges)
10
+ mutation.expect.query(edge.guard);
11
+ const endpoints = { source: { id: input.member }, target: { id: input.group } };
12
+ for (const edge of input.edges) {
13
+ if (edge.current === edge.present)
14
14
  continue;
15
- if (input.present)
16
- mutation.createEdge({ class: edge.class, ...endpoints });
15
+ const selected = edge.edge === 'authority'
16
+ ? domain.dependencies.kernel.classes.extends_with
17
+ : legacyMembershipClass(domain);
18
+ if (edge.present)
19
+ mutation.createEdge({ class: selected, ...endpoints });
17
20
  else
18
- mutation.deleteEdge({ class: edge.class, ...endpoints });
21
+ mutation.deleteEdge({ class: selected, ...endpoints });
19
22
  }
20
23
  },
21
24
  project: () => ({ ok: true }),
22
25
  reject: (failure) => {
23
26
  if (failure.outcomeKnown &&
24
27
  failure.code !== 'MUTATION_PRECONDITION_FAILED' &&
25
- failure.code !== 'MUTATION_CONFLICT')
28
+ failure.code !== 'MUTATION_CONFLICT' &&
29
+ failure.code !== 'MUTATION_POST_STATE_INVALID')
26
30
  throw failure;
27
- return { ok: false, code: failure.code, outcomeKnown: failure.outcomeKnown };
31
+ return {
32
+ ok: false,
33
+ code: failure.code,
34
+ ...('rule' in failure && failure.rule !== undefined ? { rule: failure.rule } : {}),
35
+ outcomeKnown: failure.outcomeKnown,
36
+ };
28
37
  },
29
38
  }));
@@ -0,0 +1,37 @@
1
+ import { defineMutation } from '@astrale-os/sdk/mutation';
2
+ /** Give an account or a group stored before profiles its profile. */
3
+ export const createProfile = defineMutation()((domain) => ({
4
+ id: 'shell.profile.create',
5
+ build(input, mutation) {
6
+ if (input.kind === 'User') {
7
+ const profile = mutation.createNode({
8
+ as: 'profile',
9
+ class: domain.classes.UserProfile,
10
+ props: input.values,
11
+ });
12
+ mutation.createEdge({
13
+ class: domain.classes.user_profile,
14
+ source: { id: input.owner },
15
+ target: profile,
16
+ });
17
+ return;
18
+ }
19
+ const profile = mutation.createNode({
20
+ as: 'profile',
21
+ class: domain.classes.GroupProfile,
22
+ props: input.values,
23
+ });
24
+ mutation.createEdge({
25
+ class: domain.classes.group_profile,
26
+ source: { id: input.owner },
27
+ target: profile,
28
+ });
29
+ },
30
+ project: () => ({ ok: true }),
31
+ reject: (failure) => {
32
+ // Another write gave it its profile meanwhile, or another profile holds the email.
33
+ if (failure.outcomeKnown)
34
+ return { ok: false, refusal: failure.code };
35
+ throw failure;
36
+ },
37
+ }));
@@ -0,0 +1,28 @@
1
+ import { defineMutation } from '@astrale-os/sdk/mutation';
2
+ /** Delete one account or group, and every membership into it, in one write. */
3
+ export const deleteIdentity = defineMutation()((domain) => ({
4
+ id: 'shell.identity.delete',
5
+ build({ kind, node, members, profile }, mutation) {
6
+ for (const { source, target } of members) {
7
+ mutation.deleteEdge({
8
+ class: domain.dependencies.kernel.classes.extends_with,
9
+ source: { id: source },
10
+ target: { id: target },
11
+ });
12
+ }
13
+ mutation.deleteNode({ node: { id: node }, class: domain.classes[kind] });
14
+ if (profile !== undefined) {
15
+ mutation.deleteNode({
16
+ node: { id: profile },
17
+ class: kind === 'User' ? domain.classes.UserProfile : domain.classes.GroupProfile,
18
+ });
19
+ }
20
+ },
21
+ project: () => ({ ok: true }),
22
+ reject: (failure) => {
23
+ // A member joined, or the Node changed, since it was read.
24
+ if (failure.outcomeKnown)
25
+ return { ok: false, refusal: failure.code };
26
+ throw failure;
27
+ },
28
+ }));
@@ -0,0 +1,4 @@
1
+ export { PROFILE_PROPERTIES, createAccount, newProfileValues, profileValues, updateAccount, } from './write-account.js';
2
+ export { GROUP_PROFILE_PROPERTIES, createGroup, groupProfileValues, updateGroup, } from './write-group.js';
3
+ export { deleteIdentity } from './delete-identity.js';
4
+ export { createProfile } from './create-profile.js';
@@ -0,0 +1,109 @@
1
+ import { defineMutation } from '@astrale-os/sdk/mutation';
2
+ /** What an account shares on its profile: the rest stays private on the account. */
3
+ export const PROFILE_PROPERTIES = ['name', 'givenName', 'familyName', 'email'];
4
+ /**
5
+ * The values of a new profile: its name is the account's name, else "given family", else the
6
+ * email; undefined for an account that has none of them (it gets its profile once it has one).
7
+ */
8
+ export function newProfileValues(values) {
9
+ const shared = profileValues(values);
10
+ const full = [values.givenName, values.familyName].filter(Boolean).join(' ');
11
+ const name = values.name ?? (full === '' ? undefined : full) ?? values.email;
12
+ return name === undefined ? undefined : { ...shared, name: name.slice(0, 100) };
13
+ }
14
+ /** A profile keeps an email of 100 characters at most; a longer one stays on the account alone. */
15
+ export const PROFILE_EMAIL_MAX = 100;
16
+ /** The profile values among account values. */
17
+ export function profileValues(values) {
18
+ return Object.fromEntries(PROFILE_PROPERTIES.flatMap((key) => {
19
+ const value = values[key];
20
+ if (value === undefined)
21
+ return [];
22
+ if (key === 'email' && value.length > PROFILE_EMAIL_MAX)
23
+ return [];
24
+ return [[key, value]];
25
+ }));
26
+ }
27
+ const refuse = (failure) => {
28
+ if (failure.outcomeKnown && failure.code === 'MUTATION_POST_STATE_INVALID') {
29
+ if (failure.rule === 'duplicate-key')
30
+ return { ok: false, refusal: 'DUPLICATE_KEY' };
31
+ }
32
+ if (failure.outcomeKnown && failure.code === 'MUTATION_PRECONDITION_FAILED') {
33
+ return { ok: false, refusal: 'CHANGED' };
34
+ }
35
+ throw failure;
36
+ };
37
+ /** Create one account, optionally granting it Roles in the same write. */
38
+ export const createAccount = defineMutation()((domain) => ({
39
+ id: 'shell.account.create',
40
+ build({ values }, mutation) {
41
+ const account = mutation.createNode({
42
+ as: 'account',
43
+ class: domain.classes.User,
44
+ props: values,
45
+ });
46
+ const shared = newProfileValues(values);
47
+ if (shared === undefined)
48
+ return;
49
+ const profile = mutation.createNode({
50
+ as: 'profile',
51
+ class: domain.classes.UserProfile,
52
+ props: shared,
53
+ });
54
+ mutation.createEdge({ class: domain.classes.user_profile, source: account, target: profile });
55
+ },
56
+ project: (result) => {
57
+ const user = result.createdNodes['account'];
58
+ if (user === undefined)
59
+ throw new TypeError('The Kernel created no account.');
60
+ return { ok: true, user };
61
+ },
62
+ reject: refuse,
63
+ }));
64
+ /** Set and erase Properties of one account, on the values it was decided on. */
65
+ export const updateAccount = defineMutation()((domain) => ({
66
+ id: 'shell.account.update',
67
+ build({ user, set, unset, expected, profile }, mutation) {
68
+ if (expected !== undefined) {
69
+ const absent = Object.keys(expected).filter((key) => expected[key] === undefined);
70
+ const equals = Object.fromEntries(Object.entries(expected).filter(([, value]) => value !== undefined));
71
+ mutation.expect.node({
72
+ node: { id: user },
73
+ class: domain.classes.User,
74
+ props: { equals, ...(absent.length === 0 ? {} : { absent }) },
75
+ });
76
+ }
77
+ mutation.updateNode({
78
+ node: { id: user },
79
+ class: domain.classes.User,
80
+ props: { set, ...(unset === undefined || unset.length === 0 ? {} : { unset }) },
81
+ });
82
+ if (profile === undefined)
83
+ return;
84
+ if ('create' in profile) {
85
+ const created = mutation.createNode({
86
+ as: 'profile',
87
+ class: domain.classes.UserProfile,
88
+ props: profile.create,
89
+ });
90
+ mutation.createEdge({
91
+ class: domain.classes.user_profile,
92
+ source: { id: user },
93
+ target: created,
94
+ });
95
+ return;
96
+ }
97
+ const shared = profileValues(set);
98
+ const erased = (unset ?? []).filter((key) => PROFILE_PROPERTIES.includes(key));
99
+ if (Object.keys(shared).length === 0 && erased.length === 0)
100
+ return;
101
+ mutation.updateNode({
102
+ node: { id: profile.id },
103
+ class: domain.classes.UserProfile,
104
+ props: { set: shared, ...(erased.length === 0 ? {} : { unset: erased }) },
105
+ });
106
+ },
107
+ project: () => ({ ok: true }),
108
+ reject: refuse,
109
+ }));
@@ -0,0 +1,72 @@
1
+ import { defineMutation } from '@astrale-os/sdk/mutation';
2
+ /** What a group shares on its profile: the rest stays private on the group. */
3
+ export const GROUP_PROFILE_PROPERTIES = ['name', 'description'];
4
+ /** The profile values among group values. */
5
+ export function groupProfileValues(values) {
6
+ return Object.fromEntries(GROUP_PROFILE_PROPERTIES.flatMap((key) => values[key] === undefined ? [] : [[key, values[key]]]));
7
+ }
8
+ const refuse = (failure) => {
9
+ if (failure.outcomeKnown &&
10
+ failure.code === 'MUTATION_POST_STATE_INVALID' &&
11
+ failure.rule === 'duplicate-key') {
12
+ return { ok: false, refusal: 'DUPLICATE_KEY' };
13
+ }
14
+ throw failure;
15
+ };
16
+ /** Create one group. */
17
+ export const createGroup = defineMutation()((domain) => ({
18
+ id: 'shell.group.create',
19
+ build({ values }, mutation) {
20
+ const group = mutation.createNode({ as: 'group', class: domain.classes.Group, props: values });
21
+ const profile = mutation.createNode({
22
+ as: 'profile',
23
+ class: domain.classes.GroupProfile,
24
+ props: groupProfileValues(values),
25
+ });
26
+ mutation.createEdge({ class: domain.classes.group_profile, source: group, target: profile });
27
+ },
28
+ project: (result) => {
29
+ const group = result.createdNodes['group'];
30
+ if (group === undefined)
31
+ throw new TypeError('The Kernel created no group.');
32
+ return { ok: true, group };
33
+ },
34
+ reject: refuse,
35
+ }));
36
+ /** Set and erase Properties of one group. */
37
+ export const updateGroup = defineMutation()((domain) => ({
38
+ id: 'shell.group.update',
39
+ build({ group, set, unset, profile, }, mutation) {
40
+ mutation.updateNode({
41
+ node: { id: group },
42
+ class: domain.classes.Group,
43
+ props: { set, ...(unset === undefined || unset.length === 0 ? {} : { unset }) },
44
+ });
45
+ if (profile === undefined)
46
+ return;
47
+ if ('create' in profile) {
48
+ const created = mutation.createNode({
49
+ as: 'profile',
50
+ class: domain.classes.GroupProfile,
51
+ props: profile.create,
52
+ });
53
+ mutation.createEdge({
54
+ class: domain.classes.group_profile,
55
+ source: { id: group },
56
+ target: created,
57
+ });
58
+ return;
59
+ }
60
+ const shared = groupProfileValues(set);
61
+ const erased = (unset ?? []).filter((key) => GROUP_PROFILE_PROPERTIES.includes(key));
62
+ if (Object.keys(shared).length === 0 && erased.length === 0)
63
+ return;
64
+ mutation.updateNode({
65
+ node: { id: profile.id },
66
+ class: domain.classes.GroupProfile,
67
+ props: { set: shared, ...(erased.length === 0 ? {} : { unset: erased }) },
68
+ });
69
+ },
70
+ project: () => ({ ok: true }),
71
+ reject: refuse,
72
+ }));
@@ -0,0 +1 @@
1
+ export { bindAccount, createInvitation, updateInvitation, } from './write-invitation.js';
@@ -0,0 +1,62 @@
1
+ import { defineMutation } from '@astrale-os/sdk/mutation';
2
+ /** Record a pending invitation of one account, kept by one durable run. */
3
+ export const createInvitation = defineMutation()((domain) => ({
4
+ id: 'shell.invitation.create',
5
+ build(input, mutation) {
6
+ const invitation = mutation.createNode({
7
+ as: 'invitation',
8
+ class: domain.classes.Invitation,
9
+ props: {
10
+ status: 'pending',
11
+ expiresAt: input.expiresAt,
12
+ execution: input.execution,
13
+ round: 0,
14
+ },
15
+ });
16
+ mutation.createEdge({
17
+ class: domain.classes.invitation_user,
18
+ source: invitation,
19
+ target: { id: input.user },
20
+ });
21
+ },
22
+ project: (result) => {
23
+ const invitation = result.createdNodes['invitation'];
24
+ if (invitation === undefined)
25
+ throw new TypeError('The Kernel created no invitation.');
26
+ return { invitation };
27
+ },
28
+ }));
29
+ /** Change an invitation's status or its round, while it is still pending. */
30
+ export const updateInvitation = defineMutation()((domain) => ({
31
+ id: 'shell.invitation.update',
32
+ build(input, mutation) {
33
+ mutation.expect.node({
34
+ node: { id: input.invitation },
35
+ class: domain.classes.Invitation,
36
+ props: { equals: { status: 'pending' } },
37
+ });
38
+ mutation.updateNode({
39
+ node: { id: input.invitation },
40
+ class: domain.classes.Invitation,
41
+ props: {
42
+ set: {
43
+ ...(input.status === undefined ? {} : { status: input.status }),
44
+ ...(input.round === undefined ? {} : { round: input.round }),
45
+ },
46
+ },
47
+ });
48
+ },
49
+ project: () => ({ ok: true }),
50
+ }));
51
+ /** Bind an account to the sign-in that accepted its invitation (U8). */
52
+ export const bindAccount = defineMutation()((domain) => ({
53
+ id: 'shell.account.bind',
54
+ build(input, mutation) {
55
+ mutation.updateNode({
56
+ node: { id: input.user },
57
+ class: domain.classes.User,
58
+ props: { set: { iss: input.iss, sub: input.sub } },
59
+ });
60
+ },
61
+ project: () => ({ ok: true }),
62
+ }));
@@ -0,0 +1 @@
1
+ export { legacyRecordUserProfile } from './record-user-profile.js';
@@ -1,6 +1,9 @@
1
1
  import { defineMutation } from '@astrale-os/sdk/mutation';
2
- /** Set the email and display name an account recorded about itself. */
3
- export const recordUserProfile = defineMutation()((domain) => ({
2
+ /**
3
+ * @deprecated Set the email and display name an account recorded about itself (L-SHELL-5). Replaced
4
+ * by `User.updateProfile`. Removal: Shell major (delivery step 5) after 1Pact step 4.
5
+ */
6
+ export const legacyRecordUserProfile = defineMutation()((domain) => ({
4
7
  id: 'shell.user.profile.record',
5
8
  build({ user, set }, mutation) {
6
9
  const props = {
@@ -1,8 +1,9 @@
1
1
  import { TransportError } from '@astrale-os/sdk/client';
2
2
  import { defineProvider } from '@astrale-os/sdk/integration';
3
3
  import { z } from 'zod';
4
- import { enrollment, InvitationRequestFailure } from '../../integrations/user-invitation/index.js';
4
+ import { enrollment, InvitationRequestFailure, } from '../../integrations/user-invitation/index.js';
5
5
  import { decodeAdminInvitationResponse } from './decode-admin-invitation-response.js';
6
+ import { legacySendWithoutProfile } from './legacy/index.js';
6
7
  import { readReply } from './read-enrollment-response.js';
7
8
  // Environment is a representation boundary, unlike already-admitted handler input.
8
9
  const configuration = z.object({ ENROLLMENT_INGRESS_URL: z.string().url() });
@@ -33,7 +34,7 @@ export function enrollmentProvider(environment) {
33
34
  }
34
35
  // No automatic HTTP retry. A timeout after send has an unknown effect.
35
36
  const signal = AbortSignal.any([execution.signal, AbortSignal.timeout(remaining)]);
36
- const response = await fetch(audience, {
37
+ const send = (body) => fetch(audience, {
37
38
  method: 'POST',
38
39
  // Workers reject redirect:error before sending. Manual preserves the refusal:
39
40
  // every 3xx is rejected below, without forwarding the proof to its Location.
@@ -44,7 +45,7 @@ export function enrollmentProvider(environment) {
44
45
  Accept: 'application/json',
45
46
  'Idempotency-Key': input.requestId,
46
47
  },
47
- body: JSON.stringify(input),
48
+ body: JSON.stringify(body),
48
49
  signal,
49
50
  }).catch((cause) => {
50
51
  if (!(cause instanceof TypeError) && !(cause instanceof DOMException))
@@ -54,6 +55,8 @@ export function enrollmentProvider(environment) {
54
55
  delivery: 'unknown',
55
56
  });
56
57
  });
58
+ // Seam L-SHELL-15: an Admin older than `profile` refuses the field; send it again without.
59
+ const response = await legacySendWithoutProfile(input, send);
57
60
  if (!response.ok) {
58
61
  await response.body?.cancel();
59
62
  if (response.status === 409)
@@ -0,0 +1 @@
1
+ export { legacySendWithoutProfile } from './send-without-profile.js';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @deprecated Admin before profiles validates its invitation request strictly and refuses the
3
+ * `profile` field with 400: the request is sent once more without it, which Admin then admits
4
+ * (L-SHELL-15). A first 400 has no effect, so resending under the same idempotency key is safe.
5
+ * Replaced by one request carrying `profile`. Removal: Shell major (delivery step 5), once every
6
+ * Admin deployment admits `profile` (Admin delivery step 2).
7
+ */
8
+ export async function legacySendWithoutProfile(request, send) {
9
+ const response = await send(request);
10
+ if (request.profile === undefined || (response.status !== 400 && response.status !== 422)) {
11
+ return response;
12
+ }
13
+ await response.body?.cancel();
14
+ const { profile: _profile, ...legacy } = request;
15
+ return send(legacy);
16
+ }
@@ -0,0 +1 @@
1
+ export { legacyReadInstalledEntrypoints } from './read-installed-entrypoints.js';
@@ -0,0 +1,47 @@
1
+ import { defineQuery, Query, queryResult, } from '@astrale-os/sdk/query';
2
+ const VIEW_MEMBER = /^view\.([A-Za-z][A-Za-z0-9_]*)$/u;
3
+ /**
4
+ * @deprecated The catalogue `listEntrypoints` answers (L-SHELL-11). Replaced by the visitor's own
5
+ * Query (V2). Removal: Shell major (delivery step 5) after 1Pact step 4.
6
+ *
7
+ * Every installed Domain entrypoint this authority can read, in origin order. The `of_domain`
8
+ * locator of a View that has an outgoing `entrypoint` Edge names that View (`view.<name>`), and its
9
+ * target is the Domain, whose Node carries the origin and the display name.
10
+ */
11
+ export const legacyReadInstalledEntrypoints = defineQuery()((domain) => {
12
+ const kernel = domain.dependencies.kernel.classes;
13
+ return {
14
+ id: 'shell.application.entrypoints',
15
+ page: { size: 200 },
16
+ pagination: { maximumPages: 50 },
17
+ build: () => {
18
+ const located = Query.from({ edges: [kernel.of_domain] });
19
+ return located
20
+ .expand({ from: located.source, via: [kernel.entrypoint], direction: 'outgoing' })
21
+ .select({
22
+ kind: 'edges',
23
+ binding: located.edge,
24
+ projection: { edge: 'value', source: 'reference', target: 'value' },
25
+ });
26
+ },
27
+ project(pages) {
28
+ const entries = [];
29
+ for (const page of pages) {
30
+ for (const item of queryResult.edgeItems(page, 'Installed entrypoints')) {
31
+ if (item.edge.kind !== 'value' || item.target.kind !== 'value') {
32
+ throw new TypeError('Installed entrypoint is not a complete value.');
33
+ }
34
+ const member = VIEW_MEMBER.exec(String(item.edge.value.props[kernel.of_domain.properties.member.key]));
35
+ const props = item.target.value.props;
36
+ const origin = props[kernel.Domain.properties.origin.key];
37
+ const name = props[kernel.Domain.properties.name.key];
38
+ if (member === null || typeof origin !== 'string' || typeof name !== 'string') {
39
+ throw new TypeError('Installed entrypoint has no View, origin, or name.');
40
+ }
41
+ entries.push({ origin, name, view: `${origin}:view.${member[1]}` });
42
+ }
43
+ }
44
+ return entries.sort((left, right) => left.origin.localeCompare(right.origin));
45
+ },
46
+ };
47
+ });
@@ -0,0 +1,2 @@
1
+ export { readExtensions } from './read-extensions.js';
2
+ export { readRoleNodes } from './read-role-nodes.js';
@@ -0,0 +1,34 @@
1
+ import { Path } from '@astrale-os/sdk/graph/path';
2
+ import { defineQuery, Query, queryResult, } from '@astrale-os/sdk/query';
3
+ /** The `extends_with` Edges at one frontier of a composition walk, read with Shell's authority. */
4
+ export const readExtensions = defineQuery()((domain) => ({
5
+ id: 'shell.composition.extensions',
6
+ page: { size: 256 },
7
+ pagination: { maximumPages: 16 },
8
+ build({ identities, direction }) {
9
+ const walk = Query.from({
10
+ nodes: [Path.id(identities[0]), ...identities.slice(1).map((id) => Path.id(id))],
11
+ }).expand({ via: [domain.dependencies.kernel.classes.extends_with], direction });
12
+ return walk.select({
13
+ kind: 'edges',
14
+ binding: walk.edge,
15
+ projection: { edge: 'reference', source: 'reference', target: 'reference' },
16
+ });
17
+ },
18
+ project(pages) {
19
+ const extensions = [];
20
+ for (const page of pages) {
21
+ for (const item of queryResult.edgeItems(page, 'Shell composition')) {
22
+ if (item.source.kind !== 'reference' || item.target.kind !== 'reference')
23
+ continue;
24
+ extensions.push({
25
+ source: item.source.value.id,
26
+ target: item.target.value.id,
27
+ sourceClass: item.source.value.class,
28
+ targetClass: item.target.value.class,
29
+ });
30
+ }
31
+ }
32
+ return extensions;
33
+ },
34
+ }));