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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/dist/cjs/b2b-organisation/b2b-organisation-group-membership.d.ts +0 -6
  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 +0 -6
  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 +2 -0
  6. package/dist/cjs/cms-hack/cms-searchable-hack.d.ts.map +1 -1
  7. package/dist/cjs/draft-hack/draft-hack.d.ts +2 -0
  8. package/dist/cjs/draft-hack/draft-hack.d.ts.map +1 -1
  9. package/dist/cjs/enriched-hack/enriched-hack.d.ts +2 -0
  10. package/dist/cjs/enriched-hack/enriched-hack.d.ts.map +1 -1
  11. package/dist/cjs/hack/hack.d.ts +2 -0
  12. package/dist/cjs/hack/hack.d.ts.map +1 -1
  13. package/dist/cjs/index.d.ts +2 -0
  14. package/dist/cjs/index.d.ts.map +1 -1
  15. package/dist/cjs/index.js +2 -0
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/pending-hack/pending-hack.d.ts +2 -0
  18. package/dist/cjs/pending-hack/pending-hack.d.ts.map +1 -1
  19. package/dist/cjs/topics/primary-topic-mapping.d.ts +7 -0
  20. package/dist/cjs/topics/primary-topic-mapping.d.ts.map +1 -0
  21. package/dist/cjs/topics/primary-topic-mapping.js +98 -0
  22. package/dist/cjs/topics/primary-topic-mapping.js.map +1 -0
  23. package/dist/cjs/topics/primary-topic.d.ts +4 -0
  24. package/dist/cjs/topics/primary-topic.d.ts.map +1 -0
  25. package/dist/cjs/topics/primary-topic.js +3 -0
  26. package/dist/cjs/topics/primary-topic.js.map +1 -0
  27. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.d.ts +0 -6
  28. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.d.ts.map +1 -1
  29. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.js +1 -5
  30. package/dist/esm/b2b-organisation/b2b-organisation-group-membership.js.map +1 -1
  31. package/dist/esm/cms-hack/cms-searchable-hack.d.ts +2 -0
  32. package/dist/esm/cms-hack/cms-searchable-hack.d.ts.map +1 -1
  33. package/dist/esm/draft-hack/draft-hack.d.ts +2 -0
  34. package/dist/esm/draft-hack/draft-hack.d.ts.map +1 -1
  35. package/dist/esm/enriched-hack/enriched-hack.d.ts +2 -0
  36. package/dist/esm/enriched-hack/enriched-hack.d.ts.map +1 -1
  37. package/dist/esm/hack/hack.d.ts +2 -0
  38. package/dist/esm/hack/hack.d.ts.map +1 -1
  39. package/dist/esm/index.d.ts +2 -0
  40. package/dist/esm/index.d.ts.map +1 -1
  41. package/dist/esm/index.js +2 -0
  42. package/dist/esm/index.js.map +1 -1
  43. package/dist/esm/pending-hack/pending-hack.d.ts +2 -0
  44. package/dist/esm/pending-hack/pending-hack.d.ts.map +1 -1
  45. package/dist/esm/topics/primary-topic-mapping.d.ts +7 -0
  46. package/dist/esm/topics/primary-topic-mapping.d.ts.map +1 -0
  47. package/dist/esm/topics/primary-topic-mapping.js +94 -0
  48. package/dist/esm/topics/primary-topic-mapping.js.map +1 -0
  49. package/dist/esm/topics/primary-topic.d.ts +4 -0
  50. package/dist/esm/topics/primary-topic.d.ts.map +1 -0
  51. package/dist/esm/topics/primary-topic.js +2 -0
  52. package/dist/esm/topics/primary-topic.js.map +1 -0
  53. package/package.json +2 -2
  54. package/src/access-control/collection-access-control.ts +28 -28
  55. package/src/access-control/hack-access-control.ts +32 -32
  56. package/src/access-control/profile-access-control.ts +27 -27
  57. package/src/app-hack/app-full-hack.ts +9 -9
  58. package/src/app-hack/app-hack-metadata.ts +61 -61
  59. package/src/app-hack/app-restricted-hack.ts +22 -22
  60. package/src/b2b-content-access/b2b-content-access.ts +57 -57
  61. package/src/b2b-organisation/b2b-carousel.ts +33 -33
  62. package/src/b2b-organisation/b2b-org-idp-mapping.ts +27 -27
  63. package/src/b2b-organisation/b2b-organisation-events.ts +21 -21
  64. package/src/b2b-organisation/b2b-organisation-group-events.ts +21 -21
  65. package/src/b2b-organisation/b2b-organisation-group-membership-events.ts +21 -21
  66. package/src/b2b-organisation/b2b-organisation-group-membership.ts +10 -17
  67. package/src/b2b-organisation/b2b-organisation-group.ts +65 -65
  68. package/src/b2b-organisation/b2b-organisation-invitation.ts +45 -45
  69. package/src/b2b-organisation/b2b-organisation-member-events.ts +21 -21
  70. package/src/b2b-organisation/b2b-organisation-member.ts +19 -19
  71. package/src/b2b-organisation/b2b-organisation.ts +38 -38
  72. package/src/brand/brand-events.ts +21 -21
  73. package/src/brand/brand.ts +77 -77
  74. package/src/catalogue/catalogue.ts +152 -152
  75. package/src/cms-hack/cms-searchable-hack.ts +47 -44
  76. package/src/collection/collection-events.ts +21 -21
  77. package/src/collection/collection.ts +59 -59
  78. package/src/collection/enriched-collection-events.ts +18 -18
  79. package/src/collection/enriched-collection.ts +65 -65
  80. package/src/course/course-events.ts +20 -20
  81. package/src/course/course.ts +25 -25
  82. package/src/course-enrollment/course-enrollment-events.ts +21 -21
  83. package/src/course-enrollment/course-enrollment.ts +36 -36
  84. package/src/creator/creator-events.ts +32 -32
  85. package/src/creator/creator.ts +7 -7
  86. package/src/distribution/collection-distribution.ts +17 -17
  87. package/src/distribution/distribution-events.ts +61 -61
  88. package/src/distribution/hack-distribution.ts +18 -18
  89. package/src/draft-hack/draft-hack-events.ts +21 -21
  90. package/src/draft-hack/draft-hack.ts +192 -190
  91. package/src/draft-hack-generation/draft-hack-generation-commands.ts +10 -10
  92. package/src/draft-hack-generation/draft-hack-generation-events.ts +52 -52
  93. package/src/draft-hack-generation/draft-hack-generation.ts +39 -39
  94. package/src/enriched-b2b-organisation/enriched-b2b-organisation-events.ts +21 -21
  95. package/src/enriched-b2b-organisation/enriched-b2b-organisation.ts +37 -37
  96. package/src/enriched-hack/enriched-hack-events.ts +21 -21
  97. package/src/enriched-hack/enriched-hack.ts +111 -109
  98. package/src/enrichment-asset/hack-enrichment-video-events.ts +55 -55
  99. package/src/enrichment-asset/hack-enrichment-video.ts +40 -40
  100. package/src/entitlement/user-entitlements-commands.ts +54 -54
  101. package/src/entitlement/user-entitlements-events.ts +29 -29
  102. package/src/entitlement/user-entitlements.ts +18 -18
  103. package/src/flashcard/flashcard-events.ts +85 -85
  104. package/src/flashcard/flashcard.ts +138 -138
  105. package/src/hack/extracted-subject.ts +31 -31
  106. package/src/hack/hack-events.ts +21 -21
  107. package/src/hack/hack-published.ts +21 -21
  108. package/src/hack/hack-with-voice-events.ts +20 -20
  109. package/src/hack/hack-with-voice.ts +36 -36
  110. package/src/hack/hack.ts +129 -127
  111. package/src/hack/publish-hack-command.ts +20 -20
  112. package/src/hack/republish-hack-command.ts +20 -20
  113. package/src/hack-mapping/access-helper.ts +6 -6
  114. package/src/hack-mapping/app-full-hack.test.ts +13 -13
  115. package/src/hack-mapping/app-full-hack.ts +14 -14
  116. package/src/hack-mapping/app-hack-metadata.test.ts +22 -22
  117. package/src/hack-mapping/app-hack-metadata.ts +82 -82
  118. package/src/hack-mapping/app-restricted-hack.test.ts +12 -12
  119. package/src/hack-mapping/app-restricted-hack.ts +21 -21
  120. package/src/hack-mapping/remove-undefined-fields.ts +9 -9
  121. package/src/hack-mapping/test-data/enriched-hack.json +2576 -2576
  122. package/src/hack-mapping/test-data/expected-app-full-hack.json +2509 -2509
  123. package/src/hack-mapping/test-data/expected-app-hack-metadata.json +93 -93
  124. package/src/hack-mapping/test-data/expected-app-restricted-hack.json +799 -799
  125. package/src/hack-metadata/hack-metadata-events.ts +21 -21
  126. package/src/hack-metadata/hack-metadata.ts +17 -17
  127. package/src/hack-of-the-day/hack-of-the-day-events.ts +21 -21
  128. package/src/hack-of-the-day/hack-of-the-day.ts +13 -13
  129. package/src/hack-statistics/hack-statistics-events.ts +21 -21
  130. package/src/hack-statistics/hack-statistics.ts +9 -9
  131. package/src/hackbot/hack-generation-commands.ts +45 -45
  132. package/src/hackbot/hack-generation-trigger-events.ts +21 -21
  133. package/src/hackbot/hack-generation-trigger.ts +57 -57
  134. package/src/history/history-events.ts +21 -21
  135. package/src/history/history.ts +46 -46
  136. package/src/identity-provider/domain-identity-provider-events.ts +20 -20
  137. package/src/identity-provider/domain-identity-provider.ts +10 -10
  138. package/src/identity-provider/identity-provider-events.ts +20 -20
  139. package/src/identity-provider/identity-provider.ts +46 -46
  140. package/src/index.ts +223 -220
  141. package/src/interest/interest-events.ts +21 -21
  142. package/src/interest/interest.ts +15 -15
  143. package/src/knowledge-source/knowledge-source-events.ts +21 -21
  144. package/src/knowledge-source/knowledge-source.ts +43 -43
  145. package/src/knowledge-source-parsing/knowledge-source-metadata.ts +58 -58
  146. package/src/knowledge-source-parsing/knowledge-source-parsing-commands.ts +41 -41
  147. package/src/knowledge-source-parsing/knowledge-source-parsing-events.ts +30 -30
  148. package/src/knowledge-source-parsing/podcast-metadata.ts +44 -44
  149. package/src/knowledge-source-parsing/podcast-parsing-events.ts +28 -28
  150. package/src/knowledge-source-parsing/user-hack-generation-options.ts +7 -7
  151. package/src/learn-upon/get-sqsso-url.ts +13 -13
  152. package/src/learn-upon/learn-upon-commands.ts +33 -33
  153. package/src/learn-upon/learn-upon-course-enrollment-events.ts +21 -21
  154. package/src/learn-upon/learn-upon-course-enrollment.ts +62 -62
  155. package/src/learn-upon/learn-upon-course-events.ts +21 -21
  156. package/src/learn-upon/learn-upon-course.ts +108 -108
  157. package/src/learn-upon/learn-upon-portal-events.ts +20 -20
  158. package/src/learn-upon/learn-upon-portal.ts +12 -12
  159. package/src/learn-upon/learn-upon-user-mapping-events.ts +18 -18
  160. package/src/learn-upon/learn-upon-user-mapping.ts +9 -9
  161. package/src/learn-upon/learn-upon-webhook-events.ts +80 -80
  162. package/src/learn-upon/learn-upon-webhook-types.ts +346 -346
  163. package/src/learn-upon/unenroll.ts +6 -6
  164. package/src/localisation/live-locales.ts +45 -45
  165. package/src/localisation/locale-language.ts +4 -4
  166. package/src/localisation/supported-languages.ts +11 -11
  167. package/src/localisation/supported-locales.ts +67 -67
  168. package/src/message-bus/context.ts +7 -7
  169. package/src/moderation/hack-moderation-result-events.ts +18 -18
  170. package/src/moderation/hack-moderation-result.ts +27 -27
  171. package/src/partner/partner-events.ts +21 -21
  172. package/src/partner/partner.ts +7 -7
  173. package/src/pending-hack/pending-hack-events.ts +20 -20
  174. package/src/pending-hack/pending-hack-with-voice-events.ts +20 -20
  175. package/src/pending-hack/pending-hack-with-voice.ts +25 -25
  176. package/src/pending-hack/pending-hack.ts +80 -78
  177. package/src/preferences/preferences-events.ts +15 -15
  178. package/src/preferences/preferences.ts +35 -35
  179. package/src/profile/profile-events.ts +41 -41
  180. package/src/profile/profile.ts +36 -36
  181. package/src/profile/translated-profile.ts +13 -13
  182. package/src/referral/referral-events.ts +41 -41
  183. package/src/referral/referral.ts +76 -76
  184. package/src/saved/saved-collection-events.ts +19 -19
  185. package/src/saved/saved-collection.ts +8 -8
  186. package/src/segment/segment-commands.ts +75 -75
  187. package/src/segment/segment-events.ts +42 -42
  188. package/src/segment/segment-with-voice.ts +8 -8
  189. package/src/segment/segment.ts +82 -82
  190. package/src/service-simulator/mock-request-response.ts +17 -17
  191. package/src/skin/skin-events.ts +21 -21
  192. package/src/skin/skin.ts +6 -6
  193. package/src/spark/spark-board-read-model.ts +17 -17
  194. package/src/spark/spark-board.ts +12 -12
  195. package/src/spark/spark-events.ts +42 -42
  196. package/src/spark/spark.ts +75 -75
  197. package/src/text-analysis/hack-categories-extracted-events.ts +24 -24
  198. package/src/text-analysis/hack-categories-extracted.ts +31 -31
  199. package/src/text-analysis/hack-entities-extracted-events.ts +24 -24
  200. package/src/text-analysis/hack-entities-extracted.ts +38 -38
  201. package/src/text-analysis/hack-keywords-extracted-events.ts +24 -24
  202. package/src/text-analysis/hack-keywords-extracted.ts +23 -23
  203. package/src/text-analysis/hack-topics-extracted-events.ts +24 -24
  204. package/src/text-analysis/hack-topics-extracted.ts +31 -31
  205. package/src/text-to-speech/create-speech-commands.ts +22 -22
  206. package/src/text-to-speech/speech-created-events.ts +10 -10
  207. package/src/text-to-speech/supported-voices.ts +222 -222
  208. package/src/text-to-speech/text-segment.ts +29 -29
  209. package/src/theme/theme-events.ts +21 -21
  210. package/src/theme/theme.ts +68 -68
  211. package/src/topics/primary-topic-mapping.test.ts +36 -0
  212. package/src/topics/primary-topic-mapping.ts +110 -0
  213. package/src/topics/primary-topic.ts +3 -0
  214. package/src/translation/translation-events.ts +73 -73
  215. package/src/translation/translation.ts +25 -25
  216. package/src/trending/trending-events.ts +21 -21
  217. package/src/trending/trending-hacks.ts +8 -8
  218. package/src/user/cognito-user-authentication-log.ts +7 -7
  219. package/src/user/get-user.ts +63 -63
  220. package/src/user/user-events.ts +39 -39
  221. package/src/user-acl/user-acl-utils.test.ts +273 -273
  222. package/src/user-acl/user-acl-utils.ts +176 -176
  223. package/src/user-context/user-context-events.ts +21 -21
  224. package/src/user-context/user-context.ts +15 -15
  225. package/src/user-notification-context/user-notification-context-events.ts +21 -21
  226. package/src/user-notification-context/user-notification-context.ts +9 -9
  227. package/src/user-statistics/user-statistics-events.ts +97 -97
  228. package/src/user-statistics/user-statistics.ts +82 -82
  229. package/src/video-cropping/video-cropping-events.ts +43 -43
  230. package/src/video-cropping/video-metadata.ts +12 -12
  231. package/tsconfig.cjs.json +7 -7
  232. package/tsconfig.npm.json +22 -22
@@ -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
+ });