@atproto/api 0.20.22 → 0.20.25
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 +26 -0
- package/dist/client/lexicons.d.ts +28 -12
- package/dist/client/lexicons.d.ts.map +1 -1
- package/dist/client/lexicons.js +14 -6
- package/dist/client/lexicons.js.map +1 -1
- package/dist/client/types/app/bsky/ageassurance/defs.d.ts +2 -0
- package/dist/client/types/app/bsky/ageassurance/defs.d.ts.map +1 -1
- package/dist/client/types/app/bsky/ageassurance/defs.js.map +1 -1
- package/package.json +22 -16
- package/definitions/labels.json +0 -170
- package/docs/moderation.md +0 -260
- package/jest.config.cjs +0 -23
- package/jest.d.ts +0 -20
- package/jest.setup.ts +0 -97
- package/scripts/code/labels.mjs +0 -77
- package/scripts/generate-code.mjs +0 -4
- package/src/age-assurance.test.ts +0 -218
- package/src/age-assurance.ts +0 -137
- package/src/agent.ts +0 -1645
- package/src/atp-agent.ts +0 -570
- package/src/bsky-agent.ts +0 -14
- package/src/const.ts +0 -1
- package/src/index.ts +0 -44
- package/src/mocker.ts +0 -220
- package/src/moderation/decision.ts +0 -389
- package/src/moderation/index.ts +0 -69
- package/src/moderation/mutewords.ts +0 -170
- package/src/moderation/subjects/account.ts +0 -44
- package/src/moderation/subjects/feed-generator.ts +0 -24
- package/src/moderation/subjects/notification.ts +0 -25
- package/src/moderation/subjects/post.ts +0 -433
- package/src/moderation/subjects/profile.ts +0 -26
- package/src/moderation/subjects/status.ts +0 -27
- package/src/moderation/subjects/user-list.ts +0 -48
- package/src/moderation/types.ts +0 -191
- package/src/moderation/ui.ts +0 -21
- package/src/moderation/util.ts +0 -111
- package/src/predicate.ts +0 -52
- package/src/rich-text/detection.ts +0 -146
- package/src/rich-text/rich-text.ts +0 -418
- package/src/rich-text/sanitization.ts +0 -42
- package/src/rich-text/unicode.ts +0 -47
- package/src/rich-text/util.ts +0 -15
- package/src/session-manager.ts +0 -5
- package/src/types.ts +0 -165
- package/src/util.ts +0 -96
- package/tests/atp-agent.test.ts +0 -3863
- package/tests/dispatcher.test.ts +0 -527
- package/tests/errors.test.ts +0 -29
- package/tests/moderation-behaviors.test.ts +0 -951
- package/tests/moderation-custom-labels.test.ts +0 -334
- package/tests/moderation-mutewords.test.ts +0 -1299
- package/tests/moderation-prefs.test.ts +0 -402
- package/tests/moderation-quoteposts.test.ts +0 -277
- package/tests/moderation.test.ts +0 -739
- package/tests/rich-text-detection.test.ts +0 -456
- package/tests/rich-text-sanitization.test.ts +0 -216
- package/tests/rich-text.test.ts +0 -705
- package/tests/util/echo-server.ts +0 -37
- package/tests/util/moderation-behavior.ts +0 -268
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -10
package/jest.setup.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { ModerationUI } from './src/index.js'
|
|
2
|
-
import { ModerationTestSuiteResultFlag } from './tests/util/moderation-behavior.js'
|
|
3
|
-
|
|
4
|
-
expect.extend({
|
|
5
|
-
toBeModerationResult(
|
|
6
|
-
actual: ModerationUI,
|
|
7
|
-
expected: ModerationTestSuiteResultFlag[] | undefined,
|
|
8
|
-
context = '',
|
|
9
|
-
stringifiedResult: string | undefined = undefined,
|
|
10
|
-
_ignoreCause = false,
|
|
11
|
-
) {
|
|
12
|
-
const fail = (msg: string) => ({
|
|
13
|
-
pass: false,
|
|
14
|
-
message: () =>
|
|
15
|
-
`${msg}.${
|
|
16
|
-
stringifiedResult ? ` Full result: ${stringifiedResult}` : ''
|
|
17
|
-
}`,
|
|
18
|
-
})
|
|
19
|
-
// let cause = actual.causes?.type as string
|
|
20
|
-
// if (actual.cause?.type === 'label') {
|
|
21
|
-
// cause = `label:${actual.cause.labelDef.id}`
|
|
22
|
-
// } else if (actual.cause?.type === 'muted') {
|
|
23
|
-
// if (actual.cause.source.type === 'list') {
|
|
24
|
-
// cause = 'muted-by-list'
|
|
25
|
-
// }
|
|
26
|
-
// } else if (actual.cause?.type === 'blocking') {
|
|
27
|
-
// if (actual.cause.source.type === 'list') {
|
|
28
|
-
// cause = 'blocking-by-list'
|
|
29
|
-
// }
|
|
30
|
-
// }
|
|
31
|
-
if (!expected) {
|
|
32
|
-
// if (!ignoreCause && actual.cause) {
|
|
33
|
-
// return fail(`${context} expected to be a no-op, got ${cause}`)
|
|
34
|
-
// }
|
|
35
|
-
if (actual.inform) {
|
|
36
|
-
return fail(`${context} expected to be a no-op, got inform=true`)
|
|
37
|
-
}
|
|
38
|
-
if (actual.alert) {
|
|
39
|
-
return fail(`${context} expected to be a no-op, got alert=true`)
|
|
40
|
-
}
|
|
41
|
-
if (actual.blur) {
|
|
42
|
-
return fail(`${context} expected to be a no-op, got blur=true`)
|
|
43
|
-
}
|
|
44
|
-
if (actual.filter) {
|
|
45
|
-
return fail(`${context} expected to be a no-op, got filter=true`)
|
|
46
|
-
}
|
|
47
|
-
if (actual.noOverride) {
|
|
48
|
-
return fail(`${context} expected to be a no-op, got noOverride=true`)
|
|
49
|
-
}
|
|
50
|
-
} else {
|
|
51
|
-
// if (!ignoreCause && cause !== expected.cause) {
|
|
52
|
-
// return fail(`${context} expected to be ${expected.cause}, got ${cause}`)
|
|
53
|
-
// }
|
|
54
|
-
const expectedInform = expected.includes('inform')
|
|
55
|
-
if (!!actual.inform !== expectedInform) {
|
|
56
|
-
return fail(
|
|
57
|
-
`${context} expected to be inform=${expectedInform}, got ${
|
|
58
|
-
actual.inform || false
|
|
59
|
-
}`,
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
const expectedAlert = expected.includes('alert')
|
|
63
|
-
if (!!actual.alert !== expectedAlert) {
|
|
64
|
-
return fail(
|
|
65
|
-
`${context} expected to be alert=${expectedAlert}, got ${
|
|
66
|
-
actual.alert || false
|
|
67
|
-
}`,
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
const expectedBlur = expected.includes('blur')
|
|
71
|
-
if (!!actual.blur !== expectedBlur) {
|
|
72
|
-
return fail(
|
|
73
|
-
`${context} expected to be blur=${expectedBlur}, got ${
|
|
74
|
-
actual.blur || false
|
|
75
|
-
}`,
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
const expectedFilter = expected.includes('filter')
|
|
79
|
-
if (!!actual.filter !== expectedFilter) {
|
|
80
|
-
return fail(
|
|
81
|
-
`${context} expected to be filter=${expectedFilter}, got ${
|
|
82
|
-
actual.filter || false
|
|
83
|
-
}`,
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
const expectedNoOverride = expected.includes('noOverride')
|
|
87
|
-
if (!!actual.noOverride !== expectedNoOverride) {
|
|
88
|
-
return fail(
|
|
89
|
-
`${context} expected to be noOverride=${expectedNoOverride}, got ${
|
|
90
|
-
actual.noOverride || false
|
|
91
|
-
}`,
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return { pass: true, message: () => '' }
|
|
96
|
-
},
|
|
97
|
-
})
|
package/scripts/code/labels.mjs
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from 'node:fs/promises'
|
|
2
|
-
import { dirname, join } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import * as prettier from 'prettier'
|
|
5
|
-
import labelsDef from '../../definitions/labels.json' with { type: 'json' }
|
|
6
|
-
|
|
7
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
8
|
-
|
|
9
|
-
export async function labels() {
|
|
10
|
-
const content = await gen()
|
|
11
|
-
|
|
12
|
-
const path = join(
|
|
13
|
-
__dirname,
|
|
14
|
-
'..',
|
|
15
|
-
'..',
|
|
16
|
-
'src',
|
|
17
|
-
'moderation',
|
|
18
|
-
'const',
|
|
19
|
-
'labels.ts',
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
await mkdir(dirname(path), { recursive: true })
|
|
23
|
-
await writeFile(path, content, 'utf8')
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function gen() {
|
|
27
|
-
const knownValues = new Set()
|
|
28
|
-
const flattenedLabelDefs = []
|
|
29
|
-
|
|
30
|
-
for (const { alias: aliases, ...label } of labelsDef) {
|
|
31
|
-
knownValues.add(label.identifier)
|
|
32
|
-
flattenedLabelDefs.push([label.identifier, { ...label, locales: [] }])
|
|
33
|
-
|
|
34
|
-
if (aliases) {
|
|
35
|
-
for (const alias of aliases) {
|
|
36
|
-
knownValues.add(alias)
|
|
37
|
-
flattenedLabelDefs.push([
|
|
38
|
-
alias,
|
|
39
|
-
{
|
|
40
|
-
...label,
|
|
41
|
-
identifier: alias,
|
|
42
|
-
locales: [],
|
|
43
|
-
comment: `@deprecated alias for \`${label.identifier}\``,
|
|
44
|
-
},
|
|
45
|
-
])
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let labelDefsStr = `{`
|
|
51
|
-
for (const [key, { comment, ...value }] of flattenedLabelDefs) {
|
|
52
|
-
const commentStr = comment ? `\n/** ${comment} */\n` : ''
|
|
53
|
-
labelDefsStr += `${commentStr}'${key}': ${JSON.stringify(value, null, 2)},`
|
|
54
|
-
}
|
|
55
|
-
labelDefsStr += `}`
|
|
56
|
-
|
|
57
|
-
return prettier.format(
|
|
58
|
-
`/** this doc is generated by ./scripts/code/labels.mjs **/
|
|
59
|
-
import {InterpretedLabelValueDefinition, LabelPreference} from '../types.js'
|
|
60
|
-
|
|
61
|
-
export type KnownLabelValue = ${Array.from(knownValues)
|
|
62
|
-
.map((value) => `"${value}"`)
|
|
63
|
-
.join(' | ')}
|
|
64
|
-
|
|
65
|
-
export const DEFAULT_LABEL_SETTINGS: Record<string, LabelPreference> = ${JSON.stringify(
|
|
66
|
-
Object.fromEntries(
|
|
67
|
-
labelsDef
|
|
68
|
-
.filter((label) => label.configurable)
|
|
69
|
-
.map((label) => [label.identifier, label.defaultSetting]),
|
|
70
|
-
),
|
|
71
|
-
)}
|
|
72
|
-
|
|
73
|
-
export const LABELS: Record<KnownLabelValue, InterpretedLabelValueDefinition> = ${labelDefsStr}
|
|
74
|
-
`,
|
|
75
|
-
{ semi: false, parser: 'typescript', singleQuote: true },
|
|
76
|
-
)
|
|
77
|
-
}
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from '@jest/globals'
|
|
2
|
-
import {
|
|
3
|
-
ageAssuranceRuleIDs,
|
|
4
|
-
computeAgeAssuranceRegionAccess,
|
|
5
|
-
getAgeAssuranceRegionConfig,
|
|
6
|
-
} from './age-assurance.js'
|
|
7
|
-
import { AppBskyAgeassuranceDefs } from './client/index.js'
|
|
8
|
-
|
|
9
|
-
describe('age-assurance', () => {
|
|
10
|
-
describe('getAgeAssuranceRegionConfig', () => {
|
|
11
|
-
const config: AppBskyAgeassuranceDefs.Config = {
|
|
12
|
-
regions: [
|
|
13
|
-
{
|
|
14
|
-
countryCode: 'US',
|
|
15
|
-
regionCode: 'CA',
|
|
16
|
-
minAccessAge: 13,
|
|
17
|
-
rules: [],
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
countryCode: 'US',
|
|
21
|
-
minAccessAge: 13,
|
|
22
|
-
rules: [],
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
it('should find region by country code only', () => {
|
|
28
|
-
const result = getAgeAssuranceRegionConfig(config, {
|
|
29
|
-
countryCode: 'US',
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
expect(result).toEqual({
|
|
33
|
-
countryCode: 'US',
|
|
34
|
-
minAccessAge: 13,
|
|
35
|
-
rules: [],
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('should find region by country code and region code', () => {
|
|
40
|
-
const result = getAgeAssuranceRegionConfig(config, {
|
|
41
|
-
countryCode: 'US',
|
|
42
|
-
regionCode: 'CA',
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
expect(result).toEqual({
|
|
46
|
-
countryCode: 'US',
|
|
47
|
-
regionCode: 'CA',
|
|
48
|
-
minAccessAge: 13,
|
|
49
|
-
rules: [],
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should return undefined when no matching region found', () => {
|
|
54
|
-
const result = getAgeAssuranceRegionConfig(config, {
|
|
55
|
-
countryCode: 'GB',
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
expect(result).toBeUndefined()
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
describe('computeAgeAssuranceRegionAccess', () => {
|
|
63
|
-
const region: AppBskyAgeassuranceDefs.ConfigRegion = {
|
|
64
|
-
countryCode: 'US',
|
|
65
|
-
minAccessAge: 13,
|
|
66
|
-
rules: [
|
|
67
|
-
{
|
|
68
|
-
$type: ageAssuranceRuleIDs.IfAccountNewerThan,
|
|
69
|
-
date: '2025-12-10T00:00:00Z',
|
|
70
|
-
access: 'none',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
$type: ageAssuranceRuleIDs.IfAssuredOverAge,
|
|
74
|
-
age: 18,
|
|
75
|
-
access: 'full',
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
$type: ageAssuranceRuleIDs.IfAssuredOverAge,
|
|
79
|
-
age: 16,
|
|
80
|
-
access: 'safe',
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
$type: ageAssuranceRuleIDs.IfDeclaredOverAge,
|
|
84
|
-
age: 16,
|
|
85
|
-
access: 'safe',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
$type: ageAssuranceRuleIDs.Default,
|
|
89
|
-
access: 'none',
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
it('should apply default if no data provided', () => {
|
|
95
|
-
const result = computeAgeAssuranceRegionAccess(region, {})
|
|
96
|
-
|
|
97
|
-
expect(result).toEqual({
|
|
98
|
-
access: 'none',
|
|
99
|
-
reason: ageAssuranceRuleIDs.Default,
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
describe('IfAccountNewerThan', () => {
|
|
104
|
-
it('should block accounts created after threshold', () => {
|
|
105
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
106
|
-
accountCreatedAt: new Date(2025, 11, 15).toISOString(),
|
|
107
|
-
declaredAge: 18,
|
|
108
|
-
})
|
|
109
|
-
expect(result).toEqual({
|
|
110
|
-
access: 'none',
|
|
111
|
-
reason: ageAssuranceRuleIDs.IfAccountNewerThan,
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('should allow accounts created before threshold', () => {
|
|
116
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
117
|
-
accountCreatedAt: new Date(2025, 10, 1).toISOString(),
|
|
118
|
-
declaredAge: 18,
|
|
119
|
-
})
|
|
120
|
-
expect(result).toEqual({
|
|
121
|
-
access: 'safe',
|
|
122
|
-
reason: ageAssuranceRuleIDs.IfDeclaredOverAge,
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
it('should allow accounts created exactly at threshold', () => {
|
|
127
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
128
|
-
accountCreatedAt: new Date(2025, 11, 1).toISOString(),
|
|
129
|
-
declaredAge: 18,
|
|
130
|
-
})
|
|
131
|
-
expect(result).toEqual({
|
|
132
|
-
access: 'safe',
|
|
133
|
-
reason: ageAssuranceRuleIDs.IfDeclaredOverAge,
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
it('should not apply rule when accountCreatedAt is not provided', () => {
|
|
138
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
139
|
-
declaredAge: 15,
|
|
140
|
-
})
|
|
141
|
-
expect(result).toEqual({
|
|
142
|
-
access: 'none',
|
|
143
|
-
reason: ageAssuranceRuleIDs.Default,
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('should not apply rule when assuredAge is present', () => {
|
|
148
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
149
|
-
accountCreatedAt: new Date(2025, 11, 15).toISOString(),
|
|
150
|
-
assuredAge: 20,
|
|
151
|
-
})
|
|
152
|
-
expect(result).toEqual({
|
|
153
|
-
access: 'full',
|
|
154
|
-
reason: ageAssuranceRuleIDs.IfAssuredOverAge,
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
describe('IfDeclaredOverAge rule', () => {
|
|
160
|
-
it('should allow users at or above age threshold', () => {
|
|
161
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
162
|
-
declaredAge: 18,
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
expect(result).toEqual({
|
|
166
|
-
access: 'safe',
|
|
167
|
-
reason: ageAssuranceRuleIDs.IfDeclaredOverAge,
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('should allow users above age threshold', () => {
|
|
172
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
173
|
-
declaredAge: 25,
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
expect(result).toEqual({
|
|
177
|
-
access: 'safe',
|
|
178
|
-
reason: ageAssuranceRuleIDs.IfDeclaredOverAge,
|
|
179
|
-
})
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('should not allow users below age threshold', () => {
|
|
183
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
184
|
-
declaredAge: 17,
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
expect(result).toEqual({
|
|
188
|
-
access: 'safe',
|
|
189
|
-
reason: ageAssuranceRuleIDs.IfDeclaredOverAge,
|
|
190
|
-
})
|
|
191
|
-
})
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
describe('IfAssuredOverAge rule', () => {
|
|
195
|
-
it('should allow users at or above assured age threshold', () => {
|
|
196
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
197
|
-
assuredAge: 18,
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
expect(result).toEqual({
|
|
201
|
-
access: 'full',
|
|
202
|
-
reason: ageAssuranceRuleIDs.IfAssuredOverAge,
|
|
203
|
-
})
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
it('should not allow users below assured age threshold', () => {
|
|
207
|
-
const result = computeAgeAssuranceRegionAccess(region, {
|
|
208
|
-
assuredAge: 17,
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
expect(result).toEqual({
|
|
212
|
-
access: 'safe',
|
|
213
|
-
reason: ageAssuranceRuleIDs.IfAssuredOverAge,
|
|
214
|
-
})
|
|
215
|
-
})
|
|
216
|
-
})
|
|
217
|
-
})
|
|
218
|
-
})
|
package/src/age-assurance.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { AppBskyAgeassuranceDefs } from './client/index.js'
|
|
2
|
-
import { ids } from './client/lexicons.js'
|
|
3
|
-
|
|
4
|
-
export type AgeAssuranceRuleID = Exclude<
|
|
5
|
-
| AppBskyAgeassuranceDefs.ConfigRegionRuleDefault['$type']
|
|
6
|
-
| AppBskyAgeassuranceDefs.ConfigRegionRuleIfDeclaredOverAge['$type']
|
|
7
|
-
| AppBskyAgeassuranceDefs.ConfigRegionRuleIfDeclaredUnderAge['$type']
|
|
8
|
-
| AppBskyAgeassuranceDefs.ConfigRegionRuleIfAssuredOverAge['$type']
|
|
9
|
-
| AppBskyAgeassuranceDefs.ConfigRegionRuleIfAssuredUnderAge['$type']
|
|
10
|
-
| AppBskyAgeassuranceDefs.ConfigRegionRuleIfAccountNewerThan['$type']
|
|
11
|
-
| AppBskyAgeassuranceDefs.ConfigRegionRuleIfAccountOlderThan['$type'],
|
|
12
|
-
undefined
|
|
13
|
-
>
|
|
14
|
-
|
|
15
|
-
export const ageAssuranceRuleIDs: Record<string, AgeAssuranceRuleID> = {
|
|
16
|
-
Default: `${ids.AppBskyAgeassuranceDefs}#configRegionRuleDefault`,
|
|
17
|
-
IfDeclaredOverAge: `${ids.AppBskyAgeassuranceDefs}#configRegionRuleIfDeclaredOverAge`,
|
|
18
|
-
IfDeclaredUnderAge: `${ids.AppBskyAgeassuranceDefs}#configRegionRuleIfDeclaredUnderAge`,
|
|
19
|
-
IfAssuredOverAge: `${ids.AppBskyAgeassuranceDefs}#configRegionRuleIfAssuredOverAge`,
|
|
20
|
-
IfAssuredUnderAge: `${ids.AppBskyAgeassuranceDefs}#configRegionRuleIfAssuredUnderAge`,
|
|
21
|
-
IfAccountNewerThan: `${ids.AppBskyAgeassuranceDefs}#configRegionRuleIfAccountNewerThan`,
|
|
22
|
-
IfAccountOlderThan: `${ids.AppBskyAgeassuranceDefs}#configRegionRuleIfAccountOlderThan`,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Returns the first matched region configuration based on the provided geolocation.
|
|
27
|
-
*/
|
|
28
|
-
export function getAgeAssuranceRegionConfig(
|
|
29
|
-
config: AppBskyAgeassuranceDefs.Config,
|
|
30
|
-
geolocation: {
|
|
31
|
-
countryCode: string
|
|
32
|
-
regionCode?: string
|
|
33
|
-
},
|
|
34
|
-
): AppBskyAgeassuranceDefs.ConfigRegion | undefined {
|
|
35
|
-
const { regions } = config
|
|
36
|
-
return regions.find(({ countryCode, regionCode }) => {
|
|
37
|
-
if (countryCode === geolocation.countryCode) {
|
|
38
|
-
return !regionCode || regionCode === geolocation.regionCode
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function computeAgeAssuranceRegionAccess(
|
|
44
|
-
region: AppBskyAgeassuranceDefs.ConfigRegion,
|
|
45
|
-
data:
|
|
46
|
-
| {
|
|
47
|
-
/**
|
|
48
|
-
* The account creation date in ISO 8601 format. Only checked if we
|
|
49
|
-
* don't have an assured age, such as on the client.
|
|
50
|
-
*/
|
|
51
|
-
accountCreatedAt?: string
|
|
52
|
-
/**
|
|
53
|
-
* The user's declared age
|
|
54
|
-
*/
|
|
55
|
-
declaredAge?: number
|
|
56
|
-
/**
|
|
57
|
-
* The user's minimum age as assured by a trusted third party.
|
|
58
|
-
*/
|
|
59
|
-
assuredAge?: number
|
|
60
|
-
}
|
|
61
|
-
| undefined,
|
|
62
|
-
):
|
|
63
|
-
| {
|
|
64
|
-
access: AppBskyAgeassuranceDefs.Access
|
|
65
|
-
reason: AgeAssuranceRuleID
|
|
66
|
-
}
|
|
67
|
-
| undefined {
|
|
68
|
-
// first match wins
|
|
69
|
-
for (const rule of region.rules) {
|
|
70
|
-
if (AppBskyAgeassuranceDefs.isConfigRegionRuleIfAccountNewerThan(rule)) {
|
|
71
|
-
if (data?.accountCreatedAt && !data?.assuredAge) {
|
|
72
|
-
const accountCreatedAt = new Date(data.accountCreatedAt)
|
|
73
|
-
const threshold = new Date(rule.date)
|
|
74
|
-
if (accountCreatedAt >= threshold) {
|
|
75
|
-
return {
|
|
76
|
-
access: rule.access,
|
|
77
|
-
reason: rule.$type,
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
} else if (
|
|
82
|
-
AppBskyAgeassuranceDefs.isConfigRegionRuleIfAccountOlderThan(rule)
|
|
83
|
-
) {
|
|
84
|
-
if (data?.accountCreatedAt && !data?.assuredAge) {
|
|
85
|
-
const accountCreatedAt = new Date(data.accountCreatedAt)
|
|
86
|
-
const threshold = new Date(rule.date)
|
|
87
|
-
if (accountCreatedAt < threshold) {
|
|
88
|
-
return {
|
|
89
|
-
access: rule.access,
|
|
90
|
-
reason: rule.$type,
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
} else if (
|
|
95
|
-
AppBskyAgeassuranceDefs.isConfigRegionRuleIfDeclaredOverAge(rule)
|
|
96
|
-
) {
|
|
97
|
-
if (data?.declaredAge !== undefined && data.declaredAge >= rule.age) {
|
|
98
|
-
return {
|
|
99
|
-
access: rule.access,
|
|
100
|
-
reason: rule.$type,
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} else if (
|
|
104
|
-
AppBskyAgeassuranceDefs.isConfigRegionRuleIfDeclaredUnderAge(rule)
|
|
105
|
-
) {
|
|
106
|
-
if (data?.declaredAge !== undefined && data.declaredAge < rule.age) {
|
|
107
|
-
return {
|
|
108
|
-
access: rule.access,
|
|
109
|
-
reason: rule.$type,
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} else if (
|
|
113
|
-
AppBskyAgeassuranceDefs.isConfigRegionRuleIfAssuredOverAge(rule)
|
|
114
|
-
) {
|
|
115
|
-
if (data?.assuredAge && data.assuredAge >= rule.age) {
|
|
116
|
-
return {
|
|
117
|
-
access: rule.access,
|
|
118
|
-
reason: rule.$type,
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
} else if (
|
|
122
|
-
AppBskyAgeassuranceDefs.isConfigRegionRuleIfAssuredUnderAge(rule)
|
|
123
|
-
) {
|
|
124
|
-
if (data?.assuredAge && data.assuredAge < rule.age) {
|
|
125
|
-
return {
|
|
126
|
-
access: rule.access,
|
|
127
|
-
reason: rule.$type,
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
} else if (AppBskyAgeassuranceDefs.isConfigRegionRuleDefault(rule)) {
|
|
131
|
-
return {
|
|
132
|
-
access: rule.access,
|
|
133
|
-
reason: rule.$type,
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|