@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
@@ -1,6 +1,7 @@
1
1
  import { type Database } from '@orthacms/database';
2
2
  import { type AnyContentType } from '../../../types/content-type';
3
3
  import { type AnyFieldSpec } from '../../../types/fields';
4
+ import { ContentReadScopeRegistry } from '../../../extension/read-scope';
4
5
  import type { RelationDelta, RelationFieldView, RelationRef } from '../../types/entry-list-view';
5
6
  /**
6
7
  * Reject any target row that lives in a different locale from the source.
@@ -35,7 +36,16 @@ export declare const RELATION_PAGE_SIZE = 20;
35
36
  * links to drafts and needs to see them.
36
37
  */
37
38
  export interface RelationTargetVisibility {
38
- /** Restrict targets to entries that are currently published. */
39
+ /**
40
+ * Restrict targets to entries that are currently published.
41
+ *
42
+ * It also turns on the bound **read scopes** — segments' reader
43
+ * entitlements — against the target type. The two ride one flag because
44
+ * they answer one question ("may this caller reach this target?") and are
45
+ * needed at exactly the same call sites: every public read passes it, and
46
+ * no admin read does. A second flag would be a second thing to remember on
47
+ * a path where forgetting it means counting links a reader cannot follow.
48
+ */
39
49
  publishedOnly?: boolean;
40
50
  }
41
51
  /**
@@ -65,7 +75,8 @@ export type QueryRunner = Pick<Database, 'select'>;
65
75
  */
66
76
  export declare class RelationLinkService {
67
77
  private readonly db;
68
- constructor(db: Database);
78
+ private readonly readScopes?;
79
+ constructor(db: Database, readScopes?: ContentReadScopeRegistry | undefined);
69
80
  /**
70
81
  * First page (+ total) of **every** relation field of `type` for one entry,
71
82
  * keyed by field name — what the editor loads on open. Each field is read
@@ -208,11 +219,33 @@ export declare class RelationLinkService {
208
219
  /**
209
220
  * The visibility predicate applied to a relation's target rows: the
210
221
  * workspace scope, the soft-delete guard, and — only when the caller asks
211
- * for it — the published-only restriction. One definition, so the row read
212
- * (`refsFor`) and the windowed link reads that must *count* correctly can't
213
- * disagree on what a visible target is.
222
+ * for it — the published-only restriction plus the bound read scopes.
223
+ *
224
+ * **One definition**, and that is the load-bearing part. Every path that
225
+ * reads a target goes through it: the row read (`refsFor`), the windowed
226
+ * link reads whose `count(*) over` must not over-report, and the sub-select
227
+ * the join-table reads restrict their window by. So `items` and `total`
228
+ * cannot disagree about what a visible target is — which is exactly what
229
+ * they did while the read scopes were applied only when *hydrating* the
230
+ * items: a reader denied three of five linked records saw two items under a
231
+ * `total` of five, and could infer three restricted records existed.
214
232
  */
215
233
  private targetVisibleWhere;
234
+ /**
235
+ * The bound read scopes' fragments for a relation's **target** type, on a
236
+ * public read.
237
+ *
238
+ * Asked about the target rather than the type the request named: a reader
239
+ * allowed to see an article is not thereby allowed to see everything it
240
+ * points at, and a scope keyed to the source would leave every target
241
+ * unguarded.
242
+ *
243
+ * Gated on `publishedOnly`, which is the flag that marks a public read. The
244
+ * admin's calls pass no visibility and are untouched — an editor must see
245
+ * the records their entry links to in order to manage them, including ones
246
+ * no reader may fetch.
247
+ */
248
+ private readScopeWhere;
216
249
  /**
217
250
  * The id sub-select of a target's visible rows — how a link read that reads
218
251
  * only the JOIN table (so has no target columns of its own) restricts to
@@ -1 +1 @@
1
- {"version":3,"file":"relation-link.service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/infrastructure/persistence/relation-link.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relation-link.service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/infrastructure/persistence/relation-link.service.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ const drizzle_orm_1 = require("drizzle-orm");
8
8
  const database_1 = require("@orthacms/database");
9
9
  const content_type_1 = require("../../../types/content-type");
10
10
  const fields_1 = require("../../../types/fields");
11
+ const read_scope_1 = require("../../../extension/read-scope");
11
12
  const entry_row_1 = require("./entry-row");
12
13
  /**
13
14
  * Reject any target row that lives in a different locale from the source.
@@ -76,8 +77,10 @@ const RELATION_APPEND_LOCK_CLASS = 0x524c; // 'RL'
76
77
  */
77
78
  let RelationLinkService = class RelationLinkService {
78
79
  db;
79
- constructor(db) {
80
+ readScopes;
81
+ constructor(db, readScopes) {
80
82
  this.db = db;
83
+ this.readScopes = readScopes;
81
84
  }
82
85
  // ---- reads -------------------------------------------------------------
83
86
  /**
@@ -644,15 +647,41 @@ let RelationLinkService = class RelationLinkService {
644
647
  /**
645
648
  * The visibility predicate applied to a relation's target rows: the
646
649
  * workspace scope, the soft-delete guard, and — only when the caller asks
647
- * for it — the published-only restriction. One definition, so the row read
648
- * (`refsFor`) and the windowed link reads that must *count* correctly can't
649
- * disagree on what a visible target is.
650
+ * for it — the published-only restriction plus the bound read scopes.
651
+ *
652
+ * **One definition**, and that is the load-bearing part. Every path that
653
+ * reads a target goes through it: the row read (`refsFor`), the windowed
654
+ * link reads whose `count(*) over` must not over-report, and the sub-select
655
+ * the join-table reads restrict their window by. So `items` and `total`
656
+ * cannot disagree about what a visible target is — which is exactly what
657
+ * they did while the read scopes were applied only when *hydrating* the
658
+ * items: a reader denied three of five linked records saw two items under a
659
+ * `total` of five, and could infer three restricted records existed.
650
660
  */
651
661
  targetVisibleWhere(target, workspaceId, visibility) {
652
662
  const cols = target.table;
653
663
  return (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(cols['workspaceId'], workspaceId), cols['deletedAt'] ? (0, drizzle_orm_1.isNull)(cols['deletedAt']) : undefined, visibility?.publishedOnly && target.publishable
654
664
  ? (0, drizzle_orm_1.eq)(cols['status'], content_type_1.ENTRY_STATUS.Published)
655
- : undefined);
665
+ : undefined, ...this.readScopeWhere(target, workspaceId, visibility));
666
+ }
667
+ /**
668
+ * The bound read scopes' fragments for a relation's **target** type, on a
669
+ * public read.
670
+ *
671
+ * Asked about the target rather than the type the request named: a reader
672
+ * allowed to see an article is not thereby allowed to see everything it
673
+ * points at, and a scope keyed to the source would leave every target
674
+ * unguarded.
675
+ *
676
+ * Gated on `publishedOnly`, which is the flag that marks a public read. The
677
+ * admin's calls pass no visibility and are untouched — an editor must see
678
+ * the records their entry links to in order to manage them, including ones
679
+ * no reader may fetch.
680
+ */
681
+ readScopeWhere(target, workspaceId, visibility) {
682
+ if (!visibility?.publishedOnly)
683
+ return [];
684
+ return this.readScopes?.fragments({ type: target, workspaceId }) ?? [];
656
685
  }
657
686
  /**
658
687
  * The id sub-select of a target's visible rows — how a link read that reads
@@ -661,6 +690,8 @@ let RelationLinkService = class RelationLinkService {
661
690
  * when no extra restriction applies, so the predicate collapses away.
662
691
  */
663
692
  visibleTargetIds(target, workspaceId, visibility) {
693
+ // `publishedOnly` is the public-read marker, so it also gates the read
694
+ // scopes (see {@link targetVisibleWhere}) — one check covers both.
664
695
  if (!visibility?.publishedOnly)
665
696
  return undefined;
666
697
  const cols = target.table;
@@ -981,7 +1012,8 @@ exports.RelationLinkService = RelationLinkService;
981
1012
  exports.RelationLinkService = RelationLinkService = tslib_1.__decorate([
982
1013
  (0, common_1.Injectable)(),
983
1014
  tslib_1.__param(0, (0, database_1.InjectDatabase)()),
984
- tslib_1.__metadata("design:paramtypes", [Object])
1015
+ tslib_1.__param(1, (0, common_1.Optional)()),
1016
+ tslib_1.__metadata("design:paramtypes", [Object, read_scope_1.ContentReadScopeRegistry])
985
1017
  ], RelationLinkService);
986
1018
  /**
987
1019
  * The composite map key for "this translation group, in this locale".
@@ -1,5 +1,6 @@
1
1
  import { type Database } from '@orthacms/database';
2
2
  import { type ContentEntryExtension } from '../../../extension/entry-extension';
3
+ import { EntryFilterProviderRegistry } from '../../../extension/entry-filter-provider';
3
4
  import type { AnyContentType } from '../../../types/content-type';
4
5
  import { type ListEntriesQueryDto } from '../../http/dto/list-entries-query.dto';
5
6
  import type { EntryListView } from '../../types/entry-list-view';
@@ -14,8 +15,9 @@ import { RelationLinkService } from '../persistence/relation-link.service';
14
15
  export declare class EntriesService {
15
16
  private readonly db;
16
17
  private readonly relationLinks;
18
+ private readonly filterProviders;
17
19
  private readonly extension?;
18
- constructor(db: Database, relationLinks: RelationLinkService, extension?: ContentEntryExtension | undefined);
20
+ constructor(db: Database, relationLinks: RelationLinkService, filterProviders: EntryFilterProviderRegistry, extension?: ContentEntryExtension | undefined);
19
21
  /**
20
22
  * One page of a collection's entries: search → filter → sort → paginate, run
21
23
  * in SQL against the type's generated table, returned as the
@@ -1 +1 @@
1
- {"version":3,"file":"entries.service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/infrastructure/queries/entries.service.ts"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EAEH,KAAK,qBAAqB,EAE7B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAGH,KAAK,mBAAmB,EAC3B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAMjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AA+B3E;;;;;;GAMG;AACH,qBACa,cAAc;IAED,OAAO,CAAC,QAAQ,CAAC,EAAE;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAK9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;gBANQ,EAAE,EAAE,QAAQ,EAC9B,aAAa,EAAE,mBAAmB,EAKlC,SAAS,CAAC,EAAE,qBAAqB,YAAA;IAGtD;;;;;;OAMG;IACG,IAAI,CACN,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,mBAAmB,EAC1B,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC;IAgDzB;;;;;;;OAOG;YACW,SAAS;IA0CvB;;;;;;;OAOG;YACW,eAAe;IAyB7B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;;;;;;;;OASG;IACH,OAAO,CAAC,OAAO;CAsBlB"}
1
+ {"version":3,"file":"entries.service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/infrastructure/queries/entries.service.ts"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EAEH,KAAK,qBAAqB,EAE7B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAGH,KAAK,mBAAmB,EAC3B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAMjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AA+B3E;;;;;;GAMG;AACH,qBACa,cAAc;IAED,OAAO,CAAC,QAAQ,CAAC,EAAE;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAI9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAKhC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAVQ,EAAE,EAAE,QAAQ,EAC9B,aAAa,EAAE,mBAAmB,EAIlC,eAAe,EAAE,2BAA2B,EAK5C,SAAS,CAAC,EAAE,qBAAqB,YAAA;IAGtD;;;;;;OAMG;IACG,IAAI,CACN,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,mBAAmB,EAC1B,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC;IAgDzB;;;;;;;OAOG;YACW,SAAS;IAiDvB;;;;;;;OAOG;YACW,eAAe;IAyB7B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;;;;;;;;OASG;IACH,OAAO,CAAC,OAAO;CAsBlB"}
@@ -7,6 +7,7 @@ const drizzle_orm_1 = require("drizzle-orm");
7
7
  const database_1 = require("@orthacms/database");
8
8
  const utils_server_1 = require("@orthacms/utils-server");
9
9
  const entry_extension_1 = require("../../../extension/entry-extension");
10
+ const entry_filter_provider_1 = require("../../../extension/entry-filter-provider");
10
11
  const fields_1 = require("../../../types/fields");
11
12
  const list_entries_query_dto_1 = require("../../http/dto/list-entries-query.dto");
12
13
  const entries_constants_1 = require("../../entries.constants");
@@ -43,10 +44,16 @@ function previewFields(type, raw) {
43
44
  let EntriesService = class EntriesService {
44
45
  db;
45
46
  relationLinks;
47
+ filterProviders;
46
48
  extension;
47
- constructor(db, relationLinks, extension) {
49
+ constructor(db, relationLinks,
50
+ // Virtual filter fields contributed by plugins other than the single
51
+ // bound extension — segments' audience fields today. A registry rather
52
+ // than a token, for the reason its own file documents.
53
+ filterProviders, extension) {
48
54
  this.db = db;
49
55
  this.relationLinks = relationLinks;
56
+ this.filterProviders = filterProviders;
50
57
  this.extension = extension;
51
58
  }
52
59
  /**
@@ -105,7 +112,11 @@ let EntriesService = class EntriesService {
105
112
  // into the schema, resolved to its own SQL) and an extra scope
106
113
  // predicate (e.g. the active locale). Both are no-ops for types the
107
114
  // extension doesn't apply to.
108
- const filterExtension = this.extension?.filterExtension(type);
115
+ // The bound extension (i18n) plus every registered provider (segments'
116
+ // audience fields), folded into one contribution — see
117
+ // `EntryFilterProviderRegistry`, which exists because that port is a
118
+ // single binding and a second plugin would otherwise replace it.
119
+ const filterExtension = this.filterProviders.compose(type, this.extension?.filterExtension(type));
109
120
  // Building the surface walks the type's whole relation graph to the
110
121
  // hop budget, so only pay for it when there is actually a filter to
111
122
  // translate — an unfiltered list (the common case) skips it entirely.
@@ -196,7 +207,8 @@ exports.EntriesService = EntriesService;
196
207
  exports.EntriesService = EntriesService = tslib_1.__decorate([
197
208
  (0, common_1.Injectable)(),
198
209
  tslib_1.__param(0, (0, database_1.InjectDatabase)()),
199
- tslib_1.__param(2, (0, common_1.Optional)()),
200
- tslib_1.__param(2, (0, common_1.Inject)(entry_extension_1.CONTENT_ENTRY_EXTENSION)),
201
- tslib_1.__metadata("design:paramtypes", [Object, relation_link_service_1.RelationLinkService, Object])
210
+ tslib_1.__param(3, (0, common_1.Optional)()),
211
+ tslib_1.__param(3, (0, common_1.Inject)(entry_extension_1.CONTENT_ENTRY_EXTENSION)),
212
+ tslib_1.__metadata("design:paramtypes", [Object, relation_link_service_1.RelationLinkService,
213
+ entry_filter_provider_1.EntryFilterProviderRegistry, Object])
202
214
  ], EntriesService);
@@ -0,0 +1,82 @@
1
+ import { type Database } from '@orthacms/database';
2
+ import { type ContentEntryExtension } from '../../../extension/entry-extension';
3
+ import { EntryFilterProviderRegistry } from '../../../extension/entry-filter-provider';
4
+ import type { AnyContentType } from '../../../types/content-type';
5
+ /** Options narrowing a {@link EntryMatchQuery.matchingIds} scan. */
6
+ export interface EntryMatchOptions {
7
+ /**
8
+ * Restrict the scan to these entry ids. This is what turns a
9
+ * collection-wide predicate into the point check an event-driven caller
10
+ * needs — "does *this* row match?" is the same SQL with an extra
11
+ * `id IN (...)`, so one code path serves both and they cannot disagree
12
+ * about what a filter means.
13
+ */
14
+ entryIds?: readonly string[];
15
+ /** Upper bound on returned ids. */
16
+ limit?: number;
17
+ /** Rows to skip — for batching a full-collection scan. */
18
+ offset?: number;
19
+ }
20
+ /**
21
+ * Answers "which entries of this type match this filter?" against the same
22
+ * surface, parser and translator the records list uses.
23
+ *
24
+ * It exists so a downstream plugin can reuse content's filter semantics
25
+ * without reaching for the database or rebuilding the surface: the filterable
26
+ * paths, the relation-hop budget, the workspace scoping of every relation
27
+ * subquery, and the 400 on a malformed tree are all inherited rather than
28
+ * re-implemented. `@orthacms/alarms-server` evaluates its rules through this,
29
+ * which is what makes "the rule I saved from the list means what the list
30
+ * showed me" true by construction rather than by review.
31
+ *
32
+ * Two deliberate differences from the list query:
33
+ *
34
+ * - **No locale scoping.** The entries extension's `listScope` narrows a list
35
+ * to the active locale; a rule is a statement about the collection, so it
36
+ * must see every translation. The extension's *virtual filter fields* are
37
+ * still wired, so a rule may filter on them explicitly.
38
+ * - **Trashed rows are excluded, always.** There is no trash view here, and a
39
+ * tombstoned row is not something anyone can act on.
40
+ */
41
+ export declare class EntryMatchQuery {
42
+ private readonly db;
43
+ private readonly filterProviders;
44
+ private readonly extension?;
45
+ constructor(db: Database, filterProviders: EntryFilterProviderRegistry, extension?: ContentEntryExtension | undefined);
46
+ /**
47
+ * Parses `filter` against `type`'s surface without running it, throwing a
48
+ * `FilterException` when it does not hold up. Callers store filters that
49
+ * will be replayed by a background subscriber for months, so the parse
50
+ * belongs at the moment the filter is written — a tree that only fails at
51
+ * evaluation time fails somewhere nobody is looking.
52
+ */
53
+ assertParses(type: AnyContentType, filter: unknown, workspaceId: string): void;
54
+ /**
55
+ * Ids of `type` in `workspaceId` matching `filter`, oldest-id first so a
56
+ * batched scan is stable across calls.
57
+ */
58
+ matchingIds(type: AnyContentType, filter: unknown, workspaceId: string, options?: EntryMatchOptions): Promise<string[]>;
59
+ /** How many entries of `type` match `filter` right now. */
60
+ countMatching(type: AnyContentType, filter: unknown, workspaceId: string): Promise<number>;
61
+ /**
62
+ * How many entries of `type` the workspace holds at all — the denominator
63
+ * the rule editor shows beside a match count ("14 of 312"), which is what
64
+ * turns a number into a judgement about whether the rule is too broad.
65
+ */
66
+ countAll(type: AnyContentType, workspaceId: string): Promise<number>;
67
+ /**
68
+ * The workspace an entry belongs to, or `null` when no such row exists.
69
+ *
70
+ * A domain event names an entry and its content type but not its workspace
71
+ * — every content table carries its own `workspace_id`, so there is nothing
72
+ * for the envelope to add that a one-column lookup cannot answer. Exposed
73
+ * here rather than left to each subscriber, so a downstream plugin never
74
+ * has a reason to query a `content_<name>` table directly.
75
+ */
76
+ workspaceOf(type: AnyContentType, entryId: string): Promise<string | null>;
77
+ /** The workspace + not-trashed guard every scan starts from. */
78
+ private scope;
79
+ /** Scope + translated filter + optional id restriction. */
80
+ private predicate;
81
+ }
82
+ //# sourceMappingURL=entry-match.query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-match.query.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/infrastructure/queries/entry-match.query.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EAEH,KAAK,qBAAqB,EAC7B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAalE,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBACa,eAAe;IAEF,OAAO,CAAC,QAAQ,CAAC,EAAE;IAIrC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAGhC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAPQ,EAAE,EAAE,QAAQ,EAI9B,eAAe,EAAE,2BAA2B,EAG5C,SAAS,CAAC,EAAE,qBAAqB,YAAA;IAGtD;;;;;;OAMG;IACH,YAAY,CACR,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,MAAM,GACpB,IAAI;IAWP;;;OAGG;IACG,WAAW,CACb,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,iBAAsB,GAChC,OAAO,CAAC,MAAM,EAAE,CAAC;IAmBpB,2DAA2D;IACrD,aAAa,CACf,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC;IASlB;;;;OAIG;IACG,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQ1E;;;;;;;;OAQG;IACG,WAAW,CACb,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUzB,gEAAgE;IAChE,OAAO,CAAC,KAAK;IAQb,2DAA2D;YAC7C,SAAS;CAoC1B"}
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EntryMatchQuery = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const drizzle_orm_1 = require("drizzle-orm");
7
+ const database_1 = require("@orthacms/database");
8
+ const utils_server_1 = require("@orthacms/utils-server");
9
+ const entry_extension_1 = require("../../../extension/entry-extension");
10
+ const entry_filter_provider_1 = require("../../../extension/entry-filter-provider");
11
+ const entry_filter_surface_1 = require("./entry-filter-surface");
12
+ /**
13
+ * Answers "which entries of this type match this filter?" against the same
14
+ * surface, parser and translator the records list uses.
15
+ *
16
+ * It exists so a downstream plugin can reuse content's filter semantics
17
+ * without reaching for the database or rebuilding the surface: the filterable
18
+ * paths, the relation-hop budget, the workspace scoping of every relation
19
+ * subquery, and the 400 on a malformed tree are all inherited rather than
20
+ * re-implemented. `@orthacms/alarms-server` evaluates its rules through this,
21
+ * which is what makes "the rule I saved from the list means what the list
22
+ * showed me" true by construction rather than by review.
23
+ *
24
+ * Two deliberate differences from the list query:
25
+ *
26
+ * - **No locale scoping.** The entries extension's `listScope` narrows a list
27
+ * to the active locale; a rule is a statement about the collection, so it
28
+ * must see every translation. The extension's *virtual filter fields* are
29
+ * still wired, so a rule may filter on them explicitly.
30
+ * - **Trashed rows are excluded, always.** There is no trash view here, and a
31
+ * tombstoned row is not something anyone can act on.
32
+ */
33
+ let EntryMatchQuery = class EntryMatchQuery {
34
+ db;
35
+ filterProviders;
36
+ extension;
37
+ constructor(db,
38
+ // Same pair as `EntriesService`: an alarm rule stores the records
39
+ // list's filter verbatim, so it has to parse against exactly the
40
+ // surface that list offers — including the fields other plugins add.
41
+ filterProviders, extension) {
42
+ this.db = db;
43
+ this.filterProviders = filterProviders;
44
+ this.extension = extension;
45
+ }
46
+ /**
47
+ * Parses `filter` against `type`'s surface without running it, throwing a
48
+ * `FilterException` when it does not hold up. Callers store filters that
49
+ * will be replayed by a background subscriber for months, so the parse
50
+ * belongs at the moment the filter is written — a tree that only fails at
51
+ * evaluation time fails somewhere nobody is looking.
52
+ */
53
+ assertParses(type, filter, workspaceId) {
54
+ const { schema } = (0, entry_filter_surface_1.buildEntryFilterSurface)(type, {
55
+ workspaceId,
56
+ extensionFields: this.filterProviders.compose(type, this.extension?.filterExtension(type))?.fields
57
+ });
58
+ (0, utils_server_1.parseFilterTree)(filter, schema);
59
+ }
60
+ /**
61
+ * Ids of `type` in `workspaceId` matching `filter`, oldest-id first so a
62
+ * batched scan is stable across calls.
63
+ */
64
+ async matchingIds(type, filter, workspaceId, options = {}) {
65
+ // An empty restriction means "none of them", not "all of them" — an
66
+ // `inArray(col, [])` would be a false predicate anyway, but returning
67
+ // early keeps a caller that batched its ids from paying for a query
68
+ // whose answer is known.
69
+ if (options.entryIds && options.entryIds.length === 0)
70
+ return [];
71
+ const where = await this.predicate(type, filter, workspaceId, options);
72
+ const table = type.table;
73
+ const rows = await this.db
74
+ .select({ id: table['id'] })
75
+ .from(type.table)
76
+ .where(where)
77
+ .orderBy(table['id'])
78
+ .limit(options.limit ?? Number.MAX_SAFE_INTEGER)
79
+ .offset(options.offset ?? 0);
80
+ return rows.map((row) => String(row.id));
81
+ }
82
+ /** How many entries of `type` match `filter` right now. */
83
+ async countMatching(type, filter, workspaceId) {
84
+ const where = await this.predicate(type, filter, workspaceId, {});
85
+ const [row] = await this.db
86
+ .select({ total: (0, drizzle_orm_1.count)() })
87
+ .from(type.table)
88
+ .where(where);
89
+ return Number(row?.total ?? 0);
90
+ }
91
+ /**
92
+ * How many entries of `type` the workspace holds at all — the denominator
93
+ * the rule editor shows beside a match count ("14 of 312"), which is what
94
+ * turns a number into a judgement about whether the rule is too broad.
95
+ */
96
+ async countAll(type, workspaceId) {
97
+ const [row] = await this.db
98
+ .select({ total: (0, drizzle_orm_1.count)() })
99
+ .from(type.table)
100
+ .where(this.scope(type, workspaceId));
101
+ return Number(row?.total ?? 0);
102
+ }
103
+ /**
104
+ * The workspace an entry belongs to, or `null` when no such row exists.
105
+ *
106
+ * A domain event names an entry and its content type but not its workspace
107
+ * — every content table carries its own `workspace_id`, so there is nothing
108
+ * for the envelope to add that a one-column lookup cannot answer. Exposed
109
+ * here rather than left to each subscriber, so a downstream plugin never
110
+ * has a reason to query a `content_<name>` table directly.
111
+ */
112
+ async workspaceOf(type, entryId) {
113
+ const table = type.table;
114
+ const [row] = await this.db
115
+ .select({ workspaceId: table['workspaceId'] })
116
+ .from(type.table)
117
+ .where((0, drizzle_orm_1.eq)(table['id'], entryId))
118
+ .limit(1);
119
+ return row?.workspaceId ? String(row.workspaceId) : null;
120
+ }
121
+ /** The workspace + not-trashed guard every scan starts from. */
122
+ scope(type, workspaceId) {
123
+ const table = type.table;
124
+ return (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(table['workspaceId'], workspaceId), type.paranoid ? (0, drizzle_orm_1.isNull)(table['deletedAt']) : undefined);
125
+ }
126
+ /** Scope + translated filter + optional id restriction. */
127
+ async predicate(type, filter, workspaceId, options) {
128
+ const filterExtension = this.filterProviders.compose(type, this.extension?.filterExtension(type));
129
+ const { schema } = (0, entry_filter_surface_1.buildEntryFilterSurface)(type, {
130
+ workspaceId,
131
+ extensionFields: filterExtension?.fields
132
+ });
133
+ const tree = (0, utils_server_1.parseFilterTree)(filter, schema);
134
+ const filterSql = await (0, utils_server_1.applyFilterTree)(tree, schema, type.table, this.db, filterExtension
135
+ ? {
136
+ resolveExtension: (rule) => filterExtension.resolve(rule, { type, workspaceId })
137
+ }
138
+ : {});
139
+ const table = type.table;
140
+ return (0, drizzle_orm_1.and)(this.scope(type, workspaceId), filterSql, options.entryIds
141
+ ? (0, drizzle_orm_1.inArray)(table['id'], [...options.entryIds])
142
+ : undefined);
143
+ }
144
+ };
145
+ exports.EntryMatchQuery = EntryMatchQuery;
146
+ exports.EntryMatchQuery = EntryMatchQuery = tslib_1.__decorate([
147
+ (0, common_1.Injectable)(),
148
+ tslib_1.__param(0, (0, database_1.InjectDatabase)()),
149
+ tslib_1.__param(2, (0, common_1.Optional)()),
150
+ tslib_1.__param(2, (0, common_1.Inject)(entry_extension_1.CONTENT_ENTRY_EXTENSION)),
151
+ tslib_1.__metadata("design:paramtypes", [Object, entry_filter_provider_1.EntryFilterProviderRegistry, Object])
152
+ ], EntryMatchQuery);
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The **virtual filter-field registry** — how more than one plugin contributes
3
+ * fields to a content type's `?filter=` surface.
4
+ *
5
+ * `CONTENT_ENTRY_EXTENSION.filterExtension` already declares virtual fields
6
+ * (i18n's `hasLocale` / `missingLocale` / `localeCount`), but that port is a
7
+ * documented **single binding**: one provider per app, and i18n holds it. Nest
8
+ * has no multi-provider, so a second plugin binding the token would silently
9
+ * replace the first — for a filter surface that means fields quietly vanishing
10
+ * from the picker and saved filters starting to 400.
11
+ *
12
+ * So a plugin registers here instead (`@orthacms/segments-server` contributes
13
+ * "can be seen by" / "cannot be seen by" / "restricted"), the same shape and the
14
+ * same reason as `contentReadScopeRegistrar`. The bound extension keeps its
15
+ * method; {@link EntryFilterProviderRegistry.compose} folds the two together
16
+ * into the one `EntryFilterExtension` the query path already knows how to use,
17
+ * so nothing downstream changed.
18
+ *
19
+ * ## What a contribution owes
20
+ *
21
+ * **Every emitted subquery MUST scope to the workspace it is handed.** A virtual
22
+ * field is a subquery over a table this package knows nothing about; one that
23
+ * forgets the workspace turns a filter into a cross-tenant read.
24
+ *
25
+ * **Only declare fields you can answer, with only the operators you support.**
26
+ * The declared `fields` become the SQL whitelist, so an operator the resolver
27
+ * refuses reaches the user as "couldn't load this collection" over a rule the
28
+ * picker itself proposed. Narrow the operator set in the admin's `FilterField`
29
+ * to match.
30
+ *
31
+ * **A filter narrows what is listed; it is not a visibility rule.** Reachability
32
+ * is `CONTENT_READ_SCOPE`'s job and is applied separately. A field here must not
33
+ * be relied on to hide anything.
34
+ */
35
+ import { type Provider, type Type } from '@nestjs/common';
36
+ import type { AnyContentType } from '../types/content-type';
37
+ import type { EntryFilterExtension } from './entry-extension';
38
+ /** One plugin's virtual filter fields. */
39
+ export interface EntryFilterProvider {
40
+ /**
41
+ * The fields this plugin adds to `type`'s filter surface, or `undefined`
42
+ * when it adds none — which is what a plugin that is installed but
43
+ * unconfigured must return, so a bare install pays nothing.
44
+ */
45
+ filterFor(type: AnyContentType): EntryFilterExtension | undefined;
46
+ }
47
+ /** Every registered provider, in registration order. */
48
+ export declare class EntryFilterProviderRegistry {
49
+ private readonly logger;
50
+ private readonly providers;
51
+ /** Adds a provider. Registering the same instance twice is a no-op. */
52
+ register(provider: EntryFilterProvider): void;
53
+ /**
54
+ * Fold the bound extension's contribution and every registered provider's
55
+ * into one {@link EntryFilterExtension}.
56
+ *
57
+ * Returns `undefined` when nobody contributed anything, so a type with no
58
+ * virtual fields builds exactly the surface it did before this registry
59
+ * existed — and `entries.service` keeps its "no extension, no
60
+ * `resolveExtension`" branch.
61
+ *
62
+ * `resolve` routes by the rule's **field name**, to whoever declared it.
63
+ * A name declared twice is kept by its first declarer and the duplicate is
64
+ * dropped with a warning: silently letting the last writer win would make
65
+ * the meaning of a saved filter depend on plugin registration order.
66
+ */
67
+ compose(type: AnyContentType, bound: EntryFilterExtension | undefined): EntryFilterExtension | undefined;
68
+ }
69
+ /**
70
+ * Builds the DI provider that registers `providers` with content's filter-field
71
+ * registry at bootstrap.
72
+ *
73
+ * A factory with an explicit `inject` list rather than a class with reflected
74
+ * parameters, for the reason `contentReadScopeRegistrar` documents: an optional
75
+ * dependency typed `Foo | null` emits `Object` for `design:paramtypes`, Nest
76
+ * injects `undefined` with no error, and the plugin registers nothing.
77
+ */
78
+ export declare function entryFilterProviderRegistrar(label: string, ...providers: Type<EntryFilterProvider>[]): Provider;
79
+ //# sourceMappingURL=entry-filter-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-filter-provider.d.ts","sourceRoot":"","sources":["../../../src/lib/extension/entry-filter-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAIH,KAAK,QAAQ,EACb,KAAK,IAAI,EACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IAChC;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,cAAc,GAAG,oBAAoB,GAAG,SAAS,CAAC;CACrE;AAED,wDAAwD;AACxD,qBACa,2BAA2B;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgD;IACvE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IAEvD,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAM7C;;;;;;;;;;;;;OAaG;IACH,OAAO,CACH,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,oBAAoB,GAAG,SAAS,GACxC,oBAAoB,GAAG,SAAS;CAyCtC;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CACxC,KAAK,EAAE,MAAM,EACb,GAAG,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,EAAE,GAC1C,QAAQ,CAYV"}