@oxy.so/core 4.6.0 → 4.9.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.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OxyServices = exports.OXY_API_URL = exports.OXY_CLOUD_URL = void 0;
4
+ const safety_1 = require("./api/safety");
4
5
  /**
5
6
  * `OxyServices` — the Oxy API client.
6
7
  *
@@ -33,6 +34,7 @@ const privacy_1 = require("./api/privacy");
33
34
  const notifications_1 = require("./api/notifications");
34
35
  const assets_1 = require("./api/assets");
35
36
  const identity_1 = require("./api/identity");
37
+ const organizations_1 = require("./api/organizations");
36
38
  const accounts_1 = require("./api/accounts");
37
39
  const apps_1 = require("./api/apps");
38
40
  const linkedAccounts_1 = require("./api/linkedAccounts");
@@ -196,6 +198,16 @@ class OxyServices {
196
198
  return this._identity;
197
199
  }
198
200
  /** The account graph: accounts and their members. */
201
+ get safety() {
202
+ if (!this._safety)
203
+ this._safety = new safety_1.SafetyApi(this.context);
204
+ return this._safety;
205
+ }
206
+ get organizations() {
207
+ if (!this._organizations)
208
+ this._organizations = new organizations_1.OrganizationsApi(this.context);
209
+ return this._organizations;
210
+ }
199
211
  get accounts() {
200
212
  if (!this._accounts)
201
213
  this._accounts = new accounts_1.AccountsApi(this.context);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AccountsApi = exports.AccountAgentKeysApi = exports.AccountMembersApi = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.isOperatorSwitchTargetKind = exports.isDelegatedActAsEligibleKind = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_KINDS = exports.ACCOUNT_CATEGORY_IDS = void 0;
3
+ exports.AccountsApi = exports.AccountInvitationsApi = exports.AccountAgentKeysApi = exports.AccountMembersApi = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.isOperatorSwitchTargetKind = exports.isDelegatedActAsEligibleKind = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_KINDS = exports.ACCOUNT_CATEGORY_IDS = void 0;
4
4
  /**
5
5
  * `oxy.accounts` — the unified account graph (`/accounts`).
6
6
  *
@@ -65,7 +65,8 @@ class AccountMembersApi {
65
65
  return res.members ?? [];
66
66
  }
67
67
  /**
68
- * Add a member to an account.
68
+ * @deprecated The API returns 410. Use accounts.invitations.create with an email.
69
+ * Access is granted only after recipient confirmation.
69
70
  * @param accountId - The account's `_id`.
70
71
  * @param data - Target user's username or email and role (never `owner`).
71
72
  * The server resolves `usernameOrEmail` to a personal account; an unknown
@@ -119,10 +120,40 @@ class AccountAgentKeysApi {
119
120
  }
120
121
  }
121
122
  exports.AccountAgentKeysApi = AccountAgentKeysApi;
123
+ /** Consent-based invitations; never cached or queued as offline mutations. */
124
+ class AccountInvitationsApi {
125
+ constructor(ctx) {
126
+ this.ctx = ctx;
127
+ }
128
+ async list(accountId) {
129
+ return (await this.ctx.request('GET', `/accounts/${enc(accountId)}/invitations`, undefined, { cache: false })).invitations;
130
+ }
131
+ async create(accountId, data) {
132
+ return (await this.ctx.request('POST', `/accounts/${enc(accountId)}/invitations`, data, { cache: false })).invitation;
133
+ }
134
+ async resend(accountId, id) {
135
+ return (await this.ctx.request('POST', `/accounts/${enc(accountId)}/invitations/${enc(id)}/resend`, undefined, { cache: false })).invitation;
136
+ }
137
+ async cancel(accountId, id) {
138
+ return this.ctx.request('DELETE', `/accounts/${enc(accountId)}/invitations/${enc(id)}`, undefined, { cache: false });
139
+ }
140
+ async preview(token) {
141
+ return this.ctx.request('POST', '/accounts/invitations/preview', { token }, { cache: false });
142
+ }
143
+ /** selectedUserId must equal the currently authenticated personal account. */
144
+ async accept(token, selectedUserId) {
145
+ const result = await this.ctx.request('POST', '/accounts/invitations/accept', { token, selectedUserId, confirmed: true }, { cache: false });
146
+ invalidateAccountMembership(this.ctx, result.accountId);
147
+ (0, accountCacheSweep_1.evictOxyAccountForestCache)(this.ctx.http);
148
+ return result;
149
+ }
150
+ }
151
+ exports.AccountInvitationsApi = AccountInvitationsApi;
122
152
  class AccountsApi {
123
153
  constructor(ctx) {
124
154
  this.ctx = ctx;
125
155
  this.members = new AccountMembersApi(ctx);
156
+ this.invitations = new AccountInvitationsApi(ctx);
126
157
  this.agentKeys = new AccountAgentKeysApi(ctx);
127
158
  }
128
159
  /**
@@ -22,6 +22,15 @@ class BillingApi {
22
22
  constructor(ctx) {
23
23
  this.ctx = ctx;
24
24
  }
25
+ async renewProductSubscription(request) {
26
+ return contracts_1.personalPlanCheckoutResultSchema.parse(await this.ctx.request('POST', '/billing/product-subscriptions/renew', contracts_1.productSubscriptionRenewalRequestSchema.parse(request), { cache: false }));
27
+ }
28
+ async startProductPlanCheckout(request) {
29
+ return contracts_1.personalPlanCheckoutResultSchema.parse(await this.ctx.request('POST', '/billing/checkout/product-plan', contracts_1.productPlanCheckoutRequestSchema.parse(request), { cache: false }));
30
+ }
31
+ async productPlans(productId) {
32
+ return contracts_1.productPlanCatalogueSchema.parse(await this.ctx.request('GET', `/billing/product-plans/${encodeURIComponent(productId)}`, undefined, { cache: false }));
33
+ }
25
34
  async startPersonalPlanCheckout(request) {
26
35
  return contracts_1.personalPlanCheckoutResultSchema.parse(await this.ctx.request('POST', '/billing/checkout/personal-plan', contracts_1.personalPlanCheckoutRequestSchema.parse(request), { cache: false }));
27
36
  }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OrganizationsApi = void 0;
4
+ const contracts_1 = require("@oxy.so/contracts");
5
+ const identityCacheSweep_1 = require("../utils/identityCacheSweep");
6
+ /** The public canonical organization directory. Claiming changes authority, never identity. */
7
+ class OrganizationsApi {
8
+ constructor(ctx) {
9
+ this.ctx = ctx;
10
+ }
11
+ async search(input) {
12
+ return contracts_1.organizationListSchema.parse(await this.ctx.request('GET', '/organizations', input, { cache: false, skipAuth: true }));
13
+ }
14
+ async get(accountId) {
15
+ return contracts_1.organizationSchema.parse(await this.ctx.request('GET', `/organizations/${encodeURIComponent(accountId)}`, undefined, {
16
+ cache: false,
17
+ skipAuth: true,
18
+ }));
19
+ }
20
+ async batch(input) {
21
+ return contracts_1.organizationBatchSchema.parse(await this.ctx.request('POST', '/organizations/batch', input, {
22
+ cache: false,
23
+ skipAuth: true,
24
+ }));
25
+ }
26
+ async create(input) {
27
+ return contracts_1.organizationSchema.parse(await this.ctx.request('POST', '/organizations', input, { cache: false }));
28
+ }
29
+ async update(accountId, input) {
30
+ const result = contracts_1.organizationSchema.parse(await this.ctx.request('PATCH', `/organizations/${encodeURIComponent(accountId)}`, input, {
31
+ cache: false,
32
+ }));
33
+ (0, identityCacheSweep_1.evictOxyIdentityCache)(this.ctx.http, accountId);
34
+ return result;
35
+ }
36
+ async claim(accountId, input) {
37
+ return contracts_1.organizationClaimSchema.parse(await this.ctx.request('POST', `/organizations/${encodeURIComponent(accountId)}/claims`, input, { cache: false }));
38
+ }
39
+ async myClaims(input = {}) {
40
+ return contracts_1.organizationClaimListSchema.parse(await this.ctx.request('GET', '/organizations/claims/mine', input, { cache: false }));
41
+ }
42
+ async myClaim(claimId) {
43
+ return contracts_1.organizationClaimDetailSchema.parse(await this.ctx.request('GET', `/organizations/claims/mine/${encodeURIComponent(claimId)}`, undefined, { cache: false }));
44
+ }
45
+ async reviewClaims(input = {}) {
46
+ return contracts_1.organizationClaimListSchema.parse(await this.ctx.request('GET', '/organizations/claims/review', input, { cache: false }));
47
+ }
48
+ async reviewClaimDetail(claimId) {
49
+ return contracts_1.organizationClaimDetailSchema.parse(await this.ctx.request('GET', `/organizations/claims/review/${encodeURIComponent(claimId)}`, undefined, { cache: false }));
50
+ }
51
+ async reviewClaim(claimId, input) {
52
+ const result = contracts_1.organizationClaimSchema.parse(await this.ctx.request('POST', `/organizations/claims/${encodeURIComponent(claimId)}/review`, input, { cache: false }));
53
+ (0, identityCacheSweep_1.evictOxyIdentityCache)(this.ctx.http, result.accountId);
54
+ return result;
55
+ }
56
+ async importBatch(input) {
57
+ if (!this.ctx.service?.available)
58
+ throw new Error('Organization batch import requires an OxyServer service identity');
59
+ return contracts_1.organizationImportBatchResultSchema.parse(await this.ctx.service.request('POST', '/organizations/external/batch', input, {
60
+ cache: false,
61
+ }));
62
+ }
63
+ async importExternal(input) {
64
+ return contracts_1.organizationExternalSchema.parse(await this.ctx.request('POST', '/organizations/external', input, { cache: false }));
65
+ }
66
+ async resolveExternal(input) {
67
+ return contracts_1.organizationExternalResolutionSchema.parse(await this.ctx.request('GET', `/organizations/external/${encodeURIComponent(input.namespace)}/${encodeURIComponent(input.externalId)}`, undefined, { cache: false }));
68
+ }
69
+ async curate(accountId, input) {
70
+ const result = contracts_1.organizationCurationResultSchema.parse(await this.ctx.request('POST', `/organizations/${encodeURIComponent(accountId)}/curation`, input, { cache: false }));
71
+ (0, identityCacheSweep_1.evictOxyIdentityCache)(this.ctx.http, accountId);
72
+ return result;
73
+ }
74
+ async merge(accountId, input) {
75
+ const result = contracts_1.organizationSchema.parse(await this.ctx.request('POST', `/organizations/${encodeURIComponent(accountId)}/merge`, input, { cache: false }));
76
+ (0, identityCacheSweep_1.evictOxyIdentityCache)(this.ctx.http, accountId);
77
+ (0, identityCacheSweep_1.evictOxyIdentityCache)(this.ctx.http, result.accountId);
78
+ return result;
79
+ }
80
+ async *iterate(input) {
81
+ let cursor = input.cursor;
82
+ const seen = new Set();
83
+ do {
84
+ const page = await this.search({ ...input, cursor });
85
+ yield* page.items;
86
+ cursor = page.nextCursor ?? undefined;
87
+ if (cursor && seen.has(cursor))
88
+ throw new Error('Organization directory repeated a cursor');
89
+ if (cursor)
90
+ seen.add(cursor);
91
+ } while (cursor);
92
+ }
93
+ }
94
+ exports.OrganizationsApi = OrganizationsApi;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SafetyApi = void 0;
4
+ const contracts_1 = require("@oxy.so/contracts");
5
+ class SafetyApi {
6
+ constructor(ctx) {
7
+ this.ctx = ctx;
8
+ }
9
+ /** Server callers must explicitly supply the incoming user bearer or null for anonymous access. */
10
+ async profileAccess(input, options) {
11
+ if (this.ctx.service && options === undefined)
12
+ throw new Error('Server profile access requires an explicit requester bearer or null');
13
+ return contracts_1.profileAccessDecisionSchema.parse(await this.ctx.request('GET', '/safety/profile-access', contracts_1.profileAccessInputSchema.parse(input), {
14
+ cache: false,
15
+ ...(options
16
+ ? {
17
+ skipAuth: true,
18
+ headers: options.accessToken
19
+ ? { Authorization: `Bearer ${options.accessToken}` }
20
+ : {},
21
+ }
22
+ : {}),
23
+ }));
24
+ }
25
+ }
26
+ exports.SafetyApi = SafetyApi;
@@ -1,6 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const dict = {
4
+ organizationDirectory: {
5
+ invalidName: 'Use a plain-text organization name (up to 300 characters).',
6
+ review: {
7
+ title: 'Review organization access requests',
8
+ forbidden: 'This action requires current staff authorization.',
9
+ empty: 'No requests are waiting for review.',
10
+ hint: 'Review the supporting evidence independently. Approval grants management access to the existing organization account.',
11
+ independent: 'Another reviewer must decide your own request.',
12
+ evidence: 'Review evidence reference',
13
+ approved: 'Approve access',
14
+ rejected: 'Reject request',
15
+ conflict: 'This request or organization changed. Refresh the queue before making another decision.',
16
+ failed: 'The review could not be saved. Your decision has not been confirmed.',
17
+ },
18
+ title: 'Organization directory',
19
+ search: 'Find your organization',
20
+ searchHint: '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: 'Create and manage a new organization. Choose an existing entry if your organization is already listed.',
30
+ addDirectory: 'Add to directory',
31
+ directoryHint: 'This creates a shared, unclaimed entry. It does not give you access to manage the organization.',
32
+ added: 'Organization added to the directory',
33
+ back: 'Back to search',
34
+ type: 'Organization type',
35
+ types: {
36
+ business: 'Business',
37
+ nonprofit: 'Nonprofit',
38
+ public_body: 'Public body',
39
+ community: 'Community',
40
+ other: 'Other',
41
+ },
42
+ name: 'Organization name',
43
+ legalName: 'Legal name',
44
+ website: 'Website',
45
+ country: 'Country code',
46
+ save: 'Save organization',
47
+ saved: 'Organization updated',
48
+ invalid: 'Check the organization details. Use a complete website URL and a two-letter country code.',
49
+ saveFailed: 'Could not save the organization. Try again.',
50
+ claim: 'Request management access',
51
+ claimHint: 'Oxy reviews evidence of your authority. A request does not grant access or verify your role.',
52
+ evidence: 'Supporting evidence reference',
53
+ evidenceHint: 'Provide a reference to your private verification case or supporting records. Only you and reviewers can read it.',
54
+ claimSent: 'Request submitted for review',
55
+ claimFailed: 'Could not submit your request. Try again.',
56
+ claimedHint: 'This organization already has managers. Contact a manager to request access.',
57
+ requests: 'Organization access requests',
58
+ pending: 'Pending review',
59
+ approved: 'Approved',
60
+ rejected: 'Rejected',
61
+ requestError: 'Could not load your access requests.',
62
+ view: 'Organization details',
63
+ readonly: 'You do not have permission to edit this organization.',
64
+ },
4
65
  signin: {
5
66
  title: 'Sign in',
6
67
  subtitle: 'Use your Oxy account',
@@ -1375,6 +1436,13 @@ const dict = {
1375
1436
  },
1376
1437
  },
1377
1438
  preferences: {
1439
+ displayCurrency: {
1440
+ label: 'Display currency',
1441
+ saveFailed: 'Could not save display currency. Please try again.',
1442
+ unavailable: 'Equivalent in {{currency}} unavailable',
1443
+ approximate: 'Approx. {{amount}}',
1444
+ estimate: 'Display estimate. The final payable amount is quoted in FAIR at checkout.',
1445
+ },
1378
1446
  title: 'Preferences',
1379
1447
  subtitle: 'Theme, motion, and regional settings',
1380
1448
  sections: {
@@ -1,6 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const dict = {
4
+ organizationDirectory: {
5
+ invalidName: 'Usa un nombre de organización en texto plano (hasta 300 caracteres).',
6
+ review: {
7
+ title: 'Revisar solicitudes de acceso a organizaciones',
8
+ forbidden: 'Esta acción requiere autorización vigente del personal de Oxy.',
9
+ empty: 'No hay solicitudes pendientes de revisión.',
10
+ hint: 'Revisa la documentación de forma independiente. La aprobación concede acceso de administración a la cuenta existente.',
11
+ independent: 'Otro revisor debe decidir sobre tu propia solicitud.',
12
+ evidence: 'Referencia de la documentación de revisión',
13
+ approved: 'Aprobar acceso',
14
+ rejected: 'Rechazar solicitud',
15
+ conflict: 'La solicitud o la organización ha cambiado. Actualiza la lista antes de tomar otra decisión.',
16
+ failed: 'No se pudo guardar la revisión. Tu decisión no se ha confirmado.',
17
+ },
18
+ title: 'Directorio de organizaciones',
19
+ search: 'Busca tu organización',
20
+ searchHint: '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: 'Crea y administra una organización nueva. Si ya aparece en el directorio, elige la entrada existente.',
30
+ addDirectory: 'Añadir al directorio',
31
+ directoryHint: 'Esto crea una entrada compartida sin reclamar. No te da acceso para administrar la organización.',
32
+ added: 'Organización añadida al directorio',
33
+ back: 'Volver a la búsqueda',
34
+ type: 'Tipo de organización',
35
+ types: {
36
+ business: 'Empresa',
37
+ nonprofit: 'Sin ánimo de lucro',
38
+ public_body: 'Organismo público',
39
+ community: 'Comunidad',
40
+ other: 'Otro',
41
+ },
42
+ name: 'Nombre de la organización',
43
+ legalName: 'Nombre legal',
44
+ website: 'Sitio web',
45
+ country: 'Código de país',
46
+ save: 'Guardar organización',
47
+ saved: 'Organización actualizada',
48
+ invalid: 'Revisa los datos. Usa una URL completa y un código de país de dos letras.',
49
+ saveFailed: 'No se pudo guardar la organización. Inténtalo de nuevo.',
50
+ claim: 'Solicitar acceso de administración',
51
+ claimHint: 'Oxy revisará las pruebas de tu autorización. Una solicitud no concede acceso ni verifica tu cargo.',
52
+ evidence: 'Referencia de la documentación',
53
+ evidenceHint: 'Indica una referencia a tu expediente privado de verificación o documentación acreditativa. Solo tú y los revisores podéis verla.',
54
+ claimSent: 'Solicitud enviada para revisión',
55
+ claimFailed: 'No se pudo enviar la solicitud. Inténtalo de nuevo.',
56
+ claimedHint: 'Esta organización ya tiene administradores. Contacta con uno para solicitar acceso.',
57
+ requests: 'Solicitudes de acceso a organizaciones',
58
+ pending: 'Pendiente de revisión',
59
+ approved: 'Aprobada',
60
+ rejected: 'Rechazada',
61
+ requestError: 'No se pudieron cargar tus solicitudes.',
62
+ view: 'Datos de la organización',
63
+ readonly: 'No tienes permiso para editar esta organización.',
64
+ },
4
65
  signin: {
5
66
  title: 'Iniciar sesión',
6
67
  subtitle: 'Usa tu cuenta de Oxy',
@@ -1375,6 +1436,13 @@ const dict = {
1375
1436
  },
1376
1437
  },
1377
1438
  preferences: {
1439
+ displayCurrency: {
1440
+ label: 'Moneda mostrada',
1441
+ saveFailed: 'No se ha podido guardar la moneda. Inténtalo de nuevo.',
1442
+ unavailable: 'Equivalencia en {{currency}} no disponible',
1443
+ approximate: 'Aprox. {{amount}}',
1444
+ estimate: 'Importe orientativo. El importe final se cotiza en FAIR al pagar.',
1445
+ },
1378
1446
  title: 'Preferencias',
1379
1447
  subtitle: 'Tema, movimiento y configuración regional',
1380
1448
  sections: {
@@ -1,3 +1,4 @@
1
+ import { SafetyApi } from './api/safety.js';
1
2
  /**
2
3
  * `OxyServices` — the Oxy API client.
3
4
  *
@@ -30,6 +31,7 @@ import { PrivacyApi } from './api/privacy.js';
30
31
  import { NotificationsApi } from './api/notifications.js';
31
32
  import { AssetsApi } from './api/assets.js';
32
33
  import { IdentityApi } from './api/identity.js';
34
+ import { OrganizationsApi } from './api/organizations.js';
33
35
  import { AccountsApi } from './api/accounts.js';
34
36
  import { AppsApi } from './api/apps.js';
35
37
  import { LinkedAccountsApi } from './api/linkedAccounts.js';
@@ -193,6 +195,16 @@ export class OxyServices {
193
195
  return this._identity;
194
196
  }
195
197
  /** The account graph: accounts and their members. */
198
+ get safety() {
199
+ if (!this._safety)
200
+ this._safety = new SafetyApi(this.context);
201
+ return this._safety;
202
+ }
203
+ get organizations() {
204
+ if (!this._organizations)
205
+ this._organizations = new OrganizationsApi(this.context);
206
+ return this._organizations;
207
+ }
196
208
  get accounts() {
197
209
  if (!this._accounts)
198
210
  this._accounts = new AccountsApi(this.context);
@@ -54,7 +54,8 @@ export class AccountMembersApi {
54
54
  return res.members ?? [];
55
55
  }
56
56
  /**
57
- * Add a member to an account.
57
+ * @deprecated The API returns 410. Use accounts.invitations.create with an email.
58
+ * Access is granted only after recipient confirmation.
58
59
  * @param accountId - The account's `_id`.
59
60
  * @param data - Target user's username or email and role (never `owner`).
60
61
  * The server resolves `usernameOrEmail` to a personal account; an unknown
@@ -106,10 +107,39 @@ export class AccountAgentKeysApi {
106
107
  return agentKeyOperationResultSchema.parse(response);
107
108
  }
108
109
  }
110
+ /** Consent-based invitations; never cached or queued as offline mutations. */
111
+ export class AccountInvitationsApi {
112
+ constructor(ctx) {
113
+ this.ctx = ctx;
114
+ }
115
+ async list(accountId) {
116
+ return (await this.ctx.request('GET', `/accounts/${enc(accountId)}/invitations`, undefined, { cache: false })).invitations;
117
+ }
118
+ async create(accountId, data) {
119
+ return (await this.ctx.request('POST', `/accounts/${enc(accountId)}/invitations`, data, { cache: false })).invitation;
120
+ }
121
+ async resend(accountId, id) {
122
+ return (await this.ctx.request('POST', `/accounts/${enc(accountId)}/invitations/${enc(id)}/resend`, undefined, { cache: false })).invitation;
123
+ }
124
+ async cancel(accountId, id) {
125
+ return this.ctx.request('DELETE', `/accounts/${enc(accountId)}/invitations/${enc(id)}`, undefined, { cache: false });
126
+ }
127
+ async preview(token) {
128
+ return this.ctx.request('POST', '/accounts/invitations/preview', { token }, { cache: false });
129
+ }
130
+ /** selectedUserId must equal the currently authenticated personal account. */
131
+ async accept(token, selectedUserId) {
132
+ const result = await this.ctx.request('POST', '/accounts/invitations/accept', { token, selectedUserId, confirmed: true }, { cache: false });
133
+ invalidateAccountMembership(this.ctx, result.accountId);
134
+ evictOxyAccountForestCache(this.ctx.http);
135
+ return result;
136
+ }
137
+ }
109
138
  export class AccountsApi {
110
139
  constructor(ctx) {
111
140
  this.ctx = ctx;
112
141
  this.members = new AccountMembersApi(ctx);
142
+ this.invitations = new AccountInvitationsApi(ctx);
113
143
  this.agentKeys = new AccountAgentKeysApi(ctx);
114
144
  }
115
145
  /**
@@ -1,4 +1,4 @@
1
- import { personalPlanCheckoutRequestSchema, personalPlanCheckoutResultSchema, } from '@oxy.so/contracts';
1
+ import { personalPlanCheckoutRequestSchema, productPlanCheckoutRequestSchema, productSubscriptionRenewalRequestSchema, productPlanCatalogueSchema, personalPlanCheckoutResultSchema, } from '@oxy.so/contracts';
2
2
  /**
3
3
  * `oxy.billing` — the signed-in user's subscription, wallet and payment history.
4
4
  *
@@ -19,6 +19,15 @@ export class BillingApi {
19
19
  constructor(ctx) {
20
20
  this.ctx = ctx;
21
21
  }
22
+ async renewProductSubscription(request) {
23
+ return personalPlanCheckoutResultSchema.parse(await this.ctx.request('POST', '/billing/product-subscriptions/renew', productSubscriptionRenewalRequestSchema.parse(request), { cache: false }));
24
+ }
25
+ async startProductPlanCheckout(request) {
26
+ return personalPlanCheckoutResultSchema.parse(await this.ctx.request('POST', '/billing/checkout/product-plan', productPlanCheckoutRequestSchema.parse(request), { cache: false }));
27
+ }
28
+ async productPlans(productId) {
29
+ return productPlanCatalogueSchema.parse(await this.ctx.request('GET', `/billing/product-plans/${encodeURIComponent(productId)}`, undefined, { cache: false }));
30
+ }
22
31
  async startPersonalPlanCheckout(request) {
23
32
  return personalPlanCheckoutResultSchema.parse(await this.ctx.request('POST', '/billing/checkout/personal-plan', personalPlanCheckoutRequestSchema.parse(request), { cache: false }));
24
33
  }
@@ -0,0 +1,90 @@
1
+ import { organizationSchema, organizationListSchema, organizationBatchSchema, organizationClaimSchema, organizationExternalSchema, organizationExternalResolutionSchema, organizationImportBatchResultSchema, organizationCurationResultSchema, organizationClaimListSchema, organizationClaimDetailSchema, } from '@oxy.so/contracts';
2
+ import { evictOxyIdentityCache } from '../utils/identityCacheSweep.js';
3
+ /** The public canonical organization directory. Claiming changes authority, never identity. */
4
+ export class OrganizationsApi {
5
+ constructor(ctx) {
6
+ this.ctx = ctx;
7
+ }
8
+ async search(input) {
9
+ return organizationListSchema.parse(await this.ctx.request('GET', '/organizations', input, { cache: false, skipAuth: true }));
10
+ }
11
+ async get(accountId) {
12
+ return organizationSchema.parse(await this.ctx.request('GET', `/organizations/${encodeURIComponent(accountId)}`, undefined, {
13
+ cache: false,
14
+ skipAuth: true,
15
+ }));
16
+ }
17
+ async batch(input) {
18
+ return organizationBatchSchema.parse(await this.ctx.request('POST', '/organizations/batch', input, {
19
+ cache: false,
20
+ skipAuth: true,
21
+ }));
22
+ }
23
+ async create(input) {
24
+ return organizationSchema.parse(await this.ctx.request('POST', '/organizations', input, { cache: false }));
25
+ }
26
+ async update(accountId, input) {
27
+ const result = organizationSchema.parse(await this.ctx.request('PATCH', `/organizations/${encodeURIComponent(accountId)}`, input, {
28
+ cache: false,
29
+ }));
30
+ evictOxyIdentityCache(this.ctx.http, accountId);
31
+ return result;
32
+ }
33
+ async claim(accountId, input) {
34
+ return organizationClaimSchema.parse(await this.ctx.request('POST', `/organizations/${encodeURIComponent(accountId)}/claims`, input, { cache: false }));
35
+ }
36
+ async myClaims(input = {}) {
37
+ return organizationClaimListSchema.parse(await this.ctx.request('GET', '/organizations/claims/mine', input, { cache: false }));
38
+ }
39
+ async myClaim(claimId) {
40
+ return organizationClaimDetailSchema.parse(await this.ctx.request('GET', `/organizations/claims/mine/${encodeURIComponent(claimId)}`, undefined, { cache: false }));
41
+ }
42
+ async reviewClaims(input = {}) {
43
+ return organizationClaimListSchema.parse(await this.ctx.request('GET', '/organizations/claims/review', input, { cache: false }));
44
+ }
45
+ async reviewClaimDetail(claimId) {
46
+ return organizationClaimDetailSchema.parse(await this.ctx.request('GET', `/organizations/claims/review/${encodeURIComponent(claimId)}`, undefined, { cache: false }));
47
+ }
48
+ async reviewClaim(claimId, input) {
49
+ const result = organizationClaimSchema.parse(await this.ctx.request('POST', `/organizations/claims/${encodeURIComponent(claimId)}/review`, input, { cache: false }));
50
+ evictOxyIdentityCache(this.ctx.http, result.accountId);
51
+ return result;
52
+ }
53
+ async importBatch(input) {
54
+ if (!this.ctx.service?.available)
55
+ throw new Error('Organization batch import requires an OxyServer service identity');
56
+ return organizationImportBatchResultSchema.parse(await this.ctx.service.request('POST', '/organizations/external/batch', input, {
57
+ cache: false,
58
+ }));
59
+ }
60
+ async importExternal(input) {
61
+ return organizationExternalSchema.parse(await this.ctx.request('POST', '/organizations/external', input, { cache: false }));
62
+ }
63
+ async resolveExternal(input) {
64
+ return organizationExternalResolutionSchema.parse(await this.ctx.request('GET', `/organizations/external/${encodeURIComponent(input.namespace)}/${encodeURIComponent(input.externalId)}`, undefined, { cache: false }));
65
+ }
66
+ async curate(accountId, input) {
67
+ const result = organizationCurationResultSchema.parse(await this.ctx.request('POST', `/organizations/${encodeURIComponent(accountId)}/curation`, input, { cache: false }));
68
+ evictOxyIdentityCache(this.ctx.http, accountId);
69
+ return result;
70
+ }
71
+ async merge(accountId, input) {
72
+ const result = organizationSchema.parse(await this.ctx.request('POST', `/organizations/${encodeURIComponent(accountId)}/merge`, input, { cache: false }));
73
+ evictOxyIdentityCache(this.ctx.http, accountId);
74
+ evictOxyIdentityCache(this.ctx.http, result.accountId);
75
+ return result;
76
+ }
77
+ async *iterate(input) {
78
+ let cursor = input.cursor;
79
+ const seen = new Set();
80
+ do {
81
+ const page = await this.search({ ...input, cursor });
82
+ yield* page.items;
83
+ cursor = page.nextCursor ?? undefined;
84
+ if (cursor && seen.has(cursor))
85
+ throw new Error('Organization directory repeated a cursor');
86
+ if (cursor)
87
+ seen.add(cursor);
88
+ } while (cursor);
89
+ }
90
+ }
@@ -0,0 +1,22 @@
1
+ import { profileAccessInputSchema, profileAccessDecisionSchema, } from '@oxy.so/contracts';
2
+ export class SafetyApi {
3
+ constructor(ctx) {
4
+ this.ctx = ctx;
5
+ }
6
+ /** Server callers must explicitly supply the incoming user bearer or null for anonymous access. */
7
+ async profileAccess(input, options) {
8
+ if (this.ctx.service && options === undefined)
9
+ throw new Error('Server profile access requires an explicit requester bearer or null');
10
+ return profileAccessDecisionSchema.parse(await this.ctx.request('GET', '/safety/profile-access', profileAccessInputSchema.parse(input), {
11
+ cache: false,
12
+ ...(options
13
+ ? {
14
+ skipAuth: true,
15
+ headers: options.accessToken
16
+ ? { Authorization: `Bearer ${options.accessToken}` }
17
+ : {},
18
+ }
19
+ : {}),
20
+ }));
21
+ }
22
+ }