@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.
- package/CHANGELOG.md +38 -0
- package/dist/atproto-oauth-scope.d.ts +17 -0
- package/dist/atproto-oauth-scope.d.ts.map +1 -0
- package/dist/atproto-oauth-scope.js +67 -0
- package/dist/atproto-oauth-scope.js.map +1 -0
- package/dist/index.d.ts +9 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/lexicon.d.ts +2 -0
- package/dist/lib/lexicon.d.ts.map +1 -0
- package/dist/lib/lexicon.js +3 -0
- package/dist/lib/lexicon.js.map +1 -0
- package/dist/lib/mime.d.ts +1 -1
- package/dist/lib/mime.d.ts.map +1 -1
- package/dist/lib/mime.js +2 -0
- package/dist/lib/mime.js.map +1 -1
- package/dist/lib/nsid.d.ts +2 -2
- package/dist/lib/nsid.d.ts.map +1 -1
- package/dist/lib/nsid.js +4 -6
- package/dist/lib/nsid.js.map +1 -1
- package/dist/lib/parser.d.ts +29 -0
- package/dist/lib/parser.d.ts.map +1 -0
- package/dist/lib/parser.js +152 -0
- package/dist/lib/parser.js.map +1 -0
- package/dist/lib/resource-permission.d.ts +10 -0
- package/dist/lib/resource-permission.d.ts.map +1 -0
- package/dist/lib/resource-permission.js +3 -0
- package/dist/lib/resource-permission.js.map +1 -0
- package/dist/lib/syntax-lexicon.d.ts +26 -0
- package/dist/lib/syntax-lexicon.d.ts.map +1 -0
- package/dist/lib/syntax-lexicon.js +58 -0
- package/dist/lib/syntax-lexicon.js.map +1 -0
- package/dist/lib/syntax-string.d.ts +16 -0
- package/dist/lib/syntax-string.d.ts.map +1 -0
- package/dist/lib/syntax-string.js +121 -0
- package/dist/lib/syntax-string.js.map +1 -0
- package/dist/lib/syntax.d.ts +23 -0
- package/dist/lib/syntax.d.ts.map +1 -0
- package/dist/lib/syntax.js +22 -0
- package/dist/lib/syntax.js.map +1 -0
- package/dist/lib/util.d.ts +5 -1
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +8 -12
- package/dist/lib/util.js.map +1 -1
- package/dist/scope-permissions-transition.d.ts +15 -0
- package/dist/scope-permissions-transition.d.ts.map +1 -0
- package/dist/{permission-set-transition.js → scope-permissions-transition.js} +5 -5
- package/dist/scope-permissions-transition.js.map +1 -0
- package/dist/scope-permissions.d.ts +22 -0
- package/dist/scope-permissions.d.ts.map +1 -0
- package/dist/{permission-set.js → scope-permissions.js} +20 -16
- package/dist/scope-permissions.js.map +1 -0
- package/dist/scopes/account-permission.d.ts +35 -0
- package/dist/scopes/account-permission.d.ts.map +1 -0
- package/dist/scopes/account-permission.js +71 -0
- package/dist/scopes/account-permission.js.map +1 -0
- package/dist/scopes/blob-permission.d.ts +27 -0
- package/dist/scopes/blob-permission.d.ts.map +1 -0
- package/dist/scopes/blob-permission.js +86 -0
- package/dist/scopes/blob-permission.js.map +1 -0
- package/dist/scopes/identity-permission.d.ts +25 -0
- package/dist/scopes/identity-permission.d.ts.map +1 -0
- package/dist/scopes/identity-permission.js +53 -0
- package/dist/scopes/identity-permission.js.map +1 -0
- package/dist/scopes/include-scope.d.ts +54 -0
- package/dist/scopes/include-scope.d.ts.map +1 -0
- package/dist/scopes/include-scope.js +156 -0
- package/dist/scopes/include-scope.js.map +1 -0
- package/dist/scopes/repo-permission.d.ts +40 -0
- package/dist/scopes/repo-permission.d.ts.map +1 -0
- package/dist/scopes/repo-permission.js +101 -0
- package/dist/scopes/repo-permission.js.map +1 -0
- package/dist/scopes/rpc-permission.d.ts +38 -0
- package/dist/scopes/rpc-permission.d.ts.map +1 -0
- package/dist/scopes/rpc-permission.js +81 -0
- package/dist/scopes/rpc-permission.js.map +1 -0
- package/dist/scopes-set.d.ts +12 -1
- package/dist/scopes-set.d.ts.map +1 -1
- package/dist/scopes-set.js +49 -3
- package/dist/scopes-set.js.map +1 -1
- package/package.json +7 -3
- package/src/atproto-oauth-scope.ts +79 -0
- package/src/index.ts +10 -14
- package/src/lib/lexicon.ts +1 -0
- package/src/lib/mime.ts +2 -1
- package/src/lib/nsid.ts +5 -6
- package/src/lib/parser.ts +176 -0
- package/src/lib/resource-permission.ts +10 -0
- package/src/lib/syntax-lexicon.ts +55 -0
- package/src/lib/syntax-string.test.ts +130 -0
- package/src/lib/syntax-string.ts +132 -0
- package/src/lib/syntax.test.ts +43 -0
- package/src/lib/syntax.ts +47 -0
- package/src/lib/util.ts +11 -12
- package/src/{permission-set-transition.test.ts → scope-permissions-transition.test.ts} +33 -20
- package/src/{permission-set-transition.ts → scope-permissions-transition.ts} +11 -11
- package/src/{permission-set.test.ts → scope-permissions.test.ts} +77 -35
- package/src/scope-permissions.ts +91 -0
- package/src/{resources/account-scope.test.ts → scopes/account-permission.test.ts} +45 -33
- package/src/scopes/account-permission.ts +75 -0
- package/src/{resources/blob-scope.test.ts → scopes/blob-permission.test.ts} +31 -23
- package/src/scopes/blob-permission.ts +105 -0
- package/src/{resources/identity-scope.test.ts → scopes/identity-permission.test.ts} +13 -13
- package/src/scopes/identity-permission.ts +54 -0
- package/src/scopes/include-scope.test.ts +626 -0
- package/src/scopes/include-scope.ts +168 -0
- package/src/{resources/repo-scope.test.ts → scopes/repo-permission.test.ts} +77 -65
- package/src/scopes/repo-permission.ts +111 -0
- package/src/scopes/rpc-permission.test.ts +323 -0
- package/src/scopes/rpc-permission.ts +85 -0
- package/src/scopes-set.test.ts +5 -5
- package/src/scopes-set.ts +79 -5
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
- package/dist/lib/did.d.ts +0 -3
- package/dist/lib/did.d.ts.map +0 -1
- package/dist/lib/did.js +0 -6
- package/dist/lib/did.js.map +0 -1
- package/dist/parser.d.ts +0 -31
- package/dist/parser.d.ts.map +0 -1
- package/dist/parser.js +0 -118
- package/dist/parser.js.map +0 -1
- package/dist/permission-set-transition.d.ts +0 -15
- package/dist/permission-set-transition.d.ts.map +0 -1
- package/dist/permission-set-transition.js.map +0 -1
- package/dist/permission-set.d.ts +0 -22
- package/dist/permission-set.d.ts.map +0 -1
- package/dist/permission-set.js.map +0 -1
- package/dist/resources/account-scope.d.ts +0 -35
- package/dist/resources/account-scope.d.ts.map +0 -1
- package/dist/resources/account-scope.js +0 -60
- package/dist/resources/account-scope.js.map +0 -1
- package/dist/resources/blob-scope.d.ts +0 -25
- package/dist/resources/blob-scope.d.ts.map +0 -1
- package/dist/resources/blob-scope.js +0 -74
- package/dist/resources/blob-scope.js.map +0 -1
- package/dist/resources/identity-scope.d.ts +0 -25
- package/dist/resources/identity-scope.d.ts.map +0 -1
- package/dist/resources/identity-scope.js +0 -46
- package/dist/resources/identity-scope.js.map +0 -1
- package/dist/resources/repo-scope.d.ts +0 -37
- package/dist/resources/repo-scope.d.ts.map +0 -1
- package/dist/resources/repo-scope.js +0 -92
- package/dist/resources/repo-scope.js.map +0 -1
- package/dist/resources/rpc-scope.d.ts +0 -31
- package/dist/resources/rpc-scope.d.ts.map +0 -1
- package/dist/resources/rpc-scope.js +0 -74
- package/dist/resources/rpc-scope.js.map +0 -1
- package/dist/syntax.d.ts +0 -76
- package/dist/syntax.d.ts.map +0 -1
- package/dist/syntax.js +0 -249
- package/dist/syntax.js.map +0 -1
- package/dist/utilities.d.ts +0 -17
- package/dist/utilities.d.ts.map +0 -1
- package/dist/utilities.js +0 -108
- package/dist/utilities.js.map +0 -1
- package/src/lib/did.ts +0 -3
- package/src/parser.ts +0 -150
- package/src/permission-set.ts +0 -78
- package/src/resources/account-scope.ts +0 -66
- package/src/resources/blob-scope.ts +0 -86
- package/src/resources/identity-scope.ts +0 -49
- package/src/resources/repo-scope.ts +0 -101
- package/src/resources/rpc-scope.test.ts +0 -280
- package/src/resources/rpc-scope.ts +0 -77
- package/src/syntax.test.ts +0 -203
- package/src/syntax.ts +0 -325
- 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
|
-
}
|
package/src/permission-set.ts
DELETED
|
@@ -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
|
-
}
|