@oxyhq/core 18.0.0 → 19.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/i18n/locales/en-US.json +49 -5
- package/dist/cjs/i18n/locales/es-ES.json +49 -5
- package/dist/cjs/i18n/locales/locales/en-US.json +49 -5
- package/dist/cjs/i18n/locales/locales/es-ES.json +49 -5
- package/dist/cjs/index.js +17 -6
- package/dist/cjs/mixins/OxyServices.accounts.js +69 -31
- package/dist/cjs/mixins/OxyServices.followGraph.js +204 -0
- package/dist/cjs/mixins/OxyServices.user.js +17 -20
- package/dist/cjs/mixins/index.js +4 -0
- package/dist/cjs/server/index.js +8 -2
- package/dist/cjs/server/userInvalidation.js +6 -28
- package/dist/cjs/session/accountProjection.js +45 -9
- package/dist/cjs/utils/accountCacheSweep.js +80 -0
- package/dist/cjs/utils/identityCacheSweep.js +97 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/i18n/locales/en-US.json +49 -5
- package/dist/esm/i18n/locales/es-ES.json +49 -5
- package/dist/esm/i18n/locales/locales/en-US.json +49 -5
- package/dist/esm/i18n/locales/locales/es-ES.json +49 -5
- package/dist/esm/index.js +8 -2
- package/dist/esm/mixins/OxyServices.accounts.js +64 -30
- package/dist/esm/mixins/OxyServices.followGraph.js +201 -0
- package/dist/esm/mixins/OxyServices.user.js +17 -20
- package/dist/esm/mixins/index.js +4 -0
- package/dist/esm/server/index.js +5 -1
- package/dist/esm/server/userInvalidation.js +5 -26
- package/dist/esm/session/accountProjection.js +44 -9
- package/dist/esm/utils/accountCacheSweep.js +75 -0
- package/dist/esm/utils/identityCacheSweep.js +92 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/mixins/OxyServices.accounts.d.ts +91 -34
- package/dist/types/mixins/OxyServices.followGraph.d.ts +211 -0
- package/dist/types/mixins/OxyServices.user.d.ts +10 -7
- package/dist/types/mixins/index.d.ts +2 -1
- package/dist/types/models/interfaces.d.ts +11 -3
- package/dist/types/server/index.d.ts +4 -2
- package/dist/types/server/userInvalidation.d.ts +5 -24
- package/dist/types/session/accountProjection.d.ts +38 -4
- package/dist/types/utils/accountCacheSweep.d.ts +75 -0
- package/dist/types/utils/identityCacheSweep.d.ts +80 -0
- package/package.json +2 -2
- package/src/i18n/locales/en-US.json +49 -5
- package/src/i18n/locales/es-ES.json +49 -5
- package/src/index.ts +15 -2
- package/src/mixins/OxyServices.accounts.ts +123 -45
- package/src/mixins/OxyServices.followGraph.ts +266 -0
- package/src/mixins/OxyServices.user.ts +17 -20
- package/src/mixins/__tests__/accounts.test.ts +5 -0
- package/src/mixins/__tests__/followGraph.test.ts +128 -0
- package/src/mixins/__tests__/identityWriteCacheInvalidation.test.ts +407 -0
- package/src/mixins/index.ts +5 -0
- package/src/models/interfaces.ts +11 -3
- package/src/server/__tests__/userInvalidation.test.ts +3 -20
- package/src/server/index.ts +5 -2
- package/src/server/userInvalidation.ts +8 -36
- package/src/session/__tests__/accountProjection.test.ts +109 -2
- package/src/session/accountProjection.ts +47 -9
- package/src/utils/__tests__/identityCacheSweep.test.ts +151 -0
- package/src/utils/accountCacheSweep.ts +93 -0
- package/src/utils/identityCacheSweep.ts +104 -0
|
@@ -91,6 +91,36 @@ export interface SwitchableAccount {
|
|
|
91
91
|
/** The underlying per-account user payload. */
|
|
92
92
|
user: SwitchableAccountUser;
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether the caller can BECOME this account — the one question every account
|
|
96
|
+
* switcher asks, answered here so no surface has to re-derive it.
|
|
97
|
+
*
|
|
98
|
+
* Two independent grounds, either of which suffices:
|
|
99
|
+
*
|
|
100
|
+
* - **It is already the caller's own identity** (`relationship: 'self'`).
|
|
101
|
+
* `GET /accounts` resolves its caller through `resolveOperatorId`, so `self`
|
|
102
|
+
* is the HUMAN operator's personal account even while they are operating an
|
|
103
|
+
* org — never the operated account. Kind is irrelevant on this ground: the
|
|
104
|
+
* caller IS that account, so returning to it asks the server for nothing.
|
|
105
|
+
* - **The server will mint a session for it** — `isActAsEligibleKind(kind)` is
|
|
106
|
+
* the exact predicate `POST /accounts/:id/switch` enforces, so a row offered
|
|
107
|
+
* on this ground is never a dead button.
|
|
108
|
+
*
|
|
109
|
+
* `isActAsEligibleKind` ALONE is not this question, and reaching for it
|
|
110
|
+
* directly is the mistake this function exists to prevent: it is false for
|
|
111
|
+
* `personal` as well as `channel`, so a switcher gated on it alone renders an
|
|
112
|
+
* empty list rather than a filtered one. Equally, `kind !== 'channel'` is not
|
|
113
|
+
* this question either — it silently admits every kind invented after it was
|
|
114
|
+
* written, which is the same trap `isActAsEligibleKind` was introduced to close
|
|
115
|
+
* on the server.
|
|
116
|
+
*
|
|
117
|
+
* Takes a structural subset rather than a whole {@link AccountNode} so a caller
|
|
118
|
+
* holding a projected {@link SwitchableAccount} can ask it too.
|
|
119
|
+
*/
|
|
120
|
+
export declare function isSwitchTargetAccount(node: {
|
|
121
|
+
kind?: AccountKind | null;
|
|
122
|
+
relationship?: AccountRelationship;
|
|
123
|
+
}): boolean;
|
|
94
124
|
/** Input to {@link projectSwitchableAccounts}. */
|
|
95
125
|
export interface ProjectSwitchableAccountsInput {
|
|
96
126
|
/**
|
|
@@ -131,8 +161,9 @@ export interface ProjectSwitchableAccountsInput {
|
|
|
131
161
|
* and a graph node is deduped into ONE device row enriched with the graph
|
|
132
162
|
* metadata (relationship / kind / parent / membership).
|
|
133
163
|
*
|
|
134
|
-
* Graph nodes
|
|
135
|
-
*
|
|
164
|
+
* Graph nodes that are not switch targets — a `channel`, which nobody may act
|
|
165
|
+
* as — are omitted. {@link isSwitchTargetAccount} is the rule; see the filter
|
|
166
|
+
* below.
|
|
136
167
|
*/
|
|
137
168
|
export declare function projectSwitchableAccounts(input: ProjectSwitchableAccountsInput): SwitchableAccount[];
|
|
138
169
|
/**
|
|
@@ -142,7 +173,10 @@ export declare function projectSwitchableAccounts(input: ProjectSwitchableAccoun
|
|
|
142
173
|
* document, but including their ids lets the caller pass one id set and lets the
|
|
143
174
|
* projection prefer freshly-fetched profiles uniformly.
|
|
144
175
|
*
|
|
145
|
-
* Applies the SAME
|
|
146
|
-
* nodes, so this never fetches a
|
|
176
|
+
* Applies the SAME {@link isSwitchTargetAccount} filter as
|
|
177
|
+
* {@link projectSwitchableAccounts} to graph nodes, so this never fetches a
|
|
178
|
+
* profile for a row the projection will drop — and, just as importantly, never
|
|
179
|
+
* SKIPS one the projection will keep, which would leave that row unrendered
|
|
180
|
+
* until some later fetch happened to resolve it.
|
|
147
181
|
*/
|
|
148
182
|
export declare function switchableAccountIds(state: DeviceSessionState | null, graph: AccountNode[]): string[];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE enumeration of `OxyServices` GET-cache keys that serve the ACCOUNT FOREST
|
|
3
|
+
* — the caller's accessible accounts, as lists and as individual detail rows —
|
|
4
|
+
* and the one sweep that clears them.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS IS NOT A METHOD ON THE ACCOUNTS MIXIN
|
|
7
|
+
* ---------------------------------------------
|
|
8
|
+
* `AccountNode.account` is a whole `User`, so a forest read embeds the very
|
|
9
|
+
* profile the identity reads serve. That makes an IDENTITY write a writer of
|
|
10
|
+
* these keys too: `updateProfile` edits the caller's own personal account,
|
|
11
|
+
* which is a row in `GET /accounts` and is its own `GET /accounts/<id>`. Leave
|
|
12
|
+
* those cached and the account switcher keeps drawing the pre-edit name and
|
|
13
|
+
* picture for the full TTL, against a perfectly healthy server.
|
|
14
|
+
*
|
|
15
|
+
* The mixins compose into one class at runtime but are typed one at a time, so
|
|
16
|
+
* the user mixin cannot call a method the accounts mixin owns. The key list
|
|
17
|
+
* therefore lives here, once, and every writer calls {@link
|
|
18
|
+
* evictOxyAccountForestCache} — exactly like the identity key list in
|
|
19
|
+
* `identityCacheSweep`, which the accounts mixin already calls for the
|
|
20
|
+
* mirror-image case (an account write staling the identity reads). The
|
|
21
|
+
* alternative — a second hand-written copy of these keys in the other mixin —
|
|
22
|
+
* is the drift that shipped the two stale-profile bugs `identityCacheSweep`
|
|
23
|
+
* documents.
|
|
24
|
+
*
|
|
25
|
+
* WHY THE LIST NEEDS A PREFIX AND THE DETAIL DOES NOT
|
|
26
|
+
* --------------------------------------------------
|
|
27
|
+
* `listAccounts({tree?})` keys the flat list as `GET:/accounts` and every
|
|
28
|
+
* option variant as `GET:/accounts?<query>` (the query string is part of the
|
|
29
|
+
* URL, hence of the key), and a writer cannot enumerate which variants a caller
|
|
30
|
+
* has read. The detail key, by contrast, is derivable from the account id the
|
|
31
|
+
* writer already holds.
|
|
32
|
+
*
|
|
33
|
+
* The `GET:/accounts?` prefix matches ONLY the query-string list variants —
|
|
34
|
+
* never `GET:/accounts/<id>` or its `…/members`, `…/credentials`, `…/children`
|
|
35
|
+
* sub-resources, which are the accounts mixin's own business and stay there.
|
|
36
|
+
*/
|
|
37
|
+
import type { OxyIdentityCacheEvictor } from './identityCacheSweep';
|
|
38
|
+
/**
|
|
39
|
+
* The cache-eviction surface of an `OxyServices` instance. Reused from
|
|
40
|
+
* `identityCacheSweep` rather than re-declared: it is the SDK's one published
|
|
41
|
+
* name for these two methods, and a second identical interface would be one
|
|
42
|
+
* more shape to keep in step.
|
|
43
|
+
*/
|
|
44
|
+
export type OxyAccountCacheEvictor = OxyIdentityCacheEvictor;
|
|
45
|
+
/** The cache key `listAccounts()` reads under with no options. */
|
|
46
|
+
export declare const OXY_ACCOUNT_LIST_CACHE_KEY = "GET:/accounts";
|
|
47
|
+
/**
|
|
48
|
+
* The prefix covering every option-carrying `listAccounts(opts)` variant
|
|
49
|
+
* (`?tree=true`, …), none of which a writer can enumerate.
|
|
50
|
+
*/
|
|
51
|
+
export declare const OXY_ACCOUNT_LIST_CACHE_QUERY_PREFIX = "GET:/accounts?";
|
|
52
|
+
/**
|
|
53
|
+
* Prefix covering every per-account sub-resource cache key
|
|
54
|
+
* (`GET:/accounts/<id>`, `…/members`, `…/credentials`, `…/children`). A
|
|
55
|
+
* membership mutation on an ancestor must sweep ALL of these, not only the
|
|
56
|
+
* account named in the path: descendant member rosters embed inherited rows
|
|
57
|
+
* resolved from that ancestor, and the writer cannot enumerate which descendant
|
|
58
|
+
* ids a caller has already read. The trailing slash deliberately excludes the
|
|
59
|
+
* forest list keys (`GET:/accounts`, `GET:/accounts?…`) documented above.
|
|
60
|
+
*/
|
|
61
|
+
export declare const OXY_ACCOUNT_PER_ACCOUNT_CACHE_PREFIX = "GET:/accounts/";
|
|
62
|
+
/**
|
|
63
|
+
* Build the exact cache key `getAccount(accountId)` reads under.
|
|
64
|
+
*/
|
|
65
|
+
export declare function oxyAccountDetailCacheKey(accountId: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Sweep an `OxyServices` GET response cache of the account forest.
|
|
68
|
+
*
|
|
69
|
+
* @param oxy - Anything exposing the SDK's two eviction methods.
|
|
70
|
+
* @param accountId - The account whose detail row to drop as well. Optional: a
|
|
71
|
+
* writer that changed the SHAPE of the forest rather than one
|
|
72
|
+
* account in it (create, archive, ownership transfer) has no
|
|
73
|
+
* detail row to name, and clears only the lists.
|
|
74
|
+
*/
|
|
75
|
+
export declare function evictOxyAccountForestCache(oxy: OxyAccountCacheEvictor, accountId?: string): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE enumeration of `OxyServices` GET-cache keys that can carry a single
|
|
3
|
+
* account's identity, and the one sweep that clears them.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS IS ONE LIST
|
|
6
|
+
* --------------------
|
|
7
|
+
* An Oxy account is readable under SEVERAL cache keys, and a write that only
|
|
8
|
+
* busts the key it happens to know about leaves every other one serving the
|
|
9
|
+
* pre-write snapshot for up to its TTL — from the caller's OWN in-memory cache,
|
|
10
|
+
* with a perfectly healthy server. That failure has already shipped twice with
|
|
11
|
+
* two different sets of keys:
|
|
12
|
+
*
|
|
13
|
+
* - `updateAccount` busted `GET:/accounts/<id>` and the account lists, but a
|
|
14
|
+
* profile screen reads `GET:/profiles/username/<name>` and
|
|
15
|
+
* `GET:/users/<id>`, so a channel's new picture stayed invisible for the
|
|
16
|
+
* full 5-minute profile TTL.
|
|
17
|
+
* - `updateProfile` busted four of the six keys below, missing
|
|
18
|
+
* `GET:/auth/lookup/` (the login-flow avatar/display-name lookup) and
|
|
19
|
+
* `GET:/profiles/resolve` (handle resolution) — two independently-drifted
|
|
20
|
+
* copies of a list that has to agree.
|
|
21
|
+
*
|
|
22
|
+
* So the list lives here, once, and every writer calls
|
|
23
|
+
* {@link evictOxyIdentityCache}. Adding a new identity read means adding its key
|
|
24
|
+
* HERE and every writer inherits it.
|
|
25
|
+
*
|
|
26
|
+
* WHERE THE LINE IS DRAWN
|
|
27
|
+
* -----------------------
|
|
28
|
+
* These are the SINGLE-PROFILE reads — the account is the subject of the
|
|
29
|
+
* response and is addressable by id, handle, or session. Reads that merely
|
|
30
|
+
* CONTAIN an account among many (`GET:/profiles/search`,
|
|
31
|
+
* `GET:/users/<other>/followers`, `GET:/profiles/<other>/similar`) are
|
|
32
|
+
* deliberately NOT swept: an account cannot be located in them without the very
|
|
33
|
+
* lookup being invalidated, so sweeping them means sweeping the whole namespace
|
|
34
|
+
* on every identity change — a real cost on a backend consuming the
|
|
35
|
+
* cross-service invalidation signal, for a surface where a stale thumbnail
|
|
36
|
+
* expires on its own in ~2 minutes.
|
|
37
|
+
*
|
|
38
|
+
* WHY PREFIXES RATHER THAN EXACT KEYS
|
|
39
|
+
* -----------------------------------
|
|
40
|
+
* Only the by-id key can be built from a user id. The handle-keyed and
|
|
41
|
+
* session-keyed entries cannot — deriving a handle from an id needs the lookup
|
|
42
|
+
* we are invalidating, and the SDK never tracks active session ids centrally.
|
|
43
|
+
* Prefix sweeping is also what makes a USERNAME CHANGE correct: the entry under
|
|
44
|
+
* the OLD handle is unreachable by construction (nothing in the write response
|
|
45
|
+
* carries it), and a sweep targeted at the new handle alone would leave the old
|
|
46
|
+
* one serving the pre-rename profile until its TTL. Over-eviction costs a
|
|
47
|
+
* refetch; under-eviction serves wrong data.
|
|
48
|
+
*
|
|
49
|
+
* Platform-neutral by construction (no imports, no `OxyServices` reference) so
|
|
50
|
+
* the client mixins and the Node-only `@oxyhq/core/server` invalidation
|
|
51
|
+
* subscriber can share it without either pulling in the other.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* The cache-eviction surface of an `OxyServices` instance. Declared
|
|
55
|
+
* structurally so this module stays free of any client import.
|
|
56
|
+
*/
|
|
57
|
+
export interface OxyIdentityCacheEvictor {
|
|
58
|
+
clearCacheEntry(key: string): void;
|
|
59
|
+
clearCacheByPrefix(prefix: string): number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Cache-key PREFIXES under which an account's identity can be served, for the
|
|
63
|
+
* reads whose key cannot be derived from a user id. Swept wholesale.
|
|
64
|
+
*/
|
|
65
|
+
export declare const OXY_IDENTITY_CACHE_PREFIXES: readonly string[];
|
|
66
|
+
/**
|
|
67
|
+
* Build the exact cache key `getUserById` reads under. The only identity key
|
|
68
|
+
* derivable from a user id, so the only one that does not need a prefix sweep.
|
|
69
|
+
*/
|
|
70
|
+
export declare function oxyUserByIdCacheKey(userId: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Sweep an `OxyServices` GET response cache of everything that could carry the
|
|
73
|
+
* given account's identity.
|
|
74
|
+
*
|
|
75
|
+
* @param oxy - Anything exposing the SDK's two eviction methods.
|
|
76
|
+
* @param userId - The account whose by-id entry to drop. Optional: a caller
|
|
77
|
+
* that does not know the id still clears every handle-, session-
|
|
78
|
+
* and self-keyed entry, which is the majority of the surface.
|
|
79
|
+
*/
|
|
80
|
+
export declare function evictOxyIdentityCache(oxy: OxyIdentityCacheEvictor, userId?: string): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "19.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.
|
|
118
|
+
"@oxyhq/contracts": "^0.24.0",
|
|
119
119
|
"@oxyhq/protocol": "^0.1.6",
|
|
120
120
|
"@scure/bip39": "^1.6.0",
|
|
121
121
|
"@types/elliptic": "^6.4.18",
|
|
@@ -1748,11 +1748,53 @@
|
|
|
1748
1748
|
"description": "A programmatic account with service credentials"
|
|
1749
1749
|
}
|
|
1750
1750
|
},
|
|
1751
|
-
"
|
|
1751
|
+
"accountCategory": {
|
|
1752
|
+
"news": "News",
|
|
1753
|
+
"politics": "Politics & policy",
|
|
1754
|
+
"business": "Business",
|
|
1755
|
+
"startup": "Startups & entrepreneurship",
|
|
1756
|
+
"finance": "Finance & investing",
|
|
1757
|
+
"crypto": "Crypto & web3",
|
|
1758
|
+
"marketplace": "Marketplace & classifieds",
|
|
1759
|
+
"retail": "Retail & ecommerce",
|
|
1760
|
+
"real_estate": "Real estate",
|
|
1752
1761
|
"agency": "Real estate agency",
|
|
1762
|
+
"landlord": "Landlord & property management",
|
|
1753
1763
|
"cooperative": "Housing cooperative",
|
|
1754
|
-
"
|
|
1755
|
-
"
|
|
1764
|
+
"architecture": "Architecture & construction",
|
|
1765
|
+
"technology": "Technology",
|
|
1766
|
+
"software": "Software & development",
|
|
1767
|
+
"ai": "AI & machine learning",
|
|
1768
|
+
"security": "Security & privacy",
|
|
1769
|
+
"automation": "Automation & tools",
|
|
1770
|
+
"science": "Science & research",
|
|
1771
|
+
"education": "Education & training",
|
|
1772
|
+
"books": "Books & writing",
|
|
1773
|
+
"health": "Health & medicine",
|
|
1774
|
+
"fitness": "Fitness & wellbeing",
|
|
1775
|
+
"sports": "Sports",
|
|
1776
|
+
"gaming": "Gaming & esports",
|
|
1777
|
+
"music": "Music",
|
|
1778
|
+
"film": "Film & TV",
|
|
1779
|
+
"podcast": "Podcasts & audio",
|
|
1780
|
+
"art": "Art & design",
|
|
1781
|
+
"photography": "Photography",
|
|
1782
|
+
"comedy": "Comedy",
|
|
1783
|
+
"food": "Food & drink",
|
|
1784
|
+
"travel": "Travel & places",
|
|
1785
|
+
"fashion": "Fashion & beauty",
|
|
1786
|
+
"home_garden": "Home & garden",
|
|
1787
|
+
"diy": "DIY & making",
|
|
1788
|
+
"automotive": "Cars & motoring",
|
|
1789
|
+
"animals": "Animals & pets",
|
|
1790
|
+
"family": "Family & parenting",
|
|
1791
|
+
"nonprofit": "Non-profit & charity",
|
|
1792
|
+
"government": "Government & public sector",
|
|
1793
|
+
"community": "Local community",
|
|
1794
|
+
"activism": "Activism & advocacy",
|
|
1795
|
+
"environment": "Environment & sustainability",
|
|
1796
|
+
"religion": "Religion & spirituality",
|
|
1797
|
+
"other": "Other"
|
|
1756
1798
|
},
|
|
1757
1799
|
"create": {
|
|
1758
1800
|
"title": "Create account",
|
|
@@ -1772,8 +1814,9 @@
|
|
|
1772
1814
|
"invalidChars": "Only letters, numbers, hyphens, and underscores",
|
|
1773
1815
|
"checkFailed": "Could not check availability"
|
|
1774
1816
|
},
|
|
1775
|
-
"
|
|
1776
|
-
"label": "
|
|
1817
|
+
"accountCategory": {
|
|
1818
|
+
"label": "Categories",
|
|
1819
|
+
"hint": "Pick up to {{max}}. The first one is your main category."
|
|
1777
1820
|
},
|
|
1778
1821
|
"toasts": {
|
|
1779
1822
|
"success": "Account created",
|
|
@@ -1796,6 +1839,7 @@
|
|
|
1796
1839
|
"title": "Members",
|
|
1797
1840
|
"subtitle": "People with access to this account.",
|
|
1798
1841
|
"empty": "No members yet.",
|
|
1842
|
+
"inherited": "Inherited",
|
|
1799
1843
|
"actions": {
|
|
1800
1844
|
"remove": "Remove member",
|
|
1801
1845
|
"transfer": "Transfer ownership"
|
|
@@ -1748,11 +1748,53 @@
|
|
|
1748
1748
|
"description": "Una cuenta programática con credenciales de servicio"
|
|
1749
1749
|
}
|
|
1750
1750
|
},
|
|
1751
|
-
"
|
|
1751
|
+
"accountCategory": {
|
|
1752
|
+
"news": "Noticias",
|
|
1753
|
+
"politics": "Política",
|
|
1754
|
+
"business": "Negocios",
|
|
1755
|
+
"startup": "Startups y emprendimiento",
|
|
1756
|
+
"finance": "Finanzas e inversión",
|
|
1757
|
+
"crypto": "Cripto y web3",
|
|
1758
|
+
"marketplace": "Marketplace y anuncios",
|
|
1759
|
+
"retail": "Comercio y ecommerce",
|
|
1760
|
+
"real_estate": "Inmobiliaria",
|
|
1752
1761
|
"agency": "Agencia inmobiliaria",
|
|
1762
|
+
"landlord": "Casero y gestión de fincas",
|
|
1753
1763
|
"cooperative": "Cooperativa de vivienda",
|
|
1754
|
-
"
|
|
1755
|
-
"
|
|
1764
|
+
"architecture": "Arquitectura y construcción",
|
|
1765
|
+
"technology": "Tecnología",
|
|
1766
|
+
"software": "Software y desarrollo",
|
|
1767
|
+
"ai": "IA y aprendizaje automático",
|
|
1768
|
+
"security": "Seguridad y privacidad",
|
|
1769
|
+
"automation": "Automatización y herramientas",
|
|
1770
|
+
"science": "Ciencia e investigación",
|
|
1771
|
+
"education": "Educación y formación",
|
|
1772
|
+
"books": "Libros y escritura",
|
|
1773
|
+
"health": "Salud y medicina",
|
|
1774
|
+
"fitness": "Deporte y bienestar",
|
|
1775
|
+
"sports": "Deportes",
|
|
1776
|
+
"gaming": "Videojuegos y esports",
|
|
1777
|
+
"music": "Música",
|
|
1778
|
+
"film": "Cine y TV",
|
|
1779
|
+
"podcast": "Pódcast y audio",
|
|
1780
|
+
"art": "Arte y diseño",
|
|
1781
|
+
"photography": "Fotografía",
|
|
1782
|
+
"comedy": "Comedia",
|
|
1783
|
+
"food": "Comida y bebida",
|
|
1784
|
+
"travel": "Viajes y lugares",
|
|
1785
|
+
"fashion": "Moda y belleza",
|
|
1786
|
+
"home_garden": "Hogar y jardín",
|
|
1787
|
+
"diy": "Bricolaje y manualidades",
|
|
1788
|
+
"automotive": "Coches y motor",
|
|
1789
|
+
"animals": "Animales y mascotas",
|
|
1790
|
+
"family": "Familia y crianza",
|
|
1791
|
+
"nonprofit": "ONG y benéficas",
|
|
1792
|
+
"government": "Gobierno y sector público",
|
|
1793
|
+
"community": "Comunidad local",
|
|
1794
|
+
"activism": "Activismo y defensa",
|
|
1795
|
+
"environment": "Medio ambiente y sostenibilidad",
|
|
1796
|
+
"religion": "Religión y espiritualidad",
|
|
1797
|
+
"other": "Otra"
|
|
1756
1798
|
},
|
|
1757
1799
|
"create": {
|
|
1758
1800
|
"title": "Crear cuenta",
|
|
@@ -1772,8 +1814,9 @@
|
|
|
1772
1814
|
"invalidChars": "Solo letras, números, guiones y guiones bajos",
|
|
1773
1815
|
"checkFailed": "No se pudo comprobar la disponibilidad"
|
|
1774
1816
|
},
|
|
1775
|
-
"
|
|
1776
|
-
"label": "
|
|
1817
|
+
"accountCategory": {
|
|
1818
|
+
"label": "Categorías",
|
|
1819
|
+
"hint": "Elige hasta {{max}}. La primera es tu categoría principal."
|
|
1777
1820
|
},
|
|
1778
1821
|
"toasts": {
|
|
1779
1822
|
"success": "Cuenta creada",
|
|
@@ -1796,6 +1839,7 @@
|
|
|
1796
1839
|
"title": "Miembros",
|
|
1797
1840
|
"subtitle": "Personas con acceso a esta cuenta.",
|
|
1798
1841
|
"empty": "Aún no hay miembros.",
|
|
1842
|
+
"inherited": "Heredado",
|
|
1799
1843
|
"actions": {
|
|
1800
1844
|
"remove": "Eliminar miembro",
|
|
1801
1845
|
"transfer": "Transferir propiedad"
|
package/src/index.ts
CHANGED
|
@@ -122,7 +122,7 @@ export type {
|
|
|
122
122
|
// ---------------------------------------------------------------------------
|
|
123
123
|
export type {
|
|
124
124
|
AccountKind,
|
|
125
|
-
|
|
125
|
+
AccountCategoryId,
|
|
126
126
|
AccountRelationship,
|
|
127
127
|
AccountRole,
|
|
128
128
|
AccountMemberStatus,
|
|
@@ -167,7 +167,13 @@ export type {
|
|
|
167
167
|
ApplicationUsageStats,
|
|
168
168
|
} from './mixins/OxyServices.accounts';
|
|
169
169
|
|
|
170
|
-
export {
|
|
170
|
+
export {
|
|
171
|
+
ACCOUNT_CATEGORY_IDS,
|
|
172
|
+
MAX_ACCOUNT_CATEGORIES,
|
|
173
|
+
SELECTABLE_ACCOUNT_CATEGORY_IDS,
|
|
174
|
+
isSelectableAccountCategoryId,
|
|
175
|
+
kindAcceptsAccountCategories,
|
|
176
|
+
} from './mixins/OxyServices.accounts';
|
|
171
177
|
|
|
172
178
|
// ---------------------------------------------------------------------------
|
|
173
179
|
// Reputation (Oxy Trust: ledger, balances, disputes, rules, influence).
|
|
@@ -641,7 +647,14 @@ export {
|
|
|
641
647
|
// chooser: device sign-ins ∪ account graph, deduped by accountId). Pure +
|
|
642
648
|
// I/O-free — the caller hydrates profiles via `getUsersByIds`. Shared by
|
|
643
649
|
// `@oxyhq/services` and auth.oxy.so so the list can't diverge.
|
|
650
|
+
// `isSwitchTargetAccount` is the switcher's own question ("can I become this
|
|
651
|
+
// account?"), exported so a surface that renders `AccountNode`s rather than the
|
|
652
|
+
// projection — the Console's workspace switcher, the accounts app's
|
|
653
|
+
// managed-accounts rows — asks the SAME question instead of testing a kind
|
|
654
|
+
// literal. It is NOT `isActAsEligibleKind`: that one is false for `personal`
|
|
655
|
+
// too, so gating a switcher on it alone empties the list.
|
|
644
656
|
export {
|
|
657
|
+
isSwitchTargetAccount,
|
|
645
658
|
projectSwitchableAccounts,
|
|
646
659
|
switchableAccountIds,
|
|
647
660
|
} from './session/accountProjection';
|