@m5kdev/backend 0.8.6 → 0.8.8

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 (383) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +33 -0
  2. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/api/index.d.cts +1 -0
  3. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/adapter/factory.d.cts +1 -0
  4. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/adapter/get-field-attributes.d.cts +1 -0
  5. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/adapter/get-id-field.d.cts +1 -0
  6. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/adapter/index.d.cts +1 -0
  7. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/adapter/types.d.cts +1 -0
  8. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/get-tables.d.cts +1 -0
  9. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/index.d.cts +1 -0
  10. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/schema/account.d.cts +1 -0
  11. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/schema/rate-limit.d.cts +1 -0
  12. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/schema/session.d.cts +1 -0
  13. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/schema/shared.d.cts +1 -0
  14. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/schema/user.d.cts +1 -0
  15. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/db/schema/verification.d.cts +1 -0
  16. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/index.d.cts +1 -0
  17. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/social-providers/index.d.cts +1 -0
  18. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/types/context.d.cts +1 -0
  19. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/types/helper.d.cts +7 -0
  20. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/types/init-options.d.cts +1 -0
  21. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/types/plugin-client.d.cts +1 -0
  22. package/dist/node_modules/.pnpm/@better-auth_core@1.4.18_@better-auth_utils@0.3.0_@better-fetch_fetch@1.1.21_better-cal_347838d331444e5371f256b914727290/node_modules/@better-auth/core/dist/types/plugin.d.cts +1 -0
  23. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/dialect-adapter-base.d.cts +1 -0
  24. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/dialect-adapter.d.cts +1 -0
  25. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/dialect.d.cts +1 -0
  26. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/mssql/mssql-adapter.d.cts +1 -0
  27. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/mssql/mssql-dialect.d.cts +1 -0
  28. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/mssql/mssql-introspector.d.cts +1 -0
  29. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/mysql/mysql-adapter.d.cts +1 -0
  30. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/mysql/mysql-dialect.d.cts +1 -0
  31. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/mysql/mysql-introspector.d.cts +1 -0
  32. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/postgres/postgres-adapter.d.cts +1 -0
  33. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/postgres/postgres-dialect.d.cts +1 -0
  34. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/postgres/postgres-introspector.d.cts +1 -0
  35. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/sqlite/sqlite-adapter.d.cts +1 -0
  36. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/sqlite/sqlite-dialect.d.cts +1 -0
  37. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/dialect/sqlite/sqlite-introspector.d.cts +1 -0
  38. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/expression/expression-builder.d.cts +1 -0
  39. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/index.d.cts +1 -0
  40. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/kysely.d.cts +38 -0
  41. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/migration/file-migration-provider.d.cts +1 -0
  42. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/migration/migrator.d.cts +1 -0
  43. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/binary-operation-parser.d.cts +1 -0
  44. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/delete-from-parser.d.cts +1 -0
  45. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/expression-parser.d.cts +1 -0
  46. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/group-by-parser.d.cts +1 -0
  47. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/insert-values-parser.d.cts +1 -0
  48. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/join-parser.d.cts +1 -0
  49. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/merge-into-parser.d.cts +1 -0
  50. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/reference-parser.d.cts +1 -0
  51. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/returning-parser.d.cts +1 -0
  52. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/select-from-parser.d.cts +1 -0
  53. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/select-parser.d.cts +1 -0
  54. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/set-operation-parser.d.cts +1 -0
  55. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/tuple-parser.d.cts +1 -0
  56. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/unary-operation-parser.d.cts +1 -0
  57. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/update-parser.d.cts +1 -0
  58. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/update-set-parser.d.cts +1 -0
  59. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/value-parser.d.cts +1 -0
  60. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/parser/with-parser.d.cts +1 -0
  61. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/case-builder.d.cts +1 -0
  62. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/delete-query-builder.d.cts +1 -0
  63. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/having-interface.d.cts +1 -0
  64. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/insert-query-builder.d.cts +1 -0
  65. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/join-builder.d.cts +1 -0
  66. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/merge-query-builder.d.cts +1 -0
  67. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/on-conflict-builder.d.cts +1 -0
  68. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/output-interface.d.cts +1 -0
  69. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/returning-interface.d.cts +1 -0
  70. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/select-query-builder.d.cts +1 -0
  71. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/update-query-builder.d.cts +1 -0
  72. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-builder/where-interface.d.cts +1 -0
  73. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-creator.d.cts +1 -0
  74. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-executor/default-query-executor.d.cts +1 -0
  75. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-executor/noop-query-executor.d.cts +1 -0
  76. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-executor/query-executor-base.d.cts +1 -0
  77. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-executor/query-executor-provider.d.cts +1 -0
  78. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/query-executor/query-executor.d.cts +1 -0
  79. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/raw-builder/raw-builder.d.cts +1 -0
  80. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/raw-builder/sql.d.cts +1 -0
  81. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/alter-table-add-foreign-key-constraint-builder.d.cts +1 -0
  82. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/alter-table-add-index-builder.d.cts +1 -0
  83. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/alter-table-builder.d.cts +1 -0
  84. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/alter-table-drop-constraint-builder.d.cts +1 -0
  85. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/alter-table-executor.d.cts +1 -0
  86. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/create-index-builder.d.cts +1 -0
  87. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/create-schema-builder.d.cts +1 -0
  88. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/create-table-builder.d.cts +1 -0
  89. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/create-type-builder.d.cts +1 -0
  90. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/create-view-builder.d.cts +1 -0
  91. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/drop-index-builder.d.cts +1 -0
  92. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/drop-schema-builder.d.cts +1 -0
  93. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/drop-table-builder.d.cts +1 -0
  94. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/drop-type-builder.d.cts +1 -0
  95. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/drop-view-builder.d.cts +1 -0
  96. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/refresh-materialized-view-builder.d.cts +1 -0
  97. package/dist/node_modules/.pnpm/kysely@0.28.5/node_modules/kysely/dist/esm/schema/schema.d.cts +1 -0
  98. package/dist/src/lib/posthog.cjs +8 -0
  99. package/dist/src/lib/posthog.cjs.map +1 -0
  100. package/dist/src/lib/posthog.d.cts +7 -0
  101. package/dist/src/lib/sentry.cjs +11 -0
  102. package/dist/src/lib/sentry.cjs.map +1 -0
  103. package/dist/src/lib/sentry.d.cts +1 -0
  104. package/dist/src/modules/access/access.repository.cjs +26 -0
  105. package/dist/src/modules/access/access.repository.cjs.map +1 -0
  106. package/dist/src/modules/access/access.repository.d.cts +2352 -0
  107. package/dist/src/modules/access/access.service.cjs +42 -0
  108. package/dist/src/modules/access/access.service.cjs.map +1 -0
  109. package/dist/src/modules/access/access.service.d.cts +25 -0
  110. package/dist/src/modules/access/access.utils.cjs +23 -0
  111. package/dist/src/modules/access/access.utils.cjs.map +1 -0
  112. package/dist/src/modules/access/access.utils.d.cts +19 -0
  113. package/dist/src/modules/ai/ai.db.cjs +46 -0
  114. package/dist/src/modules/ai/ai.db.cjs.map +1 -0
  115. package/dist/src/modules/ai/ai.db.d.cts +401 -0
  116. package/dist/src/modules/ai/ai.prompt.cjs +33 -0
  117. package/dist/src/modules/ai/ai.prompt.cjs.map +1 -0
  118. package/dist/src/modules/ai/ai.prompt.d.cts +30 -0
  119. package/dist/src/modules/ai/ai.prompts.cjs +18 -0
  120. package/dist/src/modules/ai/ai.prompts.cjs.map +1 -0
  121. package/dist/src/modules/ai/ai.prompts.d.cts +10 -0
  122. package/dist/src/modules/ai/ai.repository.cjs +25 -0
  123. package/dist/src/modules/ai/ai.repository.cjs.map +1 -0
  124. package/dist/src/modules/ai/ai.repository.d.cts +428 -0
  125. package/dist/src/modules/ai/ai.router.cjs +0 -0
  126. package/dist/src/modules/ai/ai.router.d.cts +1 -0
  127. package/dist/src/modules/ai/ai.service.cjs +312 -0
  128. package/dist/src/modules/ai/ai.service.cjs.map +1 -0
  129. package/dist/src/modules/ai/ai.service.d.cts +141 -0
  130. package/dist/src/modules/ai/ai.trpc.cjs +19 -0
  131. package/dist/src/modules/ai/ai.trpc.cjs.map +1 -0
  132. package/dist/src/modules/ai/ai.trpc.d.cts +31 -0
  133. package/dist/src/modules/ai/ideogram/ideogram.constants.cjs +191 -0
  134. package/dist/src/modules/ai/ideogram/ideogram.constants.cjs.map +1 -0
  135. package/dist/src/modules/ai/ideogram/ideogram.constants.d.cts +11 -0
  136. package/dist/src/modules/ai/ideogram/ideogram.dto.cjs +49 -0
  137. package/dist/src/modules/ai/ideogram/ideogram.dto.cjs.map +1 -0
  138. package/dist/src/modules/ai/ideogram/ideogram.dto.d.cts +234 -0
  139. package/dist/src/modules/ai/ideogram/ideogram.prompt.cjs +862 -0
  140. package/dist/src/modules/ai/ideogram/ideogram.prompt.cjs.map +1 -0
  141. package/dist/src/modules/ai/ideogram/ideogram.prompt.d.cts +7 -0
  142. package/dist/src/modules/ai/ideogram/ideogram.repository.cjs +34 -0
  143. package/dist/src/modules/ai/ideogram/ideogram.repository.cjs.map +1 -0
  144. package/dist/src/modules/ai/ideogram/ideogram.repository.d.cts +11 -0
  145. package/dist/src/modules/ai/ideogram/ideogram.service.cjs +12 -0
  146. package/dist/src/modules/ai/ideogram/ideogram.service.cjs.map +1 -0
  147. package/dist/src/modules/ai/ideogram/ideogram.service.d.cts +14 -0
  148. package/dist/src/modules/auth/auth.db.cjs +187 -0
  149. package/dist/src/modules/auth/auth.db.cjs.map +1 -0
  150. package/dist/src/modules/auth/auth.db.d.cts +2341 -0
  151. package/dist/src/modules/auth/auth.dto.cjs +50 -0
  152. package/dist/src/modules/auth/auth.dto.cjs.map +1 -0
  153. package/dist/src/modules/auth/auth.dto.d.cts +70 -0
  154. package/dist/src/modules/auth/auth.lib.cjs +234 -0
  155. package/dist/src/modules/auth/auth.lib.cjs.map +1 -0
  156. package/dist/src/modules/auth/auth.lib.d.cts +4894 -0
  157. package/dist/src/modules/auth/auth.middleware.cjs +41 -0
  158. package/dist/src/modules/auth/auth.middleware.cjs.map +1 -0
  159. package/dist/src/modules/auth/auth.middleware.d.cts +619 -0
  160. package/dist/src/modules/auth/auth.repository.cjs +403 -0
  161. package/dist/src/modules/auth/auth.repository.cjs.map +1 -0
  162. package/dist/src/modules/auth/auth.repository.d.cts +2453 -0
  163. package/dist/src/modules/auth/auth.service.cjs +229 -0
  164. package/dist/src/modules/auth/auth.service.cjs.map +1 -0
  165. package/dist/src/modules/auth/auth.service.d.cts +105 -0
  166. package/dist/src/modules/auth/auth.trpc.cjs +110 -0
  167. package/dist/src/modules/auth/auth.trpc.cjs.map +1 -0
  168. package/dist/src/modules/auth/auth.trpc.d.cts +303 -0
  169. package/dist/src/modules/auth/auth.utils.cjs +80 -0
  170. package/dist/src/modules/auth/auth.utils.cjs.map +1 -0
  171. package/dist/src/modules/auth/auth.utils.d.cts +2356 -0
  172. package/dist/src/modules/base/base.abstract.cjs +62 -0
  173. package/dist/src/modules/base/base.abstract.cjs.map +1 -0
  174. package/dist/src/modules/base/base.abstract.d.cts +29 -0
  175. package/dist/src/modules/base/base.actor.cjs +83 -0
  176. package/dist/src/modules/base/base.actor.cjs.map +1 -0
  177. package/dist/src/modules/base/base.actor.d.cts +73 -0
  178. package/dist/src/modules/base/base.dto.cjs +98 -0
  179. package/dist/src/modules/base/base.dto.cjs.map +1 -0
  180. package/dist/src/modules/base/base.dto.d.cts +67 -0
  181. package/dist/src/modules/base/base.grants.cjs +107 -0
  182. package/dist/src/modules/base/base.grants.cjs.map +1 -0
  183. package/dist/src/modules/base/base.grants.d.cts +28 -0
  184. package/dist/src/modules/base/base.procedure.cjs +255 -0
  185. package/dist/src/modules/base/base.procedure.cjs.map +1 -0
  186. package/dist/src/modules/base/base.procedure.d.cts +111 -0
  187. package/dist/src/modules/base/base.repository.cjs +269 -0
  188. package/dist/src/modules/base/base.repository.cjs.map +1 -0
  189. package/dist/src/modules/base/base.repository.d.cts +125 -0
  190. package/dist/src/modules/base/base.repository.d.mts +2 -0
  191. package/dist/src/modules/base/base.repository.mjs +12 -0
  192. package/dist/src/modules/base/base.repository.mjs.map +1 -1
  193. package/dist/src/modules/base/base.service.cjs +119 -0
  194. package/dist/src/modules/base/base.service.cjs.map +1 -0
  195. package/dist/src/modules/base/base.service.d.cts +44 -0
  196. package/dist/src/modules/base/base.types.cjs +0 -0
  197. package/dist/src/modules/base/base.types.d.cts +5 -0
  198. package/dist/src/modules/billing/billing.db.cjs +38 -0
  199. package/dist/src/modules/billing/billing.db.cjs.map +1 -0
  200. package/dist/src/modules/billing/billing.db.d.cts +371 -0
  201. package/dist/src/modules/billing/billing.repository.cjs +190 -0
  202. package/dist/src/modules/billing/billing.repository.cjs.map +1 -0
  203. package/dist/src/modules/billing/billing.repository.d.cts +2787 -0
  204. package/dist/src/modules/billing/billing.repository.d.mts +11 -11
  205. package/dist/src/modules/billing/billing.router.cjs +43 -0
  206. package/dist/src/modules/billing/billing.router.cjs.map +1 -0
  207. package/dist/src/modules/billing/billing.router.d.cts +9 -0
  208. package/dist/src/modules/billing/billing.service.cjs +127 -0
  209. package/dist/src/modules/billing/billing.service.cjs.map +1 -0
  210. package/dist/src/modules/billing/billing.service.d.cts +53 -0
  211. package/dist/src/modules/billing/billing.service.d.mts +7 -7
  212. package/dist/src/modules/billing/billing.trpc.cjs +19 -0
  213. package/dist/src/modules/billing/billing.trpc.cjs.map +1 -0
  214. package/dist/src/modules/billing/billing.trpc.d.cts +48 -0
  215. package/dist/src/modules/clay/clay.repository.cjs +29 -0
  216. package/dist/src/modules/clay/clay.repository.cjs.map +1 -0
  217. package/dist/src/modules/clay/clay.repository.d.cts +10 -0
  218. package/dist/src/modules/clay/clay.service.cjs +24 -0
  219. package/dist/src/modules/clay/clay.service.cjs.map +1 -0
  220. package/dist/src/modules/clay/clay.service.d.cts +32 -0
  221. package/dist/src/modules/connect/connect.db.cjs +37 -0
  222. package/dist/src/modules/connect/connect.db.cjs.map +1 -0
  223. package/dist/src/modules/connect/connect.db.d.cts +362 -0
  224. package/dist/src/modules/connect/connect.dto.cjs +45 -0
  225. package/dist/src/modules/connect/connect.dto.cjs.map +1 -0
  226. package/dist/src/modules/connect/connect.dto.d.cts +79 -0
  227. package/dist/src/modules/connect/connect.linkedin.cjs +48 -0
  228. package/dist/src/modules/connect/connect.linkedin.cjs.map +1 -0
  229. package/dist/src/modules/connect/connect.linkedin.d.cts +7 -0
  230. package/dist/src/modules/connect/connect.oauth.cjs +153 -0
  231. package/dist/src/modules/connect/connect.oauth.cjs.map +1 -0
  232. package/dist/src/modules/connect/connect.oauth.d.cts +32 -0
  233. package/dist/src/modules/connect/connect.repository.cjs +42 -0
  234. package/dist/src/modules/connect/connect.repository.cjs.map +1 -0
  235. package/dist/src/modules/connect/connect.repository.d.cts +419 -0
  236. package/dist/src/modules/connect/connect.router.cjs +48 -0
  237. package/dist/src/modules/connect/connect.router.cjs.map +1 -0
  238. package/dist/src/modules/connect/connect.router.d.cts +9 -0
  239. package/dist/src/modules/connect/connect.service.cjs +90 -0
  240. package/dist/src/modules/connect/connect.service.cjs.map +1 -0
  241. package/dist/src/modules/connect/connect.service.d.cts +103 -0
  242. package/dist/src/modules/connect/connect.trpc.cjs +18 -0
  243. package/dist/src/modules/connect/connect.trpc.cjs.map +1 -0
  244. package/dist/src/modules/connect/connect.trpc.d.cts +53 -0
  245. package/dist/src/modules/connect/connect.types.cjs +0 -0
  246. package/dist/src/modules/connect/connect.types.d.cts +29 -0
  247. package/dist/src/modules/crypto/crypto.db.cjs +26 -0
  248. package/dist/src/modules/crypto/crypto.db.cjs.map +1 -0
  249. package/dist/src/modules/crypto/crypto.db.d.cts +157 -0
  250. package/dist/src/modules/crypto/crypto.repository.cjs +9 -0
  251. package/dist/src/modules/crypto/crypto.repository.cjs.map +1 -0
  252. package/dist/src/modules/crypto/crypto.repository.d.cts +163 -0
  253. package/dist/src/modules/crypto/crypto.service.cjs +46 -0
  254. package/dist/src/modules/crypto/crypto.service.cjs.map +1 -0
  255. package/dist/src/modules/crypto/crypto.service.d.cts +15 -0
  256. package/dist/src/modules/email/email.service.cjs +107 -0
  257. package/dist/src/modules/email/email.service.cjs.map +1 -0
  258. package/dist/src/modules/email/email.service.d.cts +62 -0
  259. package/dist/src/modules/file/file.repository.cjs +74 -0
  260. package/dist/src/modules/file/file.repository.cjs.map +1 -0
  261. package/dist/src/modules/file/file.repository.d.cts +17 -0
  262. package/dist/src/modules/file/file.router.cjs +94 -0
  263. package/dist/src/modules/file/file.router.cjs.map +1 -0
  264. package/dist/src/modules/file/file.router.d.cts +7 -0
  265. package/dist/src/modules/file/file.service.cjs +120 -0
  266. package/dist/src/modules/file/file.service.cjs.map +1 -0
  267. package/dist/src/modules/file/file.service.d.cts +30 -0
  268. package/dist/src/modules/recurrence/recurrence.db.cjs +55 -0
  269. package/dist/src/modules/recurrence/recurrence.db.cjs.map +1 -0
  270. package/dist/src/modules/recurrence/recurrence.db.d.cts +568 -0
  271. package/dist/src/modules/recurrence/recurrence.repository.cjs +31 -0
  272. package/dist/src/modules/recurrence/recurrence.repository.cjs.map +1 -0
  273. package/dist/src/modules/recurrence/recurrence.repository.d.cts +588 -0
  274. package/dist/src/modules/recurrence/recurrence.service.cjs +66 -0
  275. package/dist/src/modules/recurrence/recurrence.service.cjs.map +1 -0
  276. package/dist/src/modules/recurrence/recurrence.service.d.cts +88 -0
  277. package/dist/src/modules/recurrence/recurrence.service.d.mts +2 -1
  278. package/dist/src/modules/recurrence/recurrence.service.mjs +1 -1
  279. package/dist/src/modules/recurrence/recurrence.service.mjs.map +1 -1
  280. package/dist/src/modules/recurrence/recurrence.trpc.cjs +46 -0
  281. package/dist/src/modules/recurrence/recurrence.trpc.cjs.map +1 -0
  282. package/dist/src/modules/recurrence/recurrence.trpc.d.cts +216 -0
  283. package/dist/src/modules/recurrence/recurrence.trpc.d.mts +2 -1
  284. package/dist/src/modules/social/social.dto.cjs +26 -0
  285. package/dist/src/modules/social/social.dto.cjs.map +1 -0
  286. package/dist/src/modules/social/social.dto.d.cts +39 -0
  287. package/dist/src/modules/social/social.linkedin.cjs +349 -0
  288. package/dist/src/modules/social/social.linkedin.cjs.map +1 -0
  289. package/dist/src/modules/social/social.linkedin.d.cts +15 -0
  290. package/dist/src/modules/social/social.service.cjs +57 -0
  291. package/dist/src/modules/social/social.service.cjs.map +1 -0
  292. package/dist/src/modules/social/social.service.d.cts +34 -0
  293. package/dist/src/modules/social/social.types.cjs +0 -0
  294. package/dist/src/modules/social/social.types.d.cts +40 -0
  295. package/dist/src/modules/tag/tag.db.cjs +43 -0
  296. package/dist/src/modules/tag/tag.db.cjs.map +1 -0
  297. package/dist/src/modules/tag/tag.db.d.cts +352 -0
  298. package/dist/src/modules/tag/tag.dto.cjs +15 -0
  299. package/dist/src/modules/tag/tag.dto.cjs.map +1 -0
  300. package/dist/src/modules/tag/tag.dto.d.cts +1025 -0
  301. package/dist/src/modules/tag/tag.repository.cjs +116 -0
  302. package/dist/src/modules/tag/tag.repository.cjs.map +1 -0
  303. package/dist/src/modules/tag/tag.repository.d.cts +394 -0
  304. package/dist/src/modules/tag/tag.service.cjs +48 -0
  305. package/dist/src/modules/tag/tag.service.cjs.map +1 -0
  306. package/dist/src/modules/tag/tag.service.d.cts +120 -0
  307. package/dist/src/modules/tag/tag.trpc.cjs +32 -0
  308. package/dist/src/modules/tag/tag.trpc.cjs.map +1 -0
  309. package/dist/src/modules/tag/tag.trpc.d.cts +174 -0
  310. package/dist/src/modules/tag/tag.trpc.d.mts +3 -2
  311. package/dist/src/modules/utils/applyPagination.cjs +16 -0
  312. package/dist/src/modules/utils/applyPagination.cjs.map +1 -0
  313. package/dist/src/modules/utils/applyPagination.d.cts +10 -0
  314. package/dist/src/modules/utils/applySorting.cjs +20 -0
  315. package/dist/src/modules/utils/applySorting.cjs.map +1 -0
  316. package/dist/src/modules/utils/applySorting.d.cts +13 -0
  317. package/dist/src/modules/utils/getConditionsFromFilters.cjs +152 -0
  318. package/dist/src/modules/utils/getConditionsFromFilters.cjs.map +1 -0
  319. package/dist/src/modules/utils/getConditionsFromFilters.d.cts +9 -0
  320. package/dist/src/modules/utils/getGlobalSearchCondition.cjs +30 -0
  321. package/dist/src/modules/utils/getGlobalSearchCondition.cjs.map +1 -0
  322. package/dist/src/modules/utils/getGlobalSearchCondition.d.cts +18 -0
  323. package/dist/src/modules/utils/getGlobalSearchCondition.d.mts +18 -0
  324. package/dist/src/modules/utils/getGlobalSearchCondition.mjs +26 -0
  325. package/dist/src/modules/utils/getGlobalSearchCondition.mjs.map +1 -0
  326. package/dist/src/modules/video/video.service.cjs +114 -0
  327. package/dist/src/modules/video/video.service.cjs.map +1 -0
  328. package/dist/src/modules/video/video.service.d.cts +12 -0
  329. package/dist/src/modules/video/video.service.mjs +73 -13
  330. package/dist/src/modules/video/video.service.mjs.map +1 -1
  331. package/dist/src/modules/webhook/webhook.constants.cjs +13 -0
  332. package/dist/src/modules/webhook/webhook.constants.cjs.map +1 -0
  333. package/dist/src/modules/webhook/webhook.constants.d.cts +12 -0
  334. package/dist/src/modules/webhook/webhook.db.cjs +19 -0
  335. package/dist/src/modules/webhook/webhook.db.cjs.map +1 -0
  336. package/dist/src/modules/webhook/webhook.db.d.cts +142 -0
  337. package/dist/src/modules/webhook/webhook.dto.cjs +11 -0
  338. package/dist/src/modules/webhook/webhook.dto.cjs.map +1 -0
  339. package/dist/src/modules/webhook/webhook.dto.d.cts +402 -0
  340. package/dist/src/modules/webhook/webhook.repository.cjs +52 -0
  341. package/dist/src/modules/webhook/webhook.repository.cjs.map +1 -0
  342. package/dist/src/modules/webhook/webhook.repository.d.cts +154 -0
  343. package/dist/src/modules/webhook/webhook.router.cjs +26 -0
  344. package/dist/src/modules/webhook/webhook.router.cjs.map +1 -0
  345. package/dist/src/modules/webhook/webhook.router.d.cts +8 -0
  346. package/dist/src/modules/webhook/webhook.service.cjs +61 -0
  347. package/dist/src/modules/webhook/webhook.service.cjs.map +1 -0
  348. package/dist/src/modules/webhook/webhook.service.d.cts +14 -0
  349. package/dist/src/modules/workflow/workflow.db.cjs +35 -0
  350. package/dist/src/modules/workflow/workflow.db.cjs.map +1 -0
  351. package/dist/src/modules/workflow/workflow.db.d.cts +302 -0
  352. package/dist/src/modules/workflow/workflow.repository.cjs +95 -0
  353. package/dist/src/modules/workflow/workflow.repository.cjs.map +1 -0
  354. package/dist/src/modules/workflow/workflow.repository.d.cts +371 -0
  355. package/dist/src/modules/workflow/workflow.service.cjs +41 -0
  356. package/dist/src/modules/workflow/workflow.service.cjs.map +1 -0
  357. package/dist/src/modules/workflow/workflow.service.d.cts +68 -0
  358. package/dist/src/modules/workflow/workflow.trpc.cjs +19 -0
  359. package/dist/src/modules/workflow/workflow.trpc.cjs.map +1 -0
  360. package/dist/src/modules/workflow/workflow.trpc.d.cts +65 -0
  361. package/dist/src/modules/workflow/workflow.types.cjs +0 -0
  362. package/dist/src/modules/workflow/workflow.types.d.cts +25 -0
  363. package/dist/src/modules/workflow/workflow.utils.cjs +185 -0
  364. package/dist/src/modules/workflow/workflow.utils.cjs.map +1 -0
  365. package/dist/src/modules/workflow/workflow.utils.d.cts +36 -0
  366. package/dist/src/types.cjs +12 -0
  367. package/dist/src/types.cjs.map +1 -0
  368. package/dist/src/types.d.cts +344 -0
  369. package/dist/src/utils/errors.cjs +101 -0
  370. package/dist/src/utils/errors.cjs.map +1 -0
  371. package/dist/src/utils/errors.d.cts +62 -0
  372. package/dist/src/utils/logger.cjs +13 -0
  373. package/dist/src/utils/logger.cjs.map +1 -0
  374. package/dist/src/utils/logger.d.cts +7 -0
  375. package/dist/src/utils/posthog.cjs +31 -0
  376. package/dist/src/utils/posthog.cjs.map +1 -0
  377. package/dist/src/utils/posthog.d.cts +17 -0
  378. package/dist/src/utils/trpc.cjs +156 -0
  379. package/dist/src/utils/trpc.cjs.map +1 -0
  380. package/dist/src/utils/trpc.d.cts +54 -0
  381. package/dist/src/utils/types.cjs +0 -0
  382. package/dist/src/utils/types.d.cts +9 -0
  383. package/package.json +171 -47
@@ -0,0 +1,349 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../../../_virtual/_rolldown/runtime.cjs");
3
+ let node_fs = require("node:fs");
4
+ let node_fs_promises = require("node:fs/promises");
5
+ //#region src/modules/social/social.linkedin.ts
6
+ const LINKEDIN_API_BASE = "https://api.linkedin.com/rest";
7
+ const LINKEDIN_VERSION = "202601";
8
+ const IMAGES_URL = `${LINKEDIN_API_BASE}/images?action=initializeUpload`;
9
+ const VIDEOS_URL = `${LINKEDIN_API_BASE}/videos?action=initializeUpload`;
10
+ const VIDEOS_FINALIZE_URL = `${LINKEDIN_API_BASE}/videos?action=finalizeUpload`;
11
+ const DOCUMENTS_URL = `${LINKEDIN_API_BASE}/documents?action=initializeUpload`;
12
+ const POSTS_URL = `${LINKEDIN_API_BASE}/posts`;
13
+ function getApiHeaders(accessToken) {
14
+ return {
15
+ Authorization: `Bearer ${accessToken}`,
16
+ "Content-Type": "application/json",
17
+ "Linkedin-Version": LINKEDIN_VERSION,
18
+ "X-Restli-Protocol-Version": "2.0.0"
19
+ };
20
+ }
21
+ function createLinkedInSocialProvider() {
22
+ return {
23
+ id: "linkedin",
24
+ async post({ deps, context, payload }) {
25
+ const personUrn = resolveAuthorUrn(context.connection);
26
+ const mediaDescriptors = payload.media ?? [];
27
+ let uploadedAssets = [];
28
+ if (mediaDescriptors.length > 0) uploadedAssets = await uploadMediaAssets({
29
+ accessToken: context.accessToken,
30
+ fileService: deps.fileService,
31
+ mediaDescriptors,
32
+ personUrn
33
+ });
34
+ const response = await publishPost({
35
+ accessToken: context.accessToken,
36
+ personUrn,
37
+ payload,
38
+ uploadedAssets
39
+ });
40
+ return {
41
+ shareUrn: response.postUrn,
42
+ rawResponse: response.raw
43
+ };
44
+ }
45
+ };
46
+ }
47
+ function resolveAuthorUrn(connection) {
48
+ if (connection.metadataJson) try {
49
+ const metadata = connection.metadataJson;
50
+ if (metadata.linkedInUrn && typeof metadata.linkedInUrn === "string") return metadata.linkedInUrn;
51
+ } catch {
52
+ throw new Error("Failed to parse LinkedIn connection metadata");
53
+ }
54
+ if (connection.providerAccountId) return `urn:li:person:${connection.providerAccountId}`;
55
+ throw new Error("LinkedIn connection is missing a person URN");
56
+ }
57
+ async function uploadMediaAssets(params) {
58
+ const results = [];
59
+ for (const descriptor of params.mediaDescriptors) {
60
+ const download = await params.fileService.downloadS3ToFile(descriptor.s3Path);
61
+ if (download.isErr()) throw download.error;
62
+ const localPath = download.value;
63
+ try {
64
+ const mediaType = determineMediaType(descriptor.mediaType, localPath);
65
+ let assetUrn;
66
+ switch (mediaType) {
67
+ case "image":
68
+ assetUrn = await uploadImage({
69
+ accessToken: params.accessToken,
70
+ personUrn: params.personUrn,
71
+ localPath
72
+ });
73
+ break;
74
+ case "video":
75
+ assetUrn = await uploadVideo({
76
+ accessToken: params.accessToken,
77
+ personUrn: params.personUrn,
78
+ localPath
79
+ });
80
+ break;
81
+ case "document":
82
+ assetUrn = await uploadDocument({
83
+ accessToken: params.accessToken,
84
+ personUrn: params.personUrn,
85
+ localPath
86
+ });
87
+ break;
88
+ }
89
+ results.push({
90
+ assetUrn,
91
+ mediaType,
92
+ title: descriptor.title,
93
+ description: descriptor.description
94
+ });
95
+ } finally {
96
+ await (0, node_fs_promises.unlink)(localPath).catch(() => void 0);
97
+ }
98
+ }
99
+ return results;
100
+ }
101
+ function determineMediaType(explicitType, localPath) {
102
+ if (explicitType) return explicitType;
103
+ const extension = localPath.split(".").pop()?.toLowerCase();
104
+ if (!extension) throw new Error("Unable to determine media type from file extension");
105
+ const imageExtensions = new Set([
106
+ "jpg",
107
+ "jpeg",
108
+ "png",
109
+ "gif",
110
+ "webp"
111
+ ]);
112
+ const videoExtensions = new Set([
113
+ "mp4",
114
+ "mov",
115
+ "m4v",
116
+ "avi",
117
+ "mkv",
118
+ "webm"
119
+ ]);
120
+ const documentExtensions = new Set([
121
+ "pdf",
122
+ "ppt",
123
+ "pptx",
124
+ "doc",
125
+ "docx"
126
+ ]);
127
+ if (imageExtensions.has(extension)) return "image";
128
+ if (videoExtensions.has(extension)) return "video";
129
+ if (documentExtensions.has(extension)) return "document";
130
+ throw new Error(`Unsupported media extension: ${extension}`);
131
+ }
132
+ async function uploadImage(params) {
133
+ const initResponse = await fetch(IMAGES_URL, {
134
+ method: "POST",
135
+ headers: getApiHeaders(params.accessToken),
136
+ body: JSON.stringify({ initializeUploadRequest: { owner: params.personUrn } })
137
+ });
138
+ if (!initResponse.ok) {
139
+ const errorText = await initResponse.text().catch(() => "");
140
+ throw new Error(`LinkedIn image upload initialization failed: ${initResponse.status} ${errorText}`);
141
+ }
142
+ const { uploadUrl, image } = (await initResponse.json()).value;
143
+ if (!uploadUrl || !image) throw new Error("LinkedIn image initialization response missing required fields");
144
+ await uploadBinaryToLinkedIn({
145
+ uploadUrl,
146
+ localPath: params.localPath,
147
+ mimeType: inferImageMime(params.localPath)
148
+ });
149
+ return image;
150
+ }
151
+ async function uploadVideo(params) {
152
+ const fileSizeBytes = await getFileSize(params.localPath);
153
+ const initResponse = await fetch(VIDEOS_URL, {
154
+ method: "POST",
155
+ headers: getApiHeaders(params.accessToken),
156
+ body: JSON.stringify({ initializeUploadRequest: {
157
+ owner: params.personUrn,
158
+ fileSizeBytes,
159
+ uploadCaptions: false,
160
+ uploadThumbnail: false
161
+ } })
162
+ });
163
+ if (!initResponse.ok) {
164
+ const errorText = await initResponse.text().catch(() => "");
165
+ throw new Error(`LinkedIn video upload initialization failed: ${initResponse.status} ${errorText}`);
166
+ }
167
+ const { uploadInstructions, video, uploadToken } = (await initResponse.json()).value;
168
+ if (!uploadInstructions || !video || !uploadToken) throw new Error("LinkedIn video initialization response missing required fields");
169
+ const uploadedPartIds = await uploadVideoMultipart({
170
+ localPath: params.localPath,
171
+ uploadInstructions
172
+ });
173
+ await finalizeVideoUpload({
174
+ accessToken: params.accessToken,
175
+ video,
176
+ uploadToken,
177
+ uploadedPartIds
178
+ });
179
+ return video;
180
+ }
181
+ async function uploadVideoMultipart(params) {
182
+ const uploadedPartIds = [];
183
+ for (const instruction of params.uploadInstructions) {
184
+ const chunkStream = (0, node_fs.createReadStream)(params.localPath, {
185
+ start: instruction.firstByte,
186
+ end: instruction.lastByte
187
+ });
188
+ const uploadResponse = await fetch(instruction.uploadUrl, {
189
+ method: "PUT",
190
+ headers: { "Content-Type": "application/octet-stream" },
191
+ body: chunkStream,
192
+ duplex: "half"
193
+ });
194
+ if (!uploadResponse.ok) {
195
+ const errorText = await uploadResponse.text().catch(() => "");
196
+ throw new Error(`LinkedIn video part upload failed: ${uploadResponse.status} ${errorText}`);
197
+ }
198
+ const etag = uploadResponse.headers.get("etag");
199
+ if (!etag) throw new Error("LinkedIn video part upload response missing ETag header");
200
+ uploadedPartIds.push(etag.replace(/"/g, ""));
201
+ }
202
+ return uploadedPartIds;
203
+ }
204
+ async function finalizeVideoUpload(params) {
205
+ const response = await fetch(VIDEOS_FINALIZE_URL, {
206
+ method: "POST",
207
+ headers: getApiHeaders(params.accessToken),
208
+ body: JSON.stringify({ finalizeUploadRequest: {
209
+ video: params.video,
210
+ uploadToken: params.uploadToken,
211
+ uploadedPartIds: params.uploadedPartIds
212
+ } })
213
+ });
214
+ if (!response.ok) {
215
+ const errorText = await response.text().catch(() => "");
216
+ throw new Error(`LinkedIn video finalize failed: ${response.status} ${errorText}`);
217
+ }
218
+ }
219
+ async function uploadDocument(params) {
220
+ const initResponse = await fetch(DOCUMENTS_URL, {
221
+ method: "POST",
222
+ headers: getApiHeaders(params.accessToken),
223
+ body: JSON.stringify({ initializeUploadRequest: { owner: params.personUrn } })
224
+ });
225
+ if (!initResponse.ok) {
226
+ const errorText = await initResponse.text().catch(() => "");
227
+ throw new Error(`LinkedIn document upload initialization failed: ${initResponse.status} ${errorText}`);
228
+ }
229
+ const { uploadUrl, document } = (await initResponse.json()).value;
230
+ if (!uploadUrl || !document) throw new Error("LinkedIn document initialization response missing required fields");
231
+ await uploadBinaryToLinkedIn({
232
+ uploadUrl,
233
+ localPath: params.localPath,
234
+ mimeType: inferDocumentMime(params.localPath)
235
+ });
236
+ return document;
237
+ }
238
+ async function uploadBinaryToLinkedIn(params) {
239
+ const uploadResponse = await fetch(params.uploadUrl, {
240
+ method: "PUT",
241
+ headers: { "Content-Type": params.mimeType },
242
+ body: (0, node_fs.createReadStream)(params.localPath),
243
+ duplex: "half"
244
+ });
245
+ if (!uploadResponse.ok) {
246
+ const errorText = await uploadResponse.text().catch(() => "");
247
+ throw new Error(`LinkedIn media upload failed: ${uploadResponse.status} ${errorText}`);
248
+ }
249
+ }
250
+ async function getFileSize(filePath) {
251
+ return (await (0, node_fs_promises.stat)(filePath)).size;
252
+ }
253
+ function inferImageMime(localPath) {
254
+ switch (localPath.split(".").pop()?.toLowerCase()) {
255
+ case "png": return "image/png";
256
+ case "webp": return "image/webp";
257
+ case "gif": return "image/gif";
258
+ case "jpg":
259
+ case "jpeg": return "image/jpeg";
260
+ default: return "application/octet-stream";
261
+ }
262
+ }
263
+ function inferDocumentMime(localPath) {
264
+ switch (localPath.split(".").pop()?.toLowerCase()) {
265
+ case "pdf": return "application/pdf";
266
+ case "doc": return "application/msword";
267
+ case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
268
+ case "ppt": return "application/vnd.ms-powerpoint";
269
+ case "pptx": return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
270
+ default: return "application/octet-stream";
271
+ }
272
+ }
273
+ async function publishPost(params) {
274
+ const body = buildPostBody(params);
275
+ const response = await fetch(POSTS_URL, {
276
+ method: "POST",
277
+ headers: getApiHeaders(params.accessToken),
278
+ body: JSON.stringify(body)
279
+ });
280
+ if (!response.ok) {
281
+ const errorText = await response.text().catch(() => "");
282
+ throw new Error(`LinkedIn post failed: ${response.status} ${errorText}`);
283
+ }
284
+ return {
285
+ postUrn: response.headers.get("x-restli-id") ?? void 0,
286
+ raw: await safeJson(response)
287
+ };
288
+ }
289
+ /**
290
+ * Escapes special characters in text for LinkedIn's post commentary field.
291
+ * LinkedIn requires certain characters to be backslash-escaped.
292
+ * Preserves mentions (@[Name](urn:li:...)) and hashtag templates ({hashtag|#|tag}).
293
+ * Converts simple hashtags (#tag) to the template format.
294
+ * @see https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/little-text-format
295
+ */
296
+ function escapeLinkedInText(text) {
297
+ const mentionPattern = /@\[[^\]]*\]\(urn:li:[^)]+\)/g;
298
+ const hashtagTemplatePattern = /\{hashtag\|\\?[##]\|[^}]+\}/g;
299
+ const simpleHashtagPattern = /#([a-zA-Z\u00C0-\u024F\u1E00-\u1EFF][\w\u00C0-\u024F\u1E00-\u1EFF]*)/g;
300
+ const preserved = [];
301
+ const createPlaceholder = (content) => {
302
+ const index = preserved.length;
303
+ preserved.push(content);
304
+ return `\x00${index}\x00`;
305
+ };
306
+ let result = text.replace(mentionPattern, (match) => createPlaceholder(match));
307
+ result = result.replace(hashtagTemplatePattern, (match) => createPlaceholder(match));
308
+ result = result.replace(simpleHashtagPattern, (_match, tag) => createPlaceholder(`{hashtag|\\#|${tag}}`));
309
+ result = result.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\{/g, "\\{").replace(/\}/g, "\\}").replace(/@/g, "\\@").replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/\(/g, "\\(").replace(/\)/g, "\\)").replace(/</g, "\\<").replace(/>/g, "\\>").replace(/#/g, "\\#").replace(/\*/g, "\\*").replace(/_/g, "\\_").replace(/~/g, "\\~");
310
+ result = result.replace(/\x00(\d+)\x00/g, (_match, index) => preserved[Number(index)]);
311
+ return result;
312
+ }
313
+ function buildPostBody(params) {
314
+ const { personUrn, payload, uploadedAssets } = params;
315
+ const basePost = {
316
+ author: personUrn,
317
+ commentary: escapeLinkedInText(payload.text),
318
+ visibility: payload.visibility,
319
+ distribution: {
320
+ feedDistribution: "MAIN_FEED",
321
+ targetEntities: [],
322
+ thirdPartyDistributionChannels: []
323
+ },
324
+ lifecycleState: "PUBLISHED",
325
+ isReshareDisabledByAuthor: false
326
+ };
327
+ if (uploadedAssets.length === 0) return basePost;
328
+ if (uploadedAssets.length > 1) throw new Error("LinkedIn Posts API currently supports only a single media asset per post");
329
+ const asset = uploadedAssets[0];
330
+ const mediaContent = { id: asset.assetUrn };
331
+ if (asset.title) mediaContent.title = asset.title;
332
+ if (asset.description && asset.mediaType === "image") mediaContent.altText = asset.description;
333
+ return {
334
+ ...basePost,
335
+ content: { media: mediaContent }
336
+ };
337
+ }
338
+ async function safeJson(response) {
339
+ try {
340
+ return await response.json();
341
+ } catch {
342
+ return;
343
+ }
344
+ }
345
+ //#endregion
346
+ exports.createLinkedInSocialProvider = createLinkedInSocialProvider;
347
+ exports.escapeLinkedInText = escapeLinkedInText;
348
+
349
+ //# sourceMappingURL=social.linkedin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social.linkedin.cjs","names":[],"sources":["../../../../src/modules/social/social.linkedin.ts"],"sourcesContent":["import { createReadStream } from \"node:fs\";\r\nimport { stat, unlink } from \"node:fs/promises\";\r\nimport type { ConnectRow } from \"../connect/connect.repository\";\r\nimport type { FileService } from \"../file/file.service\";\r\nimport type { SocialMediaDescriptor, SocialPostPayload, SocialProvider } from \"./social.types\";\r\n\r\nconst LINKEDIN_API_BASE = \"https://api.linkedin.com/rest\";\r\nconst LINKEDIN_VERSION = \"202601\";\r\n\r\nconst IMAGES_URL = `${LINKEDIN_API_BASE}/images?action=initializeUpload`;\r\nconst VIDEOS_URL = `${LINKEDIN_API_BASE}/videos?action=initializeUpload`;\r\nconst VIDEOS_FINALIZE_URL = `${LINKEDIN_API_BASE}/videos?action=finalizeUpload`;\r\nconst DOCUMENTS_URL = `${LINKEDIN_API_BASE}/documents?action=initializeUpload`;\r\nconst POSTS_URL = `${LINKEDIN_API_BASE}/posts`;\r\n\r\ninterface LinkedInMetadata {\r\n linkedInUrn?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface UploadedAsset {\r\n assetUrn: string;\r\n mediaType: \"image\" | \"video\" | \"document\";\r\n title?: string;\r\n description?: string;\r\n}\r\n\r\ninterface ImageInitResponse {\r\n value: {\r\n uploadUrlExpiresAt: number;\r\n uploadUrl: string;\r\n image: string;\r\n };\r\n}\r\n\r\ninterface VideoInitResponse {\r\n value: {\r\n uploadUrlsExpireAt: number;\r\n video: string;\r\n uploadInstructions: Array<{\r\n uploadUrl: string;\r\n lastByte: number;\r\n firstByte: number;\r\n }>;\r\n uploadToken: string;\r\n };\r\n}\r\n\r\ninterface DocumentInitResponse {\r\n value: {\r\n uploadUrlExpiresAt: number;\r\n uploadUrl: string;\r\n document: string;\r\n };\r\n}\r\n\r\nfunction getApiHeaders(accessToken: string): Record<string, string> {\r\n return {\r\n Authorization: `Bearer ${accessToken}`,\r\n \"Content-Type\": \"application/json\",\r\n \"Linkedin-Version\": LINKEDIN_VERSION,\r\n \"X-Restli-Protocol-Version\": \"2.0.0\",\r\n };\r\n}\r\n\r\nexport function createLinkedInSocialProvider(): SocialProvider {\r\n return {\r\n id: \"linkedin\",\r\n async post({ deps, context, payload }) {\r\n const personUrn = resolveAuthorUrn(context.connection);\r\n const mediaDescriptors = payload.media ?? [];\r\n\r\n let uploadedAssets: UploadedAsset[] = [];\r\n if (mediaDescriptors.length > 0) {\r\n uploadedAssets = await uploadMediaAssets({\r\n accessToken: context.accessToken,\r\n fileService: deps.fileService,\r\n mediaDescriptors,\r\n personUrn,\r\n });\r\n }\r\n\r\n const response = await publishPost({\r\n accessToken: context.accessToken,\r\n personUrn,\r\n payload,\r\n uploadedAssets,\r\n });\r\n\r\n return {\r\n shareUrn: response.postUrn,\r\n rawResponse: response.raw,\r\n };\r\n },\r\n };\r\n}\r\n\r\nfunction resolveAuthorUrn(connection: ConnectRow): string {\r\n if (connection.metadataJson) {\r\n try {\r\n const metadata = connection.metadataJson as LinkedInMetadata;\r\n if (metadata.linkedInUrn && typeof metadata.linkedInUrn === \"string\") {\r\n return metadata.linkedInUrn;\r\n }\r\n } catch {\r\n throw new Error(\"Failed to parse LinkedIn connection metadata\");\r\n }\r\n }\r\n\r\n if (connection.providerAccountId) {\r\n return `urn:li:person:${connection.providerAccountId}`;\r\n }\r\n\r\n throw new Error(\"LinkedIn connection is missing a person URN\");\r\n}\r\n\r\nasync function uploadMediaAssets(params: {\r\n accessToken: string;\r\n fileService: FileService;\r\n mediaDescriptors: readonly SocialMediaDescriptor[];\r\n personUrn: string;\r\n}): Promise<UploadedAsset[]> {\r\n const results: UploadedAsset[] = [];\r\n\r\n for (const descriptor of params.mediaDescriptors) {\r\n const download = await params.fileService.downloadS3ToFile(descriptor.s3Path);\r\n if (download.isErr()) {\r\n throw download.error;\r\n }\r\n\r\n const localPath = download.value;\r\n try {\r\n const mediaType = determineMediaType(descriptor.mediaType, localPath);\r\n\r\n let assetUrn: string;\r\n switch (mediaType) {\r\n case \"image\":\r\n assetUrn = await uploadImage({\r\n accessToken: params.accessToken,\r\n personUrn: params.personUrn,\r\n localPath,\r\n });\r\n break;\r\n case \"video\":\r\n assetUrn = await uploadVideo({\r\n accessToken: params.accessToken,\r\n personUrn: params.personUrn,\r\n localPath,\r\n });\r\n break;\r\n case \"document\":\r\n assetUrn = await uploadDocument({\r\n accessToken: params.accessToken,\r\n personUrn: params.personUrn,\r\n localPath,\r\n });\r\n break;\r\n }\r\n\r\n results.push({\r\n assetUrn,\r\n mediaType,\r\n title: descriptor.title,\r\n description: descriptor.description,\r\n });\r\n } finally {\r\n await unlink(localPath).catch(() => undefined);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction determineMediaType(\r\n explicitType: SocialMediaDescriptor[\"mediaType\"],\r\n localPath: string\r\n): \"image\" | \"video\" | \"document\" {\r\n if (explicitType) {\r\n return explicitType;\r\n }\r\n\r\n const extension = localPath.split(\".\").pop()?.toLowerCase();\r\n if (!extension) {\r\n throw new Error(\"Unable to determine media type from file extension\");\r\n }\r\n\r\n const imageExtensions = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\"]);\r\n const videoExtensions = new Set([\"mp4\", \"mov\", \"m4v\", \"avi\", \"mkv\", \"webm\"]);\r\n const documentExtensions = new Set([\"pdf\", \"ppt\", \"pptx\", \"doc\", \"docx\"]);\r\n\r\n if (imageExtensions.has(extension)) {\r\n return \"image\";\r\n }\r\n if (videoExtensions.has(extension)) {\r\n return \"video\";\r\n }\r\n if (documentExtensions.has(extension)) {\r\n return \"document\";\r\n }\r\n\r\n throw new Error(`Unsupported media extension: ${extension}`);\r\n}\r\n\r\nasync function uploadImage(params: {\r\n accessToken: string;\r\n personUrn: string;\r\n localPath: string;\r\n}): Promise<string> {\r\n const initResponse = await fetch(IMAGES_URL, {\r\n method: \"POST\",\r\n headers: getApiHeaders(params.accessToken),\r\n body: JSON.stringify({\r\n initializeUploadRequest: {\r\n owner: params.personUrn,\r\n },\r\n }),\r\n });\r\n\r\n if (!initResponse.ok) {\r\n const errorText = await initResponse.text().catch(() => \"\");\r\n throw new Error(\r\n `LinkedIn image upload initialization failed: ${initResponse.status} ${errorText}`\r\n );\r\n }\r\n\r\n const initJson = (await initResponse.json()) as ImageInitResponse;\r\n const { uploadUrl, image } = initJson.value;\r\n\r\n if (!uploadUrl || !image) {\r\n throw new Error(\"LinkedIn image initialization response missing required fields\");\r\n }\r\n\r\n await uploadBinaryToLinkedIn({\r\n uploadUrl,\r\n localPath: params.localPath,\r\n mimeType: inferImageMime(params.localPath),\r\n });\r\n\r\n return image;\r\n}\r\n\r\nasync function uploadVideo(params: {\r\n accessToken: string;\r\n personUrn: string;\r\n localPath: string;\r\n}): Promise<string> {\r\n const fileSizeBytes = await getFileSize(params.localPath);\r\n\r\n const initResponse = await fetch(VIDEOS_URL, {\r\n method: \"POST\",\r\n headers: getApiHeaders(params.accessToken),\r\n body: JSON.stringify({\r\n initializeUploadRequest: {\r\n owner: params.personUrn,\r\n fileSizeBytes,\r\n uploadCaptions: false,\r\n uploadThumbnail: false,\r\n },\r\n }),\r\n });\r\n\r\n if (!initResponse.ok) {\r\n const errorText = await initResponse.text().catch(() => \"\");\r\n throw new Error(\r\n `LinkedIn video upload initialization failed: ${initResponse.status} ${errorText}`\r\n );\r\n }\r\n\r\n const initJson = (await initResponse.json()) as VideoInitResponse;\r\n const { uploadInstructions, video, uploadToken } = initJson.value;\r\n\r\n if (!uploadInstructions || !video || !uploadToken) {\r\n throw new Error(\"LinkedIn video initialization response missing required fields\");\r\n }\r\n\r\n const uploadedPartIds = await uploadVideoMultipart({\r\n localPath: params.localPath,\r\n uploadInstructions,\r\n });\r\n\r\n await finalizeVideoUpload({\r\n accessToken: params.accessToken,\r\n video,\r\n uploadToken,\r\n uploadedPartIds,\r\n });\r\n\r\n return video;\r\n}\r\n\r\nasync function uploadVideoMultipart(params: {\r\n localPath: string;\r\n uploadInstructions: VideoInitResponse[\"value\"][\"uploadInstructions\"];\r\n}): Promise<string[]> {\r\n const uploadedPartIds: string[] = [];\r\n\r\n for (const instruction of params.uploadInstructions) {\r\n const chunkStream = createReadStream(params.localPath, {\r\n start: instruction.firstByte,\r\n end: instruction.lastByte,\r\n });\r\n\r\n const uploadResponse = await fetch(instruction.uploadUrl, {\r\n method: \"PUT\",\r\n headers: {\r\n \"Content-Type\": \"application/octet-stream\",\r\n },\r\n body: chunkStream,\r\n duplex: \"half\",\r\n });\r\n\r\n if (!uploadResponse.ok) {\r\n const errorText = await uploadResponse.text().catch(() => \"\");\r\n throw new Error(`LinkedIn video part upload failed: ${uploadResponse.status} ${errorText}`);\r\n }\r\n\r\n const etag = uploadResponse.headers.get(\"etag\");\r\n if (!etag) {\r\n throw new Error(\"LinkedIn video part upload response missing ETag header\");\r\n }\r\n\r\n uploadedPartIds.push(etag.replace(/\"/g, \"\"));\r\n }\r\n\r\n return uploadedPartIds;\r\n}\r\n\r\nasync function finalizeVideoUpload(params: {\r\n accessToken: string;\r\n video: string;\r\n uploadToken: string;\r\n uploadedPartIds: string[];\r\n}): Promise<void> {\r\n const response = await fetch(VIDEOS_FINALIZE_URL, {\r\n method: \"POST\",\r\n headers: getApiHeaders(params.accessToken),\r\n body: JSON.stringify({\r\n finalizeUploadRequest: {\r\n video: params.video,\r\n uploadToken: params.uploadToken,\r\n uploadedPartIds: params.uploadedPartIds,\r\n },\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => \"\");\r\n throw new Error(`LinkedIn video finalize failed: ${response.status} ${errorText}`);\r\n }\r\n}\r\n\r\nasync function uploadDocument(params: {\r\n accessToken: string;\r\n personUrn: string;\r\n localPath: string;\r\n}): Promise<string> {\r\n const initResponse = await fetch(DOCUMENTS_URL, {\r\n method: \"POST\",\r\n headers: getApiHeaders(params.accessToken),\r\n body: JSON.stringify({\r\n initializeUploadRequest: {\r\n owner: params.personUrn,\r\n },\r\n }),\r\n });\r\n\r\n if (!initResponse.ok) {\r\n const errorText = await initResponse.text().catch(() => \"\");\r\n throw new Error(\r\n `LinkedIn document upload initialization failed: ${initResponse.status} ${errorText}`\r\n );\r\n }\r\n\r\n const initJson = (await initResponse.json()) as DocumentInitResponse;\r\n const { uploadUrl, document } = initJson.value;\r\n\r\n if (!uploadUrl || !document) {\r\n throw new Error(\"LinkedIn document initialization response missing required fields\");\r\n }\r\n\r\n await uploadBinaryToLinkedIn({\r\n uploadUrl,\r\n localPath: params.localPath,\r\n mimeType: inferDocumentMime(params.localPath),\r\n });\r\n\r\n return document;\r\n}\r\n\r\nasync function uploadBinaryToLinkedIn(params: {\r\n uploadUrl: string;\r\n localPath: string;\r\n mimeType: string;\r\n}): Promise<void> {\r\n const uploadResponse = await fetch(params.uploadUrl, {\r\n method: \"PUT\",\r\n headers: {\r\n \"Content-Type\": params.mimeType,\r\n },\r\n body: createReadStream(params.localPath),\r\n duplex: \"half\",\r\n });\r\n\r\n if (!uploadResponse.ok) {\r\n const errorText = await uploadResponse.text().catch(() => \"\");\r\n throw new Error(`LinkedIn media upload failed: ${uploadResponse.status} ${errorText}`);\r\n }\r\n}\r\n\r\nasync function getFileSize(filePath: string): Promise<number> {\r\n const stats = await stat(filePath);\r\n return stats.size;\r\n}\r\n\r\nfunction inferImageMime(localPath: string): string {\r\n const extension = localPath.split(\".\").pop()?.toLowerCase();\r\n switch (extension) {\r\n case \"png\":\r\n return \"image/png\";\r\n case \"webp\":\r\n return \"image/webp\";\r\n case \"gif\":\r\n return \"image/gif\";\r\n case \"jpg\":\r\n case \"jpeg\":\r\n return \"image/jpeg\";\r\n default:\r\n return \"application/octet-stream\";\r\n }\r\n}\r\n\r\nfunction inferDocumentMime(localPath: string): string {\r\n const extension = localPath.split(\".\").pop()?.toLowerCase();\r\n switch (extension) {\r\n case \"pdf\":\r\n return \"application/pdf\";\r\n case \"doc\":\r\n return \"application/msword\";\r\n case \"docx\":\r\n return \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\";\r\n case \"ppt\":\r\n return \"application/vnd.ms-powerpoint\";\r\n case \"pptx\":\r\n return \"application/vnd.openxmlformats-officedocument.presentationml.presentation\";\r\n default:\r\n return \"application/octet-stream\";\r\n }\r\n}\r\n\r\nasync function publishPost(params: {\r\n accessToken: string;\r\n personUrn: string;\r\n payload: SocialPostPayload;\r\n uploadedAssets: UploadedAsset[];\r\n}): Promise<{ postUrn?: string; raw: unknown }> {\r\n const body = buildPostBody(params);\r\n\r\n const response = await fetch(POSTS_URL, {\r\n method: \"POST\",\r\n headers: getApiHeaders(params.accessToken),\r\n body: JSON.stringify(body),\r\n });\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => \"\");\r\n throw new Error(`LinkedIn post failed: ${response.status} ${errorText}`);\r\n }\r\n\r\n const postUrn = response.headers.get(\"x-restli-id\") ?? undefined;\r\n const raw = await safeJson(response);\r\n\r\n return { postUrn, raw };\r\n}\r\n\r\n/**\r\n * Escapes special characters in text for LinkedIn's post commentary field.\r\n * LinkedIn requires certain characters to be backslash-escaped.\r\n * Preserves mentions (@[Name](urn:li:...)) and hashtag templates ({hashtag|#|tag}).\r\n * Converts simple hashtags (#tag) to the template format.\r\n * @see https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/little-text-format\r\n */\r\nexport function escapeLinkedInText(text: string): string {\r\n // Patterns for LinkedIn elements that should not be escaped\r\n // MentionElement: @[FallbackText](urn:li:...)\r\n const mentionPattern = /@\\[[^\\]]*\\]\\(urn:li:[^)]+\\)/g;\r\n // HashtagTemplate: {hashtag|#|text} or {hashtag|#|text} (with optional escaped #)\r\n const hashtagTemplatePattern = /\\{hashtag\\|\\\\?[##]\\|[^}]+\\}/g;\r\n // Simple hashtag: #word (must start with a letter, not digit-only)\r\n const simpleHashtagPattern =\r\n /#([a-zA-Z\\u00C0-\\u024F\\u1E00-\\u1EFF][\\w\\u00C0-\\u024F\\u1E00-\\u1EFF]*)/g;\r\n\r\n // Store matches in array, use index-based placeholders with null character delimiters\r\n const preserved: string[] = [];\r\n\r\n const createPlaceholder = (content: string): string => {\r\n const index = preserved.length;\r\n preserved.push(content);\r\n return `\\x00${index}\\x00`;\r\n };\r\n\r\n // Replace mentions with placeholders (preserve as-is)\r\n let result = text.replace(mentionPattern, (match) => createPlaceholder(match));\r\n\r\n // Replace hashtag templates with placeholders (preserve as-is)\r\n result = result.replace(hashtagTemplatePattern, (match) => createPlaceholder(match));\r\n\r\n // Convert simple hashtags to template format with escaped #\r\n result = result.replace(simpleHashtagPattern, (_match, tag: string) =>\r\n createPlaceholder(`{hashtag|\\\\#|${tag}}`)\r\n );\r\n\r\n // Escape remaining special characters (order matters: backslash first)\r\n result = result\r\n .replace(/\\\\/g, \"\\\\\\\\\")\r\n .replace(/\\|/g, \"\\\\|\")\r\n .replace(/\\{/g, \"\\\\{\")\r\n .replace(/\\}/g, \"\\\\}\")\r\n .replace(/@/g, \"\\\\@\")\r\n .replace(/\\[/g, \"\\\\[\")\r\n .replace(/\\]/g, \"\\\\]\")\r\n .replace(/\\(/g, \"\\\\(\")\r\n .replace(/\\)/g, \"\\\\)\")\r\n .replace(/</g, \"\\\\<\")\r\n .replace(/>/g, \"\\\\>\")\r\n .replace(/#/g, \"\\\\#\")\r\n .replace(/\\*/g, \"\\\\*\")\r\n .replace(/_/g, \"\\\\_\")\r\n .replace(/~/g, \"\\\\~\");\r\n\r\n // Restore placeholders with preserved/transformed content\r\n // biome-ignore lint/suspicious/noControlCharactersInRegex: null chars are intentional placeholders\r\n result = result.replace(/\\x00(\\d+)\\x00/g, (_match, index: string) => preserved[Number(index)]);\r\n\r\n return result;\r\n}\r\n\r\nfunction buildPostBody(params: {\r\n personUrn: string;\r\n payload: SocialPostPayload;\r\n uploadedAssets: UploadedAsset[];\r\n}): Record<string, unknown> {\r\n const { personUrn, payload, uploadedAssets } = params;\r\n\r\n const basePost = {\r\n author: personUrn,\r\n commentary: escapeLinkedInText(payload.text),\r\n visibility: payload.visibility,\r\n distribution: {\r\n feedDistribution: \"MAIN_FEED\",\r\n targetEntities: [],\r\n thirdPartyDistributionChannels: [],\r\n },\r\n lifecycleState: \"PUBLISHED\",\r\n isReshareDisabledByAuthor: false,\r\n };\r\n\r\n if (uploadedAssets.length === 0) {\r\n return basePost;\r\n }\r\n\r\n if (uploadedAssets.length > 1) {\r\n throw new Error(\"LinkedIn Posts API currently supports only a single media asset per post\");\r\n }\r\n\r\n const asset = uploadedAssets[0];\r\n\r\n const mediaContent: Record<string, unknown> = {\r\n id: asset.assetUrn,\r\n };\r\n\r\n if (asset.title) {\r\n mediaContent.title = asset.title;\r\n }\r\n\r\n if (asset.description && asset.mediaType === \"image\") {\r\n mediaContent.altText = asset.description;\r\n }\r\n\r\n return {\r\n ...basePost,\r\n content: {\r\n media: mediaContent,\r\n },\r\n };\r\n}\r\n\r\nasync function safeJson(response: globalThis.Response): Promise<unknown> {\r\n try {\r\n return await response.json();\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { SocialProvider } from "./social.types.cjs";
2
+
3
+ //#region src/modules/social/social.linkedin.d.ts
4
+ declare function createLinkedInSocialProvider(): SocialProvider;
5
+ /**
6
+ * Escapes special characters in text for LinkedIn's post commentary field.
7
+ * LinkedIn requires certain characters to be backslash-escaped.
8
+ * Preserves mentions (@[Name](urn:li:...)) and hashtag templates ({hashtag|#|tag}).
9
+ * Converts simple hashtags (#tag) to the template format.
10
+ * @see https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/little-text-format
11
+ */
12
+ declare function escapeLinkedInText(text: string): string;
13
+ //#endregion
14
+ export { createLinkedInSocialProvider, escapeLinkedInText };
15
+ //# sourceMappingURL=social.linkedin.d.cts.map
@@ -0,0 +1,57 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../../../_virtual/_rolldown/runtime.cjs");
3
+ const require_src_modules_base_base_service = require("../base/base.service.cjs");
4
+ let neverthrow = require("neverthrow");
5
+ //#region src/modules/social/social.service.ts
6
+ var SocialService = class extends require_src_modules_base_base_service.BaseService {
7
+ providers = /* @__PURE__ */ new Map();
8
+ constructor(repositories, services, providers) {
9
+ super(repositories, services);
10
+ this.providers = new Map(providers.map((provider) => [provider.id, provider]));
11
+ }
12
+ getProvider(id) {
13
+ return this.providers.get(id) ?? null;
14
+ }
15
+ async postToProvider(providerId, input, { actor }) {
16
+ return this.throwableAsync(async () => {
17
+ const provider = this.getProvider(providerId);
18
+ if (!provider) return this.error("BAD_REQUEST", `Unknown provider: ${providerId}`);
19
+ const connectionResult = await this.repository.connect.list({
20
+ userId: actor.userId,
21
+ providers: [providerId]
22
+ });
23
+ if (connectionResult.isErr()) return this.error("INTERNAL_SERVER_ERROR", "Failed to load connection", { cause: connectionResult.error });
24
+ const activeConnection = connectionResult.value.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())[0];
25
+ const connection = await this.ensureFreshConnection(activeConnection);
26
+ if (connection.isErr()) return this.error("INTERNAL_SERVER_ERROR", "Failed to refresh connection", { cause: connection.error });
27
+ const payload = {
28
+ text: input.text,
29
+ media: input.media,
30
+ visibility: input.visibility ?? "PUBLIC"
31
+ };
32
+ const accessToken = connection.value.accessToken;
33
+ if (!accessToken) return this.error("BAD_REQUEST", "Missing access token for connection");
34
+ return (0, neverthrow.ok)(await provider.post({
35
+ deps: { fileService: this.service.file },
36
+ context: {
37
+ userId: actor.userId,
38
+ connection: connection.value,
39
+ accessToken
40
+ },
41
+ payload
42
+ }));
43
+ });
44
+ }
45
+ async ensureFreshConnection(connection) {
46
+ if (!connection.expiresAt || !connection.refreshToken) return (0, neverthrow.ok)(connection);
47
+ const expiresAt = new Date(connection.expiresAt);
48
+ if (Date.now() < expiresAt.getTime() - 60 * 1e3) return (0, neverthrow.ok)(connection);
49
+ const refreshed = await this.service.connect.refreshToken(connection.id);
50
+ if (refreshed.isErr()) return this.error("INTERNAL_SERVER_ERROR", "Failed to refresh access token", { cause: refreshed.error });
51
+ return (0, neverthrow.ok)(refreshed.value);
52
+ }
53
+ };
54
+ //#endregion
55
+ exports.SocialService = SocialService;
56
+
57
+ //# sourceMappingURL=social.service.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social.service.cjs","names":["BaseService"],"sources":["../../../../src/modules/social/social.service.ts"],"sourcesContent":["import { ok } from \"neverthrow\";\r\nimport type { RequiredServiceActor } from \"../base/base.actor\";\r\nimport type { ServerResultAsync } from \"../base/base.dto\";\r\nimport { BaseService } from \"../base/base.service\";\r\nimport type { ConnectRepository, ConnectRow } from \"../connect/connect.repository\";\r\nimport type { ConnectService } from \"../connect/connect.service\";\r\nimport type { FileService } from \"../file/file.service\";\r\nimport type { SocialPostInput } from \"./social.dto\";\r\nimport type { SocialPostPayload, SocialPostResult, SocialProvider } from \"./social.types\";\r\n\r\nexport class SocialService extends BaseService<\r\n {\r\n connect: ConnectRepository;\r\n },\r\n {\r\n connect: ConnectService;\r\n file: FileService;\r\n }\r\n> {\r\n private providers = new Map<string, SocialProvider>();\r\n\r\n constructor(\r\n repositories: { connect: ConnectRepository },\r\n services: { connect: ConnectService; file: FileService },\r\n providers: SocialProvider[]\r\n ) {\r\n super(repositories, services);\r\n this.providers = new Map(providers.map((provider) => [provider.id, provider]));\r\n }\r\n\r\n getProvider(id: string): SocialProvider | null {\r\n return this.providers.get(id) ?? null;\r\n }\r\n\r\n async postToProvider(\r\n providerId: string,\r\n input: SocialPostInput,\r\n { actor }: { actor: RequiredServiceActor<\"user\"> }\r\n ): ServerResultAsync<SocialPostResult> {\r\n return this.throwableAsync(async () => {\r\n const provider = this.getProvider(providerId);\r\n if (!provider) {\r\n return this.error(\"BAD_REQUEST\", `Unknown provider: ${providerId}`);\r\n }\r\n\r\n const connectionResult = await this.repository.connect.list({\r\n userId: actor.userId,\r\n providers: [providerId],\r\n });\r\n\r\n if (connectionResult.isErr()) {\r\n return this.error(\"INTERNAL_SERVER_ERROR\", \"Failed to load connection\", {\r\n cause: connectionResult.error,\r\n });\r\n }\r\n\r\n const activeConnection = connectionResult.value.sort(\r\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\r\n )[0];\r\n\r\n const connection = await this.ensureFreshConnection(activeConnection);\r\n if (connection.isErr()) {\r\n return this.error(\"INTERNAL_SERVER_ERROR\", \"Failed to refresh connection\", {\r\n cause: connection.error,\r\n });\r\n }\r\n\r\n const payload: SocialPostPayload = {\r\n text: input.text,\r\n media: input.media,\r\n visibility: input.visibility ?? \"PUBLIC\",\r\n };\r\n\r\n const accessToken = connection.value.accessToken;\r\n if (!accessToken) {\r\n return this.error(\"BAD_REQUEST\", \"Missing access token for connection\");\r\n }\r\n\r\n const result = await provider.post({\r\n deps: { fileService: this.service.file },\r\n context: {\r\n userId: actor.userId,\r\n connection: connection.value,\r\n accessToken,\r\n },\r\n payload,\r\n });\r\n\r\n return ok(result);\r\n });\r\n }\r\n\r\n private async ensureFreshConnection(connection: ConnectRow): ServerResultAsync<ConnectRow> {\r\n if (!connection.expiresAt || !connection.refreshToken) {\r\n return ok(connection);\r\n }\r\n\r\n const expiresAt = new Date(connection.expiresAt);\r\n const bufferMs = 60 * 1000; // Refresh 1 minute before expiry\r\n if (Date.now() < expiresAt.getTime() - bufferMs) {\r\n return ok(connection);\r\n }\r\n\r\n const refreshed = await this.service.connect.refreshToken(connection.id);\r\n if (refreshed.isErr()) {\r\n return this.error(\"INTERNAL_SERVER_ERROR\", \"Failed to refresh access token\", {\r\n cause: refreshed.error,\r\n });\r\n }\r\n\r\n return ok(refreshed.value);\r\n }\r\n}\r\n"],"mappings":";;;;;AAUA,IAAa,gBAAb,cAAmCA,sCAAAA,YAQjC;CACA,4BAAoB,IAAI,KAA6B;CAErD,YACE,cACA,UACA,WACA;AACA,QAAM,cAAc,SAAS;AAC7B,OAAK,YAAY,IAAI,IAAI,UAAU,KAAK,aAAa,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC;;CAGhF,YAAY,IAAmC;AAC7C,SAAO,KAAK,UAAU,IAAI,GAAG,IAAI;;CAGnC,MAAM,eACJ,YACA,OACA,EAAE,SACmC;AACrC,SAAO,KAAK,eAAe,YAAY;GACrC,MAAM,WAAW,KAAK,YAAY,WAAW;AAC7C,OAAI,CAAC,SACH,QAAO,KAAK,MAAM,eAAe,qBAAqB,aAAa;GAGrE,MAAM,mBAAmB,MAAM,KAAK,WAAW,QAAQ,KAAK;IAC1D,QAAQ,MAAM;IACd,WAAW,CAAC,WAAW;IACxB,CAAC;AAEF,OAAI,iBAAiB,OAAO,CAC1B,QAAO,KAAK,MAAM,yBAAyB,6BAA6B,EACtE,OAAO,iBAAiB,OACzB,CAAC;GAGJ,MAAM,mBAAmB,iBAAiB,MAAM,MAC7C,GAAG,MAAM,EAAE,UAAU,SAAS,GAAG,EAAE,UAAU,SAAS,CACxD,CAAC;GAEF,MAAM,aAAa,MAAM,KAAK,sBAAsB,iBAAiB;AACrE,OAAI,WAAW,OAAO,CACpB,QAAO,KAAK,MAAM,yBAAyB,gCAAgC,EACzE,OAAO,WAAW,OACnB,CAAC;GAGJ,MAAM,UAA6B;IACjC,MAAM,MAAM;IACZ,OAAO,MAAM;IACb,YAAY,MAAM,cAAc;IACjC;GAED,MAAM,cAAc,WAAW,MAAM;AACrC,OAAI,CAAC,YACH,QAAO,KAAK,MAAM,eAAe,sCAAsC;AAazE,WAAA,GAAA,WAAA,IAVe,MAAM,SAAS,KAAK;IACjC,MAAM,EAAE,aAAa,KAAK,QAAQ,MAAM;IACxC,SAAS;KACP,QAAQ,MAAM;KACd,YAAY,WAAW;KACvB;KACD;IACD;IACD,CAAC,CAEe;IACjB;;CAGJ,MAAc,sBAAsB,YAAuD;AACzF,MAAI,CAAC,WAAW,aAAa,CAAC,WAAW,aACvC,SAAA,GAAA,WAAA,IAAU,WAAW;EAGvB,MAAM,YAAY,IAAI,KAAK,WAAW,UAAU;AAEhD,MAAI,KAAK,KAAK,GAAG,UAAU,SAAS,GADnB,KAAK,IAEpB,SAAA,GAAA,WAAA,IAAU,WAAW;EAGvB,MAAM,YAAY,MAAM,KAAK,QAAQ,QAAQ,aAAa,WAAW,GAAG;AACxE,MAAI,UAAU,OAAO,CACnB,QAAO,KAAK,MAAM,yBAAyB,kCAAkC,EAC3E,OAAO,UAAU,OAClB,CAAC;AAGJ,UAAA,GAAA,WAAA,IAAU,UAAU,MAAM"}
@@ -0,0 +1,34 @@
1
+ import { ServerResultAsync } from "../base/base.dto.cjs";
2
+ import { RequiredServiceActor } from "../base/base.actor.cjs";
3
+ import { BaseService } from "../base/base.service.cjs";
4
+ import { ConnectRepository } from "../connect/connect.repository.cjs";
5
+ import { ConnectService } from "../connect/connect.service.cjs";
6
+ import { FileService } from "../file/file.service.cjs";
7
+ import { SocialPostInput } from "./social.dto.cjs";
8
+ import { SocialPostResult, SocialProvider } from "./social.types.cjs";
9
+
10
+ //#region src/modules/social/social.service.d.ts
11
+ declare class SocialService extends BaseService<{
12
+ connect: ConnectRepository;
13
+ }, {
14
+ connect: ConnectService;
15
+ file: FileService;
16
+ }> {
17
+ private providers;
18
+ constructor(repositories: {
19
+ connect: ConnectRepository;
20
+ }, services: {
21
+ connect: ConnectService;
22
+ file: FileService;
23
+ }, providers: SocialProvider[]);
24
+ getProvider(id: string): SocialProvider | null;
25
+ postToProvider(providerId: string, input: SocialPostInput, {
26
+ actor
27
+ }: {
28
+ actor: RequiredServiceActor<"user">;
29
+ }): ServerResultAsync<SocialPostResult>;
30
+ private ensureFreshConnection;
31
+ }
32
+ //#endregion
33
+ export { SocialService };
34
+ //# sourceMappingURL=social.service.d.cts.map
File without changes
@@ -0,0 +1,40 @@
1
+ import { ConnectRow } from "../connect/connect.repository.cjs";
2
+ import { FileService } from "../file/file.service.cjs";
3
+
4
+ //#region src/modules/social/social.types.d.ts
5
+ type SocialMediaType = "image" | "video" | "document";
6
+ interface SocialMediaDescriptor {
7
+ readonly s3Path: string;
8
+ readonly mediaType?: SocialMediaType;
9
+ readonly title?: string;
10
+ readonly description?: string;
11
+ }
12
+ type SocialVisibility = "PUBLIC" | "CONNECTIONS";
13
+ interface SocialPostPayload {
14
+ readonly text: string;
15
+ readonly media?: readonly SocialMediaDescriptor[];
16
+ readonly visibility: SocialVisibility;
17
+ }
18
+ interface SocialProviderContext {
19
+ readonly userId: string;
20
+ readonly connection: ConnectRow;
21
+ readonly accessToken: string;
22
+ }
23
+ interface SocialProviderDeps {
24
+ readonly fileService: FileService;
25
+ }
26
+ interface SocialPostResult {
27
+ readonly shareUrn?: string;
28
+ readonly rawResponse?: unknown;
29
+ }
30
+ interface SocialProvider {
31
+ readonly id: string;
32
+ post(options: {
33
+ deps: SocialProviderDeps;
34
+ context: SocialProviderContext;
35
+ payload: SocialPostPayload;
36
+ }): Promise<SocialPostResult>;
37
+ }
38
+ //#endregion
39
+ export { SocialMediaDescriptor, SocialMediaType, SocialPostPayload, SocialPostResult, SocialProvider, SocialProviderContext, SocialProviderDeps, SocialVisibility };
40
+ //# sourceMappingURL=social.types.d.cts.map
@@ -0,0 +1,43 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
3
+ const require_src_modules_auth_auth_db = require("../auth/auth.db.cjs");
4
+ let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
5
+ let uuid = require("uuid");
6
+ //#region src/modules/tag/tag.db.ts
7
+ var tag_db_exports = /* @__PURE__ */ require_runtime.__exportAll({
8
+ taggings: () => taggings,
9
+ tags: () => tags
10
+ });
11
+ const tags = (0, drizzle_orm_sqlite_core.sqliteTable)("tags", {
12
+ id: (0, drizzle_orm_sqlite_core.text)("id").primaryKey().$default(uuid.v4),
13
+ createdAt: (0, drizzle_orm_sqlite_core.integer)("created_at", { mode: "timestamp" }).notNull().$default(() => /* @__PURE__ */ new Date()),
14
+ updatedAt: (0, drizzle_orm_sqlite_core.integer)("updated_at", { mode: "timestamp" }),
15
+ deletedAt: (0, drizzle_orm_sqlite_core.integer)("deleted_at", { mode: "timestamp" }),
16
+ userId: (0, drizzle_orm_sqlite_core.text)("user_id").notNull().references(() => require_src_modules_auth_auth_db.users.id, { onDelete: "cascade" }),
17
+ organizationId: (0, drizzle_orm_sqlite_core.text)("organization_id").references(() => require_src_modules_auth_auth_db.organizations.id, { onDelete: "cascade" }),
18
+ teamId: (0, drizzle_orm_sqlite_core.text)("team_id").references(() => require_src_modules_auth_auth_db.teams.id, { onDelete: "cascade" }),
19
+ name: (0, drizzle_orm_sqlite_core.text)("name").notNull(),
20
+ color: (0, drizzle_orm_sqlite_core.text)("color"),
21
+ type: (0, drizzle_orm_sqlite_core.text)("type"),
22
+ isEnabled: (0, drizzle_orm_sqlite_core.integer)("is_enabled", { mode: "boolean" }).notNull().default(true),
23
+ parentId: (0, drizzle_orm_sqlite_core.text)("parent_id").references(() => tags.id, { onDelete: "set null" }),
24
+ assignableTo: (0, drizzle_orm_sqlite_core.text)("assignable_to", { mode: "json" }).notNull().$type()
25
+ });
26
+ const taggings = (0, drizzle_orm_sqlite_core.sqliteTable)("taggings", {
27
+ id: (0, drizzle_orm_sqlite_core.text)("id").primaryKey().$default(uuid.v4),
28
+ createdAt: (0, drizzle_orm_sqlite_core.integer)("created_at", { mode: "timestamp" }).notNull().$default(() => /* @__PURE__ */ new Date()),
29
+ tagId: (0, drizzle_orm_sqlite_core.text)("tag_id").notNull().references(() => tags.id, { onDelete: "cascade" }),
30
+ resourceType: (0, drizzle_orm_sqlite_core.text)("resource_type").notNull(),
31
+ resourceId: (0, drizzle_orm_sqlite_core.text)("resource_id").notNull()
32
+ });
33
+ //#endregion
34
+ Object.defineProperty(exports, "tag_db_exports", {
35
+ enumerable: true,
36
+ get: function() {
37
+ return tag_db_exports;
38
+ }
39
+ });
40
+ exports.taggings = taggings;
41
+ exports.tags = tags;
42
+
43
+ //# sourceMappingURL=tag.db.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.db.cjs","names":["uuidv4","users","organizations","teams"],"sources":["../../../../src/modules/tag/tag.db.ts"],"sourcesContent":["import { type AnySQLiteColumn, integer, sqliteTable, text } from \"drizzle-orm/sqlite-core\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\nimport { organizations, teams, users } from \"../auth/auth.db\";\r\n\r\nexport const tags = sqliteTable(\"tags\", {\r\n id: text(\"id\").primaryKey().$default(uuidv4),\r\n createdAt: integer(\"created_at\", { mode: \"timestamp\" })\r\n .notNull()\r\n .$default(() => new Date()),\r\n updatedAt: integer(\"updated_at\", { mode: \"timestamp\" }),\r\n deletedAt: integer(\"deleted_at\", { mode: \"timestamp\" }),\r\n userId: text(\"user_id\")\r\n .notNull()\r\n .references(() => users.id, { onDelete: \"cascade\" }),\r\n organizationId: text(\"organization_id\").references(() => organizations.id, {\r\n onDelete: \"cascade\",\r\n }),\r\n teamId: text(\"team_id\").references(() => teams.id, { onDelete: \"cascade\" }),\r\n name: text(\"name\").notNull(),\r\n color: text(\"color\"),\r\n type: text(\"type\"),\r\n isEnabled: integer(\"is_enabled\", { mode: \"boolean\" }).notNull().default(true),\r\n parentId: text(\"parent_id\").references((): AnySQLiteColumn => tags.id, { onDelete: \"set null\" }),\r\n assignableTo: text(\"assignable_to\", {\r\n mode: \"json\",\r\n })\r\n .notNull()\r\n .$type<string[]>(),\r\n});\r\n\r\nexport const taggings = sqliteTable(\"taggings\", {\r\n id: text(\"id\").primaryKey().$default(uuidv4),\r\n createdAt: integer(\"created_at\", { mode: \"timestamp\" })\r\n .notNull()\r\n .$default(() => new Date()),\r\n tagId: text(\"tag_id\")\r\n .notNull()\r\n .references(() => tags.id, { onDelete: \"cascade\" }),\r\n resourceType: text(\"resource_type\").notNull(), // e.g., \"post\", \"image\"\r\n resourceId: text(\"resource_id\").notNull(), // id in the resource table\r\n});\r\n"],"mappings":";;;;;;;;;;AAIA,MAAa,QAAA,GAAA,wBAAA,aAAmB,QAAQ;CACtC,KAAA,GAAA,wBAAA,MAAS,KAAK,CAAC,YAAY,CAAC,SAASA,KAAAA,GAAO;CAC5C,YAAA,GAAA,wBAAA,SAAmB,cAAc,EAAE,MAAM,aAAa,CAAC,CACpD,SAAS,CACT,+BAAe,IAAI,MAAM,CAAC;CAC7B,YAAA,GAAA,wBAAA,SAAmB,cAAc,EAAE,MAAM,aAAa,CAAC;CACvD,YAAA,GAAA,wBAAA,SAAmB,cAAc,EAAE,MAAM,aAAa,CAAC;CACvD,SAAA,GAAA,wBAAA,MAAa,UAAU,CACpB,SAAS,CACT,iBAAiBC,iCAAAA,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CACtD,iBAAA,GAAA,wBAAA,MAAqB,kBAAkB,CAAC,iBAAiBC,iCAAAA,cAAc,IAAI,EACzE,UAAU,WACX,CAAC;CACF,SAAA,GAAA,wBAAA,MAAa,UAAU,CAAC,iBAAiBC,iCAAAA,MAAM,IAAI,EAAE,UAAU,WAAW,CAAC;CAC3E,OAAA,GAAA,wBAAA,MAAW,OAAO,CAAC,SAAS;CAC5B,QAAA,GAAA,wBAAA,MAAY,QAAQ;CACpB,OAAA,GAAA,wBAAA,MAAW,OAAO;CAClB,YAAA,GAAA,wBAAA,SAAmB,cAAc,EAAE,MAAM,WAAW,CAAC,CAAC,SAAS,CAAC,QAAQ,KAAK;CAC7E,WAAA,GAAA,wBAAA,MAAe,YAAY,CAAC,iBAAkC,KAAK,IAAI,EAAE,UAAU,YAAY,CAAC;CAChG,eAAA,GAAA,wBAAA,MAAmB,iBAAiB,EAClC,MAAM,QACP,CAAC,CACC,SAAS,CACT,OAAiB;CACrB,CAAC;AAEF,MAAa,YAAA,GAAA,wBAAA,aAAuB,YAAY;CAC9C,KAAA,GAAA,wBAAA,MAAS,KAAK,CAAC,YAAY,CAAC,SAASH,KAAAA,GAAO;CAC5C,YAAA,GAAA,wBAAA,SAAmB,cAAc,EAAE,MAAM,aAAa,CAAC,CACpD,SAAS,CACT,+BAAe,IAAI,MAAM,CAAC;CAC7B,QAAA,GAAA,wBAAA,MAAY,SAAS,CAClB,SAAS,CACT,iBAAiB,KAAK,IAAI,EAAE,UAAU,WAAW,CAAC;CACrD,eAAA,GAAA,wBAAA,MAAmB,gBAAgB,CAAC,SAAS;CAC7C,aAAA,GAAA,wBAAA,MAAiB,cAAc,CAAC,SAAS;CAC1C,CAAC"}