@oxyhq/contracts 0.13.2 → 0.14.1

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.
@@ -69,3 +69,44 @@ export const deviceHubTicketRedeemResponseSchema = z.object({
69
69
  deviceId: z.string().min(1),
70
70
  deviceSecret: z.string().min(1),
71
71
  });
72
+ /* -------------------------------------------------------------------------- */
73
+ /* Instant cross-app session sync (token-free socket signal) */
74
+ /* -------------------------------------------------------------------------- */
75
+ /**
76
+ * Name of the token-free Socket.IO event emitted to room `user:<userId>` on
77
+ * every DeviceSession mutation that changes what is signed in for that user.
78
+ *
79
+ * This is a pure SIGNAL — it carries NO access token, NO deviceSecret and NO
80
+ * account bodies. A client that receives it re-fetches its authenticated
81
+ * session/account state (`GET /session/device/state`, `GET /accounts`). Unlike
82
+ * `session_state` (scoped to `device:<deviceId>`, i.e. a single origin), this
83
+ * reaches ALL of a user's connected sockets across their devices/origins so
84
+ * every Oxy app reflects an add / switch / signout instantly.
85
+ */
86
+ export const SESSION_ACCOUNTS_CHANGED_EVENT = 'session_accounts_changed';
87
+ /**
88
+ * Why the signed-in set changed:
89
+ * - `login` — a brand-new session was minted for the user (QR / cross-app authorize)
90
+ * - `add` — an account was registered onto a device set
91
+ * - `switch` — the active account on a device changed
92
+ * - `signout` — one or all accounts were signed out of a device
93
+ * - `revoke` — a dead/revoked account was healed out of a device set
94
+ */
95
+ export const sessionAccountsChangedReasonSchema = z.enum([
96
+ 'login',
97
+ 'add',
98
+ 'switch',
99
+ 'signout',
100
+ 'revoke',
101
+ ]);
102
+ /**
103
+ * Payload of {@link SESSION_ACCOUNTS_CHANGED_EVENT}. `revision` is the mutated
104
+ * DeviceSession revision for device-scoped reasons (`add`/`switch`/`signout`/
105
+ * `revoke`); for `login` (no device mutation at emit time) it is `0`. The
106
+ * payload is deliberately minimal and secret-free — the client refetches.
107
+ */
108
+ export const sessionAccountsChangedEventSchema = z.object({
109
+ userId: z.string(),
110
+ revision: z.number().int().nonnegative(),
111
+ reason: sessionAccountsChangedReasonSchema,
112
+ });
package/dist/esm/index.js CHANGED
@@ -38,7 +38,7 @@ credentialRecordSchema, verifiableCredentialResponseSchema, credentialIssueResul
38
38
  export {
39
39
  // Schemas
40
40
  linkPreviewSchema, linkPreviewBatchRequestSchema, linkPreviewBatchResponseSchema, linkPreviewResponseSchema, } from './links.js';
41
- export { sessionAccountSchema, deviceSessionStateSchema, activeTokenSchema, deviceSessionSyncSchema, deviceTokenMintRequestSchema, deviceTokenMintResponseSchema, deviceHubTicketIssueRequestSchema, deviceHubTicketIssueResponseSchema, deviceHubTicketRedeemRequestSchema, deviceHubTicketRedeemResponseSchema, } from './deviceSession.js';
41
+ export { sessionAccountSchema, deviceSessionStateSchema, activeTokenSchema, deviceSessionSyncSchema, deviceTokenMintRequestSchema, deviceTokenMintResponseSchema, deviceHubTicketIssueRequestSchema, deviceHubTicketIssueResponseSchema, deviceHubTicketRedeemRequestSchema, deviceHubTicketRedeemResponseSchema, SESSION_ACCOUNTS_CHANGED_EVENT, sessionAccountsChangedReasonSchema, sessionAccountsChangedEventSchema, } from './deviceSession.js';
42
42
  export {
43
43
  // Schemas
44
44
  loginResultSchema, } from './deviceBoot.js';
@@ -52,7 +52,7 @@ export const userNameSchema = z
52
52
  *
53
53
  * `.passthrough()` keeps the large tail of profile fields
54
54
  * (`privacySettings`, `locations`, `links`, `linksMetadata`, `bio`,
55
- * `description`, `language`, `verified`, timestamps, …) available to callers
55
+ * `description`, `languages`, `verified`, timestamps, …) available to callers
56
56
  * that need them without enumerating every nested shape here — the load-bearing
57
57
  * identity/display fields are the ones we pin precisely.
58
58
  */
@@ -74,7 +74,13 @@ export const userResponseSchema = z
74
74
  color: z.string().nullable().optional(),
75
75
  name: userNameSchema,
76
76
  verified: z.boolean().optional(),
77
- language: z.string().optional(),
77
+ /**
78
+ * The account's languages as full BCP-47 locales (`language-REGION`,
79
+ * e.g. `es-ES`, `en-US`, `pt-BR`), ordered with the PRIMARY (UI) locale
80
+ * first. `languages[0]` is the primary locale — there is no singular
81
+ * `language` field.
82
+ */
83
+ languages: z.array(z.string()).optional(),
78
84
  /**
79
85
  * The account's self-sovereign identifier
80
86
  * (`did:web:<FEDERATION_DOMAIN>:u:<userId>`). Surfaced as a `User`
@@ -121,7 +127,12 @@ export const userProfileUpdateSchema = z
121
127
  id: z.string().optional(),
122
128
  }))
123
129
  .optional(),
124
- language: z.string().optional(),
130
+ /**
131
+ * Ordered account locales (`language-REGION`), primary first. Replaces
132
+ * the eliminated singular `language`; `languages[0]` is the primary UI
133
+ * locale.
134
+ */
135
+ languages: z.array(z.string()).optional(),
125
136
  accountExpiresAfterInactivityDays: z.number().nullable().optional(),
126
137
  notificationPreferences: z.record(z.unknown()).optional(),
127
138
  userPreferences: z.record(z.unknown()).optional(),