calabasas 0.1.6 → 0.1.7

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 (2) hide show
  1. package/dist/index.js +96 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2972,6 +2972,14 @@ function generateSchemaFile(sync) {
2972
2972
  syncedAt: v.number(),
2973
2973
  })
2974
2974
  .index("by_discord_id", ["discordId"]);`);
2975
+ tables.push(`export const calabasasGuildAdmins = defineTable({
2976
+ guildDiscordId: v.string(),
2977
+ discordUserId: v.string(),
2978
+ syncedAt: v.number(),
2979
+ })
2980
+ .index("by_user", ["discordUserId"])
2981
+ .index("by_guild", ["guildDiscordId"])
2982
+ .index("by_user_guild", ["discordUserId", "guildDiscordId"]);`);
2975
2983
  }
2976
2984
  if (sync.channels) {
2977
2985
  tables.push(`export const calabasasChannels = defineTable({
@@ -3020,8 +3028,10 @@ function generateSchemaFile(sync) {
3020
3028
  .index("by_guild", ["guildDiscordId"]);`);
3021
3029
  }
3022
3030
  const tableNames = [];
3023
- if (sync.guilds)
3031
+ if (sync.guilds) {
3024
3032
  tableNames.push("calabasasGuilds");
3033
+ tableNames.push("calabasasGuildAdmins");
3034
+ }
3025
3035
  if (sync.channels)
3026
3036
  tableNames.push("calabasasChannels");
3027
3037
  if (sync.roles)
@@ -3139,6 +3149,14 @@ var MEMBER_TYPE = `{
3139
3149
  roles?: string[];
3140
3150
  joinedAt?: string;
3141
3151
  }`;
3152
+ var GUILD_ADMIN_VALIDATOR = `v.object({
3153
+ guildId: v.string(),
3154
+ adminUserIds: v.array(v.string()),
3155
+ })`;
3156
+ var GUILD_ADMIN_TYPE = `{
3157
+ guildId: string;
3158
+ adminUserIds: string[];
3159
+ }`;
3142
3160
  function generateGuildMutation() {
3143
3161
  return `export const syncGuild = internalMutation({
3144
3162
  args: {
@@ -3318,6 +3336,59 @@ function generateMemberMutation() {
3318
3336
  },
3319
3337
  });`;
3320
3338
  }
3339
+ function generateGuildAdminMutation() {
3340
+ return `export const _syncGuildAdmins = internalMutation({
3341
+ args: {
3342
+ data: guildAdminValidator,
3343
+ },
3344
+ returns: v.null(),
3345
+ handler: async (ctx, { data }) => {
3346
+ // Delete all existing admin entries for this guild
3347
+ const existing = await ctx.db
3348
+ .query("calabasasGuildAdmins")
3349
+ .withIndex("by_guild", (q) => q.eq("guildDiscordId", data.guildId))
3350
+ .collect();
3351
+
3352
+ await Promise.all(existing.map((entry) => ctx.db.delete(entry._id)));
3353
+
3354
+ // Insert new admin entries
3355
+ const now = Date.now();
3356
+ await Promise.all(
3357
+ data.adminUserIds.map((userId) =>
3358
+ ctx.db.insert("calabasasGuildAdmins", {
3359
+ guildDiscordId: data.guildId,
3360
+ discordUserId: userId,
3361
+ syncedAt: now,
3362
+ })
3363
+ )
3364
+ );
3365
+
3366
+ return null;
3367
+ },
3368
+ });`;
3369
+ }
3370
+ function generateGuildAdminPublicMutation() {
3371
+ return `
3372
+ /**
3373
+ * Public mutation called by Calabasas gateway.
3374
+ * Validates secret and delegates to internal mutation.
3375
+ */
3376
+ export const syncGuildAdmins = mutation({
3377
+ args: {
3378
+ secret: v.string(),
3379
+ data: guildAdminValidator,
3380
+ },
3381
+ returns: v.null(),
3382
+ handler: async (ctx, { secret, data }) => {
3383
+ const expectedSecret = process.env.CALABASAS_SECRET;
3384
+ if (!expectedSecret || secret !== expectedSecret) {
3385
+ throw new Error("Invalid secret");
3386
+ }
3387
+ await ctx.runMutation(internal.calabasas.sync._syncGuildAdmins, { data });
3388
+ return null;
3389
+ },
3390
+ });`;
3391
+ }
3321
3392
  function generatePublicSyncMutation(type) {
3322
3393
  const capitalType = type.charAt(0).toUpperCase() + type.slice(1);
3323
3394
  return `
@@ -3353,9 +3424,13 @@ function generateSyncFile(sync) {
3353
3424
  const enabledTypes = [];
3354
3425
  if (sync.guilds) {
3355
3426
  validators.push(`const guildValidator = ${GUILD_VALIDATOR};`);
3427
+ validators.push(`const guildAdminValidator = ${GUILD_ADMIN_VALIDATOR};`);
3356
3428
  types.push(`export type GuildSync = ${GUILD_TYPE};`);
3429
+ types.push(`export type GuildAdminSync = ${GUILD_ADMIN_TYPE};`);
3357
3430
  internalMutations.push(generateInternalSyncMutation("guild", generateGuildMutation()));
3431
+ internalMutations.push(generateGuildAdminMutation());
3358
3432
  publicMutations.push(generatePublicSyncMutation("guild"));
3433
+ publicMutations.push(generateGuildAdminPublicMutation());
3359
3434
  enabledTypes.push("guild");
3360
3435
  }
3361
3436
  if (sync.channels) {
@@ -3870,7 +3945,18 @@ async function generate(options) {
3870
3945
  console.log(" });");
3871
3946
  console.log("");
3872
3947
  console.log("2. Re-export sync mutations in convex/discord.ts:");
3873
- console.log(' export { syncGuild, syncChannel, syncRole, syncMember } from "./calabasas/sync";');
3948
+ const syncExports = [];
3949
+ if (syncConfig.guilds) {
3950
+ syncExports.push("syncGuild");
3951
+ syncExports.push("syncGuildAdmins");
3952
+ }
3953
+ if (syncConfig.channels)
3954
+ syncExports.push("syncChannel");
3955
+ if (syncConfig.roles)
3956
+ syncExports.push("syncRole");
3957
+ if (syncConfig.members)
3958
+ syncExports.push("syncMember");
3959
+ console.log(` export { ${syncExports.join(", ")} } from "./calabasas/sync";`);
3874
3960
  console.log("");
3875
3961
  console.log("3. Add CALABASAS_SECRET to your Convex environment variables");
3876
3962
  console.log("4. Create your event handler in convex/discord.ts");
@@ -3932,6 +4018,13 @@ When sync is enabled, Discord data is automatically mirrored to Convex:
3932
4018
  | Channels | \`calabasasChannels\` | Create, update, delete |
3933
4019
  | Roles | \`calabasasRoles\` | Create, update, delete |
3934
4020
  | Members | \`calabasasMembers\` | Join, update, leave |
4021
+ | Guild Admins | \`calabasasGuildAdmins\` | Guild create (reconnect) |
4022
+
4023
+ #### Guild Admins Table
4024
+
4025
+ \`calabasasGuildAdmins\` is a junction table that maps guild admins (users with ADMINISTRATOR permission or guild owners). Unlike other sync tables that store entity data, this is a many-to-many relationship table with fields: \`guildDiscordId\`, \`discordUserId\`, \`syncedAt\`.
4026
+
4027
+ Indexed by \`by_user\`, \`by_guild\`, and \`by_user_guild\` — use \`by_user\` to find all guilds where a user is admin (e.g., for "mutual servers" queries). Automatically populated when \`sync.guilds\` is enabled; recomputed on every bot connect/reconnect.
3935
4028
 
3936
4029
  ### ⚠️ Schema Integration (Required)
3937
4030
 
@@ -3952,7 +4045,7 @@ Then re-export the sync mutations so the gateway can call them:
3952
4045
 
3953
4046
  \`\`\`typescript
3954
4047
  // convex/discord.ts
3955
- export { syncGuild, syncChannel, syncRole, syncMember } from "./calabasas/sync";
4048
+ export { syncGuild, syncGuildAdmins, syncChannel, syncRole, syncMember } from "./calabasas/sync";
3956
4049
  \`\`\`
3957
4050
 
3958
4051
  Without this step, Convex will reject sync operations because the tables are not defined in the schema.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "calabasas",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "CLI for Calabasas - Discord Gateway as a Service for Convex",
5
5
  "type": "module",
6
6
  "bin": {