@atproto/lex-schema 0.1.5 → 0.1.6

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 (263) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/core/$type.d.ts +2 -2
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js.map +1 -1
  5. package/dist/core/record-key.d.ts +1 -1
  6. package/dist/core/record-key.d.ts.map +1 -1
  7. package/dist/core/record-key.js.map +1 -1
  8. package/dist/core/schema.d.ts +3 -2
  9. package/dist/core/schema.d.ts.map +1 -1
  10. package/dist/core/schema.js +1 -1
  11. package/dist/core/schema.js.map +1 -1
  12. package/dist/core/standard-schema.d.ts +2 -2
  13. package/dist/core/standard-schema.d.ts.map +1 -1
  14. package/dist/core/standard-schema.js.map +1 -1
  15. package/dist/core/string-format.d.ts +2 -2
  16. package/dist/core/string-format.d.ts.map +1 -1
  17. package/dist/core/string-format.js.map +1 -1
  18. package/dist/core/validation-error.d.ts +1 -1
  19. package/dist/core/validation-error.d.ts.map +1 -1
  20. package/dist/core/validation-error.js +1 -1
  21. package/dist/core/validation-error.js.map +1 -1
  22. package/dist/core/validator.d.ts +1 -1
  23. package/dist/core/validator.d.ts.map +1 -1
  24. package/dist/core/validator.js +1 -1
  25. package/dist/core/validator.js.map +1 -1
  26. package/dist/helpers.d.ts +2 -2
  27. package/dist/helpers.d.ts.map +1 -1
  28. package/dist/helpers.js +2 -2
  29. package/dist/helpers.js.map +1 -1
  30. package/dist/schema/array.d.ts +1 -1
  31. package/dist/schema/array.d.ts.map +1 -1
  32. package/dist/schema/array.js +1 -1
  33. package/dist/schema/array.js.map +1 -1
  34. package/dist/schema/blob.d.ts +1 -1
  35. package/dist/schema/blob.d.ts.map +1 -1
  36. package/dist/schema/blob.js +2 -2
  37. package/dist/schema/blob.js.map +1 -1
  38. package/dist/schema/boolean.js +1 -1
  39. package/dist/schema/boolean.js.map +1 -1
  40. package/dist/schema/bytes.js +1 -1
  41. package/dist/schema/bytes.js.map +1 -1
  42. package/dist/schema/cid.d.ts +1 -1
  43. package/dist/schema/cid.d.ts.map +1 -1
  44. package/dist/schema/cid.js +3 -3
  45. package/dist/schema/cid.js.map +1 -1
  46. package/dist/schema/custom.js +1 -1
  47. package/dist/schema/custom.js.map +1 -1
  48. package/dist/schema/dict.d.ts +1 -1
  49. package/dist/schema/dict.d.ts.map +1 -1
  50. package/dist/schema/dict.js +1 -1
  51. package/dist/schema/dict.js.map +1 -1
  52. package/dist/schema/discriminated-union.d.ts +1 -1
  53. package/dist/schema/discriminated-union.d.ts.map +1 -1
  54. package/dist/schema/discriminated-union.js +2 -1
  55. package/dist/schema/discriminated-union.js.map +1 -1
  56. package/dist/schema/enum.js +1 -1
  57. package/dist/schema/enum.js.map +1 -1
  58. package/dist/schema/integer.js +1 -1
  59. package/dist/schema/integer.js.map +1 -1
  60. package/dist/schema/intersection.d.ts +1 -1
  61. package/dist/schema/intersection.d.ts.map +1 -1
  62. package/dist/schema/intersection.js +3 -1
  63. package/dist/schema/intersection.js.map +1 -1
  64. package/dist/schema/lex-map.d.ts +1 -1
  65. package/dist/schema/lex-map.d.ts.map +1 -1
  66. package/dist/schema/lex-map.js +1 -1
  67. package/dist/schema/lex-map.js.map +1 -1
  68. package/dist/schema/lex-value.d.ts +1 -1
  69. package/dist/schema/lex-value.d.ts.map +1 -1
  70. package/dist/schema/lex-value.js +1 -1
  71. package/dist/schema/lex-value.js.map +1 -1
  72. package/dist/schema/literal.js +1 -1
  73. package/dist/schema/literal.js.map +1 -1
  74. package/dist/schema/never.js +1 -1
  75. package/dist/schema/never.js.map +1 -1
  76. package/dist/schema/null.js +1 -1
  77. package/dist/schema/null.js.map +1 -1
  78. package/dist/schema/nullable.d.ts +1 -1
  79. package/dist/schema/nullable.d.ts.map +1 -1
  80. package/dist/schema/nullable.js +1 -1
  81. package/dist/schema/nullable.js.map +1 -1
  82. package/dist/schema/object.d.ts +2 -1
  83. package/dist/schema/object.d.ts.map +1 -1
  84. package/dist/schema/object.js +1 -1
  85. package/dist/schema/object.js.map +1 -1
  86. package/dist/schema/optional.d.ts +2 -1
  87. package/dist/schema/optional.d.ts.map +1 -1
  88. package/dist/schema/optional.js +2 -1
  89. package/dist/schema/optional.js.map +1 -1
  90. package/dist/schema/params.d.ts +1 -1
  91. package/dist/schema/params.d.ts.map +1 -1
  92. package/dist/schema/params.js +1 -1
  93. package/dist/schema/params.js.map +1 -1
  94. package/dist/schema/payload.d.ts +3 -2
  95. package/dist/schema/payload.d.ts.map +1 -1
  96. package/dist/schema/payload.js +2 -1
  97. package/dist/schema/payload.js.map +1 -1
  98. package/dist/schema/permission-set.d.ts +1 -1
  99. package/dist/schema/permission-set.d.ts.map +1 -1
  100. package/dist/schema/permission-set.js +1 -0
  101. package/dist/schema/permission-set.js.map +1 -1
  102. package/dist/schema/permission.d.ts +1 -1
  103. package/dist/schema/permission.d.ts.map +1 -1
  104. package/dist/schema/permission.js.map +1 -1
  105. package/dist/schema/procedure.d.ts +1 -1
  106. package/dist/schema/procedure.d.ts.map +1 -1
  107. package/dist/schema/procedure.js +2 -0
  108. package/dist/schema/procedure.js.map +1 -1
  109. package/dist/schema/query.d.ts +1 -1
  110. package/dist/schema/query.d.ts.map +1 -1
  111. package/dist/schema/query.js +2 -0
  112. package/dist/schema/query.js.map +1 -1
  113. package/dist/schema/record.d.ts +2 -2
  114. package/dist/schema/record.d.ts.map +1 -1
  115. package/dist/schema/record.js +1 -1
  116. package/dist/schema/record.js.map +1 -1
  117. package/dist/schema/ref.d.ts +1 -1
  118. package/dist/schema/ref.d.ts.map +1 -1
  119. package/dist/schema/ref.js +1 -1
  120. package/dist/schema/ref.js.map +1 -1
  121. package/dist/schema/refine.d.ts +2 -2
  122. package/dist/schema/refine.d.ts.map +1 -1
  123. package/dist/schema/refine.js +1 -1
  124. package/dist/schema/refine.js.map +1 -1
  125. package/dist/schema/regexp.js +1 -1
  126. package/dist/schema/regexp.js.map +1 -1
  127. package/dist/schema/string.d.ts +2 -2
  128. package/dist/schema/string.d.ts.map +1 -1
  129. package/dist/schema/string.js +1 -1
  130. package/dist/schema/string.js.map +1 -1
  131. package/dist/schema/subscription.d.ts +3 -2
  132. package/dist/schema/subscription.d.ts.map +1 -1
  133. package/dist/schema/subscription.js +2 -0
  134. package/dist/schema/subscription.js.map +1 -1
  135. package/dist/schema/token.d.ts +1 -1
  136. package/dist/schema/token.d.ts.map +1 -1
  137. package/dist/schema/token.js +1 -1
  138. package/dist/schema/token.js.map +1 -1
  139. package/dist/schema/typed-object.d.ts +2 -2
  140. package/dist/schema/typed-object.d.ts.map +1 -1
  141. package/dist/schema/typed-object.js +1 -1
  142. package/dist/schema/typed-object.js.map +1 -1
  143. package/dist/schema/typed-ref.d.ts +1 -1
  144. package/dist/schema/typed-ref.d.ts.map +1 -1
  145. package/dist/schema/typed-ref.js +1 -1
  146. package/dist/schema/typed-ref.js.map +1 -1
  147. package/dist/schema/typed-union.d.ts +1 -1
  148. package/dist/schema/typed-union.d.ts.map +1 -1
  149. package/dist/schema/typed-union.js +3 -1
  150. package/dist/schema/typed-union.js.map +1 -1
  151. package/dist/schema/union.d.ts +1 -1
  152. package/dist/schema/union.d.ts.map +1 -1
  153. package/dist/schema/union.js +1 -1
  154. package/dist/schema/union.js.map +1 -1
  155. package/dist/schema/unknown.js +1 -1
  156. package/dist/schema/unknown.js.map +1 -1
  157. package/dist/schema/with-default.d.ts +1 -1
  158. package/dist/schema/with-default.d.ts.map +1 -1
  159. package/dist/schema/with-default.js +1 -1
  160. package/dist/schema/with-default.js.map +1 -1
  161. package/package.json +6 -10
  162. package/src/core/$type.test.ts +0 -24
  163. package/src/core/$type.ts +0 -199
  164. package/src/core/record-key.ts +0 -85
  165. package/src/core/result.ts +0 -15
  166. package/src/core/schema.ts +0 -412
  167. package/src/core/standard-schema.test.ts +0 -124
  168. package/src/core/standard-schema.ts +0 -31
  169. package/src/core/string-format.ts +0 -411
  170. package/src/core/types.ts +0 -120
  171. package/src/core/validation-error.ts +0 -134
  172. package/src/core/validation-issue.ts +0 -340
  173. package/src/core/validator.ts +0 -636
  174. package/src/core.ts +0 -9
  175. package/src/external.ts +0 -3
  176. package/src/helpers.test.ts +0 -694
  177. package/src/helpers.ts +0 -222
  178. package/src/index.ts +0 -3
  179. package/src/schema/array.test.ts +0 -251
  180. package/src/schema/array.ts +0 -126
  181. package/src/schema/blob.test.ts +0 -733
  182. package/src/schema/blob.ts +0 -150
  183. package/src/schema/boolean.test.ts +0 -118
  184. package/src/schema/boolean.ts +0 -46
  185. package/src/schema/bytes.test.ts +0 -227
  186. package/src/schema/bytes.ts +0 -81
  187. package/src/schema/cid.test.ts +0 -125
  188. package/src/schema/cid.ts +0 -69
  189. package/src/schema/custom.test.ts +0 -414
  190. package/src/schema/custom.ts +0 -106
  191. package/src/schema/dict.test.ts +0 -181
  192. package/src/schema/dict.ts +0 -122
  193. package/src/schema/discriminated-union.test.ts +0 -676
  194. package/src/schema/discriminated-union.ts +0 -196
  195. package/src/schema/enum.test.ts +0 -398
  196. package/src/schema/enum.ts +0 -77
  197. package/src/schema/integer.test.ts +0 -314
  198. package/src/schema/integer.ts +0 -86
  199. package/src/schema/intersection.test.ts +0 -33
  200. package/src/schema/intersection.ts +0 -113
  201. package/src/schema/lex-map.test.ts +0 -593
  202. package/src/schema/lex-map.ts +0 -63
  203. package/src/schema/lex-value.test.ts +0 -81
  204. package/src/schema/lex-value.ts +0 -86
  205. package/src/schema/literal.test.ts +0 -533
  206. package/src/schema/literal.ts +0 -70
  207. package/src/schema/never.test.ts +0 -175
  208. package/src/schema/never.ts +0 -56
  209. package/src/schema/null.test.ts +0 -80
  210. package/src/schema/null.ts +0 -49
  211. package/src/schema/nullable.test.ts +0 -470
  212. package/src/schema/nullable.ts +0 -74
  213. package/src/schema/object.test.ts +0 -69
  214. package/src/schema/object.ts +0 -136
  215. package/src/schema/optional.test.ts +0 -479
  216. package/src/schema/optional.ts +0 -92
  217. package/src/schema/params.test.ts +0 -1118
  218. package/src/schema/params.ts +0 -371
  219. package/src/schema/payload.test.ts +0 -340
  220. package/src/schema/payload.ts +0 -204
  221. package/src/schema/permission-set.test.ts +0 -613
  222. package/src/schema/permission-set.ts +0 -86
  223. package/src/schema/permission.test.ts +0 -537
  224. package/src/schema/permission.ts +0 -63
  225. package/src/schema/procedure.test.ts +0 -324
  226. package/src/schema/procedure.ts +0 -98
  227. package/src/schema/query.test.ts +0 -348
  228. package/src/schema/query.ts +0 -86
  229. package/src/schema/record.test.ts +0 -812
  230. package/src/schema/record.ts +0 -217
  231. package/src/schema/ref.test.ts +0 -349
  232. package/src/schema/ref.ts +0 -103
  233. package/src/schema/refine.test.ts +0 -579
  234. package/src/schema/refine.ts +0 -153
  235. package/src/schema/regexp.test.ts +0 -577
  236. package/src/schema/regexp.ts +0 -82
  237. package/src/schema/string.test.ts +0 -773
  238. package/src/schema/string.ts +0 -229
  239. package/src/schema/subscription.test.ts +0 -499
  240. package/src/schema/subscription.ts +0 -108
  241. package/src/schema/token.test.ts +0 -152
  242. package/src/schema/token.ts +0 -103
  243. package/src/schema/typed-object.test.ts +0 -745
  244. package/src/schema/typed-object.ts +0 -181
  245. package/src/schema/typed-ref.test.ts +0 -796
  246. package/src/schema/typed-ref.ts +0 -126
  247. package/src/schema/typed-union.test.ts +0 -355
  248. package/src/schema/typed-union.ts +0 -130
  249. package/src/schema/union.test.ts +0 -191
  250. package/src/schema/union.ts +0 -89
  251. package/src/schema/unknown.test.ts +0 -313
  252. package/src/schema/unknown.ts +0 -47
  253. package/src/schema/with-default.ts +0 -81
  254. package/src/schema.ts +0 -43
  255. package/src/util/array-agg.test.ts +0 -42
  256. package/src/util/array-agg.ts +0 -44
  257. package/src/util/assertion-util.ts +0 -1
  258. package/src/util/if-any.ts +0 -3
  259. package/src/util/lazy-property.ts +0 -14
  260. package/src/util/memoize.ts +0 -37
  261. package/tsconfig.build.json +0 -12
  262. package/tsconfig.json +0 -7
  263. package/tsconfig.tests.json +0 -8
@@ -1,613 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { PermissionSet, permissionSet } from './permission-set.js'
3
- import { Permission, permission } from './permission.js'
4
-
5
- describe('PermissionSet', () => {
6
- describe('constructor', () => {
7
- it('creates a PermissionSet instance with all parameters', () => {
8
- const nsid = 'app.bsky.oauth.permissions'
9
- const permissions = [
10
- permission('app.bsky.feed.post:read', {}),
11
- permission('app.bsky.feed.post:write', {}),
12
- ] as const
13
- const options = {
14
- title: 'Post Management',
15
- detail: 'Allows reading and writing posts',
16
- }
17
-
18
- const perms = permissionSet(nsid, permissions, options)
19
-
20
- expect(perms).toBeInstanceOf(PermissionSet)
21
- expect(perms.nsid).toBe(nsid)
22
- expect(perms.permissions).toBe(permissions)
23
- expect(perms.options).toBe(options)
24
- })
25
-
26
- it('creates a PermissionSet instance with minimal options', () => {
27
- const nsid = 'app.bsky.oauth.permissions'
28
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
29
- const options = {}
30
-
31
- const perms = permissionSet(nsid, permissions, options)
32
-
33
- expect(perms).toBeInstanceOf(PermissionSet)
34
- expect(perms.nsid).toBe(nsid)
35
- expect(perms.permissions).toBe(permissions)
36
- expect(perms.options).toEqual({})
37
- })
38
-
39
- it('creates a PermissionSet instance with empty permissions array', () => {
40
- const nsid = 'app.bsky.oauth.permissions'
41
- const permissions = [] as const
42
- const options = {}
43
-
44
- const perms = permissionSet(nsid, permissions, options)
45
-
46
- expect(perms).toBeInstanceOf(PermissionSet)
47
- expect(perms.permissions).toEqual([])
48
- })
49
-
50
- it('creates a PermissionSet instance with title only', () => {
51
- const nsid = 'app.bsky.oauth.permissions'
52
- const permissions = [permission('app.bsky.feed.like:read', {})] as const
53
- const options = {
54
- title: 'Like Management',
55
- }
56
-
57
- const perms = permissionSet(nsid, permissions, options)
58
-
59
- expect(perms.options.title).toBe('Like Management')
60
- expect(perms.options.detail).toBeUndefined()
61
- })
62
-
63
- it('creates a PermissionSet instance with detail only', () => {
64
- const nsid = 'app.bsky.oauth.permissions'
65
- const permissions = [permission('app.bsky.feed.like:read', {})] as const
66
- const options = {
67
- detail: 'Allows reading likes on posts',
68
- }
69
-
70
- const perms = permissionSet(nsid, permissions, options)
71
-
72
- expect(perms.options.title).toBeUndefined()
73
- expect(perms.options.detail).toBe('Allows reading likes on posts')
74
- })
75
-
76
- it('creates a PermissionSet instance with localized titles', () => {
77
- const nsid = 'app.bsky.oauth.permissions'
78
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
79
- const options = {
80
- title: 'Post Management',
81
- 'title:lang': {
82
- es: 'Gestión de Publicaciones',
83
- fr: 'Gestion des Publications',
84
- },
85
- }
86
-
87
- const perms = permissionSet(nsid, permissions, options)
88
-
89
- expect(perms.options.title).toBe('Post Management')
90
- expect(perms.options['title:lang']).toEqual({
91
- es: 'Gestión de Publicaciones',
92
- fr: 'Gestion des Publications',
93
- })
94
- })
95
-
96
- it('creates a PermissionSet instance with localized details', () => {
97
- const nsid = 'app.bsky.oauth.permissions'
98
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
99
- const options = {
100
- detail: 'Allows reading posts',
101
- 'detail:lang': {
102
- es: 'Permite leer publicaciones',
103
- fr: 'Permet de lire les publications',
104
- },
105
- }
106
-
107
- const perms = permissionSet(nsid, permissions, options)
108
-
109
- expect(perms.options.detail).toBe('Allows reading posts')
110
- expect(perms.options['detail:lang']).toEqual({
111
- es: 'Permite leer publicaciones',
112
- fr: 'Permet de lire les publications',
113
- })
114
- })
115
-
116
- it('creates a PermissionSet instance with all options including localization', () => {
117
- const nsid = 'app.bsky.oauth.permissions'
118
- const permissions = [
119
- permission('app.bsky.feed.post:read', {}),
120
- permission('app.bsky.feed.post:write', {}),
121
- ] as const
122
- const options = {
123
- title: 'Post Management',
124
- 'title:lang': {
125
- es: 'Gestión de Publicaciones',
126
- fr: 'Gestion des Publications',
127
- de: 'Beitragsverwaltung',
128
- },
129
- detail: 'Allows reading and writing posts',
130
- 'detail:lang': {
131
- es: 'Permite leer y escribir publicaciones',
132
- fr: 'Permet de lire et écrire les publications',
133
- de: 'Ermöglicht das Lesen und Schreiben von Beiträgen',
134
- },
135
- }
136
-
137
- const perms = permissionSet(nsid, permissions, options)
138
-
139
- expect(perms.options.title).toBe('Post Management')
140
- expect(perms.options['title:lang']).toEqual({
141
- es: 'Gestión de Publicaciones',
142
- fr: 'Gestion des Publications',
143
- de: 'Beitragsverwaltung',
144
- })
145
- expect(perms.options.detail).toBe('Allows reading and writing posts')
146
- expect(perms.options['detail:lang']).toEqual({
147
- es: 'Permite leer y escribir publicaciones',
148
- fr: 'Permet de lire et écrire les publications',
149
- de: 'Ermöglicht das Lesen und Schreiben von Beiträgen',
150
- })
151
- })
152
- })
153
-
154
- describe('property immutability', () => {
155
- it('options object itself is mutable', () => {
156
- const nsid = 'app.bsky.oauth.permissions'
157
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
158
- const options = { title: 'Test' }
159
-
160
- const perms = permissionSet(nsid, permissions, options)
161
-
162
- // The reference is readonly, but the object itself can be mutated
163
- options.title = 'Updated Title'
164
- expect(perms.options.title).toBe('Updated Title')
165
- })
166
- })
167
-
168
- describe('with multiple permissions', () => {
169
- it('creates a PermissionSet with multiple read permissions', () => {
170
- const nsid = 'app.bsky.oauth.read'
171
- const permissions = [
172
- permission('app.bsky.feed.post:read', {}),
173
- permission('app.bsky.feed.like:read', {}),
174
- permission('app.bsky.feed.repost:read', {}),
175
- permission('app.bsky.graph.follow:read', {}),
176
- ] as const
177
- const options = {
178
- title: 'Read Access',
179
- detail: 'Allows reading various resources',
180
- }
181
-
182
- const perms = permissionSet(nsid, permissions, options)
183
-
184
- expect(perms.permissions).toHaveLength(4)
185
- expect(perms.permissions[0].resource).toBe('app.bsky.feed.post:read')
186
- expect(perms.permissions[1].resource).toBe('app.bsky.feed.like:read')
187
- expect(perms.permissions[2].resource).toBe('app.bsky.feed.repost:read')
188
- expect(perms.permissions[3].resource).toBe('app.bsky.graph.follow:read')
189
- })
190
-
191
- it('creates a PermissionSet with mixed read/write permissions', () => {
192
- const nsid = 'app.bsky.oauth.full'
193
- const permissions = [
194
- permission('app.bsky.feed.post:read', {}),
195
- permission('app.bsky.feed.post:write', {}),
196
- permission('app.bsky.feed.like:read', {}),
197
- permission('app.bsky.feed.like:write', {}),
198
- ] as const
199
- const options = {
200
- title: 'Full Access',
201
- detail: 'Allows reading and writing posts and likes',
202
- }
203
-
204
- const perms = permissionSet(nsid, permissions, options)
205
-
206
- expect(perms.permissions).toHaveLength(4)
207
- })
208
-
209
- it('creates a PermissionSet with a single permission', () => {
210
- const nsid = 'app.bsky.oauth.limited'
211
- const permissions = [
212
- permission('app.bsky.actor.profile:read', {}),
213
- ] as const
214
- const options = {
215
- title: 'Profile Read',
216
- detail: 'Allows reading user profiles only',
217
- }
218
-
219
- const perms = permissionSet(nsid, permissions, options)
220
-
221
- expect(perms.permissions).toHaveLength(1)
222
- expect(perms.permissions[0].resource).toBe('app.bsky.actor.profile:read')
223
- })
224
- })
225
-
226
- describe('edge cases', () => {
227
- it('handles very long NSID', () => {
228
- const nsid =
229
- 'com.example.very.long.namespace.identifier.oauth.permissions'
230
- const permissions = [permission('resource:action', {})] as const
231
- const options = {}
232
-
233
- const perms = permissionSet(nsid, permissions, options)
234
-
235
- expect(perms.nsid).toBe(nsid)
236
- })
237
-
238
- it('handles long title strings', () => {
239
- const nsid = 'app.bsky.oauth.permissions'
240
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
241
- const longTitle = 'A'.repeat(500)
242
- const options = {
243
- title: longTitle,
244
- }
245
-
246
- const perms = permissionSet(nsid, permissions, options)
247
-
248
- expect(perms.options.title).toBe(longTitle)
249
- expect(perms.options.title?.length).toBe(500)
250
- })
251
-
252
- it('handles long detail strings', () => {
253
- const nsid = 'app.bsky.oauth.permissions'
254
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
255
- const longDetail = 'B'.repeat(1000)
256
- const options = {
257
- detail: longDetail,
258
- }
259
-
260
- const perms = permissionSet(nsid, permissions, options)
261
-
262
- expect(perms.options.detail).toBe(longDetail)
263
- expect(perms.options.detail?.length).toBe(1000)
264
- })
265
-
266
- it('handles multiple language codes in title:lang', () => {
267
- const nsid = 'app.bsky.oauth.permissions'
268
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
269
- const options = {
270
- title: 'Post Management',
271
- 'title:lang': {
272
- es: 'Gestión de Publicaciones',
273
- fr: 'Gestion des Publications',
274
- de: 'Beitragsverwaltung',
275
- it: 'Gestione dei Post',
276
- pt: 'Gerenciamento de Postagens',
277
- ja: '投稿管理',
278
- ko: '게시물 관리',
279
- 'zh-CN': '帖子管理',
280
- },
281
- }
282
-
283
- const perms = permissionSet(nsid, permissions, options)
284
-
285
- expect(Object.keys(perms.options['title:lang'] || {})).toHaveLength(8)
286
- })
287
-
288
- it('handles undefined values in title:lang', () => {
289
- const nsid = 'app.bsky.oauth.permissions'
290
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
291
- const options = {
292
- title: 'Post Management',
293
- 'title:lang': {
294
- es: 'Gestión de Publicaciones',
295
- fr: undefined,
296
- de: 'Beitragsverwaltung',
297
- },
298
- }
299
-
300
- const perms = permissionSet(nsid, permissions, options)
301
-
302
- expect(perms.options['title:lang']?.es).toBe('Gestión de Publicaciones')
303
- expect(perms.options['title:lang']?.fr).toBeUndefined()
304
- expect(perms.options['title:lang']?.de).toBe('Beitragsverwaltung')
305
- })
306
-
307
- it('handles undefined values in detail:lang', () => {
308
- const nsid = 'app.bsky.oauth.permissions'
309
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
310
- const options = {
311
- detail: 'Allows reading posts',
312
- 'detail:lang': {
313
- es: undefined,
314
- fr: 'Permet de lire les publications',
315
- },
316
- }
317
-
318
- const perms = permissionSet(nsid, permissions, options)
319
-
320
- expect(perms.options['detail:lang']?.es).toBeUndefined()
321
- expect(perms.options['detail:lang']?.fr).toBe(
322
- 'Permet de lire les publications',
323
- )
324
- })
325
-
326
- it('handles special characters in title', () => {
327
- const nsid = 'app.bsky.oauth.permissions'
328
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
329
- const options = {
330
- title: 'Post Management: Read & Write (Full Access)',
331
- }
332
-
333
- const perms = permissionSet(nsid, permissions, options)
334
-
335
- expect(perms.options.title).toBe(
336
- 'Post Management: Read & Write (Full Access)',
337
- )
338
- })
339
-
340
- it('handles special characters in detail', () => {
341
- const nsid = 'app.bsky.oauth.permissions'
342
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
343
- const options = {
344
- detail:
345
- 'Allows reading posts, likes & reposts (includes all sub-resources)',
346
- }
347
-
348
- const perms = permissionSet(nsid, permissions, options)
349
-
350
- expect(perms.options.detail).toBe(
351
- 'Allows reading posts, likes & reposts (includes all sub-resources)',
352
- )
353
- })
354
-
355
- it('handles unicode characters in title', () => {
356
- const nsid = 'app.bsky.oauth.permissions'
357
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
358
- const options = {
359
- title: '投稿管理 📝',
360
- }
361
-
362
- const perms = permissionSet(nsid, permissions, options)
363
-
364
- expect(perms.options.title).toBe('投稿管理 📝')
365
- })
366
-
367
- it('handles empty strings in title', () => {
368
- const nsid = 'app.bsky.oauth.permissions'
369
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
370
- const options = {
371
- title: '',
372
- }
373
-
374
- const perms = permissionSet(nsid, permissions, options)
375
-
376
- expect(perms.options.title).toBe('')
377
- })
378
-
379
- it('handles empty strings in detail', () => {
380
- const nsid = 'app.bsky.oauth.permissions'
381
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
382
- const options = {
383
- detail: '',
384
- }
385
-
386
- const perms = permissionSet(nsid, permissions, options)
387
-
388
- expect(perms.options.detail).toBe('')
389
- })
390
-
391
- it('handles large number of permissions', () => {
392
- const nsid = 'app.bsky.oauth.permissions'
393
- const permissions = Array.from({ length: 100 }, (_, i) =>
394
- permission(`resource${i}:action`, {}),
395
- ) as any
396
- const options = {}
397
-
398
- const perms = permissionSet(nsid, permissions, options)
399
-
400
- expect(perms.permissions).toHaveLength(100)
401
- })
402
- })
403
-
404
- describe('real-world permission set examples', () => {
405
- it('creates a feed management permission set', () => {
406
- const nsid = 'app.bsky.oauth.feed'
407
- const permissions = [
408
- permission('app.bsky.feed.post:read', {}),
409
- permission('app.bsky.feed.post:write', {}),
410
- permission('app.bsky.feed.like:read', {}),
411
- permission('app.bsky.feed.like:write', {}),
412
- permission('app.bsky.feed.repost:read', {}),
413
- permission('app.bsky.feed.repost:write', {}),
414
- ] as const
415
- const options = {
416
- title: 'Feed Management',
417
- 'title:lang': {
418
- es: 'Gestión de Feed',
419
- fr: 'Gestion du Feed',
420
- },
421
- detail: 'Full access to manage posts, likes, and reposts in your feed',
422
- 'detail:lang': {
423
- es: 'Acceso completo para gestionar publicaciones, me gusta y reposts en tu feed',
424
- fr: 'Accès complet pour gérer les publications, les likes et les reposts dans votre fil',
425
- },
426
- }
427
-
428
- const perms = permissionSet(nsid, permissions, options)
429
-
430
- expect(perms.nsid).toBe('app.bsky.oauth.feed')
431
- expect(perms.permissions).toHaveLength(6)
432
- expect(perms.options.title).toBe('Feed Management')
433
- })
434
-
435
- it('creates a read-only permission set', () => {
436
- const nsid = 'app.bsky.oauth.readonly'
437
- const permissions = [
438
- permission('app.bsky.feed.post:read', {}),
439
- permission('app.bsky.feed.like:read', {}),
440
- permission('app.bsky.actor.profile:read', {}),
441
- permission('app.bsky.graph.follow:read', {}),
442
- ] as const
443
- const options = {
444
- title: 'Read-Only Access',
445
- detail: 'View posts, likes, profiles, and follows without modification',
446
- }
447
-
448
- const perms = permissionSet(nsid, permissions, options)
449
-
450
- expect(perms.nsid).toBe('app.bsky.oauth.readonly')
451
- expect(perms.permissions.every((p) => p.resource.endsWith(':read'))).toBe(
452
- true,
453
- )
454
- })
455
-
456
- it('creates a profile management permission set', () => {
457
- const nsid = 'app.bsky.oauth.profile'
458
- const permissions = [
459
- permission('app.bsky.actor.profile:read', {}),
460
- permission('app.bsky.actor.profile:write', {}),
461
- ] as const
462
- const options = {
463
- title: 'Profile Management',
464
- 'title:lang': {
465
- es: 'Gestión de Perfil',
466
- fr: 'Gestion du Profil',
467
- de: 'Profilverwaltung',
468
- },
469
- detail: 'Read and update your profile information',
470
- 'detail:lang': {
471
- es: 'Leer y actualizar la información de tu perfil',
472
- fr: 'Lire et mettre à jour les informations de votre profil',
473
- de: 'Profilinformationen lesen und aktualisieren',
474
- },
475
- }
476
-
477
- const perms = permissionSet(nsid, permissions, options)
478
-
479
- expect(perms.nsid).toBe('app.bsky.oauth.profile')
480
- expect(perms.permissions).toHaveLength(2)
481
- })
482
-
483
- it('creates a minimal permission set', () => {
484
- const nsid = 'app.bsky.oauth.minimal'
485
- const permissions = [
486
- permission('app.bsky.actor.profile:read', {}),
487
- ] as const
488
- const options = {
489
- title: 'Basic Access',
490
- }
491
-
492
- const perms = permissionSet(nsid, permissions, options)
493
-
494
- expect(perms.nsid).toBe('app.bsky.oauth.minimal')
495
- expect(perms.permissions).toHaveLength(1)
496
- expect(perms.options.detail).toBeUndefined()
497
- })
498
- })
499
-
500
- describe('permission validation', () => {
501
- it('validates that permissions are Permission instances', () => {
502
- const nsid = 'app.bsky.oauth.permissions'
503
- const permission1 = permission('app.bsky.feed.post:read', {})
504
- const permission2 = permission('app.bsky.feed.post:write', {})
505
- const permissions = [permission1, permission2] as const
506
- const options = {}
507
-
508
- const perms = permissionSet(nsid, permissions, options)
509
-
510
- expect(perms.permissions[0]).toBeInstanceOf(Permission)
511
- expect(perms.permissions[1]).toBeInstanceOf(Permission)
512
- })
513
-
514
- it('preserves permission resource strings', () => {
515
- const nsid = 'app.bsky.oauth.permissions'
516
- const permissions = [
517
- permission('app.bsky.feed.post:read', {}),
518
- permission('app.bsky.feed.like:write', {}),
519
- permission('app.bsky.graph.follow:read', {}),
520
- ] as const
521
- const options = {}
522
-
523
- const perms = permissionSet(nsid, permissions, options)
524
-
525
- expect(perms.permissions[0].resource).toBe('app.bsky.feed.post:read')
526
- expect(perms.permissions[1].resource).toBe('app.bsky.feed.like:write')
527
- expect(perms.permissions[2].resource).toBe('app.bsky.graph.follow:read')
528
- })
529
-
530
- it('preserves permission options', () => {
531
- const nsid = 'app.bsky.oauth.permissions'
532
- const permissionOptions = { custom: 'value' }
533
- const permissions = [
534
- permission('app.bsky.feed.post:read', permissionOptions),
535
- ] as const
536
- const options = {}
537
-
538
- const perms = permissionSet(nsid, permissions, options)
539
-
540
- expect(perms.permissions[0].options).toBe(permissionOptions)
541
- })
542
- })
543
-
544
- describe('option variations', () => {
545
- it('accepts title without detail', () => {
546
- const nsid = 'app.bsky.oauth.permissions'
547
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
548
- const options = {
549
- title: 'Post Reading',
550
- }
551
-
552
- const perms = permissionSet(nsid, permissions, options)
553
-
554
- expect(perms.options.title).toBe('Post Reading')
555
- expect(perms.options.detail).toBeUndefined()
556
- expect(perms.options['title:lang']).toBeUndefined()
557
- expect(perms.options['detail:lang']).toBeUndefined()
558
- })
559
-
560
- it('accepts detail without title', () => {
561
- const nsid = 'app.bsky.oauth.permissions'
562
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
563
- const options = {
564
- detail: 'Allows reading posts from the feed',
565
- }
566
-
567
- const perms = permissionSet(nsid, permissions, options)
568
-
569
- expect(perms.options.title).toBeUndefined()
570
- expect(perms.options.detail).toBe('Allows reading posts from the feed')
571
- expect(perms.options['title:lang']).toBeUndefined()
572
- expect(perms.options['detail:lang']).toBeUndefined()
573
- })
574
-
575
- it('accepts title:lang without title', () => {
576
- const nsid = 'app.bsky.oauth.permissions'
577
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
578
- const options = {
579
- 'title:lang': {
580
- es: 'Gestión de Publicaciones',
581
- fr: 'Gestion des Publications',
582
- },
583
- }
584
-
585
- const perms = permissionSet(nsid, permissions, options)
586
-
587
- expect(perms.options.title).toBeUndefined()
588
- expect(perms.options['title:lang']).toEqual({
589
- es: 'Gestión de Publicaciones',
590
- fr: 'Gestion des Publications',
591
- })
592
- })
593
-
594
- it('accepts detail:lang without detail', () => {
595
- const nsid = 'app.bsky.oauth.permissions'
596
- const permissions = [permission('app.bsky.feed.post:read', {})] as const
597
- const options = {
598
- 'detail:lang': {
599
- es: 'Permite leer publicaciones',
600
- fr: 'Permet de lire les publications',
601
- },
602
- }
603
-
604
- const perms = permissionSet(nsid, permissions, options)
605
-
606
- expect(perms.options.detail).toBeUndefined()
607
- expect(perms.options['detail:lang']).toEqual({
608
- es: 'Permite leer publicaciones',
609
- fr: 'Permet de lire les publications',
610
- })
611
- })
612
- })
613
- })
@@ -1,86 +0,0 @@
1
- import { NsidString } from '../core.js'
2
- import { Permission } from './permission.js'
3
-
4
- /**
5
- * Configuration options for a permission set.
6
- *
7
- * @property title - Human-readable title for the permission set
8
- * @property title:lang - Localized titles by language code
9
- * @property detail - Detailed description of the permission set
10
- * @property detail:lang - Localized descriptions by language code
11
- */
12
- export type PermissionSetOptions = {
13
- title?: string
14
- 'title:lang'?: Record<string, undefined | string>
15
- detail?: string
16
- 'detail:lang'?: Record<string, undefined | string>
17
- }
18
-
19
- /**
20
- * Represents a collection of related permissions in AT Protocol.
21
- *
22
- * Permission sets group permissions together with metadata for OAuth
23
- * authorization flows. They are identified by an NSID.
24
- *
25
- * @template TNsid - The NSID identifying this permission set
26
- * @template TPermissions - Tuple type of the included permissions
27
- *
28
- * @example
29
- * ```ts
30
- * const feedAccess = new PermissionSet(
31
- * 'app.bsky.feed.access',
32
- * [readPermission, writePermission],
33
- * { title: 'Feed Access', detail: 'Read and write to your feed' }
34
- * )
35
- * ```
36
- */
37
- export class PermissionSet<
38
- const TNsid extends NsidString = any,
39
- const TPermissions extends readonly Permission[] = any,
40
- > {
41
- constructor(
42
- readonly nsid: TNsid,
43
- readonly permissions: TPermissions,
44
- readonly options: PermissionSetOptions = {},
45
- ) {}
46
- }
47
-
48
- /**
49
- * Creates a permission set grouping related permissions.
50
- *
51
- * Permission sets define OAuth scopes that applications can request.
52
- * They include human-readable metadata for authorization UIs.
53
- *
54
- * @param nsid - The NSID identifying this permission set
55
- * @param permissions - Array of permissions included in this set
56
- * @param options - Optional metadata (title, detail, localization)
57
- * @returns A new {@link PermissionSet} instance
58
- *
59
- * @example
60
- * ```ts
61
- * // Define individual permissions
62
- * const readPosts = l.permission('read', { collection: 'app.bsky.feed.post' })
63
- * const writePosts = l.permission('write', { collection: 'app.bsky.feed.post' })
64
- *
65
- * // Group into a permission set
66
- * const postManagement = l.permissionSet(
67
- * 'app.bsky.feed.postManagement',
68
- * [readPosts, writePosts],
69
- * {
70
- * title: 'Post Management',
71
- * detail: 'View and create posts on your behalf',
72
- * 'title:lang': {
73
- * 'es': 'Gestion de publicaciones',
74
- * 'fr': 'Gestion des publications',
75
- * },
76
- * }
77
- * )
78
- * ```
79
- */
80
- /*@__NO_SIDE_EFFECTS__*/
81
- export function permissionSet<
82
- const N extends NsidString,
83
- const P extends readonly Permission[],
84
- >(nsid: N, permissions: P, options?: PermissionSetOptions) {
85
- return new PermissionSet<N, P>(nsid, permissions, options)
86
- }