@oxyhq/contracts 0.20.0 → 0.22.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/accountGraph.js +80 -3
- package/dist/cjs/index.js +69 -4
- package/dist/cjs/moderationReputation.js +298 -0
- package/dist/cjs/reputation.js +15 -3
- package/dist/cjs/userInvalidation.js +89 -0
- package/dist/cjs/userResponse.js +21 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/accountGraph.js +77 -2
- package/dist/esm/index.js +13 -1
- package/dist/esm/moderationReputation.js +295 -0
- package/dist/esm/reputation.js +15 -3
- package/dist/esm/userInvalidation.js +85 -0
- package/dist/esm/userResponse.js +22 -2
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/accountGraph.d.ts +77 -6
- package/dist/types/index.d.ts +6 -2
- package/dist/types/moderationReputation.d.ts +487 -0
- package/dist/types/recommendations.d.ts +14 -14
- package/dist/types/reputation.d.ts +16 -0
- package/dist/types/userInvalidation.d.ts +94 -0
- package/dist/types/userResponse.d.ts +278 -28
- package/package.json +1 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Canonical contract for the Oxy user-invalidation broadcast.
|
|
4
|
+
*
|
|
5
|
+
* Oxy owns identity, but consumers cache it: Mention keeps a Redis summary per
|
|
6
|
+
* post author, and every backend using `@oxyhq/core` holds the SDK's own GET
|
|
7
|
+
* response cache. Both go stale the moment a profile is edited, and neither has
|
|
8
|
+
* any way to find out — the writer is a different process in a different repo.
|
|
9
|
+
* This is the signal that tells them.
|
|
10
|
+
*
|
|
11
|
+
* The channel name and the payload shape are wire contracts between oxy-api (the
|
|
12
|
+
* publisher) and every consuming backend (the subscribers), so they live here
|
|
13
|
+
* rather than in either side. A hand-typed copy of the channel name fails as
|
|
14
|
+
* "the invalidation never arrives" — silently, because pub/sub has no delivery
|
|
15
|
+
* receipt and a message nobody is listening for is indistinguishable from a
|
|
16
|
+
* message nobody sent.
|
|
17
|
+
*
|
|
18
|
+
* DELIVERY IS AT-MOST-ONCE, AND THAT IS THE DESIGN. Every consumer's cache still
|
|
19
|
+
* carries its own TTL, so a dropped message degrades to exactly the behaviour
|
|
20
|
+
* before this signal existed and never to something worse. That property is what
|
|
21
|
+
* makes a bare Redis PUBLISH sufficient here and an outbox, retries, delivery
|
|
22
|
+
* receipts and payload signatures unnecessary. Do not treat a received event as
|
|
23
|
+
* authoritative for anything except "re-read this user from Oxy".
|
|
24
|
+
*
|
|
25
|
+
* PRIVACY — the payload carries NO user data, only an id, a reason and a
|
|
26
|
+
* timestamp. The channel rides the shared Valkey that every Oxy backend can
|
|
27
|
+
* subscribe to, so anything placed on it is readable by every service in the
|
|
28
|
+
* ecosystem. Never add a name, handle, email, avatar or any profile field: a
|
|
29
|
+
* subscriber that wants the new values re-reads them from Oxy through its normal
|
|
30
|
+
* authenticated path, where the usual authorization applies.
|
|
31
|
+
*
|
|
32
|
+
* Platform-agnostic — zod only, no react/react-native/expo.
|
|
33
|
+
*/
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.oxyUserInvalidationEventSchema = exports.OXY_PUBLISHED_USER_CHANGE_REASONS = exports.OXY_USER_CHANGE_REASONS = exports.OXY_USER_INVALIDATION_CHANNEL = void 0;
|
|
36
|
+
exports.isPublishedOxyUserChangeReason = isPublishedOxyUserChangeReason;
|
|
37
|
+
const zod_1 = require("zod");
|
|
38
|
+
/** Redis pub/sub channel carrying user-invalidation events. */
|
|
39
|
+
exports.OXY_USER_INVALIDATION_CHANNEL = 'oxy:user:invalidate';
|
|
40
|
+
/**
|
|
41
|
+
* Why a user record changed, as classified by the writer in oxy-api.
|
|
42
|
+
*
|
|
43
|
+
* - `profile` — anything a consumer renders or caches as IDENTITY: display name,
|
|
44
|
+
* username, avatar, bio, verification, federation fields, account status. This
|
|
45
|
+
* is the DEFAULT for every writer, so a site that forgets to classify itself
|
|
46
|
+
* over-invalidates (correct, marginally slower) rather than under-invalidates
|
|
47
|
+
* (silently wrong). Keep that asymmetry if you add a reason.
|
|
48
|
+
* - `graph` — follow-edge churn only (follower/following counts). High frequency,
|
|
49
|
+
* and bulk follow/unfollow moves up to 200 edges in one call. Nothing renders
|
|
50
|
+
* identity from it and a stale count is harmless to ranking, so it is NOT
|
|
51
|
+
* broadcast — see {@link OXY_PUBLISHED_USER_CHANGE_REASONS}.
|
|
52
|
+
*/
|
|
53
|
+
exports.OXY_USER_CHANGE_REASONS = ['profile', 'graph'];
|
|
54
|
+
/**
|
|
55
|
+
* The reasons that are actually put on the wire.
|
|
56
|
+
*
|
|
57
|
+
* A reason absent from this list is a local cache eviction in oxy-api and
|
|
58
|
+
* nothing more: no message is published at all, rather than a message every
|
|
59
|
+
* subscriber receives and discards. The distinction matters at bulk-follow
|
|
60
|
+
* scale, where the discarded variant is a 200-message burst on a channel every
|
|
61
|
+
* Oxy backend is subscribed to.
|
|
62
|
+
*
|
|
63
|
+
* This is deliberately a shared list rather than a check inside the publisher:
|
|
64
|
+
* a subscriber needs to know what it can receive, and the schema below rejects
|
|
65
|
+
* anything else, so publisher and subscriber cannot drift into disagreeing about
|
|
66
|
+
* which events exist. Adding a reason therefore forces an explicit decision about
|
|
67
|
+
* whether it broadcasts.
|
|
68
|
+
*/
|
|
69
|
+
exports.OXY_PUBLISHED_USER_CHANGE_REASONS = ['profile'];
|
|
70
|
+
/** Whether a change of this kind is broadcast to consumers at all. */
|
|
71
|
+
function isPublishedOxyUserChangeReason(reason) {
|
|
72
|
+
return exports.OXY_PUBLISHED_USER_CHANGE_REASONS.includes(reason);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A single user-invalidation event.
|
|
76
|
+
*
|
|
77
|
+
* `at` is the publisher's epoch-ms clock, carried for diagnosis (measuring
|
|
78
|
+
* end-to-end propagation, spotting a wedged subscriber) — never for ordering or
|
|
79
|
+
* conflict resolution. Two Oxy tasks publish from unsynchronised clocks, and the
|
|
80
|
+
* event says only "re-read this user", which is idempotent and order-independent.
|
|
81
|
+
*/
|
|
82
|
+
exports.oxyUserInvalidationEventSchema = zod_1.z.object({
|
|
83
|
+
/** The Oxy user whose record changed. */
|
|
84
|
+
userId: zod_1.z.string().min(1),
|
|
85
|
+
/** Why it changed. Only broadcast reasons appear on the wire. */
|
|
86
|
+
reason: zod_1.z.enum(exports.OXY_PUBLISHED_USER_CHANGE_REASONS),
|
|
87
|
+
/** Publisher's epoch-ms timestamp. Diagnostic only. */
|
|
88
|
+
at: zod_1.z.number().int().nonnegative(),
|
|
89
|
+
});
|
package/dist/cjs/userResponse.js
CHANGED
|
@@ -105,9 +105,23 @@ exports.userResponseSchema = zod_1.z
|
|
|
105
105
|
* entry; present only when the account has verified at least one domain.
|
|
106
106
|
*/
|
|
107
107
|
verifiedDomains: zod_1.z.array(identity_1.verifiedDomainSchema).optional(),
|
|
108
|
+
/**
|
|
109
|
+
* Account-graph classification — what KIND of account this is.
|
|
110
|
+
*
|
|
111
|
+
* ORTHOGONAL to `type` (`local` / `federated` / `agent` / `automated`),
|
|
112
|
+
* which says where the account lives and how it is driven; the two
|
|
113
|
+
* coexist and neither substitutes for the other. A `channel` is a
|
|
114
|
+
* publishing identity nobody can act as, so a consumer that renders
|
|
115
|
+
* authored content reads THIS to tell a channel's post from a person's.
|
|
116
|
+
*
|
|
117
|
+
* Optional because a DTO produced from a source that never carried the
|
|
118
|
+
* column omits it; absent should be read as `personal`, the column's
|
|
119
|
+
* default, not as unknown.
|
|
120
|
+
*/
|
|
121
|
+
kind: accountGraph_1.accountKindSchema.optional(),
|
|
108
122
|
/**
|
|
109
123
|
* Real-estate / team taxonomy for `kind: 'organization'` accounts.
|
|
110
|
-
* Absent on personal, project, and
|
|
124
|
+
* Absent on personal, project, bot, and channel accounts.
|
|
111
125
|
*/
|
|
112
126
|
organizationCategory: accountGraph_1.organizationCategorySchema.optional(),
|
|
113
127
|
/**
|
|
@@ -131,6 +145,12 @@ exports.userProfileUpdateSchema = zod_1.z
|
|
|
131
145
|
.object({
|
|
132
146
|
first: zod_1.z.string().optional(),
|
|
133
147
|
last: zod_1.z.string().optional(),
|
|
148
|
+
/**
|
|
149
|
+
* Explicit display name, stored rather than composed. Wins over
|
|
150
|
+
* `first`/`last` when set; send `''` to clear it and fall back
|
|
151
|
+
* to the composed pair.
|
|
152
|
+
*/
|
|
153
|
+
displayName: zod_1.z.string().optional(),
|
|
134
154
|
})
|
|
135
155
|
.optional(),
|
|
136
156
|
username: zod_1.z.string().optional(),
|