@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,13 +1,13 @@
1
- import { activities, activityAssets, bundleItems, bundles, communities, contentItems, courses, downloadAssets, downloads, modules, spaceLinks, spaceTopics, spaces } from "../schema/content.js";
2
- import { posts } from "../schema/discussion.js";
1
+ import { activities, activityAssets, bundleItems, bundles, communities, contentItems, courses, downloadAssets, downloads, eventAssets, events, modules, spaceLinks, spaceTopics, spaces } from "../schema/content.js";
3
2
  import { settings } from "../schema/settings.js";
3
+ import { posts } from "../schema/discussion.js";
4
4
  import { isUniqueViolation, pgErrorFields, translateDbErrors } from "./pg-errors.js";
5
5
  import { deepMerge } from "./settings.js";
6
- import { and, asc, count, desc, eq, ilike, inArray, or, sql } from "drizzle-orm";
6
+ import { noopLogger } from "@headless-lms/core/shared/logger";
7
+ import { and, asc, count, desc, eq, gt, ilike, inArray, lt, or, sql } from "drizzle-orm";
7
8
  import { genId } from "@headless-lms/core/shared/id";
8
9
  import { ConflictError, NotFoundError } from "@headless-lms/core/shared/errors";
9
- import { noopLogger } from "@headless-lms/core/shared/logger";
10
- import { communitySettingsDefaults, courseSettingsDefaults, spaceSettingsDefaults } from "@headless-lms/core/schemas";
10
+ import { communitySettingsDefaults, courseSettingsDefaults, digestScheduleSchema, eventSettingsDefaults, spaceSettingsDefaults } from "@headless-lms/core/schemas";
11
11
  //#region src/repositories/content.ts
12
12
  const LIBRARY_ONLY = "An activity can only use ready files from the media library";
13
13
  const MISSING_ASSET_DETAIL = /^Key \(org_id, asset_id\)=\((?:[^,]+), (.+?)\) is not present/;
@@ -21,16 +21,38 @@ function refusedLink(err) {
21
21
  const SETTINGS_NAMESPACE = "content";
22
22
  const courseSettingsJoin = and(eq(settings.orgId, courses.orgId), eq(settings.namespace, SETTINGS_NAMESPACE), eq(settings.scopeId, courses.id));
23
23
  const communitySettingsJoin = and(eq(settings.orgId, communities.orgId), eq(settings.namespace, SETTINGS_NAMESPACE), eq(settings.scopeId, communities.id));
24
+ const eventSettingsJoin = and(eq(settings.orgId, events.orgId), eq(settings.namespace, SETTINGS_NAMESPACE), eq(settings.scopeId, events.id));
25
+ function toEvent(row, storedSettings) {
26
+ return {
27
+ ...row,
28
+ status: row.status,
29
+ settings: {
30
+ ...eventSettingsDefaults,
31
+ ...storedSettings
32
+ }
33
+ };
34
+ }
35
+ const eventSortColumns = {
36
+ startsAt: events.startsAt,
37
+ title: events.title
38
+ };
39
+ function storedDigest(value) {
40
+ const parsed = digestScheduleSchema.safeParse(value);
41
+ return parsed.success ? parsed.data : null;
42
+ }
24
43
  function toCommunity(row, storedSettings) {
25
44
  const raw = storedSettings?.dm;
26
45
  const stored = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
27
46
  return {
28
47
  ...row,
29
48
  status: row.status,
30
- settings: { dm: {
31
- ...communitySettingsDefaults.dm,
32
- ...stored
33
- } }
49
+ settings: {
50
+ dm: {
51
+ ...communitySettingsDefaults.dm,
52
+ ...stored
53
+ },
54
+ digest: storedDigest(storedSettings?.digest)
55
+ }
34
56
  };
35
57
  }
36
58
  function toSpace(row) {
@@ -99,8 +121,15 @@ var DrizzleContentRepository = class {
99
121
  tx(fn) {
100
122
  return this.db.transaction(fn);
101
123
  }
102
- async listCourses(orgId, query) {
124
+ async listCourses(orgId, query, ids) {
125
+ if (ids?.length === 0) return {
126
+ rows: [],
127
+ total: 0,
128
+ page: query.page,
129
+ pageSize: query.pageSize
130
+ };
103
131
  const conditions = [eq(courses.orgId, orgId)];
132
+ if (ids) conditions.push(inArray(courses.id, ids));
104
133
  if (query.status) conditions.push(eq(courses.status, query.status));
105
134
  if (query.category) conditions.push(eq(courses.category, query.category));
106
135
  const search = query.search?.trim();
@@ -173,27 +202,7 @@ var DrizzleContentRepository = class {
173
202
  return this.findCourseById(orgId, id);
174
203
  }
175
204
  async patchCourseSettings(orgId, id, value) {
176
- const key = and(eq(settings.orgId, orgId), eq(settings.namespace, SETTINGS_NAMESPACE), eq(settings.scopeId, id));
177
- const merged = await this.tx(async (tx) => {
178
- let [existing] = await tx.select().from(settings).where(key).for("update");
179
- if (!existing) {
180
- const [inserted] = await tx.insert(settings).values({
181
- orgId,
182
- namespace: SETTINGS_NAMESPACE,
183
- scopeId: id,
184
- value
185
- }).onConflictDoNothing().returning();
186
- if (inserted) return inserted.value;
187
- [existing] = await tx.select().from(settings).where(key).for("update");
188
- if (!existing) throw new Error("course settings patch: row vanished mid-transaction");
189
- }
190
- const [updated] = await tx.update(settings).set({
191
- value: deepMerge(existing.value, value),
192
- updatedAt: /* @__PURE__ */ new Date()
193
- }).where(key).returning();
194
- if (!updated) throw new Error("course settings patch returned no row");
195
- return updated.value;
196
- });
205
+ const merged = await this.patchSettings(orgId, id, value);
197
206
  return {
198
207
  ...courseSettingsDefaults,
199
208
  ...merged
@@ -334,8 +343,15 @@ var DrizzleContentRepository = class {
334
343
  return this.listModules(tx, orgId, courseId);
335
344
  });
336
345
  }
337
- async listDownloads(orgId, query) {
346
+ async listDownloads(orgId, query, ids) {
347
+ if (ids?.length === 0) return {
348
+ rows: [],
349
+ total: 0,
350
+ page: query.page,
351
+ pageSize: query.pageSize
352
+ };
338
353
  const conditions = [eq(downloads.orgId, orgId)];
354
+ if (ids) conditions.push(inArray(downloads.id, ids));
339
355
  if (query.status) conditions.push(eq(downloads.status, query.status));
340
356
  if (query.category) conditions.push(eq(downloads.category, query.category));
341
357
  const search = query.search?.trim();
@@ -444,6 +460,144 @@ var DrizzleContentRepository = class {
444
460
  for (const [seq, assetId] of assetIds.entries()) await this.db.update(downloadAssets).set({ seq }).where(and(eq(downloadAssets.orgId, orgId), eq(downloadAssets.downloadId, downloadId), eq(downloadAssets.assetId, assetId)));
445
461
  return this.listDownloadAssets(orgId, downloadId);
446
462
  }
463
+ async listEvents(orgId, query, ids) {
464
+ if (ids?.length === 0) return {
465
+ rows: [],
466
+ total: 0,
467
+ page: query.page,
468
+ pageSize: query.pageSize
469
+ };
470
+ const conditions = [eq(events.orgId, orgId)];
471
+ if (ids) conditions.push(inArray(events.id, ids));
472
+ if (query.status) conditions.push(eq(events.status, query.status));
473
+ if (query.from) conditions.push(gt(events.endsAt, query.from));
474
+ if (query.to) conditions.push(lt(events.startsAt, query.to));
475
+ const search = query.search?.trim();
476
+ if (search) conditions.push(ilike(events.title, `%${search}%`));
477
+ const where = and(...conditions);
478
+ const sort = query.sort ?? "startsAt";
479
+ const isDesc = sort.startsWith("-");
480
+ const sortExpr = eventSortColumns[isDesc ? sort.slice(1) : sort] ?? events.startsAt;
481
+ const direction = isDesc ? desc : asc;
482
+ const rows = await this.db.select({
483
+ event: events,
484
+ settingsValue: settings.value
485
+ }).from(events).leftJoin(settings, eventSettingsJoin).where(where).orderBy(direction(sortExpr), direction(events.id)).limit(query.pageSize).offset((query.page - 1) * query.pageSize);
486
+ const [totalRow] = await this.db.select({ value: count() }).from(events).where(where);
487
+ return {
488
+ rows: rows.map((row) => toEvent(row.event, row.settingsValue)),
489
+ total: totalRow?.value ?? 0,
490
+ page: query.page,
491
+ pageSize: query.pageSize
492
+ };
493
+ }
494
+ async findEventById(orgId, id) {
495
+ const [row] = await this.db.select({
496
+ event: events,
497
+ settingsValue: settings.value
498
+ }).from(events).leftJoin(settings, eventSettingsJoin).where(and(eq(events.orgId, orgId), eq(events.id, id))).limit(1);
499
+ return row ? toEvent(row.event, row.settingsValue) : null;
500
+ }
501
+ async findEventsByIds(orgId, ids) {
502
+ return (await this.db.select({
503
+ event: events,
504
+ settingsValue: settings.value
505
+ }).from(events).leftJoin(settings, eventSettingsJoin).where(and(eq(events.orgId, orgId), inArray(events.id, ids)))).map((row) => toEvent(row.event, row.settingsValue));
506
+ }
507
+ async createEvent(orgId, input) {
508
+ const id = genId("contentEvent");
509
+ await this.db.insert(contentItems).values({
510
+ orgId,
511
+ id,
512
+ type: "event"
513
+ });
514
+ await this.db.insert(events).values({
515
+ orgId,
516
+ id,
517
+ title: input.title,
518
+ description: input.description ?? "",
519
+ startsAt: input.startsAt,
520
+ endsAt: input.endsAt,
521
+ timeZone: input.timeZone,
522
+ ...input.location && { location: input.location }
523
+ });
524
+ const created = await this.findEventById(orgId, id);
525
+ if (!created) throw new Error("failed to load created event");
526
+ return created;
527
+ }
528
+ async updateEvent(orgId, id, patch) {
529
+ const set = { updatedAt: /* @__PURE__ */ new Date() };
530
+ for (const key of [
531
+ "title",
532
+ "description",
533
+ "startsAt",
534
+ "endsAt",
535
+ "timeZone",
536
+ "location",
537
+ "thumbnailAssetId",
538
+ "recording",
539
+ "status"
540
+ ]) if (patch[key] !== void 0) set[key] = patch[key];
541
+ const [updated] = await this.db.update(events).set(set).where(and(eq(events.orgId, orgId), eq(events.id, id))).returning({ id: events.id });
542
+ if (!updated) return null;
543
+ return this.findEventById(orgId, id);
544
+ }
545
+ async patchEventSettings(orgId, id, value) {
546
+ const merged = await this.patchSettings(orgId, id, value);
547
+ return {
548
+ ...eventSettingsDefaults,
549
+ ...merged
550
+ };
551
+ }
552
+ async deleteEvent(orgId, id) {
553
+ return (await this.db.delete(contentItems).where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, id), eq(contentItems.type, "event"))).returning({ id: contentItems.id })).length > 0;
554
+ }
555
+ async deleteEventSettings(orgId, id) {
556
+ await this.db.delete(settings).where(and(eq(settings.orgId, orgId), eq(settings.namespace, SETTINGS_NAMESPACE), eq(settings.scopeId, id)));
557
+ }
558
+ async listEventAssets(orgId, eventId) {
559
+ return this.db.select().from(eventAssets).where(and(eq(eventAssets.orgId, orgId), eq(eventAssets.eventId, eventId))).orderBy(asc(eventAssets.seq));
560
+ }
561
+ async listEventAssetsFor(orgId, eventIds) {
562
+ return this.db.select().from(eventAssets).where(and(eq(eventAssets.orgId, orgId), inArray(eventAssets.eventId, eventIds))).orderBy(asc(eventAssets.eventId), asc(eventAssets.seq));
563
+ }
564
+ async assertEvent(orgId, eventId) {
565
+ const [row] = await this.db.select({ id: events.id }).from(events).where(and(eq(events.orgId, orgId), eq(events.id, eventId))).limit(1);
566
+ if (!row) throw new NotFoundError("Event", eventId);
567
+ }
568
+ async addEventAsset(orgId, eventId, input) {
569
+ await this.assertEvent(orgId, eventId);
570
+ const [maxRow] = await this.db.select({ maxSeq: sql`max(${eventAssets.seq})` }).from(eventAssets).where(and(eq(eventAssets.orgId, orgId), eq(eventAssets.eventId, eventId)));
571
+ const nextSeq = (maxRow?.maxSeq ?? -1) + 1;
572
+ try {
573
+ await this.db.insert(eventAssets).values({
574
+ orgId,
575
+ eventId,
576
+ assetId: input.assetId,
577
+ seq: nextSeq,
578
+ displayName: input.displayName ?? null
579
+ });
580
+ } catch (err) {
581
+ if (isUniqueViolation(err)) throw new ConflictError("This asset is already linked to the event");
582
+ throw err;
583
+ }
584
+ return this.listEventAssets(orgId, eventId);
585
+ }
586
+ async removeEventAsset(orgId, eventId, assetId) {
587
+ await this.assertEvent(orgId, eventId);
588
+ await this.db.delete(eventAssets).where(and(eq(eventAssets.orgId, orgId), eq(eventAssets.eventId, eventId), eq(eventAssets.assetId, assetId)));
589
+ return this.listEventAssets(orgId, eventId);
590
+ }
591
+ async renameEventAsset(orgId, eventId, assetId, displayName) {
592
+ await this.assertEvent(orgId, eventId);
593
+ await this.db.update(eventAssets).set({ displayName }).where(and(eq(eventAssets.orgId, orgId), eq(eventAssets.eventId, eventId), eq(eventAssets.assetId, assetId)));
594
+ return this.listEventAssets(orgId, eventId);
595
+ }
596
+ async reorderEventAssets(orgId, eventId, assetIds) {
597
+ await this.assertEvent(orgId, eventId);
598
+ for (const [seq, assetId] of assetIds.entries()) await this.db.update(eventAssets).set({ seq }).where(and(eq(eventAssets.orgId, orgId), eq(eventAssets.eventId, eventId), eq(eventAssets.assetId, assetId)));
599
+ return this.listEventAssets(orgId, eventId);
600
+ }
447
601
  async listBundles(orgId, query) {
448
602
  const conditions = [eq(bundles.orgId, orgId)];
449
603
  const search = query.search?.trim();
@@ -536,8 +690,15 @@ var DrizzleContentRepository = class {
536
690
  await this.db.delete(bundleItems).where(and(eq(bundleItems.orgId, orgId), eq(bundleItems.bundleId, bundleId), eq(bundleItems.contentId, contentId)));
537
691
  return this.listBundleItems(orgId, bundleId);
538
692
  }
539
- async listCommunities(orgId, query) {
693
+ async listCommunities(orgId, query, ids) {
694
+ if (ids?.length === 0) return {
695
+ rows: [],
696
+ total: 0,
697
+ page: query.page,
698
+ pageSize: query.pageSize
699
+ };
540
700
  const conditions = [eq(communities.orgId, orgId)];
701
+ if (ids) conditions.push(inArray(communities.id, ids));
541
702
  if (query.status) conditions.push(eq(communities.status, query.status));
542
703
  const search = query.search?.trim();
543
704
  if (search) {
@@ -583,9 +744,6 @@ var DrizzleContentRepository = class {
583
744
  settingsValue: settings.value
584
745
  }).from(communities).leftJoin(settings, communitySettingsJoin).where(and(eq(communities.orgId, orgId), inArray(communities.id, ids))).orderBy(asc(communities.title), asc(communities.id))).map((row) => toCommunity(row.community, row.settingsValue));
585
746
  }
586
- async listPublishedCommunityIds(orgId) {
587
- return (await this.db.select({ id: communities.id }).from(communities).where(and(eq(communities.orgId, orgId), eq(communities.status, "published"))).orderBy(asc(communities.createdAt), asc(communities.id))).map((row) => row.id);
588
- }
589
747
  async createCommunity(orgId, input, slug) {
590
748
  const id = genId("community");
591
749
  await this.db.insert(contentItems).values({
@@ -615,29 +773,52 @@ var DrizzleContentRepository = class {
615
773
  if (!updated) return null;
616
774
  return this.findCommunityById(orgId, id);
617
775
  }
618
- async patchDmSettings(orgId, id, patch) {
776
+ async communitiesWithDigest() {
777
+ return (await this.db.select({
778
+ orgId: communities.orgId,
779
+ communityId: communities.id,
780
+ digest: sql`${settings.value} -> 'digest'`
781
+ }).from(communities).innerJoin(settings, communitySettingsJoin).where(and(eq(communities.status, "published"), sql`jsonb_typeof(${settings.value} -> 'digest') = 'object'`)).orderBy(asc(communities.orgId), asc(communities.id))).flatMap((row) => {
782
+ const digest = storedDigest(row.digest);
783
+ return digest ? [{
784
+ orgId: row.orgId,
785
+ communityId: row.communityId,
786
+ digest
787
+ }] : [];
788
+ });
789
+ }
790
+ async patchCommunitySettings(orgId, id, input) {
619
791
  if (!await this.findCommunityById(orgId, id)) return null;
620
- const key = and(eq(settings.orgId, orgId), eq(settings.namespace, SETTINGS_NAMESPACE), eq(settings.scopeId, id));
621
- await this.tx(async (tx) => {
792
+ const { digest, ...dm } = input;
793
+ await this.patchSettings(orgId, id, digest === void 0 ? { dm } : {
794
+ dm,
795
+ digest
796
+ });
797
+ return this.findCommunityById(orgId, id);
798
+ }
799
+ async patchSettings(orgId, scopeId, patch) {
800
+ const key = and(eq(settings.orgId, orgId), eq(settings.namespace, SETTINGS_NAMESPACE), eq(settings.scopeId, scopeId));
801
+ const value = patch;
802
+ return this.tx(async (tx) => {
622
803
  let [existing] = await tx.select().from(settings).where(key).for("update");
623
- const value = { dm: patch };
624
804
  if (!existing) {
625
805
  const [inserted] = await tx.insert(settings).values({
626
806
  orgId,
627
807
  namespace: SETTINGS_NAMESPACE,
628
- scopeId: id,
808
+ scopeId,
629
809
  value
630
810
  }).onConflictDoNothing().returning();
631
- if (inserted) return;
811
+ if (inserted) return inserted.value;
632
812
  [existing] = await tx.select().from(settings).where(key).for("update");
633
- if (!existing) throw new Error("community settings patch: row vanished mid-transaction");
813
+ if (!existing) throw new Error(`settings patch for ${scopeId}: row vanished mid-transaction`);
634
814
  }
635
- await tx.update(settings).set({
636
- value: deepMerge(existing.value, { dm: patch }),
815
+ const [updated] = await tx.update(settings).set({
816
+ value: deepMerge(existing.value, patch),
637
817
  updatedAt: /* @__PURE__ */ new Date()
638
- }).where(key);
818
+ }).where(key).returning();
819
+ if (!updated) throw new Error(`settings patch for ${scopeId} returned no row`);
820
+ return updated.value;
639
821
  });
640
- return this.findCommunityById(orgId, id);
641
822
  }
642
823
  async deleteCommunity(orgId, id) {
643
824
  return (await this.db.delete(contentItems).where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, id), eq(contentItems.type, "community"))).returning({ id: contentItems.id })).length > 0;
@@ -779,6 +960,6 @@ var DrizzleContentRepository = class {
779
960
  };
780
961
  translateDbErrors(DrizzleContentRepository);
781
962
  //#endregion
782
- export { DrizzleContentRepository };
963
+ export { DrizzleContentRepository, SETTINGS_NAMESPACE };
783
964
 
784
965
  //# sourceMappingURL=content.js.map