@oxyhq/core 17.0.3 → 17.1.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.
@@ -39,7 +39,7 @@ export type { CanonicalUserHandleInput, UserHandleInput } from './utils/userHand
39
39
  export { normalizeProfileLinks } from './utils/profileLinks';
40
40
  export type { ProfileLink, ProfileLinkMetadata } from './utils/profileLinks';
41
41
  export type { PublicApplication, ConnectedApp, } from './mixins/OxyServices.connectedApps';
42
- export type { AccountKind, OrganizationCategory, AccountRelationship, AccountRole, AccountMemberStatus, AccountMemberSource, AccountMember, AccountNode, AccountCredentialType, AccountCredentialEnvironment, AccountCredentialStatus, AccountCredential, AccountCredentialWithSecret, RotateAccountCredentialResult, ListAccountsOptions, CreateAccountInput, UpdateAccountInput, InviteAccountMemberInput, UpdateAccountMemberInput, TransferAccountOwnershipInput, CreateAccountCredentialInput, AccountSuccessResult, SwitchAccountResult, Application, ApplicationType, ApplicationStatus, ApplicationCredential, ApplicationCredentialType, ApplicationCredentialStatus, ApplicationEnvironment, CreateApplicationInput, UpdateApplicationInput, CreateApplicationCredentialInput, ApplicationCredentialWithSecret, RotateApplicationCredentialResult, ApplicationUsagePeriod, ApplicationUsageSummary, ApplicationUsageByDay, ApplicationUsageByEndpoint, ApplicationUsageStats, } from './mixins/OxyServices.accounts';
42
+ export type { AccountKind, OrganizationCategory, AccountRelationship, AccountRole, AccountMemberStatus, AccountMemberSource, AccountMember, AccountNode, AccountCredentialType, AccountCredentialEnvironment, AccountCredentialStatus, AccountCredential, AccountCredentialWithSecret, RotateAccountCredentialResult, ListAccountsOptions, UserCreatableAccountKind, CreateAccountInput, UpdateAccountInput, ProvisionChannelInput, ProvisionChannelMemberInput, ProvisionChannelResult, InviteAccountMemberInput, UpdateAccountMemberInput, TransferAccountOwnershipInput, CreateAccountCredentialInput, AccountSuccessResult, SwitchAccountResult, Application, ApplicationType, ApplicationStatus, ApplicationCredential, ApplicationCredentialType, ApplicationCredentialStatus, ApplicationEnvironment, CreateApplicationInput, UpdateApplicationInput, CreateApplicationCredentialInput, ApplicationCredentialWithSecret, RotateApplicationCredentialResult, ApplicationUsagePeriod, ApplicationUsageSummary, ApplicationUsageByDay, ApplicationUsageByEndpoint, ApplicationUsageStats, } from './mixins/OxyServices.accounts';
43
43
  export { ORGANIZATION_CATEGORIES } from './mixins/OxyServices.accounts';
44
44
  export { buildUserDid } from './mixins/OxyServices.identity';
45
45
  export type { IdentityRecordType, UnlinkableAuthMethodType, LinkAuthMethodResult, PublishRecordResult, VerifyRecordResult, VerifyDomainResult, RemoveDomainResult, RotateKeyProof, RotateKeyOptions, RotateKeyResult, } from './mixins/OxyServices.identity';
@@ -131,10 +131,12 @@ export interface ListAccountsOptions {
131
131
  */
132
132
  tree?: boolean;
133
133
  }
134
+ /** Kinds a signed-in user may create via `POST /accounts`. Channels are service-provisioned only. */
135
+ export type UserCreatableAccountKind = 'organization' | 'project' | 'bot';
134
136
  /** Input accepted by `createAccount`. */
135
137
  export interface CreateAccountInput {
136
- /** Classification of the new account. `personal` accounts are not created here. */
137
- kind: AccountKind;
138
+ /** Classification of the new account. `personal` and `channel` are not creatable here. */
139
+ kind: UserCreatableAccountKind;
138
140
  /**
139
141
  * Parent account `_id` to nest the new account under. Omitted → the API roots
140
142
  * it under the caller's personal account.
@@ -142,9 +144,16 @@ export interface CreateAccountInput {
142
144
  parentAccountId?: string;
143
145
  /** Unique handle for the account (shares the `User.username` unique index). */
144
146
  username: string;
147
+ /**
148
+ * A managed account (organization / project / bot / channel) has a TITLE, not
149
+ * a given-and-family name, so it sets `displayName` — the explicit
150
+ * `name_display` column — and leaves `first`/`last` unset. Splitting a title
151
+ * on whitespace into `first`/`last` is what this replaced.
152
+ */
145
153
  name?: {
146
154
  first?: string;
147
155
  last?: string;
156
+ displayName?: string;
148
157
  };
149
158
  bio?: string;
150
159
  avatar?: string;
@@ -154,15 +163,47 @@ export interface CreateAccountInput {
154
163
  /** Input accepted by `updateAccount`. Tree placement changes go through `/move`. */
155
164
  export interface UpdateAccountInput {
156
165
  username?: string;
166
+ /**
167
+ * Same shape as `CreateAccountInput['name']`. On update, an EMPTY STRING in
168
+ * `displayName` clears the explicit name and falls back to the composed
169
+ * `first`/`last`; omitting the key leaves the stored value untouched. The two
170
+ * are not interchangeable.
171
+ */
157
172
  name?: {
158
173
  first?: string;
159
174
  last?: string;
175
+ displayName?: string;
160
176
  };
161
177
  bio?: string | null;
162
178
  avatar?: string | null;
163
179
  /** Clears the category when `null`; only valid on `kind: 'organization'`. */
164
180
  organizationCategory?: OrganizationCategory | null;
165
181
  }
182
+ /** Input accepted by `provisionChannelAccount` (service token + `accounts:provision`). */
183
+ export interface ProvisionChannelInput {
184
+ /** Personal account `_id` whose tree owns the new channel. */
185
+ ownerUserId: string;
186
+ username: string;
187
+ name?: {
188
+ first?: string;
189
+ last?: string;
190
+ displayName?: string;
191
+ };
192
+ bio?: string;
193
+ description?: string;
194
+ avatar?: string;
195
+ }
196
+ /** Input accepted by `provisionChannelMember` (service token + `accounts:provision`). */
197
+ export interface ProvisionChannelMemberInput {
198
+ memberUserId: string;
199
+ role: Exclude<AccountRole, 'owner'>;
200
+ inherit?: boolean;
201
+ }
202
+ /** Result of `provisionChannelAccount`. */
203
+ export interface ProvisionChannelResult {
204
+ account: User;
205
+ membership: AccountMember;
206
+ }
166
207
  /** Input accepted by `inviteAccountMember`. The owner role cannot be invited. */
167
208
  export interface InviteAccountMemberInput {
168
209
  /**
@@ -430,6 +471,11 @@ export interface SwitchAccountResult extends SessionLoginResponse {
430
471
  }
431
472
  export declare function OxyServicesAccountsMixin<T extends typeof OxyServicesBase>(Base: T): {
432
473
  new (...args: any[]): {
474
+ /**
475
+ * Inherited from the auth mixin at runtime. Declared here so service-scoped
476
+ * account provisioning methods can call it with correct typing.
477
+ */
478
+ makeServiceRequest: <R = unknown>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: unknown, userId?: string) => Promise<R>;
433
479
  /**
434
480
  * List the accounts the caller can access: their own personal (root)
435
481
  * account, accounts they own, and accounts shared with them (including
@@ -483,6 +529,23 @@ export declare function OxyServicesAccountsMixin<T extends typeof OxyServicesBas
483
529
  * @param data - Account configuration: kind, optional parent, and profile.
484
530
  */
485
531
  createAccount(data: CreateAccountInput): Promise<AccountNode>;
532
+ /**
533
+ * Mint a `channel` account under `ownerUserId` via service auth
534
+ * (`accounts:provision` scope). Requires `configureServiceAuth` first.
535
+ */
536
+ provisionChannelAccount(data: ProvisionChannelInput): Promise<ProvisionChannelResult>;
537
+ /**
538
+ * Grant membership on a channel account via service auth
539
+ * (`accounts:provision` scope).
540
+ */
541
+ provisionChannelMember(channelAccountId: string, data: ProvisionChannelMemberInput): Promise<{
542
+ member: AccountMember;
543
+ }>;
544
+ /**
545
+ * Revoke membership on a channel account via service auth
546
+ * (`accounts:provision` scope).
547
+ */
548
+ revokeChannelMember(channelAccountId: string, memberUserId: string): Promise<AccountSuccessResult>;
486
549
  /**
487
550
  * Update an account's mutable profile fields. Tree placement changes
488
551
  * (reparenting) go through the dedicated move endpoint, not here.
@@ -1,4 +1,4 @@
1
- import type { OrganizationCategory, UserNameResponse, UserRelationship, ThemePreference } from '@oxyhq/contracts';
1
+ import type { AccountKind, OrganizationCategory, UserNameResponse, UserRelationship, ThemePreference } from '@oxyhq/contracts';
2
2
  export interface OxyConfig {
3
3
  baseURL: string;
4
4
  cloudURL?: string;
@@ -118,6 +118,22 @@ export interface User {
118
118
  following?: number;
119
119
  };
120
120
  accountExpiresAfterInactivityDays?: number | null;
121
+ /**
122
+ * Account-graph classification — WHAT this account is.
123
+ *
124
+ * ORTHOGONAL to `type` below, and easy to confuse with it: `type` says where
125
+ * the account lives and how it is driven (`local` / `federated` / `agent` /
126
+ * `automated`), `kind` says what it IS (`personal` / `organization` /
127
+ * `project` / `bot` / `channel`). A federated channel is `type: 'federated'`
128
+ * AND `kind: 'channel'`; neither value substitutes for the other.
129
+ *
130
+ * This is what a consumer rendering authored content reads to tell a
131
+ * channel's post from a person's — a channel is the author, so there is no
132
+ * second identity to carry alongside the user.
133
+ *
134
+ * Absent should be read as `personal` (the column's default), not as unknown.
135
+ */
136
+ kind?: AccountKind;
121
137
  type?: 'local' | 'federated' | 'agent' | 'automated';
122
138
  isFederated?: boolean;
123
139
  /** Allow sharing this user's content on the fediverse. Defaults to true. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "17.0.3",
3
+ "version": "17.1.0",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -115,7 +115,7 @@
115
115
  "dependencies": {
116
116
  "@noble/ciphers": "^1.3.0",
117
117
  "@noble/hashes": "^1.8.0",
118
- "@oxyhq/contracts": "^0.21.0",
118
+ "@oxyhq/contracts": "^0.22.0",
119
119
  "@oxyhq/protocol": "^0.1.6",
120
120
  "@scure/bip39": "^1.6.0",
121
121
  "@types/elliptic": "^6.4.18",
package/src/index.ts CHANGED
@@ -136,8 +136,12 @@ export type {
136
136
  AccountCredentialWithSecret,
137
137
  RotateAccountCredentialResult,
138
138
  ListAccountsOptions,
139
+ UserCreatableAccountKind,
139
140
  CreateAccountInput,
140
141
  UpdateAccountInput,
142
+ ProvisionChannelInput,
143
+ ProvisionChannelMemberInput,
144
+ ProvisionChannelResult,
141
145
  InviteAccountMemberInput,
142
146
  UpdateAccountMemberInput,
143
147
  TransferAccountOwnershipInput,
@@ -146,10 +146,13 @@ export interface ListAccountsOptions {
146
146
  tree?: boolean;
147
147
  }
148
148
 
149
+ /** Kinds a signed-in user may create via `POST /accounts`. Channels are service-provisioned only. */
150
+ export type UserCreatableAccountKind = 'organization' | 'project' | 'bot';
151
+
149
152
  /** Input accepted by `createAccount`. */
150
153
  export interface CreateAccountInput {
151
- /** Classification of the new account. `personal` accounts are not created here. */
152
- kind: AccountKind;
154
+ /** Classification of the new account. `personal` and `channel` are not creatable here. */
155
+ kind: UserCreatableAccountKind;
153
156
  /**
154
157
  * Parent account `_id` to nest the new account under. Omitted → the API roots
155
158
  * it under the caller's personal account.
@@ -157,7 +160,13 @@ export interface CreateAccountInput {
157
160
  parentAccountId?: string;
158
161
  /** Unique handle for the account (shares the `User.username` unique index). */
159
162
  username: string;
160
- name?: { first?: string; last?: string };
163
+ /**
164
+ * A managed account (organization / project / bot / channel) has a TITLE, not
165
+ * a given-and-family name, so it sets `displayName` — the explicit
166
+ * `name_display` column — and leaves `first`/`last` unset. Splitting a title
167
+ * on whitespace into `first`/`last` is what this replaced.
168
+ */
169
+ name?: { first?: string; last?: string; displayName?: string };
161
170
  bio?: string;
162
171
  avatar?: string;
163
172
  /** Meaningful only when `kind` is `organization`. */
@@ -167,13 +176,43 @@ export interface CreateAccountInput {
167
176
  /** Input accepted by `updateAccount`. Tree placement changes go through `/move`. */
168
177
  export interface UpdateAccountInput {
169
178
  username?: string;
170
- name?: { first?: string; last?: string };
179
+ /**
180
+ * Same shape as `CreateAccountInput['name']`. On update, an EMPTY STRING in
181
+ * `displayName` clears the explicit name and falls back to the composed
182
+ * `first`/`last`; omitting the key leaves the stored value untouched. The two
183
+ * are not interchangeable.
184
+ */
185
+ name?: { first?: string; last?: string; displayName?: string };
171
186
  bio?: string | null;
172
187
  avatar?: string | null;
173
188
  /** Clears the category when `null`; only valid on `kind: 'organization'`. */
174
189
  organizationCategory?: OrganizationCategory | null;
175
190
  }
176
191
 
192
+ /** Input accepted by `provisionChannelAccount` (service token + `accounts:provision`). */
193
+ export interface ProvisionChannelInput {
194
+ /** Personal account `_id` whose tree owns the new channel. */
195
+ ownerUserId: string;
196
+ username: string;
197
+ name?: { first?: string; last?: string; displayName?: string };
198
+ bio?: string;
199
+ description?: string;
200
+ avatar?: string;
201
+ }
202
+
203
+ /** Input accepted by `provisionChannelMember` (service token + `accounts:provision`). */
204
+ export interface ProvisionChannelMemberInput {
205
+ memberUserId: string;
206
+ role: Exclude<AccountRole, 'owner'>;
207
+ inherit?: boolean;
208
+ }
209
+
210
+ /** Result of `provisionChannelAccount`. */
211
+ export interface ProvisionChannelResult {
212
+ account: User;
213
+ membership: AccountMember;
214
+ }
215
+
177
216
  /** Input accepted by `inviteAccountMember`. The owner role cannot be invited. */
178
217
  export interface InviteAccountMemberInput {
179
218
  /**
@@ -482,6 +521,17 @@ export function OxyServicesAccountsMixin<T extends typeof OxyServicesBase>(Base:
482
521
  super(...(args as [any]));
483
522
  }
484
523
 
524
+ /**
525
+ * Inherited from the auth mixin at runtime. Declared here so service-scoped
526
+ * account provisioning methods can call it with correct typing.
527
+ */
528
+ declare makeServiceRequest: <R = unknown>(
529
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
530
+ url: string,
531
+ data?: unknown,
532
+ userId?: string,
533
+ ) => Promise<R>;
534
+
485
535
  // =========================================================================
486
536
  // Accounts
487
537
  // =========================================================================
@@ -620,6 +670,59 @@ export function OxyServicesAccountsMixin<T extends typeof OxyServicesBase>(Base:
620
670
  }
621
671
  }
622
672
 
673
+ /**
674
+ * Mint a `channel` account under `ownerUserId` via service auth
675
+ * (`accounts:provision` scope). Requires `configureServiceAuth` first.
676
+ */
677
+ async provisionChannelAccount(data: ProvisionChannelInput): Promise<ProvisionChannelResult> {
678
+ try {
679
+ return await this.makeServiceRequest<ProvisionChannelResult>(
680
+ 'POST',
681
+ '/accounts/service/channels',
682
+ data,
683
+ );
684
+ } catch (error) {
685
+ throw this.handleError(error);
686
+ }
687
+ }
688
+
689
+ /**
690
+ * Grant membership on a channel account via service auth
691
+ * (`accounts:provision` scope).
692
+ */
693
+ async provisionChannelMember(
694
+ channelAccountId: string,
695
+ data: ProvisionChannelMemberInput,
696
+ ): Promise<{ member: AccountMember }> {
697
+ try {
698
+ return await this.makeServiceRequest<{ member: AccountMember }>(
699
+ 'POST',
700
+ `/accounts/service/channels/${encodeURIComponent(channelAccountId)}/members`,
701
+ data,
702
+ );
703
+ } catch (error) {
704
+ throw this.handleError(error);
705
+ }
706
+ }
707
+
708
+ /**
709
+ * Revoke membership on a channel account via service auth
710
+ * (`accounts:provision` scope).
711
+ */
712
+ async revokeChannelMember(
713
+ channelAccountId: string,
714
+ memberUserId: string,
715
+ ): Promise<AccountSuccessResult> {
716
+ try {
717
+ return await this.makeServiceRequest<AccountSuccessResult>(
718
+ 'DELETE',
719
+ `/accounts/service/channels/${encodeURIComponent(channelAccountId)}/members/${encodeURIComponent(memberUserId)}`,
720
+ );
721
+ } catch (error) {
722
+ throw this.handleError(error);
723
+ }
724
+ }
725
+
623
726
  /**
624
727
  * Update an account's mutable profile fields. Tree placement changes
625
728
  * (reparenting) go through the dedicated move endpoint, not here.
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ AccountKind,
2
3
  OrganizationCategory,
3
4
  UserNameResponse,
4
5
  UserRelationship,
@@ -134,6 +135,22 @@ export interface User {
134
135
  following?: number;
135
136
  };
136
137
  accountExpiresAfterInactivityDays?: number | null; // Days of inactivity before account expires (null = never expire)
138
+ /**
139
+ * Account-graph classification — WHAT this account is.
140
+ *
141
+ * ORTHOGONAL to `type` below, and easy to confuse with it: `type` says where
142
+ * the account lives and how it is driven (`local` / `federated` / `agent` /
143
+ * `automated`), `kind` says what it IS (`personal` / `organization` /
144
+ * `project` / `bot` / `channel`). A federated channel is `type: 'federated'`
145
+ * AND `kind: 'channel'`; neither value substitutes for the other.
146
+ *
147
+ * This is what a consumer rendering authored content reads to tell a
148
+ * channel's post from a person's — a channel is the author, so there is no
149
+ * second identity to carry alongside the user.
150
+ *
151
+ * Absent should be read as `personal` (the column's default), not as unknown.
152
+ */
153
+ kind?: AccountKind;
137
154
  // User type and external account support
138
155
  type?: 'local' | 'federated' | 'agent' | 'automated';
139
156
  isFederated?: boolean;