@uptime.app/contracts 3.0.51-rc.0 → 3.0.51

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 (232) hide show
  1. package/dist/cjs/b2b-organisation/b2b-organisation-group-membership.d.ts +6 -0
  2. package/dist/cjs/b2b-organisation/b2b-organisation-group-membership.d.ts.map +1 -1
  3. package/dist/cjs/b2b-organisation/b2b-organisation-group-membership.js +6 -0
  4. package/dist/cjs/b2b-organisation/b2b-organisation-group-membership.js.map +1 -1
  5. package/dist/cjs/cms-hack/cms-searchable-hack.d.ts +0 -2
  6. package/dist/cjs/cms-hack/cms-searchable-hack.d.ts.map +1 -1
  7. package/dist/cjs/draft-hack/draft-hack.d.ts +0 -2
  8. package/dist/cjs/draft-hack/draft-hack.d.ts.map +1 -1
  9. package/dist/cjs/enriched-hack/enriched-hack.d.ts +0 -2
  10. package/dist/cjs/enriched-hack/enriched-hack.d.ts.map +1 -1
  11. package/dist/cjs/hack/hack.d.ts +0 -2
  12. package/dist/cjs/hack/hack.d.ts.map +1 -1
  13. package/dist/cjs/index.d.ts +0 -2
  14. package/dist/cjs/index.d.ts.map +1 -1
  15. package/dist/cjs/index.js +0 -2
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/pending-hack/pending-hack.d.ts +0 -2
  18. package/dist/cjs/pending-hack/pending-hack.d.ts.map +1 -1
  19. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.d.ts +6 -0
  20. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.d.ts.map +1 -1
  21. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.js +5 -1
  22. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.js.map +1 -1
  23. package/dist/esm/cms-hack/cms-searchable-hack.d.ts +0 -2
  24. package/dist/esm/cms-hack/cms-searchable-hack.d.ts.map +1 -1
  25. package/dist/esm/draft-hack/draft-hack.d.ts +0 -2
  26. package/dist/esm/draft-hack/draft-hack.d.ts.map +1 -1
  27. package/dist/esm/enriched-hack/enriched-hack.d.ts +0 -2
  28. package/dist/esm/enriched-hack/enriched-hack.d.ts.map +1 -1
  29. package/dist/esm/hack/hack.d.ts +0 -2
  30. package/dist/esm/hack/hack.d.ts.map +1 -1
  31. package/dist/esm/index.d.ts +0 -2
  32. package/dist/esm/index.d.ts.map +1 -1
  33. package/dist/esm/index.js +0 -2
  34. package/dist/esm/index.js.map +1 -1
  35. package/dist/esm/pending-hack/pending-hack.d.ts +0 -2
  36. package/dist/esm/pending-hack/pending-hack.d.ts.map +1 -1
  37. package/package.json +2 -2
  38. package/src/access-control/collection-access-control.ts +28 -28
  39. package/src/access-control/hack-access-control.ts +32 -32
  40. package/src/access-control/profile-access-control.ts +27 -27
  41. package/src/app-hack/app-full-hack.ts +9 -9
  42. package/src/app-hack/app-hack-metadata.ts +61 -61
  43. package/src/app-hack/app-restricted-hack.ts +22 -22
  44. package/src/b2b-content-access/b2b-content-access.ts +57 -57
  45. package/src/b2b-organisation/b2b-carousel.ts +33 -33
  46. package/src/b2b-organisation/b2b-org-idp-mapping.ts +27 -27
  47. package/src/b2b-organisation/b2b-organisation-events.ts +21 -21
  48. package/src/b2b-organisation/b2b-organisation-group-events.ts +21 -21
  49. package/src/b2b-organisation/b2b-organisation-group-membership-events.ts +21 -21
  50. package/src/b2b-organisation/b2b-organisation-group-membership.ts +17 -10
  51. package/src/b2b-organisation/b2b-organisation-group.ts +65 -65
  52. package/src/b2b-organisation/b2b-organisation-invitation.ts +45 -45
  53. package/src/b2b-organisation/b2b-organisation-member-events.ts +21 -21
  54. package/src/b2b-organisation/b2b-organisation-member.ts +19 -19
  55. package/src/b2b-organisation/b2b-organisation.ts +38 -38
  56. package/src/brand/brand-events.ts +21 -21
  57. package/src/brand/brand.ts +77 -77
  58. package/src/catalogue/catalogue.ts +152 -152
  59. package/src/cms-hack/cms-searchable-hack.ts +44 -47
  60. package/src/collection/collection-events.ts +21 -21
  61. package/src/collection/collection.ts +59 -59
  62. package/src/collection/enriched-collection-events.ts +18 -18
  63. package/src/collection/enriched-collection.ts +65 -65
  64. package/src/course/course-events.ts +20 -20
  65. package/src/course/course.ts +25 -25
  66. package/src/course-enrollment/course-enrollment-events.ts +21 -21
  67. package/src/course-enrollment/course-enrollment.ts +36 -36
  68. package/src/creator/creator-events.ts +32 -32
  69. package/src/creator/creator.ts +7 -7
  70. package/src/distribution/collection-distribution.ts +17 -17
  71. package/src/distribution/distribution-events.ts +61 -61
  72. package/src/distribution/hack-distribution.ts +18 -18
  73. package/src/draft-hack/draft-hack-events.ts +21 -21
  74. package/src/draft-hack/draft-hack.ts +190 -192
  75. package/src/draft-hack-generation/draft-hack-generation-commands.ts +10 -10
  76. package/src/draft-hack-generation/draft-hack-generation-events.ts +52 -52
  77. package/src/draft-hack-generation/draft-hack-generation.ts +39 -39
  78. package/src/enriched-b2b-organisation/enriched-b2b-organisation-events.ts +21 -21
  79. package/src/enriched-b2b-organisation/enriched-b2b-organisation.ts +37 -37
  80. package/src/enriched-hack/enriched-hack-events.ts +21 -21
  81. package/src/enriched-hack/enriched-hack.ts +109 -111
  82. package/src/enrichment-asset/hack-enrichment-video-events.ts +55 -55
  83. package/src/enrichment-asset/hack-enrichment-video.ts +40 -40
  84. package/src/entitlement/user-entitlements-commands.ts +54 -54
  85. package/src/entitlement/user-entitlements-events.ts +29 -29
  86. package/src/entitlement/user-entitlements.ts +18 -18
  87. package/src/flashcard/flashcard-events.ts +85 -85
  88. package/src/flashcard/flashcard.ts +138 -138
  89. package/src/hack/extracted-subject.ts +31 -31
  90. package/src/hack/hack-events.ts +21 -21
  91. package/src/hack/hack-published.ts +21 -21
  92. package/src/hack/hack-with-voice-events.ts +20 -20
  93. package/src/hack/hack-with-voice.ts +36 -36
  94. package/src/hack/hack.ts +127 -129
  95. package/src/hack/publish-hack-command.ts +20 -20
  96. package/src/hack/republish-hack-command.ts +20 -20
  97. package/src/hack-mapping/access-helper.ts +6 -6
  98. package/src/hack-mapping/app-full-hack.test.ts +13 -13
  99. package/src/hack-mapping/app-full-hack.ts +14 -14
  100. package/src/hack-mapping/app-hack-metadata.test.ts +22 -22
  101. package/src/hack-mapping/app-hack-metadata.ts +82 -82
  102. package/src/hack-mapping/app-restricted-hack.test.ts +12 -12
  103. package/src/hack-mapping/app-restricted-hack.ts +21 -21
  104. package/src/hack-mapping/remove-undefined-fields.ts +9 -9
  105. package/src/hack-mapping/test-data/enriched-hack.json +2576 -2576
  106. package/src/hack-mapping/test-data/expected-app-full-hack.json +2509 -2509
  107. package/src/hack-mapping/test-data/expected-app-hack-metadata.json +93 -93
  108. package/src/hack-mapping/test-data/expected-app-restricted-hack.json +799 -799
  109. package/src/hack-metadata/hack-metadata-events.ts +21 -21
  110. package/src/hack-metadata/hack-metadata.ts +17 -17
  111. package/src/hack-of-the-day/hack-of-the-day-events.ts +21 -21
  112. package/src/hack-of-the-day/hack-of-the-day.ts +13 -13
  113. package/src/hack-statistics/hack-statistics-events.ts +21 -21
  114. package/src/hack-statistics/hack-statistics.ts +9 -9
  115. package/src/hackbot/hack-generation-commands.ts +45 -45
  116. package/src/hackbot/hack-generation-trigger-events.ts +21 -21
  117. package/src/hackbot/hack-generation-trigger.ts +57 -57
  118. package/src/history/history-events.ts +21 -21
  119. package/src/history/history.ts +46 -46
  120. package/src/identity-provider/domain-identity-provider-events.ts +20 -20
  121. package/src/identity-provider/domain-identity-provider.ts +10 -10
  122. package/src/identity-provider/identity-provider-events.ts +20 -20
  123. package/src/identity-provider/identity-provider.ts +46 -46
  124. package/src/index.ts +220 -223
  125. package/src/interest/interest-events.ts +21 -21
  126. package/src/interest/interest.ts +15 -15
  127. package/src/knowledge-source/knowledge-source-events.ts +21 -21
  128. package/src/knowledge-source/knowledge-source.ts +43 -43
  129. package/src/knowledge-source-parsing/knowledge-source-metadata.ts +58 -58
  130. package/src/knowledge-source-parsing/knowledge-source-parsing-commands.ts +41 -41
  131. package/src/knowledge-source-parsing/knowledge-source-parsing-events.ts +30 -30
  132. package/src/knowledge-source-parsing/podcast-metadata.ts +44 -44
  133. package/src/knowledge-source-parsing/podcast-parsing-events.ts +28 -28
  134. package/src/knowledge-source-parsing/user-hack-generation-options.ts +7 -7
  135. package/src/learn-upon/get-sqsso-url.ts +13 -13
  136. package/src/learn-upon/learn-upon-commands.ts +33 -33
  137. package/src/learn-upon/learn-upon-course-enrollment-events.ts +21 -21
  138. package/src/learn-upon/learn-upon-course-enrollment.ts +62 -62
  139. package/src/learn-upon/learn-upon-course-events.ts +21 -21
  140. package/src/learn-upon/learn-upon-course.ts +108 -108
  141. package/src/learn-upon/learn-upon-portal-events.ts +20 -20
  142. package/src/learn-upon/learn-upon-portal.ts +12 -12
  143. package/src/learn-upon/learn-upon-user-mapping-events.ts +18 -18
  144. package/src/learn-upon/learn-upon-user-mapping.ts +9 -9
  145. package/src/learn-upon/learn-upon-webhook-events.ts +80 -80
  146. package/src/learn-upon/learn-upon-webhook-types.ts +346 -346
  147. package/src/learn-upon/unenroll.ts +6 -6
  148. package/src/localisation/live-locales.ts +45 -45
  149. package/src/localisation/locale-language.ts +4 -4
  150. package/src/localisation/supported-languages.ts +11 -11
  151. package/src/localisation/supported-locales.ts +67 -67
  152. package/src/message-bus/context.ts +7 -7
  153. package/src/moderation/hack-moderation-result-events.ts +18 -18
  154. package/src/moderation/hack-moderation-result.ts +27 -27
  155. package/src/partner/partner-events.ts +21 -21
  156. package/src/partner/partner.ts +7 -7
  157. package/src/pending-hack/pending-hack-events.ts +20 -20
  158. package/src/pending-hack/pending-hack-with-voice-events.ts +20 -20
  159. package/src/pending-hack/pending-hack-with-voice.ts +25 -25
  160. package/src/pending-hack/pending-hack.ts +78 -80
  161. package/src/preferences/preferences-events.ts +15 -15
  162. package/src/preferences/preferences.ts +35 -35
  163. package/src/profile/profile-events.ts +41 -41
  164. package/src/profile/profile.ts +36 -36
  165. package/src/profile/translated-profile.ts +13 -13
  166. package/src/referral/referral-events.ts +41 -41
  167. package/src/referral/referral.ts +76 -76
  168. package/src/saved/saved-collection-events.ts +19 -19
  169. package/src/saved/saved-collection.ts +8 -8
  170. package/src/segment/segment-commands.ts +75 -75
  171. package/src/segment/segment-events.ts +42 -42
  172. package/src/segment/segment-with-voice.ts +8 -8
  173. package/src/segment/segment.ts +82 -82
  174. package/src/service-simulator/mock-request-response.ts +17 -17
  175. package/src/skin/skin-events.ts +21 -21
  176. package/src/skin/skin.ts +6 -6
  177. package/src/spark/spark-board-read-model.ts +17 -17
  178. package/src/spark/spark-board.ts +12 -12
  179. package/src/spark/spark-events.ts +42 -42
  180. package/src/spark/spark.ts +75 -75
  181. package/src/text-analysis/hack-categories-extracted-events.ts +24 -24
  182. package/src/text-analysis/hack-categories-extracted.ts +31 -31
  183. package/src/text-analysis/hack-entities-extracted-events.ts +24 -24
  184. package/src/text-analysis/hack-entities-extracted.ts +38 -38
  185. package/src/text-analysis/hack-keywords-extracted-events.ts +24 -24
  186. package/src/text-analysis/hack-keywords-extracted.ts +23 -23
  187. package/src/text-analysis/hack-topics-extracted-events.ts +24 -24
  188. package/src/text-analysis/hack-topics-extracted.ts +31 -31
  189. package/src/text-to-speech/create-speech-commands.ts +22 -22
  190. package/src/text-to-speech/speech-created-events.ts +10 -10
  191. package/src/text-to-speech/supported-voices.ts +222 -222
  192. package/src/text-to-speech/text-segment.ts +29 -29
  193. package/src/theme/theme-events.ts +21 -21
  194. package/src/theme/theme.ts +68 -68
  195. package/src/translation/translation-events.ts +73 -73
  196. package/src/translation/translation.ts +25 -25
  197. package/src/trending/trending-events.ts +21 -21
  198. package/src/trending/trending-hacks.ts +8 -8
  199. package/src/user/cognito-user-authentication-log.ts +7 -7
  200. package/src/user/get-user.ts +63 -63
  201. package/src/user/user-events.ts +39 -39
  202. package/src/user-acl/user-acl-utils.test.ts +273 -273
  203. package/src/user-acl/user-acl-utils.ts +176 -176
  204. package/src/user-context/user-context-events.ts +21 -21
  205. package/src/user-context/user-context.ts +15 -15
  206. package/src/user-notification-context/user-notification-context-events.ts +21 -21
  207. package/src/user-notification-context/user-notification-context.ts +9 -9
  208. package/src/user-statistics/user-statistics-events.ts +97 -97
  209. package/src/user-statistics/user-statistics.ts +82 -82
  210. package/src/video-cropping/video-cropping-events.ts +43 -43
  211. package/src/video-cropping/video-metadata.ts +12 -12
  212. package/tsconfig.cjs.json +7 -7
  213. package/tsconfig.npm.json +22 -22
  214. package/dist/cjs/topics/primary-topic-mapping.d.ts +0 -7
  215. package/dist/cjs/topics/primary-topic-mapping.d.ts.map +0 -1
  216. package/dist/cjs/topics/primary-topic-mapping.js +0 -98
  217. package/dist/cjs/topics/primary-topic-mapping.js.map +0 -1
  218. package/dist/cjs/topics/primary-topic.d.ts +0 -4
  219. package/dist/cjs/topics/primary-topic.d.ts.map +0 -1
  220. package/dist/cjs/topics/primary-topic.js +0 -3
  221. package/dist/cjs/topics/primary-topic.js.map +0 -1
  222. package/dist/esm/topics/primary-topic-mapping.d.ts +0 -7
  223. package/dist/esm/topics/primary-topic-mapping.d.ts.map +0 -1
  224. package/dist/esm/topics/primary-topic-mapping.js +0 -94
  225. package/dist/esm/topics/primary-topic-mapping.js.map +0 -1
  226. package/dist/esm/topics/primary-topic.d.ts +0 -4
  227. package/dist/esm/topics/primary-topic.d.ts.map +0 -1
  228. package/dist/esm/topics/primary-topic.js +0 -2
  229. package/dist/esm/topics/primary-topic.js.map +0 -1
  230. package/src/topics/primary-topic-mapping.test.ts +0 -36
  231. package/src/topics/primary-topic-mapping.ts +0 -110
  232. package/src/topics/primary-topic.ts +0 -3
@@ -1,273 +1,273 @@
1
- import { parseAclEntry, getAclValuesByNamespace, aclFor, UserAclBuilder } from './user-acl-utils';
2
-
3
- describe('parseAclEntry', () => {
4
-
5
- test('returns null for empty string', () => {
6
- expect(parseAclEntry('')).toBeNull();
7
- });
8
-
9
- test('returns null for whitespace-only string', () => {
10
- expect(parseAclEntry(' ')).toBeNull();
11
- });
12
-
13
- test('parses a simple namespace-only entry', () => {
14
- expect(parseAclEntry('public')).toEqual({
15
- raw: 'public',
16
- excluded: false,
17
- namespace: 'public',
18
- value: null
19
- });
20
- });
21
-
22
- test('parses a namespace:value entry', () => {
23
- expect(parseAclEntry('catalogue:uptime')).toEqual({
24
- raw: 'catalogue:uptime',
25
- excluded: false,
26
- namespace: 'catalogue',
27
- value: 'uptime'
28
- });
29
- });
30
-
31
- test('parses an excluded namespace-only entry', () => {
32
- expect(parseAclEntry('!public')).toEqual({
33
- raw: '!public',
34
- excluded: true,
35
- namespace: 'public',
36
- value: null
37
- });
38
- });
39
-
40
- test('parses an excluded namespace:value entry', () => {
41
- expect(parseAclEntry('!catalogue:uptime')).toEqual({
42
- raw: '!catalogue:uptime',
43
- excluded: true,
44
- namespace: 'catalogue',
45
- value: 'uptime'
46
- });
47
- });
48
-
49
- test('trims leading and trailing whitespace', () => {
50
- expect(parseAclEntry(' catalogue:uptime ')).toEqual({
51
- raw: 'catalogue:uptime',
52
- excluded: false,
53
- namespace: 'catalogue',
54
- value: 'uptime'
55
- });
56
- });
57
-
58
- test('returns null when namespace is empty (entry starts with colon)', () => {
59
- expect(parseAclEntry(':value')).toBeNull();
60
- });
61
-
62
- test('preserves colons in value', () => {
63
- expect(parseAclEntry('catalogue:b2b:org-123')).toEqual({
64
- raw: 'catalogue:b2b:org-123',
65
- excluded: false,
66
- namespace: 'catalogue',
67
- value: 'b2b:org-123'
68
- });
69
- });
70
- });
71
-
72
- describe('getAclValuesByNamespace', () => {
73
- const acl = [
74
- 'public',
75
- 'catalogue:uptime',
76
- 'catalogue:premium',
77
- '!catalogue:restricted',
78
- 'b2b:org-1',
79
- '!b2b:org-2',
80
- 'user:user-123'
81
- ];
82
-
83
- test('returns included values for a namespace by default', () => {
84
- expect(getAclValuesByNamespace(acl, 'catalogue')).toEqual(['uptime', 'premium']);
85
- });
86
-
87
- test('returns excluded values when mode is "excluded"', () => {
88
- expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'excluded' })).toEqual(['restricted']);
89
- });
90
-
91
- test('returns all values when mode is "all"', () => {
92
- expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'all' })).toEqual(['uptime', 'premium', 'restricted']);
93
- });
94
-
95
- test('returns empty array when namespace has no matches', () => {
96
- expect(getAclValuesByNamespace(acl, 'subject')).toEqual([]);
97
- });
98
-
99
- test('deduplicates values by default', () => {
100
- const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
101
- expect(getAclValuesByNamespace(aclWithDupes, 'catalogue')).toEqual(['uptime', 'premium']);
102
- });
103
-
104
- test('preserves duplicates when dedupe is false', () => {
105
- const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
106
- expect(getAclValuesByNamespace(aclWithDupes, 'catalogue', { dedupe: false })).toEqual(['uptime', 'uptime', 'premium']);
107
- });
108
-
109
- test('excludes namespace-only entries (no value)', () => {
110
- expect(getAclValuesByNamespace(acl, 'public')).toEqual([]);
111
- });
112
-
113
- test('returns empty array for empty acl', () => {
114
- expect(getAclValuesByNamespace([], 'catalogue')).toEqual([]);
115
- });
116
-
117
- test('skips invalid entries', () => {
118
- const aclWithInvalid = ['', ' ', 'catalogue:uptime'];
119
- expect(getAclValuesByNamespace(aclWithInvalid, 'catalogue')).toEqual(['uptime']);
120
- });
121
- });
122
-
123
- describe('UserAclBuilder', () => {
124
-
125
- test('does not include "public" by default', () => {
126
- const acl = new UserAclBuilder().build();
127
- expect(acl).not.toContain('public');
128
- });
129
-
130
- test('includes "public" when allowPublic is called', () => {
131
- const acl = new UserAclBuilder().allowPublic().build();
132
- expect(acl).toContain('public');
133
- });
134
-
135
- test('includes user entry when allowUser is called', () => {
136
- const acl = new UserAclBuilder().allowUser('user-123').build();
137
- expect(acl).toContain('user:user-123');
138
- });
139
-
140
- test('merges baseAcl entries', () => {
141
- const acl = new UserAclBuilder({ baseAcl: ['catalogue:uptime', '!catalogue:restricted'] }).build();
142
- expect(acl).toContain('catalogue:uptime');
143
- expect(acl).toContain('!catalogue:restricted');
144
- });
145
-
146
- describe('allow', () => {
147
- test('adds namespace:value entry', () => {
148
- const acl = new UserAclBuilder().allow('catalogue', 'uptime').build();
149
- expect(acl).toContain('catalogue:uptime');
150
- });
151
-
152
- test('adds namespace-only entry when no value provided', () => {
153
- const acl = new UserAclBuilder().allow('public').build();
154
- expect(acl).toContain('public');
155
- });
156
-
157
- test('removes entry from excluded when allowed', () => {
158
- const acl = new UserAclBuilder()
159
- .deny('catalogue', 'uptime')
160
- .allow('catalogue', 'uptime')
161
- .build();
162
- expect(acl).toContain('catalogue:uptime');
163
- expect(acl).not.toContain('!catalogue:uptime');
164
- });
165
- });
166
-
167
- describe('deny', () => {
168
- test('adds excluded namespace:value entry', () => {
169
- const acl = new UserAclBuilder().deny('catalogue', 'restricted').build();
170
- expect(acl).toContain('!catalogue:restricted');
171
- });
172
-
173
- test('removes entry from included when denied', () => {
174
- const acl = new UserAclBuilder()
175
- .allow('catalogue', 'uptime')
176
- .deny('catalogue', 'uptime')
177
- .build();
178
- expect(acl).not.toContain('catalogue:uptime');
179
- expect(acl).toContain('!catalogue:uptime');
180
- });
181
- });
182
-
183
- describe('allowCatalogue', () => {
184
- test('adds catalogue:<id> entry', () => {
185
- const acl = new UserAclBuilder().allowCatalogue('premium').build();
186
- expect(acl).toContain('catalogue:premium');
187
- });
188
- });
189
-
190
- describe('denyCatalogue', () => {
191
- test('adds !catalogue:<id> entry', () => {
192
- const acl = new UserAclBuilder().denyCatalogue('restricted').build();
193
- expect(acl).toContain('!catalogue:restricted');
194
- });
195
- });
196
-
197
- describe('allowOrganisation', () => {
198
- test('adds b2b:<id> entry', () => {
199
- const acl = new UserAclBuilder().allowOrganisation('org-1').build();
200
- expect(acl).toContain('b2b:org-1');
201
- });
202
- });
203
-
204
- describe('denySubject', () => {
205
- test('adds !subject:<value> entry', () => {
206
- const acl = new UserAclBuilder().denySubject('some-topic').build();
207
- expect(acl).toContain('!subject:some-topic');
208
- });
209
- });
210
-
211
- describe('addRaw', () => {
212
- test('adds an included raw entry', () => {
213
- const acl = new UserAclBuilder().addRaw('catalogue:uptime').build();
214
- expect(acl).toContain('catalogue:uptime');
215
- });
216
-
217
- test('adds an excluded raw entry', () => {
218
- const acl = new UserAclBuilder().addRaw('!catalogue:restricted').build();
219
- expect(acl).toContain('!catalogue:restricted');
220
- });
221
-
222
- test('ignores invalid raw entries', () => {
223
- const acl = new UserAclBuilder().addRaw('').build();
224
- expect(acl).toEqual([]);
225
- });
226
- });
227
-
228
- describe('addMany', () => {
229
- test('adds multiple entries at once', () => {
230
- const acl = new UserAclBuilder()
231
- .addMany(['catalogue:uptime', 'b2b:org-1', '!catalogue:restricted'])
232
- .build();
233
- expect(acl).toContain('catalogue:uptime');
234
- expect(acl).toContain('b2b:org-1');
235
- expect(acl).toContain('!catalogue:restricted');
236
- });
237
- });
238
- });
239
-
240
- describe('aclFor', () => {
241
- test('includes user entry', () => {
242
- const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
243
- expect(acl).toContain('user:user-abc');
244
- });
245
-
246
- test('always includes catalogue:uptime', () => {
247
- const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
248
- expect(acl).toContain('catalogue:uptime');
249
- });
250
-
251
- test('includes provided catalogue ids', () => {
252
- const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: ['premium', 'free'] });
253
- expect(acl).toContain('catalogue:premium');
254
- expect(acl).toContain('catalogue:free');
255
- });
256
-
257
- test('includes b2b catalogue entry for each organisation', () => {
258
- const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
259
- expect(acl).toContain('catalogue:b2b:org-1');
260
- expect(acl).toContain('catalogue:b2b:org-2');
261
- });
262
-
263
- test('includes b2b namespace entry for each organisation', () => {
264
- const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
265
- expect(acl).toContain('b2b:org-1');
266
- expect(acl).toContain('b2b:org-2');
267
- });
268
-
269
- test('includes public by default', () => {
270
- const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
271
- expect(acl).toContain('public');
272
- });
273
- });
1
+ import { parseAclEntry, getAclValuesByNamespace, aclFor, UserAclBuilder } from './user-acl-utils';
2
+
3
+ describe('parseAclEntry', () => {
4
+
5
+ test('returns null for empty string', () => {
6
+ expect(parseAclEntry('')).toBeNull();
7
+ });
8
+
9
+ test('returns null for whitespace-only string', () => {
10
+ expect(parseAclEntry(' ')).toBeNull();
11
+ });
12
+
13
+ test('parses a simple namespace-only entry', () => {
14
+ expect(parseAclEntry('public')).toEqual({
15
+ raw: 'public',
16
+ excluded: false,
17
+ namespace: 'public',
18
+ value: null
19
+ });
20
+ });
21
+
22
+ test('parses a namespace:value entry', () => {
23
+ expect(parseAclEntry('catalogue:uptime')).toEqual({
24
+ raw: 'catalogue:uptime',
25
+ excluded: false,
26
+ namespace: 'catalogue',
27
+ value: 'uptime'
28
+ });
29
+ });
30
+
31
+ test('parses an excluded namespace-only entry', () => {
32
+ expect(parseAclEntry('!public')).toEqual({
33
+ raw: '!public',
34
+ excluded: true,
35
+ namespace: 'public',
36
+ value: null
37
+ });
38
+ });
39
+
40
+ test('parses an excluded namespace:value entry', () => {
41
+ expect(parseAclEntry('!catalogue:uptime')).toEqual({
42
+ raw: '!catalogue:uptime',
43
+ excluded: true,
44
+ namespace: 'catalogue',
45
+ value: 'uptime'
46
+ });
47
+ });
48
+
49
+ test('trims leading and trailing whitespace', () => {
50
+ expect(parseAclEntry(' catalogue:uptime ')).toEqual({
51
+ raw: 'catalogue:uptime',
52
+ excluded: false,
53
+ namespace: 'catalogue',
54
+ value: 'uptime'
55
+ });
56
+ });
57
+
58
+ test('returns null when namespace is empty (entry starts with colon)', () => {
59
+ expect(parseAclEntry(':value')).toBeNull();
60
+ });
61
+
62
+ test('preserves colons in value', () => {
63
+ expect(parseAclEntry('catalogue:b2b:org-123')).toEqual({
64
+ raw: 'catalogue:b2b:org-123',
65
+ excluded: false,
66
+ namespace: 'catalogue',
67
+ value: 'b2b:org-123'
68
+ });
69
+ });
70
+ });
71
+
72
+ describe('getAclValuesByNamespace', () => {
73
+ const acl = [
74
+ 'public',
75
+ 'catalogue:uptime',
76
+ 'catalogue:premium',
77
+ '!catalogue:restricted',
78
+ 'b2b:org-1',
79
+ '!b2b:org-2',
80
+ 'user:user-123'
81
+ ];
82
+
83
+ test('returns included values for a namespace by default', () => {
84
+ expect(getAclValuesByNamespace(acl, 'catalogue')).toEqual(['uptime', 'premium']);
85
+ });
86
+
87
+ test('returns excluded values when mode is "excluded"', () => {
88
+ expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'excluded' })).toEqual(['restricted']);
89
+ });
90
+
91
+ test('returns all values when mode is "all"', () => {
92
+ expect(getAclValuesByNamespace(acl, 'catalogue', { mode: 'all' })).toEqual(['uptime', 'premium', 'restricted']);
93
+ });
94
+
95
+ test('returns empty array when namespace has no matches', () => {
96
+ expect(getAclValuesByNamespace(acl, 'subject')).toEqual([]);
97
+ });
98
+
99
+ test('deduplicates values by default', () => {
100
+ const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
101
+ expect(getAclValuesByNamespace(aclWithDupes, 'catalogue')).toEqual(['uptime', 'premium']);
102
+ });
103
+
104
+ test('preserves duplicates when dedupe is false', () => {
105
+ const aclWithDupes = ['catalogue:uptime', 'catalogue:uptime', 'catalogue:premium'];
106
+ expect(getAclValuesByNamespace(aclWithDupes, 'catalogue', { dedupe: false })).toEqual(['uptime', 'uptime', 'premium']);
107
+ });
108
+
109
+ test('excludes namespace-only entries (no value)', () => {
110
+ expect(getAclValuesByNamespace(acl, 'public')).toEqual([]);
111
+ });
112
+
113
+ test('returns empty array for empty acl', () => {
114
+ expect(getAclValuesByNamespace([], 'catalogue')).toEqual([]);
115
+ });
116
+
117
+ test('skips invalid entries', () => {
118
+ const aclWithInvalid = ['', ' ', 'catalogue:uptime'];
119
+ expect(getAclValuesByNamespace(aclWithInvalid, 'catalogue')).toEqual(['uptime']);
120
+ });
121
+ });
122
+
123
+ describe('UserAclBuilder', () => {
124
+
125
+ test('does not include "public" by default', () => {
126
+ const acl = new UserAclBuilder().build();
127
+ expect(acl).not.toContain('public');
128
+ });
129
+
130
+ test('includes "public" when allowPublic is called', () => {
131
+ const acl = new UserAclBuilder().allowPublic().build();
132
+ expect(acl).toContain('public');
133
+ });
134
+
135
+ test('includes user entry when allowUser is called', () => {
136
+ const acl = new UserAclBuilder().allowUser('user-123').build();
137
+ expect(acl).toContain('user:user-123');
138
+ });
139
+
140
+ test('merges baseAcl entries', () => {
141
+ const acl = new UserAclBuilder({ baseAcl: ['catalogue:uptime', '!catalogue:restricted'] }).build();
142
+ expect(acl).toContain('catalogue:uptime');
143
+ expect(acl).toContain('!catalogue:restricted');
144
+ });
145
+
146
+ describe('allow', () => {
147
+ test('adds namespace:value entry', () => {
148
+ const acl = new UserAclBuilder().allow('catalogue', 'uptime').build();
149
+ expect(acl).toContain('catalogue:uptime');
150
+ });
151
+
152
+ test('adds namespace-only entry when no value provided', () => {
153
+ const acl = new UserAclBuilder().allow('public').build();
154
+ expect(acl).toContain('public');
155
+ });
156
+
157
+ test('removes entry from excluded when allowed', () => {
158
+ const acl = new UserAclBuilder()
159
+ .deny('catalogue', 'uptime')
160
+ .allow('catalogue', 'uptime')
161
+ .build();
162
+ expect(acl).toContain('catalogue:uptime');
163
+ expect(acl).not.toContain('!catalogue:uptime');
164
+ });
165
+ });
166
+
167
+ describe('deny', () => {
168
+ test('adds excluded namespace:value entry', () => {
169
+ const acl = new UserAclBuilder().deny('catalogue', 'restricted').build();
170
+ expect(acl).toContain('!catalogue:restricted');
171
+ });
172
+
173
+ test('removes entry from included when denied', () => {
174
+ const acl = new UserAclBuilder()
175
+ .allow('catalogue', 'uptime')
176
+ .deny('catalogue', 'uptime')
177
+ .build();
178
+ expect(acl).not.toContain('catalogue:uptime');
179
+ expect(acl).toContain('!catalogue:uptime');
180
+ });
181
+ });
182
+
183
+ describe('allowCatalogue', () => {
184
+ test('adds catalogue:<id> entry', () => {
185
+ const acl = new UserAclBuilder().allowCatalogue('premium').build();
186
+ expect(acl).toContain('catalogue:premium');
187
+ });
188
+ });
189
+
190
+ describe('denyCatalogue', () => {
191
+ test('adds !catalogue:<id> entry', () => {
192
+ const acl = new UserAclBuilder().denyCatalogue('restricted').build();
193
+ expect(acl).toContain('!catalogue:restricted');
194
+ });
195
+ });
196
+
197
+ describe('allowOrganisation', () => {
198
+ test('adds b2b:<id> entry', () => {
199
+ const acl = new UserAclBuilder().allowOrganisation('org-1').build();
200
+ expect(acl).toContain('b2b:org-1');
201
+ });
202
+ });
203
+
204
+ describe('denySubject', () => {
205
+ test('adds !subject:<value> entry', () => {
206
+ const acl = new UserAclBuilder().denySubject('some-topic').build();
207
+ expect(acl).toContain('!subject:some-topic');
208
+ });
209
+ });
210
+
211
+ describe('addRaw', () => {
212
+ test('adds an included raw entry', () => {
213
+ const acl = new UserAclBuilder().addRaw('catalogue:uptime').build();
214
+ expect(acl).toContain('catalogue:uptime');
215
+ });
216
+
217
+ test('adds an excluded raw entry', () => {
218
+ const acl = new UserAclBuilder().addRaw('!catalogue:restricted').build();
219
+ expect(acl).toContain('!catalogue:restricted');
220
+ });
221
+
222
+ test('ignores invalid raw entries', () => {
223
+ const acl = new UserAclBuilder().addRaw('').build();
224
+ expect(acl).toEqual([]);
225
+ });
226
+ });
227
+
228
+ describe('addMany', () => {
229
+ test('adds multiple entries at once', () => {
230
+ const acl = new UserAclBuilder()
231
+ .addMany(['catalogue:uptime', 'b2b:org-1', '!catalogue:restricted'])
232
+ .build();
233
+ expect(acl).toContain('catalogue:uptime');
234
+ expect(acl).toContain('b2b:org-1');
235
+ expect(acl).toContain('!catalogue:restricted');
236
+ });
237
+ });
238
+ });
239
+
240
+ describe('aclFor', () => {
241
+ test('includes user entry', () => {
242
+ const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
243
+ expect(acl).toContain('user:user-abc');
244
+ });
245
+
246
+ test('always includes catalogue:uptime', () => {
247
+ const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
248
+ expect(acl).toContain('catalogue:uptime');
249
+ });
250
+
251
+ test('includes provided catalogue ids', () => {
252
+ const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: ['premium', 'free'] });
253
+ expect(acl).toContain('catalogue:premium');
254
+ expect(acl).toContain('catalogue:free');
255
+ });
256
+
257
+ test('includes b2b catalogue entry for each organisation', () => {
258
+ const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
259
+ expect(acl).toContain('catalogue:b2b:org-1');
260
+ expect(acl).toContain('catalogue:b2b:org-2');
261
+ });
262
+
263
+ test('includes b2b namespace entry for each organisation', () => {
264
+ const acl = aclFor({ userId: 'user-abc', organisationIds: ['org-1', 'org-2'], catalogueIds: [] });
265
+ expect(acl).toContain('b2b:org-1');
266
+ expect(acl).toContain('b2b:org-2');
267
+ });
268
+
269
+ test('includes public by default', () => {
270
+ const acl = aclFor({ userId: 'user-abc', organisationIds: [], catalogueIds: [] });
271
+ expect(acl).toContain('public');
272
+ });
273
+ });