@oxy.so/core 4.5.0 → 4.8.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/dist/cjs/OxyServices.js +12 -0
- package/dist/cjs/api/organizations.js +94 -0
- package/dist/cjs/api/safety.js +26 -0
- package/dist/cjs/i18n/locales/en-US.js +61 -0
- package/dist/cjs/i18n/locales/es-ES.js +61 -0
- package/dist/esm/OxyServices.js +12 -0
- package/dist/esm/api/organizations.js +90 -0
- package/dist/esm/api/safety.js +22 -0
- package/dist/esm/i18n/locales/en-US.js +61 -0
- package/dist/esm/i18n/locales/es-ES.js +61 -0
- package/dist/types/OxyServices.d.ts +6 -0
- package/dist/types/api/accounts.d.ts +18 -2
- package/dist/types/api/organizations.d.ts +246 -0
- package/dist/types/api/safety.d.ts +14 -0
- package/dist/types/i18n/locales/en-US.d.ts +61 -0
- package/dist/types/i18n/locales/es-ES.d.ts +61 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/models/interfaces.d.ts +9 -1
- package/dist/types/server/requesterAssertion.d.ts +2 -2
- package/package.json +4 -4
- package/src/OxyServices.ts +14 -0
- package/src/api/accounts.ts +23 -2
- package/src/api/organizations.ts +180 -0
- package/src/api/safety.ts +33 -0
- package/src/i18n/locales/en-US.ts +68 -0
- package/src/i18n/locales/es-ES.ts +68 -0
- package/src/index.ts +11 -0
- package/src/models/interfaces.ts +9 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
organizationSchema,
|
|
3
|
+
organizationListSchema,
|
|
4
|
+
organizationBatchSchema,
|
|
5
|
+
organizationClaimSchema,
|
|
6
|
+
organizationExternalSchema,
|
|
7
|
+
organizationExternalResolutionSchema,
|
|
8
|
+
organizationImportBatchResultSchema,
|
|
9
|
+
organizationCurationResultSchema,
|
|
10
|
+
type OrganizationCuration,
|
|
11
|
+
type OrganizationImportBatch,
|
|
12
|
+
organizationClaimListSchema,
|
|
13
|
+
organizationClaimDetailSchema,
|
|
14
|
+
type OrganizationClaimListQuery,
|
|
15
|
+
type CreateOrganization,
|
|
16
|
+
type OrganizationSearch,
|
|
17
|
+
type UpdateOrganization,
|
|
18
|
+
type OrganizationClaimInput,
|
|
19
|
+
type OrganizationClaimReview,
|
|
20
|
+
type OrganizationExternalInput,
|
|
21
|
+
type OrganizationMerge,
|
|
22
|
+
} from '@oxy.so/contracts';
|
|
23
|
+
import type { OxyContext } from '../client/context';
|
|
24
|
+
import { evictOxyIdentityCache } from '../utils/identityCacheSweep';
|
|
25
|
+
|
|
26
|
+
/** The public canonical organization directory. Claiming changes authority, never identity. */
|
|
27
|
+
export class OrganizationsApi {
|
|
28
|
+
constructor(private readonly ctx: OxyContext) {}
|
|
29
|
+
async search(input: OrganizationSearch) {
|
|
30
|
+
return organizationListSchema.parse(
|
|
31
|
+
await this.ctx.request('GET', '/organizations', input, { cache: false, skipAuth: true }),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
async get(accountId: string) {
|
|
35
|
+
return organizationSchema.parse(
|
|
36
|
+
await this.ctx.request('GET', `/organizations/${encodeURIComponent(accountId)}`, undefined, {
|
|
37
|
+
cache: false,
|
|
38
|
+
skipAuth: true,
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
async batch(input: { ids: string[] }) {
|
|
43
|
+
return organizationBatchSchema.parse(
|
|
44
|
+
await this.ctx.request('POST', '/organizations/batch', input, {
|
|
45
|
+
cache: false,
|
|
46
|
+
skipAuth: true,
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
async create(input: CreateOrganization) {
|
|
51
|
+
return organizationSchema.parse(
|
|
52
|
+
await this.ctx.request('POST', '/organizations', input, { cache: false }),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
async update(accountId: string, input: UpdateOrganization) {
|
|
56
|
+
const result = organizationSchema.parse(
|
|
57
|
+
await this.ctx.request('PATCH', `/organizations/${encodeURIComponent(accountId)}`, input, {
|
|
58
|
+
cache: false,
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
evictOxyIdentityCache(this.ctx.http, accountId);
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
async claim(accountId: string, input: OrganizationClaimInput) {
|
|
66
|
+
return organizationClaimSchema.parse(
|
|
67
|
+
await this.ctx.request(
|
|
68
|
+
'POST',
|
|
69
|
+
`/organizations/${encodeURIComponent(accountId)}/claims`,
|
|
70
|
+
input,
|
|
71
|
+
{ cache: false },
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
async myClaims(input: OrganizationClaimListQuery = {}) {
|
|
76
|
+
return organizationClaimListSchema.parse(
|
|
77
|
+
await this.ctx.request('GET', '/organizations/claims/mine', input, { cache: false }),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
async myClaim(claimId: string) {
|
|
81
|
+
return organizationClaimDetailSchema.parse(
|
|
82
|
+
await this.ctx.request(
|
|
83
|
+
'GET',
|
|
84
|
+
`/organizations/claims/mine/${encodeURIComponent(claimId)}`,
|
|
85
|
+
undefined,
|
|
86
|
+
{ cache: false },
|
|
87
|
+
),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
async reviewClaims(input: OrganizationClaimListQuery = {}) {
|
|
91
|
+
return organizationClaimListSchema.parse(
|
|
92
|
+
await this.ctx.request('GET', '/organizations/claims/review', input, { cache: false }),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
async reviewClaimDetail(claimId: string) {
|
|
96
|
+
return organizationClaimDetailSchema.parse(
|
|
97
|
+
await this.ctx.request(
|
|
98
|
+
'GET',
|
|
99
|
+
`/organizations/claims/review/${encodeURIComponent(claimId)}`,
|
|
100
|
+
undefined,
|
|
101
|
+
{ cache: false },
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
async reviewClaim(claimId: string, input: OrganizationClaimReview) {
|
|
106
|
+
const result = organizationClaimSchema.parse(
|
|
107
|
+
await this.ctx.request(
|
|
108
|
+
'POST',
|
|
109
|
+
`/organizations/claims/${encodeURIComponent(claimId)}/review`,
|
|
110
|
+
input,
|
|
111
|
+
{ cache: false },
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
evictOxyIdentityCache(this.ctx.http, result.accountId);
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
async importBatch(input: OrganizationImportBatch) {
|
|
119
|
+
if (!this.ctx.service?.available)
|
|
120
|
+
throw new Error('Organization batch import requires an OxyServer service identity');
|
|
121
|
+
return organizationImportBatchResultSchema.parse(
|
|
122
|
+
await this.ctx.service.request('POST', '/organizations/external/batch', input, {
|
|
123
|
+
cache: false,
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
async importExternal(input: OrganizationExternalInput) {
|
|
128
|
+
return organizationExternalSchema.parse(
|
|
129
|
+
await this.ctx.request('POST', '/organizations/external', input, { cache: false }),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
async resolveExternal(input: { namespace: string; externalId: string }) {
|
|
133
|
+
return organizationExternalResolutionSchema.parse(
|
|
134
|
+
await this.ctx.request(
|
|
135
|
+
'GET',
|
|
136
|
+
`/organizations/external/${encodeURIComponent(input.namespace)}/${encodeURIComponent(input.externalId)}`,
|
|
137
|
+
undefined,
|
|
138
|
+
{ cache: false },
|
|
139
|
+
),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
async curate(accountId: string, input: OrganizationCuration) {
|
|
143
|
+
const result = organizationCurationResultSchema.parse(
|
|
144
|
+
await this.ctx.request(
|
|
145
|
+
'POST',
|
|
146
|
+
`/organizations/${encodeURIComponent(accountId)}/curation`,
|
|
147
|
+
input,
|
|
148
|
+
{ cache: false },
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
evictOxyIdentityCache(this.ctx.http, accountId);
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
async merge(accountId: string, input: OrganizationMerge) {
|
|
156
|
+
const result = organizationSchema.parse(
|
|
157
|
+
await this.ctx.request(
|
|
158
|
+
'POST',
|
|
159
|
+
`/organizations/${encodeURIComponent(accountId)}/merge`,
|
|
160
|
+
input,
|
|
161
|
+
{ cache: false },
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
evictOxyIdentityCache(this.ctx.http, accountId);
|
|
166
|
+
evictOxyIdentityCache(this.ctx.http, result.accountId);
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
async *iterate(input: OrganizationSearch) {
|
|
170
|
+
let cursor = input.cursor;
|
|
171
|
+
const seen = new Set<string>();
|
|
172
|
+
do {
|
|
173
|
+
const page = await this.search({ ...input, cursor });
|
|
174
|
+
yield* page.items;
|
|
175
|
+
cursor = page.nextCursor ?? undefined;
|
|
176
|
+
if (cursor && seen.has(cursor)) throw new Error('Organization directory repeated a cursor');
|
|
177
|
+
if (cursor) seen.add(cursor);
|
|
178
|
+
} while (cursor);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
profileAccessInputSchema,
|
|
3
|
+
profileAccessDecisionSchema,
|
|
4
|
+
type ProfileAccessInput,
|
|
5
|
+
} from '@oxy.so/contracts';
|
|
6
|
+
import type { OxyContext } from '../client/context';
|
|
7
|
+
|
|
8
|
+
export class SafetyApi {
|
|
9
|
+
constructor(private readonly ctx: OxyContext) {}
|
|
10
|
+
/** Server callers must explicitly supply the incoming user bearer or null for anonymous access. */
|
|
11
|
+
async profileAccess(input: ProfileAccessInput, options?: { accessToken: string | null }) {
|
|
12
|
+
if (this.ctx.service && options === undefined)
|
|
13
|
+
throw new Error('Server profile access requires an explicit requester bearer or null');
|
|
14
|
+
return profileAccessDecisionSchema.parse(
|
|
15
|
+
await this.ctx.request(
|
|
16
|
+
'GET',
|
|
17
|
+
'/safety/profile-access',
|
|
18
|
+
profileAccessInputSchema.parse(input),
|
|
19
|
+
{
|
|
20
|
+
cache: false,
|
|
21
|
+
...(options
|
|
22
|
+
? {
|
|
23
|
+
skipAuth: true,
|
|
24
|
+
headers: options.accessToken
|
|
25
|
+
? { Authorization: `Bearer ${options.accessToken}` }
|
|
26
|
+
: {},
|
|
27
|
+
}
|
|
28
|
+
: {}),
|
|
29
|
+
},
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,4 +1,72 @@
|
|
|
1
1
|
const dict = {
|
|
2
|
+
organizationDirectory: {
|
|
3
|
+
invalidName: 'Use a plain-text organization name (up to 300 characters).',
|
|
4
|
+
review: {
|
|
5
|
+
title: 'Review organization access requests',
|
|
6
|
+
forbidden: 'This action requires current staff authorization.',
|
|
7
|
+
empty: 'No requests are waiting for review.',
|
|
8
|
+
hint: 'Review the supporting evidence independently. Approval grants management access to the existing organization account.',
|
|
9
|
+
independent: 'Another reviewer must decide your own request.',
|
|
10
|
+
evidence: 'Review evidence reference',
|
|
11
|
+
approved: 'Approve access',
|
|
12
|
+
rejected: 'Reject request',
|
|
13
|
+
conflict:
|
|
14
|
+
'This request or organization changed. Refresh the queue before making another decision.',
|
|
15
|
+
failed: 'The review could not be saved. Your decision has not been confirmed.',
|
|
16
|
+
},
|
|
17
|
+
title: 'Organization directory',
|
|
18
|
+
search: 'Find your organization',
|
|
19
|
+
searchHint:
|
|
20
|
+
'Find an existing organization before creating an account. Its identity is shared across Oxy apps.',
|
|
21
|
+
empty: 'No organizations found.',
|
|
22
|
+
retry: 'Try again',
|
|
23
|
+
loadMore: 'Show more',
|
|
24
|
+
failed: 'Could not load the organization directory.',
|
|
25
|
+
unclaimed: 'Unclaimed',
|
|
26
|
+
claimed: 'Managed organization',
|
|
27
|
+
inactive: 'Inactive',
|
|
28
|
+
newAccount: 'Create a new organization account',
|
|
29
|
+
newAccountHint:
|
|
30
|
+
'Create and manage a new organization. Choose an existing entry if your organization is already listed.',
|
|
31
|
+
addDirectory: 'Add to directory',
|
|
32
|
+
directoryHint:
|
|
33
|
+
'This creates a shared, unclaimed entry. It does not give you access to manage the organization.',
|
|
34
|
+
added: 'Organization added to the directory',
|
|
35
|
+
back: 'Back to search',
|
|
36
|
+
type: 'Organization type',
|
|
37
|
+
types: {
|
|
38
|
+
business: 'Business',
|
|
39
|
+
nonprofit: 'Nonprofit',
|
|
40
|
+
public_body: 'Public body',
|
|
41
|
+
community: 'Community',
|
|
42
|
+
other: 'Other',
|
|
43
|
+
},
|
|
44
|
+
name: 'Organization name',
|
|
45
|
+
legalName: 'Legal name',
|
|
46
|
+
website: 'Website',
|
|
47
|
+
country: 'Country code',
|
|
48
|
+
save: 'Save organization',
|
|
49
|
+
saved: 'Organization updated',
|
|
50
|
+
invalid:
|
|
51
|
+
'Check the organization details. Use a complete website URL and a two-letter country code.',
|
|
52
|
+
saveFailed: 'Could not save the organization. Try again.',
|
|
53
|
+
claim: 'Request management access',
|
|
54
|
+
claimHint:
|
|
55
|
+
'Oxy reviews evidence of your authority. A request does not grant access or verify your role.',
|
|
56
|
+
evidence: 'Supporting evidence reference',
|
|
57
|
+
evidenceHint:
|
|
58
|
+
'Provide a reference to your private verification case or supporting records. Only you and reviewers can read it.',
|
|
59
|
+
claimSent: 'Request submitted for review',
|
|
60
|
+
claimFailed: 'Could not submit your request. Try again.',
|
|
61
|
+
claimedHint: 'This organization already has managers. Contact a manager to request access.',
|
|
62
|
+
requests: 'Organization access requests',
|
|
63
|
+
pending: 'Pending review',
|
|
64
|
+
approved: 'Approved',
|
|
65
|
+
rejected: 'Rejected',
|
|
66
|
+
requestError: 'Could not load your access requests.',
|
|
67
|
+
view: 'Organization details',
|
|
68
|
+
readonly: 'You do not have permission to edit this organization.',
|
|
69
|
+
},
|
|
2
70
|
signin: {
|
|
3
71
|
title: 'Sign in',
|
|
4
72
|
subtitle: 'Use your Oxy account',
|
|
@@ -1,4 +1,72 @@
|
|
|
1
1
|
const dict = {
|
|
2
|
+
organizationDirectory: {
|
|
3
|
+
invalidName: 'Usa un nombre de organización en texto plano (hasta 300 caracteres).',
|
|
4
|
+
review: {
|
|
5
|
+
title: 'Revisar solicitudes de acceso a organizaciones',
|
|
6
|
+
forbidden: 'Esta acción requiere autorización vigente del personal de Oxy.',
|
|
7
|
+
empty: 'No hay solicitudes pendientes de revisión.',
|
|
8
|
+
hint: 'Revisa la documentación de forma independiente. La aprobación concede acceso de administración a la cuenta existente.',
|
|
9
|
+
independent: 'Otro revisor debe decidir sobre tu propia solicitud.',
|
|
10
|
+
evidence: 'Referencia de la documentación de revisión',
|
|
11
|
+
approved: 'Aprobar acceso',
|
|
12
|
+
rejected: 'Rechazar solicitud',
|
|
13
|
+
conflict:
|
|
14
|
+
'La solicitud o la organización ha cambiado. Actualiza la lista antes de tomar otra decisión.',
|
|
15
|
+
failed: 'No se pudo guardar la revisión. Tu decisión no se ha confirmado.',
|
|
16
|
+
},
|
|
17
|
+
title: 'Directorio de organizaciones',
|
|
18
|
+
search: 'Busca tu organización',
|
|
19
|
+
searchHint:
|
|
20
|
+
'Busca una organización existente antes de crear una cuenta. Su identidad se comparte entre las aplicaciones de Oxy.',
|
|
21
|
+
empty: 'No se encontraron organizaciones.',
|
|
22
|
+
retry: 'Reintentar',
|
|
23
|
+
loadMore: 'Mostrar más',
|
|
24
|
+
failed: 'No se pudo cargar el directorio de organizaciones.',
|
|
25
|
+
unclaimed: 'Sin reclamar',
|
|
26
|
+
claimed: 'Organización administrada',
|
|
27
|
+
inactive: 'Inactiva',
|
|
28
|
+
newAccount: 'Crear una nueva cuenta de organización',
|
|
29
|
+
newAccountHint:
|
|
30
|
+
'Crea y administra una organización nueva. Si ya aparece en el directorio, elige la entrada existente.',
|
|
31
|
+
addDirectory: 'Añadir al directorio',
|
|
32
|
+
directoryHint:
|
|
33
|
+
'Esto crea una entrada compartida sin reclamar. No te da acceso para administrar la organización.',
|
|
34
|
+
added: 'Organización añadida al directorio',
|
|
35
|
+
back: 'Volver a la búsqueda',
|
|
36
|
+
type: 'Tipo de organización',
|
|
37
|
+
types: {
|
|
38
|
+
business: 'Empresa',
|
|
39
|
+
nonprofit: 'Sin ánimo de lucro',
|
|
40
|
+
public_body: 'Organismo público',
|
|
41
|
+
community: 'Comunidad',
|
|
42
|
+
other: 'Otro',
|
|
43
|
+
},
|
|
44
|
+
name: 'Nombre de la organización',
|
|
45
|
+
legalName: 'Nombre legal',
|
|
46
|
+
website: 'Sitio web',
|
|
47
|
+
country: 'Código de país',
|
|
48
|
+
save: 'Guardar organización',
|
|
49
|
+
saved: 'Organización actualizada',
|
|
50
|
+
invalid: 'Revisa los datos. Usa una URL completa y un código de país de dos letras.',
|
|
51
|
+
saveFailed: 'No se pudo guardar la organización. Inténtalo de nuevo.',
|
|
52
|
+
claim: 'Solicitar acceso de administración',
|
|
53
|
+
claimHint:
|
|
54
|
+
'Oxy revisará las pruebas de tu autorización. Una solicitud no concede acceso ni verifica tu cargo.',
|
|
55
|
+
evidence: 'Referencia de la documentación',
|
|
56
|
+
evidenceHint:
|
|
57
|
+
'Indica una referencia a tu expediente privado de verificación o documentación acreditativa. Solo tú y los revisores podéis verla.',
|
|
58
|
+
claimSent: 'Solicitud enviada para revisión',
|
|
59
|
+
claimFailed: 'No se pudo enviar la solicitud. Inténtalo de nuevo.',
|
|
60
|
+
claimedHint:
|
|
61
|
+
'Esta organización ya tiene administradores. Contacta con uno para solicitar acceso.',
|
|
62
|
+
requests: 'Solicitudes de acceso a organizaciones',
|
|
63
|
+
pending: 'Pendiente de revisión',
|
|
64
|
+
approved: 'Aprobada',
|
|
65
|
+
rejected: 'Rechazada',
|
|
66
|
+
requestError: 'No se pudieron cargar tus solicitudes.',
|
|
67
|
+
view: 'Datos de la organización',
|
|
68
|
+
readonly: 'No tienes permiso para editar esta organización.',
|
|
69
|
+
},
|
|
2
70
|
signin: {
|
|
3
71
|
title: 'Iniciar sesión',
|
|
4
72
|
subtitle: 'Usa tu cuenta de Oxy',
|
package/src/index.ts
CHANGED
|
@@ -605,3 +605,14 @@ export type { PkcePair, BuildOAuthAuthorizeUrlParams, OxyAuthScreen } from './ut
|
|
|
605
605
|
// Constants
|
|
606
606
|
// ---------------------------------------------------------------------------
|
|
607
607
|
export { packageInfo } from './constants/version';
|
|
608
|
+
|
|
609
|
+
export type {
|
|
610
|
+
Organization,
|
|
611
|
+
OrganizationType,
|
|
612
|
+
CreateOrganization,
|
|
613
|
+
UpdateOrganization,
|
|
614
|
+
OrganizationSearch,
|
|
615
|
+
OrganizationClaim,
|
|
616
|
+
} from '@oxy.so/contracts';
|
|
617
|
+
|
|
618
|
+
export type { ProfileAccessInput, ProfileAccessDecision } from '@oxy.so/contracts';
|
package/src/models/interfaces.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AccountKind,
|
|
3
3
|
AccountCategoryId,
|
|
4
|
+
AvatarCharacter,
|
|
4
5
|
UserNameResponse,
|
|
5
6
|
UserRelationship,
|
|
6
7
|
ThemePreference,
|
|
@@ -180,7 +181,15 @@ export interface User {
|
|
|
180
181
|
isFederated?: boolean;
|
|
181
182
|
/** Allow sharing this user's content on the fediverse. Defaults to true. */
|
|
182
183
|
fediverseSharing?: boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Derived from `kind === 'bot'` (the SOLE definition), NOT from `type`.
|
|
186
|
+
*/
|
|
183
187
|
isAgent?: boolean;
|
|
188
|
+
/**
|
|
189
|
+
* The 3D avatar character of a `kind: 'bot'` account, drawn by Bloom's
|
|
190
|
+
* `Avatar`. Present ONLY on a bot; absent for every other kind.
|
|
191
|
+
*/
|
|
192
|
+
character?: AvatarCharacter;
|
|
184
193
|
isAutomated?: boolean;
|
|
185
194
|
instance?: string;
|
|
186
195
|
federation?: {
|