@headless-lms/adapter-db 0.3.0 → 0.5.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 (123) hide show
  1. package/dist/client.d.ts +8 -2
  2. package/dist/client.js +10 -2
  3. package/dist/client.js.map +1 -1
  4. package/dist/index.d.ts +10 -4
  5. package/dist/index.js +9 -3
  6. package/dist/repositories/admin.d.ts +12 -4
  7. package/dist/repositories/admin.js +165 -23
  8. package/dist/repositories/admin.js.map +1 -1
  9. package/dist/repositories/assets.d.ts +2 -2
  10. package/dist/repositories/assets.js +11 -5
  11. package/dist/repositories/assets.js.map +1 -1
  12. package/dist/repositories/automations.d.ts +0 -1
  13. package/dist/repositories/automations.js +2 -18
  14. package/dist/repositories/automations.js.map +1 -1
  15. package/dist/repositories/chats.d.ts +14 -5
  16. package/dist/repositories/chats.js +194 -85
  17. package/dist/repositories/chats.js.map +1 -1
  18. package/dist/repositories/content.d.ts +24 -7
  19. package/dist/repositories/content.js +229 -48
  20. package/dist/repositories/content.js.map +1 -1
  21. package/dist/repositories/credentials.js +1 -1
  22. package/dist/repositories/devices.d.ts +17 -0
  23. package/dist/repositories/devices.js +75 -0
  24. package/dist/repositories/devices.js.map +1 -0
  25. package/dist/repositories/digest-runs.d.ts +14 -0
  26. package/dist/repositories/digest-runs.js +39 -0
  27. package/dist/repositories/digest-runs.js.map +1 -0
  28. package/dist/repositories/discussion.d.ts +7 -2
  29. package/dist/repositories/discussion.js +18 -7
  30. package/dist/repositories/discussion.js.map +1 -1
  31. package/dist/repositories/entitlements.d.ts +11 -9
  32. package/dist/repositories/entitlements.js +56 -59
  33. package/dist/repositories/entitlements.js.map +1 -1
  34. package/dist/repositories/event-reports.d.ts +16 -0
  35. package/dist/repositories/event-reports.js +199 -0
  36. package/dist/repositories/event-reports.js.map +1 -0
  37. package/dist/repositories/grant-covers.d.ts +4 -3
  38. package/dist/repositories/grant-covers.js +7 -4
  39. package/dist/repositories/grant-covers.js.map +1 -1
  40. package/dist/repositories/identity.js +1 -1
  41. package/dist/repositories/inserted-row.d.ts +5 -0
  42. package/dist/repositories/inserted-row.js +7 -0
  43. package/dist/repositories/inserted-row.js.map +1 -0
  44. package/dist/repositories/integrations.js +1 -1
  45. package/dist/repositories/member.d.ts +12 -4
  46. package/dist/repositories/member.js +19 -7
  47. package/dist/repositories/member.js.map +1 -1
  48. package/dist/repositories/members.d.ts +2 -5
  49. package/dist/repositories/members.js +39 -94
  50. package/dist/repositories/members.js.map +1 -1
  51. package/dist/repositories/mentions.d.ts +17 -0
  52. package/dist/repositories/mentions.js +55 -0
  53. package/dist/repositories/mentions.js.map +1 -0
  54. package/dist/repositories/notification-settings.d.ts +19 -0
  55. package/dist/repositories/notification-settings.js +67 -0
  56. package/dist/repositories/notification-settings.js.map +1 -0
  57. package/dist/repositories/notifications.d.ts +23 -16
  58. package/dist/repositories/notifications.js +152 -44
  59. package/dist/repositories/notifications.js.map +1 -1
  60. package/dist/repositories/org-user-profile.d.ts +0 -17
  61. package/dist/repositories/org-user-profile.js +0 -1
  62. package/dist/repositories/org-user-profile.js.map +1 -1
  63. package/dist/repositories/organizations.d.ts +4 -4
  64. package/dist/repositories/organizations.js +25 -47
  65. package/dist/repositories/organizations.js.map +1 -1
  66. package/dist/repositories/outbox.d.ts +5 -1
  67. package/dist/repositories/outbox.js +12 -3
  68. package/dist/repositories/outbox.js.map +1 -1
  69. package/dist/repositories/posts.d.ts +4 -8
  70. package/dist/repositories/posts.js +26 -29
  71. package/dist/repositories/posts.js.map +1 -1
  72. package/dist/repositories/progress.js +1 -1
  73. package/dist/repositories/roles.d.ts +35 -0
  74. package/dist/repositories/roles.js +244 -0
  75. package/dist/repositories/roles.js.map +1 -0
  76. package/dist/repositories/settings.js +1 -1
  77. package/dist/repositories/subscriptions.d.ts +18 -0
  78. package/dist/repositories/subscriptions.js +72 -0
  79. package/dist/repositories/subscriptions.js.map +1 -0
  80. package/dist/schema/assets.d.ts +17 -0
  81. package/dist/schema/assets.js +1 -0
  82. package/dist/schema/assets.js.map +1 -1
  83. package/dist/schema/automations.d.ts +524 -19
  84. package/dist/schema/automations.js +73 -7
  85. package/dist/schema/automations.js.map +1 -1
  86. package/dist/schema/content.d.ts +437 -3
  87. package/dist/schema/content.js +70 -3
  88. package/dist/schema/content.js.map +1 -1
  89. package/dist/schema/discussion.d.ts +295 -128
  90. package/dist/schema/discussion.js +63 -26
  91. package/dist/schema/discussion.js.map +1 -1
  92. package/dist/schema/identity.d.ts +109 -109
  93. package/dist/schema/identity.js +28 -70
  94. package/dist/schema/identity.js.map +1 -1
  95. package/dist/schema/index.d.ts +7 -6
  96. package/dist/schema/index.js +16 -7
  97. package/dist/schema/index.js.map +1 -1
  98. package/dist/schema/organizations.d.ts +5 -22
  99. package/dist/schema/organizations.js +1 -13
  100. package/dist/schema/organizations.js.map +1 -1
  101. package/dist/schema/roles.d.ts +369 -0
  102. package/dist/schema/roles.js +71 -0
  103. package/dist/schema/roles.js.map +1 -0
  104. package/drizzle/0011_digest_runs.sql +8 -0
  105. package/drizzle/0012_subscriptions.sql +16 -0
  106. package/drizzle/0013_mentions.sql +10 -0
  107. package/drizzle/0014_notification_settings_devices.sql +36 -0
  108. package/drizzle/0015_automations_drop_source.sql +2 -0
  109. package/drizzle/0016_notifications_automations.sql +48 -0
  110. package/drizzle/0017_configurable_roles.sql +38 -0
  111. package/drizzle/0018_messages_client_id.sql +2 -0
  112. package/drizzle/0019_events.sql +44 -0
  113. package/drizzle/meta/0011_snapshot.json +8868 -0
  114. package/drizzle/meta/0012_snapshot.json +8873 -0
  115. package/drizzle/meta/0013_snapshot.json +8943 -0
  116. package/drizzle/meta/0014_snapshot.json +9305 -0
  117. package/drizzle/meta/0015_snapshot.json +9270 -0
  118. package/drizzle/meta/0016_snapshot.json +9230 -0
  119. package/drizzle/meta/0017_snapshot.json +9518 -0
  120. package/drizzle/meta/0018_snapshot.json +9557 -0
  121. package/drizzle/meta/0019_snapshot.json +9919 -0
  122. package/drizzle/meta/_journal.json +63 -0
  123. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { DbExecutor } from "../client.js";
2
+ import { Asset, AssetsQuery, ContentType, Logger, Page } from "@headless-lms/core/types";
2
3
  import { AssetsRepository } from "@headless-lms/core/assets";
3
- import { Asset, AssetsQuery, Logger, Page } from "@headless-lms/core/types";
4
4
  //#region src/repositories/assets.d.ts
5
5
  declare class DrizzleAssetsRepository implements AssetsRepository {
6
6
  private readonly db;
@@ -12,9 +12,9 @@ declare class DrizzleAssetsRepository implements AssetsRepository {
12
12
  findByIds(orgId: string, ids: string[]): Promise<Asset[]>;
13
13
  markReady(orgId: string, id: string, patch: Pick<Asset, "key" | "kind" | "size" | "contentType">): Promise<Asset | null>;
14
14
  delete(orgId: string, id: string): Promise<boolean>;
15
- hasUnreferencedMemberUploads(orgId: string, before: Date): Promise<boolean>;
16
15
  listUnreferencedMemberUploads(orgId: string, before: Date): Promise<Asset[]>;
17
16
  heldKeys(orgId: string, keys: string[]): Promise<string[]>;
17
+ contentTypeOf(orgId: string, contentId: string): Promise<ContentType | null>;
18
18
  }
19
19
  //#endregion
20
20
  export { DrizzleAssetsRepository };
@@ -1,8 +1,9 @@
1
1
  import { assets } from "../schema/assets.js";
2
+ import { contentItems } from "../schema/content.js";
2
3
  import { pgErrorFields, translateDbErrors } from "./pg-errors.js";
4
+ import { noopLogger } from "@headless-lms/core/shared/logger";
3
5
  import { and, asc, desc, eq, ilike, inArray, lt, sql } from "drizzle-orm";
4
6
  import { ConflictError } from "@headless-lms/core/shared/errors";
5
- import { noopLogger } from "@headless-lms/core/shared/logger";
6
7
  //#region src/repositories/assets.ts
7
8
  function toAsset(row) {
8
9
  return {
@@ -16,6 +17,7 @@ function toAsset(row) {
16
17
  size: row.size,
17
18
  status: row.status,
18
19
  uploadedBy: row.uploadedBy,
20
+ contentId: row.contentId,
19
21
  createdAt: row.createdAt,
20
22
  updatedAt: row.updatedAt
21
23
  };
@@ -35,6 +37,8 @@ function referenced(orgId, assetId) {
35
37
  or exists (select 1 from courses c where c.org_id = ${orgId} and c.thumbnail_asset_id = ${assetId})
36
38
  or exists (select 1 from downloads d where d.org_id = ${orgId} and d.thumbnail_asset_id = ${assetId})
37
39
  or exists (select 1 from communities c where c.org_id = ${orgId} and c.thumbnail_asset_id = ${assetId})
40
+ or exists (select 1 from event_assets ea where ea.org_id = ${orgId} and ea.asset_id = ${assetId})
41
+ or exists (select 1 from events e where e.org_id = ${orgId} and e.thumbnail_asset_id = ${assetId})
38
42
  )`;
39
43
  }
40
44
  function unreferencedMemberUploads(orgId, before) {
@@ -60,6 +64,7 @@ var DrizzleAssetsRepository = class {
60
64
  size: asset.size,
61
65
  status: asset.status,
62
66
  uploadedBy: asset.uploadedBy,
67
+ contentId: asset.contentId,
63
68
  createdAt: new Date(asset.createdAt)
64
69
  }).returning();
65
70
  if (!row) throw new Error("failed to insert asset");
@@ -69,6 +74,7 @@ var DrizzleAssetsRepository = class {
69
74
  const filters = [eq(assets.orgId, orgId)];
70
75
  if (query.kind) filters.push(eq(assets.kind, query.kind));
71
76
  if (query.source) filters.push(eq(assets.source, query.source));
77
+ if (query.contentId) filters.push(eq(assets.contentId, query.contentId));
72
78
  const q = query.search?.trim();
73
79
  if (q) filters.push(ilike(assets.filename, `%${q}%`));
74
80
  const where = and(...filters);
@@ -115,10 +121,6 @@ var DrizzleAssetsRepository = class {
115
121
  throw err;
116
122
  }
117
123
  }
118
- async hasUnreferencedMemberUploads(orgId, before) {
119
- const [row] = await this.db.select({ id: assets.id }).from(assets).where(unreferencedMemberUploads(orgId, before)).limit(1);
120
- return row !== void 0;
121
- }
122
124
  async listUnreferencedMemberUploads(orgId, before) {
123
125
  return (await this.db.select().from(assets).where(unreferencedMemberUploads(orgId, before)).orderBy(asc(assets.createdAt))).map(toAsset);
124
126
  }
@@ -126,6 +128,10 @@ var DrizzleAssetsRepository = class {
126
128
  if (keys.length === 0) return [];
127
129
  return (await this.db.select({ key: assets.key }).from(assets).where(and(eq(assets.orgId, orgId), inArray(assets.key, keys)))).map((r) => r.key);
128
130
  }
131
+ async contentTypeOf(orgId, contentId) {
132
+ const [row] = await this.db.select({ type: contentItems.type }).from(contentItems).where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, contentId))).limit(1);
133
+ return row?.type ?? null;
134
+ }
129
135
  };
130
136
  translateDbErrors(DrizzleAssetsRepository);
131
137
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"assets.js","names":[],"sources":["../../src/repositories/assets.ts"],"sourcesContent":["import { and, eq, ilike, inArray, lt, sql, asc, desc, type SQL } from \"drizzle-orm\";\nimport type { AssetsRepository } from \"@headless-lms/core/assets\";\nimport type {\n Asset,\n AssetKind,\n AssetSource,\n AssetStatus,\n AssetsQuery,\n Logger,\n Page,\n} from \"@headless-lms/core/types\";\nimport { assets } from \"../schema/assets.js\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport type { DbExecutor } from \"../client.js\";\nimport { ConflictError } from \"@headless-lms/core/shared/errors\";\nimport { pgErrorFields, translateDbErrors } from \"./pg-errors.js\";\n\ntype Row = typeof assets.$inferSelect;\n\nfunction toAsset(row: Row): Asset {\n return {\n orgId: row.orgId,\n id: row.id,\n key: row.key,\n kind: row.kind as AssetKind,\n source: row.source as AssetSource,\n filename: row.filename,\n contentType: row.contentType,\n size: row.size,\n status: row.status as AssetStatus,\n uploadedBy: row.uploadedBy,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nconst SORT_COLUMNS = {\n filename: assets.filename,\n size: assets.size,\n createdAt: assets.createdAt,\n} as const;\n\nfunction referenced(orgId: SQL, assetId: SQL): SQL {\n return sql`(\n exists (select 1 from user_profiles p where p.image_asset_org_id = ${orgId} and p.image_asset_id = ${assetId})\n or exists (select 1 from posts p where p.org_id = ${orgId} and p.cover_asset_id = ${assetId})\n or exists (select 1 from message_attachments ma where ma.org_id = ${orgId} and ma.asset_id = ${assetId})\n or exists (select 1 from activity_assets aa where aa.org_id = ${orgId} and aa.asset_id = ${assetId})\n or exists (select 1 from download_assets da where da.org_id = ${orgId} and da.asset_id = ${assetId})\n or exists (select 1 from courses c where c.org_id = ${orgId} and c.thumbnail_asset_id = ${assetId})\n or exists (select 1 from downloads d where d.org_id = ${orgId} and d.thumbnail_asset_id = ${assetId})\n or exists (select 1 from communities c where c.org_id = ${orgId} and c.thumbnail_asset_id = ${assetId})\n )`;\n}\n\nfunction unreferencedMemberUploads(orgId: string, before: Date): SQL | undefined {\n return and(\n eq(assets.orgId, orgId),\n eq(assets.source, \"member\"),\n lt(assets.createdAt, before),\n sql`not ${referenced(sql`${assets.orgId}`, sql`${assets.id}`)}`,\n );\n}\n\nexport class DrizzleAssetsRepository implements AssetsRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async insert(orgId: string, asset: Asset): Promise<Asset> {\n if (asset.orgId !== orgId) {\n throw new Error(\"asset org mismatch\");\n }\n const [row] = await this.db\n .insert(assets)\n .values({\n id: asset.id,\n orgId: asset.orgId,\n key: asset.key,\n kind: asset.kind,\n source: asset.source,\n filename: asset.filename,\n contentType: asset.contentType,\n size: asset.size,\n status: asset.status,\n uploadedBy: asset.uploadedBy,\n createdAt: new Date(asset.createdAt),\n })\n .returning();\n if (!row) {\n throw new Error(\"failed to insert asset\");\n }\n return toAsset(row);\n }\n\n async list(orgId: string, query: AssetsQuery): Promise<Page<Asset>> {\n const filters: (SQL | undefined)[] = [eq(assets.orgId, orgId)];\n if (query.kind) {\n filters.push(eq(assets.kind, query.kind));\n }\n if (query.source) {\n filters.push(eq(assets.source, query.source));\n }\n const q = query.search?.trim();\n if (q) {\n filters.push(ilike(assets.filename, `%${q}%`));\n }\n const where = and(...filters);\n\n const orderBy = (() => {\n if (!query.sort) {\n return desc(assets.createdAt);\n }\n const descending = query.sort.startsWith(\"-\");\n const field = descending ? query.sort.slice(1) : query.sort;\n const col = SORT_COLUMNS[field as keyof typeof SORT_COLUMNS] ?? assets.createdAt;\n return descending ? desc(col) : asc(col);\n })();\n\n const offset = (query.page - 1) * query.pageSize;\n const rows = await this.db\n .select()\n .from(assets)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset(offset);\n\n const counted = await this.db\n .select({ value: sql<number>`count(*)::int` })\n .from(assets)\n .where(where);\n\n return {\n rows: rows.map(toAsset),\n total: counted[0]?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findById(orgId: string, id: string): Promise<Asset | null> {\n const [row] = await this.db\n .select()\n .from(assets)\n .where(and(eq(assets.orgId, orgId), eq(assets.id, id)))\n .limit(1);\n return row ? toAsset(row) : null;\n }\n\n async findByIds(orgId: string, ids: string[]): Promise<Asset[]> {\n if (ids.length === 0) {\n return [];\n }\n const rows = await this.db\n .select()\n .from(assets)\n .where(and(eq(assets.orgId, orgId), inArray(assets.id, ids)));\n return rows.map(toAsset);\n }\n\n async markReady(\n orgId: string,\n id: string,\n patch: Pick<Asset, \"key\" | \"kind\" | \"size\" | \"contentType\">,\n ): Promise<Asset | null> {\n const [row] = await this.db\n .update(assets)\n .set({\n key: patch.key,\n kind: patch.kind,\n size: patch.size,\n contentType: patch.contentType,\n status: \"ready\",\n })\n .where(and(eq(assets.orgId, orgId), eq(assets.id, id), eq(assets.status, \"pending\")))\n .returning();\n return row ? toAsset(row) : null;\n }\n\n async delete(orgId: string, id: string): Promise<boolean> {\n try {\n const deleted = await this.db\n .delete(assets)\n .where(and(eq(assets.orgId, orgId), eq(assets.id, id)))\n .returning({ id: assets.id });\n return deleted.length > 0;\n } catch (err) {\n if (pgErrorFields(err)?.code === \"23503\") {\n throw Object.assign(new ConflictError(\"This file is still in use\"), { cause: err });\n }\n throw err;\n }\n }\n\n async hasUnreferencedMemberUploads(orgId: string, before: Date): Promise<boolean> {\n const [row] = await this.db\n .select({ id: assets.id })\n .from(assets)\n .where(unreferencedMemberUploads(orgId, before))\n .limit(1);\n return row !== undefined;\n }\n\n async listUnreferencedMemberUploads(orgId: string, before: Date): Promise<Asset[]> {\n const rows = await this.db\n .select()\n .from(assets)\n .where(unreferencedMemberUploads(orgId, before))\n .orderBy(asc(assets.createdAt));\n return rows.map(toAsset);\n }\n\n async heldKeys(orgId: string, keys: string[]): Promise<string[]> {\n if (keys.length === 0) {\n return [];\n }\n const rows = await this.db\n .select({ key: assets.key })\n .from(assets)\n .where(and(eq(assets.orgId, orgId), inArray(assets.key, keys)));\n return rows.map((r) => r.key);\n }\n}\ntranslateDbErrors(DrizzleAssetsRepository);\n"],"mappings":"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"}
1
+ {"version":3,"file":"assets.js","names":[],"sources":["../../src/repositories/assets.ts"],"sourcesContent":["import { and, eq, ilike, inArray, lt, sql, asc, desc, type SQL } from \"drizzle-orm\";\nimport type { AssetsRepository } from \"@headless-lms/core/assets\";\nimport type {\n Asset,\n AssetKind,\n AssetSource,\n AssetStatus,\n AssetsQuery,\n ContentType,\n Logger,\n Page,\n} from \"@headless-lms/core/types\";\nimport { assets } from \"../schema/assets.js\";\nimport { contentItems } from \"../schema/content.js\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport type { DbExecutor } from \"../client.js\";\nimport { ConflictError } from \"@headless-lms/core/shared/errors\";\nimport { pgErrorFields, translateDbErrors } from \"./pg-errors.js\";\n\ntype Row = typeof assets.$inferSelect;\n\nfunction toAsset(row: Row): Asset {\n return {\n orgId: row.orgId,\n id: row.id,\n key: row.key,\n kind: row.kind as AssetKind,\n source: row.source as AssetSource,\n filename: row.filename,\n contentType: row.contentType,\n size: row.size,\n status: row.status as AssetStatus,\n uploadedBy: row.uploadedBy,\n contentId: row.contentId,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nconst SORT_COLUMNS = {\n filename: assets.filename,\n size: assets.size,\n createdAt: assets.createdAt,\n} as const;\n\nfunction referenced(orgId: SQL, assetId: SQL): SQL {\n return sql`(\n exists (select 1 from user_profiles p where p.image_asset_org_id = ${orgId} and p.image_asset_id = ${assetId})\n or exists (select 1 from posts p where p.org_id = ${orgId} and p.cover_asset_id = ${assetId})\n or exists (select 1 from message_attachments ma where ma.org_id = ${orgId} and ma.asset_id = ${assetId})\n or exists (select 1 from activity_assets aa where aa.org_id = ${orgId} and aa.asset_id = ${assetId})\n or exists (select 1 from download_assets da where da.org_id = ${orgId} and da.asset_id = ${assetId})\n or exists (select 1 from courses c where c.org_id = ${orgId} and c.thumbnail_asset_id = ${assetId})\n or exists (select 1 from downloads d where d.org_id = ${orgId} and d.thumbnail_asset_id = ${assetId})\n or exists (select 1 from communities c where c.org_id = ${orgId} and c.thumbnail_asset_id = ${assetId})\n or exists (select 1 from event_assets ea where ea.org_id = ${orgId} and ea.asset_id = ${assetId})\n or exists (select 1 from events e where e.org_id = ${orgId} and e.thumbnail_asset_id = ${assetId})\n )`;\n}\n\nfunction unreferencedMemberUploads(orgId: string, before: Date): SQL | undefined {\n return and(\n eq(assets.orgId, orgId),\n eq(assets.source, \"member\"),\n lt(assets.createdAt, before),\n sql`not ${referenced(sql`${assets.orgId}`, sql`${assets.id}`)}`,\n );\n}\n\nexport class DrizzleAssetsRepository implements AssetsRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async insert(orgId: string, asset: Asset): Promise<Asset> {\n if (asset.orgId !== orgId) {\n throw new Error(\"asset org mismatch\");\n }\n const [row] = await this.db\n .insert(assets)\n .values({\n id: asset.id,\n orgId: asset.orgId,\n key: asset.key,\n kind: asset.kind,\n source: asset.source,\n filename: asset.filename,\n contentType: asset.contentType,\n size: asset.size,\n status: asset.status,\n uploadedBy: asset.uploadedBy,\n contentId: asset.contentId,\n createdAt: new Date(asset.createdAt),\n })\n .returning();\n if (!row) {\n throw new Error(\"failed to insert asset\");\n }\n return toAsset(row);\n }\n\n async list(orgId: string, query: AssetsQuery): Promise<Page<Asset>> {\n const filters: (SQL | undefined)[] = [eq(assets.orgId, orgId)];\n if (query.kind) {\n filters.push(eq(assets.kind, query.kind));\n }\n if (query.source) {\n filters.push(eq(assets.source, query.source));\n }\n if (query.contentId) {\n filters.push(eq(assets.contentId, query.contentId));\n }\n const q = query.search?.trim();\n if (q) {\n filters.push(ilike(assets.filename, `%${q}%`));\n }\n const where = and(...filters);\n\n const orderBy = (() => {\n if (!query.sort) {\n return desc(assets.createdAt);\n }\n const descending = query.sort.startsWith(\"-\");\n const field = descending ? query.sort.slice(1) : query.sort;\n const col = SORT_COLUMNS[field as keyof typeof SORT_COLUMNS] ?? assets.createdAt;\n return descending ? desc(col) : asc(col);\n })();\n\n const offset = (query.page - 1) * query.pageSize;\n const rows = await this.db\n .select()\n .from(assets)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset(offset);\n\n const counted = await this.db\n .select({ value: sql<number>`count(*)::int` })\n .from(assets)\n .where(where);\n\n return {\n rows: rows.map(toAsset),\n total: counted[0]?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findById(orgId: string, id: string): Promise<Asset | null> {\n const [row] = await this.db\n .select()\n .from(assets)\n .where(and(eq(assets.orgId, orgId), eq(assets.id, id)))\n .limit(1);\n return row ? toAsset(row) : null;\n }\n\n async findByIds(orgId: string, ids: string[]): Promise<Asset[]> {\n if (ids.length === 0) {\n return [];\n }\n const rows = await this.db\n .select()\n .from(assets)\n .where(and(eq(assets.orgId, orgId), inArray(assets.id, ids)));\n return rows.map(toAsset);\n }\n\n async markReady(\n orgId: string,\n id: string,\n patch: Pick<Asset, \"key\" | \"kind\" | \"size\" | \"contentType\">,\n ): Promise<Asset | null> {\n const [row] = await this.db\n .update(assets)\n .set({\n key: patch.key,\n kind: patch.kind,\n size: patch.size,\n contentType: patch.contentType,\n status: \"ready\",\n })\n .where(and(eq(assets.orgId, orgId), eq(assets.id, id), eq(assets.status, \"pending\")))\n .returning();\n return row ? toAsset(row) : null;\n }\n\n async delete(orgId: string, id: string): Promise<boolean> {\n try {\n const deleted = await this.db\n .delete(assets)\n .where(and(eq(assets.orgId, orgId), eq(assets.id, id)))\n .returning({ id: assets.id });\n return deleted.length > 0;\n } catch (err) {\n if (pgErrorFields(err)?.code === \"23503\") {\n throw Object.assign(new ConflictError(\"This file is still in use\"), { cause: err });\n }\n throw err;\n }\n }\n\n async listUnreferencedMemberUploads(orgId: string, before: Date): Promise<Asset[]> {\n const rows = await this.db\n .select()\n .from(assets)\n .where(unreferencedMemberUploads(orgId, before))\n .orderBy(asc(assets.createdAt));\n return rows.map(toAsset);\n }\n\n async heldKeys(orgId: string, keys: string[]): Promise<string[]> {\n if (keys.length === 0) {\n return [];\n }\n const rows = await this.db\n .select({ key: assets.key })\n .from(assets)\n .where(and(eq(assets.orgId, orgId), inArray(assets.key, keys)));\n return rows.map((r) => r.key);\n }\n\n async contentTypeOf(orgId: string, contentId: string): Promise<ContentType | null> {\n const [row] = await this.db\n .select({ type: contentItems.type })\n .from(contentItems)\n .where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, contentId)))\n .limit(1);\n return row?.type ?? null;\n }\n}\ntranslateDbErrors(DrizzleAssetsRepository);\n"],"mappings":"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"}
@@ -8,7 +8,6 @@ declare class DrizzleAutomationsRepository implements AutomationsRepository {
8
8
  private readonly logger;
9
9
  constructor(db: DbExecutor, logger?: Logger);
10
10
  insert(orgId: string, input: NewAutomation): Promise<Automation>;
11
- insertSeed(orgId: string, input: NewAutomation): Promise<Automation | null>;
12
11
  update(orgId: string, id: string, patch: UpdateAutomationInput): Promise<Automation | null>;
13
12
  delete(orgId: string, id: string): Promise<Automation | null>;
14
13
  findById(orgId: string, id: string): Promise<Automation | null>;
@@ -1,8 +1,8 @@
1
1
  import { automationRuns, automations } from "../schema/automations.js";
2
2
  import "../schema/index.js";
3
3
  import { translateDbErrors } from "./pg-errors.js";
4
- import { and, arrayContains, asc, desc, eq, gte, ne, sql } from "drizzle-orm";
5
4
  import { noopLogger } from "@headless-lms/core/shared/logger";
5
+ import { and, arrayContains, asc, desc, eq, gte, ne, sql } from "drizzle-orm";
6
6
  //#region src/repositories/automations.ts
7
7
  const automationSelection = {
8
8
  orgId: automations.orgId,
@@ -14,7 +14,6 @@ const automationSelection = {
14
14
  actions: automations.actions,
15
15
  enabled: automations.enabled,
16
16
  filter: automations.filter,
17
- source: automations.source,
18
17
  createdAt: automations.createdAt,
19
18
  updatedAt: automations.updatedAt
20
19
  };
@@ -29,7 +28,6 @@ function toAutomation(row) {
29
28
  actions: row.actions,
30
29
  enabled: row.enabled,
31
30
  filter: row.filter,
32
- source: row.source,
33
31
  createdAt: row.createdAt,
34
32
  updatedAt: row.updatedAt
35
33
  };
@@ -49,27 +47,13 @@ var DrizzleAutomationsRepository = class {
49
47
  description: input.description,
50
48
  triggers: input.triggers,
51
49
  actions: input.actions,
52
- filter: input.filter ?? null,
53
- source: input.source ?? "user"
50
+ filter: input.filter ?? null
54
51
  }).returning({ id: automations.id });
55
52
  if (!inserted) throw new Error("failed to insert automation");
56
53
  const created = await this.findById(orgId, inserted.id);
57
54
  if (!created) throw new Error("failed to load created automation");
58
55
  return created;
59
56
  }
60
- async insertSeed(orgId, input) {
61
- const [inserted] = await this.db.insert(automations).values({
62
- orgId,
63
- name: input.name,
64
- kind: input.kind,
65
- description: input.description,
66
- triggers: input.triggers,
67
- actions: input.actions,
68
- filter: input.filter ?? null,
69
- source: "system"
70
- }).onConflictDoNothing().returning({ id: automations.id });
71
- return inserted ? this.findById(orgId, inserted.id) : null;
72
- }
73
57
  async update(orgId, id, patch) {
74
58
  const set = { updatedAt: /* @__PURE__ */ new Date() };
75
59
  if (patch.name !== void 0) set.name = patch.name;
@@ -1 +1 @@
1
- {"version":3,"file":"automations.js","names":[],"sources":["../../src/repositories/automations.ts"],"sourcesContent":["// automations — Drizzle repositories (implement the core outbound ports).\n// Org-scoped: every method takes the domain `organizations.id` and constrains\n// its queries to that tenant. `Automation`/`AutomationRun` carry no `orgId`\n// column of their own in the domain type (mirrors the `Entitlement` shape) —\n// `AutomationRun` is the one exception, since it's the run's own `orgId`\n// field the service reads back off `handle`'s event.\nimport { and, arrayContains, asc, desc, eq, gte, ne, sql, type SQL } from \"drizzle-orm\";\nimport type { DbExecutor } from \"../client.js\";\nimport type {\n Automation,\n AutomationKind,\n AutomationRun,\n AutomationRunsQuery,\n AutomationRunsRepository,\n AutomationsRepository,\n NewAutomation,\n NewAutomationRun,\n Page,\n UpdateAutomationInput,\n} from \"@headless-lms/core/automations\";\nimport type { ScheduledAutomation } from \"@headless-lms/core/types\";\nimport { automations, automationRuns } from \"../schema/index.js\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport { translateDbErrors } from \"./pg-errors.js\";\n\nconst automationSelection = {\n orgId: automations.orgId,\n id: automations.id,\n name: automations.name,\n kind: automations.kind,\n description: automations.description,\n triggers: automations.triggers,\n actions: automations.actions,\n enabled: automations.enabled,\n filter: automations.filter,\n source: automations.source,\n createdAt: automations.createdAt,\n updatedAt: automations.updatedAt,\n};\n\ninterface AutomationRow {\n orgId: string;\n id: string;\n name: string;\n kind: AutomationKind;\n description: string | null;\n triggers: string[];\n actions: Automation[\"actions\"];\n enabled: boolean;\n filter: Automation[\"filter\"];\n source: Automation[\"source\"];\n createdAt: Date;\n updatedAt: Date;\n}\n\nfunction toAutomation(row: AutomationRow): Automation {\n return {\n orgId: row.orgId,\n id: row.id,\n name: row.name,\n kind: row.kind,\n description: row.description,\n triggers: row.triggers,\n actions: row.actions,\n enabled: row.enabled,\n filter: row.filter,\n source: row.source,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nexport class DrizzleAutomationsRepository implements AutomationsRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async insert(orgId: string, input: NewAutomation): Promise<Automation> {\n const [inserted] = await this.db\n .insert(automations)\n .values({\n orgId,\n name: input.name,\n kind: input.kind,\n description: input.description,\n triggers: input.triggers,\n actions: input.actions,\n filter: input.filter ?? null,\n source: input.source ?? \"user\",\n })\n .returning({ id: automations.id });\n if (!inserted) {\n throw new Error(\"failed to insert automation\");\n }\n const created = await this.findById(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created automation\");\n }\n return created;\n }\n\n async insertSeed(orgId: string, input: NewAutomation): Promise<Automation | null> {\n const [inserted] = await this.db\n .insert(automations)\n .values({\n orgId,\n name: input.name,\n kind: input.kind,\n description: input.description,\n triggers: input.triggers,\n actions: input.actions,\n filter: input.filter ?? null,\n source: \"system\",\n })\n .onConflictDoNothing()\n .returning({ id: automations.id });\n return inserted ? this.findById(orgId, inserted.id) : null;\n }\n\n async update(\n orgId: string,\n id: string,\n patch: UpdateAutomationInput,\n ): Promise<Automation | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.name !== undefined) {\n set.name = patch.name;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.triggers !== undefined) {\n set.triggers = patch.triggers;\n }\n if (patch.actions !== undefined) {\n set.actions = patch.actions;\n }\n if (patch.enabled !== undefined) {\n set.enabled = patch.enabled;\n }\n if (patch.filter !== undefined) {\n set.filter = patch.filter;\n }\n\n const [updated] = await this.db\n .update(automations)\n .set(set)\n .where(and(eq(automations.orgId, orgId), eq(automations.id, id)))\n .returning({ id: automations.id });\n if (!updated) {\n return null;\n }\n return this.findById(orgId, id);\n }\n\n async delete(orgId: string, id: string): Promise<Automation | null> {\n const [deleted] = await this.db\n .delete(automations)\n .where(and(eq(automations.orgId, orgId), eq(automations.id, id)))\n .returning(automationSelection);\n return deleted ? toAutomation(deleted) : null;\n }\n\n async findById(orgId: string, id: string): Promise<Automation | null> {\n const [row] = await this.db\n .select(automationSelection)\n .from(automations)\n .where(and(eq(automations.orgId, orgId), eq(automations.id, id)))\n .limit(1);\n return row ? toAutomation(row) : null;\n }\n\n async listByOrg(orgId: string, kind?: AutomationKind): Promise<Automation[]> {\n const conditions: SQL[] = [eq(automations.orgId, orgId)];\n if (kind) {\n conditions.push(eq(automations.kind, kind));\n }\n const rows = await this.db\n .select(automationSelection)\n .from(automations)\n .where(and(...conditions))\n .orderBy(desc(automations.createdAt));\n return rows.map(toAutomation);\n }\n\n async listByTrigger(orgId: string, trigger: string): Promise<Automation[]> {\n const rows = await this.db\n .select(automationSelection)\n .from(automations)\n .where(and(eq(automations.orgId, orgId), arrayContains(automations.triggers, [trigger])))\n .orderBy(desc(automations.createdAt));\n return rows.map(toAutomation);\n }\n\n async listSchedules(): Promise<ScheduledAutomation[]> {\n const rows = await this.db\n .select({\n orgId: automations.orgId,\n id: automations.id,\n triggers: automations.triggers,\n })\n .from(automations)\n .where(\n and(\n eq(automations.enabled, true),\n sql`exists (select 1 from unnest(${automations.triggers}) as t(trigger) where t.trigger like 'schedule:%')`,\n ),\n );\n const schedules: ScheduledAutomation[] = [];\n for (const row of rows) {\n for (const trigger of row.triggers) {\n if (!trigger.startsWith(\"schedule:\")) {\n continue;\n }\n schedules.push({\n orgId: row.orgId,\n automationId: row.id,\n cron: trigger.slice(\"schedule:\".length),\n });\n }\n }\n return schedules;\n }\n}\n\nconst runSelection = {\n id: automationRuns.id,\n orgId: automationRuns.orgId,\n automationId: automationRuns.automationId,\n trigger: automationRuns.trigger,\n eventId: automationRuns.eventId,\n event: automationRuns.event,\n status: automationRuns.status,\n actionResults: automationRuns.actionResults,\n startedAt: automationRuns.startedAt,\n finishedAt: automationRuns.finishedAt,\n createdAt: automationRuns.createdAt,\n updatedAt: automationRuns.updatedAt,\n};\n\ninterface RunRow {\n id: string;\n orgId: string;\n automationId: string;\n trigger: string;\n eventId: string;\n event: AutomationRun[\"event\"];\n status: AutomationRun[\"status\"];\n actionResults: AutomationRun[\"actionResults\"];\n startedAt: Date;\n finishedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nfunction toAutomationRun(row: RunRow): AutomationRun {\n return {\n id: row.id,\n orgId: row.orgId,\n automationId: row.automationId,\n trigger: row.trigger,\n eventId: row.eventId,\n event: row.event,\n status: row.status,\n actionResults: row.actionResults,\n startedAt: row.startedAt,\n finishedAt: row.finishedAt,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\n// Sortable columns by the client-facing field name. `-field` = desc.\nconst sortColumns = {\n startedAt: automationRuns.startedAt,\n finishedAt: automationRuns.finishedAt,\n status: automationRuns.status,\n} as const;\n\nexport class DrizzleAutomationRunsRepository implements AutomationRunsRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async insert(orgId: string, run: NewAutomationRun): Promise<AutomationRun | null> {\n const [inserted] = await this.db\n .insert(automationRuns)\n .values({\n orgId,\n automationId: run.automationId,\n trigger: run.trigger,\n eventId: run.event.id,\n event: run.event,\n status: run.status,\n actionResults: run.actionResults,\n startedAt: run.startedAt,\n finishedAt: run.finishedAt,\n })\n // A redelivered trigger event hits the unique (org, automation, event) index; no row comes back.\n .onConflictDoNothing({\n target: [automationRuns.orgId, automationRuns.automationId, automationRuns.eventId],\n })\n .returning(runSelection);\n return inserted ? toAutomationRun(inserted) : null;\n }\n\n async findById(orgId: string, id: string): Promise<AutomationRun | null> {\n const [row] = await this.db\n .select(runSelection)\n .from(automationRuns)\n .where(and(eq(automationRuns.orgId, orgId), eq(automationRuns.id, id)))\n .limit(1);\n return row ? toAutomationRun(row) : null;\n }\n\n async restart(orgId: string, id: string, startedAt: Date): Promise<AutomationRun | null> {\n const [restarted] = await this.db\n .update(automationRuns)\n .set({ status: \"running\", actionResults: [], startedAt, finishedAt: null })\n .where(\n and(\n eq(automationRuns.orgId, orgId),\n eq(automationRuns.id, id),\n ne(automationRuns.status, \"running\"),\n ),\n )\n .returning(runSelection);\n return restarted ? toAutomationRun(restarted) : null;\n }\n\n async recordOutcome(\n orgId: string,\n id: string,\n outcome: {\n status: AutomationRun[\"status\"];\n actionResults: AutomationRun[\"actionResults\"];\n finishedAt: Date;\n },\n ): Promise<AutomationRun | null> {\n const [updated] = await this.db\n .update(automationRuns)\n .set({\n status: outcome.status,\n actionResults: outcome.actionResults,\n finishedAt: outcome.finishedAt,\n })\n .where(and(eq(automationRuns.orgId, orgId), eq(automationRuns.id, id)))\n .returning(runSelection);\n return updated ? toAutomationRun(updated) : null;\n }\n\n async list(\n orgId: string,\n automationId: string,\n query: AutomationRunsQuery,\n ): Promise<Page<AutomationRun>> {\n const conditions: SQL[] = [\n eq(automationRuns.orgId, orgId),\n eq(automationRuns.automationId, automationId),\n ];\n if (query.status) {\n conditions.push(eq(automationRuns.status, query.status));\n }\n const where = and(...conditions);\n\n // Sort: `-field` for descending; default to most-recently started first.\n let orderBy: SQL;\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const field = (isDesc ? query.sort.slice(1) : query.sort) as keyof typeof sortColumns;\n const col = sortColumns[field] ?? automationRuns.startedAt;\n orderBy = isDesc ? desc(col) : asc(col);\n } else {\n orderBy = desc(automationRuns.startedAt);\n }\n\n const offset = (query.page - 1) * query.pageSize;\n\n const rows = await this.db\n .select(runSelection)\n .from(automationRuns)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset(offset);\n\n const [{ total } = { total: 0 }] = await this.db\n .select({ total: sql<number>`cast(count(*) as int)` })\n .from(automationRuns)\n .where(where);\n\n return {\n rows: rows.map(toAutomationRun),\n total,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async recentRecipients(\n orgId: string,\n automationId: string,\n chatId: string,\n since: Date,\n ): Promise<string[]> {\n const rows = await this.db\n .select({\n actionResults: automationRuns.actionResults,\n })\n .from(automationRuns)\n .where(\n and(\n eq(automationRuns.orgId, orgId),\n eq(automationRuns.automationId, automationId),\n eq(automationRuns.status, \"completed\"),\n gte(automationRuns.finishedAt, since),\n sql`${automationRuns.event}->'data'->>'chatId' = ${chatId}`,\n ),\n );\n const ids: string[] = [];\n for (const row of rows) {\n for (const result of row.actionResults) {\n if (result.recipients) {\n ids.push(...result.recipients);\n }\n }\n }\n return [...new Set(ids)];\n }\n}\ntranslateDbErrors(DrizzleAutomationsRepository);\ntranslateDbErrors(DrizzleAutomationRunsRepository);\n"],"mappings":"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"}
1
+ {"version":3,"file":"automations.js","names":[],"sources":["../../src/repositories/automations.ts"],"sourcesContent":["// automations — Drizzle repositories (implement the core outbound ports).\n// Org-scoped: every method takes the domain `organizations.id` and constrains\n// its queries to that tenant. `Automation`/`AutomationRun` carry no `orgId`\n// column of their own in the domain type (mirrors the `Entitlement` shape) —\n// `AutomationRun` is the one exception, since it's the run's own `orgId`\n// field the service reads back off `handle`'s event.\nimport { and, arrayContains, asc, desc, eq, gte, ne, sql, type SQL } from \"drizzle-orm\";\nimport type { DbExecutor } from \"../client.js\";\nimport type {\n Automation,\n AutomationKind,\n AutomationRun,\n AutomationRunsQuery,\n AutomationRunsRepository,\n AutomationsRepository,\n NewAutomation,\n NewAutomationRun,\n Page,\n UpdateAutomationInput,\n} from \"@headless-lms/core/automations\";\nimport type { ScheduledAutomation } from \"@headless-lms/core/types\";\nimport { automations, automationRuns } from \"../schema/index.js\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport { translateDbErrors } from \"./pg-errors.js\";\n\nconst automationSelection = {\n orgId: automations.orgId,\n id: automations.id,\n name: automations.name,\n kind: automations.kind,\n description: automations.description,\n triggers: automations.triggers,\n actions: automations.actions,\n enabled: automations.enabled,\n filter: automations.filter,\n createdAt: automations.createdAt,\n updatedAt: automations.updatedAt,\n};\n\ninterface AutomationRow {\n orgId: string;\n id: string;\n name: string;\n kind: AutomationKind;\n description: string | null;\n triggers: string[];\n actions: Automation[\"actions\"];\n enabled: boolean;\n filter: Automation[\"filter\"];\n createdAt: Date;\n updatedAt: Date;\n}\n\nfunction toAutomation(row: AutomationRow): Automation {\n return {\n orgId: row.orgId,\n id: row.id,\n name: row.name,\n kind: row.kind,\n description: row.description,\n triggers: row.triggers,\n actions: row.actions,\n enabled: row.enabled,\n filter: row.filter,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nexport class DrizzleAutomationsRepository implements AutomationsRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async insert(orgId: string, input: NewAutomation): Promise<Automation> {\n const [inserted] = await this.db\n .insert(automations)\n .values({\n orgId,\n name: input.name,\n kind: input.kind,\n description: input.description,\n triggers: input.triggers,\n actions: input.actions,\n filter: input.filter ?? null,\n })\n .returning({ id: automations.id });\n if (!inserted) {\n throw new Error(\"failed to insert automation\");\n }\n const created = await this.findById(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created automation\");\n }\n return created;\n }\n\n async update(\n orgId: string,\n id: string,\n patch: UpdateAutomationInput,\n ): Promise<Automation | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.name !== undefined) {\n set.name = patch.name;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.triggers !== undefined) {\n set.triggers = patch.triggers;\n }\n if (patch.actions !== undefined) {\n set.actions = patch.actions;\n }\n if (patch.enabled !== undefined) {\n set.enabled = patch.enabled;\n }\n if (patch.filter !== undefined) {\n set.filter = patch.filter;\n }\n\n const [updated] = await this.db\n .update(automations)\n .set(set)\n .where(and(eq(automations.orgId, orgId), eq(automations.id, id)))\n .returning({ id: automations.id });\n if (!updated) {\n return null;\n }\n return this.findById(orgId, id);\n }\n\n async delete(orgId: string, id: string): Promise<Automation | null> {\n const [deleted] = await this.db\n .delete(automations)\n .where(and(eq(automations.orgId, orgId), eq(automations.id, id)))\n .returning(automationSelection);\n return deleted ? toAutomation(deleted) : null;\n }\n\n async findById(orgId: string, id: string): Promise<Automation | null> {\n const [row] = await this.db\n .select(automationSelection)\n .from(automations)\n .where(and(eq(automations.orgId, orgId), eq(automations.id, id)))\n .limit(1);\n return row ? toAutomation(row) : null;\n }\n\n async listByOrg(orgId: string, kind?: AutomationKind): Promise<Automation[]> {\n const conditions: SQL[] = [eq(automations.orgId, orgId)];\n if (kind) {\n conditions.push(eq(automations.kind, kind));\n }\n const rows = await this.db\n .select(automationSelection)\n .from(automations)\n .where(and(...conditions))\n .orderBy(desc(automations.createdAt));\n return rows.map(toAutomation);\n }\n\n async listByTrigger(orgId: string, trigger: string): Promise<Automation[]> {\n const rows = await this.db\n .select(automationSelection)\n .from(automations)\n .where(and(eq(automations.orgId, orgId), arrayContains(automations.triggers, [trigger])))\n .orderBy(desc(automations.createdAt));\n return rows.map(toAutomation);\n }\n\n async listSchedules(): Promise<ScheduledAutomation[]> {\n const rows = await this.db\n .select({\n orgId: automations.orgId,\n id: automations.id,\n triggers: automations.triggers,\n })\n .from(automations)\n .where(\n and(\n eq(automations.enabled, true),\n sql`exists (select 1 from unnest(${automations.triggers}) as t(trigger) where t.trigger like 'schedule:%')`,\n ),\n );\n const schedules: ScheduledAutomation[] = [];\n for (const row of rows) {\n for (const trigger of row.triggers) {\n if (!trigger.startsWith(\"schedule:\")) {\n continue;\n }\n schedules.push({\n orgId: row.orgId,\n automationId: row.id,\n cron: trigger.slice(\"schedule:\".length),\n });\n }\n }\n return schedules;\n }\n}\n\nconst runSelection = {\n id: automationRuns.id,\n orgId: automationRuns.orgId,\n automationId: automationRuns.automationId,\n trigger: automationRuns.trigger,\n eventId: automationRuns.eventId,\n event: automationRuns.event,\n status: automationRuns.status,\n actionResults: automationRuns.actionResults,\n startedAt: automationRuns.startedAt,\n finishedAt: automationRuns.finishedAt,\n createdAt: automationRuns.createdAt,\n updatedAt: automationRuns.updatedAt,\n};\n\ninterface RunRow {\n id: string;\n orgId: string;\n automationId: string;\n trigger: string;\n eventId: string;\n event: AutomationRun[\"event\"];\n status: AutomationRun[\"status\"];\n actionResults: AutomationRun[\"actionResults\"];\n startedAt: Date;\n finishedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nfunction toAutomationRun(row: RunRow): AutomationRun {\n return {\n id: row.id,\n orgId: row.orgId,\n automationId: row.automationId,\n trigger: row.trigger,\n eventId: row.eventId,\n event: row.event,\n status: row.status,\n actionResults: row.actionResults,\n startedAt: row.startedAt,\n finishedAt: row.finishedAt,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\n// Sortable columns by the client-facing field name. `-field` = desc.\nconst sortColumns = {\n startedAt: automationRuns.startedAt,\n finishedAt: automationRuns.finishedAt,\n status: automationRuns.status,\n} as const;\n\nexport class DrizzleAutomationRunsRepository implements AutomationRunsRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async insert(orgId: string, run: NewAutomationRun): Promise<AutomationRun | null> {\n const [inserted] = await this.db\n .insert(automationRuns)\n .values({\n orgId,\n automationId: run.automationId,\n trigger: run.trigger,\n eventId: run.event.id,\n event: run.event,\n status: run.status,\n actionResults: run.actionResults,\n startedAt: run.startedAt,\n finishedAt: run.finishedAt,\n })\n // A redelivered trigger event hits the unique (org, automation, event) index; no row comes back.\n .onConflictDoNothing({\n target: [automationRuns.orgId, automationRuns.automationId, automationRuns.eventId],\n })\n .returning(runSelection);\n return inserted ? toAutomationRun(inserted) : null;\n }\n\n async findById(orgId: string, id: string): Promise<AutomationRun | null> {\n const [row] = await this.db\n .select(runSelection)\n .from(automationRuns)\n .where(and(eq(automationRuns.orgId, orgId), eq(automationRuns.id, id)))\n .limit(1);\n return row ? toAutomationRun(row) : null;\n }\n\n async restart(orgId: string, id: string, startedAt: Date): Promise<AutomationRun | null> {\n const [restarted] = await this.db\n .update(automationRuns)\n .set({ status: \"running\", actionResults: [], startedAt, finishedAt: null })\n .where(\n and(\n eq(automationRuns.orgId, orgId),\n eq(automationRuns.id, id),\n ne(automationRuns.status, \"running\"),\n ),\n )\n .returning(runSelection);\n return restarted ? toAutomationRun(restarted) : null;\n }\n\n async recordOutcome(\n orgId: string,\n id: string,\n outcome: {\n status: AutomationRun[\"status\"];\n actionResults: AutomationRun[\"actionResults\"];\n finishedAt: Date;\n },\n ): Promise<AutomationRun | null> {\n const [updated] = await this.db\n .update(automationRuns)\n .set({\n status: outcome.status,\n actionResults: outcome.actionResults,\n finishedAt: outcome.finishedAt,\n })\n .where(and(eq(automationRuns.orgId, orgId), eq(automationRuns.id, id)))\n .returning(runSelection);\n return updated ? toAutomationRun(updated) : null;\n }\n\n async list(\n orgId: string,\n automationId: string,\n query: AutomationRunsQuery,\n ): Promise<Page<AutomationRun>> {\n const conditions: SQL[] = [\n eq(automationRuns.orgId, orgId),\n eq(automationRuns.automationId, automationId),\n ];\n if (query.status) {\n conditions.push(eq(automationRuns.status, query.status));\n }\n const where = and(...conditions);\n\n // Sort: `-field` for descending; default to most-recently started first.\n let orderBy: SQL;\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const field = (isDesc ? query.sort.slice(1) : query.sort) as keyof typeof sortColumns;\n const col = sortColumns[field] ?? automationRuns.startedAt;\n orderBy = isDesc ? desc(col) : asc(col);\n } else {\n orderBy = desc(automationRuns.startedAt);\n }\n\n const offset = (query.page - 1) * query.pageSize;\n\n const rows = await this.db\n .select(runSelection)\n .from(automationRuns)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset(offset);\n\n const [{ total } = { total: 0 }] = await this.db\n .select({ total: sql<number>`cast(count(*) as int)` })\n .from(automationRuns)\n .where(where);\n\n return {\n rows: rows.map(toAutomationRun),\n total,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async recentRecipients(\n orgId: string,\n automationId: string,\n chatId: string,\n since: Date,\n ): Promise<string[]> {\n const rows = await this.db\n .select({\n actionResults: automationRuns.actionResults,\n })\n .from(automationRuns)\n .where(\n and(\n eq(automationRuns.orgId, orgId),\n eq(automationRuns.automationId, automationId),\n eq(automationRuns.status, \"completed\"),\n gte(automationRuns.finishedAt, since),\n sql`${automationRuns.event}->'data'->>'chatId' = ${chatId}`,\n ),\n );\n const ids: string[] = [];\n for (const row of rows) {\n for (const result of row.actionResults) {\n if (result.recipients) {\n ids.push(...result.recipients);\n }\n }\n }\n return [...new Set(ids)];\n }\n}\ntranslateDbErrors(DrizzleAutomationsRepository);\ntranslateDbErrors(DrizzleAutomationRunsRepository);\n"],"mappings":";;;;;;AA0BA,MAAM,sBAAsB;CAC1B,OAAO,YAAY;CACnB,IAAI,YAAY;CAChB,MAAM,YAAY;CAClB,MAAM,YAAY;CAClB,aAAa,YAAY;CACzB,UAAU,YAAY;CACtB,SAAS,YAAY;CACrB,SAAS,YAAY;CACrB,QAAQ,YAAY;CACpB,WAAW,YAAY;CACvB,WAAW,YAAY;AACzB;AAgBA,SAAS,aAAa,KAAgC;CACpD,OAAO;EACL,OAAO,IAAI;EACX,IAAI,IAAI;EACR,MAAM,IAAI;EACV,MAAM,IAAI;EACV,aAAa,IAAI;EACjB,UAAU,IAAI;EACd,SAAS,IAAI;EACb,SAAS,IAAI;EACb,QAAQ,IAAI;EACZ,WAAW,IAAI;EACf,WAAW,IAAI;CACjB;AACF;AAEA,IAAa,+BAAb,MAA2E;CAEtD;CACA;CAFnB,YACE,IACA,SAAkC,YAClC;EAFiB,KAAA,KAAA;EACA,KAAA,SAAA;CAChB;CAEH,MAAM,OAAO,OAAe,OAA2C;EACrE,MAAM,CAAC,YAAY,MAAM,KAAK,GAC3B,OAAO,WAAW,CAAC,CACnB,OAAO;GACN;GACA,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,aAAa,MAAM;GACnB,UAAU,MAAM;GAChB,SAAS,MAAM;GACf,QAAQ,MAAM,UAAU;EAC1B,CAAC,CAAC,CACD,UAAU,EAAE,IAAI,YAAY,GAAG,CAAC;EACnC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,6BAA6B;EAE/C,MAAM,UAAU,MAAM,KAAK,SAAS,OAAO,SAAS,EAAE;EACtD,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,mCAAmC;EAErD,OAAO;CACT;CAEA,MAAM,OACJ,OACA,IACA,OAC4B;EAC5B,MAAM,MAA+B,EAAE,2BAAW,IAAI,KAAK,EAAE;EAC7D,IAAI,MAAM,SAAS,KAAA,GACjB,IAAI,OAAO,MAAM;EAEnB,IAAI,MAAM,gBAAgB,KAAA,GACxB,IAAI,cAAc,MAAM;EAE1B,IAAI,MAAM,aAAa,KAAA,GACrB,IAAI,WAAW,MAAM;EAEvB,IAAI,MAAM,YAAY,KAAA,GACpB,IAAI,UAAU,MAAM;EAEtB,IAAI,MAAM,YAAY,KAAA,GACpB,IAAI,UAAU,MAAM;EAEtB,IAAI,MAAM,WAAW,KAAA,GACnB,IAAI,SAAS,MAAM;EAGrB,MAAM,CAAC,WAAW,MAAM,KAAK,GAC1B,OAAO,WAAW,CAAC,CACnB,IAAI,GAAG,CAAC,CACR,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,CAChE,UAAU,EAAE,IAAI,YAAY,GAAG,CAAC;EACnC,IAAI,CAAC,SACH,OAAO;EAET,OAAO,KAAK,SAAS,OAAO,EAAE;CAChC;CAEA,MAAM,OAAO,OAAe,IAAwC;EAClE,MAAM,CAAC,WAAW,MAAM,KAAK,GAC1B,OAAO,WAAW,CAAC,CACnB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,CAChE,UAAU,mBAAmB;EAChC,OAAO,UAAU,aAAa,OAAO,IAAI;CAC3C;CAEA,MAAM,SAAS,OAAe,IAAwC;EACpE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,mBAAmB,CAAC,CAC3B,KAAK,WAAW,CAAC,CACjB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,CAChE,MAAM,CAAC;EACV,OAAO,MAAM,aAAa,GAAG,IAAI;CACnC;CAEA,MAAM,UAAU,OAAe,MAA8C;EAC3E,MAAM,aAAoB,CAAC,GAAG,YAAY,OAAO,KAAK,CAAC;EACvD,IAAI,MACF,WAAW,KAAK,GAAG,YAAY,MAAM,IAAI,CAAC;EAO5C,QAAO,MALY,KAAK,GACrB,OAAO,mBAAmB,CAAC,CAC3B,KAAK,WAAW,CAAC,CACjB,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CACzB,QAAQ,KAAK,YAAY,SAAS,CAAC,EAAA,CAC1B,IAAI,YAAY;CAC9B;CAEA,MAAM,cAAc,OAAe,SAAwC;EAMzE,QAAO,MALY,KAAK,GACrB,OAAO,mBAAmB,CAAC,CAC3B,KAAK,WAAW,CAAC,CACjB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,cAAc,YAAY,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CACxF,QAAQ,KAAK,YAAY,SAAS,CAAC,EAAA,CAC1B,IAAI,YAAY;CAC9B;CAEA,MAAM,gBAAgD;EACpD,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO;GACN,OAAO,YAAY;GACnB,IAAI,YAAY;GAChB,UAAU,YAAY;EACxB,CAAC,CAAC,CACD,KAAK,WAAW,CAAC,CACjB,MACC,IACE,GAAG,YAAY,SAAS,IAAI,GAC5B,GAAG,gCAAgC,YAAY,SAAS,mDAC1D,CACF;EACF,MAAM,YAAmC,CAAC;EAC1C,KAAK,MAAM,OAAO,MAChB,KAAK,MAAM,WAAW,IAAI,UAAU;GAClC,IAAI,CAAC,QAAQ,WAAW,WAAW,GACjC;GAEF,UAAU,KAAK;IACb,OAAO,IAAI;IACX,cAAc,IAAI;IAClB,MAAM,QAAQ,MAAM,CAAkB;GACxC,CAAC;EACH;EAEF,OAAO;CACT;AACF;AAEA,MAAM,eAAe;CACnB,IAAI,eAAe;CACnB,OAAO,eAAe;CACtB,cAAc,eAAe;CAC7B,SAAS,eAAe;CACxB,SAAS,eAAe;CACxB,OAAO,eAAe;CACtB,QAAQ,eAAe;CACvB,eAAe,eAAe;CAC9B,WAAW,eAAe;CAC1B,YAAY,eAAe;CAC3B,WAAW,eAAe;CAC1B,WAAW,eAAe;AAC5B;AAiBA,SAAS,gBAAgB,KAA4B;CACnD,OAAO;EACL,IAAI,IAAI;EACR,OAAO,IAAI;EACX,cAAc,IAAI;EAClB,SAAS,IAAI;EACb,SAAS,IAAI;EACb,OAAO,IAAI;EACX,QAAQ,IAAI;EACZ,eAAe,IAAI;EACnB,WAAW,IAAI;EACf,YAAY,IAAI;EAChB,WAAW,IAAI;EACf,WAAW,IAAI;CACjB;AACF;AAGA,MAAM,cAAc;CAClB,WAAW,eAAe;CAC1B,YAAY,eAAe;CAC3B,QAAQ,eAAe;AACzB;AAEA,IAAa,kCAAb,MAAiF;CAE5D;CACA;CAFnB,YACE,IACA,SAAkC,YAClC;EAFiB,KAAA,KAAA;EACA,KAAA,SAAA;CAChB;CAEH,MAAM,OAAO,OAAe,KAAsD;EAChF,MAAM,CAAC,YAAY,MAAM,KAAK,GAC3B,OAAO,cAAc,CAAC,CACtB,OAAO;GACN;GACA,cAAc,IAAI;GAClB,SAAS,IAAI;GACb,SAAS,IAAI,MAAM;GACnB,OAAO,IAAI;GACX,QAAQ,IAAI;GACZ,eAAe,IAAI;GACnB,WAAW,IAAI;GACf,YAAY,IAAI;EAClB,CAAC,CAAC,CAED,oBAAoB,EACnB,QAAQ;GAAC,eAAe;GAAO,eAAe;GAAc,eAAe;EAAO,EACpF,CAAC,CAAC,CACD,UAAU,YAAY;EACzB,OAAO,WAAW,gBAAgB,QAAQ,IAAI;CAChD;CAEA,MAAM,SAAS,OAAe,IAA2C;EACvE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,YAAY,CAAC,CACpB,KAAK,cAAc,CAAC,CACpB,MAAM,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,GAAG,eAAe,IAAI,EAAE,CAAC,CAAC,CAAC,CACtE,MAAM,CAAC;EACV,OAAO,MAAM,gBAAgB,GAAG,IAAI;CACtC;CAEA,MAAM,QAAQ,OAAe,IAAY,WAAgD;EACvF,MAAM,CAAC,aAAa,MAAM,KAAK,GAC5B,OAAO,cAAc,CAAC,CACtB,IAAI;GAAE,QAAQ;GAAW,eAAe,CAAC;GAAG;GAAW,YAAY;EAAK,CAAC,CAAC,CAC1E,MACC,IACE,GAAG,eAAe,OAAO,KAAK,GAC9B,GAAG,eAAe,IAAI,EAAE,GACxB,GAAG,eAAe,QAAQ,SAAS,CACrC,CACF,CAAC,CACA,UAAU,YAAY;EACzB,OAAO,YAAY,gBAAgB,SAAS,IAAI;CAClD;CAEA,MAAM,cACJ,OACA,IACA,SAK+B;EAC/B,MAAM,CAAC,WAAW,MAAM,KAAK,GAC1B,OAAO,cAAc,CAAC,CACtB,IAAI;GACH,QAAQ,QAAQ;GAChB,eAAe,QAAQ;GACvB,YAAY,QAAQ;EACtB,CAAC,CAAC,CACD,MAAM,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,GAAG,eAAe,IAAI,EAAE,CAAC,CAAC,CAAC,CACtE,UAAU,YAAY;EACzB,OAAO,UAAU,gBAAgB,OAAO,IAAI;CAC9C;CAEA,MAAM,KACJ,OACA,cACA,OAC8B;EAC9B,MAAM,aAAoB,CACxB,GAAG,eAAe,OAAO,KAAK,GAC9B,GAAG,eAAe,cAAc,YAAY,CAC9C;EACA,IAAI,MAAM,QACR,WAAW,KAAK,GAAG,eAAe,QAAQ,MAAM,MAAM,CAAC;EAEzD,MAAM,QAAQ,IAAI,GAAG,UAAU;EAG/B,IAAI;EACJ,IAAI,MAAM,MAAM;GACd,MAAM,SAAS,MAAM,KAAK,WAAW,GAAG;GACxC,MAAM,QAAS,SAAS,MAAM,KAAK,MAAM,CAAC,IAAI,MAAM;GACpD,MAAM,MAAM,YAAY,UAAU,eAAe;GACjD,UAAU,SAAS,KAAK,GAAG,IAAI,IAAI,GAAG;EACxC,OACE,UAAU,KAAK,eAAe,SAAS;EAGzC,MAAM,UAAU,MAAM,OAAO,KAAK,MAAM;EAExC,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,YAAY,CAAC,CACpB,KAAK,cAAc,CAAC,CACpB,MAAM,KAAK,CAAC,CACZ,QAAQ,OAAO,CAAC,CAChB,MAAM,MAAM,QAAQ,CAAC,CACrB,OAAO,MAAM;EAEhB,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,MAAM,KAAK,GAC3C,OAAO,EAAE,OAAO,GAAW,wBAAwB,CAAC,CAAC,CACrD,KAAK,cAAc,CAAC,CACpB,MAAM,KAAK;EAEd,OAAO;GACL,MAAM,KAAK,IAAI,eAAe;GAC9B;GACA,MAAM,MAAM;GACZ,UAAU,MAAM;EAClB;CACF;CAEA,MAAM,iBACJ,OACA,cACA,QACA,OACmB;EACnB,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,EACN,eAAe,eAAe,cAChC,CAAC,CAAC,CACD,KAAK,cAAc,CAAC,CACpB,MACC,IACE,GAAG,eAAe,OAAO,KAAK,GAC9B,GAAG,eAAe,cAAc,YAAY,GAC5C,GAAG,eAAe,QAAQ,WAAW,GACrC,IAAI,eAAe,YAAY,KAAK,GACpC,GAAG,GAAG,eAAe,MAAM,wBAAwB,QACrD,CACF;EACF,MAAM,MAAgB,CAAC;EACvB,KAAK,MAAM,OAAO,MAChB,KAAK,MAAM,UAAU,IAAI,eACvB,IAAI,OAAO,YACT,IAAI,KAAK,GAAG,OAAO,UAAU;EAInC,OAAO,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC;CACzB;AACF;AACA,kBAAkB,4BAA4B;AAC9C,kBAAkB,+BAA+B"}
@@ -1,6 +1,6 @@
1
1
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
2
  import { Chat, ChatUser, Logger, Message, MessageFlag, MessageFlagResolution, Page, ReactionEmoji } from "@headless-lms/core/types";
3
- import { ChatListItem, ChatRepository, FeedQuery, MessageReactions, PageQuery } from "@headless-lms/core/discussion";
3
+ import { ChatInboxRow, ChatListQuery, ChatMemberStates, ChatRepository, ChatUnread, ChatUnreadSummary, FeedQuery, MessageReactions, ReactionChange } from "@headless-lms/core/discussion";
4
4
  //#region src/repositories/chats.d.ts
5
5
  declare class DrizzleChatRepository implements ChatRepository {
6
6
  private readonly db;
@@ -11,19 +11,24 @@ declare class DrizzleChatRepository implements ChatRepository {
11
11
  insertChat(orgId: string, chat: Chat): Promise<Chat>;
12
12
  updateChat(orgId: string, id: string, patch: Partial<Pick<Chat, "status" | "lockedAt" | "lockedBy">>): Promise<Chat | null>;
13
13
  findChat(orgId: string, id: string): Promise<Chat | null>;
14
- listForUser(orgId: string, communityId: string, orgUserId: string, query: PageQuery): Promise<Page<ChatListItem>>;
14
+ listForUser(orgId: string, communityId: string, orgUserId: string, query: ChatListQuery): Promise<Page<ChatInboxRow>>;
15
+ inboxRow(orgId: string, chatId: string, orgUserId: string): Promise<ChatInboxRow | null>;
16
+ activeParticipants(orgId: string, chatId: string): Promise<string[]>;
17
+ unreadSummary(orgId: string, communityId: string, orgUserId: string, at: Date): Promise<ChatUnreadSummary>;
18
+ memberStates(orgId: string, communityId: string, orgUserIds: string[], at: Date): Promise<ChatMemberStates[]>;
19
+ findByClientId(orgId: string, chatId: string, orgUserId: string, clientId: string): Promise<Message | null>;
15
20
  participants(orgId: string, chatId: string): Promise<ChatUser[]>;
16
21
  join(orgId: string, user: ChatUser): Promise<void>;
17
22
  markLeft(orgId: string, chatId: string, orgUserId: string, leftAt: Date): Promise<void>;
18
- setRead(orgId: string, chatId: string, orgUserId: string, messageId: string): Promise<void>;
19
- setMuted(orgId: string, chatId: string, orgUserId: string, muted: boolean, mutedUntil: Date | null): Promise<void>;
23
+ setRead(orgId: string, chatId: string, orgUserId: string, messageId: string): Promise<boolean>;
24
+ unreadFor(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;
20
25
  insertMessage(orgId: string, message: Message): Promise<Message>;
21
26
  updateMessage(orgId: string, id: string, patch: Partial<Pick<Message, "body" | "editedAt" | "deletedAt">>): Promise<Message | null>;
22
27
  findMessage(orgId: string, id: string): Promise<Message | null>;
23
28
  listMessages(orgId: string, chatId: string, query: Pick<FeedQuery, "page" | "pageSize">): Promise<Page<Message>>;
24
29
  attach(orgId: string, communityId: string, messageId: string, assetIds: string[]): Promise<void>;
25
30
  attachmentsOf(orgId: string, messageIds: string[]): Promise<Record<string, string[]>>;
26
- setReaction(orgId: string, communityId: string, messageId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<void>;
31
+ setReaction(orgId: string, communityId: string, messageId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<ReactionChange>;
27
32
  reactionsOf(orgId: string, messageIds: string[], viewerId: string): Promise<Record<string, MessageReactions>>;
28
33
  block(orgId: string, communityId: string, blockerOrgUserId: string, blockedOrgUserId: string): Promise<void>;
29
34
  unblock(orgId: string, communityId: string, blockerOrgUserId: string, blockedOrgUserId: string): Promise<void>;
@@ -48,6 +53,10 @@ declare class DrizzleChatRepository implements ChatRepository {
48
53
  resolvedBy: string;
49
54
  resolution: MessageFlagResolution;
50
55
  }): Promise<void>;
56
+ private requestTo;
57
+ private unreadCount;
58
+ private inbox;
59
+ private withParticipants;
51
60
  private touch;
52
61
  }
53
62
  //#endregion