@lightdash/common 0.2572.2 → 0.2573.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/authorization/space/spaceAccessResolver.d.ts +2 -3
  3. package/dist/cjs/authorization/space/spaceAccessResolver.d.ts.map +1 -1
  4. package/dist/cjs/authorization/space/spaceAccessResolver.js +14 -78
  5. package/dist/cjs/authorization/space/spaceAccessResolver.js.map +1 -1
  6. package/dist/cjs/authorization/space/spaceAccessResolver.test.js +18 -529
  7. package/dist/cjs/authorization/space/spaceAccessResolver.test.js.map +1 -1
  8. package/dist/cjs/compiler/exploreCompiler.d.ts.map +1 -1
  9. package/dist/cjs/compiler/exploreCompiler.js +5 -3
  10. package/dist/cjs/compiler/exploreCompiler.js.map +1 -1
  11. package/dist/cjs/compiler/filtersCompiler.d.ts.map +1 -1
  12. package/dist/cjs/compiler/filtersCompiler.js +1 -0
  13. package/dist/cjs/compiler/filtersCompiler.js.map +1 -1
  14. package/dist/cjs/dbt/schemas/lightdashMetadata.json +1 -0
  15. package/dist/cjs/ee/AiAgent/schemas/customMetrics.d.ts +148 -148
  16. package/dist/cjs/ee/AiAgent/schemas/filters/index.d.ts +180 -180
  17. package/dist/cjs/ee/AiAgent/schemas/filters/numberFilters.d.ts +12 -12
  18. package/dist/cjs/ee/AiAgent/schemas/filters/numberFilters.d.ts.map +1 -1
  19. package/dist/cjs/ee/AiAgent/schemas/filters/numberFilters.js +1 -0
  20. package/dist/cjs/ee/AiAgent/schemas/filters/numberFilters.js.map +1 -1
  21. package/dist/cjs/ee/AiAgent/schemas/tools/toolDashboardArgs.d.ts +1608 -1608
  22. package/dist/cjs/ee/AiAgent/schemas/tools/toolDashboardV2Args.d.ts +440 -440
  23. package/dist/cjs/ee/AiAgent/schemas/tools/toolProposeChangeArgs.d.ts +60 -60
  24. package/dist/cjs/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.d.ts +256 -256
  25. package/dist/cjs/ee/AiAgent/schemas/tools/toolRunQueryArgs.d.ts +256 -256
  26. package/dist/cjs/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts +180 -180
  27. package/dist/cjs/ee/AiAgent/schemas/tools/toolTableVizArgs.d.ts +320 -320
  28. package/dist/cjs/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.d.ts +320 -320
  29. package/dist/cjs/ee/AiAgent/schemas/tools/toolVerticalBarArgs.d.ts +320 -320
  30. package/dist/cjs/preAggregates/additivity.d.ts.map +1 -1
  31. package/dist/cjs/preAggregates/additivity.js +1 -0
  32. package/dist/cjs/preAggregates/additivity.js.map +1 -1
  33. package/dist/cjs/preAggregates/metricRepresentation.d.ts.map +1 -1
  34. package/dist/cjs/preAggregates/metricRepresentation.js +1 -0
  35. package/dist/cjs/preAggregates/metricRepresentation.js.map +1 -1
  36. package/dist/cjs/schemas/json/chart-as-code-1.0.json +443 -114
  37. package/dist/cjs/types/catalog.d.ts.map +1 -1
  38. package/dist/cjs/types/catalog.js +1 -0
  39. package/dist/cjs/types/catalog.js.map +1 -1
  40. package/dist/cjs/types/featureFlags.d.ts +0 -6
  41. package/dist/cjs/types/featureFlags.d.ts.map +1 -1
  42. package/dist/cjs/types/featureFlags.js +0 -6
  43. package/dist/cjs/types/featureFlags.js.map +1 -1
  44. package/dist/cjs/types/field.d.ts +1 -0
  45. package/dist/cjs/types/field.d.ts.map +1 -1
  46. package/dist/cjs/types/field.js +3 -0
  47. package/dist/cjs/types/field.js.map +1 -1
  48. package/dist/cjs/types/results.d.ts.map +1 -1
  49. package/dist/cjs/types/results.js +1 -0
  50. package/dist/cjs/types/results.js.map +1 -1
  51. package/dist/cjs/types/space.d.ts +0 -7
  52. package/dist/cjs/types/space.d.ts.map +1 -1
  53. package/dist/cjs/types/space.js.map +1 -1
  54. package/dist/cjs/utils/filters.d.ts.map +1 -1
  55. package/dist/cjs/utils/filters.js +1 -0
  56. package/dist/cjs/utils/filters.js.map +1 -1
  57. package/dist/cjs/utils/item.d.ts.map +1 -1
  58. package/dist/cjs/utils/item.js +1 -0
  59. package/dist/cjs/utils/item.js.map +1 -1
  60. package/dist/esm/.tsbuildinfo +1 -1
  61. package/dist/esm/authorization/space/spaceAccessResolver.d.ts +2 -3
  62. package/dist/esm/authorization/space/spaceAccessResolver.d.ts.map +1 -1
  63. package/dist/esm/authorization/space/spaceAccessResolver.js +6 -69
  64. package/dist/esm/authorization/space/spaceAccessResolver.js.map +1 -1
  65. package/dist/esm/authorization/space/spaceAccessResolver.test.js +19 -530
  66. package/dist/esm/authorization/space/spaceAccessResolver.test.js.map +1 -1
  67. package/dist/esm/compiler/exploreCompiler.d.ts.map +1 -1
  68. package/dist/esm/compiler/exploreCompiler.js +5 -3
  69. package/dist/esm/compiler/exploreCompiler.js.map +1 -1
  70. package/dist/esm/compiler/filtersCompiler.d.ts.map +1 -1
  71. package/dist/esm/compiler/filtersCompiler.js +1 -0
  72. package/dist/esm/compiler/filtersCompiler.js.map +1 -1
  73. package/dist/esm/dbt/schemas/lightdashMetadata.json +1 -0
  74. package/dist/esm/ee/AiAgent/schemas/customMetrics.d.ts +148 -148
  75. package/dist/esm/ee/AiAgent/schemas/filters/index.d.ts +180 -180
  76. package/dist/esm/ee/AiAgent/schemas/filters/numberFilters.d.ts +12 -12
  77. package/dist/esm/ee/AiAgent/schemas/filters/numberFilters.d.ts.map +1 -1
  78. package/dist/esm/ee/AiAgent/schemas/filters/numberFilters.js +1 -0
  79. package/dist/esm/ee/AiAgent/schemas/filters/numberFilters.js.map +1 -1
  80. package/dist/esm/ee/AiAgent/schemas/tools/toolDashboardArgs.d.ts +1608 -1608
  81. package/dist/esm/ee/AiAgent/schemas/tools/toolDashboardV2Args.d.ts +440 -440
  82. package/dist/esm/ee/AiAgent/schemas/tools/toolProposeChangeArgs.d.ts +60 -60
  83. package/dist/esm/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.d.ts +256 -256
  84. package/dist/esm/ee/AiAgent/schemas/tools/toolRunQueryArgs.d.ts +256 -256
  85. package/dist/esm/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts +180 -180
  86. package/dist/esm/ee/AiAgent/schemas/tools/toolTableVizArgs.d.ts +320 -320
  87. package/dist/esm/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.d.ts +320 -320
  88. package/dist/esm/ee/AiAgent/schemas/tools/toolVerticalBarArgs.d.ts +320 -320
  89. package/dist/esm/preAggregates/additivity.d.ts.map +1 -1
  90. package/dist/esm/preAggregates/additivity.js +1 -0
  91. package/dist/esm/preAggregates/additivity.js.map +1 -1
  92. package/dist/esm/preAggregates/metricRepresentation.d.ts.map +1 -1
  93. package/dist/esm/preAggregates/metricRepresentation.js +1 -0
  94. package/dist/esm/preAggregates/metricRepresentation.js.map +1 -1
  95. package/dist/esm/schemas/json/chart-as-code-1.0.json +443 -114
  96. package/dist/esm/types/catalog.d.ts.map +1 -1
  97. package/dist/esm/types/catalog.js +1 -0
  98. package/dist/esm/types/catalog.js.map +1 -1
  99. package/dist/esm/types/featureFlags.d.ts +0 -6
  100. package/dist/esm/types/featureFlags.d.ts.map +1 -1
  101. package/dist/esm/types/featureFlags.js +0 -6
  102. package/dist/esm/types/featureFlags.js.map +1 -1
  103. package/dist/esm/types/field.d.ts +1 -0
  104. package/dist/esm/types/field.d.ts.map +1 -1
  105. package/dist/esm/types/field.js +3 -0
  106. package/dist/esm/types/field.js.map +1 -1
  107. package/dist/esm/types/results.d.ts.map +1 -1
  108. package/dist/esm/types/results.js +1 -0
  109. package/dist/esm/types/results.js.map +1 -1
  110. package/dist/esm/types/space.d.ts +0 -7
  111. package/dist/esm/types/space.d.ts.map +1 -1
  112. package/dist/esm/types/space.js.map +1 -1
  113. package/dist/esm/utils/filters.d.ts.map +1 -1
  114. package/dist/esm/utils/filters.js +1 -0
  115. package/dist/esm/utils/filters.js.map +1 -1
  116. package/dist/esm/utils/item.d.ts.map +1 -1
  117. package/dist/esm/utils/item.js +1 -0
  118. package/dist/esm/utils/item.js.map +1 -1
  119. package/dist/types/.tsbuildinfo +1 -1
  120. package/dist/types/authorization/space/spaceAccessResolver.d.ts +2 -3
  121. package/dist/types/authorization/space/spaceAccessResolver.d.ts.map +1 -1
  122. package/dist/types/authorization/space/spaceAccessResolver.js +6 -69
  123. package/dist/types/authorization/space/spaceAccessResolver.js.map +1 -1
  124. package/dist/types/authorization/space/spaceAccessResolver.test.js +19 -530
  125. package/dist/types/authorization/space/spaceAccessResolver.test.js.map +1 -1
  126. package/dist/types/compiler/exploreCompiler.d.ts.map +1 -1
  127. package/dist/types/compiler/exploreCompiler.js +5 -3
  128. package/dist/types/compiler/exploreCompiler.js.map +1 -1
  129. package/dist/types/compiler/filtersCompiler.d.ts.map +1 -1
  130. package/dist/types/compiler/filtersCompiler.js +1 -0
  131. package/dist/types/compiler/filtersCompiler.js.map +1 -1
  132. package/dist/types/dbt/schemas/lightdashMetadata.json +1 -0
  133. package/dist/types/ee/AiAgent/schemas/customMetrics.d.ts +148 -148
  134. package/dist/types/ee/AiAgent/schemas/filters/index.d.ts +180 -180
  135. package/dist/types/ee/AiAgent/schemas/filters/numberFilters.d.ts +12 -12
  136. package/dist/types/ee/AiAgent/schemas/filters/numberFilters.d.ts.map +1 -1
  137. package/dist/types/ee/AiAgent/schemas/filters/numberFilters.js +1 -0
  138. package/dist/types/ee/AiAgent/schemas/filters/numberFilters.js.map +1 -1
  139. package/dist/types/ee/AiAgent/schemas/tools/toolDashboardArgs.d.ts +1608 -1608
  140. package/dist/types/ee/AiAgent/schemas/tools/toolDashboardV2Args.d.ts +440 -440
  141. package/dist/types/ee/AiAgent/schemas/tools/toolProposeChangeArgs.d.ts +60 -60
  142. package/dist/types/ee/AiAgent/schemas/tools/toolRunMetricQueryArgs.d.ts +256 -256
  143. package/dist/types/ee/AiAgent/schemas/tools/toolRunQueryArgs.d.ts +256 -256
  144. package/dist/types/ee/AiAgent/schemas/tools/toolSearchFieldValuesArgs.d.ts +180 -180
  145. package/dist/types/ee/AiAgent/schemas/tools/toolTableVizArgs.d.ts +320 -320
  146. package/dist/types/ee/AiAgent/schemas/tools/toolTimeSeriesArgs.d.ts +320 -320
  147. package/dist/types/ee/AiAgent/schemas/tools/toolVerticalBarArgs.d.ts +320 -320
  148. package/dist/types/preAggregates/additivity.d.ts.map +1 -1
  149. package/dist/types/preAggregates/additivity.js +1 -0
  150. package/dist/types/preAggregates/additivity.js.map +1 -1
  151. package/dist/types/preAggregates/metricRepresentation.d.ts.map +1 -1
  152. package/dist/types/preAggregates/metricRepresentation.js +1 -0
  153. package/dist/types/preAggregates/metricRepresentation.js.map +1 -1
  154. package/dist/types/schemas/json/chart-as-code-1.0.json +443 -114
  155. package/dist/types/types/catalog.d.ts.map +1 -1
  156. package/dist/types/types/catalog.js +1 -0
  157. package/dist/types/types/catalog.js.map +1 -1
  158. package/dist/types/types/featureFlags.d.ts +0 -6
  159. package/dist/types/types/featureFlags.d.ts.map +1 -1
  160. package/dist/types/types/featureFlags.js +0 -6
  161. package/dist/types/types/featureFlags.js.map +1 -1
  162. package/dist/types/types/field.d.ts +1 -0
  163. package/dist/types/types/field.d.ts.map +1 -1
  164. package/dist/types/types/field.js +3 -0
  165. package/dist/types/types/field.js.map +1 -1
  166. package/dist/types/types/results.d.ts.map +1 -1
  167. package/dist/types/types/results.js +1 -0
  168. package/dist/types/types/results.js.map +1 -1
  169. package/dist/types/types/space.d.ts +0 -7
  170. package/dist/types/types/space.d.ts.map +1 -1
  171. package/dist/types/types/space.js.map +1 -1
  172. package/dist/types/utils/filters.d.ts.map +1 -1
  173. package/dist/types/utils/filters.js +1 -0
  174. package/dist/types/utils/filters.js.map +1 -1
  175. package/dist/types/utils/item.d.ts.map +1 -1
  176. package/dist/types/utils/item.js +1 -0
  177. package/dist/types/utils/item.js.map +1 -1
  178. package/package.json +1 -1
@@ -1,519 +1,8 @@
1
1
  import { OrganizationMemberRole } from '../../types/organizationMemberProfile';
2
2
  import { ProjectMemberRole } from '../../types/projectMemberRole';
3
3
  import { DirectSpaceAccessOrigin, ProjectSpaceAccessOrigin, SpaceMemberRole, } from '../../types/space';
4
- import { resolveSpaceAccess, resolveSpaceAccessWithInheritance, } from './spaceAccessResolver';
5
- const makeInput = (overrides = {}) => ({
6
- spaceUuid: 'space-1',
7
- inheritsFromOrgOrProject: true,
8
- directAccess: [],
9
- projectAccess: [],
10
- organizationAccess: [],
11
- ...overrides,
12
- });
4
+ import { resolveSpaceAccess } from './spaceAccessResolver';
13
5
  describe('resolveSpaceAccess', () => {
14
- it('returns empty array for empty inputs', () => {
15
- const result = resolveSpaceAccess(makeInput());
16
- expect(result).toEqual([]);
17
- });
18
- describe('admin override', () => {
19
- it('org admin gets space ADMIN', () => {
20
- const result = resolveSpaceAccess(makeInput({
21
- organizationAccess: [
22
- {
23
- userUuid: 'user-1',
24
- spaceUuid: 'space-1',
25
- role: OrganizationMemberRole.ADMIN,
26
- },
27
- ],
28
- }));
29
- expect(result).toHaveLength(1);
30
- expect(result[0].role).toBe(SpaceMemberRole.ADMIN);
31
- expect(result[0].inheritedFrom).toBe('organization');
32
- });
33
- it('project admin gets space ADMIN', () => {
34
- const result = resolveSpaceAccess(makeInput({
35
- organizationAccess: [
36
- {
37
- userUuid: 'user-1',
38
- spaceUuid: 'space-1',
39
- role: OrganizationMemberRole.VIEWER,
40
- },
41
- ],
42
- projectAccess: [
43
- {
44
- userUuid: 'user-1',
45
- spaceUuid: 'space-1',
46
- role: ProjectMemberRole.ADMIN,
47
- from: ProjectSpaceAccessOrigin.PROJECT_MEMBERSHIP,
48
- },
49
- ],
50
- }));
51
- expect(result).toHaveLength(1);
52
- expect(result[0].role).toBe(SpaceMemberRole.ADMIN);
53
- });
54
- it('group admin (project group) gets space ADMIN', () => {
55
- const result = resolveSpaceAccess(makeInput({
56
- organizationAccess: [
57
- {
58
- userUuid: 'user-1',
59
- spaceUuid: 'space-1',
60
- role: OrganizationMemberRole.VIEWER,
61
- },
62
- ],
63
- projectAccess: [
64
- {
65
- userUuid: 'user-1',
66
- spaceUuid: 'space-1',
67
- role: ProjectMemberRole.ADMIN,
68
- from: ProjectSpaceAccessOrigin.GROUP_MEMBERSHIP,
69
- },
70
- ],
71
- }));
72
- expect(result).toHaveLength(1);
73
- expect(result[0].role).toBe(SpaceMemberRole.ADMIN);
74
- });
75
- it('admin can access private space even without direct access', () => {
76
- const result = resolveSpaceAccess(makeInput({
77
- inheritsFromOrgOrProject: false,
78
- organizationAccess: [
79
- {
80
- userUuid: 'user-1',
81
- spaceUuid: 'space-1',
82
- role: OrganizationMemberRole.ADMIN,
83
- },
84
- ],
85
- }));
86
- expect(result).toHaveLength(1);
87
- expect(result[0].role).toBe(SpaceMemberRole.ADMIN);
88
- });
89
- });
90
- describe('direct access', () => {
91
- it('USER_ACCESS role used directly', () => {
92
- const result = resolveSpaceAccess(makeInput({
93
- organizationAccess: [
94
- {
95
- userUuid: 'user-1',
96
- spaceUuid: 'space-1',
97
- role: OrganizationMemberRole.VIEWER,
98
- },
99
- ],
100
- directAccess: [
101
- {
102
- userUuid: 'user-1',
103
- spaceUuid: 'space-1',
104
- role: SpaceMemberRole.EDITOR,
105
- groupUuid: null,
106
- from: DirectSpaceAccessOrigin.USER_ACCESS,
107
- },
108
- ],
109
- }));
110
- expect(result).toHaveLength(1);
111
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
112
- expect(result[0].hasDirectAccess).toBe(true);
113
- });
114
- it('GROUP_ACCESS role used when no user role', () => {
115
- const result = resolveSpaceAccess(makeInput({
116
- organizationAccess: [
117
- {
118
- userUuid: 'user-1',
119
- spaceUuid: 'space-1',
120
- role: OrganizationMemberRole.VIEWER,
121
- },
122
- ],
123
- directAccess: [
124
- {
125
- userUuid: 'user-1',
126
- spaceUuid: 'space-1',
127
- role: SpaceMemberRole.EDITOR,
128
- groupUuid: 'group-1',
129
- from: DirectSpaceAccessOrigin.GROUP_ACCESS,
130
- },
131
- ],
132
- }));
133
- expect(result).toHaveLength(1);
134
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
135
- expect(result[0].hasDirectAccess).toBe(true);
136
- });
137
- it('user + group direct access: user role wins when higher', () => {
138
- const result = resolveSpaceAccess(makeInput({
139
- organizationAccess: [
140
- {
141
- userUuid: 'user-1',
142
- spaceUuid: 'space-1',
143
- role: OrganizationMemberRole.VIEWER,
144
- },
145
- ],
146
- directAccess: [
147
- {
148
- userUuid: 'user-1',
149
- spaceUuid: 'space-1',
150
- role: SpaceMemberRole.EDITOR,
151
- groupUuid: null,
152
- from: DirectSpaceAccessOrigin.USER_ACCESS,
153
- },
154
- {
155
- userUuid: 'user-1',
156
- spaceUuid: 'space-1',
157
- role: SpaceMemberRole.VIEWER,
158
- groupUuid: 'group-1',
159
- from: DirectSpaceAccessOrigin.GROUP_ACCESS,
160
- },
161
- ],
162
- }));
163
- expect(result).toHaveLength(1);
164
- // User access takes precedence over group access
165
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
166
- });
167
- });
168
- describe('public space inheritance', () => {
169
- it('viewer gets viewer space role', () => {
170
- const result = resolveSpaceAccess(makeInput({
171
- inheritsFromOrgOrProject: true,
172
- organizationAccess: [
173
- {
174
- userUuid: 'user-1',
175
- spaceUuid: 'space-1',
176
- role: OrganizationMemberRole.VIEWER,
177
- },
178
- ],
179
- }));
180
- expect(result).toHaveLength(1);
181
- expect(result[0].role).toBe(SpaceMemberRole.VIEWER);
182
- expect(result[0].hasDirectAccess).toBe(false);
183
- expect(result[0].inheritedFrom).toBe('organization');
184
- });
185
- it('editor gets editor space role', () => {
186
- const result = resolveSpaceAccess(makeInput({
187
- inheritsFromOrgOrProject: true,
188
- organizationAccess: [
189
- {
190
- userUuid: 'user-1',
191
- spaceUuid: 'space-1',
192
- role: OrganizationMemberRole.EDITOR,
193
- },
194
- ],
195
- }));
196
- expect(result).toHaveLength(1);
197
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
198
- });
199
- it('developer gets editor space role', () => {
200
- const result = resolveSpaceAccess(makeInput({
201
- inheritsFromOrgOrProject: true,
202
- organizationAccess: [
203
- {
204
- userUuid: 'user-1',
205
- spaceUuid: 'space-1',
206
- role: OrganizationMemberRole.DEVELOPER,
207
- },
208
- ],
209
- }));
210
- expect(result).toHaveLength(1);
211
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
212
- });
213
- it('interactive_viewer gets viewer space role', () => {
214
- const result = resolveSpaceAccess(makeInput({
215
- inheritsFromOrgOrProject: true,
216
- organizationAccess: [
217
- {
218
- userUuid: 'user-1',
219
- spaceUuid: 'space-1',
220
- role: OrganizationMemberRole.INTERACTIVE_VIEWER,
221
- },
222
- ],
223
- }));
224
- expect(result).toHaveLength(1);
225
- expect(result[0].role).toBe(SpaceMemberRole.VIEWER);
226
- });
227
- });
228
- describe('private space exclusion', () => {
229
- it('non-admin without direct access excluded from private space', () => {
230
- const result = resolveSpaceAccess(makeInput({
231
- inheritsFromOrgOrProject: false,
232
- organizationAccess: [
233
- {
234
- userUuid: 'user-1',
235
- spaceUuid: 'space-1',
236
- role: OrganizationMemberRole.EDITOR,
237
- },
238
- ],
239
- }));
240
- expect(result).toHaveLength(0);
241
- });
242
- it('private space with direct access works', () => {
243
- const result = resolveSpaceAccess(makeInput({
244
- inheritsFromOrgOrProject: false,
245
- organizationAccess: [
246
- {
247
- userUuid: 'user-1',
248
- spaceUuid: 'space-1',
249
- role: OrganizationMemberRole.VIEWER,
250
- },
251
- ],
252
- directAccess: [
253
- {
254
- userUuid: 'user-1',
255
- spaceUuid: 'space-1',
256
- role: SpaceMemberRole.EDITOR,
257
- groupUuid: null,
258
- from: DirectSpaceAccessOrigin.USER_ACCESS,
259
- },
260
- ],
261
- }));
262
- expect(result).toHaveLength(1);
263
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
264
- });
265
- });
266
- describe('org MEMBER role', () => {
267
- it('org MEMBER with no other access is excluded', () => {
268
- const result = resolveSpaceAccess(makeInput({
269
- inheritsFromOrgOrProject: true,
270
- organizationAccess: [
271
- {
272
- userUuid: 'user-1',
273
- spaceUuid: 'space-1',
274
- role: OrganizationMemberRole.MEMBER,
275
- },
276
- ],
277
- }));
278
- // MEMBER converts to undefined project role, so no highest role → excluded
279
- expect(result).toHaveLength(0);
280
- });
281
- it('org MEMBER with project access is included', () => {
282
- const result = resolveSpaceAccess(makeInput({
283
- inheritsFromOrgOrProject: true,
284
- organizationAccess: [
285
- {
286
- userUuid: 'user-1',
287
- spaceUuid: 'space-1',
288
- role: OrganizationMemberRole.VIEWER,
289
- },
290
- ],
291
- projectAccess: [
292
- {
293
- userUuid: 'user-1',
294
- spaceUuid: 'space-1',
295
- role: ProjectMemberRole.EDITOR,
296
- from: ProjectSpaceAccessOrigin.PROJECT_MEMBERSHIP,
297
- },
298
- ],
299
- }));
300
- expect(result).toHaveLength(1);
301
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
302
- });
303
- });
304
- describe('multiple group roles', () => {
305
- it('highest group role wins', () => {
306
- const result = resolveSpaceAccess(makeInput({
307
- inheritsFromOrgOrProject: true,
308
- organizationAccess: [
309
- {
310
- userUuid: 'user-1',
311
- spaceUuid: 'space-1',
312
- role: OrganizationMemberRole.VIEWER,
313
- },
314
- ],
315
- projectAccess: [
316
- {
317
- userUuid: 'user-1',
318
- spaceUuid: 'space-1',
319
- role: ProjectMemberRole.VIEWER,
320
- from: ProjectSpaceAccessOrigin.GROUP_MEMBERSHIP,
321
- },
322
- {
323
- userUuid: 'user-1',
324
- spaceUuid: 'space-1',
325
- role: ProjectMemberRole.EDITOR,
326
- from: ProjectSpaceAccessOrigin.GROUP_MEMBERSHIP,
327
- },
328
- ],
329
- }));
330
- expect(result).toHaveLength(1);
331
- expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
332
- });
333
- });
334
- describe('inheritedFrom metadata', () => {
335
- it('reports organization when org role is highest', () => {
336
- const result = resolveSpaceAccess(makeInput({
337
- organizationAccess: [
338
- {
339
- userUuid: 'user-1',
340
- spaceUuid: 'space-1',
341
- role: OrganizationMemberRole.EDITOR,
342
- },
343
- ],
344
- }));
345
- expect(result[0].inheritedFrom).toBe('organization');
346
- });
347
- it('reports project when project role is highest', () => {
348
- const result = resolveSpaceAccess(makeInput({
349
- organizationAccess: [
350
- {
351
- userUuid: 'user-1',
352
- spaceUuid: 'space-1',
353
- role: OrganizationMemberRole.VIEWER,
354
- },
355
- ],
356
- projectAccess: [
357
- {
358
- userUuid: 'user-1',
359
- spaceUuid: 'space-1',
360
- role: ProjectMemberRole.EDITOR,
361
- from: ProjectSpaceAccessOrigin.PROJECT_MEMBERSHIP,
362
- },
363
- ],
364
- }));
365
- expect(result[0].inheritedFrom).toBe('project');
366
- });
367
- it('reports group when group project role is highest', () => {
368
- const result = resolveSpaceAccess(makeInput({
369
- organizationAccess: [
370
- {
371
- userUuid: 'user-1',
372
- spaceUuid: 'space-1',
373
- role: OrganizationMemberRole.VIEWER,
374
- },
375
- ],
376
- projectAccess: [
377
- {
378
- userUuid: 'user-1',
379
- spaceUuid: 'space-1',
380
- role: ProjectMemberRole.DEVELOPER,
381
- from: ProjectSpaceAccessOrigin.GROUP_MEMBERSHIP,
382
- },
383
- ],
384
- }));
385
- expect(result[0].inheritedFrom).toBe('group');
386
- });
387
- it('reports space_group when space group access role is highest', () => {
388
- const result = resolveSpaceAccess(makeInput({
389
- organizationAccess: [
390
- {
391
- userUuid: 'user-1',
392
- spaceUuid: 'space-1',
393
- role: OrganizationMemberRole.VIEWER,
394
- },
395
- ],
396
- directAccess: [
397
- {
398
- userUuid: 'user-1',
399
- spaceUuid: 'space-1',
400
- role: SpaceMemberRole.EDITOR,
401
- groupUuid: 'group-1',
402
- from: DirectSpaceAccessOrigin.GROUP_ACCESS,
403
- },
404
- ],
405
- }));
406
- expect(result[0].inheritedFrom).toBe('space_group');
407
- });
408
- it('reports org access when org access is highest', () => {
409
- const result = resolveSpaceAccess(makeInput({
410
- organizationAccess: [
411
- {
412
- userUuid: 'user-1',
413
- spaceUuid: 'space-1',
414
- role: OrganizationMemberRole.DEVELOPER,
415
- },
416
- ],
417
- directAccess: [
418
- {
419
- userUuid: 'user-1',
420
- spaceUuid: 'space-1',
421
- role: SpaceMemberRole.VIEWER,
422
- groupUuid: null,
423
- from: DirectSpaceAccessOrigin.USER_ACCESS,
424
- },
425
- ],
426
- }));
427
- expect(result[0].inheritedFrom).toBe('organization');
428
- // Direct user access takes precedence over inherited org role
429
- // for the space role, even if the org role is higher.
430
- // The org source is reflected in inheritedFrom/inheritedRole.
431
- expect(result[0].role).toBe(SpaceMemberRole.VIEWER);
432
- });
433
- });
434
- describe('projectRole field', () => {
435
- it('only considers org + direct project membership (not groups)', () => {
436
- const result = resolveSpaceAccess(makeInput({
437
- organizationAccess: [
438
- {
439
- userUuid: 'user-1',
440
- spaceUuid: 'space-1',
441
- role: OrganizationMemberRole.VIEWER,
442
- },
443
- ],
444
- projectAccess: [
445
- {
446
- userUuid: 'user-1',
447
- spaceUuid: 'space-1',
448
- role: ProjectMemberRole.DEVELOPER,
449
- from: ProjectSpaceAccessOrigin.GROUP_MEMBERSHIP,
450
- },
451
- ],
452
- }));
453
- // projectRole should be VIEWER (from org), not DEVELOPER (from group)
454
- expect(result[0].projectRole).toBe(ProjectMemberRole.VIEWER);
455
- });
456
- it('includes direct project membership in projectRole', () => {
457
- const result = resolveSpaceAccess(makeInput({
458
- organizationAccess: [
459
- {
460
- userUuid: 'user-1',
461
- spaceUuid: 'space-1',
462
- role: OrganizationMemberRole.VIEWER,
463
- },
464
- ],
465
- projectAccess: [
466
- {
467
- userUuid: 'user-1',
468
- spaceUuid: 'space-1',
469
- role: ProjectMemberRole.EDITOR,
470
- from: ProjectSpaceAccessOrigin.PROJECT_MEMBERSHIP,
471
- },
472
- ],
473
- }));
474
- expect(result[0].projectRole).toBe(ProjectMemberRole.EDITOR);
475
- });
476
- });
477
- describe('multiple users', () => {
478
- it('resolves access for multiple users independently', () => {
479
- const orgAccess = [
480
- {
481
- userUuid: 'user-1',
482
- spaceUuid: 'space-1',
483
- role: OrganizationMemberRole.EDITOR,
484
- },
485
- {
486
- userUuid: 'user-2',
487
- spaceUuid: 'space-1',
488
- role: OrganizationMemberRole.VIEWER,
489
- },
490
- ];
491
- const directAccess = [
492
- {
493
- userUuid: 'user-2',
494
- spaceUuid: 'space-1',
495
- role: SpaceMemberRole.ADMIN,
496
- groupUuid: null,
497
- from: DirectSpaceAccessOrigin.USER_ACCESS,
498
- },
499
- ];
500
- const projectAccess = [];
501
- const result = resolveSpaceAccess(makeInput({
502
- organizationAccess: orgAccess,
503
- directAccess,
504
- projectAccess,
505
- }));
506
- expect(result).toHaveLength(2);
507
- const user1 = result.find((r) => r.userUuid === 'user-1');
508
- const user2 = result.find((r) => r.userUuid === 'user-2');
509
- expect(user1?.role).toBe(SpaceMemberRole.EDITOR);
510
- expect(user1?.hasDirectAccess).toBe(false);
511
- expect(user2?.role).toBe(SpaceMemberRole.ADMIN);
512
- expect(user2?.hasDirectAccess).toBe(true);
513
- });
514
- });
515
- });
516
- describe('resolveSpaceAccessWithInheritance', () => {
517
6
  const makeChainInput = (overrides = {}) => ({
518
7
  spaceUuid: 'child-space',
519
8
  inheritsFromOrgOrProject: true,
@@ -523,12 +12,12 @@ describe('resolveSpaceAccessWithInheritance', () => {
523
12
  ...overrides,
524
13
  });
525
14
  it('returns empty array for empty inputs', () => {
526
- const result = resolveSpaceAccessWithInheritance(makeChainInput());
15
+ const result = resolveSpaceAccess(makeChainInput());
527
16
  expect(result).toEqual([]);
528
17
  });
529
18
  describe('single-space chain (backward compat)', () => {
530
19
  it('org admin gets space ADMIN', () => {
531
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
20
+ const result = resolveSpaceAccess(makeChainInput({
532
21
  chainDirectAccess: [
533
22
  { spaceUuid: 'child-space', directAccess: [] },
534
23
  ],
@@ -544,7 +33,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
544
33
  expect(result[0].role).toBe(SpaceMemberRole.ADMIN);
545
34
  });
546
35
  it('direct user access on single space resolves correctly', () => {
547
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
36
+ const result = resolveSpaceAccess(makeChainInput({
548
37
  chainDirectAccess: [
549
38
  {
550
39
  spaceUuid: 'child-space',
@@ -574,7 +63,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
574
63
  });
575
64
  describe('most permissive wins across chain', () => {
576
65
  it('USER_ACCESS EDITOR on parent beats USER_ACCESS VIEWER on child', () => {
577
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
66
+ const result = resolveSpaceAccess(makeChainInput({
578
67
  chainDirectAccess: [
579
68
  {
580
69
  spaceUuid: 'child-space',
@@ -614,7 +103,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
614
103
  expect(result[0].hasDirectAccess).toBe(true);
615
104
  });
616
105
  it('GROUP_ACCESS EDITOR on parent beats USER_ACCESS VIEWER on child', () => {
617
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
106
+ const result = resolveSpaceAccess(makeChainInput({
618
107
  chainDirectAccess: [
619
108
  {
620
109
  spaceUuid: 'child-space',
@@ -653,7 +142,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
653
142
  expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
654
143
  });
655
144
  it('USER_ACCESS EDITOR on child already wins (child is higher)', () => {
656
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
145
+ const result = resolveSpaceAccess(makeChainInput({
657
146
  chainDirectAccess: [
658
147
  {
659
148
  spaceUuid: 'child-space',
@@ -692,7 +181,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
692
181
  expect(result[0].role).toBe(SpaceMemberRole.EDITOR);
693
182
  });
694
183
  it('three-level chain: grandparent ADMIN wins over child VIEWER', () => {
695
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
184
+ const result = resolveSpaceAccess(makeChainInput({
696
185
  chainDirectAccess: [
697
186
  {
698
187
  spaceUuid: 'child-space',
@@ -737,7 +226,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
737
226
  });
738
227
  describe('hasDirectAccess', () => {
739
228
  it('is false when user has direct access only on parent (inherited)', () => {
740
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
229
+ const result = resolveSpaceAccess(makeChainInput({
741
230
  chainDirectAccess: [
742
231
  { spaceUuid: 'child-space', directAccess: [] },
743
232
  {
@@ -765,7 +254,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
765
254
  expect(result[0].hasDirectAccess).toBe(false);
766
255
  });
767
256
  it('is true when user has direct access on the leaf space', () => {
768
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
257
+ const result = resolveSpaceAccess(makeChainInput({
769
258
  chainDirectAccess: [
770
259
  {
771
260
  spaceUuid: 'child-space',
@@ -792,7 +281,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
792
281
  expect(result[0].hasDirectAccess).toBe(true);
793
282
  });
794
283
  it('is false when user has group access only on parent', () => {
795
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
284
+ const result = resolveSpaceAccess(makeChainInput({
796
285
  chainDirectAccess: [
797
286
  { spaceUuid: 'child-space', directAccess: [] },
798
287
  {
@@ -821,7 +310,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
821
310
  expect(result[0].inheritedFrom).toBe('parent_space');
822
311
  });
823
312
  it('is false when user has only project/org access', () => {
824
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
313
+ const result = resolveSpaceAccess(makeChainInput({
825
314
  chainDirectAccess: [
826
315
  { spaceUuid: 'child-space', directAccess: [] },
827
316
  ],
@@ -839,7 +328,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
839
328
  });
840
329
  describe('inheritedFrom metadata', () => {
841
330
  it('reports parent_space when winning role is from ancestor', () => {
842
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
331
+ const result = resolveSpaceAccess(makeChainInput({
843
332
  chainDirectAccess: [
844
333
  { spaceUuid: 'child-space', directAccess: [] },
845
334
  {
@@ -867,7 +356,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
867
356
  expect(result[0].inheritedFrom).toBe('parent_space');
868
357
  });
869
358
  it('reports parent_space when group access is from ancestor', () => {
870
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
359
+ const result = resolveSpaceAccess(makeChainInput({
871
360
  chainDirectAccess: [
872
361
  { spaceUuid: 'child-space', directAccess: [] },
873
362
  {
@@ -896,7 +385,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
896
385
  expect(result[0].hasDirectAccess).toBe(false);
897
386
  });
898
387
  it('does not report parent_space when winning role is from leaf', () => {
899
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
388
+ const result = resolveSpaceAccess(makeChainInput({
900
389
  chainDirectAccess: [
901
390
  {
902
391
  spaceUuid: 'child-space',
@@ -925,7 +414,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
925
414
  });
926
415
  describe('private chain', () => {
927
416
  it('no access without direct access on private chain', () => {
928
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
417
+ const result = resolveSpaceAccess(makeChainInput({
929
418
  inheritsFromOrgOrProject: false,
930
419
  chainDirectAccess: [
931
420
  { spaceUuid: 'child-space', directAccess: [] },
@@ -949,7 +438,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
949
438
  expect(result).toHaveLength(0);
950
439
  });
951
440
  it('admin without direct access is excluded (CASL handles admin access)', () => {
952
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
441
+ const result = resolveSpaceAccess(makeChainInput({
953
442
  inheritsFromOrgOrProject: false,
954
443
  chainDirectAccess: [
955
444
  { spaceUuid: 'child-space', directAccess: [] },
@@ -965,7 +454,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
965
454
  expect(result).toHaveLength(0);
966
455
  });
967
456
  it('direct access on parent grants access on private child', () => {
968
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
457
+ const result = resolveSpaceAccess(makeChainInput({
969
458
  inheritsFromOrgOrProject: false,
970
459
  chainDirectAccess: [
971
460
  { spaceUuid: 'child-space', directAccess: [] },
@@ -996,7 +485,7 @@ describe('resolveSpaceAccessWithInheritance', () => {
996
485
  });
997
486
  describe('multiple users', () => {
998
487
  it('resolves each user independently', () => {
999
- const result = resolveSpaceAccessWithInheritance(makeChainInput({
488
+ const result = resolveSpaceAccess(makeChainInput({
1000
489
  chainDirectAccess: [
1001
490
  {
1002
491
  spaceUuid: 'child-space',