@crowdedkingdoms/crowdyjs 8.2.0 → 8.3.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 (102) hide show
  1. package/README.md +21 -3
  2. package/dist/crowdy-client.d.ts.map +1 -1
  3. package/dist/crowdy-client.js +5 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -2
  7. package/dist/kit/blueprints/combat.d.ts +75 -0
  8. package/dist/kit/blueprints/combat.d.ts.map +1 -0
  9. package/dist/kit/blueprints/combat.js +351 -0
  10. package/dist/kit/blueprints/core.d.ts +222 -0
  11. package/dist/kit/blueprints/core.d.ts.map +1 -0
  12. package/dist/kit/blueprints/core.js +185 -0
  13. package/dist/kit/blueprints/decks.d.ts +50 -0
  14. package/dist/kit/blueprints/decks.d.ts.map +1 -0
  15. package/dist/kit/blueprints/decks.js +163 -0
  16. package/dist/kit/blueprints/economy.d.ts +88 -0
  17. package/dist/kit/blueprints/economy.d.ts.map +1 -0
  18. package/dist/kit/blueprints/economy.js +533 -0
  19. package/dist/kit/blueprints/guild.d.ts +51 -0
  20. package/dist/kit/blueprints/guild.d.ts.map +1 -0
  21. package/dist/kit/blueprints/guild.js +50 -0
  22. package/dist/kit/blueprints/index.d.ts +21 -0
  23. package/dist/kit/blueprints/index.d.ts.map +1 -0
  24. package/dist/kit/blueprints/index.js +20 -0
  25. package/dist/kit/blueprints/inventory.d.ts +38 -0
  26. package/dist/kit/blueprints/inventory.d.ts.map +1 -0
  27. package/dist/kit/blueprints/inventory.js +157 -0
  28. package/dist/kit/blueprints/leaderboards.d.ts +49 -0
  29. package/dist/kit/blueprints/leaderboards.d.ts.map +1 -0
  30. package/dist/kit/blueprints/leaderboards.js +126 -0
  31. package/dist/kit/blueprints/locks.d.ts +82 -0
  32. package/dist/kit/blueprints/locks.d.ts.map +1 -0
  33. package/dist/kit/blueprints/locks.js +155 -0
  34. package/dist/kit/blueprints/loot.d.ts +84 -0
  35. package/dist/kit/blueprints/loot.d.ts.map +1 -0
  36. package/dist/kit/blueprints/loot.js +228 -0
  37. package/dist/kit/blueprints/matches.d.ts +59 -0
  38. package/dist/kit/blueprints/matches.d.ts.map +1 -0
  39. package/dist/kit/blueprints/matches.js +246 -0
  40. package/dist/kit/blueprints/npcs.d.ts +71 -0
  41. package/dist/kit/blueprints/npcs.d.ts.map +1 -0
  42. package/dist/kit/blueprints/npcs.js +122 -0
  43. package/dist/kit/blueprints/plots.d.ts +54 -0
  44. package/dist/kit/blueprints/plots.d.ts.map +1 -0
  45. package/dist/kit/blueprints/plots.js +146 -0
  46. package/dist/kit/blueprints/progression.d.ts +68 -0
  47. package/dist/kit/blueprints/progression.d.ts.map +1 -0
  48. package/dist/kit/blueprints/progression.js +324 -0
  49. package/dist/kit/blueprints/quests.d.ts +65 -0
  50. package/dist/kit/blueprints/quests.d.ts.map +1 -0
  51. package/dist/kit/blueprints/quests.js +284 -0
  52. package/dist/kit/blueprints/worldsim.d.ts +84 -0
  53. package/dist/kit/blueprints/worldsim.d.ts.map +1 -0
  54. package/dist/kit/blueprints/worldsim.js +461 -0
  55. package/dist/kit/combat.d.ts +103 -0
  56. package/dist/kit/combat.d.ts.map +1 -0
  57. package/dist/kit/combat.js +151 -0
  58. package/dist/kit/decks.d.ts +123 -0
  59. package/dist/kit/decks.d.ts.map +1 -0
  60. package/dist/kit/decks.js +138 -0
  61. package/dist/kit/economy.d.ts +244 -0
  62. package/dist/kit/economy.d.ts.map +1 -0
  63. package/dist/kit/economy.js +365 -0
  64. package/dist/kit/features.d.ts +60 -0
  65. package/dist/kit/features.d.ts.map +1 -0
  66. package/dist/kit/features.js +65 -0
  67. package/dist/kit/index.d.ts +13 -2
  68. package/dist/kit/index.d.ts.map +1 -1
  69. package/dist/kit/index.js +12 -1
  70. package/dist/kit/inventory.d.ts +5 -0
  71. package/dist/kit/inventory.d.ts.map +1 -1
  72. package/dist/kit/inventory.js +14 -1
  73. package/dist/kit/kit.d.ts +58 -2
  74. package/dist/kit/kit.d.ts.map +1 -1
  75. package/dist/kit/kit.js +24 -2
  76. package/dist/kit/leaderboards.d.ts +66 -0
  77. package/dist/kit/leaderboards.d.ts.map +1 -0
  78. package/dist/kit/leaderboards.js +94 -0
  79. package/dist/kit/loot.d.ts +106 -0
  80. package/dist/kit/loot.d.ts.map +1 -0
  81. package/dist/kit/loot.js +120 -0
  82. package/dist/kit/matches.d.ts +140 -0
  83. package/dist/kit/matches.d.ts.map +1 -0
  84. package/dist/kit/matches.js +253 -0
  85. package/dist/kit/objects.js +1 -1
  86. package/dist/kit/plots.js +1 -1
  87. package/dist/kit/progression.d.ts +181 -0
  88. package/dist/kit/progression.d.ts.map +1 -0
  89. package/dist/kit/progression.js +265 -0
  90. package/dist/kit/quests.d.ts +110 -0
  91. package/dist/kit/quests.d.ts.map +1 -0
  92. package/dist/kit/quests.js +158 -0
  93. package/dist/kit/social.d.ts +267 -0
  94. package/dist/kit/social.d.ts.map +1 -0
  95. package/dist/kit/social.js +230 -0
  96. package/dist/kit/worldsim.d.ts +225 -0
  97. package/dist/kit/worldsim.d.ts.map +1 -0
  98. package/dist/kit/worldsim.js +231 -0
  99. package/package.json +1 -1
  100. package/dist/kit/blueprints.d.ts +0 -370
  101. package/dist/kit/blueprints.d.ts.map +0 -1
  102. package/dist/kit/blueprints.js +0 -643
@@ -0,0 +1,267 @@
1
+ import type { ChannelsAPI } from '../domains/channels.js';
2
+ import type { GameAppsAPI } from '../domains/gameApps.js';
3
+ import type { TeamsAPI } from '../domains/teams.js';
4
+ import type { UdpAPI } from '../domains/udp.js';
5
+ import type { Scalars } from '../generated/graphql.js';
6
+ /** Options for {@link SocialKit}. */
7
+ export interface SocialKitOptions {
8
+ /**
9
+ * The 32-ASCII-char actor uuid used as the sender id on chat messages.
10
+ * Defaults to a random uuid per kit instance — set it to YOUR actor uuid
11
+ * so receivers can attribute messages.
12
+ */
13
+ actorUuid?: string;
14
+ /** Name prefix for party teams/channels. Defaults to `'party:'`. */
15
+ partyPrefix?: string;
16
+ /** Name prefix for guild teams/channels. Defaults to `'guild:'`. */
17
+ guildPrefix?: string;
18
+ }
19
+ /** A party or guild: the team plus its paired chat channel. */
20
+ export interface KitGroupWithChannel {
21
+ /** The team's group id (membership/roles live here). */
22
+ teamId: string;
23
+ /** The paired chat channel's group id (may be '' if pairing failed). */
24
+ channelId: string;
25
+ name: string;
26
+ }
27
+ /** A decoded chat message from a kit chat room. */
28
+ export interface KitChatMessage {
29
+ channelId: string;
30
+ /** The sender's actor uuid (attribution is by app convention). */
31
+ senderUuid: string;
32
+ text: string;
33
+ epochMillis: string;
34
+ }
35
+ /**
36
+ * Runtime **social** helpers — parties, guilds, and chat rooms in familiar
37
+ * words, wrapped over the platform's teams (membership + roles) and
38
+ * channels (location-independent messaging) with realtime delivery via the
39
+ * UDP notification subscription. No model schema needed; the only
40
+ * deployable is the optional {@link guildBlueprint} composite (guild hall +
41
+ * bank).
42
+ *
43
+ * Conventions: a party is a team named `party:<name>` paired with an
44
+ * equally-named channel; a guild is `guild:<name>` likewise. Guild
45
+ * territory = a grid group-grant (`claimTerritory`), enforced by the
46
+ * replication layer.
47
+ *
48
+ * Obtained via `client.kit(appId).social`.
49
+ */
50
+ export declare class SocialKit {
51
+ private readonly appId;
52
+ private readonly teams;
53
+ private readonly channels;
54
+ private readonly udp;
55
+ private readonly gameApps;
56
+ private readonly actorUuid;
57
+ private readonly partyPrefix;
58
+ private readonly guildPrefix;
59
+ constructor(appId: Scalars['BigInt']['input'], teams: TeamsAPI | undefined, channels: ChannelsAPI | undefined, udp: UdpAPI | undefined, gameApps: GameAppsAPI, options?: SocialKitOptions);
60
+ private requireTeams;
61
+ private requireChannels;
62
+ private requireUdp;
63
+ /** Create a team + equally-named chat channel pair. */
64
+ private createPair;
65
+ /** Find a team + channel pair by its full name. */
66
+ private findPair;
67
+ /** Parties: small invite-based groups with their own chat channel. */
68
+ readonly party: {
69
+ /** Create a party (invite-only by default). The creator becomes leader. */
70
+ create: (name: string) => Promise<KitGroupWithChannel>;
71
+ /** Find a party by name. */
72
+ find: (name: string) => Promise<KitGroupWithChannel | undefined>;
73
+ /**
74
+ * Invite (add) a player to the party — requires the leader's
75
+ * `manage_members`. Adds them to the chat channel membership too.
76
+ */
77
+ invite: (party: KitGroupWithChannel, userId: Scalars["BigInt"]["input"]) => Promise<{
78
+ __typename?: "GroupMember";
79
+ groupMemberId: string;
80
+ groupId: string;
81
+ userId: string;
82
+ status: string;
83
+ createdAt: string;
84
+ roles: Array<{
85
+ __typename?: "GroupRole";
86
+ groupRoleId: string;
87
+ roleName: string;
88
+ rank: number;
89
+ isSystem: boolean;
90
+ permissions: Array<string>;
91
+ }>;
92
+ }>;
93
+ /** Join an open party (and its chat channel) as the caller. */
94
+ join: (party: KitGroupWithChannel) => Promise<{
95
+ __typename?: "GroupMember";
96
+ groupMemberId: string;
97
+ groupId: string;
98
+ userId: string;
99
+ status: string;
100
+ createdAt: string;
101
+ roles: Array<{
102
+ __typename?: "GroupRole";
103
+ groupRoleId: string;
104
+ roleName: string;
105
+ rank: number;
106
+ isSystem: boolean;
107
+ permissions: Array<string>;
108
+ }>;
109
+ }>;
110
+ /** Leave the party (and its chat channel). */
111
+ leave: (party: KitGroupWithChannel) => Promise<boolean>;
112
+ /** The party roster. */
113
+ members: (party: KitGroupWithChannel) => Promise<{
114
+ __typename?: "GroupMember";
115
+ groupMemberId: string;
116
+ groupId: string;
117
+ userId: string;
118
+ status: string;
119
+ createdAt: string;
120
+ roles: Array<{
121
+ __typename?: "GroupRole";
122
+ groupRoleId: string;
123
+ roleName: string;
124
+ rank: number;
125
+ isSystem: boolean;
126
+ permissions: Array<string>;
127
+ }>;
128
+ }[]>;
129
+ };
130
+ /** Guilds: persistent role-based organizations with chat + territory. */
131
+ readonly guild: {
132
+ /** Create a guild (request-to-join by default). The creator becomes leader. */
133
+ create: (name: string, options?: {
134
+ membershipPolicy?: string;
135
+ description?: string;
136
+ }) => Promise<KitGroupWithChannel>;
137
+ /** Find a guild by name. */
138
+ find: (name: string) => Promise<KitGroupWithChannel | undefined>;
139
+ /** The guild roster (members + pending join requests). */
140
+ roster: (guild: KitGroupWithChannel) => Promise<{
141
+ __typename?: "GroupMember";
142
+ groupMemberId: string;
143
+ groupId: string;
144
+ userId: string;
145
+ status: string;
146
+ createdAt: string;
147
+ roles: Array<{
148
+ __typename?: "GroupRole";
149
+ groupRoleId: string;
150
+ roleName: string;
151
+ rank: number;
152
+ isSystem: boolean;
153
+ permissions: Array<string>;
154
+ }>;
155
+ }[]>;
156
+ /** The guild's roles (including the system leader role). */
157
+ roles: (guild: KitGroupWithChannel) => Promise<{
158
+ __typename?: "GroupRole";
159
+ groupRoleId: string;
160
+ groupId: string;
161
+ roleName: string;
162
+ rank: number;
163
+ isSystem: boolean;
164
+ permissions: Array<string>;
165
+ createdAt: string;
166
+ }[]>;
167
+ /** Create a custom guild role (requires `manage_roles`). */
168
+ createRole: (guild: KitGroupWithChannel, input: {
169
+ roleName: string;
170
+ permissions?: string[];
171
+ rank?: number;
172
+ }) => Promise<{
173
+ __typename?: "GroupRole";
174
+ groupRoleId: string;
175
+ groupId: string;
176
+ roleName: string;
177
+ rank: number;
178
+ isSystem: boolean;
179
+ permissions: Array<string>;
180
+ createdAt: string;
181
+ }>;
182
+ /**
183
+ * Promote/demote a member: REPLACES their role set (requires
184
+ * `manage_roles`).
185
+ */
186
+ promote: (guild: KitGroupWithChannel, userId: Scalars["BigInt"]["input"], roleIds: string[]) => Promise<{
187
+ __typename?: "GroupMember";
188
+ groupMemberId: string;
189
+ groupId: string;
190
+ userId: string;
191
+ status: string;
192
+ createdAt: string;
193
+ roles: Array<{
194
+ __typename?: "GroupRole";
195
+ groupRoleId: string;
196
+ roleName: string;
197
+ rank: number;
198
+ isSystem: boolean;
199
+ permissions: Array<string>;
200
+ }>;
201
+ }>;
202
+ /**
203
+ * Claim territory for the guild: grants runtime permission keys on a
204
+ * grid to every guild member (optionally one role) — enforced by the
205
+ * replication layer on movement/voxel writes. Requires grid admin
206
+ * rights on the app.
207
+ */
208
+ claimTerritory: (guild: KitGroupWithChannel, gridId: Scalars["BigInt"]["input"], options?: {
209
+ permissionKeys?: string[];
210
+ groupRoleId?: string;
211
+ }) => Promise<{
212
+ __typename?: "GridGroupGrant";
213
+ appId: string;
214
+ gridId: string;
215
+ groupId: string;
216
+ groupRoleId: string | null;
217
+ permissionKey: string;
218
+ expiresAt: string | null;
219
+ }[]>;
220
+ };
221
+ /** Chat rooms: named channels with realtime text delivery. */
222
+ readonly chat: {
223
+ /** Find-or-create a chat room (an open channel) by name. */
224
+ room: (name: string) => Promise<{
225
+ __typename?: "Group";
226
+ groupId: string;
227
+ appId: string;
228
+ groupType: string;
229
+ name: string;
230
+ description: string | null;
231
+ ownerUserId: string | null;
232
+ membershipPolicy: string;
233
+ status: string;
234
+ defaultRoleId: string | null;
235
+ createdAt: string;
236
+ }>;
237
+ /** Join a chat room. */
238
+ join: (channelId: Scalars["BigInt"]["input"]) => Promise<{
239
+ __typename?: "GroupMember";
240
+ groupMemberId: string;
241
+ groupId: string;
242
+ userId: string;
243
+ status: string;
244
+ createdAt: string;
245
+ roles: Array<{
246
+ __typename?: "GroupRole";
247
+ groupRoleId: string;
248
+ roleName: string;
249
+ rank: number;
250
+ isSystem: boolean;
251
+ permissions: Array<string>;
252
+ }>;
253
+ }>;
254
+ /**
255
+ * Send a UTF-8 text message to a room (requires channel membership with
256
+ * `send_messages`). Delivery is fan-out to every active member's
257
+ * notification subscription, regardless of world location.
258
+ */
259
+ send: (channelId: Scalars["BigInt"]["input"], text: string) => Promise<boolean>;
260
+ /**
261
+ * Listen for messages in one room: decodes each ping's payload as UTF-8
262
+ * text. Returns the unsubscribe function.
263
+ */
264
+ onMessage: (channelId: Scalars["BigInt"]["input"], callback: (message: KitChatMessage) => void) => (() => void);
265
+ };
266
+ }
267
+ //# sourceMappingURL=social.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social.d.ts","sourceRoot":"","sources":["../../src/kit/social.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,230 @@
1
+ import { decodeBase64, encodeBase64, generateCrowdyUuid } from '../utils.js';
2
+ /**
3
+ * Runtime **social** helpers — parties, guilds, and chat rooms in familiar
4
+ * words, wrapped over the platform's teams (membership + roles) and
5
+ * channels (location-independent messaging) with realtime delivery via the
6
+ * UDP notification subscription. No model schema needed; the only
7
+ * deployable is the optional {@link guildBlueprint} composite (guild hall +
8
+ * bank).
9
+ *
10
+ * Conventions: a party is a team named `party:<name>` paired with an
11
+ * equally-named channel; a guild is `guild:<name>` likewise. Guild
12
+ * territory = a grid group-grant (`claimTerritory`), enforced by the
13
+ * replication layer.
14
+ *
15
+ * Obtained via `client.kit(appId).social`.
16
+ */
17
+ export class SocialKit {
18
+ constructor(appId, teams, channels, udp, gameApps, options = {}) {
19
+ this.appId = appId;
20
+ this.teams = teams;
21
+ this.channels = channels;
22
+ this.udp = udp;
23
+ this.gameApps = gameApps;
24
+ /** Parties: small invite-based groups with their own chat channel. */
25
+ this.party = {
26
+ /** Create a party (invite-only by default). The creator becomes leader. */
27
+ create: async (name) => {
28
+ return this.createPair(`${this.partyPrefix}${name}`, 'invite', 'Party');
29
+ },
30
+ /** Find a party by name. */
31
+ find: async (name) => {
32
+ return this.findPair(`${this.partyPrefix}${name}`);
33
+ },
34
+ /**
35
+ * Invite (add) a player to the party — requires the leader's
36
+ * `manage_members`. Adds them to the chat channel membership too.
37
+ */
38
+ invite: async (party, userId) => {
39
+ const member = await this.requireTeams().addMember(party.teamId, userId);
40
+ if (party.channelId) {
41
+ await this.requireChannels().addMember(party.channelId, userId);
42
+ }
43
+ return member;
44
+ },
45
+ /** Join an open party (and its chat channel) as the caller. */
46
+ join: async (party) => {
47
+ const member = await this.requireTeams().join(party.teamId);
48
+ if (party.channelId) {
49
+ await this.requireChannels().join(party.channelId);
50
+ }
51
+ return member;
52
+ },
53
+ /** Leave the party (and its chat channel). */
54
+ leave: async (party) => {
55
+ if (party.channelId) {
56
+ await this.requireChannels().leave(party.channelId);
57
+ }
58
+ return this.requireTeams().leave(party.teamId);
59
+ },
60
+ /** The party roster. */
61
+ members: async (party) => {
62
+ return this.requireTeams().members(party.teamId);
63
+ },
64
+ };
65
+ /** Guilds: persistent role-based organizations with chat + territory. */
66
+ this.guild = {
67
+ /** Create a guild (request-to-join by default). The creator becomes leader. */
68
+ create: async (name, options = {}) => {
69
+ return this.createPair(`${this.guildPrefix}${name}`, options.membershipPolicy ?? 'request', options.description ?? 'Guild');
70
+ },
71
+ /** Find a guild by name. */
72
+ find: async (name) => {
73
+ return this.findPair(`${this.guildPrefix}${name}`);
74
+ },
75
+ /** The guild roster (members + pending join requests). */
76
+ roster: async (guild) => {
77
+ return this.requireTeams().members(guild.teamId);
78
+ },
79
+ /** The guild's roles (including the system leader role). */
80
+ roles: async (guild) => {
81
+ return this.requireTeams().roles(guild.teamId);
82
+ },
83
+ /** Create a custom guild role (requires `manage_roles`). */
84
+ createRole: async (guild, input) => {
85
+ return this.requireTeams().createRole({
86
+ groupId: guild.teamId,
87
+ roleName: input.roleName,
88
+ ...(input.permissions !== undefined ? { permissions: input.permissions } : {}),
89
+ ...(input.rank !== undefined ? { rank: input.rank } : {}),
90
+ });
91
+ },
92
+ /**
93
+ * Promote/demote a member: REPLACES their role set (requires
94
+ * `manage_roles`).
95
+ */
96
+ promote: async (guild, userId, roleIds) => {
97
+ return this.requireTeams().setMemberRoles({
98
+ groupId: guild.teamId,
99
+ userId,
100
+ roleIds,
101
+ });
102
+ },
103
+ /**
104
+ * Claim territory for the guild: grants runtime permission keys on a
105
+ * grid to every guild member (optionally one role) — enforced by the
106
+ * replication layer on movement/voxel writes. Requires grid admin
107
+ * rights on the app.
108
+ */
109
+ claimTerritory: async (guild, gridId, options = {}) => {
110
+ return this.gameApps.assignGroup({
111
+ appId: this.appId,
112
+ gridId,
113
+ groupId: guild.teamId,
114
+ permissionKeys: options.permissionKeys ?? ['access', 'update_voxel_data'],
115
+ ...(options.groupRoleId !== undefined
116
+ ? { groupRoleId: options.groupRoleId }
117
+ : {}),
118
+ });
119
+ },
120
+ };
121
+ /** Chat rooms: named channels with realtime text delivery. */
122
+ this.chat = {
123
+ /** Find-or-create a chat room (an open channel) by name. */
124
+ room: async (name) => {
125
+ const channels = this.requireChannels();
126
+ const existing = (await channels.list(this.appId)).find((c) => c.name === name);
127
+ if (existing)
128
+ return existing;
129
+ return channels.create({ appId: this.appId, name });
130
+ },
131
+ /** Join a chat room. */
132
+ join: async (channelId) => {
133
+ return this.requireChannels().join(channelId);
134
+ },
135
+ /**
136
+ * Send a UTF-8 text message to a room (requires channel membership with
137
+ * `send_messages`). Delivery is fan-out to every active member's
138
+ * notification subscription, regardless of world location.
139
+ */
140
+ send: async (channelId, text) => {
141
+ return this.requireUdp().sendChannelMessage({
142
+ channelId,
143
+ uuid: this.actorUuid,
144
+ payload: encodeBase64(new TextEncoder().encode(text)),
145
+ });
146
+ },
147
+ /**
148
+ * Listen for messages in one room: decodes each ping's payload as UTF-8
149
+ * text. Returns the unsubscribe function.
150
+ */
151
+ onMessage: (channelId, callback) => {
152
+ return this.requireUdp().subscribe({
153
+ channelMessage: (notification) => {
154
+ if (String(notification.channelId) !== String(channelId))
155
+ return;
156
+ let text = '';
157
+ try {
158
+ text = new TextDecoder().decode(decodeBase64(notification.payload));
159
+ }
160
+ catch {
161
+ text = '';
162
+ }
163
+ callback({
164
+ channelId: String(notification.channelId),
165
+ senderUuid: notification.uuid,
166
+ text,
167
+ epochMillis: String(notification.epochMillis),
168
+ });
169
+ },
170
+ }, String(this.appId));
171
+ },
172
+ };
173
+ this.actorUuid = options.actorUuid ?? generateCrowdyUuid();
174
+ this.partyPrefix = options.partyPrefix ?? 'party:';
175
+ this.guildPrefix = options.guildPrefix ?? 'guild:';
176
+ }
177
+ requireTeams() {
178
+ if (!this.teams) {
179
+ throw new Error('kit.social needs the teams domain — construct the kit via client.kit(appId)');
180
+ }
181
+ return this.teams;
182
+ }
183
+ requireChannels() {
184
+ if (!this.channels) {
185
+ throw new Error('kit.social needs the channels domain — construct the kit via client.kit(appId)');
186
+ }
187
+ return this.channels;
188
+ }
189
+ requireUdp() {
190
+ if (!this.udp) {
191
+ throw new Error('kit.social needs the udp domain — construct the kit via client.kit(appId)');
192
+ }
193
+ return this.udp;
194
+ }
195
+ /** Create a team + equally-named chat channel pair. */
196
+ async createPair(name, membershipPolicy, description) {
197
+ const team = await this.requireTeams().create({
198
+ appId: this.appId,
199
+ name,
200
+ description,
201
+ ...(membershipPolicy !== undefined ? { membershipPolicy } : {}),
202
+ });
203
+ const channel = await this.requireChannels().create({
204
+ appId: this.appId,
205
+ name,
206
+ description: `Chat for ${name}`,
207
+ });
208
+ return {
209
+ teamId: String(team.groupId),
210
+ channelId: String(channel.groupId),
211
+ name,
212
+ };
213
+ }
214
+ /** Find a team + channel pair by its full name. */
215
+ async findPair(name) {
216
+ const [teams, channels] = await Promise.all([
217
+ this.requireTeams().list(this.appId),
218
+ this.requireChannels().list(this.appId),
219
+ ]);
220
+ const team = teams.find((t) => t.name === name);
221
+ if (!team)
222
+ return undefined;
223
+ const channel = channels.find((c) => c.name === name);
224
+ return {
225
+ teamId: String(team.groupId),
226
+ channelId: channel ? String(channel.groupId) : '',
227
+ name,
228
+ };
229
+ }
230
+ }