@voltro/plugin-chat 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.
Files changed (50) hide show
  1. package/CHANGELOG.md +24492 -0
  2. package/LICENSE +61 -0
  3. package/README.md +516 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +16105 -0
  6. package/dist/admissions.entity.js +10 -0
  7. package/dist/channels.entity.js +19 -0
  8. package/dist/chat-app/events/chat.mentioned.event.ts +1 -0
  9. package/dist/chat-app/events/chat.messageChanged.event.ts +1 -0
  10. package/dist/chat-app/events/chat.typing.event.ts +1 -0
  11. package/dist/chat-app/schedules/chat.retention.cron.tsx +16 -0
  12. package/dist/chat-app/subscribers/chat.messages.subscribe.ts +4 -0
  13. package/dist/components.js +431 -0
  14. package/dist/contract.js +91 -0
  15. package/dist/errors.d.ts +13 -0
  16. package/dist/errors.js +20 -0
  17. package/dist/eventProducer.d.ts +18 -0
  18. package/dist/eventProducer.js +45 -0
  19. package/dist/events.d.ts +38 -0
  20. package/dist/events.js +35 -0
  21. package/dist/evidence.entity.js +24 -0
  22. package/dist/index.d.ts +623 -0
  23. package/dist/index.js +180 -0
  24. package/dist/inspect.js +37 -0
  25. package/dist/media.entity.js +14 -0
  26. package/dist/members.entity.js +21 -0
  27. package/dist/mentionDraft.js +20 -0
  28. package/dist/mentionNotifications.js +109 -0
  29. package/dist/messages.entity.js +27 -0
  30. package/dist/reportEvidence.js +15 -0
  31. package/dist/retention.entity.js +10 -0
  32. package/dist/rpc.d.ts +295 -0
  33. package/dist/rpc.js +266 -0
  34. package/dist/rpcRoutes.js +96 -0
  35. package/dist/server.d.ts +200 -0
  36. package/dist/server.js +6 -0
  37. package/dist/service.js +1411 -0
  38. package/dist/strategies.d.ts +148 -0
  39. package/dist/strategies.js +89 -0
  40. package/dist/testing.d.ts +139 -0
  41. package/dist/testing.js +110 -0
  42. package/dist/typing.js +87 -0
  43. package/dist/typingInvalidation.js +85 -0
  44. package/dist/typingLease.js +58 -0
  45. package/dist/typingPublisher.js +58 -0
  46. package/dist/useChatTypingPublisher.js +32 -0
  47. package/dist/useTyping.js +23 -0
  48. package/dist/web.d.ts +354 -0
  49. package/dist/web.js +18 -0
  50. package/package.json +98 -4
package/dist/index.js ADDED
@@ -0,0 +1,180 @@
1
+ import { ChatError as e, chatError as t } from "./errors.js";
2
+ import { chatAdmissionsTable as n } from "./admissions.entity.js";
3
+ import { chatEvidenceTable as r } from "./evidence.entity.js";
4
+ import { CHAT_MAX_MESSAGE_VERSION as i, CHAT_MEDIA_SWEEP_BATCH_SIZE as a, CHAT_RETENTION_BATCH_SIZE as o, CHAT_RETENTION_RUN_BATCHES as s } from "./contract.js";
5
+ import { chatMediaTable as c } from "./media.entity.js";
6
+ import { chatRetentionTable as l } from "./retention.entity.js";
7
+ import { mentioned as u, messageChanged as d, typing as f } from "./events.js";
8
+ import { chatChannelsTable as p } from "./channels.entity.js";
9
+ import { chatMembersTable as m } from "./members.entity.js";
10
+ import { chatMessagesTable as h } from "./messages.entity.js";
11
+ import { dataStoreChat as g } from "./service.js";
12
+ import { BASE_NAME as _, chatRpcClientImports as v } from "./rpc.js";
13
+ import { chatRpcRoutes as y } from "./rpcRoutes.js";
14
+ import { defaultChatStrategies as b, resolveChatOptions as x } from "./strategies.js";
15
+ import { chatStatus as S } from "./inspect.js";
16
+ import { ChatAdministration as C } from "./server.js";
17
+ import { defineLicensedGamePlugin as w } from "@voltro/plugin-licensing/gate";
18
+ import { Effect as T, Layer as E } from "effect";
19
+ import { fileURLToPath as D } from "node:url";
20
+ import { replicaEvidence as O } from "@voltro/protocol/identity";
21
+ import { redisStore as k } from "@voltro/plugin-ratelimit";
22
+ // src/index.ts
23
+ var A = {
24
+ slots: {
25
+ moderate: {
26
+ defaultExport: "keywordChatModeration",
27
+ fixtureExport: "chatModerationFixture"
28
+ },
29
+ rateLimit: {
30
+ defaultExport: "fixedChatRateBudgets",
31
+ fixtureExport: "chatRateLimitFixture"
32
+ },
33
+ mentionResolver: {
34
+ defaultExport: "playerChatMentions",
35
+ fixtureExport: "chatMentionResolverFixture"
36
+ },
37
+ retention: {
38
+ defaultExport: "channelChatRetention",
39
+ fixtureExport: "chatRetentionFixture"
40
+ },
41
+ moderateMedia: {
42
+ defaultExport: "typedChatMedia",
43
+ fixtureExport: "chatMediaModerationFixture"
44
+ }
45
+ },
46
+ ports: {},
47
+ events: [
48
+ d.name,
49
+ u.name,
50
+ f.name
51
+ ]
52
+ }, j = (i = {}) => {
53
+ let a = x(i), o = k(a.rateLimit), s, u, d = !1, b = (e) => T.suspend(() => u === void 0 ? T.fail(t("unavailable")) : e(u)), A = (e, n, r) => {
54
+ try {
55
+ let t = structuredClone(e), i = structuredClone(n);
56
+ return b((e) => r(e, t, i));
57
+ } catch {
58
+ return T.fail(t("invalid_input"));
59
+ }
60
+ };
61
+ return w({
62
+ name: _,
63
+ description: "Membership-filtered durable chat with replaceable rule slots.",
64
+ eventClientPolicies: [{
65
+ id: f.clientPolicy,
66
+ admit: (e) => b((t) => t.typingPolicy.admit(e)),
67
+ deliver: (e) => b((t) => t.typingPolicy.deliver(e)),
68
+ monitor: (e) => b((t) => t.typingPolicy.monitor(e))
69
+ }],
70
+ services: E.succeed(C, {
71
+ readReportEvidence: (e, t) => A(e, t, (e, t, n) => e.readReportEvidence(t, n)),
72
+ collectReportContext: (e, t) => A(e, t, (e, t, n) => e.collectReportContext(t, n)),
73
+ prune: (e) => A(e, null, (e, t) => e.prune(t)),
74
+ sweepMedia: (e) => A(e, null, (e, t) => e.sweepMedia(t)),
75
+ canNotifyMention: (e, t) => A(e, t, (e, t, n) => e.canNotifyMention(t, n)),
76
+ createChannel: (e, t) => A(e, t, (e, t, n) => e.createChannel(t, n)),
77
+ addMember: (e, t) => A(e, t, (e, t, n) => e.addMember(t, n)),
78
+ removeMember: (e, t) => A(e, t, (e, t, n) => e.removeMember(t, n)),
79
+ archiveMatch: (e, t) => A(e, t, (e, t, n) => e.archiveMatch(t, n))
80
+ }),
81
+ permissions: ["store:write", "inspect:read"],
82
+ routes: y({
83
+ mentionCandidates: (e, t) => A(e, t, (e, t, n) => e.service.mentionCandidates(t, n)),
84
+ authors: (e, t) => A(e, t, (e, t, n) => e.service.authors(t, n)),
85
+ prepareTyping: (e, t) => b((n) => n.service.prepareTyping(e, t)),
86
+ dm: (e, t) => b((n) => n.service.dm(e, t)),
87
+ send: (e, t) => b((n) => n.service.send(e, t)),
88
+ edit: (e, t) => b((n) => n.service.edit(e, t)),
89
+ remove: (e, t) => b((n) => n.service.remove(e, t)),
90
+ messages: (e, t) => b((n) => n.service.messages(e, t)),
91
+ channel: (e, t) => b((n) => n.service.channel(e, t)),
92
+ membership: (e, t) => b((n) => n.service.membership(e, t)),
93
+ unread: (e) => b((t) => t.service.unread(e)),
94
+ original: (e, t) => b((n) => n.service.original(e, t)),
95
+ mediaUrl: (e, t) => b((n) => n.service.mediaUrl(e, t)),
96
+ channels: (e) => b((t) => t.service.channels(e)),
97
+ markRead: (e, t, n) => b((r) => r.service.markRead(e, t, n)),
98
+ moderateMember: (e, t) => b((n) => n.service.moderateMember(e, t)),
99
+ configure: (e, t) => b((n) => n.service.configure(e, t)),
100
+ join: (e, t) => b((n) => n.service.join(e, t)),
101
+ leave: (e, t) => b((n) => n.service.leave(e, t)),
102
+ transferOwnership: (e, t, n) => b((r) => r.service.transferOwnership(e, t, n))
103
+ }),
104
+ rpcClientDescriptors: v,
105
+ sources: D(new URL("./chat-app", "" + import.meta.url)),
106
+ errorSchemas: [{
107
+ schema: e,
108
+ import: {
109
+ module: "@voltro/plugin-chat/errors",
110
+ name: "ChatError"
111
+ }
112
+ }],
113
+ extendSchema: { tables: [
114
+ p,
115
+ m,
116
+ h,
117
+ l,
118
+ r,
119
+ n,
120
+ c
121
+ ] },
122
+ subjectPrivacy: {
123
+ tables: [
124
+ p.tableName,
125
+ m.tableName,
126
+ h.tableName,
127
+ l.tableName,
128
+ r.tableName,
129
+ n.tableName,
130
+ c.tableName
131
+ ],
132
+ kinds: ["user", "guest"]
133
+ },
134
+ inspectEndpoints: [{
135
+ method: "GET",
136
+ path: "/status",
137
+ scope: "process",
138
+ description: "Declared chat budgets and process binding; no channel/message/body enumeration.",
139
+ handler: () => T.try({
140
+ try: () => ({
141
+ kind: "json",
142
+ data: S(a, u !== void 0, d)
143
+ }),
144
+ catch: () => t("unavailable")
145
+ })
146
+ }],
147
+ bindDataStore: (e) => {
148
+ if (s !== void 0 && s !== e) throw Error("Chat plugin instance cannot cross app stores");
149
+ s = e, u ??= g(e, a, o);
150
+ },
151
+ onActivate: (e) => T.tryPromise({
152
+ try: async () => {
153
+ if (a.rateLimit?.hotStore !== void 0 && O(e.env) !== null) throw t("unavailable");
154
+ await o.activate(e.env, e.connectionRegistry), d = !0;
155
+ },
156
+ catch: () => t("unavailable")
157
+ }),
158
+ onExport: (e, t) => b((n) => n.exportSubject({
159
+ subjectId: e,
160
+ tenantId: t.tenantId,
161
+ kind: t.kind
162
+ })),
163
+ onErase: (e, t) => b((n) => n.eraseSubject({
164
+ subjectId: e,
165
+ tenantId: t.tenantId,
166
+ kind: t.kind
167
+ })),
168
+ onDeactivate: () => T.tryPromise({
169
+ try: async () => {
170
+ try {
171
+ u?.disposeTyping();
172
+ } finally {
173
+ u = void 0, s = void 0, d = !1, await o.deactivate();
174
+ }
175
+ },
176
+ catch: () => t("unavailable")
177
+ })
178
+ });
179
+ };
180
+ export { i as CHAT_MAX_MESSAGE_VERSION, a as CHAT_MEDIA_SWEEP_BATCH_SIZE, o as CHAT_RETENTION_BATCH_SIZE, s as CHAT_RETENTION_RUN_BATCHES, e as ChatError, n as chatAdmissionsTable, p as chatChannelsTable, r as chatEvidenceTable, c as chatMediaTable, m as chatMembersTable, h as chatMessagesTable, j as chatPlugin, l as chatRetentionTable, A as chatSystemContract, g as dataStoreChat, b as defaultChatStrategies, x as resolveChatOptions };
@@ -0,0 +1,37 @@
1
+ import { chatError as e } from "./errors.js";
2
+ import { defaultChatStrategies as t } from "./strategies.js";
3
+ // src/inspect.ts
4
+ var n = (n, r, i) => {
5
+ let a = n.now();
6
+ if (!Number.isSafeInteger(a) || a < 0 || a > 864e13) throw e("unavailable");
7
+ return {
8
+ observedAt: a,
9
+ bound: r,
10
+ rateActive: i,
11
+ maxBodyLength: n.maxBodyLength,
12
+ pageSize: n.pageSize,
13
+ maxMembers: n.maxMembers,
14
+ maxChannelsPerSubject: n.maxChannelsPerSubject,
15
+ retentionMs: n.retentionMs,
16
+ evidenceRetentionMs: n.evidenceRetentionMs,
17
+ perChannelRate: n.perChannelRate,
18
+ globalRate: n.globalRate,
19
+ maxMentions: n.maxMentions,
20
+ moderationAction: n.moderationAction,
21
+ denyWordCount: n.denyWords.length,
22
+ mediaContentTypes: n.mediaContentTypes,
23
+ maxMediaBytes: n.maxMediaBytes,
24
+ mediaUrlTtlSec: n.mediaUrlTtlSec,
25
+ slots: Object.fromEntries(Object.entries(t).map(([e, t]) => [e, n.strategies[e] === t ? "default" : "custom"])),
26
+ tables: [
27
+ "_voltro_chat_channels",
28
+ "_voltro_chat_members",
29
+ "_voltro_chat_messages",
30
+ "_voltro_chat_retention",
31
+ "_voltro_chat_evidence",
32
+ "_voltro_chat_admissions",
33
+ "_voltro_chat_media"
34
+ ]
35
+ };
36
+ };
37
+ export { n as chatStatus };
@@ -0,0 +1,14 @@
1
+ import { MEDIA_TABLE as e } from "./contract.js";
2
+ import { id as t, table as n, text as r, timestamp as i } from "@voltro/database";
3
+ // src/media.entity.ts
4
+ var a = n(e, {
5
+ id: t({ prefix: "cmed" }),
6
+ tenantId: r().maxLength(256).externalId().serverOnly(),
7
+ channelId: r().maxLength(64).serverOnly(),
8
+ messageId: r().maxLength(64).serverOnly(),
9
+ storageId: r().maxLength(256).serverOnly(),
10
+ ownerId: r().maxLength(256).nullable().externalId().sensitive("pii").serverOnly(),
11
+ state: r().maxLength(16).oneOf(["active", "deleting"]).serverOnly(),
12
+ createdAt: i().serverOnly()
13
+ }).index(["tenantId", "ownerId"]).index(["state", "id"]).index(["tenantId", "storageId"]);
14
+ export { a as chatMediaTable };
@@ -0,0 +1,21 @@
1
+ import { MEMBERS_TABLE as e } from "./contract.js";
2
+ import { boolean as t, id as n, json as r, table as i, text as a, timestamp as o } from "@voltro/database";
3
+ // src/members.entity.ts
4
+ var s = i(e, {
5
+ id: n({ prefix: "cmem" }),
6
+ tenantId: a().maxLength(256).externalId().sensitive("pii"),
7
+ channelId: a().maxLength(64).safe(),
8
+ subjectId: a().maxLength(256).externalId().sensitive("pii"),
9
+ role: a().maxLength(16).oneOf([
10
+ "member",
11
+ "mod",
12
+ "owner"
13
+ ]).safe(),
14
+ mutedUntil: o().nullable().safe(),
15
+ lastReadAt: o().safe(),
16
+ lastReadId: a().maxLength(64).nullable().safe(),
17
+ lastSentAt: o().nullable().serverOnly(),
18
+ notify: t().safe(),
19
+ metadata: r().sensitive("pii")
20
+ }).index(["tenantId", "subjectId"]).index(["channelId"]);
21
+ export { s as chatMembersTable };
@@ -0,0 +1,20 @@
1
+ // src/mentionDraft.ts
2
+ var e = (e, t, n) => {
3
+ if (t !== n || !Number.isSafeInteger(t) || t < 0 || t > e.length || t < e.length && /[\p{L}\p{N}\p{M}_#]/u.test(String.fromCodePoint(e.codePointAt(t)))) return null;
4
+ let r = e.lastIndexOf("@", t - 1);
5
+ if (r < 0 || t <= r || r > 0 && !/[\s(\[{]/u.test(e[r - 1])) return null;
6
+ let i = e.slice(r + 1, t);
7
+ return i.length > 261 || /[@\r\n]/u.test(i) ? null : {
8
+ start: r,
9
+ end: t,
10
+ prefix: i
11
+ };
12
+ }, t = (t, n, r, i) => {
13
+ if (!r || r.length > 256 || /[@\r\n]/u.test(r) || !/#\d{4}$/u.test(r) || e(t, n.end, n.end)?.start !== n.start || t.slice(n.start + 1, n.end) !== n.prefix) return null;
14
+ let a = "@" + r + " ", o = t.slice(0, n.start) + a + t.slice(n.end);
15
+ return [...o].length <= i ? {
16
+ body: o,
17
+ caret: n.start + a.length
18
+ } : null;
19
+ };
20
+ export { t as insertMentionDraft, e as mentionDraftToken };
@@ -0,0 +1,109 @@
1
+ import { chatError as e } from "./errors.js";
2
+ import { MESSAGES_TABLE as t } from "./contract.js";
3
+ import { chatEventTenant as n, committedChatMentions as r } from "./eventProducer.js";
4
+ import { ChatAdministration as i } from "./server.js";
5
+ import { Effect as a, Schema as o } from "effect";
6
+ import { createHash as s } from "node:crypto";
7
+ import { defineOutboxHandler as c, defineSubscriber as l } from "@voltro/runtime";
8
+ import { defineNotification as u, notificationOutboxHandler as d } from "@voltro/plugin-notifications";
9
+ // src/mentionNotifications.ts
10
+ var f = "voltro.chat.mention.notify", p = "chat.mention", m = o.NonEmptyString.pipe(o.check(o.isMaxLength(256)), o.check(o.makeFilter((e) => e.trim().length > 0 && !e.includes("\0") && e !== "~"))), h = o.Struct({
11
+ tenantId: m,
12
+ channelId: m,
13
+ messageId: m,
14
+ recipientId: m
15
+ }), g = o.Struct({
16
+ tenantId: m,
17
+ channelId: m,
18
+ messageId: m,
19
+ recipientId: m,
20
+ locale: o.NonEmptyString.pipe(o.check(o.isMaxLength(80)))
21
+ }), _ = u({
22
+ name: "chat.mentioned",
23
+ audience: o.Struct({ userId: m }),
24
+ payload: o.Struct({
25
+ channelId: m,
26
+ messageId: m
27
+ }),
28
+ render: (e, t) => ({
29
+ title: t.locale.startsWith("de") ? "Du wurdest erwähnt" : "You were mentioned",
30
+ body: t.locale.startsWith("de") ? "Öffne den Chat, um die Nachricht zu lesen." : "Open the chat to read the message.",
31
+ data: {
32
+ channelId: e.channelId,
33
+ messageId: e.messageId
34
+ }
35
+ })
36
+ }), v = (u) => {
37
+ let m = structuredClone(u.subject), v = u.run, y = u.service, b = u.outboxForTenant, x = u.localeFor, S = (e) => v(a.flatMap(i, (t) => t.canNotifyMention(m, e))), C = async (e) => {
38
+ let t = o.decodeUnknownSync(h)(e.payload);
39
+ return e.tenantId !== t.tenantId || e.recipientId !== t.recipientId ? !1 : S(t);
40
+ }, w = async (e) => {
41
+ let t = o.decodeUnknownSync(h)(structuredClone(e));
42
+ if (!await S(t)) return null;
43
+ let n = await x?.(t.recipientId, t.tenantId) ?? "en", r = o.decodeUnknownSync(g)({
44
+ ...t,
45
+ locale: n
46
+ }), i = s("sha256").update(JSON.stringify([
47
+ t.tenantId,
48
+ t.messageId,
49
+ t.recipientId
50
+ ])).digest("hex");
51
+ return b(t.tenantId).enqueue(f, { ...r }, {
52
+ idempotencyKey: "chat.mention:" + i,
53
+ maxAttempts: 5
54
+ });
55
+ };
56
+ return {
57
+ deliveryGuard: C,
58
+ enqueue: w,
59
+ outboxHandler: c({
60
+ effect: f,
61
+ maxAttempts: 5,
62
+ handler: async (t) => {
63
+ let n = o.decodeUnknownSync(g)(t.payload);
64
+ if (t.tenantId !== n.tenantId) throw e("identity_required");
65
+ if (!await S(n)) return { skipped: !0 };
66
+ let r = y();
67
+ if (!r) throw e("unavailable");
68
+ return d({
69
+ notifications: [_],
70
+ service: () => ({ send: async (t) => {
71
+ let i = await r.send({
72
+ ...t,
73
+ deliveryGuard: {
74
+ id: p,
75
+ payload: {
76
+ tenantId: n.tenantId,
77
+ channelId: n.channelId,
78
+ messageId: n.messageId,
79
+ recipientId: n.recipientId
80
+ }
81
+ }
82
+ });
83
+ if (i.failed.length > 0) throw e("unavailable");
84
+ return i;
85
+ } })
86
+ }).handler({ payload: {
87
+ notification: _.name,
88
+ recipientId: n.recipientId,
89
+ locale: n.locale,
90
+ tenantId: n.tenantId,
91
+ payload: {
92
+ channelId: n.channelId,
93
+ messageId: n.messageId
94
+ }
95
+ } });
96
+ }
97
+ }),
98
+ subscriber: l({
99
+ table: t,
100
+ on: ["insert"],
101
+ once: !0,
102
+ eventTenant: n,
103
+ handler: async (e) => {
104
+ for (let t of r(e)) await w(t);
105
+ }
106
+ })
107
+ };
108
+ };
109
+ export { f as CHAT_MENTION_NOTIFICATION_EFFECT, p as CHAT_MENTION_NOTIFICATION_GUARD, v as createChatMentionNotifications };
@@ -0,0 +1,27 @@
1
+ import { MESSAGES_TABLE as e } from "./contract.js";
2
+ import { boolean as t, id as n, integer as r, json as i, table as a, text as o, timestamp as s } from "@voltro/database";
3
+ // src/messages.entity.ts
4
+ var c = a(e, {
5
+ id: n({ prefix: "cmsg" }),
6
+ tenantId: o().maxLength(256).externalId().sensitive("pii"),
7
+ channelId: o().maxLength(64).safe(),
8
+ authorId: o().maxLength(256).nullable().externalId().sensitive("pii"),
9
+ requestKey: o().maxLength(64).serverOnly(),
10
+ kind: o().maxLength(16).oneOf([
11
+ "text",
12
+ "emote",
13
+ "system",
14
+ "media"
15
+ ]).safe(),
16
+ body: o().sensitive("pii"),
17
+ originalBody: o().sensitive("pii").serverOnly(),
18
+ payload: i().sensitive("pii"),
19
+ replyToId: o().maxLength(64).nullable().safe(),
20
+ editedAt: s().nullable().safe(),
21
+ deletedAt: s().nullable().safe(),
22
+ flagged: t().safe(),
23
+ createdAt: s().safe(),
24
+ version: r().safe(),
25
+ metadata: i().sensitive("pii")
26
+ }).index(["channelId", "createdAt"]).index(["tenantId", "authorId"]);
27
+ export { c as chatMessagesTable };
@@ -0,0 +1,15 @@
1
+ import { ChatAdministration as e } from "./server.js";
2
+ import { Effect as t } from "effect";
3
+ import { sanctionsError as n } from "@voltro/plugin-sanctions/errors";
4
+ // src/reportEvidence.ts
5
+ var r = (r) => t.gen(function* () {
6
+ if (r.context.channelId === void 0) return [];
7
+ if (!Array.isArray(r.context.messageIds) || r.context.messageIds.length !== 1) return yield* t.fail(n("invalid_input"));
8
+ let i = yield* t.serviceOption(e);
9
+ return i._tag === "None" ? yield* t.fail(n("unavailable")) : [{ ...yield* i.value.collectReportContext(r.subject, {
10
+ channelId: r.context.channelId,
11
+ messageId: r.context.messageIds[0],
12
+ targetId: r.targetId
13
+ }).pipe(t.mapError((e) => n(e.code === "unavailable" ? "unavailable" : "forbidden")), t.catchDefect(() => t.fail(n("unavailable")))) }];
14
+ });
15
+ export { r as chatSanctionEvidenceCollector };
@@ -0,0 +1,10 @@
1
+ import { id as e, integer as t, table as n, text as r, timestamp as i } from "@voltro/database";
2
+ // src/retention.entity.ts
3
+ var a = n("_voltro_chat_retention", {
4
+ id: e({ prefix: "cret" }),
5
+ tenantId: r().maxLength(256).nullable().serverOnly(),
6
+ afterChannelId: r().maxLength(64).nullable().serverOnly(),
7
+ admission: t().serverOnly(),
8
+ updatedAt: i().serverOnly()
9
+ });
10
+ export { a as chatRetentionTable };