@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 +1 @@
1
- {"version":3,"file":"content.js","names":[],"sources":["../../src/repositories/content.ts"],"sourcesContent":["// content — Drizzle repository for the course aggregate root (implements the core\n// outbound `ContentRepository` port). Org-scoped: every method takes the domain\n// `organizations.id` and constrains its queries to that tenant. Rows come back\n// as stored — derived counts and cross-entity composition live in reporting.\nimport {\n eq,\n and,\n sql,\n count,\n asc,\n desc,\n ilike,\n or,\n inArray,\n type SQL,\n type AnyColumn,\n} from \"drizzle-orm\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type { DbExecutor, Tx } from \"../client.js\";\nimport type {\n Activity,\n ActivityAsset,\n AddDownloadAssetInput,\n Bundle,\n BundleItem,\n Community,\n CommunityStatus,\n ContentRepository,\n Course,\n CourseSettings,\n CourseStatus,\n CreateBundleInput,\n CreateCommunityInput,\n CreateCourseInput,\n CreateDownloadInput,\n CreateSpaceInput,\n Download,\n DownloadAsset,\n DownloadStatus,\n ListBundlesQuery,\n ListCommunitiesQuery,\n ListCoursesQuery,\n ListDownloadsQuery,\n Module,\n Page,\n PatchDmSettingsInput,\n SaveActivityInput,\n SetSpaceTopicsInput,\n Space,\n SpaceAccessRule,\n SpaceDetail,\n UpdateBundleInput,\n UpdateCommunityInput,\n UpdateCourseInput,\n UpdateDownloadInput,\n UpdateSpaceInput,\n} from \"@headless-lms/core/content\";\nimport {\n communitySettingsDefaults,\n courseSettingsDefaults,\n spaceSettingsDefaults,\n type Setting,\n} from \"@headless-lms/core/schemas\";\nimport {\n bundles,\n bundleItems,\n communities,\n contentItems,\n courses,\n modules,\n activities,\n activityAssets,\n downloads,\n downloadAssets,\n spaceLinks,\n spaceTopics,\n spaces,\n} from \"../schema/content.js\";\nimport { settings } from \"../schema/settings.js\";\nimport { posts } from \"../schema/discussion.js\";\nimport { deepMerge } from \"./settings.js\";\nimport { genId } from \"@headless-lms/core/shared/id\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport { NotFoundError, ConflictError } from \"@headless-lms/core/shared/errors\";\nimport { isUniqueViolation, pgErrorFields, translateDbErrors } from \"./pg-errors.js\";\n\nconst LIBRARY_ONLY = \"An activity can only use ready files from the media library\";\nconst MISSING_ASSET_DETAIL = /^Key \\(org_id, asset_id\\)=\\((?:[^,]+), (.+?)\\) is not present/;\n\nfunction refusedLink(err: unknown): ConflictError | undefined {\n const pg = pgErrorFields(err);\n if (pg?.code !== \"23503\") {\n return undefined;\n }\n const missing = pg.detail?.match(MISSING_ASSET_DETAIL)?.[1];\n const message = missing ? `${LIBRARY_ONLY}. Not available: ${missing}` : LIBRARY_ONLY;\n return Object.assign(new ConflictError(message), { cause: err });\n}\n\n// A course's settings live in the shared `settings` table (namespace\n// 'content', scoped by course id) — joined here so the repository answers in\n// whole courses regardless of where the pieces are stored.\nconst SETTINGS_NAMESPACE = \"content\";\n\nconst courseSettingsJoin = and(\n eq(settings.orgId, courses.orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, courses.id),\n);\n\nconst communitySettingsJoin = and(\n eq(settings.orgId, communities.orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, communities.id),\n);\n\nfunction toCommunity(\n row: typeof communities.$inferSelect,\n storedSettings: Record<string, unknown> | null,\n): Community {\n const raw = storedSettings?.dm;\n const stored =\n raw && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n return {\n ...row,\n status: row.status as CommunityStatus,\n settings: { dm: { ...communitySettingsDefaults.dm, ...stored } as Community[\"settings\"][\"dm\"] },\n };\n}\n\nfunction toSpace(row: typeof spaces.$inferSelect): Space {\n return {\n ...row,\n accessRule: row.accessRule as SpaceAccessRule,\n settings: { ...spaceSettingsDefaults, ...row.settings },\n };\n}\n\nconst communitySortColumns: Record<string, AnyColumn | SQL> = {\n title: communities.title,\n slug: communities.slug,\n status: communities.status,\n createdAt: communities.createdAt,\n updatedAt: communities.updatedAt,\n};\n\nfunction toCourse(\n row: typeof courses.$inferSelect,\n storedSettings: Record<string, unknown> | null,\n): Course {\n return {\n ...row,\n status: row.status as CourseStatus,\n settings: { ...courseSettingsDefaults, ...storedSettings } as Course[\"settings\"],\n };\n}\n\n// Columns a caller may sort by. Default falls back to createdAt desc.\nconst sortColumns: Record<string, AnyColumn | SQL> = {\n title: courses.title,\n slug: courses.slug,\n status: courses.status,\n category: courses.category,\n createdAt: courses.createdAt,\n updatedAt: courses.updatedAt,\n};\n\nconst downloadSortColumns: Record<string, AnyColumn | SQL> = {\n title: downloads.title,\n slug: downloads.slug,\n status: downloads.status,\n category: downloads.category,\n createdAt: downloads.createdAt,\n updatedAt: downloads.updatedAt,\n};\n\nfunction toDownload(row: typeof downloads.$inferSelect): Download {\n return { ...row, status: row.status as DownloadStatus };\n}\n\nconst bundleSortColumns: Record<string, AnyColumn | SQL> = {\n name: bundles.name,\n createdAt: bundles.createdAt,\n updatedAt: bundles.updatedAt,\n};\n\nexport class DrizzleContentRepository implements ContentRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n /** On the root db this opens a transaction; tx-bound (ContentUnitOfWork) it\n * nests as a savepoint inside the surrounding transaction. */\n private tx<T>(fn: (tx: Tx) => Promise<T>): Promise<T> {\n return (this.db as NodePgDatabase).transaction(fn);\n }\n\n async listCourses(orgId: string, query: ListCoursesQuery): Promise<Page<Course>> {\n const conditions: SQL[] = [eq(courses.orgId, orgId)];\n if (query.status) {\n conditions.push(eq(courses.status, query.status));\n }\n if (query.category) {\n conditions.push(eq(courses.category, query.category));\n }\n\n const search = query.search?.trim();\n if (search) {\n const like = `%${search}%`;\n const match = or(ilike(courses.title, like), ilike(courses.category, like));\n if (match) {\n conditions.push(match);\n }\n }\n\n const where = and(...conditions);\n\n // Resolve sort: `-` prefix = desc, default createdAt desc.\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in sortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = sortColumns[sortKey] ?? courses.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n\n const rows = await this.db\n .select({ course: courses, settingsValue: settings.value })\n .from(courses)\n .leftJoin(settings, courseSettingsJoin)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const [totalRow] = await this.db.select({ value: count() }).from(courses).where(where);\n\n return {\n rows: rows.map((row) => toCourse(row.course, row.settingsValue)),\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findCourseById(orgId: string, id: string): Promise<Course | null> {\n const [row] = await this.db\n .select({ course: courses, settingsValue: settings.value })\n .from(courses)\n .leftJoin(settings, courseSettingsJoin)\n .where(and(eq(courses.orgId, orgId), eq(courses.id, id)))\n .limit(1);\n return row ? toCourse(row.course, row.settingsValue) : null;\n }\n\n async createCourse(orgId: string, input: CreateCourseInput, slug: string): Promise<Course> {\n // Registry row + concrete row share one id. Both inserts run on the same\n // executor — mutations reach this repository tx-bound (ContentUnitOfWork),\n // so they commit or roll back together.\n const id = genId(\"course\");\n await this.db.insert(contentItems).values({ orgId, id, type: \"course\" });\n const [inserted] = await this.db\n .insert(courses)\n .values({\n orgId,\n id,\n title: input.title,\n slug,\n description: input.description ?? \"\",\n category: input.category ?? \"\",\n })\n .returning({ id: courses.id });\n if (!inserted) {\n throw new Error(\"failed to insert course\");\n }\n const created = await this.findCourseById(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created course\");\n }\n return created;\n }\n\n async updateCourse(orgId: string, id: string, patch: UpdateCourseInput): Promise<Course | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.title !== undefined) {\n set.title = patch.title;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.category !== undefined) {\n set.category = patch.category;\n }\n if (patch.status !== undefined) {\n set.status = patch.status;\n }\n // null is meaningful here — it clears the cover — so only `undefined` skips.\n if (patch.thumbnailAssetId !== undefined) {\n set.thumbnailAssetId = patch.thumbnailAssetId;\n }\n\n const [updated] = await this.db\n .update(courses)\n .set(set)\n .where(and(eq(courses.orgId, orgId), eq(courses.id, id)))\n .returning({ id: courses.id });\n if (!updated) {\n return null;\n }\n return this.findCourseById(orgId, id);\n }\n\n async patchCourseSettings(\n orgId: string,\n id: string,\n value: Partial<CourseSettings>,\n ): Promise<CourseSettings> {\n const key = and(\n eq(settings.orgId, orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, id),\n );\n\n // Merge in JS under a row lock, same as the settings repository: the read\n // and the write must be one atomic unit or concurrent patches would\n // clobber each other's sibling keys.\n const merged = await this.tx(async (tx) => {\n let [existing] = await tx.select().from(settings).where(key).for(\"update\");\n\n if (!existing) {\n const [inserted] = await tx\n .insert(settings)\n .values({\n orgId,\n namespace: SETTINGS_NAMESPACE,\n scopeId: id,\n value: value as Setting[\"value\"],\n })\n .onConflictDoNothing()\n .returning();\n if (inserted) {\n return inserted.value;\n }\n [existing] = await tx.select().from(settings).where(key).for(\"update\");\n if (!existing) {\n throw new Error(\"course settings patch: row vanished mid-transaction\");\n }\n }\n\n const [updated] = await tx\n .update(settings)\n .set({ value: deepMerge(existing.value, value) as Setting[\"value\"], updatedAt: new Date() })\n .where(key)\n .returning();\n if (!updated) {\n throw new Error(\"course settings patch returned no row\");\n }\n return updated.value;\n });\n\n return { ...courseSettingsDefaults, ...merged } as CourseSettings;\n }\n\n async deleteCourse(orgId: string, id: string): Promise<boolean> {\n // Deletes go through the registry: cascades to the course row and its\n // entitlements in one statement. Never delete from `courses` directly —\n // that would strand the registry row.\n const deleted = await this.db\n .delete(contentItems)\n .where(\n and(\n eq(contentItems.orgId, orgId),\n eq(contentItems.id, id),\n eq(contentItems.type, \"course\"),\n ),\n )\n .returning({ id: contentItems.id });\n return deleted.length > 0;\n }\n\n // --- modules & activities ----------------------------------------------\n // Every mutation returns the course's full ordered module list (by `seq`),\n // so `listModules` runs at the end of every mutation within the same\n // transaction. An Activity's media is the many-to-many `activity_assets`\n // join: `saveActivity` upserts the row and replaces its asset links; deletes\n // drop the links first (they FK the activity), then the activity rows.\n\n listCourseModules(orgId: string, courseId: string): Promise<Module[]> {\n return this.tx((tx) => this.listModules(tx, orgId, courseId));\n }\n\n async listActivitiesForCourse(orgId: string, courseId: string): Promise<Activity[]> {\n return this.db\n .select()\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.courseId, courseId)))\n .orderBy(activities.moduleId, activities.seq);\n }\n\n // Bare join-table rows; the activities join only scopes them to the course.\n async listActivityAssetsForCourse(orgId: string, courseId: string): Promise<ActivityAsset[]> {\n return this.db\n .select({\n orgId: activityAssets.orgId,\n id: activityAssets.id,\n activityId: activityAssets.activityId,\n assetId: activityAssets.assetId,\n seq: activityAssets.seq,\n createdAt: activityAssets.createdAt,\n updatedAt: activityAssets.updatedAt,\n })\n .from(activityAssets)\n .innerJoin(\n activities,\n and(\n eq(activities.orgId, activityAssets.orgId),\n eq(activities.id, activityAssets.activityId),\n ),\n )\n .where(and(eq(activityAssets.orgId, orgId), eq(activities.courseId, courseId)))\n .orderBy(activityAssets.activityId, activityAssets.seq);\n }\n\n async findActivity(orgId: string, activityId: string): Promise<Activity | null> {\n const [row] = await this.db\n .select()\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.id, activityId)))\n .limit(1);\n return row ?? null;\n }\n\n async findModule(orgId: string, moduleId: string): Promise<Module | null> {\n const [row] = await this.db\n .select()\n .from(modules)\n .where(and(eq(modules.orgId, orgId), eq(modules.id, moduleId)))\n .limit(1);\n return row ?? null;\n }\n\n /** The course's full ordered module list. */\n private async listModules(tx: Tx, orgId: string, courseId: string): Promise<Module[]> {\n return tx\n .select()\n .from(modules)\n .where(and(eq(modules.orgId, orgId), eq(modules.courseId, courseId)))\n .orderBy(modules.seq);\n }\n\n /** Assert the module belongs to the org + course; throw otherwise. */\n private async assertModule(\n tx: Tx,\n orgId: string,\n courseId: string,\n moduleId: string,\n ): Promise<void> {\n const [row] = await tx\n .select({ id: modules.id })\n .from(modules)\n .where(\n and(eq(modules.orgId, orgId), eq(modules.id, moduleId), eq(modules.courseId, courseId)),\n )\n .limit(1);\n if (!row) {\n throw new NotFoundError(\"Module\", moduleId);\n }\n }\n\n /** Replace an activity's ordered asset links with `assetIds`. */\n private async replaceActivityAssets(\n tx: Tx,\n orgId: string,\n activityId: string,\n assetIds: string[],\n ): Promise<void> {\n await tx\n .delete(activityAssets)\n .where(and(eq(activityAssets.orgId, orgId), eq(activityAssets.activityId, activityId)));\n if (assetIds.length) {\n try {\n await tx\n .insert(activityAssets)\n .values(assetIds.map((assetId, i) => ({ orgId, activityId, assetId, seq: i })));\n } catch (err) {\n throw refusedLink(err) ?? err;\n }\n }\n }\n\n createModule(orgId: string, courseId: string, title: string): Promise<Module[]> {\n return this.tx(async (tx) => {\n const existing = await tx\n .select({ seq: modules.seq })\n .from(modules)\n .where(and(eq(modules.orgId, orgId), eq(modules.courseId, courseId)));\n const nextSeq = existing.reduce((max, r) => Math.max(max, r.seq), -1) + 1;\n await tx.insert(modules).values({ orgId, courseId, title, seq: nextSeq });\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n updateModule(\n orgId: string,\n courseId: string,\n moduleId: string,\n title: string,\n ): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n await tx\n .update(modules)\n .set({ title })\n .where(\n and(eq(modules.orgId, orgId), eq(modules.id, moduleId), eq(modules.courseId, courseId)),\n );\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n deleteModule(orgId: string, courseId: string, moduleId: string): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n\n // Drop the module's activities: their asset links first (they FK the\n // activity), then the activity rows, then the module.\n const activityRows = await tx\n .select({ id: activities.id })\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.moduleId, moduleId)));\n const activityIds = activityRows.map((a) => a.id);\n\n if (activityIds.length) {\n await tx\n .delete(activityAssets)\n .where(\n and(eq(activityAssets.orgId, orgId), inArray(activityAssets.activityId, activityIds)),\n );\n await tx\n .delete(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.moduleId, moduleId)));\n }\n\n await tx\n .delete(modules)\n .where(\n and(eq(modules.orgId, orgId), eq(modules.id, moduleId), eq(modules.courseId, courseId)),\n );\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n reorderModules(orgId: string, courseId: string, orderedIds: string[]): Promise<Module[]> {\n return this.tx(async (tx) => {\n // Two-phase to dodge the unique(org_id, id) is fine, but modules have no\n // unique(seq); still, park then assign to stay consistent with activities.\n for (let i = 0; i < orderedIds.length; i++) {\n await tx\n .update(modules)\n .set({ seq: i })\n .where(\n and(\n eq(modules.orgId, orgId),\n eq(modules.courseId, courseId),\n eq(modules.id, orderedIds[i]!),\n ),\n );\n }\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n reorderActivities(\n orgId: string,\n courseId: string,\n moduleId: string,\n orderedIds: string[],\n ): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n // Two-phase to dodge the unique(org_id, module_id, seq) constraint mid-swap:\n // park rows at negative seqs, then assign the final 0..n-1.\n for (let i = 0; i < orderedIds.length; i++) {\n await tx\n .update(activities)\n .set({ seq: -(i + 1) })\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.moduleId, moduleId),\n eq(activities.id, orderedIds[i]!),\n ),\n );\n }\n for (let i = 0; i < orderedIds.length; i++) {\n await tx\n .update(activities)\n .set({ seq: i })\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.moduleId, moduleId),\n eq(activities.id, orderedIds[i]!),\n ),\n );\n }\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n saveActivity(\n orgId: string,\n courseId: string,\n moduleId: string,\n input: SaveActivityInput,\n activityId?: string,\n ): Promise<{ modules: Module[]; activity: Activity }> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n\n let savedId: string;\n if (activityId) {\n const [existing] = await tx\n .select({ id: activities.id })\n .from(activities)\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.id, activityId),\n eq(activities.moduleId, moduleId),\n ),\n )\n .limit(1);\n if (!existing) {\n throw new NotFoundError(\"Activity\", activityId);\n }\n\n await tx\n .update(activities)\n .set({ settings: input.settings ?? null })\n .where(and(eq(activities.orgId, orgId), eq(activities.id, activityId)));\n if (input.assetIds !== undefined) {\n await this.replaceActivityAssets(tx, orgId, activityId, input.assetIds);\n }\n savedId = activityId;\n } else {\n const existing = await tx\n .select({ seq: activities.seq })\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.moduleId, moduleId)));\n const nextSeq = existing.reduce((max, r) => Math.max(max, r.seq), -1) + 1;\n\n const [ins] = await tx\n .insert(activities)\n .values({ orgId, moduleId, courseId, seq: nextSeq, settings: input.settings ?? null })\n .returning({ id: activities.id });\n if (!ins) {\n throw new Error(\"failed to insert activity\");\n }\n await this.replaceActivityAssets(tx, orgId, ins.id, input.assetIds ?? []);\n savedId = ins.id;\n }\n\n const [activity] = await tx\n .select()\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.id, savedId)))\n .limit(1);\n if (!activity) {\n throw new Error(\"failed to load saved activity\");\n }\n return { modules: await this.listModules(tx, orgId, courseId), activity };\n });\n }\n\n deleteActivity(\n orgId: string,\n courseId: string,\n moduleId: string,\n activityId: string,\n ): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n\n // Drop the asset links first (they FK the activity), then the activity.\n await tx\n .delete(activityAssets)\n .where(and(eq(activityAssets.orgId, orgId), eq(activityAssets.activityId, activityId)));\n await tx\n .delete(activities)\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.id, activityId),\n eq(activities.moduleId, moduleId),\n ),\n );\n\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n // --- downloads --------------------------------------------------------\n\n async listDownloads(orgId: string, query: ListDownloadsQuery): Promise<Page<Download>> {\n const conditions: SQL[] = [eq(downloads.orgId, orgId)];\n if (query.status) {\n conditions.push(eq(downloads.status, query.status));\n }\n if (query.category) {\n conditions.push(eq(downloads.category, query.category));\n }\n\n const search = query.search?.trim();\n if (search) {\n const like = `%${search}%`;\n const match = or(ilike(downloads.title, like), ilike(downloads.category, like));\n if (match) {\n conditions.push(match);\n }\n }\n\n const where = and(...conditions);\n\n // Resolve sort: `-` prefix = desc, default createdAt desc.\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in downloadSortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = downloadSortColumns[sortKey] ?? downloads.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n\n const rows = await this.db\n .select()\n .from(downloads)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const [totalRow] = await this.db.select({ value: count() }).from(downloads).where(where);\n\n return {\n rows: rows.map(toDownload),\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async getDownload(orgId: string, id: string): Promise<Download | null> {\n const [row] = await this.db\n .select()\n .from(downloads)\n .where(and(eq(downloads.orgId, orgId), eq(downloads.id, id)))\n .limit(1);\n return row ? toDownload(row) : null;\n }\n\n async createDownload(orgId: string, input: CreateDownloadInput, slug: string): Promise<Download> {\n // Registry row + concrete row share one id, same as courses. Both inserts\n // run on the same executor — mutations reach this repository tx-bound.\n const id = genId(\"download\");\n await this.db.insert(contentItems).values({ orgId, id, type: \"download\" });\n await this.db.insert(downloads).values({\n orgId,\n id,\n title: input.title,\n slug,\n description: input.description ?? \"\",\n category: input.category ?? \"\",\n });\n const created = await this.getDownload(orgId, id);\n if (!created) {\n throw new Error(\"failed to load created download\");\n }\n return created;\n }\n\n async updateDownload(\n orgId: string,\n id: string,\n patch: UpdateDownloadInput,\n ): Promise<Download | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.title !== undefined) {\n set.title = patch.title;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.category !== undefined) {\n set.category = patch.category;\n }\n if (patch.status !== undefined) {\n set.status = patch.status;\n }\n if (patch.thumbnailAssetId !== undefined) {\n set.thumbnailAssetId = patch.thumbnailAssetId;\n }\n\n const [updated] = await this.db\n .update(downloads)\n .set(set)\n .where(and(eq(downloads.orgId, orgId), eq(downloads.id, id)))\n .returning({ id: downloads.id });\n if (!updated) {\n return null;\n }\n return this.getDownload(orgId, id);\n }\n\n async deleteDownload(orgId: string, id: string): Promise<boolean> {\n // Through the registry, so the cascade reaches the download row, its asset\n // links, and the grants. Never delete from `downloads` directly.\n const deleted = await this.db\n .delete(contentItems)\n .where(\n and(\n eq(contentItems.orgId, orgId),\n eq(contentItems.id, id),\n eq(contentItems.type, \"download\"),\n ),\n )\n .returning({ id: contentItems.id });\n return deleted.length > 0;\n }\n\n async listDownloadAssets(orgId: string, downloadId: string): Promise<DownloadAsset[]> {\n return this.db\n .select()\n .from(downloadAssets)\n .where(and(eq(downloadAssets.orgId, orgId), eq(downloadAssets.downloadId, downloadId)))\n .orderBy(asc(downloadAssets.seq));\n }\n\n /** Assert the download exists in the org; throw otherwise. */\n private async assertDownload(orgId: string, downloadId: string): Promise<void> {\n const [row] = await this.db\n .select({ id: downloads.id })\n .from(downloads)\n .where(and(eq(downloads.orgId, orgId), eq(downloads.id, downloadId)))\n .limit(1);\n if (!row) {\n throw new NotFoundError(\"Download\", downloadId);\n }\n }\n\n async addDownloadAsset(\n orgId: string,\n downloadId: string,\n input: AddDownloadAssetInput,\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n const [maxRow] = await this.db\n .select({ maxSeq: sql<number | null>`max(${downloadAssets.seq})` })\n .from(downloadAssets)\n .where(and(eq(downloadAssets.orgId, orgId), eq(downloadAssets.downloadId, downloadId)));\n const nextSeq = (maxRow?.maxSeq ?? -1) + 1;\n try {\n await this.db.insert(downloadAssets).values({\n orgId,\n downloadId,\n assetId: input.assetId,\n seq: nextSeq,\n displayName: input.displayName ?? null,\n });\n } catch (err) {\n if (isUniqueViolation(err)) {\n throw new ConflictError(\"This asset is already linked to the download\");\n }\n throw err;\n }\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n async removeDownloadAsset(\n orgId: string,\n downloadId: string,\n assetId: string,\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n await this.db\n .delete(downloadAssets)\n .where(\n and(\n eq(downloadAssets.orgId, orgId),\n eq(downloadAssets.downloadId, downloadId),\n eq(downloadAssets.assetId, assetId),\n ),\n );\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n async renameDownloadAsset(\n orgId: string,\n downloadId: string,\n assetId: string,\n displayName: string | null,\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n await this.db\n .update(downloadAssets)\n .set({ displayName })\n .where(\n and(\n eq(downloadAssets.orgId, orgId),\n eq(downloadAssets.downloadId, downloadId),\n eq(downloadAssets.assetId, assetId),\n ),\n );\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n async reorderDownloadAssets(\n orgId: string,\n downloadId: string,\n assetIds: string[],\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n // The service has already verified this is exactly the current set.\n for (const [seq, assetId] of assetIds.entries()) {\n await this.db\n .update(downloadAssets)\n .set({ seq })\n .where(\n and(\n eq(downloadAssets.orgId, orgId),\n eq(downloadAssets.downloadId, downloadId),\n eq(downloadAssets.assetId, assetId),\n ),\n );\n }\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n // --- bundles ----------------------------------------------------------\n\n async listBundles(orgId: string, query: ListBundlesQuery): Promise<Page<Bundle>> {\n const conditions: SQL[] = [eq(bundles.orgId, orgId)];\n\n const search = query.search?.trim();\n if (search) {\n conditions.push(ilike(bundles.name, `%${search}%`));\n }\n\n const where = and(...conditions);\n\n // Resolve sort: `-` prefix = desc, default createdAt desc.\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in bundleSortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = bundleSortColumns[sortKey] ?? bundles.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n\n const rows = await this.db\n .select()\n .from(bundles)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const [totalRow] = await this.db.select({ value: count() }).from(bundles).where(where);\n\n return {\n rows,\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findBundleById(orgId: string, id: string): Promise<Bundle | null> {\n const [row] = await this.db\n .select()\n .from(bundles)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, id)))\n .limit(1);\n return row ?? null;\n }\n\n /** The bundle and its initial items are one write: an unknown content id\n * rolls the bundle back rather than leaving it created and half-filled. */\n async createBundle(orgId: string, input: CreateBundleInput): Promise<Bundle> {\n const contentIds = [...new Set(input.contentIds ?? [])];\n return this.tx(async (tx) => {\n // A bundle is not content: it groups registry rows, so it gets no\n // content_items row of its own.\n const [row] = await tx\n .insert(bundles)\n .values({ orgId, id: genId(\"bundle\"), name: input.name })\n .returning();\n if (!row) {\n throw new Error(\"failed to load created bundle\");\n }\n\n if (contentIds.length > 0) {\n // Against the registry, so any content type can be bundled.\n const found = await tx\n .select({ id: contentItems.id })\n .from(contentItems)\n .where(and(eq(contentItems.orgId, orgId), inArray(contentItems.id, contentIds)));\n const known = new Set(found.map((item) => item.id));\n const missing = contentIds.find((id) => !known.has(id));\n if (missing) {\n throw new NotFoundError(\"Content\", missing);\n }\n\n await tx\n .insert(bundleItems)\n .values(contentIds.map((contentId) => ({ orgId, bundleId: row.id, contentId })));\n }\n\n return row;\n });\n }\n\n async updateBundle(orgId: string, id: string, patch: UpdateBundleInput): Promise<Bundle | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.name !== undefined) {\n set.name = patch.name;\n }\n\n const [row] = await this.db\n .update(bundles)\n .set(set)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, id)))\n .returning();\n return row ?? null;\n }\n\n async deleteBundle(orgId: string, id: string): Promise<boolean> {\n // Cascades to the bundle's items and to the grants made on it. The content\n // itself is untouched — a bundle only groups registry rows.\n const deleted = await this.db\n .delete(bundles)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, id)))\n .returning({ id: bundles.id });\n return deleted.length > 0;\n }\n\n async listBundleItems(orgId: string, bundleId: string): Promise<BundleItem[]> {\n return this.db\n .select()\n .from(bundleItems)\n .where(and(eq(bundleItems.orgId, orgId), eq(bundleItems.bundleId, bundleId)))\n .orderBy(asc(bundleItems.createdAt));\n }\n\n /** Assert the bundle exists in the org; throw otherwise. */\n private async assertBundle(orgId: string, bundleId: string): Promise<void> {\n const [row] = await this.db\n .select({ id: bundles.id })\n .from(bundles)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, bundleId)))\n .limit(1);\n if (!row) {\n throw new NotFoundError(\"Bundle\", bundleId);\n }\n }\n\n async addBundleItem(orgId: string, bundleId: string, contentId: string): Promise<BundleItem[]> {\n await this.assertBundle(orgId, bundleId);\n // Against the registry, so any content type can be bundled.\n const [item] = await this.db\n .select({ id: contentItems.id })\n .from(contentItems)\n .where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, contentId)))\n .limit(1);\n if (!item) {\n throw new NotFoundError(\"Content\", contentId);\n }\n await this.db\n .insert(bundleItems)\n .values({ orgId, bundleId, contentId })\n .onConflictDoNothing({\n target: [bundleItems.orgId, bundleItems.bundleId, bundleItems.contentId],\n });\n return this.listBundleItems(orgId, bundleId);\n }\n\n async removeBundleItem(\n orgId: string,\n bundleId: string,\n contentId: string,\n ): Promise<BundleItem[]> {\n await this.assertBundle(orgId, bundleId);\n await this.db\n .delete(bundleItems)\n .where(\n and(\n eq(bundleItems.orgId, orgId),\n eq(bundleItems.bundleId, bundleId),\n eq(bundleItems.contentId, contentId),\n ),\n );\n return this.listBundleItems(orgId, bundleId);\n }\n\n async listCommunities(orgId: string, query: ListCommunitiesQuery): Promise<Page<Community>> {\n const conditions: SQL[] = [eq(communities.orgId, orgId)];\n if (query.status) {\n conditions.push(eq(communities.status, query.status));\n }\n const search = query.search?.trim();\n if (search) {\n const like = `%${search}%`;\n const match = or(ilike(communities.title, like), ilike(communities.description, like));\n if (match) {\n conditions.push(match);\n }\n }\n const where = and(...conditions);\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in communitySortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = communitySortColumns[sortKey] ?? communities.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n const rows = await this.db\n .select({ community: communities, settingsValue: settings.value })\n .from(communities)\n .leftJoin(settings, communitySettingsJoin)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n const [totalRow] = await this.db.select({ value: count() }).from(communities).where(where);\n return {\n rows: rows.map((row) => toCommunity(row.community, row.settingsValue)),\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findCommunityById(orgId: string, id: string): Promise<Community | null> {\n const [row] = await this.db\n .select({ community: communities, settingsValue: settings.value })\n .from(communities)\n .leftJoin(settings, communitySettingsJoin)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, id)))\n .limit(1);\n return row ? toCommunity(row.community, row.settingsValue) : null;\n }\n\n async findCommunitiesByIds(orgId: string, ids: string[]): Promise<Community[]> {\n const rows = await this.db\n .select({ community: communities, settingsValue: settings.value })\n .from(communities)\n .leftJoin(settings, communitySettingsJoin)\n .where(and(eq(communities.orgId, orgId), inArray(communities.id, ids)))\n .orderBy(asc(communities.title), asc(communities.id));\n return rows.map((row) => toCommunity(row.community, row.settingsValue));\n }\n\n async listPublishedCommunityIds(orgId: string): Promise<string[]> {\n const rows = await this.db\n .select({ id: communities.id })\n .from(communities)\n .where(and(eq(communities.orgId, orgId), eq(communities.status, \"published\")))\n .orderBy(asc(communities.createdAt), asc(communities.id));\n return rows.map((row) => row.id);\n }\n\n async createCommunity(\n orgId: string,\n input: CreateCommunityInput,\n slug: string,\n ): Promise<Community> {\n const id = genId(\"community\");\n await this.db.insert(contentItems).values({ orgId, id, type: \"community\" });\n const [inserted] = await this.db\n .insert(communities)\n .values({\n orgId,\n id,\n title: input.title,\n slug,\n description: input.description ?? \"\",\n })\n .returning({ id: communities.id });\n if (!inserted) {\n throw new Error(\"failed to insert community\");\n }\n const created = await this.findCommunityById(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created community\");\n }\n return created;\n }\n\n async updateCommunity(\n orgId: string,\n id: string,\n patch: UpdateCommunityInput,\n ): Promise<Community | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.title !== undefined) {\n set.title = patch.title;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.status !== undefined) {\n set.status = patch.status;\n }\n if (patch.thumbnailAssetId !== undefined) {\n set.thumbnailAssetId = patch.thumbnailAssetId;\n }\n const [updated] = await this.db\n .update(communities)\n .set(set)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, id)))\n .returning({ id: communities.id });\n if (!updated) {\n return null;\n }\n return this.findCommunityById(orgId, id);\n }\n\n async patchDmSettings(\n orgId: string,\n id: string,\n patch: PatchDmSettingsInput,\n ): Promise<Community | null> {\n const community = await this.findCommunityById(orgId, id);\n if (!community) {\n return null;\n }\n const key = and(\n eq(settings.orgId, orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, id),\n );\n await this.tx(async (tx) => {\n let [existing] = await tx.select().from(settings).where(key).for(\"update\");\n const value = { dm: patch } as Setting[\"value\"];\n if (!existing) {\n const [inserted] = await tx\n .insert(settings)\n .values({ orgId, namespace: SETTINGS_NAMESPACE, scopeId: id, value })\n .onConflictDoNothing()\n .returning();\n if (inserted) {\n return;\n }\n [existing] = await tx.select().from(settings).where(key).for(\"update\");\n if (!existing) {\n throw new Error(\"community settings patch: row vanished mid-transaction\");\n }\n }\n await tx\n .update(settings)\n .set({\n value: deepMerge(existing.value, { dm: patch }) as Setting[\"value\"],\n updatedAt: new Date(),\n })\n .where(key);\n });\n return this.findCommunityById(orgId, id);\n }\n\n async deleteCommunity(orgId: string, id: string): Promise<boolean> {\n const deleted = await this.db\n .delete(contentItems)\n .where(\n and(\n eq(contentItems.orgId, orgId),\n eq(contentItems.id, id),\n eq(contentItems.type, \"community\"),\n ),\n )\n .returning({ id: contentItems.id });\n return deleted.length > 0;\n }\n\n async deleteCommunitySettings(orgId: string, id: string): Promise<void> {\n await this.db\n .delete(settings)\n .where(\n and(\n eq(settings.orgId, orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, id),\n ),\n );\n }\n\n async listSpaces(orgId: string, communityId: string): Promise<SpaceDetail[]> {\n const rows = await this.db\n .select()\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.communityId, communityId)))\n .orderBy(asc(spaces.seq));\n return this.withSpaceParts(orgId, rows.map(toSpace));\n }\n\n async findSpace(orgId: string, spaceId: string): Promise<SpaceDetail | null> {\n const [row] = await this.db\n .select()\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId)))\n .limit(1);\n if (!row) {\n return null;\n }\n const [detail] = await this.withSpaceParts(orgId, [toSpace(row)]);\n return detail ?? null;\n }\n\n async lockSpace(orgId: string, spaceId: string): Promise<Space | null> {\n const [row] = await this.db\n .select()\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId)))\n .for(\"update\")\n .limit(1);\n return row ? toSpace(row) : null;\n }\n\n async createSpace(\n orgId: string,\n communityId: string,\n input: CreateSpaceInput,\n ): Promise<SpaceDetail> {\n const existing = await this.db\n .select({ seq: spaces.seq })\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.communityId, communityId)));\n const nextSeq = existing.reduce((max, row) => Math.max(max, row.seq), -1) + 1;\n const [inserted] = await this.db\n .insert(spaces)\n .values({\n orgId,\n communityId,\n name: input.name,\n description: input.description ?? \"\",\n seq: nextSeq,\n accessRule: input.accessRule ?? \"all_members\",\n capacity: input.capacity ?? null,\n settings: { ...spaceSettingsDefaults, ...input.settings },\n })\n .returning({ id: spaces.id });\n if (!inserted) {\n throw new Error(\"failed to insert space\");\n }\n const created = await this.findSpace(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created space\");\n }\n return created;\n }\n\n async updateSpace(\n orgId: string,\n communityId: string,\n spaceId: string,\n patch: UpdateSpaceInput,\n ): Promise<SpaceDetail | 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.accessRule !== undefined) {\n set.accessRule = patch.accessRule;\n }\n if (patch.capacity !== undefined) {\n set.capacity = patch.capacity;\n }\n if (patch.settings !== undefined) {\n set.settings = { ...spaceSettingsDefaults, ...patch.settings };\n }\n const [updated] = await this.db\n .update(spaces)\n .set(set)\n .where(\n and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId), eq(spaces.communityId, communityId)),\n )\n .returning({ id: spaces.id });\n if (!updated) {\n return null;\n }\n return this.findSpace(orgId, spaceId);\n }\n\n async deleteSpace(orgId: string, communityId: string, spaceId: string): Promise<boolean> {\n const deleted = await this.db\n .delete(spaces)\n .where(\n and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId), eq(spaces.communityId, communityId)),\n )\n .returning({ id: spaces.id });\n return deleted.length > 0;\n }\n\n async reorderSpaces(\n orgId: string,\n communityId: string,\n orderedIds: string[],\n ): Promise<SpaceDetail[]> {\n for (let i = 0; i < orderedIds.length; i++) {\n await this.db\n .update(spaces)\n .set({ seq: i })\n .where(\n and(\n eq(spaces.orgId, orgId),\n eq(spaces.communityId, communityId),\n eq(spaces.id, orderedIds[i]!),\n ),\n );\n }\n return this.listSpaces(orgId, communityId);\n }\n\n async setSpaceTopics(\n orgId: string,\n spaceId: string,\n input: SetSpaceTopicsInput,\n ): Promise<SpaceDetail> {\n await this.tx(async (tx) => {\n const existing = await tx\n .select({ id: spaceTopics.id })\n .from(spaceTopics)\n .where(and(eq(spaceTopics.orgId, orgId), eq(spaceTopics.spaceId, spaceId)))\n .for(\"update\");\n const kept = new Set(input.topics.flatMap((topic) => (topic.id ? [topic.id] : [])));\n const known = new Set(existing.map((topic) => topic.id));\n for (const id of kept) {\n if (!known.has(id)) {\n throw new NotFoundError(\"Topic\", id);\n }\n }\n const removed = existing.filter((topic) => !kept.has(topic.id)).map((topic) => topic.id);\n if (removed.length) {\n await tx\n .delete(spaceTopics)\n .where(and(eq(spaceTopics.orgId, orgId), inArray(spaceTopics.id, removed)));\n }\n for (let seq = 0; seq < input.topics.length; seq++) {\n const topic = input.topics[seq]!;\n if (topic.id) {\n await tx\n .update(spaceTopics)\n .set({ name: topic.name, seq, updatedAt: new Date() })\n .where(and(eq(spaceTopics.orgId, orgId), eq(spaceTopics.id, topic.id)));\n } else {\n await tx.insert(spaceTopics).values({ orgId, spaceId, name: topic.name, seq });\n }\n }\n });\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async topicsInUse(orgId: string, spaceId: string, topicIds: string[]): Promise<string[]> {\n if (topicIds.length === 0) {\n return [];\n }\n await this.db\n .select({ id: spaceTopics.id })\n .from(spaceTopics)\n .where(\n and(\n eq(spaceTopics.orgId, orgId),\n eq(spaceTopics.spaceId, spaceId),\n inArray(spaceTopics.id, topicIds),\n ),\n )\n .for(\"update\");\n const rows = await this.db\n .selectDistinct({ topicId: posts.topicId })\n .from(posts)\n .where(\n and(eq(posts.orgId, orgId), eq(posts.spaceId, spaceId), inArray(posts.topicId, topicIds)),\n );\n return rows.flatMap((row) => (row.topicId ? [row.topicId] : []));\n }\n\n async addSpaceLink(orgId: string, spaceId: string, contentId: string): Promise<SpaceDetail> {\n const [item] = await this.db\n .select({ id: contentItems.id, type: contentItems.type })\n .from(contentItems)\n .where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, contentId)))\n .limit(1);\n if (\n !item ||\n (item.type !== \"course\" && item.type !== \"download\" && item.type !== \"community\")\n ) {\n throw new NotFoundError(\"Content\", contentId);\n }\n const existing = await this.db\n .select({ seq: spaceLinks.seq })\n .from(spaceLinks)\n .where(and(eq(spaceLinks.orgId, orgId), eq(spaceLinks.spaceId, spaceId)));\n const nextSeq = existing.reduce((max, row) => Math.max(max, row.seq), -1) + 1;\n await this.db.insert(spaceLinks).values({ orgId, spaceId, contentId, seq: nextSeq });\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async removeSpaceLink(orgId: string, spaceId: string, linkId: string): Promise<SpaceDetail> {\n const deleted = await this.db\n .delete(spaceLinks)\n .where(\n and(\n eq(spaceLinks.orgId, orgId),\n eq(spaceLinks.spaceId, spaceId),\n eq(spaceLinks.id, linkId),\n ),\n )\n .returning({ id: spaceLinks.id });\n if (deleted.length === 0) {\n throw new NotFoundError(\"Link\", linkId);\n }\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async reorderSpaceLinks(\n orgId: string,\n spaceId: string,\n orderedIds: string[],\n ): Promise<SpaceDetail> {\n for (let i = 0; i < orderedIds.length; i++) {\n await this.db\n .update(spaceLinks)\n .set({ seq: i })\n .where(\n and(\n eq(spaceLinks.orgId, orgId),\n eq(spaceLinks.spaceId, spaceId),\n eq(spaceLinks.id, orderedIds[i]!),\n ),\n );\n }\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async spacesLinkingContent(orgId: string, contentId: string): Promise<string[]> {\n const rows = await this.db\n .select({ spaceId: spaceLinks.spaceId })\n .from(spaceLinks)\n .where(and(eq(spaceLinks.orgId, orgId), eq(spaceLinks.contentId, contentId)));\n return [...new Set(rows.map((row) => row.spaceId))];\n }\n\n private async withSpaceParts(orgId: string, rows: Space[]): Promise<SpaceDetail[]> {\n if (rows.length === 0) {\n return [];\n }\n const ids = rows.map((row) => row.id);\n const topics = await this.db\n .select()\n .from(spaceTopics)\n .where(and(eq(spaceTopics.orgId, orgId), inArray(spaceTopics.spaceId, ids)))\n .orderBy(asc(spaceTopics.seq));\n const links = await this.db\n .select()\n .from(spaceLinks)\n .where(and(eq(spaceLinks.orgId, orgId), inArray(spaceLinks.spaceId, ids)))\n .orderBy(asc(spaceLinks.seq));\n return rows.map((space) => ({\n ...space,\n topics: topics.filter((topic) => topic.spaceId === space.id),\n links: links.filter((link) => link.spaceId === space.id),\n }));\n }\n}\ntranslateDbErrors(DrizzleContentRepository);\n"],"mappings":";;;;;;;;;;;AAuFA,MAAM,eAAe;AACrB,MAAM,uBAAuB;AAE7B,SAAS,YAAY,KAAyC;CAC5D,MAAM,KAAK,cAAc,GAAG;CAC5B,IAAI,IAAI,SAAS,SACf;CAEF,MAAM,UAAU,GAAG,QAAQ,MAAM,oBAAoB,CAAC,GAAG;CACzD,MAAM,UAAU,UAAU,GAAG,aAAa,mBAAmB,YAAY;CACzE,OAAO,OAAO,OAAO,IAAI,cAAc,OAAO,GAAG,EAAE,OAAO,IAAI,CAAC;AACjE;AAKA,MAAM,qBAAqB;AAE3B,MAAM,qBAAqB,IACzB,GAAG,SAAS,OAAO,QAAQ,KAAK,GAChC,GAAG,SAAS,WAAW,kBAAkB,GACzC,GAAG,SAAS,SAAS,QAAQ,EAAE,CACjC;AAEA,MAAM,wBAAwB,IAC5B,GAAG,SAAS,OAAO,YAAY,KAAK,GACpC,GAAG,SAAS,WAAW,kBAAkB,GACzC,GAAG,SAAS,SAAS,YAAY,EAAE,CACrC;AAEA,SAAS,YACP,KACA,gBACW;CACX,MAAM,MAAM,gBAAgB;CAC5B,MAAM,SACJ,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,IAAK,MAAkC,CAAC;CAC9F,OAAO;EACL,GAAG;EACH,QAAQ,IAAI;EACZ,UAAU,EAAE,IAAI;GAAE,GAAG,0BAA0B;GAAI,GAAG;EAAO,EAAiC;CAChG;AACF;AAEA,SAAS,QAAQ,KAAwC;CACvD,OAAO;EACL,GAAG;EACH,YAAY,IAAI;EAChB,UAAU;GAAE,GAAG;GAAuB,GAAG,IAAI;EAAS;CACxD;AACF;AAEA,MAAM,uBAAwD;CAC5D,OAAO,YAAY;CACnB,MAAM,YAAY;CAClB,QAAQ,YAAY;CACpB,WAAW,YAAY;CACvB,WAAW,YAAY;AACzB;AAEA,SAAS,SACP,KACA,gBACQ;CACR,OAAO;EACL,GAAG;EACH,QAAQ,IAAI;EACZ,UAAU;GAAE,GAAG;GAAwB,GAAG;EAAe;CAC3D;AACF;AAGA,MAAM,cAA+C;CACnD,OAAO,QAAQ;CACf,MAAM,QAAQ;CACd,QAAQ,QAAQ;CAChB,UAAU,QAAQ;CAClB,WAAW,QAAQ;CACnB,WAAW,QAAQ;AACrB;AAEA,MAAM,sBAAuD;CAC3D,OAAO,UAAU;CACjB,MAAM,UAAU;CAChB,QAAQ,UAAU;CAClB,UAAU,UAAU;CACpB,WAAW,UAAU;CACrB,WAAW,UAAU;AACvB;AAEA,SAAS,WAAW,KAA8C;CAChE,OAAO;EAAE,GAAG;EAAK,QAAQ,IAAI;CAAyB;AACxD;AAEA,MAAM,oBAAqD;CACzD,MAAM,QAAQ;CACd,WAAW,QAAQ;CACnB,WAAW,QAAQ;AACrB;AAEA,IAAa,2BAAb,MAAmE;CAE9C;CACA;CAFnB,YACE,IACA,SAAkC,YAClC;EAFiB,KAAA,KAAA;EACA,KAAA,SAAA;CAChB;;;CAIH,GAAc,IAAwC;EACpD,OAAQ,KAAK,GAAsB,YAAY,EAAE;CACnD;CAEA,MAAM,YAAY,OAAe,OAAgD;EAC/E,MAAM,aAAoB,CAAC,GAAG,QAAQ,OAAO,KAAK,CAAC;EACnD,IAAI,MAAM,QACR,WAAW,KAAK,GAAG,QAAQ,QAAQ,MAAM,MAAM,CAAC;EAElD,IAAI,MAAM,UACR,WAAW,KAAK,GAAG,QAAQ,UAAU,MAAM,QAAQ,CAAC;EAGtD,MAAM,SAAS,MAAM,QAAQ,KAAK;EAClC,IAAI,QAAQ;GACV,MAAM,OAAO,IAAI,OAAO;GACxB,MAAM,QAAQ,GAAG,MAAM,QAAQ,OAAO,IAAI,GAAG,MAAM,QAAQ,UAAU,IAAI,CAAC;GAC1E,IAAI,OACF,WAAW,KAAK,KAAK;EAEzB;EAEA,MAAM,QAAQ,IAAI,GAAG,UAAU;EAG/B,IAAI,UAAU;EACd,IAAI,YAA4B;EAChC,IAAI,MAAM,MAAM;GACd,MAAM,SAAS,MAAM,KAAK,WAAW,GAAG;GACxC,MAAM,MAAM,SAAS,MAAM,KAAK,MAAM,CAAC,IAAI,MAAM;GACjD,IAAI,OAAO,aAAa;IACtB,UAAU;IACV,YAAY,SAAS,SAAS;GAChC;EACF;EACA,MAAM,WAAW,YAAY,YAAY,QAAQ;EACjD,MAAM,UAAU,cAAc,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;EAEpE,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO;GAAE,QAAQ;GAAS,eAAe,SAAS;EAAM,CAAC,CAAC,CAC1D,KAAK,OAAO,CAAC,CACb,SAAS,UAAU,kBAAkB,CAAC,CACtC,MAAM,KAAK,CAAC,CACZ,QAAQ,OAAO,CAAC,CAChB,MAAM,MAAM,QAAQ,CAAC,CACrB,QAAQ,MAAM,OAAO,KAAK,MAAM,QAAQ;EAE3C,MAAM,CAAC,YAAY,MAAM,KAAK,GAAG,OAAO,EAAE,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,MAAM,KAAK;EAErF,OAAO;GACL,MAAM,KAAK,KAAK,QAAQ,SAAS,IAAI,QAAQ,IAAI,aAAa,CAAC;GAC/D,OAAO,UAAU,SAAS;GAC1B,MAAM,MAAM;GACZ,UAAU,MAAM;EAClB;CACF;CAEA,MAAM,eAAe,OAAe,IAAoC;EACtE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO;GAAE,QAAQ;GAAS,eAAe,SAAS;EAAM,CAAC,CAAC,CAC1D,KAAK,OAAO,CAAC,CACb,SAAS,UAAU,kBAAkB,CAAC,CACtC,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CACxD,MAAM,CAAC;EACV,OAAO,MAAM,SAAS,IAAI,QAAQ,IAAI,aAAa,IAAI;CACzD;CAEA,MAAM,aAAa,OAAe,OAA0B,MAA+B;EAIzF,MAAM,KAAK,MAAM,QAAQ;EACzB,MAAM,KAAK,GAAG,OAAO,YAAY,CAAC,CAAC,OAAO;GAAE;GAAO;GAAI,MAAM;EAAS,CAAC;EACvE,MAAM,CAAC,YAAY,MAAM,KAAK,GAC3B,OAAO,OAAO,CAAC,CACf,OAAO;GACN;GACA;GACA,OAAO,MAAM;GACb;GACA,aAAa,MAAM,eAAe;GAClC,UAAU,MAAM,YAAY;EAC9B,CAAC,CAAC,CACD,UAAU,EAAE,IAAI,QAAQ,GAAG,CAAC;EAC/B,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,yBAAyB;EAE3C,MAAM,UAAU,MAAM,KAAK,eAAe,OAAO,SAAS,EAAE;EAC5D,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,+BAA+B;EAEjD,OAAO;CACT;CAEA,MAAM,aAAa,OAAe,IAAY,OAAkD;EAC9F,MAAM,MAA+B,EAAE,2BAAW,IAAI,KAAK,EAAE;EAC7D,IAAI,MAAM,UAAU,KAAA,GAClB,IAAI,QAAQ,MAAM;EAEpB,IAAI,MAAM,gBAAgB,KAAA,GACxB,IAAI,cAAc,MAAM;EAE1B,IAAI,MAAM,aAAa,KAAA,GACrB,IAAI,WAAW,MAAM;EAEvB,IAAI,MAAM,WAAW,KAAA,GACnB,IAAI,SAAS,MAAM;EAGrB,IAAI,MAAM,qBAAqB,KAAA,GAC7B,IAAI,mBAAmB,MAAM;EAG/B,MAAM,CAAC,WAAW,MAAM,KAAK,GAC1B,OAAO,OAAO,CAAC,CACf,IAAI,GAAG,CAAC,CACR,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CACxD,UAAU,EAAE,IAAI,QAAQ,GAAG,CAAC;EAC/B,IAAI,CAAC,SACH,OAAO;EAET,OAAO,KAAK,eAAe,OAAO,EAAE;CACtC;CAEA,MAAM,oBACJ,OACA,IACA,OACyB;EACzB,MAAM,MAAM,IACV,GAAG,SAAS,OAAO,KAAK,GACxB,GAAG,SAAS,WAAW,kBAAkB,GACzC,GAAG,SAAS,SAAS,EAAE,CACzB;EAKA,MAAM,SAAS,MAAM,KAAK,GAAG,OAAO,OAAO;GACzC,IAAI,CAAC,YAAY,MAAM,GAAG,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,QAAQ;GAEzE,IAAI,CAAC,UAAU;IACb,MAAM,CAAC,YAAY,MAAM,GACtB,OAAO,QAAQ,CAAC,CAChB,OAAO;KACN;KACA,WAAW;KACX,SAAS;KACF;IACT,CAAC,CAAC,CACD,oBAAoB,CAAC,CACrB,UAAU;IACb,IAAI,UACF,OAAO,SAAS;IAElB,CAAC,YAAY,MAAM,GAAG,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,QAAQ;IACrE,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,qDAAqD;GAEzE;GAEA,MAAM,CAAC,WAAW,MAAM,GACrB,OAAO,QAAQ,CAAC,CAChB,IAAI;IAAE,OAAO,UAAU,SAAS,OAAO,KAAK;IAAuB,2BAAW,IAAI,KAAK;GAAE,CAAC,CAAC,CAC3F,MAAM,GAAG,CAAC,CACV,UAAU;GACb,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,uCAAuC;GAEzD,OAAO,QAAQ;EACjB,CAAC;EAED,OAAO;GAAE,GAAG;GAAwB,GAAG;EAAO;CAChD;CAEA,MAAM,aAAa,OAAe,IAA8B;EAc9D,QAAO,MAVe,KAAK,GACxB,OAAO,YAAY,CAAC,CACpB,MACC,IACE,GAAG,aAAa,OAAO,KAAK,GAC5B,GAAG,aAAa,IAAI,EAAE,GACtB,GAAG,aAAa,MAAM,QAAQ,CAChC,CACF,CAAC,CACA,UAAU,EAAE,IAAI,aAAa,GAAG,CAAC,EAAA,CACrB,SAAS;CAC1B;CASA,kBAAkB,OAAe,UAAqC;EACpE,OAAO,KAAK,IAAI,OAAO,KAAK,YAAY,IAAI,OAAO,QAAQ,CAAC;CAC9D;CAEA,MAAM,wBAAwB,OAAe,UAAuC;EAClF,OAAO,KAAK,GACT,OAAO,CAAC,CACR,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,UAAU,QAAQ,CAAC,CAAC,CAAC,CAC1E,QAAQ,WAAW,UAAU,WAAW,GAAG;CAChD;CAGA,MAAM,4BAA4B,OAAe,UAA4C;EAC3F,OAAO,KAAK,GACT,OAAO;GACN,OAAO,eAAe;GACtB,IAAI,eAAe;GACnB,YAAY,eAAe;GAC3B,SAAS,eAAe;GACxB,KAAK,eAAe;GACpB,WAAW,eAAe;GAC1B,WAAW,eAAe;EAC5B,CAAC,CAAC,CACD,KAAK,cAAc,CAAC,CACpB,UACC,YACA,IACE,GAAG,WAAW,OAAO,eAAe,KAAK,GACzC,GAAG,WAAW,IAAI,eAAe,UAAU,CAC7C,CACF,CAAC,CACA,MAAM,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,GAAG,WAAW,UAAU,QAAQ,CAAC,CAAC,CAAC,CAC9E,QAAQ,eAAe,YAAY,eAAe,GAAG;CAC1D;CAEA,MAAM,aAAa,OAAe,YAA8C;EAC9E,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,CAAC,CACR,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,IAAI,UAAU,CAAC,CAAC,CAAC,CACtE,MAAM,CAAC;EACV,OAAO,OAAO;CAChB;CAEA,MAAM,WAAW,OAAe,UAA0C;EACxE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,CAAC,CACR,KAAK,OAAO,CAAC,CACb,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,CAC9D,MAAM,CAAC;EACV,OAAO,OAAO;CAChB;;CAGA,MAAc,YAAY,IAAQ,OAAe,UAAqC;EACpF,OAAO,GACJ,OAAO,CAAC,CACR,KAAK,OAAO,CAAC,CACb,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,UAAU,QAAQ,CAAC,CAAC,CAAC,CACpE,QAAQ,QAAQ,GAAG;CACxB;;CAGA,MAAc,aACZ,IACA,OACA,UACA,UACe;EACf,MAAM,CAAC,OAAO,MAAM,GACjB,OAAO,EAAE,IAAI,QAAQ,GAAG,CAAC,CAAC,CAC1B,KAAK,OAAO,CAAC,CACb,MACC,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,QAAQ,GAAG,GAAG,QAAQ,UAAU,QAAQ,CAAC,CACxF,CAAC,CACA,MAAM,CAAC;EACV,IAAI,CAAC,KACH,MAAM,IAAI,cAAc,UAAU,QAAQ;CAE9C;;CAGA,MAAc,sBACZ,IACA,OACA,YACA,UACe;EACf,MAAM,GACH,OAAO,cAAc,CAAC,CACtB,MAAM,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,GAAG,eAAe,YAAY,UAAU,CAAC,CAAC;EACxF,IAAI,SAAS,QACX,IAAI;GACF,MAAM,GACH,OAAO,cAAc,CAAC,CACtB,OAAO,SAAS,KAAK,SAAS,OAAO;IAAE;IAAO;IAAY;IAAS,KAAK;GAAE,EAAE,CAAC;EAClF,SAAS,KAAK;GACZ,MAAM,YAAY,GAAG,KAAK;EAC5B;CAEJ;CAEA,aAAa,OAAe,UAAkB,OAAkC;EAC9E,OAAO,KAAK,GAAG,OAAO,OAAO;GAK3B,MAAM,WAAU,MAJO,GACpB,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,CAC5B,KAAK,OAAO,CAAC,CACb,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,UAAU,QAAQ,CAAC,CAAC,EAAA,CAC7C,QAAQ,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,GAAG,GAAG,EAAE,IAAI;GACxE,MAAM,GAAG,OAAO,OAAO,CAAC,CAAC,OAAO;IAAE;IAAO;IAAU;IAAO,KAAK;GAAQ,CAAC;GACxE,OAAO,KAAK,YAAY,IAAI,OAAO,QAAQ;EAC7C,CAAC;CACH;CAEA,aACE,OACA,UACA,UACA,OACmB;EACnB,OAAO,KAAK,GAAG,OAAO,OAAO;GAC3B,MAAM,KAAK,aAAa,IAAI,OAAO,UAAU,QAAQ;GACrD,MAAM,GACH,OAAO,OAAO,CAAC,CACf,IAAI,EAAE,MAAM,CAAC,CAAC,CACd,MACC,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,QAAQ,GAAG,GAAG,QAAQ,UAAU,QAAQ,CAAC,CACxF;GACF,OAAO,KAAK,YAAY,IAAI,OAAO,QAAQ;EAC7C,CAAC;CACH;CAEA,aAAa,OAAe,UAAkB,UAAqC;EACjF,OAAO,KAAK,GAAG,OAAO,OAAO;GAC3B,MAAM,KAAK,aAAa,IAAI,OAAO,UAAU,QAAQ;GAQrD,MAAM,eAAc,MAJO,GACxB,OAAO,EAAE,IAAI,WAAW,GAAG,CAAC,CAAC,CAC7B,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,UAAU,QAAQ,CAAC,CAAC,EAAA,CAC3C,KAAK,MAAM,EAAE,EAAE;GAEhD,IAAI,YAAY,QAAQ;IACtB,MAAM,GACH,OAAO,cAAc,CAAC,CACtB,MACC,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,QAAQ,eAAe,YAAY,WAAW,CAAC,CACtF;IACF,MAAM,GACH,OAAO,UAAU,CAAC,CAClB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,UAAU,QAAQ,CAAC,CAAC;GAC9E;GAEA,MAAM,GACH,OAAO,OAAO,CAAC,CACf,MACC,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,QAAQ,GAAG,GAAG,QAAQ,UAAU,QAAQ,CAAC,CACxF;GACF,OAAO,KAAK,YAAY,IAAI,OAAO,QAAQ;EAC7C,CAAC;CACH;CAEA,eAAe,OAAe,UAAkB,YAAyC;EACvF,OAAO,KAAK,GAAG,OAAO,OAAO;GAG3B,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KACrC,MAAM,GACH,OAAO,OAAO,CAAC,CACf,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CACf,MACC,IACE,GAAG,QAAQ,OAAO,KAAK,GACvB,GAAG,QAAQ,UAAU,QAAQ,GAC7B,GAAG,QAAQ,IAAI,WAAW,EAAG,CAC/B,CACF;GAEJ,OAAO,KAAK,YAAY,IAAI,OAAO,QAAQ;EAC7C,CAAC;CACH;CAEA,kBACE,OACA,UACA,UACA,YACmB;EACnB,OAAO,KAAK,GAAG,OAAO,OAAO;GAC3B,MAAM,KAAK,aAAa,IAAI,OAAO,UAAU,QAAQ;GAGrD,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KACrC,MAAM,GACH,OAAO,UAAU,CAAC,CAClB,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC,CACtB,MACC,IACE,GAAG,WAAW,OAAO,KAAK,GAC1B,GAAG,WAAW,UAAU,QAAQ,GAChC,GAAG,WAAW,IAAI,WAAW,EAAG,CAClC,CACF;GAEJ,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KACrC,MAAM,GACH,OAAO,UAAU,CAAC,CAClB,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CACf,MACC,IACE,GAAG,WAAW,OAAO,KAAK,GAC1B,GAAG,WAAW,UAAU,QAAQ,GAChC,GAAG,WAAW,IAAI,WAAW,EAAG,CAClC,CACF;GAEJ,OAAO,KAAK,YAAY,IAAI,OAAO,QAAQ;EAC7C,CAAC;CACH;CAEA,aACE,OACA,UACA,UACA,OACA,YACoD;EACpD,OAAO,KAAK,GAAG,OAAO,OAAO;GAC3B,MAAM,KAAK,aAAa,IAAI,OAAO,UAAU,QAAQ;GAErD,IAAI;GACJ,IAAI,YAAY;IACd,MAAM,CAAC,YAAY,MAAM,GACtB,OAAO,EAAE,IAAI,WAAW,GAAG,CAAC,CAAC,CAC7B,KAAK,UAAU,CAAC,CAChB,MACC,IACE,GAAG,WAAW,OAAO,KAAK,GAC1B,GAAG,WAAW,IAAI,UAAU,GAC5B,GAAG,WAAW,UAAU,QAAQ,CAClC,CACF,CAAC,CACA,MAAM,CAAC;IACV,IAAI,CAAC,UACH,MAAM,IAAI,cAAc,YAAY,UAAU;IAGhD,MAAM,GACH,OAAO,UAAU,CAAC,CAClB,IAAI,EAAE,UAAU,MAAM,YAAY,KAAK,CAAC,CAAC,CACzC,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,IAAI,UAAU,CAAC,CAAC;IACxE,IAAI,MAAM,aAAa,KAAA,GACrB,MAAM,KAAK,sBAAsB,IAAI,OAAO,YAAY,MAAM,QAAQ;IAExE,UAAU;GACZ,OAAO;IAKL,MAAM,WAAU,MAJO,GACpB,OAAO,EAAE,KAAK,WAAW,IAAI,CAAC,CAAC,CAC/B,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,UAAU,QAAQ,CAAC,CAAC,EAAA,CACnD,QAAQ,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,GAAG,GAAG,EAAE,IAAI;IAExE,MAAM,CAAC,OAAO,MAAM,GACjB,OAAO,UAAU,CAAC,CAClB,OAAO;KAAE;KAAO;KAAU;KAAU,KAAK;KAAS,UAAU,MAAM,YAAY;IAAK,CAAC,CAAC,CACrF,UAAU,EAAE,IAAI,WAAW,GAAG,CAAC;IAClC,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,2BAA2B;IAE7C,MAAM,KAAK,sBAAsB,IAAI,OAAO,IAAI,IAAI,MAAM,YAAY,CAAC,CAAC;IACxE,UAAU,IAAI;GAChB;GAEA,MAAM,CAAC,YAAY,MAAM,GACtB,OAAO,CAAC,CACR,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,IAAI,OAAO,CAAC,CAAC,CAAC,CACnE,MAAM,CAAC;GACV,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,+BAA+B;GAEjD,OAAO;IAAE,SAAS,MAAM,KAAK,YAAY,IAAI,OAAO,QAAQ;IAAG;GAAS;EAC1E,CAAC;CACH;CAEA,eACE,OACA,UACA,UACA,YACmB;EACnB,OAAO,KAAK,GAAG,OAAO,OAAO;GAC3B,MAAM,KAAK,aAAa,IAAI,OAAO,UAAU,QAAQ;GAGrD,MAAM,GACH,OAAO,cAAc,CAAC,CACtB,MAAM,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,GAAG,eAAe,YAAY,UAAU,CAAC,CAAC;GACxF,MAAM,GACH,OAAO,UAAU,CAAC,CAClB,MACC,IACE,GAAG,WAAW,OAAO,KAAK,GAC1B,GAAG,WAAW,IAAI,UAAU,GAC5B,GAAG,WAAW,UAAU,QAAQ,CAClC,CACF;GAEF,OAAO,KAAK,YAAY,IAAI,OAAO,QAAQ;EAC7C,CAAC;CACH;CAIA,MAAM,cAAc,OAAe,OAAoD;EACrF,MAAM,aAAoB,CAAC,GAAG,UAAU,OAAO,KAAK,CAAC;EACrD,IAAI,MAAM,QACR,WAAW,KAAK,GAAG,UAAU,QAAQ,MAAM,MAAM,CAAC;EAEpD,IAAI,MAAM,UACR,WAAW,KAAK,GAAG,UAAU,UAAU,MAAM,QAAQ,CAAC;EAGxD,MAAM,SAAS,MAAM,QAAQ,KAAK;EAClC,IAAI,QAAQ;GACV,MAAM,OAAO,IAAI,OAAO;GACxB,MAAM,QAAQ,GAAG,MAAM,UAAU,OAAO,IAAI,GAAG,MAAM,UAAU,UAAU,IAAI,CAAC;GAC9E,IAAI,OACF,WAAW,KAAK,KAAK;EAEzB;EAEA,MAAM,QAAQ,IAAI,GAAG,UAAU;EAG/B,IAAI,UAAU;EACd,IAAI,YAA4B;EAChC,IAAI,MAAM,MAAM;GACd,MAAM,SAAS,MAAM,KAAK,WAAW,GAAG;GACxC,MAAM,MAAM,SAAS,MAAM,KAAK,MAAM,CAAC,IAAI,MAAM;GACjD,IAAI,OAAO,qBAAqB;IAC9B,UAAU;IACV,YAAY,SAAS,SAAS;GAChC;EACF;EACA,MAAM,WAAW,oBAAoB,YAAY,UAAU;EAC3D,MAAM,UAAU,cAAc,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;EAEpE,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,CAAC,CACR,KAAK,SAAS,CAAC,CACf,MAAM,KAAK,CAAC,CACZ,QAAQ,OAAO,CAAC,CAChB,MAAM,MAAM,QAAQ,CAAC,CACrB,QAAQ,MAAM,OAAO,KAAK,MAAM,QAAQ;EAE3C,MAAM,CAAC,YAAY,MAAM,KAAK,GAAG,OAAO,EAAE,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,MAAM,KAAK;EAEvF,OAAO;GACL,MAAM,KAAK,IAAI,UAAU;GACzB,OAAO,UAAU,SAAS;GAC1B,MAAM,MAAM;GACZ,UAAU,MAAM;EAClB;CACF;CAEA,MAAM,YAAY,OAAe,IAAsC;EACrE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,CAAC,CACR,KAAK,SAAS,CAAC,CACf,MAAM,IAAI,GAAG,UAAU,OAAO,KAAK,GAAG,GAAG,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,CAC5D,MAAM,CAAC;EACV,OAAO,MAAM,WAAW,GAAG,IAAI;CACjC;CAEA,MAAM,eAAe,OAAe,OAA4B,MAAiC;EAG/F,MAAM,KAAK,MAAM,UAAU;EAC3B,MAAM,KAAK,GAAG,OAAO,YAAY,CAAC,CAAC,OAAO;GAAE;GAAO;GAAI,MAAM;EAAW,CAAC;EACzE,MAAM,KAAK,GAAG,OAAO,SAAS,CAAC,CAAC,OAAO;GACrC;GACA;GACA,OAAO,MAAM;GACb;GACA,aAAa,MAAM,eAAe;GAClC,UAAU,MAAM,YAAY;EAC9B,CAAC;EACD,MAAM,UAAU,MAAM,KAAK,YAAY,OAAO,EAAE;EAChD,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,iCAAiC;EAEnD,OAAO;CACT;CAEA,MAAM,eACJ,OACA,IACA,OAC0B;EAC1B,MAAM,MAA+B,EAAE,2BAAW,IAAI,KAAK,EAAE;EAC7D,IAAI,MAAM,UAAU,KAAA,GAClB,IAAI,QAAQ,MAAM;EAEpB,IAAI,MAAM,gBAAgB,KAAA,GACxB,IAAI,cAAc,MAAM;EAE1B,IAAI,MAAM,aAAa,KAAA,GACrB,IAAI,WAAW,MAAM;EAEvB,IAAI,MAAM,WAAW,KAAA,GACnB,IAAI,SAAS,MAAM;EAErB,IAAI,MAAM,qBAAqB,KAAA,GAC7B,IAAI,mBAAmB,MAAM;EAG/B,MAAM,CAAC,WAAW,MAAM,KAAK,GAC1B,OAAO,SAAS,CAAC,CACjB,IAAI,GAAG,CAAC,CACR,MAAM,IAAI,GAAG,UAAU,OAAO,KAAK,GAAG,GAAG,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,CAC5D,UAAU,EAAE,IAAI,UAAU,GAAG,CAAC;EACjC,IAAI,CAAC,SACH,OAAO;EAET,OAAO,KAAK,YAAY,OAAO,EAAE;CACnC;CAEA,MAAM,eAAe,OAAe,IAA8B;EAahE,QAAO,MAVe,KAAK,GACxB,OAAO,YAAY,CAAC,CACpB,MACC,IACE,GAAG,aAAa,OAAO,KAAK,GAC5B,GAAG,aAAa,IAAI,EAAE,GACtB,GAAG,aAAa,MAAM,UAAU,CAClC,CACF,CAAC,CACA,UAAU,EAAE,IAAI,aAAa,GAAG,CAAC,EAAA,CACrB,SAAS;CAC1B;CAEA,MAAM,mBAAmB,OAAe,YAA8C;EACpF,OAAO,KAAK,GACT,OAAO,CAAC,CACR,KAAK,cAAc,CAAC,CACpB,MAAM,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,GAAG,eAAe,YAAY,UAAU,CAAC,CAAC,CAAC,CACtF,QAAQ,IAAI,eAAe,GAAG,CAAC;CACpC;;CAGA,MAAc,eAAe,OAAe,YAAmC;EAC7E,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,EAAE,IAAI,UAAU,GAAG,CAAC,CAAC,CAC5B,KAAK,SAAS,CAAC,CACf,MAAM,IAAI,GAAG,UAAU,OAAO,KAAK,GAAG,GAAG,UAAU,IAAI,UAAU,CAAC,CAAC,CAAC,CACpE,MAAM,CAAC;EACV,IAAI,CAAC,KACH,MAAM,IAAI,cAAc,YAAY,UAAU;CAElD;CAEA,MAAM,iBACJ,OACA,YACA,OAC0B;EAC1B,MAAM,KAAK,eAAe,OAAO,UAAU;EAC3C,MAAM,CAAC,UAAU,MAAM,KAAK,GACzB,OAAO,EAAE,QAAQ,GAAkB,OAAO,eAAe,IAAI,GAAG,CAAC,CAAC,CAClE,KAAK,cAAc,CAAC,CACpB,MAAM,IAAI,GAAG,eAAe,OAAO,KAAK,GAAG,GAAG,eAAe,YAAY,UAAU,CAAC,CAAC;EACxF,MAAM,WAAW,QAAQ,UAAU,MAAM;EACzC,IAAI;GACF,MAAM,KAAK,GAAG,OAAO,cAAc,CAAC,CAAC,OAAO;IAC1C;IACA;IACA,SAAS,MAAM;IACf,KAAK;IACL,aAAa,MAAM,eAAe;GACpC,CAAC;EACH,SAAS,KAAK;GACZ,IAAI,kBAAkB,GAAG,GACvB,MAAM,IAAI,cAAc,8CAA8C;GAExE,MAAM;EACR;EACA,OAAO,KAAK,mBAAmB,OAAO,UAAU;CAClD;CAEA,MAAM,oBACJ,OACA,YACA,SAC0B;EAC1B,MAAM,KAAK,eAAe,OAAO,UAAU;EAC3C,MAAM,KAAK,GACR,OAAO,cAAc,CAAC,CACtB,MACC,IACE,GAAG,eAAe,OAAO,KAAK,GAC9B,GAAG,eAAe,YAAY,UAAU,GACxC,GAAG,eAAe,SAAS,OAAO,CACpC,CACF;EACF,OAAO,KAAK,mBAAmB,OAAO,UAAU;CAClD;CAEA,MAAM,oBACJ,OACA,YACA,SACA,aAC0B;EAC1B,MAAM,KAAK,eAAe,OAAO,UAAU;EAC3C,MAAM,KAAK,GACR,OAAO,cAAc,CAAC,CACtB,IAAI,EAAE,YAAY,CAAC,CAAC,CACpB,MACC,IACE,GAAG,eAAe,OAAO,KAAK,GAC9B,GAAG,eAAe,YAAY,UAAU,GACxC,GAAG,eAAe,SAAS,OAAO,CACpC,CACF;EACF,OAAO,KAAK,mBAAmB,OAAO,UAAU;CAClD;CAEA,MAAM,sBACJ,OACA,YACA,UAC0B;EAC1B,MAAM,KAAK,eAAe,OAAO,UAAU;EAE3C,KAAK,MAAM,CAAC,KAAK,YAAY,SAAS,QAAQ,GAC5C,MAAM,KAAK,GACR,OAAO,cAAc,CAAC,CACtB,IAAI,EAAE,IAAI,CAAC,CAAC,CACZ,MACC,IACE,GAAG,eAAe,OAAO,KAAK,GAC9B,GAAG,eAAe,YAAY,UAAU,GACxC,GAAG,eAAe,SAAS,OAAO,CACpC,CACF;EAEJ,OAAO,KAAK,mBAAmB,OAAO,UAAU;CAClD;CAIA,MAAM,YAAY,OAAe,OAAgD;EAC/E,MAAM,aAAoB,CAAC,GAAG,QAAQ,OAAO,KAAK,CAAC;EAEnD,MAAM,SAAS,MAAM,QAAQ,KAAK;EAClC,IAAI,QACF,WAAW,KAAK,MAAM,QAAQ,MAAM,IAAI,OAAO,EAAE,CAAC;EAGpD,MAAM,QAAQ,IAAI,GAAG,UAAU;EAG/B,IAAI,UAAU;EACd,IAAI,YAA4B;EAChC,IAAI,MAAM,MAAM;GACd,MAAM,SAAS,MAAM,KAAK,WAAW,GAAG;GACxC,MAAM,MAAM,SAAS,MAAM,KAAK,MAAM,CAAC,IAAI,MAAM;GACjD,IAAI,OAAO,mBAAmB;IAC5B,UAAU;IACV,YAAY,SAAS,SAAS;GAChC;EACF;EACA,MAAM,WAAW,kBAAkB,YAAY,QAAQ;EACvD,MAAM,UAAU,cAAc,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;EAEpE,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,CAAC,CACR,KAAK,OAAO,CAAC,CACb,MAAM,KAAK,CAAC,CACZ,QAAQ,OAAO,CAAC,CAChB,MAAM,MAAM,QAAQ,CAAC,CACrB,QAAQ,MAAM,OAAO,KAAK,MAAM,QAAQ;EAE3C,MAAM,CAAC,YAAY,MAAM,KAAK,GAAG,OAAO,EAAE,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,MAAM,KAAK;EAErF,OAAO;GACL;GACA,OAAO,UAAU,SAAS;GAC1B,MAAM,MAAM;GACZ,UAAU,MAAM;EAClB;CACF;CAEA,MAAM,eAAe,OAAe,IAAoC;EACtE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,CAAC,CACR,KAAK,OAAO,CAAC,CACb,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CACxD,MAAM,CAAC;EACV,OAAO,OAAO;CAChB;;;CAIA,MAAM,aAAa,OAAe,OAA2C;EAC3E,MAAM,aAAa,CAAC,GAAG,IAAI,IAAI,MAAM,cAAc,CAAC,CAAC,CAAC;EACtD,OAAO,KAAK,GAAG,OAAO,OAAO;GAG3B,MAAM,CAAC,OAAO,MAAM,GACjB,OAAO,OAAO,CAAC,CACf,OAAO;IAAE;IAAO,IAAI,MAAM,QAAQ;IAAG,MAAM,MAAM;GAAK,CAAC,CAAC,CACxD,UAAU;GACb,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,+BAA+B;GAGjD,IAAI,WAAW,SAAS,GAAG;IAEzB,MAAM,QAAQ,MAAM,GACjB,OAAO,EAAE,IAAI,aAAa,GAAG,CAAC,CAAC,CAC/B,KAAK,YAAY,CAAC,CAClB,MAAM,IAAI,GAAG,aAAa,OAAO,KAAK,GAAG,QAAQ,aAAa,IAAI,UAAU,CAAC,CAAC;IACjF,MAAM,QAAQ,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,EAAE,CAAC;IAClD,MAAM,UAAU,WAAW,MAAM,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IACtD,IAAI,SACF,MAAM,IAAI,cAAc,WAAW,OAAO;IAG5C,MAAM,GACH,OAAO,WAAW,CAAC,CACnB,OAAO,WAAW,KAAK,eAAe;KAAE;KAAO,UAAU,IAAI;KAAI;IAAU,EAAE,CAAC;GACnF;GAEA,OAAO;EACT,CAAC;CACH;CAEA,MAAM,aAAa,OAAe,IAAY,OAAkD;EAC9F,MAAM,MAA+B,EAAE,2BAAW,IAAI,KAAK,EAAE;EAC7D,IAAI,MAAM,SAAS,KAAA,GACjB,IAAI,OAAO,MAAM;EAGnB,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,OAAO,CAAC,CACf,IAAI,GAAG,CAAC,CACR,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CACxD,UAAU;EACb,OAAO,OAAO;CAChB;CAEA,MAAM,aAAa,OAAe,IAA8B;EAO9D,QAAO,MAJe,KAAK,GACxB,OAAO,OAAO,CAAC,CACf,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CACxD,UAAU,EAAE,IAAI,QAAQ,GAAG,CAAC,EAAA,CAChB,SAAS;CAC1B;CAEA,MAAM,gBAAgB,OAAe,UAAyC;EAC5E,OAAO,KAAK,GACT,OAAO,CAAC,CACR,KAAK,WAAW,CAAC,CACjB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,UAAU,QAAQ,CAAC,CAAC,CAAC,CAC5E,QAAQ,IAAI,YAAY,SAAS,CAAC;CACvC;;CAGA,MAAc,aAAa,OAAe,UAAiC;EACzE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,EAAE,IAAI,QAAQ,GAAG,CAAC,CAAC,CAC1B,KAAK,OAAO,CAAC,CACb,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,CAC9D,MAAM,CAAC;EACV,IAAI,CAAC,KACH,MAAM,IAAI,cAAc,UAAU,QAAQ;CAE9C;CAEA,MAAM,cAAc,OAAe,UAAkB,WAA0C;EAC7F,MAAM,KAAK,aAAa,OAAO,QAAQ;EAEvC,MAAM,CAAC,QAAQ,MAAM,KAAK,GACvB,OAAO,EAAE,IAAI,aAAa,GAAG,CAAC,CAAC,CAC/B,KAAK,YAAY,CAAC,CAClB,MAAM,IAAI,GAAG,aAAa,OAAO,KAAK,GAAG,GAAG,aAAa,IAAI,SAAS,CAAC,CAAC,CAAC,CACzE,MAAM,CAAC;EACV,IAAI,CAAC,MACH,MAAM,IAAI,cAAc,WAAW,SAAS;EAE9C,MAAM,KAAK,GACR,OAAO,WAAW,CAAC,CACnB,OAAO;GAAE;GAAO;GAAU;EAAU,CAAC,CAAC,CACtC,oBAAoB,EACnB,QAAQ;GAAC,YAAY;GAAO,YAAY;GAAU,YAAY;EAAS,EACzE,CAAC;EACH,OAAO,KAAK,gBAAgB,OAAO,QAAQ;CAC7C;CAEA,MAAM,iBACJ,OACA,UACA,WACuB;EACvB,MAAM,KAAK,aAAa,OAAO,QAAQ;EACvC,MAAM,KAAK,GACR,OAAO,WAAW,CAAC,CACnB,MACC,IACE,GAAG,YAAY,OAAO,KAAK,GAC3B,GAAG,YAAY,UAAU,QAAQ,GACjC,GAAG,YAAY,WAAW,SAAS,CACrC,CACF;EACF,OAAO,KAAK,gBAAgB,OAAO,QAAQ;CAC7C;CAEA,MAAM,gBAAgB,OAAe,OAAuD;EAC1F,MAAM,aAAoB,CAAC,GAAG,YAAY,OAAO,KAAK,CAAC;EACvD,IAAI,MAAM,QACR,WAAW,KAAK,GAAG,YAAY,QAAQ,MAAM,MAAM,CAAC;EAEtD,MAAM,SAAS,MAAM,QAAQ,KAAK;EAClC,IAAI,QAAQ;GACV,MAAM,OAAO,IAAI,OAAO;GACxB,MAAM,QAAQ,GAAG,MAAM,YAAY,OAAO,IAAI,GAAG,MAAM,YAAY,aAAa,IAAI,CAAC;GACrF,IAAI,OACF,WAAW,KAAK,KAAK;EAEzB;EACA,MAAM,QAAQ,IAAI,GAAG,UAAU;EAC/B,IAAI,UAAU;EACd,IAAI,YAA4B;EAChC,IAAI,MAAM,MAAM;GACd,MAAM,SAAS,MAAM,KAAK,WAAW,GAAG;GACxC,MAAM,MAAM,SAAS,MAAM,KAAK,MAAM,CAAC,IAAI,MAAM;GACjD,IAAI,OAAO,sBAAsB;IAC/B,UAAU;IACV,YAAY,SAAS,SAAS;GAChC;EACF;EACA,MAAM,WAAW,qBAAqB,YAAY,YAAY;EAC9D,MAAM,UAAU,cAAc,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;EACpE,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO;GAAE,WAAW;GAAa,eAAe,SAAS;EAAM,CAAC,CAAC,CACjE,KAAK,WAAW,CAAC,CACjB,SAAS,UAAU,qBAAqB,CAAC,CACzC,MAAM,KAAK,CAAC,CACZ,QAAQ,OAAO,CAAC,CAChB,MAAM,MAAM,QAAQ,CAAC,CACrB,QAAQ,MAAM,OAAO,KAAK,MAAM,QAAQ;EAC3C,MAAM,CAAC,YAAY,MAAM,KAAK,GAAG,OAAO,EAAE,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,MAAM,KAAK;EACzF,OAAO;GACL,MAAM,KAAK,KAAK,QAAQ,YAAY,IAAI,WAAW,IAAI,aAAa,CAAC;GACrE,OAAO,UAAU,SAAS;GAC1B,MAAM,MAAM;GACZ,UAAU,MAAM;EAClB;CACF;CAEA,MAAM,kBAAkB,OAAe,IAAuC;EAC5E,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO;GAAE,WAAW;GAAa,eAAe,SAAS;EAAM,CAAC,CAAC,CACjE,KAAK,WAAW,CAAC,CACjB,SAAS,UAAU,qBAAqB,CAAC,CACzC,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,CAChE,MAAM,CAAC;EACV,OAAO,MAAM,YAAY,IAAI,WAAW,IAAI,aAAa,IAAI;CAC/D;CAEA,MAAM,qBAAqB,OAAe,KAAqC;EAO7E,QAAO,MANY,KAAK,GACrB,OAAO;GAAE,WAAW;GAAa,eAAe,SAAS;EAAM,CAAC,CAAC,CACjE,KAAK,WAAW,CAAC,CACjB,SAAS,UAAU,qBAAqB,CAAC,CACzC,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,QAAQ,YAAY,IAAI,GAAG,CAAC,CAAC,CAAC,CACtE,QAAQ,IAAI,YAAY,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC,EAAA,CAC1C,KAAK,QAAQ,YAAY,IAAI,WAAW,IAAI,aAAa,CAAC;CACxE;CAEA,MAAM,0BAA0B,OAAkC;EAMhE,QAAO,MALY,KAAK,GACrB,OAAO,EAAE,IAAI,YAAY,GAAG,CAAC,CAAC,CAC9B,KAAK,WAAW,CAAC,CACjB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,QAAQ,WAAW,CAAC,CAAC,CAAC,CAC7E,QAAQ,IAAI,YAAY,SAAS,GAAG,IAAI,YAAY,EAAE,CAAC,EAAA,CAC9C,KAAK,QAAQ,IAAI,EAAE;CACjC;CAEA,MAAM,gBACJ,OACA,OACA,MACoB;EACpB,MAAM,KAAK,MAAM,WAAW;EAC5B,MAAM,KAAK,GAAG,OAAO,YAAY,CAAC,CAAC,OAAO;GAAE;GAAO;GAAI,MAAM;EAAY,CAAC;EAC1E,MAAM,CAAC,YAAY,MAAM,KAAK,GAC3B,OAAO,WAAW,CAAC,CACnB,OAAO;GACN;GACA;GACA,OAAO,MAAM;GACb;GACA,aAAa,MAAM,eAAe;EACpC,CAAC,CAAC,CACD,UAAU,EAAE,IAAI,YAAY,GAAG,CAAC;EACnC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,4BAA4B;EAE9C,MAAM,UAAU,MAAM,KAAK,kBAAkB,OAAO,SAAS,EAAE;EAC/D,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,kCAAkC;EAEpD,OAAO;CACT;CAEA,MAAM,gBACJ,OACA,IACA,OAC2B;EAC3B,MAAM,MAA+B,EAAE,2BAAW,IAAI,KAAK,EAAE;EAC7D,IAAI,MAAM,UAAU,KAAA,GAClB,IAAI,QAAQ,MAAM;EAEpB,IAAI,MAAM,gBAAgB,KAAA,GACxB,IAAI,cAAc,MAAM;EAE1B,IAAI,MAAM,WAAW,KAAA,GACnB,IAAI,SAAS,MAAM;EAErB,IAAI,MAAM,qBAAqB,KAAA,GAC7B,IAAI,mBAAmB,MAAM;EAE/B,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,kBAAkB,OAAO,EAAE;CACzC;CAEA,MAAM,gBACJ,OACA,IACA,OAC2B;EAE3B,IAAI,CAAC,MADmB,KAAK,kBAAkB,OAAO,EAAE,GAEtD,OAAO;EAET,MAAM,MAAM,IACV,GAAG,SAAS,OAAO,KAAK,GACxB,GAAG,SAAS,WAAW,kBAAkB,GACzC,GAAG,SAAS,SAAS,EAAE,CACzB;EACA,MAAM,KAAK,GAAG,OAAO,OAAO;GAC1B,IAAI,CAAC,YAAY,MAAM,GAAG,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,QAAQ;GACzE,MAAM,QAAQ,EAAE,IAAI,MAAM;GAC1B,IAAI,CAAC,UAAU;IACb,MAAM,CAAC,YAAY,MAAM,GACtB,OAAO,QAAQ,CAAC,CAChB,OAAO;KAAE;KAAO,WAAW;KAAoB,SAAS;KAAI;IAAM,CAAC,CAAC,CACpE,oBAAoB,CAAC,CACrB,UAAU;IACb,IAAI,UACF;IAEF,CAAC,YAAY,MAAM,GAAG,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,QAAQ;IACrE,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,wDAAwD;GAE5E;GACA,MAAM,GACH,OAAO,QAAQ,CAAC,CAChB,IAAI;IACH,OAAO,UAAU,SAAS,OAAO,EAAE,IAAI,MAAM,CAAC;IAC9C,2BAAW,IAAI,KAAK;GACtB,CAAC,CAAC,CACD,MAAM,GAAG;EACd,CAAC;EACD,OAAO,KAAK,kBAAkB,OAAO,EAAE;CACzC;CAEA,MAAM,gBAAgB,OAAe,IAA8B;EAWjE,QAAO,MAVe,KAAK,GACxB,OAAO,YAAY,CAAC,CACpB,MACC,IACE,GAAG,aAAa,OAAO,KAAK,GAC5B,GAAG,aAAa,IAAI,EAAE,GACtB,GAAG,aAAa,MAAM,WAAW,CACnC,CACF,CAAC,CACA,UAAU,EAAE,IAAI,aAAa,GAAG,CAAC,EAAA,CACrB,SAAS;CAC1B;CAEA,MAAM,wBAAwB,OAAe,IAA2B;EACtE,MAAM,KAAK,GACR,OAAO,QAAQ,CAAC,CAChB,MACC,IACE,GAAG,SAAS,OAAO,KAAK,GACxB,GAAG,SAAS,WAAW,kBAAkB,GACzC,GAAG,SAAS,SAAS,EAAE,CACzB,CACF;CACJ;CAEA,MAAM,WAAW,OAAe,aAA6C;EAC3E,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,CAAC,CACR,KAAK,MAAM,CAAC,CACZ,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC,CAAC,CACxE,QAAQ,IAAI,OAAO,GAAG,CAAC;EAC1B,OAAO,KAAK,eAAe,OAAO,KAAK,IAAI,OAAO,CAAC;CACrD;CAEA,MAAM,UAAU,OAAe,SAA8C;EAC3E,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,CAAC,CACR,KAAK,MAAM,CAAC,CACZ,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,CAC3D,MAAM,CAAC;EACV,IAAI,CAAC,KACH,OAAO;EAET,MAAM,CAAC,UAAU,MAAM,KAAK,eAAe,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;EAChE,OAAO,UAAU;CACnB;CAEA,MAAM,UAAU,OAAe,SAAwC;EACrE,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,CAAC,CACR,KAAK,MAAM,CAAC,CACZ,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,CAC3D,IAAI,QAAQ,CAAC,CACb,MAAM,CAAC;EACV,OAAO,MAAM,QAAQ,GAAG,IAAI;CAC9B;CAEA,MAAM,YACJ,OACA,aACA,OACsB;EAKtB,MAAM,WAAU,MAJO,KAAK,GACzB,OAAO,EAAE,KAAK,OAAO,IAAI,CAAC,CAAC,CAC3B,KAAK,MAAM,CAAC,CACZ,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,OAAO,aAAa,WAAW,CAAC,CAAC,EAAA,CACjD,QAAQ,KAAK,QAAQ,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG,EAAE,IAAI;EAC5E,MAAM,CAAC,YAAY,MAAM,KAAK,GAC3B,OAAO,MAAM,CAAC,CACd,OAAO;GACN;GACA;GACA,MAAM,MAAM;GACZ,aAAa,MAAM,eAAe;GAClC,KAAK;GACL,YAAY,MAAM,cAAc;GAChC,UAAU,MAAM,YAAY;GAC5B,UAAU;IAAE,GAAG;IAAuB,GAAG,MAAM;GAAS;EAC1D,CAAC,CAAC,CACD,UAAU,EAAE,IAAI,OAAO,GAAG,CAAC;EAC9B,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,wBAAwB;EAE1C,MAAM,UAAU,MAAM,KAAK,UAAU,OAAO,SAAS,EAAE;EACvD,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,8BAA8B;EAEhD,OAAO;CACT;CAEA,MAAM,YACJ,OACA,aACA,SACA,OAC6B;EAC7B,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,eAAe,KAAA,GACvB,IAAI,aAAa,MAAM;EAEzB,IAAI,MAAM,aAAa,KAAA,GACrB,IAAI,WAAW,MAAM;EAEvB,IAAI,MAAM,aAAa,KAAA,GACrB,IAAI,WAAW;GAAE,GAAG;GAAuB,GAAG,MAAM;EAAS;EAE/D,MAAM,CAAC,WAAW,MAAM,KAAK,GAC1B,OAAO,MAAM,CAAC,CACd,IAAI,GAAG,CAAC,CACR,MACC,IAAI,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,OAAO,IAAI,OAAO,GAAG,GAAG,OAAO,aAAa,WAAW,CAAC,CAC1F,CAAC,CACA,UAAU,EAAE,IAAI,OAAO,GAAG,CAAC;EAC9B,IAAI,CAAC,SACH,OAAO;EAET,OAAO,KAAK,UAAU,OAAO,OAAO;CACtC;CAEA,MAAM,YAAY,OAAe,aAAqB,SAAmC;EAOvF,QAAO,MANe,KAAK,GACxB,OAAO,MAAM,CAAC,CACd,MACC,IAAI,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,OAAO,IAAI,OAAO,GAAG,GAAG,OAAO,aAAa,WAAW,CAAC,CAC1F,CAAC,CACA,UAAU,EAAE,IAAI,OAAO,GAAG,CAAC,EAAA,CACf,SAAS;CAC1B;CAEA,MAAM,cACJ,OACA,aACA,YACwB;EACxB,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KACrC,MAAM,KAAK,GACR,OAAO,MAAM,CAAC,CACd,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CACf,MACC,IACE,GAAG,OAAO,OAAO,KAAK,GACtB,GAAG,OAAO,aAAa,WAAW,GAClC,GAAG,OAAO,IAAI,WAAW,EAAG,CAC9B,CACF;EAEJ,OAAO,KAAK,WAAW,OAAO,WAAW;CAC3C;CAEA,MAAM,eACJ,OACA,SACA,OACsB;EACtB,MAAM,KAAK,GAAG,OAAO,OAAO;GAC1B,MAAM,WAAW,MAAM,GACpB,OAAO,EAAE,IAAI,YAAY,GAAG,CAAC,CAAC,CAC9B,KAAK,WAAW,CAAC,CACjB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,SAAS,OAAO,CAAC,CAAC,CAAC,CAC1E,IAAI,QAAQ;GACf,MAAM,OAAO,IAAI,IAAI,MAAM,OAAO,SAAS,UAAW,MAAM,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAE,CAAC;GAClF,MAAM,QAAQ,IAAI,IAAI,SAAS,KAAK,UAAU,MAAM,EAAE,CAAC;GACvD,KAAK,MAAM,MAAM,MACf,IAAI,CAAC,MAAM,IAAI,EAAE,GACf,MAAM,IAAI,cAAc,SAAS,EAAE;GAGvC,MAAM,UAAU,SAAS,QAAQ,UAAU,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,UAAU,MAAM,EAAE;GACvF,IAAI,QAAQ,QACV,MAAM,GACH,OAAO,WAAW,CAAC,CACnB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,QAAQ,YAAY,IAAI,OAAO,CAAC,CAAC;GAE9E,KAAK,IAAI,MAAM,GAAG,MAAM,MAAM,OAAO,QAAQ,OAAO;IAClD,MAAM,QAAQ,MAAM,OAAO;IAC3B,IAAI,MAAM,IACR,MAAM,GACH,OAAO,WAAW,CAAC,CACnB,IAAI;KAAE,MAAM,MAAM;KAAM;KAAK,2BAAW,IAAI,KAAK;IAAE,CAAC,CAAC,CACrD,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,GAAG,YAAY,IAAI,MAAM,EAAE,CAAC,CAAC;SAExE,MAAM,GAAG,OAAO,WAAW,CAAC,CAAC,OAAO;KAAE;KAAO;KAAS,MAAM,MAAM;KAAM;IAAI,CAAC;GAEjF;EACF,CAAC;EACD,MAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,OAAO;EACjD,IAAI,CAAC,OACH,MAAM,IAAI,cAAc,SAAS,OAAO;EAE1C,OAAO;CACT;CAEA,MAAM,YAAY,OAAe,SAAiB,UAAuC;EACvF,IAAI,SAAS,WAAW,GACtB,OAAO,CAAC;EAEV,MAAM,KAAK,GACR,OAAO,EAAE,IAAI,YAAY,GAAG,CAAC,CAAC,CAC9B,KAAK,WAAW,CAAC,CACjB,MACC,IACE,GAAG,YAAY,OAAO,KAAK,GAC3B,GAAG,YAAY,SAAS,OAAO,GAC/B,QAAQ,YAAY,IAAI,QAAQ,CAClC,CACF,CAAC,CACA,IAAI,QAAQ;EAOf,QAAO,MANY,KAAK,GACrB,eAAe,EAAE,SAAS,MAAM,QAAQ,CAAC,CAAC,CAC1C,KAAK,KAAK,CAAC,CACX,MACC,IAAI,GAAG,MAAM,OAAO,KAAK,GAAG,GAAG,MAAM,SAAS,OAAO,GAAG,QAAQ,MAAM,SAAS,QAAQ,CAAC,CAC1F,EAAA,CACU,SAAS,QAAS,IAAI,UAAU,CAAC,IAAI,OAAO,IAAI,CAAC,CAAE;CACjE;CAEA,MAAM,aAAa,OAAe,SAAiB,WAAyC;EAC1F,MAAM,CAAC,QAAQ,MAAM,KAAK,GACvB,OAAO;GAAE,IAAI,aAAa;GAAI,MAAM,aAAa;EAAK,CAAC,CAAC,CACxD,KAAK,YAAY,CAAC,CAClB,MAAM,IAAI,GAAG,aAAa,OAAO,KAAK,GAAG,GAAG,aAAa,IAAI,SAAS,CAAC,CAAC,CAAC,CACzE,MAAM,CAAC;EACV,IACE,CAAC,QACA,KAAK,SAAS,YAAY,KAAK,SAAS,cAAc,KAAK,SAAS,aAErE,MAAM,IAAI,cAAc,WAAW,SAAS;EAM9C,MAAM,WAAU,MAJO,KAAK,GACzB,OAAO,EAAE,KAAK,WAAW,IAAI,CAAC,CAAC,CAC/B,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,SAAS,OAAO,CAAC,CAAC,EAAA,CACjD,QAAQ,KAAK,QAAQ,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG,EAAE,IAAI;EAC5E,MAAM,KAAK,GAAG,OAAO,UAAU,CAAC,CAAC,OAAO;GAAE;GAAO;GAAS;GAAW,KAAK;EAAQ,CAAC;EACnF,MAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,OAAO;EACjD,IAAI,CAAC,OACH,MAAM,IAAI,cAAc,SAAS,OAAO;EAE1C,OAAO;CACT;CAEA,MAAM,gBAAgB,OAAe,SAAiB,QAAsC;EAW1F,KAAI,MAVkB,KAAK,GACxB,OAAO,UAAU,CAAC,CAClB,MACC,IACE,GAAG,WAAW,OAAO,KAAK,GAC1B,GAAG,WAAW,SAAS,OAAO,GAC9B,GAAG,WAAW,IAAI,MAAM,CAC1B,CACF,CAAC,CACA,UAAU,EAAE,IAAI,WAAW,GAAG,CAAC,EAAA,CACtB,WAAW,GACrB,MAAM,IAAI,cAAc,QAAQ,MAAM;EAExC,MAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,OAAO;EACjD,IAAI,CAAC,OACH,MAAM,IAAI,cAAc,SAAS,OAAO;EAE1C,OAAO;CACT;CAEA,MAAM,kBACJ,OACA,SACA,YACsB;EACtB,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KACrC,MAAM,KAAK,GACR,OAAO,UAAU,CAAC,CAClB,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CACf,MACC,IACE,GAAG,WAAW,OAAO,KAAK,GAC1B,GAAG,WAAW,SAAS,OAAO,GAC9B,GAAG,WAAW,IAAI,WAAW,EAAG,CAClC,CACF;EAEJ,MAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,OAAO;EACjD,IAAI,CAAC,OACH,MAAM,IAAI,cAAc,SAAS,OAAO;EAE1C,OAAO;CACT;CAEA,MAAM,qBAAqB,OAAe,WAAsC;EAC9E,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,EAAE,SAAS,WAAW,QAAQ,CAAC,CAAC,CACvC,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,GAAG,WAAW,WAAW,SAAS,CAAC,CAAC;EAC9E,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC;CACpD;CAEA,MAAc,eAAe,OAAe,MAAuC;EACjF,IAAI,KAAK,WAAW,GAClB,OAAO,CAAC;EAEV,MAAM,MAAM,KAAK,KAAK,QAAQ,IAAI,EAAE;EACpC,MAAM,SAAS,MAAM,KAAK,GACvB,OAAO,CAAC,CACR,KAAK,WAAW,CAAC,CACjB,MAAM,IAAI,GAAG,YAAY,OAAO,KAAK,GAAG,QAAQ,YAAY,SAAS,GAAG,CAAC,CAAC,CAAC,CAC3E,QAAQ,IAAI,YAAY,GAAG,CAAC;EAC/B,MAAM,QAAQ,MAAM,KAAK,GACtB,OAAO,CAAC,CACR,KAAK,UAAU,CAAC,CAChB,MAAM,IAAI,GAAG,WAAW,OAAO,KAAK,GAAG,QAAQ,WAAW,SAAS,GAAG,CAAC,CAAC,CAAC,CACzE,QAAQ,IAAI,WAAW,GAAG,CAAC;EAC9B,OAAO,KAAK,KAAK,WAAW;GAC1B,GAAG;GACH,QAAQ,OAAO,QAAQ,UAAU,MAAM,YAAY,MAAM,EAAE;GAC3D,OAAO,MAAM,QAAQ,SAAS,KAAK,YAAY,MAAM,EAAE;EACzD,EAAE;CACJ;AACF;AACA,kBAAkB,wBAAwB"}
1
+ {"version":3,"file":"content.js","names":[],"sources":["../../src/repositories/content.ts"],"sourcesContent":["// content — Drizzle repository for the course aggregate root (implements the core\n// outbound `ContentRepository` port). Org-scoped: every method takes the domain\n// `organizations.id` and constrains its queries to that tenant. Rows come back\n// as stored — derived counts and cross-entity composition live in reporting.\nimport {\n eq,\n and,\n sql,\n count,\n asc,\n desc,\n ilike,\n or,\n inArray,\n gt,\n lt,\n type SQL,\n type AnyColumn,\n} from \"drizzle-orm\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type { DbExecutor, Tx } from \"../client.js\";\nimport type {\n Activity,\n ActivityAsset,\n AddDownloadAssetInput,\n Bundle,\n BundleItem,\n Community,\n CommunityDigest,\n CommunityStatus,\n ContentRepository,\n Course,\n CourseSettings,\n CourseStatus,\n CreateBundleInput,\n CreateCommunityInput,\n CreateCourseInput,\n CreateDownloadInput,\n CreateSpaceInput,\n DigestSchedule,\n Download,\n DownloadAsset,\n DownloadStatus,\n Event,\n EventAsset,\n EventPatch,\n EventSettings,\n EventStatus,\n AddEventAssetInput,\n CreateEventInput,\n ListEventsQuery,\n ListBundlesQuery,\n ListCommunitiesQuery,\n ListCoursesQuery,\n ListDownloadsQuery,\n Module,\n Page,\n PatchCommunitySettingsInput,\n SaveActivityInput,\n SetSpaceTopicsInput,\n Space,\n SpaceAccessRule,\n SpaceDetail,\n UpdateBundleInput,\n UpdateCommunityInput,\n UpdateCourseInput,\n UpdateDownloadInput,\n UpdateSpaceInput,\n} from \"@headless-lms/core/content\";\nimport {\n communitySettingsDefaults,\n courseSettingsDefaults,\n digestScheduleSchema,\n eventSettingsDefaults,\n spaceSettingsDefaults,\n type Setting,\n} from \"@headless-lms/core/schemas\";\nimport {\n bundles,\n bundleItems,\n communities,\n contentItems,\n courses,\n modules,\n activities,\n activityAssets,\n downloads,\n downloadAssets,\n eventAssets,\n events,\n spaceLinks,\n spaceTopics,\n spaces,\n} from \"../schema/content.js\";\nimport { settings } from \"../schema/settings.js\";\nimport { posts } from \"../schema/discussion.js\";\nimport { deepMerge } from \"./settings.js\";\nimport { genId } from \"@headless-lms/core/shared/id\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport { NotFoundError, ConflictError } from \"@headless-lms/core/shared/errors\";\nimport { isUniqueViolation, pgErrorFields, translateDbErrors } from \"./pg-errors.js\";\n\nconst LIBRARY_ONLY = \"An activity can only use ready files from the media library\";\nconst MISSING_ASSET_DETAIL = /^Key \\(org_id, asset_id\\)=\\((?:[^,]+), (.+?)\\) is not present/;\n\nfunction refusedLink(err: unknown): ConflictError | undefined {\n const pg = pgErrorFields(err);\n if (pg?.code !== \"23503\") {\n return undefined;\n }\n const missing = pg.detail?.match(MISSING_ASSET_DETAIL)?.[1];\n const message = missing ? `${LIBRARY_ONLY}. Not available: ${missing}` : LIBRARY_ONLY;\n return Object.assign(new ConflictError(message), { cause: err });\n}\n\n// A course's settings live in the shared `settings` table (namespace\n// 'content', scoped by course id) — joined here so the repository answers in\n// whole courses regardless of where the pieces are stored.\nexport const SETTINGS_NAMESPACE = \"content\";\n\nconst courseSettingsJoin = and(\n eq(settings.orgId, courses.orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, courses.id),\n);\n\nconst communitySettingsJoin = and(\n eq(settings.orgId, communities.orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, communities.id),\n);\n\nconst eventSettingsJoin = and(\n eq(settings.orgId, events.orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, events.id),\n);\n\nfunction toEvent(\n row: typeof events.$inferSelect,\n storedSettings: Record<string, unknown> | null,\n): Event {\n return {\n ...row,\n status: row.status as EventStatus,\n settings: { ...eventSettingsDefaults, ...storedSettings } as EventSettings,\n };\n}\n\nconst eventSortColumns: Record<string, AnyColumn | SQL> = {\n startsAt: events.startsAt,\n title: events.title,\n};\n\nfunction storedDigest(value: unknown): DigestSchedule | null {\n const parsed = digestScheduleSchema.safeParse(value);\n return parsed.success ? parsed.data : null;\n}\n\nfunction toCommunity(\n row: typeof communities.$inferSelect,\n storedSettings: Record<string, unknown> | null,\n): Community {\n const raw = storedSettings?.dm;\n const stored =\n raw && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n return {\n ...row,\n status: row.status as CommunityStatus,\n settings: {\n dm: { ...communitySettingsDefaults.dm, ...stored } as Community[\"settings\"][\"dm\"],\n digest: storedDigest(storedSettings?.digest),\n },\n };\n}\n\nfunction toSpace(row: typeof spaces.$inferSelect): Space {\n return {\n ...row,\n accessRule: row.accessRule as SpaceAccessRule,\n settings: { ...spaceSettingsDefaults, ...row.settings },\n };\n}\n\nconst communitySortColumns: Record<string, AnyColumn | SQL> = {\n title: communities.title,\n slug: communities.slug,\n status: communities.status,\n createdAt: communities.createdAt,\n updatedAt: communities.updatedAt,\n};\n\nfunction toCourse(\n row: typeof courses.$inferSelect,\n storedSettings: Record<string, unknown> | null,\n): Course {\n return {\n ...row,\n status: row.status as CourseStatus,\n settings: { ...courseSettingsDefaults, ...storedSettings } as Course[\"settings\"],\n };\n}\n\n// Columns a caller may sort by. Default falls back to createdAt desc.\nconst sortColumns: Record<string, AnyColumn | SQL> = {\n title: courses.title,\n slug: courses.slug,\n status: courses.status,\n category: courses.category,\n createdAt: courses.createdAt,\n updatedAt: courses.updatedAt,\n};\n\nconst downloadSortColumns: Record<string, AnyColumn | SQL> = {\n title: downloads.title,\n slug: downloads.slug,\n status: downloads.status,\n category: downloads.category,\n createdAt: downloads.createdAt,\n updatedAt: downloads.updatedAt,\n};\n\nfunction toDownload(row: typeof downloads.$inferSelect): Download {\n return { ...row, status: row.status as DownloadStatus };\n}\n\nconst bundleSortColumns: Record<string, AnyColumn | SQL> = {\n name: bundles.name,\n createdAt: bundles.createdAt,\n updatedAt: bundles.updatedAt,\n};\n\nexport class DrizzleContentRepository implements ContentRepository {\n constructor(\n private readonly db: DbExecutor,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n /** On the root db this opens a transaction; tx-bound (ContentUnitOfWork) it\n * nests as a savepoint inside the surrounding transaction. */\n private tx<T>(fn: (tx: Tx) => Promise<T>): Promise<T> {\n return (this.db as NodePgDatabase).transaction(fn);\n }\n\n async listCourses(orgId: string, query: ListCoursesQuery, ids?: string[]): Promise<Page<Course>> {\n if (ids?.length === 0) {\n return { rows: [], total: 0, page: query.page, pageSize: query.pageSize };\n }\n const conditions: SQL[] = [eq(courses.orgId, orgId)];\n if (ids) {\n conditions.push(inArray(courses.id, ids));\n }\n if (query.status) {\n conditions.push(eq(courses.status, query.status));\n }\n if (query.category) {\n conditions.push(eq(courses.category, query.category));\n }\n\n const search = query.search?.trim();\n if (search) {\n const like = `%${search}%`;\n const match = or(ilike(courses.title, like), ilike(courses.category, like));\n if (match) {\n conditions.push(match);\n }\n }\n\n const where = and(...conditions);\n\n // Resolve sort: `-` prefix = desc, default createdAt desc.\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in sortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = sortColumns[sortKey] ?? courses.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n\n const rows = await this.db\n .select({ course: courses, settingsValue: settings.value })\n .from(courses)\n .leftJoin(settings, courseSettingsJoin)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const [totalRow] = await this.db.select({ value: count() }).from(courses).where(where);\n\n return {\n rows: rows.map((row) => toCourse(row.course, row.settingsValue)),\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findCourseById(orgId: string, id: string): Promise<Course | null> {\n const [row] = await this.db\n .select({ course: courses, settingsValue: settings.value })\n .from(courses)\n .leftJoin(settings, courseSettingsJoin)\n .where(and(eq(courses.orgId, orgId), eq(courses.id, id)))\n .limit(1);\n return row ? toCourse(row.course, row.settingsValue) : null;\n }\n\n async createCourse(orgId: string, input: CreateCourseInput, slug: string): Promise<Course> {\n // Registry row + concrete row share one id. Both inserts run on the same\n // executor — mutations reach this repository tx-bound (ContentUnitOfWork),\n // so they commit or roll back together.\n const id = genId(\"course\");\n await this.db.insert(contentItems).values({ orgId, id, type: \"course\" });\n const [inserted] = await this.db\n .insert(courses)\n .values({\n orgId,\n id,\n title: input.title,\n slug,\n description: input.description ?? \"\",\n category: input.category ?? \"\",\n })\n .returning({ id: courses.id });\n if (!inserted) {\n throw new Error(\"failed to insert course\");\n }\n const created = await this.findCourseById(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created course\");\n }\n return created;\n }\n\n async updateCourse(orgId: string, id: string, patch: UpdateCourseInput): Promise<Course | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.title !== undefined) {\n set.title = patch.title;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.category !== undefined) {\n set.category = patch.category;\n }\n if (patch.status !== undefined) {\n set.status = patch.status;\n }\n // null is meaningful here — it clears the cover — so only `undefined` skips.\n if (patch.thumbnailAssetId !== undefined) {\n set.thumbnailAssetId = patch.thumbnailAssetId;\n }\n\n const [updated] = await this.db\n .update(courses)\n .set(set)\n .where(and(eq(courses.orgId, orgId), eq(courses.id, id)))\n .returning({ id: courses.id });\n if (!updated) {\n return null;\n }\n return this.findCourseById(orgId, id);\n }\n\n async patchCourseSettings(\n orgId: string,\n id: string,\n value: Partial<CourseSettings>,\n ): Promise<CourseSettings> {\n const merged = await this.patchSettings(orgId, id, value);\n return { ...courseSettingsDefaults, ...merged } as CourseSettings;\n }\n\n async deleteCourse(orgId: string, id: string): Promise<boolean> {\n // Deletes go through the registry: cascades to the course row and its\n // entitlements in one statement. Never delete from `courses` directly —\n // that would strand the registry row.\n const deleted = await this.db\n .delete(contentItems)\n .where(\n and(\n eq(contentItems.orgId, orgId),\n eq(contentItems.id, id),\n eq(contentItems.type, \"course\"),\n ),\n )\n .returning({ id: contentItems.id });\n return deleted.length > 0;\n }\n\n // --- modules & activities ----------------------------------------------\n // Every mutation returns the course's full ordered module list (by `seq`),\n // so `listModules` runs at the end of every mutation within the same\n // transaction. An Activity's media is the many-to-many `activity_assets`\n // join: `saveActivity` upserts the row and replaces its asset links; deletes\n // drop the links first (they FK the activity), then the activity rows.\n\n listCourseModules(orgId: string, courseId: string): Promise<Module[]> {\n return this.tx((tx) => this.listModules(tx, orgId, courseId));\n }\n\n async listActivitiesForCourse(orgId: string, courseId: string): Promise<Activity[]> {\n return this.db\n .select()\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.courseId, courseId)))\n .orderBy(activities.moduleId, activities.seq);\n }\n\n // Bare join-table rows; the activities join only scopes them to the course.\n async listActivityAssetsForCourse(orgId: string, courseId: string): Promise<ActivityAsset[]> {\n return this.db\n .select({\n orgId: activityAssets.orgId,\n id: activityAssets.id,\n activityId: activityAssets.activityId,\n assetId: activityAssets.assetId,\n seq: activityAssets.seq,\n createdAt: activityAssets.createdAt,\n updatedAt: activityAssets.updatedAt,\n })\n .from(activityAssets)\n .innerJoin(\n activities,\n and(\n eq(activities.orgId, activityAssets.orgId),\n eq(activities.id, activityAssets.activityId),\n ),\n )\n .where(and(eq(activityAssets.orgId, orgId), eq(activities.courseId, courseId)))\n .orderBy(activityAssets.activityId, activityAssets.seq);\n }\n\n async findActivity(orgId: string, activityId: string): Promise<Activity | null> {\n const [row] = await this.db\n .select()\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.id, activityId)))\n .limit(1);\n return row ?? null;\n }\n\n async findModule(orgId: string, moduleId: string): Promise<Module | null> {\n const [row] = await this.db\n .select()\n .from(modules)\n .where(and(eq(modules.orgId, orgId), eq(modules.id, moduleId)))\n .limit(1);\n return row ?? null;\n }\n\n /** The course's full ordered module list. */\n private async listModules(tx: Tx, orgId: string, courseId: string): Promise<Module[]> {\n return tx\n .select()\n .from(modules)\n .where(and(eq(modules.orgId, orgId), eq(modules.courseId, courseId)))\n .orderBy(modules.seq);\n }\n\n /** Assert the module belongs to the org + course; throw otherwise. */\n private async assertModule(\n tx: Tx,\n orgId: string,\n courseId: string,\n moduleId: string,\n ): Promise<void> {\n const [row] = await tx\n .select({ id: modules.id })\n .from(modules)\n .where(\n and(eq(modules.orgId, orgId), eq(modules.id, moduleId), eq(modules.courseId, courseId)),\n )\n .limit(1);\n if (!row) {\n throw new NotFoundError(\"Module\", moduleId);\n }\n }\n\n /** Replace an activity's ordered asset links with `assetIds`. */\n private async replaceActivityAssets(\n tx: Tx,\n orgId: string,\n activityId: string,\n assetIds: string[],\n ): Promise<void> {\n await tx\n .delete(activityAssets)\n .where(and(eq(activityAssets.orgId, orgId), eq(activityAssets.activityId, activityId)));\n if (assetIds.length) {\n try {\n await tx\n .insert(activityAssets)\n .values(assetIds.map((assetId, i) => ({ orgId, activityId, assetId, seq: i })));\n } catch (err) {\n throw refusedLink(err) ?? err;\n }\n }\n }\n\n createModule(orgId: string, courseId: string, title: string): Promise<Module[]> {\n return this.tx(async (tx) => {\n const existing = await tx\n .select({ seq: modules.seq })\n .from(modules)\n .where(and(eq(modules.orgId, orgId), eq(modules.courseId, courseId)));\n const nextSeq = existing.reduce((max, r) => Math.max(max, r.seq), -1) + 1;\n await tx.insert(modules).values({ orgId, courseId, title, seq: nextSeq });\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n updateModule(\n orgId: string,\n courseId: string,\n moduleId: string,\n title: string,\n ): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n await tx\n .update(modules)\n .set({ title })\n .where(\n and(eq(modules.orgId, orgId), eq(modules.id, moduleId), eq(modules.courseId, courseId)),\n );\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n deleteModule(orgId: string, courseId: string, moduleId: string): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n\n // Drop the module's activities: their asset links first (they FK the\n // activity), then the activity rows, then the module.\n const activityRows = await tx\n .select({ id: activities.id })\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.moduleId, moduleId)));\n const activityIds = activityRows.map((a) => a.id);\n\n if (activityIds.length) {\n await tx\n .delete(activityAssets)\n .where(\n and(eq(activityAssets.orgId, orgId), inArray(activityAssets.activityId, activityIds)),\n );\n await tx\n .delete(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.moduleId, moduleId)));\n }\n\n await tx\n .delete(modules)\n .where(\n and(eq(modules.orgId, orgId), eq(modules.id, moduleId), eq(modules.courseId, courseId)),\n );\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n reorderModules(orgId: string, courseId: string, orderedIds: string[]): Promise<Module[]> {\n return this.tx(async (tx) => {\n // Two-phase to dodge the unique(org_id, id) is fine, but modules have no\n // unique(seq); still, park then assign to stay consistent with activities.\n for (let i = 0; i < orderedIds.length; i++) {\n await tx\n .update(modules)\n .set({ seq: i })\n .where(\n and(\n eq(modules.orgId, orgId),\n eq(modules.courseId, courseId),\n eq(modules.id, orderedIds[i]!),\n ),\n );\n }\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n reorderActivities(\n orgId: string,\n courseId: string,\n moduleId: string,\n orderedIds: string[],\n ): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n // Two-phase to dodge the unique(org_id, module_id, seq) constraint mid-swap:\n // park rows at negative seqs, then assign the final 0..n-1.\n for (let i = 0; i < orderedIds.length; i++) {\n await tx\n .update(activities)\n .set({ seq: -(i + 1) })\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.moduleId, moduleId),\n eq(activities.id, orderedIds[i]!),\n ),\n );\n }\n for (let i = 0; i < orderedIds.length; i++) {\n await tx\n .update(activities)\n .set({ seq: i })\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.moduleId, moduleId),\n eq(activities.id, orderedIds[i]!),\n ),\n );\n }\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n saveActivity(\n orgId: string,\n courseId: string,\n moduleId: string,\n input: SaveActivityInput,\n activityId?: string,\n ): Promise<{ modules: Module[]; activity: Activity }> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n\n let savedId: string;\n if (activityId) {\n const [existing] = await tx\n .select({ id: activities.id })\n .from(activities)\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.id, activityId),\n eq(activities.moduleId, moduleId),\n ),\n )\n .limit(1);\n if (!existing) {\n throw new NotFoundError(\"Activity\", activityId);\n }\n\n await tx\n .update(activities)\n .set({ settings: input.settings ?? null })\n .where(and(eq(activities.orgId, orgId), eq(activities.id, activityId)));\n if (input.assetIds !== undefined) {\n await this.replaceActivityAssets(tx, orgId, activityId, input.assetIds);\n }\n savedId = activityId;\n } else {\n const existing = await tx\n .select({ seq: activities.seq })\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.moduleId, moduleId)));\n const nextSeq = existing.reduce((max, r) => Math.max(max, r.seq), -1) + 1;\n\n const [ins] = await tx\n .insert(activities)\n .values({ orgId, moduleId, courseId, seq: nextSeq, settings: input.settings ?? null })\n .returning({ id: activities.id });\n if (!ins) {\n throw new Error(\"failed to insert activity\");\n }\n await this.replaceActivityAssets(tx, orgId, ins.id, input.assetIds ?? []);\n savedId = ins.id;\n }\n\n const [activity] = await tx\n .select()\n .from(activities)\n .where(and(eq(activities.orgId, orgId), eq(activities.id, savedId)))\n .limit(1);\n if (!activity) {\n throw new Error(\"failed to load saved activity\");\n }\n return { modules: await this.listModules(tx, orgId, courseId), activity };\n });\n }\n\n deleteActivity(\n orgId: string,\n courseId: string,\n moduleId: string,\n activityId: string,\n ): Promise<Module[]> {\n return this.tx(async (tx) => {\n await this.assertModule(tx, orgId, courseId, moduleId);\n\n // Drop the asset links first (they FK the activity), then the activity.\n await tx\n .delete(activityAssets)\n .where(and(eq(activityAssets.orgId, orgId), eq(activityAssets.activityId, activityId)));\n await tx\n .delete(activities)\n .where(\n and(\n eq(activities.orgId, orgId),\n eq(activities.id, activityId),\n eq(activities.moduleId, moduleId),\n ),\n );\n\n return this.listModules(tx, orgId, courseId);\n });\n }\n\n // --- downloads --------------------------------------------------------\n\n async listDownloads(\n orgId: string,\n query: ListDownloadsQuery,\n ids?: string[],\n ): Promise<Page<Download>> {\n if (ids?.length === 0) {\n return { rows: [], total: 0, page: query.page, pageSize: query.pageSize };\n }\n const conditions: SQL[] = [eq(downloads.orgId, orgId)];\n if (ids) {\n conditions.push(inArray(downloads.id, ids));\n }\n if (query.status) {\n conditions.push(eq(downloads.status, query.status));\n }\n if (query.category) {\n conditions.push(eq(downloads.category, query.category));\n }\n\n const search = query.search?.trim();\n if (search) {\n const like = `%${search}%`;\n const match = or(ilike(downloads.title, like), ilike(downloads.category, like));\n if (match) {\n conditions.push(match);\n }\n }\n\n const where = and(...conditions);\n\n // Resolve sort: `-` prefix = desc, default createdAt desc.\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in downloadSortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = downloadSortColumns[sortKey] ?? downloads.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n\n const rows = await this.db\n .select()\n .from(downloads)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const [totalRow] = await this.db.select({ value: count() }).from(downloads).where(where);\n\n return {\n rows: rows.map(toDownload),\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async getDownload(orgId: string, id: string): Promise<Download | null> {\n const [row] = await this.db\n .select()\n .from(downloads)\n .where(and(eq(downloads.orgId, orgId), eq(downloads.id, id)))\n .limit(1);\n return row ? toDownload(row) : null;\n }\n\n async createDownload(orgId: string, input: CreateDownloadInput, slug: string): Promise<Download> {\n // Registry row + concrete row share one id, same as courses. Both inserts\n // run on the same executor — mutations reach this repository tx-bound.\n const id = genId(\"download\");\n await this.db.insert(contentItems).values({ orgId, id, type: \"download\" });\n await this.db.insert(downloads).values({\n orgId,\n id,\n title: input.title,\n slug,\n description: input.description ?? \"\",\n category: input.category ?? \"\",\n });\n const created = await this.getDownload(orgId, id);\n if (!created) {\n throw new Error(\"failed to load created download\");\n }\n return created;\n }\n\n async updateDownload(\n orgId: string,\n id: string,\n patch: UpdateDownloadInput,\n ): Promise<Download | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.title !== undefined) {\n set.title = patch.title;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.category !== undefined) {\n set.category = patch.category;\n }\n if (patch.status !== undefined) {\n set.status = patch.status;\n }\n if (patch.thumbnailAssetId !== undefined) {\n set.thumbnailAssetId = patch.thumbnailAssetId;\n }\n\n const [updated] = await this.db\n .update(downloads)\n .set(set)\n .where(and(eq(downloads.orgId, orgId), eq(downloads.id, id)))\n .returning({ id: downloads.id });\n if (!updated) {\n return null;\n }\n return this.getDownload(orgId, id);\n }\n\n async deleteDownload(orgId: string, id: string): Promise<boolean> {\n // Through the registry, so the cascade reaches the download row, its asset\n // links, and the grants. Never delete from `downloads` directly.\n const deleted = await this.db\n .delete(contentItems)\n .where(\n and(\n eq(contentItems.orgId, orgId),\n eq(contentItems.id, id),\n eq(contentItems.type, \"download\"),\n ),\n )\n .returning({ id: contentItems.id });\n return deleted.length > 0;\n }\n\n async listDownloadAssets(orgId: string, downloadId: string): Promise<DownloadAsset[]> {\n return this.db\n .select()\n .from(downloadAssets)\n .where(and(eq(downloadAssets.orgId, orgId), eq(downloadAssets.downloadId, downloadId)))\n .orderBy(asc(downloadAssets.seq));\n }\n\n /** Assert the download exists in the org; throw otherwise. */\n private async assertDownload(orgId: string, downloadId: string): Promise<void> {\n const [row] = await this.db\n .select({ id: downloads.id })\n .from(downloads)\n .where(and(eq(downloads.orgId, orgId), eq(downloads.id, downloadId)))\n .limit(1);\n if (!row) {\n throw new NotFoundError(\"Download\", downloadId);\n }\n }\n\n async addDownloadAsset(\n orgId: string,\n downloadId: string,\n input: AddDownloadAssetInput,\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n const [maxRow] = await this.db\n .select({ maxSeq: sql<number | null>`max(${downloadAssets.seq})` })\n .from(downloadAssets)\n .where(and(eq(downloadAssets.orgId, orgId), eq(downloadAssets.downloadId, downloadId)));\n const nextSeq = (maxRow?.maxSeq ?? -1) + 1;\n try {\n await this.db.insert(downloadAssets).values({\n orgId,\n downloadId,\n assetId: input.assetId,\n seq: nextSeq,\n displayName: input.displayName ?? null,\n });\n } catch (err) {\n if (isUniqueViolation(err)) {\n throw new ConflictError(\"This asset is already linked to the download\");\n }\n throw err;\n }\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n async removeDownloadAsset(\n orgId: string,\n downloadId: string,\n assetId: string,\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n await this.db\n .delete(downloadAssets)\n .where(\n and(\n eq(downloadAssets.orgId, orgId),\n eq(downloadAssets.downloadId, downloadId),\n eq(downloadAssets.assetId, assetId),\n ),\n );\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n async renameDownloadAsset(\n orgId: string,\n downloadId: string,\n assetId: string,\n displayName: string | null,\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n await this.db\n .update(downloadAssets)\n .set({ displayName })\n .where(\n and(\n eq(downloadAssets.orgId, orgId),\n eq(downloadAssets.downloadId, downloadId),\n eq(downloadAssets.assetId, assetId),\n ),\n );\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n async reorderDownloadAssets(\n orgId: string,\n downloadId: string,\n assetIds: string[],\n ): Promise<DownloadAsset[]> {\n await this.assertDownload(orgId, downloadId);\n // The service has already verified this is exactly the current set.\n for (const [seq, assetId] of assetIds.entries()) {\n await this.db\n .update(downloadAssets)\n .set({ seq })\n .where(\n and(\n eq(downloadAssets.orgId, orgId),\n eq(downloadAssets.downloadId, downloadId),\n eq(downloadAssets.assetId, assetId),\n ),\n );\n }\n return this.listDownloadAssets(orgId, downloadId);\n }\n\n async listEvents(orgId: string, query: ListEventsQuery, ids?: string[]): Promise<Page<Event>> {\n if (ids?.length === 0) {\n return { rows: [], total: 0, page: query.page, pageSize: query.pageSize };\n }\n const conditions: SQL[] = [eq(events.orgId, orgId)];\n if (ids) {\n conditions.push(inArray(events.id, ids));\n }\n if (query.status) {\n conditions.push(eq(events.status, query.status));\n }\n if (query.from) {\n conditions.push(gt(events.endsAt, query.from));\n }\n if (query.to) {\n conditions.push(lt(events.startsAt, query.to));\n }\n const search = query.search?.trim();\n if (search) {\n conditions.push(ilike(events.title, `%${search}%`));\n }\n const where = and(...conditions);\n\n const sort = query.sort ?? \"startsAt\";\n const isDesc = sort.startsWith(\"-\");\n const sortExpr = eventSortColumns[isDesc ? sort.slice(1) : sort] ?? events.startsAt;\n const direction = isDesc ? desc : asc;\n\n const rows = await this.db\n .select({ event: events, settingsValue: settings.value })\n .from(events)\n .leftJoin(settings, eventSettingsJoin)\n .where(where)\n .orderBy(direction(sortExpr), direction(events.id))\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const [totalRow] = await this.db.select({ value: count() }).from(events).where(where);\n\n return {\n rows: rows.map((row) => toEvent(row.event, row.settingsValue)),\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findEventById(orgId: string, id: string): Promise<Event | null> {\n const [row] = await this.db\n .select({ event: events, settingsValue: settings.value })\n .from(events)\n .leftJoin(settings, eventSettingsJoin)\n .where(and(eq(events.orgId, orgId), eq(events.id, id)))\n .limit(1);\n return row ? toEvent(row.event, row.settingsValue) : null;\n }\n\n async findEventsByIds(orgId: string, ids: string[]): Promise<Event[]> {\n const rows = await this.db\n .select({ event: events, settingsValue: settings.value })\n .from(events)\n .leftJoin(settings, eventSettingsJoin)\n .where(and(eq(events.orgId, orgId), inArray(events.id, ids)));\n return rows.map((row) => toEvent(row.event, row.settingsValue));\n }\n\n async createEvent(orgId: string, input: Omit<CreateEventInput, \"settings\">): Promise<Event> {\n const id = genId(\"contentEvent\");\n await this.db.insert(contentItems).values({ orgId, id, type: \"event\" });\n await this.db.insert(events).values({\n orgId,\n id,\n title: input.title,\n description: input.description ?? \"\",\n startsAt: input.startsAt,\n endsAt: input.endsAt,\n timeZone: input.timeZone,\n ...(input.location && { location: input.location }),\n });\n const created = await this.findEventById(orgId, id);\n if (!created) {\n throw new Error(\"failed to load created event\");\n }\n return created;\n }\n\n async updateEvent(orgId: string, id: string, patch: EventPatch): Promise<Event | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n for (const key of [\n \"title\",\n \"description\",\n \"startsAt\",\n \"endsAt\",\n \"timeZone\",\n \"location\",\n \"thumbnailAssetId\",\n \"recording\",\n \"status\",\n ] as const) {\n if (patch[key] !== undefined) {\n set[key] = patch[key];\n }\n }\n const [updated] = await this.db\n .update(events)\n .set(set)\n .where(and(eq(events.orgId, orgId), eq(events.id, id)))\n .returning({ id: events.id });\n if (!updated) {\n return null;\n }\n return this.findEventById(orgId, id);\n }\n\n async patchEventSettings(\n orgId: string,\n id: string,\n value: Partial<EventSettings>,\n ): Promise<EventSettings> {\n const merged = await this.patchSettings(orgId, id, value);\n return { ...eventSettingsDefaults, ...merged } as EventSettings;\n }\n\n async deleteEvent(orgId: string, id: string): Promise<boolean> {\n const deleted = await this.db\n .delete(contentItems)\n .where(\n and(eq(contentItems.orgId, orgId), eq(contentItems.id, id), eq(contentItems.type, \"event\")),\n )\n .returning({ id: contentItems.id });\n return deleted.length > 0;\n }\n\n async deleteEventSettings(orgId: string, id: string): Promise<void> {\n await this.db\n .delete(settings)\n .where(\n and(\n eq(settings.orgId, orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, id),\n ),\n );\n }\n\n async listEventAssets(orgId: string, eventId: string): Promise<EventAsset[]> {\n return this.db\n .select()\n .from(eventAssets)\n .where(and(eq(eventAssets.orgId, orgId), eq(eventAssets.eventId, eventId)))\n .orderBy(asc(eventAssets.seq));\n }\n\n async listEventAssetsFor(orgId: string, eventIds: string[]): Promise<EventAsset[]> {\n return this.db\n .select()\n .from(eventAssets)\n .where(and(eq(eventAssets.orgId, orgId), inArray(eventAssets.eventId, eventIds)))\n .orderBy(asc(eventAssets.eventId), asc(eventAssets.seq));\n }\n\n private async assertEvent(orgId: string, eventId: string): Promise<void> {\n const [row] = await this.db\n .select({ id: events.id })\n .from(events)\n .where(and(eq(events.orgId, orgId), eq(events.id, eventId)))\n .limit(1);\n if (!row) {\n throw new NotFoundError(\"Event\", eventId);\n }\n }\n\n async addEventAsset(\n orgId: string,\n eventId: string,\n input: AddEventAssetInput,\n ): Promise<EventAsset[]> {\n await this.assertEvent(orgId, eventId);\n const [maxRow] = await this.db\n .select({ maxSeq: sql<number | null>`max(${eventAssets.seq})` })\n .from(eventAssets)\n .where(and(eq(eventAssets.orgId, orgId), eq(eventAssets.eventId, eventId)));\n const nextSeq = (maxRow?.maxSeq ?? -1) + 1;\n try {\n await this.db.insert(eventAssets).values({\n orgId,\n eventId,\n assetId: input.assetId,\n seq: nextSeq,\n displayName: input.displayName ?? null,\n });\n } catch (err) {\n if (isUniqueViolation(err)) {\n throw new ConflictError(\"This asset is already linked to the event\");\n }\n throw err;\n }\n return this.listEventAssets(orgId, eventId);\n }\n\n async removeEventAsset(orgId: string, eventId: string, assetId: string): Promise<EventAsset[]> {\n await this.assertEvent(orgId, eventId);\n await this.db\n .delete(eventAssets)\n .where(\n and(\n eq(eventAssets.orgId, orgId),\n eq(eventAssets.eventId, eventId),\n eq(eventAssets.assetId, assetId),\n ),\n );\n return this.listEventAssets(orgId, eventId);\n }\n\n async renameEventAsset(\n orgId: string,\n eventId: string,\n assetId: string,\n displayName: string | null,\n ): Promise<EventAsset[]> {\n await this.assertEvent(orgId, eventId);\n await this.db\n .update(eventAssets)\n .set({ displayName })\n .where(\n and(\n eq(eventAssets.orgId, orgId),\n eq(eventAssets.eventId, eventId),\n eq(eventAssets.assetId, assetId),\n ),\n );\n return this.listEventAssets(orgId, eventId);\n }\n\n async reorderEventAssets(\n orgId: string,\n eventId: string,\n assetIds: string[],\n ): Promise<EventAsset[]> {\n await this.assertEvent(orgId, eventId);\n for (const [seq, assetId] of assetIds.entries()) {\n await this.db\n .update(eventAssets)\n .set({ seq })\n .where(\n and(\n eq(eventAssets.orgId, orgId),\n eq(eventAssets.eventId, eventId),\n eq(eventAssets.assetId, assetId),\n ),\n );\n }\n return this.listEventAssets(orgId, eventId);\n }\n\n // --- bundles ----------------------------------------------------------\n\n async listBundles(orgId: string, query: ListBundlesQuery): Promise<Page<Bundle>> {\n const conditions: SQL[] = [eq(bundles.orgId, orgId)];\n\n const search = query.search?.trim();\n if (search) {\n conditions.push(ilike(bundles.name, `%${search}%`));\n }\n\n const where = and(...conditions);\n\n // Resolve sort: `-` prefix = desc, default createdAt desc.\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in bundleSortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = bundleSortColumns[sortKey] ?? bundles.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n\n const rows = await this.db\n .select()\n .from(bundles)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const [totalRow] = await this.db.select({ value: count() }).from(bundles).where(where);\n\n return {\n rows,\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findBundleById(orgId: string, id: string): Promise<Bundle | null> {\n const [row] = await this.db\n .select()\n .from(bundles)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, id)))\n .limit(1);\n return row ?? null;\n }\n\n /** The bundle and its initial items are one write: an unknown content id\n * rolls the bundle back rather than leaving it created and half-filled. */\n async createBundle(orgId: string, input: CreateBundleInput): Promise<Bundle> {\n const contentIds = [...new Set(input.contentIds ?? [])];\n return this.tx(async (tx) => {\n // A bundle is not content: it groups registry rows, so it gets no\n // content_items row of its own.\n const [row] = await tx\n .insert(bundles)\n .values({ orgId, id: genId(\"bundle\"), name: input.name })\n .returning();\n if (!row) {\n throw new Error(\"failed to load created bundle\");\n }\n\n if (contentIds.length > 0) {\n // Against the registry, so any content type can be bundled.\n const found = await tx\n .select({ id: contentItems.id })\n .from(contentItems)\n .where(and(eq(contentItems.orgId, orgId), inArray(contentItems.id, contentIds)));\n const known = new Set(found.map((item) => item.id));\n const missing = contentIds.find((id) => !known.has(id));\n if (missing) {\n throw new NotFoundError(\"Content\", missing);\n }\n\n await tx\n .insert(bundleItems)\n .values(contentIds.map((contentId) => ({ orgId, bundleId: row.id, contentId })));\n }\n\n return row;\n });\n }\n\n async updateBundle(orgId: string, id: string, patch: UpdateBundleInput): Promise<Bundle | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.name !== undefined) {\n set.name = patch.name;\n }\n\n const [row] = await this.db\n .update(bundles)\n .set(set)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, id)))\n .returning();\n return row ?? null;\n }\n\n async deleteBundle(orgId: string, id: string): Promise<boolean> {\n // Cascades to the bundle's items and to the grants made on it. The content\n // itself is untouched — a bundle only groups registry rows.\n const deleted = await this.db\n .delete(bundles)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, id)))\n .returning({ id: bundles.id });\n return deleted.length > 0;\n }\n\n async listBundleItems(orgId: string, bundleId: string): Promise<BundleItem[]> {\n return this.db\n .select()\n .from(bundleItems)\n .where(and(eq(bundleItems.orgId, orgId), eq(bundleItems.bundleId, bundleId)))\n .orderBy(asc(bundleItems.createdAt));\n }\n\n /** Assert the bundle exists in the org; throw otherwise. */\n private async assertBundle(orgId: string, bundleId: string): Promise<void> {\n const [row] = await this.db\n .select({ id: bundles.id })\n .from(bundles)\n .where(and(eq(bundles.orgId, orgId), eq(bundles.id, bundleId)))\n .limit(1);\n if (!row) {\n throw new NotFoundError(\"Bundle\", bundleId);\n }\n }\n\n async addBundleItem(orgId: string, bundleId: string, contentId: string): Promise<BundleItem[]> {\n await this.assertBundle(orgId, bundleId);\n // Against the registry, so any content type can be bundled.\n const [item] = await this.db\n .select({ id: contentItems.id })\n .from(contentItems)\n .where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, contentId)))\n .limit(1);\n if (!item) {\n throw new NotFoundError(\"Content\", contentId);\n }\n await this.db\n .insert(bundleItems)\n .values({ orgId, bundleId, contentId })\n .onConflictDoNothing({\n target: [bundleItems.orgId, bundleItems.bundleId, bundleItems.contentId],\n });\n return this.listBundleItems(orgId, bundleId);\n }\n\n async removeBundleItem(\n orgId: string,\n bundleId: string,\n contentId: string,\n ): Promise<BundleItem[]> {\n await this.assertBundle(orgId, bundleId);\n await this.db\n .delete(bundleItems)\n .where(\n and(\n eq(bundleItems.orgId, orgId),\n eq(bundleItems.bundleId, bundleId),\n eq(bundleItems.contentId, contentId),\n ),\n );\n return this.listBundleItems(orgId, bundleId);\n }\n\n async listCommunities(\n orgId: string,\n query: ListCommunitiesQuery,\n ids?: string[],\n ): Promise<Page<Community>> {\n if (ids?.length === 0) {\n return { rows: [], total: 0, page: query.page, pageSize: query.pageSize };\n }\n const conditions: SQL[] = [eq(communities.orgId, orgId)];\n if (ids) {\n conditions.push(inArray(communities.id, ids));\n }\n if (query.status) {\n conditions.push(eq(communities.status, query.status));\n }\n const search = query.search?.trim();\n if (search) {\n const like = `%${search}%`;\n const match = or(ilike(communities.title, like), ilike(communities.description, like));\n if (match) {\n conditions.push(match);\n }\n }\n const where = and(...conditions);\n let sortKey = \"createdAt\";\n let direction: \"asc\" | \"desc\" = \"desc\";\n if (query.sort) {\n const isDesc = query.sort.startsWith(\"-\");\n const key = isDesc ? query.sort.slice(1) : query.sort;\n if (key in communitySortColumns) {\n sortKey = key;\n direction = isDesc ? \"desc\" : \"asc\";\n }\n }\n const sortExpr = communitySortColumns[sortKey] ?? communities.createdAt;\n const orderBy = direction === \"desc\" ? desc(sortExpr) : asc(sortExpr);\n const rows = await this.db\n .select({ community: communities, settingsValue: settings.value })\n .from(communities)\n .leftJoin(settings, communitySettingsJoin)\n .where(where)\n .orderBy(orderBy)\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n const [totalRow] = await this.db.select({ value: count() }).from(communities).where(where);\n return {\n rows: rows.map((row) => toCommunity(row.community, row.settingsValue)),\n total: totalRow?.value ?? 0,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async findCommunityById(orgId: string, id: string): Promise<Community | null> {\n const [row] = await this.db\n .select({ community: communities, settingsValue: settings.value })\n .from(communities)\n .leftJoin(settings, communitySettingsJoin)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, id)))\n .limit(1);\n return row ? toCommunity(row.community, row.settingsValue) : null;\n }\n\n async findCommunitiesByIds(orgId: string, ids: string[]): Promise<Community[]> {\n const rows = await this.db\n .select({ community: communities, settingsValue: settings.value })\n .from(communities)\n .leftJoin(settings, communitySettingsJoin)\n .where(and(eq(communities.orgId, orgId), inArray(communities.id, ids)))\n .orderBy(asc(communities.title), asc(communities.id));\n return rows.map((row) => toCommunity(row.community, row.settingsValue));\n }\n\n async createCommunity(\n orgId: string,\n input: CreateCommunityInput,\n slug: string,\n ): Promise<Community> {\n const id = genId(\"community\");\n await this.db.insert(contentItems).values({ orgId, id, type: \"community\" });\n const [inserted] = await this.db\n .insert(communities)\n .values({\n orgId,\n id,\n title: input.title,\n slug,\n description: input.description ?? \"\",\n })\n .returning({ id: communities.id });\n if (!inserted) {\n throw new Error(\"failed to insert community\");\n }\n const created = await this.findCommunityById(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created community\");\n }\n return created;\n }\n\n async updateCommunity(\n orgId: string,\n id: string,\n patch: UpdateCommunityInput,\n ): Promise<Community | null> {\n const set: Record<string, unknown> = { updatedAt: new Date() };\n if (patch.title !== undefined) {\n set.title = patch.title;\n }\n if (patch.description !== undefined) {\n set.description = patch.description;\n }\n if (patch.status !== undefined) {\n set.status = patch.status;\n }\n if (patch.thumbnailAssetId !== undefined) {\n set.thumbnailAssetId = patch.thumbnailAssetId;\n }\n const [updated] = await this.db\n .update(communities)\n .set(set)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, id)))\n .returning({ id: communities.id });\n if (!updated) {\n return null;\n }\n return this.findCommunityById(orgId, id);\n }\n\n async communitiesWithDigest(): Promise<CommunityDigest[]> {\n const rows = await this.db\n .select({\n orgId: communities.orgId,\n communityId: communities.id,\n digest: sql<unknown>`${settings.value} -> 'digest'`,\n })\n .from(communities)\n .innerJoin(settings, communitySettingsJoin)\n .where(\n and(\n eq(communities.status, \"published\"),\n sql`jsonb_typeof(${settings.value} -> 'digest') = 'object'`,\n ),\n )\n .orderBy(asc(communities.orgId), asc(communities.id));\n return rows.flatMap((row) => {\n const digest = storedDigest(row.digest);\n return digest ? [{ orgId: row.orgId, communityId: row.communityId, digest }] : [];\n });\n }\n\n async patchCommunitySettings(\n orgId: string,\n id: string,\n input: PatchCommunitySettingsInput,\n ): Promise<Community | null> {\n const community = await this.findCommunityById(orgId, id);\n if (!community) {\n return null;\n }\n const { digest, ...dm } = input;\n await this.patchSettings(orgId, id, digest === undefined ? { dm } : { dm, digest });\n return this.findCommunityById(orgId, id);\n }\n\n private async patchSettings(\n orgId: string,\n scopeId: string,\n patch: object,\n ): Promise<Setting[\"value\"]> {\n const key = and(\n eq(settings.orgId, orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, scopeId),\n );\n const value = patch as Setting[\"value\"];\n return this.tx(async (tx) => {\n let [existing] = await tx.select().from(settings).where(key).for(\"update\");\n if (!existing) {\n const [inserted] = await tx\n .insert(settings)\n .values({ orgId, namespace: SETTINGS_NAMESPACE, scopeId, value })\n .onConflictDoNothing()\n .returning();\n if (inserted) {\n return inserted.value;\n }\n [existing] = await tx.select().from(settings).where(key).for(\"update\");\n if (!existing) {\n throw new Error(`settings patch for ${scopeId}: row vanished mid-transaction`);\n }\n }\n const [updated] = await tx\n .update(settings)\n .set({ value: deepMerge(existing.value, patch) as Setting[\"value\"], updatedAt: new Date() })\n .where(key)\n .returning();\n if (!updated) {\n throw new Error(`settings patch for ${scopeId} returned no row`);\n }\n return updated.value;\n });\n }\n\n async deleteCommunity(orgId: string, id: string): Promise<boolean> {\n const deleted = await this.db\n .delete(contentItems)\n .where(\n and(\n eq(contentItems.orgId, orgId),\n eq(contentItems.id, id),\n eq(contentItems.type, \"community\"),\n ),\n )\n .returning({ id: contentItems.id });\n return deleted.length > 0;\n }\n\n async deleteCommunitySettings(orgId: string, id: string): Promise<void> {\n await this.db\n .delete(settings)\n .where(\n and(\n eq(settings.orgId, orgId),\n eq(settings.namespace, SETTINGS_NAMESPACE),\n eq(settings.scopeId, id),\n ),\n );\n }\n\n async listSpaces(orgId: string, communityId: string): Promise<SpaceDetail[]> {\n const rows = await this.db\n .select()\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.communityId, communityId)))\n .orderBy(asc(spaces.seq));\n return this.withSpaceParts(orgId, rows.map(toSpace));\n }\n\n async findSpace(orgId: string, spaceId: string): Promise<SpaceDetail | null> {\n const [row] = await this.db\n .select()\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId)))\n .limit(1);\n if (!row) {\n return null;\n }\n const [detail] = await this.withSpaceParts(orgId, [toSpace(row)]);\n return detail ?? null;\n }\n\n async lockSpace(orgId: string, spaceId: string): Promise<Space | null> {\n const [row] = await this.db\n .select()\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId)))\n .for(\"update\")\n .limit(1);\n return row ? toSpace(row) : null;\n }\n\n async createSpace(\n orgId: string,\n communityId: string,\n input: CreateSpaceInput,\n ): Promise<SpaceDetail> {\n const existing = await this.db\n .select({ seq: spaces.seq })\n .from(spaces)\n .where(and(eq(spaces.orgId, orgId), eq(spaces.communityId, communityId)));\n const nextSeq = existing.reduce((max, row) => Math.max(max, row.seq), -1) + 1;\n const [inserted] = await this.db\n .insert(spaces)\n .values({\n orgId,\n communityId,\n name: input.name,\n description: input.description ?? \"\",\n seq: nextSeq,\n accessRule: input.accessRule ?? \"all_members\",\n capacity: input.capacity ?? null,\n settings: { ...spaceSettingsDefaults, ...input.settings },\n })\n .returning({ id: spaces.id });\n if (!inserted) {\n throw new Error(\"failed to insert space\");\n }\n const created = await this.findSpace(orgId, inserted.id);\n if (!created) {\n throw new Error(\"failed to load created space\");\n }\n return created;\n }\n\n async updateSpace(\n orgId: string,\n communityId: string,\n spaceId: string,\n patch: UpdateSpaceInput,\n ): Promise<SpaceDetail | 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.accessRule !== undefined) {\n set.accessRule = patch.accessRule;\n }\n if (patch.capacity !== undefined) {\n set.capacity = patch.capacity;\n }\n if (patch.settings !== undefined) {\n set.settings = { ...spaceSettingsDefaults, ...patch.settings };\n }\n const [updated] = await this.db\n .update(spaces)\n .set(set)\n .where(\n and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId), eq(spaces.communityId, communityId)),\n )\n .returning({ id: spaces.id });\n if (!updated) {\n return null;\n }\n return this.findSpace(orgId, spaceId);\n }\n\n async deleteSpace(orgId: string, communityId: string, spaceId: string): Promise<boolean> {\n const deleted = await this.db\n .delete(spaces)\n .where(\n and(eq(spaces.orgId, orgId), eq(spaces.id, spaceId), eq(spaces.communityId, communityId)),\n )\n .returning({ id: spaces.id });\n return deleted.length > 0;\n }\n\n async reorderSpaces(\n orgId: string,\n communityId: string,\n orderedIds: string[],\n ): Promise<SpaceDetail[]> {\n for (let i = 0; i < orderedIds.length; i++) {\n await this.db\n .update(spaces)\n .set({ seq: i })\n .where(\n and(\n eq(spaces.orgId, orgId),\n eq(spaces.communityId, communityId),\n eq(spaces.id, orderedIds[i]!),\n ),\n );\n }\n return this.listSpaces(orgId, communityId);\n }\n\n async setSpaceTopics(\n orgId: string,\n spaceId: string,\n input: SetSpaceTopicsInput,\n ): Promise<SpaceDetail> {\n await this.tx(async (tx) => {\n const existing = await tx\n .select({ id: spaceTopics.id })\n .from(spaceTopics)\n .where(and(eq(spaceTopics.orgId, orgId), eq(spaceTopics.spaceId, spaceId)))\n .for(\"update\");\n const kept = new Set(input.topics.flatMap((topic) => (topic.id ? [topic.id] : [])));\n const known = new Set(existing.map((topic) => topic.id));\n for (const id of kept) {\n if (!known.has(id)) {\n throw new NotFoundError(\"Topic\", id);\n }\n }\n const removed = existing.filter((topic) => !kept.has(topic.id)).map((topic) => topic.id);\n if (removed.length) {\n await tx\n .delete(spaceTopics)\n .where(and(eq(spaceTopics.orgId, orgId), inArray(spaceTopics.id, removed)));\n }\n for (let seq = 0; seq < input.topics.length; seq++) {\n const topic = input.topics[seq]!;\n if (topic.id) {\n await tx\n .update(spaceTopics)\n .set({ name: topic.name, seq, updatedAt: new Date() })\n .where(and(eq(spaceTopics.orgId, orgId), eq(spaceTopics.id, topic.id)));\n } else {\n await tx.insert(spaceTopics).values({ orgId, spaceId, name: topic.name, seq });\n }\n }\n });\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async topicsInUse(orgId: string, spaceId: string, topicIds: string[]): Promise<string[]> {\n if (topicIds.length === 0) {\n return [];\n }\n await this.db\n .select({ id: spaceTopics.id })\n .from(spaceTopics)\n .where(\n and(\n eq(spaceTopics.orgId, orgId),\n eq(spaceTopics.spaceId, spaceId),\n inArray(spaceTopics.id, topicIds),\n ),\n )\n .for(\"update\");\n const rows = await this.db\n .selectDistinct({ topicId: posts.topicId })\n .from(posts)\n .where(\n and(eq(posts.orgId, orgId), eq(posts.spaceId, spaceId), inArray(posts.topicId, topicIds)),\n );\n return rows.flatMap((row) => (row.topicId ? [row.topicId] : []));\n }\n\n async addSpaceLink(orgId: string, spaceId: string, contentId: string): Promise<SpaceDetail> {\n const [item] = await this.db\n .select({ id: contentItems.id, type: contentItems.type })\n .from(contentItems)\n .where(and(eq(contentItems.orgId, orgId), eq(contentItems.id, contentId)))\n .limit(1);\n if (\n !item ||\n (item.type !== \"course\" && item.type !== \"download\" && item.type !== \"community\")\n ) {\n throw new NotFoundError(\"Content\", contentId);\n }\n const existing = await this.db\n .select({ seq: spaceLinks.seq })\n .from(spaceLinks)\n .where(and(eq(spaceLinks.orgId, orgId), eq(spaceLinks.spaceId, spaceId)));\n const nextSeq = existing.reduce((max, row) => Math.max(max, row.seq), -1) + 1;\n await this.db.insert(spaceLinks).values({ orgId, spaceId, contentId, seq: nextSeq });\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async removeSpaceLink(orgId: string, spaceId: string, linkId: string): Promise<SpaceDetail> {\n const deleted = await this.db\n .delete(spaceLinks)\n .where(\n and(\n eq(spaceLinks.orgId, orgId),\n eq(spaceLinks.spaceId, spaceId),\n eq(spaceLinks.id, linkId),\n ),\n )\n .returning({ id: spaceLinks.id });\n if (deleted.length === 0) {\n throw new NotFoundError(\"Link\", linkId);\n }\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async reorderSpaceLinks(\n orgId: string,\n spaceId: string,\n orderedIds: string[],\n ): Promise<SpaceDetail> {\n for (let i = 0; i < orderedIds.length; i++) {\n await this.db\n .update(spaceLinks)\n .set({ seq: i })\n .where(\n and(\n eq(spaceLinks.orgId, orgId),\n eq(spaceLinks.spaceId, spaceId),\n eq(spaceLinks.id, orderedIds[i]!),\n ),\n );\n }\n const space = await this.findSpace(orgId, spaceId);\n if (!space) {\n throw new NotFoundError(\"Space\", spaceId);\n }\n return space;\n }\n\n async spacesLinkingContent(orgId: string, contentId: string): Promise<string[]> {\n const rows = await this.db\n .select({ spaceId: spaceLinks.spaceId })\n .from(spaceLinks)\n .where(and(eq(spaceLinks.orgId, orgId), eq(spaceLinks.contentId, contentId)));\n return [...new Set(rows.map((row) => row.spaceId))];\n }\n\n private async withSpaceParts(orgId: string, rows: Space[]): Promise<SpaceDetail[]> {\n if (rows.length === 0) {\n return [];\n }\n const ids = rows.map((row) => row.id);\n const topics = await this.db\n .select()\n .from(spaceTopics)\n .where(and(eq(spaceTopics.orgId, orgId), inArray(spaceTopics.spaceId, ids)))\n .orderBy(asc(spaceTopics.seq));\n const links = await this.db\n .select()\n .from(spaceLinks)\n .where(and(eq(spaceLinks.orgId, orgId), inArray(spaceLinks.spaceId, ids)))\n .orderBy(asc(spaceLinks.seq));\n return rows.map((space) => ({\n ...space,\n topics: topics.filter((topic) => topic.spaceId === space.id),\n links: links.filter((link) => link.spaceId === space.id),\n }));\n }\n}\ntranslateDbErrors(DrizzleContentRepository);\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { credentials } from "../schema/credentials.js";
2
2
  import { translateDbErrors } from "./pg-errors.js";
3
+ import { noopLogger } from "@headless-lms/core/shared/logger";
3
4
  import { and, eq } from "drizzle-orm";
4
5
  import { genId } from "@headless-lms/core/shared/id";
5
- import { noopLogger } from "@headless-lms/core/shared/logger";
6
6
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
7
7
  //#region src/repositories/credentials.ts
8
8
  const IV_BYTES = 12;
@@ -0,0 +1,17 @@
1
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
+ import { DeviceView, Logger } from "@headless-lms/core/types";
3
+ import { DevicesRepository, NewDevice } from "@headless-lms/core/identity";
4
+ //#region src/repositories/devices.d.ts
5
+ declare class DrizzleDevicesRepository implements DevicesRepository {
6
+ private readonly db;
7
+ private readonly logger;
8
+ constructor(db: NodePgDatabase, logger?: Logger);
9
+ upsert(orgId: string, device: NewDevice, at: Date): Promise<DeviceView>;
10
+ listForUser(orgId: string, userId: string, since: Date | null): Promise<DeviceView[]>;
11
+ usersWithDevices(orgId: string, userIds: string[], since: Date): Promise<string[]>;
12
+ deleteOwn(orgId: string, userId: string, deviceId: string): Promise<boolean>;
13
+ deleteMany(orgId: string, deviceIds: string[]): Promise<void>;
14
+ }
15
+ //#endregion
16
+ export { DrizzleDevicesRepository };
17
+ //# sourceMappingURL=devices.d.ts.map
@@ -0,0 +1,75 @@
1
+ import { devices } from "../schema/identity.js";
2
+ import "../schema/index.js";
3
+ import { translateDbErrors } from "./pg-errors.js";
4
+ import { noopLogger } from "@headless-lms/core/shared/logger";
5
+ import { and, asc, desc, eq, gte, inArray } from "drizzle-orm";
6
+ //#region src/repositories/devices.ts
7
+ function toDevice(row) {
8
+ return {
9
+ id: row.id,
10
+ provider: row.provider,
11
+ token: row.token,
12
+ p256dh: row.p256dh,
13
+ auth: row.auth,
14
+ appId: row.appId,
15
+ environment: row.environment,
16
+ lastSeenAt: row.lastSeenAt
17
+ };
18
+ }
19
+ var DrizzleDevicesRepository = class {
20
+ db;
21
+ logger;
22
+ constructor(db, logger = noopLogger) {
23
+ this.db = db;
24
+ this.logger = logger;
25
+ }
26
+ async upsert(orgId, device, at) {
27
+ const [row] = await this.db.insert(devices).values({
28
+ orgId,
29
+ ...device,
30
+ createdAt: at,
31
+ lastSeenAt: at
32
+ }).onConflictDoUpdate({
33
+ target: [
34
+ devices.orgId,
35
+ devices.provider,
36
+ devices.token
37
+ ],
38
+ set: {
39
+ userId: device.userId,
40
+ p256dh: device.p256dh,
41
+ auth: device.auth,
42
+ appId: device.appId,
43
+ environment: device.environment,
44
+ lastSeenAt: at
45
+ }
46
+ }).returning();
47
+ if (!row) throw new Error("device upsert returned no row");
48
+ this.logger.debug("device upserted", {
49
+ orgId,
50
+ deviceId: row.id
51
+ });
52
+ return toDevice(row);
53
+ }
54
+ async listForUser(orgId, userId, since) {
55
+ const conditions = [eq(devices.orgId, orgId), eq(devices.userId, userId)];
56
+ if (since) conditions.push(gte(devices.lastSeenAt, since));
57
+ return (await this.db.select().from(devices).where(and(...conditions)).orderBy(desc(devices.lastSeenAt), asc(devices.id))).map(toDevice);
58
+ }
59
+ async usersWithDevices(orgId, userIds, since) {
60
+ if (userIds.length === 0) return [];
61
+ return (await this.db.selectDistinct({ userId: devices.userId }).from(devices).where(and(eq(devices.orgId, orgId), inArray(devices.userId, userIds), gte(devices.lastSeenAt, since)))).map((row) => row.userId);
62
+ }
63
+ async deleteOwn(orgId, userId, deviceId) {
64
+ return (await this.db.delete(devices).where(and(eq(devices.orgId, orgId), eq(devices.userId, userId), eq(devices.id, deviceId))).returning({ id: devices.id })).length > 0;
65
+ }
66
+ async deleteMany(orgId, deviceIds) {
67
+ if (deviceIds.length === 0) return;
68
+ await this.db.delete(devices).where(and(eq(devices.orgId, orgId), inArray(devices.id, deviceIds)));
69
+ }
70
+ };
71
+ translateDbErrors(DrizzleDevicesRepository);
72
+ //#endregion
73
+ export { DrizzleDevicesRepository };
74
+
75
+ //# sourceMappingURL=devices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices.js","names":[],"sources":["../../src/repositories/devices.ts"],"sourcesContent":["import { and, asc, desc, eq, gte, inArray } from \"drizzle-orm\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type { DevicesRepository, NewDevice } from \"@headless-lms/core/identity\";\nimport type { DeviceView, Logger } from \"@headless-lms/core/types\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport { devices } from \"../schema/index.js\";\nimport { translateDbErrors } from \"./pg-errors.js\";\n\ntype Row = typeof devices.$inferSelect;\n\nfunction toDevice(row: Row): DeviceView {\n return {\n id: row.id,\n provider: row.provider,\n token: row.token,\n p256dh: row.p256dh,\n auth: row.auth,\n appId: row.appId,\n environment: row.environment,\n lastSeenAt: row.lastSeenAt,\n };\n}\n\nexport class DrizzleDevicesRepository implements DevicesRepository {\n constructor(\n private readonly db: NodePgDatabase,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async upsert(orgId: string, device: NewDevice, at: Date): Promise<DeviceView> {\n const [row] = await this.db\n .insert(devices)\n .values({ orgId, ...device, createdAt: at, lastSeenAt: at })\n .onConflictDoUpdate({\n target: [devices.orgId, devices.provider, devices.token],\n set: {\n userId: device.userId,\n p256dh: device.p256dh,\n auth: device.auth,\n appId: device.appId,\n environment: device.environment,\n lastSeenAt: at,\n },\n })\n .returning();\n if (!row) {\n throw new Error(\"device upsert returned no row\");\n }\n this.logger.debug(\"device upserted\", { orgId, deviceId: row.id });\n return toDevice(row);\n }\n\n async listForUser(orgId: string, userId: string, since: Date | null): Promise<DeviceView[]> {\n const conditions = [eq(devices.orgId, orgId), eq(devices.userId, userId)];\n if (since) {\n conditions.push(gte(devices.lastSeenAt, since));\n }\n const rows = await this.db\n .select()\n .from(devices)\n .where(and(...conditions))\n .orderBy(desc(devices.lastSeenAt), asc(devices.id));\n return rows.map(toDevice);\n }\n\n async usersWithDevices(orgId: string, userIds: string[], since: Date): Promise<string[]> {\n if (userIds.length === 0) {\n return [];\n }\n const rows = await this.db\n .selectDistinct({ userId: devices.userId })\n .from(devices)\n .where(\n and(\n eq(devices.orgId, orgId),\n inArray(devices.userId, userIds),\n gte(devices.lastSeenAt, since),\n ),\n );\n return rows.map((row) => row.userId);\n }\n\n async deleteOwn(orgId: string, userId: string, deviceId: string): Promise<boolean> {\n const deleted = await this.db\n .delete(devices)\n .where(and(eq(devices.orgId, orgId), eq(devices.userId, userId), eq(devices.id, deviceId)))\n .returning({ id: devices.id });\n return deleted.length > 0;\n }\n\n async deleteMany(orgId: string, deviceIds: string[]): Promise<void> {\n if (deviceIds.length === 0) {\n return;\n }\n await this.db\n .delete(devices)\n .where(and(eq(devices.orgId, orgId), inArray(devices.id, deviceIds)));\n }\n}\ntranslateDbErrors(DrizzleDevicesRepository);\n"],"mappings":";;;;;;AAUA,SAAS,SAAS,KAAsB;CACtC,OAAO;EACL,IAAI,IAAI;EACR,UAAU,IAAI;EACd,OAAO,IAAI;EACX,QAAQ,IAAI;EACZ,MAAM,IAAI;EACV,OAAO,IAAI;EACX,aAAa,IAAI;EACjB,YAAY,IAAI;CAClB;AACF;AAEA,IAAa,2BAAb,MAAmE;CAE9C;CACA;CAFnB,YACE,IACA,SAAkC,YAClC;EAFiB,KAAA,KAAA;EACA,KAAA,SAAA;CAChB;CAEH,MAAM,OAAO,OAAe,QAAmB,IAA+B;EAC5E,MAAM,CAAC,OAAO,MAAM,KAAK,GACtB,OAAO,OAAO,CAAC,CACf,OAAO;GAAE;GAAO,GAAG;GAAQ,WAAW;GAAI,YAAY;EAAG,CAAC,CAAC,CAC3D,mBAAmB;GAClB,QAAQ;IAAC,QAAQ;IAAO,QAAQ;IAAU,QAAQ;GAAK;GACvD,KAAK;IACH,QAAQ,OAAO;IACf,QAAQ,OAAO;IACf,MAAM,OAAO;IACb,OAAO,OAAO;IACd,aAAa,OAAO;IACpB,YAAY;GACd;EACF,CAAC,CAAC,CACD,UAAU;EACb,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,+BAA+B;EAEjD,KAAK,OAAO,MAAM,mBAAmB;GAAE;GAAO,UAAU,IAAI;EAAG,CAAC;EAChE,OAAO,SAAS,GAAG;CACrB;CAEA,MAAM,YAAY,OAAe,QAAgB,OAA2C;EAC1F,MAAM,aAAa,CAAC,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,QAAQ,MAAM,CAAC;EACxE,IAAI,OACF,WAAW,KAAK,IAAI,QAAQ,YAAY,KAAK,CAAC;EAOhD,QAAO,MALY,KAAK,GACrB,OAAO,CAAC,CACR,KAAK,OAAO,CAAC,CACb,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CACzB,QAAQ,KAAK,QAAQ,UAAU,GAAG,IAAI,QAAQ,EAAE,CAAC,EAAA,CACxC,IAAI,QAAQ;CAC1B;CAEA,MAAM,iBAAiB,OAAe,SAAmB,OAAgC;EACvF,IAAI,QAAQ,WAAW,GACrB,OAAO,CAAC;EAYV,QAAO,MAVY,KAAK,GACrB,eAAe,EAAE,QAAQ,QAAQ,OAAO,CAAC,CAAC,CAC1C,KAAK,OAAO,CAAC,CACb,MACC,IACE,GAAG,QAAQ,OAAO,KAAK,GACvB,QAAQ,QAAQ,QAAQ,OAAO,GAC/B,IAAI,QAAQ,YAAY,KAAK,CAC/B,CACF,EAAA,CACU,KAAK,QAAQ,IAAI,MAAM;CACrC;CAEA,MAAM,UAAU,OAAe,QAAgB,UAAoC;EAKjF,QAAO,MAJe,KAAK,GACxB,OAAO,OAAO,CAAC,CACf,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,GAAG,QAAQ,QAAQ,MAAM,GAAG,GAAG,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,CAC1F,UAAU,EAAE,IAAI,QAAQ,GAAG,CAAC,EAAA,CAChB,SAAS;CAC1B;CAEA,MAAM,WAAW,OAAe,WAAoC;EAClE,IAAI,UAAU,WAAW,GACvB;EAEF,MAAM,KAAK,GACR,OAAO,OAAO,CAAC,CACf,MAAM,IAAI,GAAG,QAAQ,OAAO,KAAK,GAAG,QAAQ,QAAQ,IAAI,SAAS,CAAC,CAAC;CACxE;AACF;AACA,kBAAkB,wBAAwB"}
@@ -0,0 +1,14 @@
1
+ import { NodePgDatabase } from "drizzle-orm/node-postgres";
2
+ import { DigestRun, Logger } from "@headless-lms/core/types";
3
+ import { DigestRunRepository } from "@headless-lms/core/discussion";
4
+ //#region src/repositories/digest-runs.d.ts
5
+ declare class DrizzleDigestRunRepository implements DigestRunRepository {
6
+ private readonly db;
7
+ private readonly logger;
8
+ constructor(db: NodePgDatabase, logger?: Logger);
9
+ listRuns(): Promise<DigestRun[]>;
10
+ advance(orgId: string, communityId: string, dueAt: Date): Promise<boolean>;
11
+ }
12
+ //#endregion
13
+ export { DrizzleDigestRunRepository };
14
+ //# sourceMappingURL=digest-runs.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { digestRuns } from "../schema/discussion.js";
2
+ import "../schema/index.js";
3
+ import { translateDbErrors } from "./pg-errors.js";
4
+ import { noopLogger } from "@headless-lms/core/shared/logger";
5
+ import { sql } from "drizzle-orm";
6
+ //#region src/repositories/digest-runs.ts
7
+ var DrizzleDigestRunRepository = class {
8
+ db;
9
+ logger;
10
+ constructor(db, logger = noopLogger) {
11
+ this.db = db;
12
+ this.logger = logger;
13
+ }
14
+ async listRuns() {
15
+ return this.db.select().from(digestRuns);
16
+ }
17
+ async advance(orgId, communityId, dueAt) {
18
+ const rows = await this.db.insert(digestRuns).values({
19
+ orgId,
20
+ communityId,
21
+ lastDueAt: dueAt
22
+ }).onConflictDoUpdate({
23
+ target: [digestRuns.orgId, digestRuns.communityId],
24
+ set: { lastDueAt: sql`excluded.last_due_at` },
25
+ setWhere: sql`${digestRuns.lastDueAt} < excluded.last_due_at`
26
+ }).returning({ orgId: digestRuns.orgId });
27
+ this.logger.debug("digest run advanced", {
28
+ orgId,
29
+ communityId,
30
+ moved: rows.length > 0
31
+ });
32
+ return rows.length > 0;
33
+ }
34
+ };
35
+ translateDbErrors(DrizzleDigestRunRepository);
36
+ //#endregion
37
+ export { DrizzleDigestRunRepository };
38
+
39
+ //# sourceMappingURL=digest-runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest-runs.js","names":[],"sources":["../../src/repositories/digest-runs.ts"],"sourcesContent":["import { sql } from \"drizzle-orm\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type { DigestRunRepository } from \"@headless-lms/core/discussion\";\nimport type { DigestRun, Logger } from \"@headless-lms/core/types\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport { digestRuns } from \"../schema/index.js\";\nimport { translateDbErrors } from \"./pg-errors.js\";\n\nexport class DrizzleDigestRunRepository implements DigestRunRepository {\n constructor(\n private readonly db: NodePgDatabase,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async listRuns(): Promise<DigestRun[]> {\n return this.db.select().from(digestRuns);\n }\n\n async advance(orgId: string, communityId: string, dueAt: Date): Promise<boolean> {\n const rows = await this.db\n .insert(digestRuns)\n .values({ orgId, communityId, lastDueAt: dueAt })\n .onConflictDoUpdate({\n target: [digestRuns.orgId, digestRuns.communityId],\n set: { lastDueAt: sql`excluded.last_due_at` },\n setWhere: sql`${digestRuns.lastDueAt} < excluded.last_due_at`,\n })\n .returning({ orgId: digestRuns.orgId });\n this.logger.debug(\"digest run advanced\", { orgId, communityId, moved: rows.length > 0 });\n return rows.length > 0;\n }\n}\ntranslateDbErrors(DrizzleDigestRunRepository);\n"],"mappings":";;;;;;AAQA,IAAa,6BAAb,MAAuE;CAElD;CACA;CAFnB,YACE,IACA,SAAkC,YAClC;EAFiB,KAAA,KAAA;EACA,KAAA,SAAA;CAChB;CAEH,MAAM,WAAiC;EACrC,OAAO,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,UAAU;CACzC;CAEA,MAAM,QAAQ,OAAe,aAAqB,OAA+B;EAC/E,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,UAAU,CAAC,CAClB,OAAO;GAAE;GAAO;GAAa,WAAW;EAAM,CAAC,CAAC,CAChD,mBAAmB;GAClB,QAAQ,CAAC,WAAW,OAAO,WAAW,WAAW;GACjD,KAAK,EAAE,WAAW,GAAG,uBAAuB;GAC5C,UAAU,GAAG,GAAG,WAAW,UAAU;EACvC,CAAC,CAAC,CACD,UAAU,EAAE,OAAO,WAAW,MAAM,CAAC;EACxC,KAAK,OAAO,MAAM,uBAAuB;GAAE;GAAO;GAAa,OAAO,KAAK,SAAS;EAAE,CAAC;EACvF,OAAO,KAAK,SAAS;CACvB;AACF;AACA,kBAAkB,0BAA0B"}