@voltro/plugin-social 0.0.0-stage → 0.88.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/CHANGELOG.md +24492 -0
- package/LICENSE +61 -0
- package/README.md +151 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +8333 -0
- package/dist/components.js +201 -0
- package/dist/contract.js +39 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +15 -0
- package/dist/eventProducer.d.ts +8 -0
- package/dist/eventProducer.js +19 -0
- package/dist/events.d.ts +22 -0
- package/dist/events.js +15 -0
- package/dist/index.d.ts +235 -0
- package/dist/index.js +134 -0
- package/dist/inspect.js +27 -0
- package/dist/port.d.ts +35 -0
- package/dist/port.js +4 -0
- package/dist/recent.entity.js +17 -0
- package/dist/relations.entity.js +29 -0
- package/dist/rpc.d.ts +150 -0
- package/dist/rpc.js +154 -0
- package/dist/rpcRoutes.js +69 -0
- package/dist/service.js +353 -0
- package/dist/settings.entity.js +19 -0
- package/dist/social-app/events/social.accepted.event.ts +1 -0
- package/dist/social-app/events/social.requested.event.ts +1 -0
- package/dist/social-app/subscribers/social.relations.subscribe.ts +4 -0
- package/dist/strategies.d.ts +85 -0
- package/dist/strategies.js +39 -0
- package/dist/testing.d.ts +122 -0
- package/dist/testing.js +85 -0
- package/dist/web.d.ts +162 -0
- package/dist/web.js +7 -0
- package/package.json +90 -4
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { useEffect as e, useId as t, useRef as n } from "react";
|
|
2
|
+
import { Button as r, Dialog as i, DialogDescription as a, DialogFooter as o, DialogHeader as s, DialogTitle as c, Input as l } from "@voltro/ui-shadcn";
|
|
3
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
4
|
+
// src/components.tsx
|
|
5
|
+
var f = {
|
|
6
|
+
en: {
|
|
7
|
+
title: "Friends",
|
|
8
|
+
empty: "Your friends will appear here after a request is accepted.",
|
|
9
|
+
loading: "Loading friends…",
|
|
10
|
+
error: "Friends could not be loaded. Try again.",
|
|
11
|
+
online: "Online",
|
|
12
|
+
offline: "Offline",
|
|
13
|
+
unknown: "Status unavailable",
|
|
14
|
+
invite: "Invite to party",
|
|
15
|
+
message: "Message",
|
|
16
|
+
profile: "Profile"
|
|
17
|
+
},
|
|
18
|
+
de: {
|
|
19
|
+
title: "Freunde",
|
|
20
|
+
empty: "Deine Freunde erscheinen hier, sobald eine Anfrage angenommen wurde.",
|
|
21
|
+
loading: "Freunde werden geladen…",
|
|
22
|
+
error: "Freunde konnten nicht geladen werden. Versuche es erneut.",
|
|
23
|
+
online: "Online",
|
|
24
|
+
offline: "Offline",
|
|
25
|
+
unknown: "Status nicht verfügbar",
|
|
26
|
+
invite: "In Party einladen",
|
|
27
|
+
message: "Nachricht",
|
|
28
|
+
profile: "Profil"
|
|
29
|
+
}
|
|
30
|
+
}, p = ({ friend: e, labels: t = f.en, pending: n = !1, onInvite: i, onMessage: a, onProfile: o }) => /* @__PURE__ */ d("li", {
|
|
31
|
+
className: "flex min-w-0 flex-wrap items-center justify-between gap-4 py-4",
|
|
32
|
+
children: [/* @__PURE__ */ d("div", {
|
|
33
|
+
className: "min-w-0 flex-1 space-y-1",
|
|
34
|
+
children: [/* @__PURE__ */ d("p", {
|
|
35
|
+
className: "break-words font-medium",
|
|
36
|
+
children: [e.displayName, /* @__PURE__ */ d("span", {
|
|
37
|
+
className: "text-muted-foreground",
|
|
38
|
+
children: ["#", e.tag]
|
|
39
|
+
})]
|
|
40
|
+
}), /* @__PURE__ */ d("p", {
|
|
41
|
+
className: "break-words text-sm text-muted-foreground",
|
|
42
|
+
children: [e.online === null ? t.unknown : e.online ? t.online : t.offline, e.activity ? ` · ${e.activity}` : ""]
|
|
43
|
+
})]
|
|
44
|
+
}), /* @__PURE__ */ u("div", {
|
|
45
|
+
className: "flex flex-wrap gap-2",
|
|
46
|
+
children: [
|
|
47
|
+
[i, t.invite],
|
|
48
|
+
[a, t.message],
|
|
49
|
+
[o, t.profile]
|
|
50
|
+
].map(([t, i]) => t ? /* @__PURE__ */ u(r, {
|
|
51
|
+
type: "button",
|
|
52
|
+
variant: "outline",
|
|
53
|
+
size: "sm",
|
|
54
|
+
"aria-disabled": n,
|
|
55
|
+
onClick: () => {
|
|
56
|
+
n || t(e.subjectId);
|
|
57
|
+
},
|
|
58
|
+
children: i
|
|
59
|
+
}, i) : null)
|
|
60
|
+
})]
|
|
61
|
+
}), m = ({ friends: e, loading: n = !1, error: r, pending: i = !1, labels: a = f.en, ...o }) => {
|
|
62
|
+
let s = t();
|
|
63
|
+
return /* @__PURE__ */ d("section", {
|
|
64
|
+
"aria-labelledby": s,
|
|
65
|
+
"aria-busy": n,
|
|
66
|
+
className: "min-w-0 space-y-3",
|
|
67
|
+
children: [/* @__PURE__ */ u("h2", {
|
|
68
|
+
id: s,
|
|
69
|
+
className: "text-lg font-semibold",
|
|
70
|
+
children: a.title
|
|
71
|
+
}), r === void 0 ? n && e.length === 0 ? /* @__PURE__ */ u("p", {
|
|
72
|
+
role: "status",
|
|
73
|
+
className: "text-sm text-muted-foreground",
|
|
74
|
+
children: a.loading
|
|
75
|
+
}) : e.length === 0 ? /* @__PURE__ */ u("p", {
|
|
76
|
+
className: "max-w-prose text-sm text-muted-foreground",
|
|
77
|
+
children: a.empty
|
|
78
|
+
}) : /* @__PURE__ */ u("ul", {
|
|
79
|
+
className: "divide-y divide-border",
|
|
80
|
+
children: e.map((e) => /* @__PURE__ */ u(p, {
|
|
81
|
+
friend: e,
|
|
82
|
+
labels: a,
|
|
83
|
+
pending: i || n,
|
|
84
|
+
...o
|
|
85
|
+
}, e.subjectId))
|
|
86
|
+
}) : /* @__PURE__ */ u("p", {
|
|
87
|
+
role: "alert",
|
|
88
|
+
className: "text-sm text-destructive",
|
|
89
|
+
children: a.error
|
|
90
|
+
})]
|
|
91
|
+
});
|
|
92
|
+
}, h = {
|
|
93
|
+
en: {
|
|
94
|
+
title: "Add a friend",
|
|
95
|
+
description: "Find a player by their exact name and four-digit tag.",
|
|
96
|
+
name: "Name#Tag",
|
|
97
|
+
find: "Find player",
|
|
98
|
+
request: "Send friend request",
|
|
99
|
+
close: "Close",
|
|
100
|
+
notFound: "No player matches this name and tag.",
|
|
101
|
+
failed: "The operation failed. Check the result before retrying."
|
|
102
|
+
},
|
|
103
|
+
de: {
|
|
104
|
+
title: "Freund hinzufügen",
|
|
105
|
+
description: "Finde einen Spieler über seinen genauen Namen und vierstelligen Tag.",
|
|
106
|
+
name: "Name#Tag",
|
|
107
|
+
find: "Spieler finden",
|
|
108
|
+
request: "Freundesanfrage senden",
|
|
109
|
+
close: "Schließen",
|
|
110
|
+
notFound: "Kein Spieler mit diesem Namen und Tag gefunden.",
|
|
111
|
+
failed: "Der Vorgang ist fehlgeschlagen. Prüfe das Ergebnis vor einem erneuten Versuch."
|
|
112
|
+
}
|
|
113
|
+
}, g = ({ open: f, onOpenChange: p, query: m, onQueryChange: g, found: _, scopeKey: v, pending: y = !1, error: b, labels: x = h.en, onFind: S, onRequest: C }) => {
|
|
114
|
+
let w = n(null), T = t(), E = t(), D = t();
|
|
115
|
+
e(() => {
|
|
116
|
+
let e = w.current;
|
|
117
|
+
e && (f && !e.open ? e.showModal() : !f && e.open && e.close());
|
|
118
|
+
}, [f]);
|
|
119
|
+
let O = _ && _.query === m.trim() && _.scopeKey === v ? _ : void 0;
|
|
120
|
+
return /* @__PURE__ */ d(i, {
|
|
121
|
+
ref: w,
|
|
122
|
+
"aria-labelledby": T,
|
|
123
|
+
"aria-describedby": E,
|
|
124
|
+
onClose: () => p(!1),
|
|
125
|
+
className: "max-h-[90dvh] max-w-[min(32rem,calc(100vw-2rem))] overflow-y-auto",
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ d(s, { children: [/* @__PURE__ */ u(c, {
|
|
128
|
+
id: T,
|
|
129
|
+
children: x.title
|
|
130
|
+
}), /* @__PURE__ */ u(a, {
|
|
131
|
+
id: E,
|
|
132
|
+
children: x.description
|
|
133
|
+
})] }),
|
|
134
|
+
/* @__PURE__ */ d("form", {
|
|
135
|
+
className: "space-y-4",
|
|
136
|
+
"aria-busy": y,
|
|
137
|
+
onSubmit: (e) => {
|
|
138
|
+
e.preventDefault(), !y && m.trim() && S(m.trim());
|
|
139
|
+
},
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ d("div", {
|
|
142
|
+
className: "space-y-2",
|
|
143
|
+
children: [/* @__PURE__ */ u("label", {
|
|
144
|
+
htmlFor: D,
|
|
145
|
+
className: "text-sm font-medium",
|
|
146
|
+
children: x.name
|
|
147
|
+
}), /* @__PURE__ */ u(l, {
|
|
148
|
+
id: D,
|
|
149
|
+
value: m,
|
|
150
|
+
maxLength: 261,
|
|
151
|
+
autoComplete: "off",
|
|
152
|
+
readOnly: y,
|
|
153
|
+
onChange: (e) => {
|
|
154
|
+
y || g(e.target.value);
|
|
155
|
+
}
|
|
156
|
+
})]
|
|
157
|
+
}),
|
|
158
|
+
/* @__PURE__ */ u(r, {
|
|
159
|
+
type: "submit",
|
|
160
|
+
variant: "outline",
|
|
161
|
+
"aria-disabled": y || !m.trim(),
|
|
162
|
+
children: x.find
|
|
163
|
+
}),
|
|
164
|
+
b === void 0 ? _ === null ? /* @__PURE__ */ u("p", {
|
|
165
|
+
role: "status",
|
|
166
|
+
className: "text-sm text-muted-foreground",
|
|
167
|
+
children: x.notFound
|
|
168
|
+
}) : O ? /* @__PURE__ */ d("div", {
|
|
169
|
+
className: "space-y-3",
|
|
170
|
+
children: [/* @__PURE__ */ d("p", {
|
|
171
|
+
className: "break-words font-medium",
|
|
172
|
+
children: [
|
|
173
|
+
O.displayName,
|
|
174
|
+
"#",
|
|
175
|
+
O.tag
|
|
176
|
+
]
|
|
177
|
+
}), /* @__PURE__ */ u(r, {
|
|
178
|
+
type: "button",
|
|
179
|
+
"aria-disabled": y,
|
|
180
|
+
onClick: () => {
|
|
181
|
+
y || C(O.subjectId);
|
|
182
|
+
},
|
|
183
|
+
children: x.request
|
|
184
|
+
})]
|
|
185
|
+
}) : null : /* @__PURE__ */ u("p", {
|
|
186
|
+
role: "alert",
|
|
187
|
+
className: "text-sm text-destructive",
|
|
188
|
+
children: x.failed
|
|
189
|
+
})
|
|
190
|
+
]
|
|
191
|
+
}),
|
|
192
|
+
/* @__PURE__ */ u(o, { children: /* @__PURE__ */ u(r, {
|
|
193
|
+
type: "button",
|
|
194
|
+
variant: "ghost",
|
|
195
|
+
onClick: () => p(!1),
|
|
196
|
+
children: x.close
|
|
197
|
+
}) })
|
|
198
|
+
]
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
export { g as AddFriendDialog, p as FriendRow, m as FriendsPanel, h as addFriendLabels, f as friendsLabels };
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
// src/contract.ts
|
|
3
|
+
var t = "_voltro_social_relations", n = "_voltro_social_recent", r = "_voltro_social_settings", i = e.Number.pipe(e.check(e.isInt()), e.check(e.isGreaterThanOrEqualTo(0))), a = e.Struct({
|
|
4
|
+
ownerId: e.String,
|
|
5
|
+
requestsFrom: e.Literals([
|
|
6
|
+
"anyone",
|
|
7
|
+
"friends-of-friends",
|
|
8
|
+
"nobody"
|
|
9
|
+
]),
|
|
10
|
+
showOnline: e.Boolean,
|
|
11
|
+
showActivity: e.Boolean,
|
|
12
|
+
allowPartyInvites: e.Literals(["friends", "anyone"]),
|
|
13
|
+
metadata: e.Record(e.String, e.Unknown)
|
|
14
|
+
}), o = e.Struct({
|
|
15
|
+
id: e.String,
|
|
16
|
+
fromId: e.String,
|
|
17
|
+
toId: e.String,
|
|
18
|
+
kind: e.Literals([
|
|
19
|
+
"request",
|
|
20
|
+
"friend",
|
|
21
|
+
"block",
|
|
22
|
+
"favorite"
|
|
23
|
+
]),
|
|
24
|
+
createdAt: i,
|
|
25
|
+
respondedAt: e.NullOr(i),
|
|
26
|
+
note: e.NullOr(e.String),
|
|
27
|
+
metadata: e.Record(e.String, e.Unknown)
|
|
28
|
+
}), s = e.Struct({
|
|
29
|
+
id: e.String,
|
|
30
|
+
otherId: e.String,
|
|
31
|
+
context: e.String,
|
|
32
|
+
at: i,
|
|
33
|
+
metadata: e.Record(e.String, e.Unknown)
|
|
34
|
+
}), c = e.Struct({
|
|
35
|
+
subjectId: e.String,
|
|
36
|
+
mutualFriends: e.Number.pipe(e.check(e.isInt()), e.check(e.isGreaterThan(0))),
|
|
37
|
+
recentMatches: i
|
|
38
|
+
});
|
|
39
|
+
export { n as RECENT_TABLE, t as RELATIONS_TABLE, s as RecentPlayerSchema, r as SETTINGS_TABLE, o as SocialRelationSchema, a as SocialSettingsSchema, c as SuggestionSchema };
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { YieldableError } from 'effect/Cause';
|
|
3
|
+
|
|
4
|
+
export declare class SocialError extends SocialError_base {
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export declare const socialError: (code: SocialError["code"]) => SocialError;
|
|
8
|
+
|
|
9
|
+
declare const SocialError_base: Schema.Class<SocialError, Schema.TaggedStruct<"SocialError", {
|
|
10
|
+
readonly code: Schema.Literals<readonly ["identity_required", "invalid_input", "blocked", "request_refused", "not_found", "friend_limit", "pending_limit", "rate_limited", "unavailable", "export_limit"]>;
|
|
11
|
+
}>, YieldableError>;
|
|
12
|
+
|
|
13
|
+
export { }
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
// src/errors.ts
|
|
3
|
+
var t = class extends e.TaggedError()("SocialError", { code: e.Literals([
|
|
4
|
+
"identity_required",
|
|
5
|
+
"invalid_input",
|
|
6
|
+
"blocked",
|
|
7
|
+
"request_refused",
|
|
8
|
+
"not_found",
|
|
9
|
+
"friend_limit",
|
|
10
|
+
"pending_limit",
|
|
11
|
+
"rate_limited",
|
|
12
|
+
"unavailable",
|
|
13
|
+
"export_limit"
|
|
14
|
+
]) }) {}, n = (e) => new t({ code: e });
|
|
15
|
+
export { t as SocialError, n as socialError };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ChangeEvent } from '@voltro/database';
|
|
2
|
+
import { SubscribeContext } from '@voltro/runtime';
|
|
3
|
+
|
|
4
|
+
export declare const publishSocialChange: (event: ChangeEvent, ctx: SubscribeContext) => Promise<void>;
|
|
5
|
+
|
|
6
|
+
export declare const socialEventTenant: (event: ChangeEvent) => string;
|
|
7
|
+
|
|
8
|
+
export { }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "./contract.js";
|
|
2
|
+
import { accepted as e, requested as t } from "./events.js";
|
|
3
|
+
// src/eventProducer.ts
|
|
4
|
+
var n = (e) => {
|
|
5
|
+
if (e.table !== "_voltro_social_relations") throw Error("Social events require the owned relation table");
|
|
6
|
+
let t = (e.op === "delete" ? e.old : e.new)?.tenantId;
|
|
7
|
+
if (typeof t != "string" || !t.trim() || t.length > 256 || t.includes("\0") || t === "~" || e.op === "update" && e.old?.tenantId !== t) throw Error("Social events require stable committed tenant evidence");
|
|
8
|
+
return t;
|
|
9
|
+
}, r = async (r, i) => {
|
|
10
|
+
if (n(r), r.op !== "insert") return;
|
|
11
|
+
let a = r.new;
|
|
12
|
+
if (!(!a || a.kind !== "request" && a.kind !== "friend")) {
|
|
13
|
+
if (!i.publish) throw Error("Social event publisher is unavailable");
|
|
14
|
+
if (typeof a.id != "string" || !a.id || a.id.length > 64 || typeof a.fromId != "string" || !a.fromId.trim() || typeof a.toId != "string" || !a.toId.trim() || a.fromId === a.toId) throw Error("Social events require committed relation evidence");
|
|
15
|
+
if (a.kind === "request") await i.publish(t, { recipientId: a.toId }, { relationId: a.id });
|
|
16
|
+
else for (let t of [a.fromId, a.toId]) await i.publish(e, { recipientId: t }, { relationId: a.id });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export { r as publishSocialChange, n as socialEventTenant };
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DeclaredAccess } from '@voltro/protocol';
|
|
2
|
+
import { EventDescriptor } from '@voltro/protocol';
|
|
3
|
+
import { Schema } from 'effect';
|
|
4
|
+
|
|
5
|
+
export declare const accepted: EventDescriptor<"social.accepted", Schema.Struct<{
|
|
6
|
+
readonly recipientId: Schema.String;
|
|
7
|
+
}>, Schema.Struct<{
|
|
8
|
+
readonly relationId: Schema.String;
|
|
9
|
+
}>, DeclaredAccess<{
|
|
10
|
+
readonly recipientId: string;
|
|
11
|
+
}>>;
|
|
12
|
+
|
|
13
|
+
/** Operator-scoped CDC notifications; not a public player event stream. */
|
|
14
|
+
export declare const requested: EventDescriptor<"social.requested", Schema.Struct<{
|
|
15
|
+
readonly recipientId: Schema.String;
|
|
16
|
+
}>, Schema.Struct<{
|
|
17
|
+
readonly relationId: Schema.String;
|
|
18
|
+
}>, DeclaredAccess<{
|
|
19
|
+
readonly recipientId: string;
|
|
20
|
+
}>>;
|
|
21
|
+
|
|
22
|
+
export { }
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
import { defineEvent as t } from "@voltro/protocol";
|
|
3
|
+
// src/events.ts
|
|
4
|
+
var n = t({
|
|
5
|
+
name: "social.requested",
|
|
6
|
+
key: e.Struct({ recipientId: e.String }),
|
|
7
|
+
payload: e.Struct({ relationId: e.String }),
|
|
8
|
+
guards: [{ scope: "social:events:read" }]
|
|
9
|
+
}), r = t({
|
|
10
|
+
name: "social.accepted",
|
|
11
|
+
key: e.Struct({ recipientId: e.String }),
|
|
12
|
+
payload: e.Struct({ relationId: e.String }),
|
|
13
|
+
guards: [{ scope: "social:events:read" }]
|
|
14
|
+
});
|
|
15
|
+
export { r as accepted, n as requested };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { ColumnBuilder } from '@voltro/database';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
import { DataStore } from '@voltro/database';
|
|
4
|
+
import { Effect } from 'effect';
|
|
5
|
+
import { FieldDefinitions } from '@voltro/database';
|
|
6
|
+
import { PluginPrivacyTarget } from '@voltro/protocol';
|
|
7
|
+
import { RateLimitStore } from '@voltro/plugin-ratelimit';
|
|
8
|
+
import { RedisStoreOptions } from '@voltro/plugin-ratelimit';
|
|
9
|
+
import { Schema } from 'effect';
|
|
10
|
+
import { Subject } from '@voltro/protocol';
|
|
11
|
+
import { Table } from '@voltro/database';
|
|
12
|
+
import { VoltroPlugin } from '@voltro/protocol';
|
|
13
|
+
import { YieldableError } from 'effect/Cause';
|
|
14
|
+
|
|
15
|
+
/** All graph decisions read SQL. The HotStore only owns ephemeral request-rate counters. */
|
|
16
|
+
export declare const dataStoreSocial: (store: DataStore, options: ResolvedSocialOptions, limiter: RateLimitStore) => {
|
|
17
|
+
service: SocialService;
|
|
18
|
+
blockPort: SocialBlockService;
|
|
19
|
+
exportSubject: (target: PluginPrivacyTarget) => Effect.Effect<unknown, SocialError, never>;
|
|
20
|
+
eraseSubject: (target: PluginPrivacyTarget) => Effect.Effect<void, SocialError, never>;
|
|
21
|
+
pruneRecent: (subject: Subject) => Effect.Effect<void, SocialError, never>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export declare const defaultSocialStrategies: SocialStrategies;
|
|
25
|
+
|
|
26
|
+
declare const mutualMatchSuggestions: (candidates: ReadonlyArray<SocialSuggestion>, limit: number) => ReadonlyArray<SocialSuggestion>;
|
|
27
|
+
|
|
28
|
+
export declare type RecentPlayer = typeof RecentPlayerSchema.Type;
|
|
29
|
+
|
|
30
|
+
declare const RecentPlayerSchema: Schema.Struct<{
|
|
31
|
+
readonly id: Schema.String;
|
|
32
|
+
readonly otherId: Schema.String;
|
|
33
|
+
readonly context: Schema.String;
|
|
34
|
+
readonly at: Schema.Number;
|
|
35
|
+
readonly metadata: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
36
|
+
}>;
|
|
37
|
+
|
|
38
|
+
export declare interface RecentPolicyInput {
|
|
39
|
+
readonly ownerId: string;
|
|
40
|
+
readonly otherId: string;
|
|
41
|
+
readonly context: string;
|
|
42
|
+
readonly at: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export declare interface RecordRecentInput {
|
|
46
|
+
readonly tenantId: string;
|
|
47
|
+
readonly participants: ReadonlyArray<string>;
|
|
48
|
+
readonly context: string;
|
|
49
|
+
readonly at?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export declare interface RequestPolicyInput {
|
|
53
|
+
readonly settings: SocialSettings;
|
|
54
|
+
readonly mutualFriends: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare type ResolvedSocialOptions = ReturnType<typeof resolveSocialOptions>;
|
|
58
|
+
|
|
59
|
+
export declare const resolveSocialOptions: (options?: SocialOptions) => Readonly<{
|
|
60
|
+
maxFriends: number;
|
|
61
|
+
maxPendingRequests: number;
|
|
62
|
+
recentLimit: number;
|
|
63
|
+
recentRetentionMs: number;
|
|
64
|
+
maxMatchParticipants: number;
|
|
65
|
+
maxExportRows: number;
|
|
66
|
+
requestLimit: number;
|
|
67
|
+
requestWindowMs: number;
|
|
68
|
+
suggestionLimit: number;
|
|
69
|
+
strategies: Readonly<{
|
|
70
|
+
suggestions: typeof mutualMatchSuggestions;
|
|
71
|
+
requestPolicy: (input: RequestPolicyInput) => boolean;
|
|
72
|
+
recentPolicy: (input: RecentPolicyInput) => boolean;
|
|
73
|
+
}>;
|
|
74
|
+
rateLimit: RedisStoreOptions | undefined;
|
|
75
|
+
now: () => number;
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
export declare type SettingsPatch = Partial<Omit<SocialSettings, 'ownerId' | 'metadata'>>;
|
|
79
|
+
|
|
80
|
+
export declare class SocialBlockPort extends SocialBlockPort_base {
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
declare const SocialBlockPort_base: Context.ServiceClass<SocialBlockPort, "@voltro/SocialBlockPort", SocialBlockService>;
|
|
84
|
+
|
|
85
|
+
/** Chat, matchmaking and party admission depend on this port, never Social SQL. */
|
|
86
|
+
export declare interface SocialBlockService {
|
|
87
|
+
readonly isBlocked: (subject: Subject, otherId: string) => Effect.Effect<boolean, SocialError>;
|
|
88
|
+
readonly blockPairs: (subject: Subject, subjectIds: ReadonlyArray<string>) => Effect.Effect<ReadonlyArray<readonly [string, string]>, SocialError>;
|
|
89
|
+
readonly canMessage: (subject: Subject, otherId: string) => Effect.Effect<boolean, SocialError>;
|
|
90
|
+
readonly canInvite: (subject: Subject, otherId: string) => Effect.Effect<boolean, SocialError>;
|
|
91
|
+
readonly recordRecent: (subject: Subject, input: RecordRecentInput) => Effect.Effect<void, SocialError>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export declare class SocialError extends SocialError_base {
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
declare const SocialError_base: Schema.Class<SocialError, Schema.TaggedStruct<"SocialError", {
|
|
98
|
+
readonly code: Schema.Literals<readonly ["identity_required", "invalid_input", "blocked", "request_refused", "not_found", "friend_limit", "pending_limit", "rate_limited", "unavailable", "export_limit"]>;
|
|
99
|
+
}>, YieldableError>;
|
|
100
|
+
|
|
101
|
+
export declare interface SocialOptions {
|
|
102
|
+
readonly maxFriends?: number;
|
|
103
|
+
readonly maxPendingRequests?: number;
|
|
104
|
+
readonly recentLimit?: number;
|
|
105
|
+
readonly recentRetentionMs?: number;
|
|
106
|
+
readonly maxMatchParticipants?: number;
|
|
107
|
+
readonly maxExportRows?: number;
|
|
108
|
+
readonly requestLimit?: number;
|
|
109
|
+
readonly requestWindowMs?: number;
|
|
110
|
+
readonly suggestionLimit?: number;
|
|
111
|
+
readonly strategies?: Partial<SocialStrategies>;
|
|
112
|
+
readonly rateLimit?: RedisStoreOptions;
|
|
113
|
+
readonly now?: () => number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export declare const socialPlugin: (input?: SocialOptions) => VoltroPlugin;
|
|
117
|
+
|
|
118
|
+
export declare const socialRecentTable: Table<"_voltro_social_recent", FieldDefinitions<{
|
|
119
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
120
|
+
readonly tenantId: ColumnBuilder<string, "text", boolean>;
|
|
121
|
+
readonly ownerId: ColumnBuilder<string, "text", boolean>;
|
|
122
|
+
readonly otherId: ColumnBuilder<string, "text", boolean>;
|
|
123
|
+
readonly context: ColumnBuilder<string, "text", boolean>;
|
|
124
|
+
readonly at: ColumnBuilder<Date, "timestamp", boolean>;
|
|
125
|
+
readonly metadata: ColumnBuilder<unknown, "json", boolean>;
|
|
126
|
+
}>, true, never>;
|
|
127
|
+
|
|
128
|
+
export declare type SocialRelation = typeof SocialRelationSchema.Type;
|
|
129
|
+
|
|
130
|
+
declare const SocialRelationSchema: Schema.Struct<{
|
|
131
|
+
readonly id: Schema.String;
|
|
132
|
+
readonly fromId: Schema.String;
|
|
133
|
+
readonly toId: Schema.String;
|
|
134
|
+
readonly kind: Schema.Literals<readonly ["request", "friend", "block", "favorite"]>;
|
|
135
|
+
readonly createdAt: Schema.Number;
|
|
136
|
+
readonly respondedAt: Schema.NullOr<Schema.Number>;
|
|
137
|
+
readonly note: Schema.NullOr<Schema.String>;
|
|
138
|
+
readonly metadata: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
139
|
+
}>;
|
|
140
|
+
|
|
141
|
+
export declare const socialRelationsTable: Table<"_voltro_social_relations", FieldDefinitions<{
|
|
142
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
143
|
+
readonly tenantId: ColumnBuilder<string, "text", boolean>;
|
|
144
|
+
readonly fromId: ColumnBuilder<string, "text", boolean>;
|
|
145
|
+
readonly toId: ColumnBuilder<string, "text", boolean>;
|
|
146
|
+
readonly pairKey: ColumnBuilder<string, "text", boolean>;
|
|
147
|
+
readonly kind: ColumnBuilder<"request" | "friend" | "block" | "favorite", "text", boolean>;
|
|
148
|
+
readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
|
|
149
|
+
readonly respondedAt: ColumnBuilder<Date | null, "timestamp", boolean>;
|
|
150
|
+
readonly note: ColumnBuilder<string | null, "text", boolean>;
|
|
151
|
+
readonly metadata: ColumnBuilder<unknown, "json", boolean>;
|
|
152
|
+
}>, true, never>;
|
|
153
|
+
|
|
154
|
+
export declare interface SocialService {
|
|
155
|
+
readonly request: (subject: Subject, otherId: string) => Effect.Effect<SocialRelation, SocialError>;
|
|
156
|
+
readonly accept: (subject: Subject, otherId: string) => Effect.Effect<SocialRelation, SocialError>;
|
|
157
|
+
readonly decline: (subject: Subject, otherId: string) => Effect.Effect<void, SocialError>;
|
|
158
|
+
readonly cancel: (subject: Subject, otherId: string) => Effect.Effect<void, SocialError>;
|
|
159
|
+
readonly remove: (subject: Subject, otherId: string) => Effect.Effect<void, SocialError>;
|
|
160
|
+
readonly block: (subject: Subject, otherId: string) => Effect.Effect<void, SocialError>;
|
|
161
|
+
readonly unblock: (subject: Subject, otherId: string) => Effect.Effect<void, SocialError>;
|
|
162
|
+
readonly favorite: (subject: Subject, otherId: string, enabled: boolean) => Effect.Effect<void, SocialError>;
|
|
163
|
+
readonly setSettings: (subject: Subject, patch: SettingsPatch) => Effect.Effect<SocialSettings, SocialError>;
|
|
164
|
+
readonly getSettings: (subject: Subject) => Effect.Effect<SocialSettings, SocialError>;
|
|
165
|
+
readonly friends: (subject: Subject) => Effect.Effect<ReadonlyArray<SocialRelation>, SocialError>;
|
|
166
|
+
readonly favorites: (subject: Subject) => Effect.Effect<ReadonlyArray<SocialRelation>, SocialError>;
|
|
167
|
+
readonly requests: (subject: Subject) => Effect.Effect<ReadonlyArray<SocialRelation>, SocialError>;
|
|
168
|
+
readonly blocked: (subject: Subject) => Effect.Effect<ReadonlyArray<SocialRelation>, SocialError>;
|
|
169
|
+
readonly recent: (subject: Subject) => Effect.Effect<ReadonlyArray<RecentPlayer>, SocialError>;
|
|
170
|
+
readonly suggestions: (subject: Subject) => Effect.Effect<ReadonlyArray<SocialSuggestion>, SocialError>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export declare type SocialSettings = typeof SocialSettingsSchema.Type;
|
|
174
|
+
|
|
175
|
+
declare const SocialSettingsSchema: Schema.Struct<{
|
|
176
|
+
readonly ownerId: Schema.String;
|
|
177
|
+
readonly requestsFrom: Schema.Literals<readonly ["anyone", "friends-of-friends", "nobody"]>;
|
|
178
|
+
readonly showOnline: Schema.Boolean;
|
|
179
|
+
readonly showActivity: Schema.Boolean;
|
|
180
|
+
readonly allowPartyInvites: Schema.Literals<readonly ["friends", "anyone"]>;
|
|
181
|
+
readonly metadata: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
182
|
+
}>;
|
|
183
|
+
|
|
184
|
+
export declare const socialSettingsTable: Table<"_voltro_social_settings", FieldDefinitions<{
|
|
185
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
186
|
+
readonly tenantId: ColumnBuilder<string, "text", boolean>;
|
|
187
|
+
readonly ownerId: ColumnBuilder<string, "text", boolean>;
|
|
188
|
+
readonly requestsFrom: ColumnBuilder<"anyone" | "friends-of-friends" | "nobody", "text", boolean>;
|
|
189
|
+
readonly showOnline: ColumnBuilder<boolean, "boolean", boolean>;
|
|
190
|
+
readonly showActivity: ColumnBuilder<boolean, "boolean", boolean>;
|
|
191
|
+
readonly allowPartyInvites: ColumnBuilder<"anyone" | "friends", "text", boolean>;
|
|
192
|
+
readonly admission: ColumnBuilder<number, "integer", boolean>;
|
|
193
|
+
readonly metadata: ColumnBuilder<unknown, "json", boolean>;
|
|
194
|
+
}>, true, never>;
|
|
195
|
+
|
|
196
|
+
export declare interface SocialStrategies {
|
|
197
|
+
readonly suggestions: typeof mutualMatchSuggestions;
|
|
198
|
+
readonly requestPolicy: (input: RequestPolicyInput) => boolean;
|
|
199
|
+
readonly recentPolicy: (input: RecentPolicyInput) => boolean;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export declare type SocialSuggestion = typeof SuggestionSchema.Type;
|
|
203
|
+
|
|
204
|
+
export declare const socialSystemContract: {
|
|
205
|
+
readonly slots: {
|
|
206
|
+
readonly suggestions: {
|
|
207
|
+
readonly defaultExport: "mutualMatchSuggestions";
|
|
208
|
+
readonly fixtureExport: "socialSuggestionsFixture";
|
|
209
|
+
};
|
|
210
|
+
readonly requestPolicy: {
|
|
211
|
+
readonly defaultExport: "recipientRequestPolicy";
|
|
212
|
+
readonly fixtureExport: "socialRequestPolicyFixture";
|
|
213
|
+
};
|
|
214
|
+
readonly recentPolicy: {
|
|
215
|
+
readonly defaultExport: "distinctRecentPeers";
|
|
216
|
+
readonly fixtureExport: "socialRecentPolicyFixture";
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
readonly ports: {
|
|
220
|
+
readonly socialBlock: {
|
|
221
|
+
readonly tagExport: "SocialBlockPort";
|
|
222
|
+
readonly fakeLayerExport: "socialBlockFakeLayer";
|
|
223
|
+
readonly fixtureExport: "socialBlockPortFixture";
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
readonly events: readonly ["social.requested", "social.accepted"];
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
declare const SuggestionSchema: Schema.Struct<{
|
|
230
|
+
readonly subjectId: Schema.String;
|
|
231
|
+
readonly mutualFriends: Schema.Number;
|
|
232
|
+
readonly recentMatches: Schema.Number;
|
|
233
|
+
}>;
|
|
234
|
+
|
|
235
|
+
export { }
|