@orthacms/content-server 0.4.2 → 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 (246) hide show
  1. package/dist/index.d.ts +16 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +41 -1
  4. package/dist/lib/content.module.d.ts.map +1 -1
  5. package/dist/lib/content.module.js +31 -1
  6. package/dist/lib/copilot/entry-proposal.applier.d.ts.map +1 -1
  7. package/dist/lib/copilot/entry-proposal.applier.js +5 -4
  8. package/dist/lib/docs/describe-content-api.d.ts +7 -0
  9. package/dist/lib/docs/describe-content-api.d.ts.map +1 -1
  10. package/dist/lib/docs/describe-content-api.js +177 -53
  11. package/dist/lib/docs/describe-views-api.d.ts +19 -0
  12. package/dist/lib/docs/describe-views-api.d.ts.map +1 -0
  13. package/dist/lib/docs/describe-views-api.js +76 -0
  14. package/dist/lib/docs/field-schema.d.ts.map +1 -1
  15. package/dist/lib/docs/field-schema.js +36 -2
  16. package/dist/lib/docs/openapi-writer.d.ts +49 -0
  17. package/dist/lib/docs/openapi-writer.d.ts.map +1 -0
  18. package/dist/lib/docs/openapi-writer.js +76 -0
  19. package/dist/lib/docs/public-api-schemas.d.ts +45 -0
  20. package/dist/lib/docs/public-api-schemas.d.ts.map +1 -0
  21. package/dist/lib/docs/public-api-schemas.js +377 -0
  22. package/dist/lib/docs/views-schemas.d.ts +22 -0
  23. package/dist/lib/docs/views-schemas.d.ts.map +1 -0
  24. package/dist/lib/docs/views-schemas.js +156 -0
  25. package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.d.ts +8 -2
  26. package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.d.ts.map +1 -1
  27. package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.js +19 -4
  28. package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.d.ts +2 -2
  29. package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.d.ts.map +1 -1
  30. package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.js +14 -4
  31. package/dist/lib/entries/application/use-cases/publish-entry.use-case.d.ts +6 -8
  32. package/dist/lib/entries/application/use-cases/publish-entry.use-case.d.ts.map +1 -1
  33. package/dist/lib/entries/application/use-cases/publish-entry.use-case.js +10 -4
  34. package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.d.ts +2 -5
  35. package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.d.ts.map +1 -1
  36. package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.js +6 -1
  37. package/dist/lib/entries/domain/entry.d.ts +8 -1
  38. package/dist/lib/entries/domain/entry.d.ts.map +1 -1
  39. package/dist/lib/entries/domain/entry.js +12 -12
  40. package/dist/lib/entries/domain/events/entry-events.d.ts +47 -5
  41. package/dist/lib/entries/domain/events/entry-events.d.ts.map +1 -1
  42. package/dist/lib/entries/domain/events/entry-events.js +26 -10
  43. package/dist/lib/entries/entries.constants.d.ts +10 -2
  44. package/dist/lib/entries/entries.constants.d.ts.map +1 -1
  45. package/dist/lib/entries/entries.constants.js +11 -2
  46. package/dist/lib/entries/http/controllers/bulk-entries.controller.d.ts +2 -2
  47. package/dist/lib/entries/http/controllers/bulk-entries.controller.d.ts.map +1 -1
  48. package/dist/lib/entries/http/controllers/bulk-entries.controller.js +8 -6
  49. package/dist/lib/entries/http/controllers/create-entry.controller.d.ts.map +1 -1
  50. package/dist/lib/entries/http/controllers/create-entry.controller.js +1 -1
  51. package/dist/lib/entries/http/controllers/update-entry.controller.d.ts.map +1 -1
  52. package/dist/lib/entries/http/controllers/update-entry.controller.js +1 -1
  53. package/dist/lib/entries/http/dto/extension-bag.validator.d.ts +14 -0
  54. package/dist/lib/entries/http/dto/extension-bag.validator.d.ts.map +1 -0
  55. package/dist/lib/entries/http/dto/extension-bag.validator.js +42 -0
  56. package/dist/lib/entries/http/dto/save-entry.dto.d.ts +16 -0
  57. package/dist/lib/entries/http/dto/save-entry.dto.d.ts.map +1 -1
  58. package/dist/lib/entries/http/dto/save-entry.dto.js +30 -0
  59. package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts +50 -8
  60. package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts.map +1 -1
  61. package/dist/lib/entries/infrastructure/persistence/entry-writer.service.js +183 -32
  62. package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts +38 -5
  63. package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts.map +1 -1
  64. package/dist/lib/entries/infrastructure/persistence/relation-link.service.js +38 -6
  65. package/dist/lib/entries/infrastructure/queries/entries.service.d.ts +3 -1
  66. package/dist/lib/entries/infrastructure/queries/entries.service.d.ts.map +1 -1
  67. package/dist/lib/entries/infrastructure/queries/entries.service.js +17 -5
  68. package/dist/lib/entries/infrastructure/queries/entry-match.query.d.ts +82 -0
  69. package/dist/lib/entries/infrastructure/queries/entry-match.query.d.ts.map +1 -0
  70. package/dist/lib/entries/infrastructure/queries/entry-match.query.js +152 -0
  71. package/dist/lib/extension/entry-filter-provider.d.ts +79 -0
  72. package/dist/lib/extension/entry-filter-provider.d.ts.map +1 -0
  73. package/dist/lib/extension/entry-filter-provider.js +149 -0
  74. package/dist/lib/extension/entry-write-extension.d.ts +185 -0
  75. package/dist/lib/extension/entry-write-extension.d.ts.map +1 -0
  76. package/dist/lib/extension/entry-write-extension.js +182 -0
  77. package/dist/lib/extension/read-scope.d.ts +107 -0
  78. package/dist/lib/extension/read-scope.d.ts.map +1 -0
  79. package/dist/lib/extension/read-scope.js +131 -0
  80. package/dist/lib/insights/docs/describe-content-insights-api.d.ts +23 -0
  81. package/dist/lib/insights/docs/describe-content-insights-api.d.ts.map +1 -0
  82. package/dist/lib/insights/docs/describe-content-insights-api.js +329 -0
  83. package/dist/lib/mcp/content-tools.provider.d.ts.map +1 -1
  84. package/dist/lib/mcp/content-tools.provider.js +10 -9
  85. package/dist/lib/public-api/http/controllers/public-entry-writes.controller.d.ts +14 -13
  86. package/dist/lib/public-api/http/controllers/public-entry-writes.controller.d.ts.map +1 -1
  87. package/dist/lib/public-api/http/controllers/public-entry-writes.controller.js +62 -46
  88. package/dist/lib/public-api/http/guards/api-token-workspace.guard.d.ts.map +1 -1
  89. package/dist/lib/public-api/http/guards/api-token-workspace.guard.js +8 -0
  90. package/dist/lib/public-api/infrastructure/public-entries.query.d.ts +14 -1
  91. package/dist/lib/public-api/infrastructure/public-entries.query.d.ts.map +1 -1
  92. package/dist/lib/public-api/infrastructure/public-entries.query.js +20 -3
  93. package/dist/lib/public-api/infrastructure/public-entry-writes.service.d.ts +10 -9
  94. package/dist/lib/public-api/infrastructure/public-entry-writes.service.d.ts.map +1 -1
  95. package/dist/lib/public-api/infrastructure/public-entry-writes.service.js +23 -24
  96. package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts +16 -4
  97. package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts.map +1 -1
  98. package/dist/lib/public-api/infrastructure/public-expansion.query.js +29 -9
  99. package/dist/lib/public-api/infrastructure/token-actor.d.ts +37 -0
  100. package/dist/lib/public-api/infrastructure/token-actor.d.ts.map +1 -0
  101. package/dist/lib/public-api/infrastructure/token-actor.js +58 -0
  102. package/dist/lib/revisions/application/use-cases/restore-revision.use-case.d.ts.map +1 -1
  103. package/dist/lib/revisions/application/use-cases/restore-revision.use-case.js +23 -1
  104. package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts +7 -1
  105. package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts.map +1 -1
  106. package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.js +9 -2
  107. package/dist/lib/revisions/infrastructure/purge/revisions-workspace.purger.d.ts +28 -0
  108. package/dist/lib/revisions/infrastructure/purge/revisions-workspace.purger.d.ts.map +1 -0
  109. package/dist/lib/revisions/infrastructure/purge/revisions-workspace.purger.js +52 -0
  110. package/dist/lib/revisions/types/revision-view.d.ts +11 -0
  111. package/dist/lib/revisions/types/revision-view.d.ts.map +1 -1
  112. package/dist/lib/utils/content-plugin.d.ts.map +1 -1
  113. package/dist/lib/utils/content-plugin.js +10 -1
  114. package/dist/lib/utils/content-views-plugin.d.ts +28 -0
  115. package/dist/lib/utils/content-views-plugin.d.ts.map +1 -0
  116. package/dist/lib/utils/content-views-plugin.js +37 -0
  117. package/dist/lib/views/application/queries/saved-views.query.d.ts +16 -0
  118. package/dist/lib/views/application/queries/saved-views.query.d.ts.map +1 -0
  119. package/dist/lib/views/application/queries/saved-views.query.js +46 -0
  120. package/dist/lib/views/application/saved-view-access.service.d.ts +30 -0
  121. package/dist/lib/views/application/saved-view-access.service.d.ts.map +1 -0
  122. package/dist/lib/views/application/saved-view-access.service.js +55 -0
  123. package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts +30 -0
  124. package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts.map +1 -0
  125. package/dist/lib/views/application/use-cases/create-saved-view.use-case.js +81 -0
  126. package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts +18 -0
  127. package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts.map +1 -0
  128. package/dist/lib/views/application/use-cases/delete-saved-view.use-case.js +56 -0
  129. package/dist/lib/views/application/use-cases/set-default-view.use-case.d.ts +23 -0
  130. package/dist/lib/views/application/use-cases/set-default-view.use-case.d.ts.map +1 -0
  131. package/dist/lib/views/application/use-cases/set-default-view.use-case.js +51 -0
  132. package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts +24 -0
  133. package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts.map +1 -0
  134. package/dist/lib/views/application/use-cases/update-saved-view.use-case.js +90 -0
  135. package/dist/lib/views/application/view-scope.service.d.ts +25 -0
  136. package/dist/lib/views/application/view-scope.service.d.ts.map +1 -0
  137. package/dist/lib/views/application/view-scope.service.js +45 -0
  138. package/dist/lib/views/content-views.module.d.ts +22 -0
  139. package/dist/lib/views/content-views.module.d.ts.map +1 -0
  140. package/dist/lib/views/content-views.module.js +63 -0
  141. package/dist/lib/views/domain/errors/index.d.ts +6 -0
  142. package/dist/lib/views/domain/errors/index.d.ts.map +1 -0
  143. package/dist/lib/views/domain/errors/index.js +12 -0
  144. package/dist/lib/views/domain/errors/saved-view-forbidden.error.d.ts +10 -0
  145. package/dist/lib/views/domain/errors/saved-view-forbidden.error.d.ts.map +1 -0
  146. package/dist/lib/views/domain/errors/saved-view-forbidden.error.js +16 -0
  147. package/dist/lib/views/domain/errors/saved-view-limit-error.d.ts +9 -0
  148. package/dist/lib/views/domain/errors/saved-view-limit-error.d.ts.map +1 -0
  149. package/dist/lib/views/domain/errors/saved-view-limit-error.js +16 -0
  150. package/dist/lib/views/domain/errors/saved-view-name-taken.error.d.ts +5 -0
  151. package/dist/lib/views/domain/errors/saved-view-name-taken.error.d.ts.map +1 -0
  152. package/dist/lib/views/domain/errors/saved-view-name-taken.error.js +11 -0
  153. package/dist/lib/views/domain/errors/saved-view-not-found.error.d.ts +9 -0
  154. package/dist/lib/views/domain/errors/saved-view-not-found.error.d.ts.map +1 -0
  155. package/dist/lib/views/domain/errors/saved-view-not-found.error.js +15 -0
  156. package/dist/lib/views/domain/events/saved-view-events.d.ts +28 -0
  157. package/dist/lib/views/domain/events/saved-view-events.d.ts.map +1 -0
  158. package/dist/lib/views/domain/events/saved-view-events.js +38 -0
  159. package/dist/lib/views/domain/saved-view.d.ts +66 -0
  160. package/dist/lib/views/domain/saved-view.d.ts.map +1 -0
  161. package/dist/lib/views/domain/saved-view.js +18 -0
  162. package/dist/lib/views/domain/saved-view.repository.d.ts +77 -0
  163. package/dist/lib/views/domain/saved-view.repository.d.ts.map +1 -0
  164. package/dist/lib/views/domain/saved-view.repository.js +5 -0
  165. package/dist/lib/views/http/controllers/saved-views.controller.d.ts +45 -0
  166. package/dist/lib/views/http/controllers/saved-views.controller.d.ts.map +1 -0
  167. package/dist/lib/views/http/controllers/saved-views.controller.js +216 -0
  168. package/dist/lib/views/http/controllers/to-http-error.d.ts +10 -0
  169. package/dist/lib/views/http/controllers/to-http-error.d.ts.map +1 -0
  170. package/dist/lib/views/http/controllers/to-http-error.js +26 -0
  171. package/dist/lib/views/http/dto/save-view.dto.d.ts +54 -0
  172. package/dist/lib/views/http/dto/save-view.dto.d.ts.map +1 -0
  173. package/dist/lib/views/http/dto/save-view.dto.js +230 -0
  174. package/dist/lib/views/http/dto/view-payload.validator.d.ts +20 -0
  175. package/dist/lib/views/http/dto/view-payload.validator.d.ts.map +1 -0
  176. package/dist/lib/views/http/dto/view-payload.validator.js +52 -0
  177. package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.d.ts +26 -0
  178. package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.d.ts.map +1 -0
  179. package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.js +135 -0
  180. package/dist/lib/views/infrastructure/schema/external-refs.d.ts +68 -0
  181. package/dist/lib/views/infrastructure/schema/external-refs.d.ts.map +1 -0
  182. package/dist/lib/views/infrastructure/schema/external-refs.js +29 -0
  183. package/dist/lib/views/infrastructure/schema/index.d.ts +8 -0
  184. package/dist/lib/views/infrastructure/schema/index.d.ts.map +1 -0
  185. package/dist/lib/views/infrastructure/schema/index.js +13 -0
  186. package/dist/lib/views/infrastructure/schema/saved-views.d.ts +275 -0
  187. package/dist/lib/views/infrastructure/schema/saved-views.d.ts.map +1 -0
  188. package/dist/lib/views/infrastructure/schema/saved-views.js +87 -0
  189. package/dist/lib/views/views.constants.d.ts +26 -0
  190. package/dist/lib/views/views.constants.d.ts.map +1 -0
  191. package/dist/lib/views/views.constants.js +28 -0
  192. package/dist/lib/views/views.tokens.d.ts +17 -0
  193. package/dist/lib/views/views.tokens.d.ts.map +1 -0
  194. package/dist/lib/views/views.tokens.js +21 -0
  195. package/migrations/0000_saved_views.sql +29 -0
  196. package/migrations/meta/0000_snapshot.json +256 -0
  197. package/migrations/meta/_journal.json +13 -0
  198. package/package.json +13 -12
  199. package/dist/lib/entries/controllers/bulk-entries.controller.d.ts +0 -26
  200. package/dist/lib/entries/controllers/bulk-entries.controller.d.ts.map +0 -1
  201. package/dist/lib/entries/controllers/create-entry.controller.d.ts +0 -20
  202. package/dist/lib/entries/controllers/create-entry.controller.d.ts.map +0 -1
  203. package/dist/lib/entries/controllers/delete-entry.controller.d.ts +0 -24
  204. package/dist/lib/entries/controllers/delete-entry.controller.d.ts.map +0 -1
  205. package/dist/lib/entries/controllers/get-entry.controller.d.ts +0 -34
  206. package/dist/lib/entries/controllers/get-entry.controller.d.ts.map +0 -1
  207. package/dist/lib/entries/controllers/list-entries.controller.d.ts +0 -20
  208. package/dist/lib/entries/controllers/list-entries.controller.d.ts.map +0 -1
  209. package/dist/lib/entries/controllers/publish-entry.controller.d.ts +0 -19
  210. package/dist/lib/entries/controllers/publish-entry.controller.d.ts.map +0 -1
  211. package/dist/lib/entries/controllers/resolve-type.d.ts +0 -9
  212. package/dist/lib/entries/controllers/resolve-type.d.ts.map +0 -1
  213. package/dist/lib/entries/controllers/update-entry.controller.d.ts +0 -18
  214. package/dist/lib/entries/controllers/update-entry.controller.d.ts.map +0 -1
  215. package/dist/lib/entries/dto/bulk-ids.dto.d.ts +0 -11
  216. package/dist/lib/entries/dto/bulk-ids.dto.d.ts.map +0 -1
  217. package/dist/lib/entries/dto/list-entries-query.dto.d.ts +0 -46
  218. package/dist/lib/entries/dto/list-entries-query.dto.d.ts.map +0 -1
  219. package/dist/lib/entries/dto/relation-delta-map.validator.d.ts +0 -19
  220. package/dist/lib/entries/dto/relation-delta-map.validator.d.ts.map +0 -1
  221. package/dist/lib/entries/dto/save-entry.dto.d.ts +0 -46
  222. package/dist/lib/entries/dto/save-entry.dto.d.ts.map +0 -1
  223. package/dist/lib/entries/infrastructure/persistence/field-selection.d.ts +0 -24
  224. package/dist/lib/entries/infrastructure/persistence/field-selection.d.ts.map +0 -1
  225. package/dist/lib/entries/infrastructure/queries/entry-filter-schema.d.ts +0 -40
  226. package/dist/lib/entries/infrastructure/queries/entry-filter-schema.d.ts.map +0 -1
  227. package/dist/lib/entries/services/entries.service.d.ts +0 -59
  228. package/dist/lib/entries/services/entries.service.d.ts.map +0 -1
  229. package/dist/lib/entries/services/entry-counter.service.d.ts +0 -31
  230. package/dist/lib/entries/services/entry-counter.service.d.ts.map +0 -1
  231. package/dist/lib/entries/services/entry-filter-schema.d.ts +0 -32
  232. package/dist/lib/entries/services/entry-filter-schema.d.ts.map +0 -1
  233. package/dist/lib/entries/services/entry-row.d.ts +0 -56
  234. package/dist/lib/entries/services/entry-row.d.ts.map +0 -1
  235. package/dist/lib/entries/services/entry-writer.service.d.ts +0 -193
  236. package/dist/lib/entries/services/entry-writer.service.d.ts.map +0 -1
  237. package/dist/lib/entries/services/relation-link.service.d.ts +0 -115
  238. package/dist/lib/entries/services/relation-link.service.d.ts.map +0 -1
  239. package/dist/lib/extension/per-locale-relation.d.ts +0 -18
  240. package/dist/lib/extension/per-locale-relation.d.ts.map +0 -1
  241. package/dist/lib/public/controllers/get-public-entry.controller.d.ts +0 -26
  242. package/dist/lib/public/controllers/get-public-entry.controller.d.ts.map +0 -1
  243. package/dist/lib/public/controllers/list-public-entries.controller.d.ts +0 -24
  244. package/dist/lib/public/controllers/list-public-entries.controller.d.ts.map +0 -1
  245. package/dist/lib/public/controllers/public-schema.controller.d.ts +0 -18
  246. package/dist/lib/public/controllers/public-schema.controller.d.ts.map +0 -1
@@ -0,0 +1,30 @@
1
+ import { AccessPolicy, PermissionsService, type PublicUser } from '@orthacms/identity-server';
2
+ import { type ViewVisibility } from '../domain/saved-view';
3
+ import type { SavedViewRecord } from '../domain/saved-view.repository';
4
+ /**
5
+ * The two authorization rules saved views add on top of the route guards.
6
+ *
7
+ * The guards already establish that the caller is authenticated, a member of
8
+ * the open workspace, and may read content. What is left is per-row and
9
+ * per-visibility, so it cannot live in a decorator:
10
+ *
11
+ * - **Only the owner writes.** Editing or deleting someone else's view is a
12
+ * 403 even for an administrator — a shared view is its author's, and the
13
+ * remedy for disagreeing with one is "Save as new", not a silent rewrite.
14
+ * - **Sharing is a permission.** Making a view workspace-visible needs
15
+ * `views:share`; a member without it still saves as many private views as
16
+ * they like.
17
+ */
18
+ export declare class SavedViewAccessService {
19
+ private readonly permissions;
20
+ private readonly accessPolicy;
21
+ constructor(permissions: PermissionsService, accessPolicy: AccessPolicy);
22
+ /** Throws unless `user` owns `view`. */
23
+ assertOwned(view: SavedViewRecord, user: PublicUser): void;
24
+ /**
25
+ * Throws unless `user` may store a view at `visibility`. Private views need
26
+ * nothing beyond membership; workspace-visible ones need `views:share`.
27
+ */
28
+ assertCanUseVisibility(visibility: ViewVisibility, user: PublicUser): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=saved-view-access.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saved-view-access.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/views/application/saved-view-access.service.ts"],"names":[],"mappings":"AACA,OAAO,EACH,YAAY,EAGZ,kBAAkB,EAClB,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAGvE;;;;;;;;;;;;;GAaG;AACH,qBACa,sBAAsB;IAE3B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY;gBADZ,WAAW,EAAE,kBAAkB,EAC/B,YAAY,EAAE,YAAY;IAG/C,wCAAwC;IACxC,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI;IAQ1D;;;OAGG;IACG,sBAAsB,CACxB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC;CAanB"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SavedViewAccessService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const identity_server_1 = require("@orthacms/identity-server");
7
+ const saved_view_1 = require("../domain/saved-view");
8
+ const errors_1 = require("../domain/errors");
9
+ /**
10
+ * The two authorization rules saved views add on top of the route guards.
11
+ *
12
+ * The guards already establish that the caller is authenticated, a member of
13
+ * the open workspace, and may read content. What is left is per-row and
14
+ * per-visibility, so it cannot live in a decorator:
15
+ *
16
+ * - **Only the owner writes.** Editing or deleting someone else's view is a
17
+ * 403 even for an administrator — a shared view is its author's, and the
18
+ * remedy for disagreeing with one is "Save as new", not a silent rewrite.
19
+ * - **Sharing is a permission.** Making a view workspace-visible needs
20
+ * `views:share`; a member without it still saves as many private views as
21
+ * they like.
22
+ */
23
+ let SavedViewAccessService = class SavedViewAccessService {
24
+ permissions;
25
+ accessPolicy;
26
+ constructor(permissions, accessPolicy) {
27
+ this.permissions = permissions;
28
+ this.accessPolicy = accessPolicy;
29
+ }
30
+ /** Throws unless `user` owns `view`. */
31
+ assertOwned(view, user) {
32
+ if (view.ownerId !== user.id) {
33
+ throw new errors_1.SavedViewForbiddenError('Only the person who saved a view can change it. Use “Save as new” instead.');
34
+ }
35
+ }
36
+ /**
37
+ * Throws unless `user` may store a view at `visibility`. Private views need
38
+ * nothing beyond membership; workspace-visible ones need `views:share`.
39
+ */
40
+ async assertCanUseVisibility(visibility, user) {
41
+ if (visibility !== saved_view_1.VIEW_VISIBILITY.Workspace)
42
+ return;
43
+ const granted = await this.permissions.forRole(user.roleId);
44
+ const allowed = this.accessPolicy.can({ userId: user.id, grantedPermissions: new Set(granted) }, identity_server_1.Permission.create(identity_server_1.PERMISSIONS.VIEWS_SHARE));
45
+ if (!allowed) {
46
+ throw new errors_1.SavedViewForbiddenError('Sharing a view with the workspace requires the “views:share” permission.');
47
+ }
48
+ }
49
+ };
50
+ exports.SavedViewAccessService = SavedViewAccessService;
51
+ exports.SavedViewAccessService = SavedViewAccessService = tslib_1.__decorate([
52
+ (0, common_1.Injectable)(),
53
+ tslib_1.__metadata("design:paramtypes", [identity_server_1.PermissionsService,
54
+ identity_server_1.AccessPolicy])
55
+ ], SavedViewAccessService);
@@ -0,0 +1,30 @@
1
+ import type { PublicUser } from '@orthacms/identity-server';
2
+ import { OutboxWriter, UnitOfWork } from '@orthacms/database';
3
+ import type { SavedView, SavedViewPayload, ViewVisibility } from '../../domain/saved-view';
4
+ import { type SavedViewRepository } from '../../domain/saved-view.repository';
5
+ import { SavedViewAccessService } from '../saved-view-access.service';
6
+ /** Input for {@link CreateSavedViewUseCase}. */
7
+ export interface CreateSavedViewInput {
8
+ /** The workspace the view belongs to. */
9
+ workspaceId: string;
10
+ /** The list it is over (`content:<typeName>`). */
11
+ scope: string;
12
+ /** Display name, already trimmed by the DTO. */
13
+ name: string;
14
+ /** Who may see it. */
15
+ visibility: ViewVisibility;
16
+ /** The slice it restores. */
17
+ payload: SavedViewPayload;
18
+ /** Whether to make it the caller's default for this scope. */
19
+ makeDefault?: boolean;
20
+ }
21
+ /** Saves a new view for the calling user. */
22
+ export declare class CreateSavedViewUseCase {
23
+ private readonly repository;
24
+ private readonly access;
25
+ private readonly uow;
26
+ private readonly outbox;
27
+ constructor(repository: SavedViewRepository, access: SavedViewAccessService, uow: UnitOfWork, outbox: OutboxWriter);
28
+ execute(input: CreateSavedViewInput, user: PublicUser): Promise<SavedView>;
29
+ }
30
+ //# sourceMappingURL=create-saved-view.use-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-saved-view.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/application/use-cases/create-saved-view.use-case.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAEH,YAAY,EACZ,UAAU,EACb,MAAM,oBAAoB,CAAC;AAM5B,OAAO,KAAK,EACR,SAAS,EACT,gBAAgB,EAChB,cAAc,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEH,KAAK,mBAAmB,EAC3B,MAAM,oCAAoC,CAAC;AAM5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAGtE,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACjC,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,UAAU,EAAE,cAAc,CAAC;IAC3B,6BAA6B;IAC7B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,6CAA6C;AAC7C,qBACa,sBAAsB;IAG3B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAHN,UAAU,EAAE,mBAAmB,EAC/B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY;IAGnC,OAAO,CACT,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC;CAiExB"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CreateSavedViewUseCase = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const database_1 = require("@orthacms/database");
7
+ const saved_view_events_1 = require("../../domain/events/saved-view-events");
8
+ const utils_server_1 = require("@orthacms/utils-server");
9
+ const saved_view_repository_1 = require("../../domain/saved-view.repository");
10
+ const errors_1 = require("../../domain/errors");
11
+ const views_constants_1 = require("../../views.constants");
12
+ const saved_view_access_service_1 = require("../saved-view-access.service");
13
+ const saved_views_query_1 = require("../queries/saved-views.query");
14
+ /** Saves a new view for the calling user. */
15
+ let CreateSavedViewUseCase = class CreateSavedViewUseCase {
16
+ repository;
17
+ access;
18
+ uow;
19
+ outbox;
20
+ constructor(repository, access, uow, outbox) {
21
+ this.repository = repository;
22
+ this.access = access;
23
+ this.uow = uow;
24
+ this.outbox = outbox;
25
+ }
26
+ async execute(input, user) {
27
+ await this.access.assertCanUseVisibility(input.visibility, user);
28
+ const held = await this.repository.countForOwner(input.workspaceId, input.scope, user.id);
29
+ if (held >= views_constants_1.VIEW_MAX_PER_SCOPE)
30
+ throw new errors_1.SavedViewLimitError();
31
+ // The unique index is the arbiter, not the count above: two concurrent
32
+ // saves of the same name both pass a read-then-write check and one of
33
+ // them has to lose at the constraint. Translate that loss into the same
34
+ // 409 a sequential duplicate gets.
35
+ let created;
36
+ try {
37
+ created = await this.uow.run(async () => {
38
+ const row = await this.repository.create({
39
+ workspaceId: input.workspaceId,
40
+ scope: input.scope,
41
+ ownerId: user.id,
42
+ visibility: input.visibility,
43
+ name: input.name,
44
+ payload: input.payload
45
+ });
46
+ await this.outbox.append((0, database_1.attachActor)([
47
+ (0, saved_view_events_1.savedViewEvent)(saved_view_events_1.SAVED_VIEW_EVENT_KINDS.CREATED, row.id, {
48
+ workspaceId: input.workspaceId,
49
+ scope: input.scope,
50
+ name: input.name,
51
+ // The half that makes the row worth
52
+ // keeping: a `workspace` view is shared
53
+ // state everyone in the switcher sees.
54
+ visibility: input.visibility
55
+ })
56
+ ], { id: user.id, email: user.email ?? null }));
57
+ return row;
58
+ });
59
+ }
60
+ catch (error) {
61
+ if ((0, utils_server_1.isUniqueViolation)(error)) {
62
+ throw new errors_1.SavedViewNameTakenError(input.name);
63
+ }
64
+ throw error;
65
+ }
66
+ if (input.makeDefault) {
67
+ // Outside the run on purpose: a personal default raises no event
68
+ // (see `saved-view-events.ts`), so it has nothing to commit with.
69
+ await this.repository.setDefault(user.id, input.scope, created.id);
70
+ }
71
+ return (0, saved_views_query_1.toSavedView)(created, user.id, input.makeDefault ? created.id : null);
72
+ }
73
+ };
74
+ exports.CreateSavedViewUseCase = CreateSavedViewUseCase;
75
+ exports.CreateSavedViewUseCase = CreateSavedViewUseCase = tslib_1.__decorate([
76
+ (0, common_1.Injectable)(),
77
+ tslib_1.__param(0, (0, common_1.Inject)(saved_view_repository_1.SAVED_VIEW_REPOSITORY)),
78
+ tslib_1.__metadata("design:paramtypes", [Object, saved_view_access_service_1.SavedViewAccessService,
79
+ database_1.UnitOfWork,
80
+ database_1.OutboxWriter])
81
+ ], CreateSavedViewUseCase);
@@ -0,0 +1,18 @@
1
+ import type { PublicUser } from '@orthacms/identity-server';
2
+ import { OutboxWriter, UnitOfWork } from '@orthacms/database';
3
+ import { type SavedViewRepository } from '../../domain/saved-view.repository';
4
+ import { SavedViewAccessService } from '../saved-view-access.service';
5
+ /**
6
+ * Deletes a view the caller owns. Anyone else's default pointing at it is
7
+ * cascaded away by the FK, so they fall back to the unfiltered list rather than
8
+ * to a dangling id.
9
+ */
10
+ export declare class DeleteSavedViewUseCase {
11
+ private readonly repository;
12
+ private readonly access;
13
+ private readonly uow;
14
+ private readonly outbox;
15
+ constructor(repository: SavedViewRepository, access: SavedViewAccessService, uow: UnitOfWork, outbox: OutboxWriter);
16
+ execute(id: string, workspaceId: string, user: PublicUser): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=delete-saved-view.use-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-saved-view.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/application/use-cases/delete-saved-view.use-case.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAEH,YAAY,EACZ,UAAU,EACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAEH,KAAK,mBAAmB,EAC3B,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAEtE;;;;GAIG;AACH,qBACa,sBAAsB;IAG3B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAHN,UAAU,EAAE,mBAAmB,EAC/B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY;IAGnC,OAAO,CACT,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC;CA0BnB"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeleteSavedViewUseCase = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const database_1 = require("@orthacms/database");
7
+ const saved_view_events_1 = require("../../domain/events/saved-view-events");
8
+ const saved_view_repository_1 = require("../../domain/saved-view.repository");
9
+ const errors_1 = require("../../domain/errors");
10
+ const saved_view_access_service_1 = require("../saved-view-access.service");
11
+ /**
12
+ * Deletes a view the caller owns. Anyone else's default pointing at it is
13
+ * cascaded away by the FK, so they fall back to the unfiltered list rather than
14
+ * to a dangling id.
15
+ */
16
+ let DeleteSavedViewUseCase = class DeleteSavedViewUseCase {
17
+ repository;
18
+ access;
19
+ uow;
20
+ outbox;
21
+ constructor(repository, access, uow, outbox) {
22
+ this.repository = repository;
23
+ this.access = access;
24
+ this.uow = uow;
25
+ this.outbox = outbox;
26
+ }
27
+ async execute(id, workspaceId, user) {
28
+ const existing = await this.repository.findById(id);
29
+ if (!existing || existing.workspaceId !== workspaceId) {
30
+ throw new errors_1.SavedViewNotFoundError(id);
31
+ }
32
+ this.access.assertOwned(existing, user);
33
+ await this.uow.run(async () => {
34
+ await this.repository.delete(id);
35
+ // The view's own details, because this is the last place they
36
+ // exist — and a shared view leaving everyone's switcher is a
37
+ // change to what a team can see, not just to one person's list.
38
+ await this.outbox.append((0, database_1.attachActor)([
39
+ (0, saved_view_events_1.savedViewEvent)(saved_view_events_1.SAVED_VIEW_EVENT_KINDS.DELETED, id, {
40
+ workspaceId,
41
+ scope: existing.scope,
42
+ name: existing.name,
43
+ visibility: existing.visibility
44
+ })
45
+ ], { id: user.id, email: user.email ?? null }));
46
+ });
47
+ }
48
+ };
49
+ exports.DeleteSavedViewUseCase = DeleteSavedViewUseCase;
50
+ exports.DeleteSavedViewUseCase = DeleteSavedViewUseCase = tslib_1.__decorate([
51
+ (0, common_1.Injectable)(),
52
+ tslib_1.__param(0, (0, common_1.Inject)(saved_view_repository_1.SAVED_VIEW_REPOSITORY)),
53
+ tslib_1.__metadata("design:paramtypes", [Object, saved_view_access_service_1.SavedViewAccessService,
54
+ database_1.UnitOfWork,
55
+ database_1.OutboxWriter])
56
+ ], DeleteSavedViewUseCase);
@@ -0,0 +1,23 @@
1
+ import type { PublicUser } from '@orthacms/identity-server';
2
+ import { type SavedViewRepository } from '../../domain/saved-view.repository';
3
+ /**
4
+ * Points a user's default for one list at a view, or clears it.
5
+ *
6
+ * Deliberately **not** owner-gated: a default is the reader's own landing
7
+ * choice, so anyone who can see a shared view may land on it. What is gated is
8
+ * visibility — the view has to be one this caller can actually read, which is
9
+ * what the ownership-or-shared check below establishes.
10
+ */
11
+ export declare class SetDefaultViewUseCase {
12
+ private readonly repository;
13
+ constructor(repository: SavedViewRepository);
14
+ execute(id: string, workspaceId: string, user: PublicUser): Promise<void>;
15
+ /**
16
+ * Clears the caller's default for the scope `id` belongs to. Addressed by
17
+ * view id rather than by scope so the route sits beside its `PUT`
18
+ * counterpart instead of colliding with `DELETE /views/:id`. Idempotent:
19
+ * clearing when the default points elsewhere, or nowhere, still succeeds.
20
+ */
21
+ executeClear(id: string, workspaceId: string, user: PublicUser): Promise<void>;
22
+ }
23
+ //# sourceMappingURL=set-default-view.use-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-default-view.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/application/use-cases/set-default-view.use-case.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAEH,KAAK,mBAAmB,EAC3B,MAAM,oCAAoC,CAAC;AAG5C;;;;;;;GAOG;AACH,qBACa,qBAAqB;IAG1B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,mBAAmB;IAG9C,OAAO,CACT,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;;OAKG;IACG,YAAY,CACd,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC;CAOnB"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SetDefaultViewUseCase = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const saved_view_1 = require("../../domain/saved-view");
7
+ const saved_view_repository_1 = require("../../domain/saved-view.repository");
8
+ const errors_1 = require("../../domain/errors");
9
+ /**
10
+ * Points a user's default for one list at a view, or clears it.
11
+ *
12
+ * Deliberately **not** owner-gated: a default is the reader's own landing
13
+ * choice, so anyone who can see a shared view may land on it. What is gated is
14
+ * visibility — the view has to be one this caller can actually read, which is
15
+ * what the ownership-or-shared check below establishes.
16
+ */
17
+ let SetDefaultViewUseCase = class SetDefaultViewUseCase {
18
+ repository;
19
+ constructor(repository) {
20
+ this.repository = repository;
21
+ }
22
+ async execute(id, workspaceId, user) {
23
+ const existing = await this.repository.findById(id);
24
+ const readable = existing &&
25
+ existing.workspaceId === workspaceId &&
26
+ (existing.ownerId === user.id ||
27
+ existing.visibility === saved_view_1.VIEW_VISIBILITY.Workspace);
28
+ if (!existing || !readable)
29
+ throw new errors_1.SavedViewNotFoundError(id);
30
+ await this.repository.setDefault(user.id, existing.scope, id);
31
+ }
32
+ /**
33
+ * Clears the caller's default for the scope `id` belongs to. Addressed by
34
+ * view id rather than by scope so the route sits beside its `PUT`
35
+ * counterpart instead of colliding with `DELETE /views/:id`. Idempotent:
36
+ * clearing when the default points elsewhere, or nowhere, still succeeds.
37
+ */
38
+ async executeClear(id, workspaceId, user) {
39
+ const existing = await this.repository.findById(id);
40
+ if (!existing || existing.workspaceId !== workspaceId) {
41
+ throw new errors_1.SavedViewNotFoundError(id);
42
+ }
43
+ await this.repository.clearDefault(user.id, existing.scope);
44
+ }
45
+ };
46
+ exports.SetDefaultViewUseCase = SetDefaultViewUseCase;
47
+ exports.SetDefaultViewUseCase = SetDefaultViewUseCase = tslib_1.__decorate([
48
+ (0, common_1.Injectable)(),
49
+ tslib_1.__param(0, (0, common_1.Inject)(saved_view_repository_1.SAVED_VIEW_REPOSITORY)),
50
+ tslib_1.__metadata("design:paramtypes", [Object])
51
+ ], SetDefaultViewUseCase);
@@ -0,0 +1,24 @@
1
+ import type { PublicUser } from '@orthacms/identity-server';
2
+ import { OutboxWriter, UnitOfWork } from '@orthacms/database';
3
+ import type { SavedView, SavedViewPayload, ViewVisibility } from '../../domain/saved-view';
4
+ import { type SavedViewRepository } from '../../domain/saved-view.repository';
5
+ import { SavedViewAccessService } from '../saved-view-access.service';
6
+ /** The fields `PATCH /api/views/:id` may change. */
7
+ export interface UpdateSavedViewInput {
8
+ /** New display name. */
9
+ name?: string;
10
+ /** New visibility. */
11
+ visibility?: ViewVisibility;
12
+ /** New slice. */
13
+ payload?: SavedViewPayload;
14
+ }
15
+ /** Renames, re-shares, or re-captures an existing view. */
16
+ export declare class UpdateSavedViewUseCase {
17
+ private readonly repository;
18
+ private readonly access;
19
+ private readonly uow;
20
+ private readonly outbox;
21
+ constructor(repository: SavedViewRepository, access: SavedViewAccessService, uow: UnitOfWork, outbox: OutboxWriter);
22
+ execute(id: string, workspaceId: string, input: UpdateSavedViewInput, user: PublicUser): Promise<SavedView>;
23
+ }
24
+ //# sourceMappingURL=update-saved-view.use-case.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-saved-view.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/application/use-cases/update-saved-view.use-case.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAEH,YAAY,EACZ,UAAU,EACb,MAAM,oBAAoB,CAAC;AAM5B,OAAO,KAAK,EACR,SAAS,EACT,gBAAgB,EAChB,cAAc,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEH,KAAK,mBAAmB,EAC3B,MAAM,oCAAoC,CAAC;AAK5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAGtE,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACjC,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,iBAAiB;IACjB,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,2DAA2D;AAC3D,qBACa,sBAAsB;IAG3B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAHN,UAAU,EAAE,mBAAmB,EAC/B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY;IAGnC,OAAO,CACT,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,CAAC;CA0ExB"}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UpdateSavedViewUseCase = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const database_1 = require("@orthacms/database");
7
+ const saved_view_events_1 = require("../../domain/events/saved-view-events");
8
+ const utils_server_1 = require("@orthacms/utils-server");
9
+ const saved_view_repository_1 = require("../../domain/saved-view.repository");
10
+ const errors_1 = require("../../domain/errors");
11
+ const saved_view_access_service_1 = require("../saved-view-access.service");
12
+ const saved_views_query_1 = require("../queries/saved-views.query");
13
+ /** Renames, re-shares, or re-captures an existing view. */
14
+ let UpdateSavedViewUseCase = class UpdateSavedViewUseCase {
15
+ repository;
16
+ access;
17
+ uow;
18
+ outbox;
19
+ constructor(repository, access, uow, outbox) {
20
+ this.repository = repository;
21
+ this.access = access;
22
+ this.uow = uow;
23
+ this.outbox = outbox;
24
+ }
25
+ async execute(id, workspaceId, input, user) {
26
+ const existing = await this.repository.findById(id);
27
+ // The workspace check is part of the not-found decision, not a separate
28
+ // 403: an id from another workspace must read exactly like an unknown
29
+ // one, or the endpoint becomes a probe for views elsewhere.
30
+ if (!existing || existing.workspaceId !== workspaceId) {
31
+ throw new errors_1.SavedViewNotFoundError(id);
32
+ }
33
+ this.access.assertOwned(existing, user);
34
+ if (input.visibility !== undefined) {
35
+ await this.access.assertCanUseVisibility(input.visibility, user);
36
+ }
37
+ // Every field is optional, so `{}` is a valid body — and it would reach
38
+ // Drizzle as `.set({})`, which throws rather than updating nothing.
39
+ // Answer the row unchanged instead: a patch that asks for no change has
40
+ // already got what it asked for.
41
+ const changes = input.name !== undefined ||
42
+ input.visibility !== undefined ||
43
+ input.payload !== undefined;
44
+ if (!changes) {
45
+ const currentDefault = await this.repository.findDefaultId(user.id, existing.scope);
46
+ return (0, saved_views_query_1.toSavedView)(existing, user.id, currentDefault);
47
+ }
48
+ let updated;
49
+ try {
50
+ updated = await this.uow.run(async () => {
51
+ const row = await this.repository.update(id, input);
52
+ await this.outbox.append((0, database_1.attachActor)([
53
+ (0, saved_view_events_1.savedViewEvent)(saved_view_events_1.SAVED_VIEW_EVENT_KINDS.UPDATED, id, {
54
+ workspaceId,
55
+ scope: existing.scope,
56
+ name: row.name,
57
+ // The keys the caller actually sent — a
58
+ // patch, so "what changed" is the request.
59
+ fields: Object.keys(input),
60
+ // Called out on its own: sharing is a
61
+ // permission of its own, and unsharing
62
+ // takes a view out of every member's
63
+ // switcher without telling them.
64
+ visibility: {
65
+ from: existing.visibility,
66
+ to: row.visibility
67
+ }
68
+ })
69
+ ], { id: user.id, email: user.email ?? null }));
70
+ return row;
71
+ });
72
+ }
73
+ catch (error) {
74
+ if ((0, utils_server_1.isUniqueViolation)(error)) {
75
+ throw new errors_1.SavedViewNameTakenError(input.name ?? existing.name);
76
+ }
77
+ throw error;
78
+ }
79
+ const defaultId = await this.repository.findDefaultId(user.id, updated.scope);
80
+ return (0, saved_views_query_1.toSavedView)(updated, user.id, defaultId);
81
+ }
82
+ };
83
+ exports.UpdateSavedViewUseCase = UpdateSavedViewUseCase;
84
+ exports.UpdateSavedViewUseCase = UpdateSavedViewUseCase = tslib_1.__decorate([
85
+ (0, common_1.Injectable)(),
86
+ tslib_1.__param(0, (0, common_1.Inject)(saved_view_repository_1.SAVED_VIEW_REPOSITORY)),
87
+ tslib_1.__metadata("design:paramtypes", [Object, saved_view_access_service_1.SavedViewAccessService,
88
+ database_1.UnitOfWork,
89
+ database_1.OutboxWriter])
90
+ ], UpdateSavedViewUseCase);
@@ -0,0 +1,25 @@
1
+ import type { ContentTypeRegistry } from '../../registry/content-type-registry';
2
+ import { WorkspaceGrantsQuery } from '../../content-types/queries/workspace-grants.query';
3
+ /**
4
+ * Resolves a view's `scope` to a content type the open workspace may actually
5
+ * reach.
6
+ *
7
+ * Without this the views endpoint would be a way around
8
+ * {@link ContentGrantGuard}: `scope` names a content type, and an unchecked one
9
+ * lets a caller confirm that `content:salaries` exists in some workspace by
10
+ * saving a view over it. So the answer for an unknown type and for an ungranted
11
+ * one is the **same 404** the entries routes give — grants read before any
12
+ * registry decision, same status, same message.
13
+ */
14
+ export declare class ViewScopeService {
15
+ private readonly registry;
16
+ private readonly grants;
17
+ constructor(registry: ContentTypeRegistry, grants: WorkspaceGrantsQuery);
18
+ /**
19
+ * Throws `NotFoundException` unless `scope` names a content type this
20
+ * workspace was granted. The DTO has already enforced the
21
+ * `content:<typeName>` shape.
22
+ */
23
+ assertReachable(scope: string, workspaceId: string): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=view-scope.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-scope.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/views/application/view-scope.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,oDAAoD,CAAC;AAG1F;;;;;;;;;;GAUG;AACH,qBACa,gBAAgB;IAGrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,QAAQ,EAAE,mBAAmB,EAC7B,MAAM,EAAE,oBAAoB;IAGjD;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO3E"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ViewScopeService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const views_tokens_1 = require("../views.tokens");
7
+ const workspace_grants_query_1 = require("../../content-types/queries/workspace-grants.query");
8
+ const views_constants_1 = require("../views.constants");
9
+ /**
10
+ * Resolves a view's `scope` to a content type the open workspace may actually
11
+ * reach.
12
+ *
13
+ * Without this the views endpoint would be a way around
14
+ * {@link ContentGrantGuard}: `scope` names a content type, and an unchecked one
15
+ * lets a caller confirm that `content:salaries` exists in some workspace by
16
+ * saving a view over it. So the answer for an unknown type and for an ungranted
17
+ * one is the **same 404** the entries routes give — grants read before any
18
+ * registry decision, same status, same message.
19
+ */
20
+ let ViewScopeService = class ViewScopeService {
21
+ registry;
22
+ grants;
23
+ constructor(registry, grants) {
24
+ this.registry = registry;
25
+ this.grants = grants;
26
+ }
27
+ /**
28
+ * Throws `NotFoundException` unless `scope` names a content type this
29
+ * workspace was granted. The DTO has already enforced the
30
+ * `content:<typeName>` shape.
31
+ */
32
+ async assertReachable(scope, workspaceId) {
33
+ const typeName = scope.slice(views_constants_1.CONTENT_SCOPE_PREFIX.length);
34
+ const granted = await this.grants.grantedSlugs(workspaceId);
35
+ if (!granted.has(typeName) || !this.registry.get(typeName)) {
36
+ throw new common_1.NotFoundException(`Unknown content type "${typeName}".`);
37
+ }
38
+ }
39
+ };
40
+ exports.ViewScopeService = ViewScopeService;
41
+ exports.ViewScopeService = ViewScopeService = tslib_1.__decorate([
42
+ (0, common_1.Injectable)(),
43
+ tslib_1.__param(0, (0, views_tokens_1.InjectViewContentRegistry)()),
44
+ tslib_1.__metadata("design:paramtypes", [Function, workspace_grants_query_1.WorkspaceGrantsQuery])
45
+ ], ViewScopeService);
@@ -0,0 +1,22 @@
1
+ import { type DynamicModule } from '@nestjs/common';
2
+ import type { ContentTypeRegistry } from '../registry/content-type-registry';
3
+ /**
4
+ * The saved-views module.
5
+ *
6
+ * A **second** module from this package rather than a branch of
7
+ * `ContentModule`, because it needs its own `ServerPlugin` entry — the contract
8
+ * carries exactly one `migrations` descriptor per plugin, and content's is
9
+ * already spent on the host-owned generated collection tables. Splitting the
10
+ * module keeps that seam honest: the views feature owns fixed tables it ships
11
+ * migrations for, and the content model's tables stay the host's.
12
+ *
13
+ * It provides the registry the plugin factory hands it under its **own** token
14
+ * (`VIEW_CONTENT_REGISTRY`), so `ViewScopeService` can resolve a scope without
15
+ * importing `ContentModule` — which would make the two plugins' load order
16
+ * load-bearing — and without two global modules both claiming
17
+ * `CONTENT_REGISTRY`.
18
+ */
19
+ export declare class ContentViewsModule {
20
+ static forRoot(registry: ContentTypeRegistry): DynamicModule;
21
+ }
22
+ //# sourceMappingURL=content-views.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-views.module.d.ts","sourceRoot":"","sources":["../../../src/lib/views/content-views.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAa7E;;;;;;;;;;;;;;;GAeG;AACH,qBACa,kBAAkB;IAC3B,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,aAAa;CAuB/D"}