@atproto/oauth-scopes 0.0.2 → 0.2.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 (169) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/atproto-oauth-scope.d.ts +17 -0
  3. package/dist/atproto-oauth-scope.d.ts.map +1 -0
  4. package/dist/atproto-oauth-scope.js +67 -0
  5. package/dist/atproto-oauth-scope.js.map +1 -0
  6. package/dist/index.d.ts +9 -13
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +9 -13
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/lexicon.d.ts +2 -0
  11. package/dist/lib/lexicon.d.ts.map +1 -0
  12. package/dist/lib/lexicon.js +3 -0
  13. package/dist/lib/lexicon.js.map +1 -0
  14. package/dist/lib/mime.d.ts +1 -1
  15. package/dist/lib/mime.d.ts.map +1 -1
  16. package/dist/lib/mime.js +2 -0
  17. package/dist/lib/mime.js.map +1 -1
  18. package/dist/lib/nsid.d.ts +2 -2
  19. package/dist/lib/nsid.d.ts.map +1 -1
  20. package/dist/lib/nsid.js +4 -6
  21. package/dist/lib/nsid.js.map +1 -1
  22. package/dist/lib/parser.d.ts +29 -0
  23. package/dist/lib/parser.d.ts.map +1 -0
  24. package/dist/lib/parser.js +152 -0
  25. package/dist/lib/parser.js.map +1 -0
  26. package/dist/lib/resource-permission.d.ts +10 -0
  27. package/dist/lib/resource-permission.d.ts.map +1 -0
  28. package/dist/lib/resource-permission.js +3 -0
  29. package/dist/lib/resource-permission.js.map +1 -0
  30. package/dist/lib/syntax-lexicon.d.ts +26 -0
  31. package/dist/lib/syntax-lexicon.d.ts.map +1 -0
  32. package/dist/lib/syntax-lexicon.js +58 -0
  33. package/dist/lib/syntax-lexicon.js.map +1 -0
  34. package/dist/lib/syntax-string.d.ts +16 -0
  35. package/dist/lib/syntax-string.d.ts.map +1 -0
  36. package/dist/lib/syntax-string.js +121 -0
  37. package/dist/lib/syntax-string.js.map +1 -0
  38. package/dist/lib/syntax.d.ts +23 -0
  39. package/dist/lib/syntax.d.ts.map +1 -0
  40. package/dist/lib/syntax.js +22 -0
  41. package/dist/lib/syntax.js.map +1 -0
  42. package/dist/lib/util.d.ts +5 -1
  43. package/dist/lib/util.d.ts.map +1 -1
  44. package/dist/lib/util.js +8 -12
  45. package/dist/lib/util.js.map +1 -1
  46. package/dist/scope-permissions-transition.d.ts +15 -0
  47. package/dist/scope-permissions-transition.d.ts.map +1 -0
  48. package/dist/{permission-set-transition.js → scope-permissions-transition.js} +5 -5
  49. package/dist/scope-permissions-transition.js.map +1 -0
  50. package/dist/scope-permissions.d.ts +22 -0
  51. package/dist/scope-permissions.d.ts.map +1 -0
  52. package/dist/{permission-set.js → scope-permissions.js} +20 -16
  53. package/dist/scope-permissions.js.map +1 -0
  54. package/dist/scopes/account-permission.d.ts +35 -0
  55. package/dist/scopes/account-permission.d.ts.map +1 -0
  56. package/dist/scopes/account-permission.js +71 -0
  57. package/dist/scopes/account-permission.js.map +1 -0
  58. package/dist/scopes/blob-permission.d.ts +27 -0
  59. package/dist/scopes/blob-permission.d.ts.map +1 -0
  60. package/dist/scopes/blob-permission.js +86 -0
  61. package/dist/scopes/blob-permission.js.map +1 -0
  62. package/dist/scopes/identity-permission.d.ts +25 -0
  63. package/dist/scopes/identity-permission.d.ts.map +1 -0
  64. package/dist/scopes/identity-permission.js +53 -0
  65. package/dist/scopes/identity-permission.js.map +1 -0
  66. package/dist/scopes/include-scope.d.ts +54 -0
  67. package/dist/scopes/include-scope.d.ts.map +1 -0
  68. package/dist/scopes/include-scope.js +156 -0
  69. package/dist/scopes/include-scope.js.map +1 -0
  70. package/dist/scopes/repo-permission.d.ts +40 -0
  71. package/dist/scopes/repo-permission.d.ts.map +1 -0
  72. package/dist/scopes/repo-permission.js +101 -0
  73. package/dist/scopes/repo-permission.js.map +1 -0
  74. package/dist/scopes/rpc-permission.d.ts +38 -0
  75. package/dist/scopes/rpc-permission.d.ts.map +1 -0
  76. package/dist/scopes/rpc-permission.js +81 -0
  77. package/dist/scopes/rpc-permission.js.map +1 -0
  78. package/dist/scopes-set.d.ts +12 -1
  79. package/dist/scopes-set.d.ts.map +1 -1
  80. package/dist/scopes-set.js +49 -3
  81. package/dist/scopes-set.js.map +1 -1
  82. package/package.json +7 -3
  83. package/src/atproto-oauth-scope.ts +79 -0
  84. package/src/index.ts +10 -14
  85. package/src/lib/lexicon.ts +1 -0
  86. package/src/lib/mime.ts +2 -1
  87. package/src/lib/nsid.ts +5 -6
  88. package/src/lib/parser.ts +176 -0
  89. package/src/lib/resource-permission.ts +10 -0
  90. package/src/lib/syntax-lexicon.ts +55 -0
  91. package/src/lib/syntax-string.test.ts +130 -0
  92. package/src/lib/syntax-string.ts +132 -0
  93. package/src/lib/syntax.test.ts +43 -0
  94. package/src/lib/syntax.ts +47 -0
  95. package/src/lib/util.ts +11 -12
  96. package/src/{permission-set-transition.test.ts → scope-permissions-transition.test.ts} +33 -20
  97. package/src/{permission-set-transition.ts → scope-permissions-transition.ts} +11 -11
  98. package/src/{permission-set.test.ts → scope-permissions.test.ts} +77 -35
  99. package/src/scope-permissions.ts +91 -0
  100. package/src/{resources/account-scope.test.ts → scopes/account-permission.test.ts} +45 -33
  101. package/src/scopes/account-permission.ts +75 -0
  102. package/src/{resources/blob-scope.test.ts → scopes/blob-permission.test.ts} +31 -23
  103. package/src/scopes/blob-permission.ts +105 -0
  104. package/src/{resources/identity-scope.test.ts → scopes/identity-permission.test.ts} +13 -13
  105. package/src/scopes/identity-permission.ts +54 -0
  106. package/src/scopes/include-scope.test.ts +626 -0
  107. package/src/scopes/include-scope.ts +168 -0
  108. package/src/{resources/repo-scope.test.ts → scopes/repo-permission.test.ts} +77 -65
  109. package/src/scopes/repo-permission.ts +111 -0
  110. package/src/scopes/rpc-permission.test.ts +323 -0
  111. package/src/scopes/rpc-permission.ts +85 -0
  112. package/src/scopes-set.test.ts +5 -5
  113. package/src/scopes-set.ts +79 -5
  114. package/tsconfig.build.tsbuildinfo +1 -1
  115. package/tsconfig.tests.tsbuildinfo +1 -1
  116. package/dist/lib/did.d.ts +0 -3
  117. package/dist/lib/did.d.ts.map +0 -1
  118. package/dist/lib/did.js +0 -6
  119. package/dist/lib/did.js.map +0 -1
  120. package/dist/parser.d.ts +0 -31
  121. package/dist/parser.d.ts.map +0 -1
  122. package/dist/parser.js +0 -118
  123. package/dist/parser.js.map +0 -1
  124. package/dist/permission-set-transition.d.ts +0 -15
  125. package/dist/permission-set-transition.d.ts.map +0 -1
  126. package/dist/permission-set-transition.js.map +0 -1
  127. package/dist/permission-set.d.ts +0 -22
  128. package/dist/permission-set.d.ts.map +0 -1
  129. package/dist/permission-set.js.map +0 -1
  130. package/dist/resources/account-scope.d.ts +0 -35
  131. package/dist/resources/account-scope.d.ts.map +0 -1
  132. package/dist/resources/account-scope.js +0 -60
  133. package/dist/resources/account-scope.js.map +0 -1
  134. package/dist/resources/blob-scope.d.ts +0 -25
  135. package/dist/resources/blob-scope.d.ts.map +0 -1
  136. package/dist/resources/blob-scope.js +0 -74
  137. package/dist/resources/blob-scope.js.map +0 -1
  138. package/dist/resources/identity-scope.d.ts +0 -25
  139. package/dist/resources/identity-scope.d.ts.map +0 -1
  140. package/dist/resources/identity-scope.js +0 -46
  141. package/dist/resources/identity-scope.js.map +0 -1
  142. package/dist/resources/repo-scope.d.ts +0 -37
  143. package/dist/resources/repo-scope.d.ts.map +0 -1
  144. package/dist/resources/repo-scope.js +0 -92
  145. package/dist/resources/repo-scope.js.map +0 -1
  146. package/dist/resources/rpc-scope.d.ts +0 -31
  147. package/dist/resources/rpc-scope.d.ts.map +0 -1
  148. package/dist/resources/rpc-scope.js +0 -74
  149. package/dist/resources/rpc-scope.js.map +0 -1
  150. package/dist/syntax.d.ts +0 -76
  151. package/dist/syntax.d.ts.map +0 -1
  152. package/dist/syntax.js +0 -249
  153. package/dist/syntax.js.map +0 -1
  154. package/dist/utilities.d.ts +0 -17
  155. package/dist/utilities.d.ts.map +0 -1
  156. package/dist/utilities.js +0 -108
  157. package/dist/utilities.js.map +0 -1
  158. package/src/lib/did.ts +0 -3
  159. package/src/parser.ts +0 -150
  160. package/src/permission-set.ts +0 -78
  161. package/src/resources/account-scope.ts +0 -66
  162. package/src/resources/blob-scope.ts +0 -86
  163. package/src/resources/identity-scope.ts +0 -49
  164. package/src/resources/repo-scope.ts +0 -101
  165. package/src/resources/rpc-scope.test.ts +0 -280
  166. package/src/resources/rpc-scope.ts +0 -77
  167. package/src/syntax.test.ts +0 -203
  168. package/src/syntax.ts +0 -325
  169. package/src/utilities.ts +0 -109
package/src/parser.ts DELETED
@@ -1,150 +0,0 @@
1
- import {
2
- NeRoArray,
3
- ResourceSyntax,
4
- ScopeForResource,
5
- formatScope,
6
- } from './syntax.js'
7
-
8
- type InferStringPredicate<T extends undefined | ((value: string) => boolean)> =
9
- T extends ((value: string) => value is infer U extends string) ? U : string
10
-
11
- type ParamsSchema = Record<
12
- string,
13
- | {
14
- multiple: false
15
- required: boolean
16
- default?: string
17
- normalize?: (value: string) => string
18
- validate?: (value: string) => boolean
19
- }
20
- | {
21
- multiple: true
22
- required: boolean
23
- default?: NeRoArray<string>
24
- normalize?: (value: NeRoArray<string>) => NeRoArray<string>
25
- validate?: (value: string) => boolean
26
- }
27
- >
28
-
29
- type ParsedParams<S extends ParamsSchema> = {
30
- [K in keyof S]:
31
- | (S[K]['required'] extends true
32
- ? never
33
- : 'default' extends keyof S[K]
34
- ? S[K]['default']
35
- : undefined)
36
- | (S[K]['multiple'] extends true
37
- ? NeRoArray<InferStringPredicate<S[K]['validate']>>
38
- : InferStringPredicate<S[K]['validate']>)
39
- } & NonNullable<unknown>
40
-
41
- export class Parser<R extends string, S extends ParamsSchema> {
42
- readonly schemaKeys: ReadonlyArray<keyof S & string>
43
-
44
- constructor(
45
- readonly resource: R,
46
- readonly schema: S,
47
- readonly positionalName?: keyof S & string,
48
- ) {
49
- this.schemaKeys = Object.keys(schema)
50
- }
51
-
52
- format(values: ParsedParams<S>): ScopeForResource<R> {
53
- // Build params
54
- const params: [
55
- name: string,
56
- value: undefined | string | NeRoArray<string>,
57
- ][] = []
58
-
59
- for (const key of this.schemaKeys) {
60
- const value = values[key]
61
- // Ignore undefined values
62
- if (value === undefined) continue
63
-
64
- const schema = this.schema[key]
65
-
66
- // @TODO: when the value is an array, we could remove duplicates
67
-
68
- // Normalize the value if a normalization function is provided
69
- const normalized = schema.normalize
70
- ? schema.normalize(value as any)
71
- : value
72
-
73
- // Ignore values that are equal to the default value
74
- if (!schema.required) {
75
- if (schema.default === normalized) continue
76
- if (
77
- schema.multiple &&
78
- schema.default &&
79
- arrayParamEquals(schema.default, normalized as NeRoArray<string>)
80
- ) {
81
- continue
82
- }
83
- }
84
-
85
- params.push([key, normalized])
86
- }
87
-
88
- return formatScope<R>(this.resource, params, this.positionalName)
89
- }
90
-
91
- parse(syntax: ResourceSyntax) {
92
- if (!syntax.is(this.resource)) return null
93
- if (syntax.containsParamsOtherThan(this.schemaKeys)) return null
94
-
95
- const result: Record<string, undefined | string | NeRoArray<string>> =
96
- Object.create(null)
97
-
98
- for (const key of this.schemaKeys) {
99
- const definition = this.schema[key]
100
-
101
- const value = definition.multiple
102
- ? syntax.getMulti(key, key === this.positionalName)
103
- : syntax.getSingle(key, key === this.positionalName)
104
-
105
- if (value === null) return null // Value is not valid
106
- if (value === undefined && definition.required) return null
107
-
108
- if (value !== undefined && definition.validate) {
109
- if (definition.multiple) {
110
- if (!(value as NeRoArray<string>).every(definition.validate)) {
111
- return null
112
- }
113
- } else {
114
- if (!definition.validate(value as string)) {
115
- return null
116
- }
117
- }
118
- }
119
-
120
- result[key] = value ?? definition.default
121
- }
122
-
123
- return result as ParsedParams<S>
124
- }
125
-
126
- parseString(scope: string): ParsedParams<S> | null {
127
- const syntax = ResourceSyntax.fromString(scope)
128
- return this.parse(syntax)
129
- }
130
- }
131
-
132
- /**
133
- * Two param arrays are considered equal if they contain the same values,
134
- * regardless of the order and duplicates.
135
- * @param a - The first array to compare.
136
- * @param b - The second array to compare.
137
- */
138
- function arrayParamEquals(
139
- a: readonly unknown[],
140
- b: readonly unknown[],
141
- ): boolean {
142
- for (const item of a) if (!b.includes(item)) return false
143
- for (const item of b) if (!a.includes(item)) return false
144
- return true
145
- }
146
-
147
- export function knownValuesValidator<T extends string>(values: Iterable<T>) {
148
- const set = new Set<string>(values)
149
- return (value: string): value is T => set.has(value)
150
- }
@@ -1,78 +0,0 @@
1
- import { AccountScope, AccountScopeMatch } from './resources/account-scope.js'
2
- import { BlobScope, BlobScopeMatch } from './resources/blob-scope.js'
3
- import {
4
- IdentityScope,
5
- IdentityScopeMatch,
6
- } from './resources/identity-scope.js'
7
- import { RepoScope, RepoScopeMatch } from './resources/repo-scope.js'
8
- import { RpcScope, RpcScopeMatch } from './resources/rpc-scope.js'
9
- import { ScopeMissingError } from './scope-missing-error.js'
10
- import { ScopesSet } from './scopes-set.js'
11
-
12
- export type {
13
- AccountScopeMatch,
14
- BlobScopeMatch,
15
- IdentityScopeMatch,
16
- RepoScopeMatch,
17
- RpcScopeMatch,
18
- }
19
-
20
- export class PermissionSet {
21
- public readonly scopes: ScopesSet
22
-
23
- constructor(scopes?: null | string | Iterable<string>) {
24
- this.scopes = new ScopesSet(
25
- typeof scopes === 'string' ? scopes.split(' ') : scopes,
26
- )
27
- }
28
-
29
- public allowsAccount(options: AccountScopeMatch): boolean {
30
- return this.scopes.matches('account', options)
31
- }
32
- public assertAccount(options: AccountScopeMatch): void {
33
- if (!this.allowsAccount(options)) {
34
- const scope = AccountScope.scopeNeededFor(options)
35
- throw new ScopeMissingError(scope)
36
- }
37
- }
38
-
39
- public allowsIdentity(options: IdentityScopeMatch): boolean {
40
- return this.scopes.matches('identity', options)
41
- }
42
- public assertIdentity(options: IdentityScopeMatch): void {
43
- if (!this.allowsIdentity(options)) {
44
- const scope = IdentityScope.scopeNeededFor(options)
45
- throw new ScopeMissingError(scope)
46
- }
47
- }
48
-
49
- public allowsBlob(options: BlobScopeMatch): boolean {
50
- return this.scopes.matches('blob', options)
51
- }
52
- public assertBlob(options: BlobScopeMatch): void {
53
- if (!this.allowsBlob(options)) {
54
- const scope = BlobScope.scopeNeededFor(options)
55
- throw new ScopeMissingError(scope)
56
- }
57
- }
58
-
59
- public allowsRepo(options: RepoScopeMatch): boolean {
60
- return this.scopes.matches('repo', options)
61
- }
62
- public assertRepo(options: RepoScopeMatch): void {
63
- if (!this.allowsRepo(options)) {
64
- const scope = RepoScope.scopeNeededFor(options)
65
- throw new ScopeMissingError(scope)
66
- }
67
- }
68
-
69
- public allowsRpc(options: RpcScopeMatch): boolean {
70
- return this.scopes.matches('rpc', options)
71
- }
72
- public assertRpc(options: RpcScopeMatch): void {
73
- if (!this.allowsRpc(options)) {
74
- const scope = RpcScope.scopeNeededFor(options)
75
- throw new ScopeMissingError(scope)
76
- }
77
- }
78
- }
@@ -1,66 +0,0 @@
1
- import { Parser, knownValuesValidator } from '../parser.js'
2
- import { ResourceSyntax, isScopeForResource } from '../syntax.js'
3
-
4
- const ACCOUNT_ATTRIBUTES = Object.freeze(['email', 'repo', 'status'] as const)
5
- export type AccountAttribute = (typeof ACCOUNT_ATTRIBUTES)[number]
6
-
7
- const ACCOUNT_ACTIONS = Object.freeze(['read', 'manage'] as const)
8
- export type AccountAction = (typeof ACCOUNT_ACTIONS)[number]
9
-
10
- export const accountParser = new Parser(
11
- 'account',
12
- {
13
- attr: {
14
- multiple: false,
15
- required: true,
16
- validate: knownValuesValidator(ACCOUNT_ATTRIBUTES),
17
- },
18
- action: {
19
- multiple: false,
20
- required: false,
21
- validate: knownValuesValidator(ACCOUNT_ACTIONS),
22
- default: 'read' as const,
23
- },
24
- },
25
- 'attr',
26
- )
27
-
28
- export type AccountScopeMatch = {
29
- attr: AccountAttribute
30
- action: AccountAction
31
- }
32
-
33
- export class AccountScope {
34
- constructor(
35
- public readonly attr: AccountAttribute,
36
- public readonly action: AccountAction,
37
- ) {}
38
-
39
- matches(options: AccountScopeMatch): boolean {
40
- return (
41
- this.attr === options.attr &&
42
- (this.action === 'manage' || this.action === options.action)
43
- )
44
- }
45
-
46
- toString() {
47
- return accountParser.format(this)
48
- }
49
-
50
- static fromString(scope: string) {
51
- if (!isScopeForResource(scope, 'account')) return null
52
- const syntax = ResourceSyntax.fromString(scope)
53
- return this.fromSyntax(syntax)
54
- }
55
-
56
- static fromSyntax(syntax: ResourceSyntax) {
57
- const result = accountParser.parse(syntax)
58
- if (!result) return null
59
-
60
- return new AccountScope(result.attr, result.action)
61
- }
62
-
63
- static scopeNeededFor(options: AccountScopeMatch): string {
64
- return accountParser.format(options)
65
- }
66
- }
@@ -1,86 +0,0 @@
1
- import { Accept, isAccept, matchesAnyAccept } from '../lib/mime.js'
2
- import { Parser } from '../parser.js'
3
- import { NeRoArray, ResourceSyntax, isScopeForResource } from '../syntax.js'
4
-
5
- export const DEFAULT_ACCEPT = Object.freeze(['*/*'] as const)
6
-
7
- export const blobParser = new Parser(
8
- 'blob',
9
- {
10
- accept: {
11
- multiple: true,
12
- required: true,
13
- validate: isAccept,
14
- normalize: (value) => {
15
- // Returns a more concise representation of the accept values.
16
- if (value.includes('*/*')) return DEFAULT_ACCEPT
17
-
18
- return value.map(toLowerCase).filter(isNonRedundant) as [
19
- Accept,
20
- ...Accept[],
21
- ]
22
- },
23
- },
24
- },
25
- 'accept',
26
- )
27
-
28
- export type BlobScopeMatch = {
29
- mime: string
30
- }
31
-
32
- export class BlobScope {
33
- constructor(public readonly accept: NeRoArray<Accept>) {}
34
-
35
- matches(options: BlobScopeMatch) {
36
- return matchesAnyAccept(this.accept, options.mime)
37
- }
38
-
39
- toString() {
40
- return blobParser.format(this)
41
- }
42
-
43
- static fromString(scope: string) {
44
- if (!isScopeForResource(scope, 'blob')) return null
45
- const syntax = ResourceSyntax.fromString(scope)
46
- return this.fromSyntax(syntax)
47
- }
48
-
49
- static fromSyntax(syntax: ResourceSyntax) {
50
- const result = blobParser.parse(syntax)
51
- if (!result) return null
52
-
53
- return new BlobScope(result.accept)
54
- }
55
-
56
- static scopeNeededFor(options: BlobScopeMatch) {
57
- return blobParser.format({
58
- accept: [options.mime as Accept],
59
- })
60
- }
61
- }
62
-
63
- function toLowerCase(value: string): string {
64
- return value.toLowerCase()
65
- }
66
-
67
- function isNonRedundant(
68
- value: string,
69
- index: number,
70
- arr: readonly string[],
71
- ): boolean {
72
- if (value.endsWith('/*')) {
73
- // assuming the array contains unique element, wildcards cannot be redundant
74
- // with one another ('image/*' is not redundant with 'text/*')
75
- return true
76
- }
77
- const base = value.split('/', 1)[0]
78
- if (arr.includes(`${base}/*`)) {
79
- // If another value in the array is a wildcard for the same base, we can
80
- // skip this one as it is redundant. e.g. if the array contains 'image/png'
81
- // and 'image/*', we can skip 'image/png' because 'image/*' already covers
82
- // it.
83
- return false
84
- }
85
- return true
86
- }
@@ -1,49 +0,0 @@
1
- import { Parser, knownValuesValidator } from '../parser.js'
2
- import { ResourceSyntax, isScopeForResource } from '../syntax.js'
3
-
4
- const IDENTITY_ATTRIBUTES = Object.freeze(['handle', '*'] as const)
5
- export type IdentityAttribute = (typeof IDENTITY_ATTRIBUTES)[number]
6
-
7
- export const identityParser = new Parser(
8
- 'identity',
9
- {
10
- attr: {
11
- multiple: false,
12
- required: true,
13
- validate: knownValuesValidator(IDENTITY_ATTRIBUTES),
14
- },
15
- },
16
- 'attr',
17
- )
18
-
19
- export type IdentityScopeMatch = {
20
- attr: IdentityAttribute
21
- }
22
-
23
- export class IdentityScope {
24
- constructor(public readonly attr: IdentityAttribute) {}
25
-
26
- matches(options: IdentityScopeMatch): boolean {
27
- return this.attr === '*' || this.attr === options.attr
28
- }
29
-
30
- toString() {
31
- return identityParser.format(this)
32
- }
33
-
34
- static fromString(scope: string) {
35
- if (!isScopeForResource(scope, 'identity')) return null
36
- const syntax = ResourceSyntax.fromString(scope)
37
- return this.fromSyntax(syntax)
38
- }
39
-
40
- static fromSyntax(syntax: ResourceSyntax) {
41
- const result = identityParser.parse(syntax)
42
- if (!result) return null
43
- return new IdentityScope(result.attr)
44
- }
45
-
46
- static scopeNeededFor(options: IdentityScopeMatch): string {
47
- return identityParser.format(options)
48
- }
49
- }
@@ -1,101 +0,0 @@
1
- import { NSID, isNSID } from '../lib/nsid.js'
2
- import { Parser, knownValuesValidator } from '../parser.js'
3
- import { NeRoArray, ResourceSyntax, isScopeForResource } from '../syntax.js'
4
-
5
- const REPO_ACTIONS = Object.freeze(['create', 'update', 'delete'] as const)
6
- export type RepoAction = (typeof REPO_ACTIONS)[number]
7
- export const isRepoAction = knownValuesValidator(REPO_ACTIONS)
8
-
9
- export const repoParser = new Parser(
10
- 'repo',
11
- {
12
- collection: {
13
- multiple: true,
14
- required: true,
15
- validate: (value) => value === '*' || isNSID(value),
16
- normalize: (value) => {
17
- if (value.length > 1 && value.includes('*')) return ['*'] as const
18
- return value
19
- },
20
- },
21
- action: {
22
- multiple: true,
23
- required: false,
24
- validate: isRepoAction,
25
- default: REPO_ACTIONS,
26
- },
27
- },
28
- 'collection',
29
- )
30
-
31
- export type RepoScopeMatch = {
32
- collection: string
33
- action: RepoAction
34
- }
35
-
36
- export class RepoScope {
37
- constructor(
38
- public readonly collection: NeRoArray<'*' | NSID>,
39
- public readonly action: NeRoArray<RepoAction>,
40
- ) {}
41
-
42
- get allowsAnyCollection() {
43
- return this.collection.includes('*')
44
- }
45
-
46
- matches({ action, collection }: RepoScopeMatch): boolean {
47
- return (
48
- this.action.includes(action) &&
49
- (this.allowsAnyCollection ||
50
- (this.collection as readonly string[]).includes(collection))
51
- )
52
- }
53
-
54
- toString(): string {
55
- // Normalize (compress, de-dupe, sort)
56
- return repoParser.format({
57
- collection: this.allowsAnyCollection
58
- ? ['*']
59
- : this.collection.length > 1
60
- ? ([...new Set(this.collection)].sort() as [NSID, ...NSID[]])
61
- : this.collection,
62
- action:
63
- this.action === REPO_ACTIONS
64
- ? REPO_ACTIONS // No need to filter if the default was used
65
- : (REPO_ACTIONS.filter(includedIn, this.action) as [
66
- RepoAction,
67
- ...RepoAction[],
68
- ]),
69
- })
70
- }
71
-
72
- static fromString(scope: string): RepoScope | null {
73
- if (!isScopeForResource(scope, 'repo')) return null
74
- const syntax = ResourceSyntax.fromString(scope)
75
- return this.fromSyntax(syntax)
76
- }
77
-
78
- static fromSyntax(syntax: ResourceSyntax): RepoScope | null {
79
- const result = repoParser.parse(syntax)
80
- if (!result) return null
81
-
82
- return new RepoScope(result.collection, result.action)
83
- }
84
-
85
- static scopeNeededFor(options: RepoScopeMatch): string {
86
- return repoParser.format({
87
- collection: [options.collection as '*' | NSID],
88
- action: [options.action],
89
- })
90
- }
91
- }
92
-
93
- /**
94
- * Special utility function to be used as predicate for array methods like
95
- * `Array.prototype.includes`, etc. When used as predicate, it expects that
96
- * the array method is called with a `thisArg` that is a readonly array of
97
- * the same type as the `value` parameter.
98
- */
99
- function includedIn<T>(this: readonly T[], value: T): boolean {
100
- return this.includes(value)
101
- }