@orthacms/content-server 0.0.0-reserve.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.
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/define.d.ts +17 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +28 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/lib/collection/define.d.ts +35 -0
- package/dist/lib/collection/define.d.ts.map +1 -0
- package/dist/lib/collection/define.js +217 -0
- package/dist/lib/collection/table-builder.d.ts +28 -0
- package/dist/lib/collection/table-builder.d.ts.map +1 -0
- package/dist/lib/collection/table-builder.js +306 -0
- package/dist/lib/content-types/controllers/get-content-schema.controller.d.ts +24 -0
- package/dist/lib/content-types/controllers/get-content-schema.controller.d.ts.map +1 -0
- package/dist/lib/content-types/controllers/get-content-schema.controller.js +58 -0
- package/dist/lib/content-types/controllers/get-filter-fields.controller.d.ts +35 -0
- package/dist/lib/content-types/controllers/get-filter-fields.controller.d.ts.map +1 -0
- package/dist/lib/content-types/controllers/get-filter-fields.controller.js +74 -0
- package/dist/lib/content-types/controllers/list-content-schema.controller.d.ts +15 -0
- package/dist/lib/content-types/controllers/list-content-schema.controller.d.ts.map +1 -0
- package/dist/lib/content-types/controllers/list-content-schema.controller.js +38 -0
- package/dist/lib/content-types/queries/workspace-grants.query.d.ts +25 -0
- package/dist/lib/content-types/queries/workspace-grants.query.d.ts.map +1 -0
- package/dist/lib/content-types/queries/workspace-grants.query.js +45 -0
- package/dist/lib/content.module.d.ts +21 -0
- package/dist/lib/content.module.d.ts.map +1 -0
- package/dist/lib/content.module.js +264 -0
- package/dist/lib/content.tokens.d.ts +5 -0
- package/dist/lib/content.tokens.d.ts.map +1 -0
- package/dist/lib/content.tokens.js +9 -0
- package/dist/lib/copilot/content-tool.provider.d.ts +64 -0
- package/dist/lib/copilot/content-tool.provider.d.ts.map +1 -0
- package/dist/lib/copilot/content-tool.provider.js +309 -0
- package/dist/lib/copilot/diff-snapshots.d.ts +42 -0
- package/dist/lib/copilot/diff-snapshots.d.ts.map +1 -0
- package/dist/lib/copilot/diff-snapshots.js +77 -0
- package/dist/lib/copilot/entry-proposal.applier.d.ts +89 -0
- package/dist/lib/copilot/entry-proposal.applier.d.ts.map +1 -0
- package/dist/lib/copilot/entry-proposal.applier.js +245 -0
- package/dist/lib/copilot/entry-proposal.provider.d.ts +157 -0
- package/dist/lib/copilot/entry-proposal.provider.d.ts.map +1 -0
- package/dist/lib/copilot/entry-proposal.provider.js +619 -0
- package/dist/lib/copilot/filter-schema.d.ts +61 -0
- package/dist/lib/copilot/filter-schema.d.ts.map +1 -0
- package/dist/lib/copilot/filter-schema.js +139 -0
- package/dist/lib/copilot/project-entry.d.ts +23 -0
- package/dist/lib/copilot/project-entry.d.ts.map +1 -0
- package/dist/lib/copilot/project-entry.js +36 -0
- package/dist/lib/copilot/proposal-kinds.d.ts +30 -0
- package/dist/lib/copilot/proposal-kinds.d.ts.map +1 -0
- package/dist/lib/copilot/proposal-kinds.js +32 -0
- package/dist/lib/copilot/revision-tool.provider.d.ts +56 -0
- package/dist/lib/copilot/revision-tool.provider.d.ts.map +1 -0
- package/dist/lib/copilot/revision-tool.provider.js +215 -0
- package/dist/lib/docs/content-schemas.d.ts +34 -0
- package/dist/lib/docs/content-schemas.d.ts.map +1 -0
- package/dist/lib/docs/content-schemas.js +617 -0
- package/dist/lib/docs/describe-content-api.d.ts +22 -0
- package/dist/lib/docs/describe-content-api.d.ts.map +1 -0
- package/dist/lib/docs/describe-content-api.js +173 -0
- package/dist/lib/docs/field-schema.d.ts +28 -0
- package/dist/lib/docs/field-schema.d.ts.map +1 -0
- package/dist/lib/docs/field-schema.js +214 -0
- package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.d.ts +23 -0
- package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.d.ts.map +1 -0
- package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.js +77 -0
- package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.d.ts +23 -0
- package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.d.ts.map +1 -0
- package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.js +66 -0
- package/dist/lib/entries/application/use-cases/publish-entry.use-case.d.ts +43 -0
- package/dist/lib/entries/application/use-cases/publish-entry.use-case.d.ts.map +1 -0
- package/dist/lib/entries/application/use-cases/publish-entry.use-case.js +106 -0
- package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.d.ts +30 -0
- package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.d.ts.map +1 -0
- package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.js +70 -0
- package/dist/lib/entries/controllers/bulk-entries.controller.d.ts +26 -0
- package/dist/lib/entries/controllers/bulk-entries.controller.d.ts.map +1 -0
- package/dist/lib/entries/controllers/create-entry.controller.d.ts +20 -0
- package/dist/lib/entries/controllers/create-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/controllers/delete-entry.controller.d.ts +24 -0
- package/dist/lib/entries/controllers/delete-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/controllers/get-entry.controller.d.ts +34 -0
- package/dist/lib/entries/controllers/get-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/controllers/list-entries.controller.d.ts +20 -0
- package/dist/lib/entries/controllers/list-entries.controller.d.ts.map +1 -0
- package/dist/lib/entries/controllers/publish-entry.controller.d.ts +19 -0
- package/dist/lib/entries/controllers/publish-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/controllers/resolve-type.d.ts +9 -0
- package/dist/lib/entries/controllers/resolve-type.d.ts.map +1 -0
- package/dist/lib/entries/controllers/update-entry.controller.d.ts +18 -0
- package/dist/lib/entries/controllers/update-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/domain/entry-publish-blocked.error.d.ts +13 -0
- package/dist/lib/entries/domain/entry-publish-blocked.error.d.ts.map +1 -0
- package/dist/lib/entries/domain/entry-publish-blocked.error.js +19 -0
- package/dist/lib/entries/domain/entry.d.ts +71 -0
- package/dist/lib/entries/domain/entry.d.ts.map +1 -0
- package/dist/lib/entries/domain/entry.js +90 -0
- package/dist/lib/entries/domain/events/entry-events.d.ts +67 -0
- package/dist/lib/entries/domain/events/entry-events.d.ts.map +1 -0
- package/dist/lib/entries/domain/events/entry-events.js +100 -0
- package/dist/lib/entries/dto/bulk-ids.dto.d.ts +11 -0
- package/dist/lib/entries/dto/bulk-ids.dto.d.ts.map +1 -0
- package/dist/lib/entries/dto/list-entries-query.dto.d.ts +46 -0
- package/dist/lib/entries/dto/list-entries-query.dto.d.ts.map +1 -0
- package/dist/lib/entries/dto/relation-delta-map.validator.d.ts +19 -0
- package/dist/lib/entries/dto/relation-delta-map.validator.d.ts.map +1 -0
- package/dist/lib/entries/dto/save-entry.dto.d.ts +46 -0
- package/dist/lib/entries/dto/save-entry.dto.d.ts.map +1 -0
- package/dist/lib/entries/entries.constants.d.ts +19 -0
- package/dist/lib/entries/entries.constants.d.ts.map +1 -0
- package/dist/lib/entries/entries.constants.js +21 -0
- package/dist/lib/entries/http/controllers/bulk-entries.controller.d.ts +33 -0
- package/dist/lib/entries/http/controllers/bulk-entries.controller.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/bulk-entries.controller.js +143 -0
- package/dist/lib/entries/http/controllers/create-entry.controller.d.ts +21 -0
- package/dist/lib/entries/http/controllers/create-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/create-entry.controller.js +52 -0
- package/dist/lib/entries/http/controllers/delete-entry.controller.d.ts +25 -0
- package/dist/lib/entries/http/controllers/delete-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/delete-entry.controller.js +84 -0
- package/dist/lib/entries/http/controllers/get-entry.controller.d.ts +44 -0
- package/dist/lib/entries/http/controllers/get-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/get-entry.controller.js +124 -0
- package/dist/lib/entries/http/controllers/list-entries.controller.d.ts +20 -0
- package/dist/lib/entries/http/controllers/list-entries.controller.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/list-entries.controller.js +52 -0
- package/dist/lib/entries/http/controllers/publish-entry.controller.d.ts +22 -0
- package/dist/lib/entries/http/controllers/publish-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/publish-entry.controller.js +68 -0
- package/dist/lib/entries/http/controllers/resolve-type.d.ts +9 -0
- package/dist/lib/entries/http/controllers/resolve-type.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/resolve-type.js +16 -0
- package/dist/lib/entries/http/controllers/to-actor.d.ts +13 -0
- package/dist/lib/entries/http/controllers/to-actor.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/to-actor.js +15 -0
- package/dist/lib/entries/http/controllers/update-entry.controller.d.ts +19 -0
- package/dist/lib/entries/http/controllers/update-entry.controller.d.ts.map +1 -0
- package/dist/lib/entries/http/controllers/update-entry.controller.js +51 -0
- package/dist/lib/entries/http/dto/bulk-ids.dto.d.ts +11 -0
- package/dist/lib/entries/http/dto/bulk-ids.dto.d.ts.map +1 -0
- package/dist/lib/entries/http/dto/bulk-ids.dto.js +33 -0
- package/dist/lib/entries/http/dto/list-entries-query.dto.d.ts +66 -0
- package/dist/lib/entries/http/dto/list-entries-query.dto.d.ts.map +1 -0
- package/dist/lib/entries/http/dto/list-entries-query.dto.js +192 -0
- package/dist/lib/entries/http/dto/relation-delta-map.validator.d.ts +30 -0
- package/dist/lib/entries/http/dto/relation-delta-map.validator.d.ts.map +1 -0
- package/dist/lib/entries/http/dto/relation-delta-map.validator.js +102 -0
- package/dist/lib/entries/http/dto/save-entry.dto.d.ts +48 -0
- package/dist/lib/entries/http/dto/save-entry.dto.d.ts.map +1 -0
- package/dist/lib/entries/http/dto/save-entry.dto.js +120 -0
- package/dist/lib/entries/http/guards/content-grant.guard.d.ts +37 -0
- package/dist/lib/entries/http/guards/content-grant.guard.d.ts.map +1 -0
- package/dist/lib/entries/http/guards/content-grant.guard.js +67 -0
- package/dist/lib/entries/infrastructure/persistence/bulk-publish-verdicts.d.ts +22 -0
- package/dist/lib/entries/infrastructure/persistence/bulk-publish-verdicts.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/persistence/bulk-publish-verdicts.js +81 -0
- package/dist/lib/entries/infrastructure/persistence/entry-counter.service.d.ts +42 -0
- package/dist/lib/entries/infrastructure/persistence/entry-counter.service.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/persistence/entry-counter.service.js +73 -0
- package/dist/lib/entries/infrastructure/persistence/entry-row.d.ts +56 -0
- package/dist/lib/entries/infrastructure/persistence/entry-row.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/persistence/entry-row.js +185 -0
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts +392 -0
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.js +1223 -0
- package/dist/lib/entries/infrastructure/persistence/field-selection.d.ts +24 -0
- package/dist/lib/entries/infrastructure/persistence/field-selection.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/persistence/media-accept.d.ts +23 -0
- package/dist/lib/entries/infrastructure/persistence/media-accept.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/persistence/media-accept.js +55 -0
- package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts +327 -0
- package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/persistence/relation-link.service.js +1008 -0
- package/dist/lib/entries/infrastructure/queries/bulk-publish-preview.query.d.ts +19 -0
- package/dist/lib/entries/infrastructure/queries/bulk-publish-preview.query.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/bulk-publish-preview.query.js +39 -0
- package/dist/lib/entries/infrastructure/queries/entries.service.d.ts +64 -0
- package/dist/lib/entries/infrastructure/queries/entries.service.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/entries.service.js +202 -0
- package/dist/lib/entries/infrastructure/queries/entry-filter-schema.d.ts +40 -0
- package/dist/lib/entries/infrastructure/queries/entry-filter-schema.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/entry-filter-surface.d.ts +38 -0
- package/dist/lib/entries/infrastructure/queries/entry-filter-surface.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/entry-filter-surface.js +310 -0
- package/dist/lib/entries/infrastructure/queries/entry-scalar-fields.d.ts +25 -0
- package/dist/lib/entries/infrastructure/queries/entry-scalar-fields.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/entry-scalar-fields.js +51 -0
- package/dist/lib/entries/infrastructure/queries/entry-search.d.ts +19 -0
- package/dist/lib/entries/infrastructure/queries/entry-search.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/entry-search.js +52 -0
- package/dist/lib/entries/infrastructure/queries/media-refs.query.d.ts +21 -0
- package/dist/lib/entries/infrastructure/queries/media-refs.query.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/media-refs.query.js +117 -0
- package/dist/lib/entries/services/entries.service.d.ts +59 -0
- package/dist/lib/entries/services/entries.service.d.ts.map +1 -0
- package/dist/lib/entries/services/entry-counter.service.d.ts +31 -0
- package/dist/lib/entries/services/entry-counter.service.d.ts.map +1 -0
- package/dist/lib/entries/services/entry-filter-schema.d.ts +32 -0
- package/dist/lib/entries/services/entry-filter-schema.d.ts.map +1 -0
- package/dist/lib/entries/services/entry-row.d.ts +56 -0
- package/dist/lib/entries/services/entry-row.d.ts.map +1 -0
- package/dist/lib/entries/services/entry-writer.service.d.ts +193 -0
- package/dist/lib/entries/services/entry-writer.service.d.ts.map +1 -0
- package/dist/lib/entries/services/relation-link.service.d.ts +115 -0
- package/dist/lib/entries/services/relation-link.service.d.ts.map +1 -0
- package/dist/lib/entries/types/bulk-publish.d.ts +75 -0
- package/dist/lib/entries/types/bulk-publish.d.ts.map +1 -0
- package/dist/lib/entries/types/bulk-publish.js +22 -0
- package/dist/lib/entries/types/entry-list-view.d.ts +205 -0
- package/dist/lib/entries/types/entry-list-view.d.ts.map +1 -0
- package/dist/lib/entries/types/entry-list-view.js +5 -0
- package/dist/lib/entries/types/filter-surface.d.ts +35 -0
- package/dist/lib/entries/types/filter-surface.d.ts.map +1 -0
- package/dist/lib/entries/types/filter-surface.js +2 -0
- package/dist/lib/extension/entry-extension-boot-check.d.ts +16 -0
- package/dist/lib/extension/entry-extension-boot-check.d.ts.map +1 -0
- package/dist/lib/extension/entry-extension-boot-check.js +42 -0
- package/dist/lib/extension/entry-extension.d.ts +225 -0
- package/dist/lib/extension/entry-extension.d.ts.map +1 -0
- package/dist/lib/extension/entry-extension.js +27 -0
- package/dist/lib/extension/media-asset-resolver.d.ts +85 -0
- package/dist/lib/extension/media-asset-resolver.d.ts.map +1 -0
- package/dist/lib/extension/media-asset-resolver.js +26 -0
- package/dist/lib/extension/per-locale-relation.d.ts +18 -0
- package/dist/lib/extension/per-locale-relation.d.ts.map +1 -0
- package/dist/lib/extension/relation-locale-sync.d.ts +62 -0
- package/dist/lib/extension/relation-locale-sync.d.ts.map +1 -0
- package/dist/lib/extension/relation-locale-sync.js +88 -0
- package/dist/lib/fields/index.d.ts +94 -0
- package/dist/lib/fields/index.d.ts.map +1 -0
- package/dist/lib/fields/index.js +203 -0
- package/dist/lib/insights/http/controllers/content-pipeline.controller.d.ts +16 -0
- package/dist/lib/insights/http/controllers/content-pipeline.controller.d.ts.map +1 -0
- package/dist/lib/insights/http/controllers/content-pipeline.controller.js +39 -0
- package/dist/lib/insights/http/controllers/content-punchcard.controller.d.ts +18 -0
- package/dist/lib/insights/http/controllers/content-punchcard.controller.d.ts.map +1 -0
- package/dist/lib/insights/http/controllers/content-punchcard.controller.js +42 -0
- package/dist/lib/insights/http/controllers/content-stale.controller.d.ts +17 -0
- package/dist/lib/insights/http/controllers/content-stale.controller.d.ts.map +1 -0
- package/dist/lib/insights/http/controllers/content-stale.controller.js +40 -0
- package/dist/lib/insights/http/controllers/content-totals.controller.d.ts +18 -0
- package/dist/lib/insights/http/controllers/content-totals.controller.d.ts.map +1 -0
- package/dist/lib/insights/http/controllers/content-totals.controller.js +42 -0
- package/dist/lib/insights/http/controllers/content-unshipped.controller.d.ts +17 -0
- package/dist/lib/insights/http/controllers/content-unshipped.controller.d.ts.map +1 -0
- package/dist/lib/insights/http/controllers/content-unshipped.controller.js +40 -0
- package/dist/lib/insights/http/controllers/content-velocity.controller.d.ts +17 -0
- package/dist/lib/insights/http/controllers/content-velocity.controller.d.ts.map +1 -0
- package/dist/lib/insights/http/controllers/content-velocity.controller.js +41 -0
- package/dist/lib/insights/http/dto/insights-range-query.dto.d.ts +15 -0
- package/dist/lib/insights/http/dto/insights-range-query.dto.d.ts.map +1 -0
- package/dist/lib/insights/http/dto/insights-range-query.dto.js +38 -0
- package/dist/lib/insights/infrastructure/queries/content-insights.query.d.ts +77 -0
- package/dist/lib/insights/infrastructure/queries/content-insights.query.d.ts.map +1 -0
- package/dist/lib/insights/infrastructure/queries/content-insights.query.js +380 -0
- package/dist/lib/insights/types/content-insights-view.d.ts +134 -0
- package/dist/lib/insights/types/content-insights-view.d.ts.map +1 -0
- package/dist/lib/insights/types/content-insights-view.js +10 -0
- package/dist/lib/mcp/content-tools.provider.d.ts +83 -0
- package/dist/lib/mcp/content-tools.provider.d.ts.map +1 -0
- package/dist/lib/mcp/content-tools.provider.js +441 -0
- package/dist/lib/mcp/tool-input.d.ts +43 -0
- package/dist/lib/mcp/tool-input.d.ts.map +1 -0
- package/dist/lib/mcp/tool-input.js +117 -0
- package/dist/lib/mcp/tool-schemas.d.ts +34 -0
- package/dist/lib/mcp/tool-schemas.d.ts.map +1 -0
- package/dist/lib/mcp/tool-schemas.js +282 -0
- package/dist/lib/public/controllers/get-public-entry.controller.d.ts +26 -0
- package/dist/lib/public/controllers/get-public-entry.controller.d.ts.map +1 -0
- package/dist/lib/public/controllers/list-public-entries.controller.d.ts +24 -0
- package/dist/lib/public/controllers/list-public-entries.controller.d.ts.map +1 -0
- package/dist/lib/public/controllers/public-schema.controller.d.ts +18 -0
- package/dist/lib/public/controllers/public-schema.controller.d.ts.map +1 -0
- package/dist/lib/public-api/http/api-token-request.d.ts +41 -0
- package/dist/lib/public-api/http/api-token-request.d.ts.map +1 -0
- package/dist/lib/public-api/http/api-token-request.js +2 -0
- package/dist/lib/public-api/http/controllers/public-content-types.controller.d.ts +31 -0
- package/dist/lib/public-api/http/controllers/public-content-types.controller.d.ts.map +1 -0
- package/dist/lib/public-api/http/controllers/public-content-types.controller.js +93 -0
- package/dist/lib/public-api/http/controllers/public-entries.controller.d.ts +101 -0
- package/dist/lib/public-api/http/controllers/public-entries.controller.d.ts.map +1 -0
- package/dist/lib/public-api/http/controllers/public-entries.controller.js +307 -0
- package/dist/lib/public-api/http/controllers/public-entry-writes.controller.d.ts +76 -0
- package/dist/lib/public-api/http/controllers/public-entry-writes.controller.d.ts.map +1 -0
- package/dist/lib/public-api/http/controllers/public-entry-writes.controller.js +353 -0
- package/dist/lib/public-api/http/controllers/resolve-granted-type.d.ts +42 -0
- package/dist/lib/public-api/http/controllers/resolve-granted-type.d.ts.map +1 -0
- package/dist/lib/public-api/http/controllers/resolve-granted-type.js +25 -0
- package/dist/lib/public-api/http/decorators/current-api-token.decorator.d.ts +8 -0
- package/dist/lib/public-api/http/decorators/current-api-token.decorator.d.ts.map +1 -0
- package/dist/lib/public-api/http/decorators/current-api-token.decorator.js +17 -0
- package/dist/lib/public-api/http/dto/public-bulk.dto.d.ts +58 -0
- package/dist/lib/public-api/http/dto/public-bulk.dto.d.ts.map +1 -0
- package/dist/lib/public-api/http/dto/public-bulk.dto.js +118 -0
- package/dist/lib/public-api/http/dto/public-list-entries-query.dto.d.ts +106 -0
- package/dist/lib/public-api/http/dto/public-list-entries-query.dto.d.ts.map +1 -0
- package/dist/lib/public-api/http/dto/public-list-entries-query.dto.js +297 -0
- package/dist/lib/public-api/http/dto/public-save-entry.dto.d.ts +39 -0
- package/dist/lib/public-api/http/dto/public-save-entry.dto.d.ts.map +1 -0
- package/dist/lib/public-api/http/dto/public-save-entry.dto.js +112 -0
- package/dist/lib/public-api/http/guards/api-token-workspace.guard.d.ts +25 -0
- package/dist/lib/public-api/http/guards/api-token-workspace.guard.d.ts.map +1 -0
- package/dist/lib/public-api/http/guards/api-token-workspace.guard.js +58 -0
- package/dist/lib/public-api/http/guards/api-token.guard.d.ts +32 -0
- package/dist/lib/public-api/http/guards/api-token.guard.d.ts.map +1 -0
- package/dist/lib/public-api/http/guards/api-token.guard.js +92 -0
- package/dist/lib/public-api/http/guards/draft-visibility.guard.d.ts +25 -0
- package/dist/lib/public-api/http/guards/draft-visibility.guard.d.ts.map +1 -0
- package/dist/lib/public-api/http/guards/draft-visibility.guard.js +56 -0
- package/dist/lib/public-api/infrastructure/bulk-save-op.d.ts +55 -0
- package/dist/lib/public-api/infrastructure/bulk-save-op.d.ts.map +1 -0
- package/dist/lib/public-api/infrastructure/bulk-save-op.js +61 -0
- package/dist/lib/public-api/infrastructure/field-selection.d.ts +21 -0
- package/dist/lib/public-api/infrastructure/field-selection.d.ts.map +1 -0
- package/dist/lib/public-api/infrastructure/field-selection.js +72 -0
- package/dist/lib/public-api/infrastructure/public-entries.query.d.ts +248 -0
- package/dist/lib/public-api/infrastructure/public-entries.query.d.ts.map +1 -0
- package/dist/lib/public-api/infrastructure/public-entries.query.js +557 -0
- package/dist/lib/public-api/infrastructure/public-entry-row.d.ts +31 -0
- package/dist/lib/public-api/infrastructure/public-entry-row.d.ts.map +1 -0
- package/dist/lib/public-api/infrastructure/public-entry-row.js +76 -0
- package/dist/lib/public-api/infrastructure/public-entry-writes.service.d.ts +188 -0
- package/dist/lib/public-api/infrastructure/public-entry-writes.service.d.ts.map +1 -0
- package/dist/lib/public-api/infrastructure/public-entry-writes.service.js +321 -0
- package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts +98 -0
- package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts.map +1 -0
- package/dist/lib/public-api/infrastructure/public-expansion.query.js +384 -0
- package/dist/lib/public-api/types/public-bulk.d.ts +77 -0
- package/dist/lib/public-api/types/public-bulk.d.ts.map +1 -0
- package/dist/lib/public-api/types/public-bulk.js +23 -0
- package/dist/lib/public-api/types/public-entry.d.ts +97 -0
- package/dist/lib/public-api/types/public-entry.d.ts.map +1 -0
- package/dist/lib/public-api/types/public-entry.js +8 -0
- package/dist/lib/public-api/types/public-expansion.d.ts +92 -0
- package/dist/lib/public-api/types/public-expansion.d.ts.map +1 -0
- package/dist/lib/public-api/types/public-expansion.js +2 -0
- package/dist/lib/registry/content-type-registry.d.ts +103 -0
- package/dist/lib/registry/content-type-registry.d.ts.map +1 -0
- package/dist/lib/registry/content-type-registry.js +155 -0
- package/dist/lib/revisions/application/ports/revision-store.d.ts +71 -0
- package/dist/lib/revisions/application/ports/revision-store.d.ts.map +1 -0
- package/dist/lib/revisions/application/ports/revision-store.js +9 -0
- package/dist/lib/revisions/application/use-cases/publish-revision.use-case.d.ts +40 -0
- package/dist/lib/revisions/application/use-cases/publish-revision.use-case.d.ts.map +1 -0
- package/dist/lib/revisions/application/use-cases/publish-revision.use-case.js +66 -0
- package/dist/lib/revisions/application/use-cases/restore-revision.use-case.d.ts +36 -0
- package/dist/lib/revisions/application/use-cases/restore-revision.use-case.d.ts.map +1 -0
- package/dist/lib/revisions/application/use-cases/restore-revision.use-case.js +57 -0
- package/dist/lib/revisions/domain/revision-status.d.ts +18 -0
- package/dist/lib/revisions/domain/revision-status.d.ts.map +1 -0
- package/dist/lib/revisions/domain/revision-status.js +18 -0
- package/dist/lib/revisions/domain/revision.d.ts +80 -0
- package/dist/lib/revisions/domain/revision.d.ts.map +1 -0
- package/dist/lib/revisions/domain/revision.js +84 -0
- package/dist/lib/revisions/http/controllers/publish-revision.controller.d.ts +19 -0
- package/dist/lib/revisions/http/controllers/publish-revision.controller.d.ts.map +1 -0
- package/dist/lib/revisions/http/controllers/publish-revision.controller.js +51 -0
- package/dist/lib/revisions/http/controllers/restore-revision.controller.d.ts +17 -0
- package/dist/lib/revisions/http/controllers/restore-revision.controller.d.ts.map +1 -0
- package/dist/lib/revisions/http/controllers/restore-revision.controller.js +49 -0
- package/dist/lib/revisions/http/controllers/revisions.controller.d.ts +20 -0
- package/dist/lib/revisions/http/controllers/revisions.controller.d.ts.map +1 -0
- package/dist/lib/revisions/http/controllers/revisions.controller.js +76 -0
- package/dist/lib/revisions/infrastructure/persistence/drizzle-revision.store.d.ts +33 -0
- package/dist/lib/revisions/infrastructure/persistence/drizzle-revision.store.d.ts.map +1 -0
- package/dist/lib/revisions/infrastructure/persistence/drizzle-revision.store.js +174 -0
- package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts +17 -0
- package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts.map +1 -0
- package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.js +20 -0
- package/dist/lib/revisions/infrastructure/persistence/revision-table.d.ts +228 -0
- package/dist/lib/revisions/infrastructure/persistence/revision-table.d.ts.map +1 -0
- package/dist/lib/revisions/infrastructure/persistence/revision-table.js +57 -0
- package/dist/lib/revisions/infrastructure/queries/revision-refs.query.d.ts +22 -0
- package/dist/lib/revisions/infrastructure/queries/revision-refs.query.d.ts.map +1 -0
- package/dist/lib/revisions/infrastructure/queries/revision-refs.query.js +66 -0
- package/dist/lib/revisions/revisions.constants.d.ts +10 -0
- package/dist/lib/revisions/revisions.constants.d.ts.map +1 -0
- package/dist/lib/revisions/revisions.constants.js +12 -0
- package/dist/lib/revisions/types/revision-view.d.ts +74 -0
- package/dist/lib/revisions/types/revision-view.d.ts.map +1 -0
- package/dist/lib/revisions/types/revision-view.js +2 -0
- package/dist/lib/types/content-type.d.ts +131 -0
- package/dist/lib/types/content-type.d.ts.map +1 -0
- package/dist/lib/types/content-type.js +28 -0
- package/dist/lib/types/fields.d.ts +303 -0
- package/dist/lib/types/fields.d.ts.map +1 -0
- package/dist/lib/types/fields.js +52 -0
- package/dist/lib/utils/content-plugin.d.ts +35 -0
- package/dist/lib/utils/content-plugin.d.ts.map +1 -0
- package/dist/lib/utils/content-plugin.js +35 -0
- package/dist/lib/validation/services/entry-validation.service.d.ts +24 -0
- package/dist/lib/validation/services/entry-validation.service.d.ts.map +1 -0
- package/dist/lib/validation/services/entry-validation.service.js +35 -0
- package/package.json +54 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { AnyContentType } from '../../types/content-type';
|
|
2
|
+
import { EntryWriterService } from '../../entries/infrastructure/persistence/entry-writer.service';
|
|
3
|
+
import { PublishEntryUseCase } from '../../entries/application/use-cases/publish-entry.use-case';
|
|
4
|
+
import { UnpublishEntryUseCase } from '../../entries/application/use-cases/unpublish-entry.use-case';
|
|
5
|
+
import { BulkPublishEntriesUseCase } from '../../entries/application/use-cases/bulk-publish-entries.use-case';
|
|
6
|
+
import { BulkUnpublishEntriesUseCase } from '../../entries/application/use-cases/bulk-unpublish-entries.use-case';
|
|
7
|
+
import type { BulkActionResult, BulkPublishResult } from '../../entries/types/bulk-publish';
|
|
8
|
+
import type { PublicSaveEntryDto } from '../http/dto/public-save-entry.dto';
|
|
9
|
+
import type { PublicBulkSaveItemDto } from '../http/dto/public-bulk.dto';
|
|
10
|
+
import { type PublicBulkSaveResult } from '../types/public-bulk';
|
|
11
|
+
import type { PublicEntry } from '../types/public-entry';
|
|
12
|
+
import { PublicEntriesQuery, type EntryLocator } from './public-entries.query';
|
|
13
|
+
/**
|
|
14
|
+
* The write side of the public content API.
|
|
15
|
+
*
|
|
16
|
+
* **A thin edge over the admin's write pipeline, deliberately** — the opposite
|
|
17
|
+
* call from the read side, and for the opposite reason. The read stated its own
|
|
18
|
+
* WHERE because `EntriesService`'s knobs select rows a token must never see, so
|
|
19
|
+
* reaching through it would have made the visibility rule an argument about what
|
|
20
|
+
* was *not* passed. A write has no such hazard and a great deal more to get
|
|
21
|
+
* right: `EntryWriterService` already owns field validation, relation-delta
|
|
22
|
+
* application, media-target checks, revision numbering serialized by a per-entry
|
|
23
|
+
* advisory lock, the transactional outbox, and the i18n extension's sibling sync
|
|
24
|
+
* (which demotes rewritten published translations and re-validates them). A
|
|
25
|
+
* second implementation of any of that is how you get duplicate version numbers
|
|
26
|
+
* and translations that quietly drift. So this class adds exactly three things
|
|
27
|
+
* the writer does not know about — the locator, the grant gate (in the
|
|
28
|
+
* controller), and the public wire shape — and delegates the rest.
|
|
29
|
+
*
|
|
30
|
+
* **Responses are re-read through the public projection** rather than mapped
|
|
31
|
+
* from the writer's `EntryRecord`. It costs one SELECT per write and buys the
|
|
32
|
+
* guarantee that what a write returns is byte-identical in shape to what a read
|
|
33
|
+
* returns — including the reference fields the public `values` omits, which a
|
|
34
|
+
* hand-written mapper would have to remember to strip every time the field
|
|
35
|
+
* vocabulary grows.
|
|
36
|
+
*
|
|
37
|
+
* **No `OriginGuard`.** The admin's write controllers carry it because they are
|
|
38
|
+
* cookie-authenticated and therefore CSRF-able. A bearer token is not sent
|
|
39
|
+
* ambiently by a browser, so there is nothing to forge here — and requiring an
|
|
40
|
+
* `Origin` header would break every non-browser client, which is all of them.
|
|
41
|
+
*/
|
|
42
|
+
export declare class PublicEntryWritesService {
|
|
43
|
+
private readonly writer;
|
|
44
|
+
private readonly entries;
|
|
45
|
+
private readonly publishEntry;
|
|
46
|
+
private readonly unpublishEntry;
|
|
47
|
+
private readonly bulkPublishEntries;
|
|
48
|
+
private readonly bulkUnpublishEntries;
|
|
49
|
+
constructor(writer: EntryWriterService, entries: PublicEntriesQuery, publishEntry: PublishEntryUseCase, unpublishEntry: UnpublishEntryUseCase, bulkPublishEntries: BulkPublishEntriesUseCase, bulkUnpublishEntries: BulkUnpublishEntriesUseCase);
|
|
50
|
+
/**
|
|
51
|
+
* Create an entry. It lands as a **draft** on publishable types — the same
|
|
52
|
+
* as the editor's "new record", and the reason publish is its own call: a
|
|
53
|
+
* create that went live immediately would have no reviewable state.
|
|
54
|
+
*
|
|
55
|
+
* With `localeGroupId` the row joins an existing translation group instead
|
|
56
|
+
* of starting one, which is how a new locale of an existing record is
|
|
57
|
+
* written.
|
|
58
|
+
*/
|
|
59
|
+
create(type: AnyContentType, body: PublicSaveEntryDto, workspaceId: string, grantedTypes: ReadonlySet<string>): Promise<PublicEntry>;
|
|
60
|
+
/**
|
|
61
|
+
* Update an entry's values and apply its relation deltas. On a publishable
|
|
62
|
+
* type this moves a published row back to `draft` while its published
|
|
63
|
+
* version stays live — the admin's **Modified** state — so an edit never
|
|
64
|
+
* silently changes what the world is reading.
|
|
65
|
+
*
|
|
66
|
+
* **A genuine partial update**, and this is a deliberate divergence from the
|
|
67
|
+
* admin's `PATCH`, which replaces the whole values bag because the editor
|
|
68
|
+
* always submits the full document. An API client does not: it sends the two
|
|
69
|
+
* fields it changed. Under replace semantics that silently nulls everything
|
|
70
|
+
* else — and since `required` only bites at publish time, the damage is
|
|
71
|
+
* invisible until a later publish fails on fields the caller never touched.
|
|
72
|
+
* A method named PATCH that destroys unsent data is a data-loss footgun, so
|
|
73
|
+
* the stored values are merged under the submitted ones here.
|
|
74
|
+
*
|
|
75
|
+
* Merge is **by key presence**, so nulling a field explicitly still works:
|
|
76
|
+
* `{"excerpt": null}` clears it, while omitting `excerpt` leaves it alone.
|
|
77
|
+
*
|
|
78
|
+
* `locale` here is the **addressing** locale — the query string's, which
|
|
79
|
+
* picks the row out of a translation group. It is deliberately not
|
|
80
|
+
* `body.locale`: that field is create-only (it stamps a new row's locale),
|
|
81
|
+
* and reading it here would let a body that omits it silently retarget a
|
|
82
|
+
* group write at the default-locale row. Which is exactly what it did, until
|
|
83
|
+
* a live check caught a German update rewriting the English article.
|
|
84
|
+
*/
|
|
85
|
+
update(type: AnyContentType, locator: EntryLocator, body: PublicSaveEntryDto, workspaceId: string, grantedTypes: ReadonlySet<string>, locale?: string): Promise<PublicEntry>;
|
|
86
|
+
/**
|
|
87
|
+
* Save many entries of one type in a single request — the batch form of
|
|
88
|
+
* {@link create} and {@link update}, and the reason it is one method rather
|
|
89
|
+
* than two: an import is a list of records, some of which the caller already
|
|
90
|
+
* has and some of which it does not, and splitting that into two calls would
|
|
91
|
+
* make the client sort them first.
|
|
92
|
+
*
|
|
93
|
+
* **Each item is dispatched to the very same `create` / `update` above.**
|
|
94
|
+
* Nothing about what a write means is restated here — validation, relation
|
|
95
|
+
* deltas, media checks, revision numbering, the outbox, i18n's sibling sync
|
|
96
|
+
* all come from the one pipeline, so "saved in bulk" and "saved one at a
|
|
97
|
+
* time" cannot mean different things.
|
|
98
|
+
*
|
|
99
|
+
* **Partial success, item by item.** The items are written one transaction
|
|
100
|
+
* at a time (`EntryWriterService` opens its own per write, taking the
|
|
101
|
+
* workspace's shared content lock), so there is no batch to roll back even
|
|
102
|
+
* in principle — and rejecting forty-nine good entries because the fiftieth
|
|
103
|
+
* names a relation that does not exist would defeat the point of the
|
|
104
|
+
* endpoint. Every item therefore gets a verdict, failures carry the status
|
|
105
|
+
* and message their single-entry call would have produced, and the caller
|
|
106
|
+
* retries the ones it can fix. This is the same contract the bulk-publish
|
|
107
|
+
* commit already reports through its `skipped` list.
|
|
108
|
+
*
|
|
109
|
+
* **Written in order, one after another** rather than concurrently. Every
|
|
110
|
+
* write takes the workspace's shared content lock and (on a localized type)
|
|
111
|
+
* an advisory lock over the translation group, so a batch touching siblings
|
|
112
|
+
* would spend a fan-out contending with itself; sequential also makes the
|
|
113
|
+
* per-item results deterministic, which a caller diffing two runs relies on.
|
|
114
|
+
*/
|
|
115
|
+
bulkSave(type: AnyContentType, items: readonly PublicBulkSaveItemDto[], workspaceId: string, grantedTypes: ReadonlySet<string>): Promise<PublicBulkSaveResult>;
|
|
116
|
+
/**
|
|
117
|
+
* The values bag an update submits: the stored values with the caller's
|
|
118
|
+
* merged over them (see {@link update}), minus any single relation the
|
|
119
|
+
* caller is setting through `relations`.
|
|
120
|
+
*
|
|
121
|
+
* That subtraction is what makes the two channels coexist. `toRecord`
|
|
122
|
+
* re-supplies every column-backed field including single-relation FKs, so
|
|
123
|
+
* without it a `relations: { author: { set } }` would always arrive
|
|
124
|
+
* alongside a stored `values.author` and be rejected as "sent in both
|
|
125
|
+
* places" — a collision this method created rather than the caller.
|
|
126
|
+
*/
|
|
127
|
+
private mergedValues;
|
|
128
|
+
/**
|
|
129
|
+
* Publish many entries at once.
|
|
130
|
+
*
|
|
131
|
+
* **Delegates to `BulkPublishEntriesUseCase` — the admin's own** — rather
|
|
132
|
+
* than looping over {@link publish}. That use-case selects the candidate
|
|
133
|
+
* rows `FOR UPDATE` and re-validates them inside **one** transaction, which
|
|
134
|
+
* is the only thing that closes the window between "this draft validates"
|
|
135
|
+
* and "publish it"; a loop of single publishes would reopen it once per id.
|
|
136
|
+
* It also reports the ids it skipped and why (already published, blocked by
|
|
137
|
+
* validation, not found in this workspace), so a partial batch is legible
|
|
138
|
+
* rather than a flat count.
|
|
139
|
+
*
|
|
140
|
+
* The grant gate has already run in the caller, and the use-case is
|
|
141
|
+
* workspace-scoped, so an id from another workspace is reported as
|
|
142
|
+
* `not-found` — the same answer an id that never existed gets.
|
|
143
|
+
*/
|
|
144
|
+
bulkPublish(type: AnyContentType, ids: string[], workspaceId: string): Promise<BulkPublishResult>;
|
|
145
|
+
/**
|
|
146
|
+
* Take many entries off the air, reverting each to a draft.
|
|
147
|
+
*
|
|
148
|
+
* `{ count }` rather than a per-id verdict, because unpublishing has no
|
|
149
|
+
* gate to fail: an id that is not a live published row of this workspace is
|
|
150
|
+
* simply not one of the rows that changed. The count is the number that
|
|
151
|
+
* actually transitioned, and one `entry.unpublished` event is emitted per
|
|
152
|
+
* transition — never per submitted id.
|
|
153
|
+
*/
|
|
154
|
+
bulkUnpublish(type: AnyContentType, ids: string[], workspaceId: string): Promise<BulkActionResult>;
|
|
155
|
+
/**
|
|
156
|
+
* Remove many entries — soft-deleting on a `paranoid` type (recoverable
|
|
157
|
+
* from the admin's trash) and hard-deleting otherwise, exactly as
|
|
158
|
+
* {@link remove} does for one.
|
|
159
|
+
*
|
|
160
|
+
* Rows, not records: on a localized type this deletes the listed locales
|
|
161
|
+
* and leaves their siblings live.
|
|
162
|
+
*/
|
|
163
|
+
bulkRemove(type: AnyContentType, ids: string[], workspaceId: string): Promise<BulkActionResult>;
|
|
164
|
+
/** Take an entry live. 422 when the stored draft no longer validates. */
|
|
165
|
+
publish(type: AnyContentType, locator: EntryLocator, workspaceId: string, grantedTypes: ReadonlySet<string>, locale?: string): Promise<PublicEntry>;
|
|
166
|
+
/** Take an entry back off the air, reverting it to a draft. */
|
|
167
|
+
unpublish(type: AnyContentType, locator: EntryLocator, workspaceId: string, grantedTypes: ReadonlySet<string>, locale?: string): Promise<PublicEntry>;
|
|
168
|
+
/**
|
|
169
|
+
* Remove an entry — soft delete on a `paranoid` type (recoverable from the
|
|
170
|
+
* admin's trash), a hard delete otherwise. Either way it leaves the public
|
|
171
|
+
* API immediately.
|
|
172
|
+
*
|
|
173
|
+
* Only **this** row: a translation is deleted on its own, so removing the
|
|
174
|
+
* German article leaves the English one live. Deleting a whole group means
|
|
175
|
+
* deleting each locale.
|
|
176
|
+
*/
|
|
177
|
+
remove(type: AnyContentType, locator: EntryLocator, workspaceId: string, locale?: string): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Re-read a just-written row through the public projection.
|
|
180
|
+
*
|
|
181
|
+
* `status: 'any'` because the row is usually a draft — the published-only
|
|
182
|
+
* default would 404 the thing that was just created. The locale is carried
|
|
183
|
+
* through so a sibling translation resolves to itself rather than to the
|
|
184
|
+
* default-locale row of its group.
|
|
185
|
+
*/
|
|
186
|
+
private readBack;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=public-entry-writes.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-entry-writes.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/public-api/infrastructure/public-entry-writes.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,+DAA+D,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAE,MAAM,4DAA4D,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,8DAA8D,CAAC;AACrG,OAAO,EAAE,yBAAyB,EAAE,MAAM,mEAAmE,CAAC;AAC9G,OAAO,EAAE,2BAA2B,EAAE,MAAM,qEAAqE,CAAC;AAClH,OAAO,KAAK,EACR,gBAAgB,EAChB,iBAAiB,EACpB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAKH,KAAK,oBAAoB,EAC5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBACa,wBAAwB;IAE7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB;gBALpB,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,kBAAkB,EAC3B,YAAY,EAAE,mBAAmB,EACjC,cAAc,EAAE,qBAAqB,EACrC,kBAAkB,EAAE,yBAAyB,EAC7C,oBAAoB,EAAE,2BAA2B;IAGtE;;;;;;;;OAQG;IACG,MAAM,CACR,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,kBAAkB,EACxB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,WAAW,CAAC;IAkBvB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,MAAM,CACR,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,kBAAkB,EACxB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,EACjC,MAAM,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC;IAwBvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,QAAQ,CACV,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,SAAS,qBAAqB,EAAE,EACvC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,oBAAoB,CAAC;IAoDhC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,YAAY;IAqBpB;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CACP,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC;IAI7B;;;;;;;;OAQG;IACH,aAAa,CACT,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC;IAI5B;;;;;;;OAOG;IACH,UAAU,CACN,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC;IAI5B,yEAAyE;IACnE,OAAO,CACT,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,EACjC,MAAM,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC;IAYvB,+DAA+D;IACzD,SAAS,CACX,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,EACjC,MAAM,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC;IAYvB;;;;;;;;OAQG;IACG,MAAM,CACR,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAUhB;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ;CAenB"}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PublicEntryWritesService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const entry_row_1 = require("../../entries/infrastructure/persistence/entry-row");
|
|
7
|
+
const entry_writer_service_1 = require("../../entries/infrastructure/persistence/entry-writer.service");
|
|
8
|
+
const publish_entry_use_case_1 = require("../../entries/application/use-cases/publish-entry.use-case");
|
|
9
|
+
const unpublish_entry_use_case_1 = require("../../entries/application/use-cases/unpublish-entry.use-case");
|
|
10
|
+
const bulk_publish_entries_use_case_1 = require("../../entries/application/use-cases/bulk-publish-entries.use-case");
|
|
11
|
+
const bulk_unpublish_entries_use_case_1 = require("../../entries/application/use-cases/bulk-unpublish-entries.use-case");
|
|
12
|
+
const public_bulk_1 = require("../types/public-bulk");
|
|
13
|
+
const bulk_save_op_1 = require("./bulk-save-op");
|
|
14
|
+
const public_entries_query_1 = require("./public-entries.query");
|
|
15
|
+
/**
|
|
16
|
+
* The write side of the public content API.
|
|
17
|
+
*
|
|
18
|
+
* **A thin edge over the admin's write pipeline, deliberately** — the opposite
|
|
19
|
+
* call from the read side, and for the opposite reason. The read stated its own
|
|
20
|
+
* WHERE because `EntriesService`'s knobs select rows a token must never see, so
|
|
21
|
+
* reaching through it would have made the visibility rule an argument about what
|
|
22
|
+
* was *not* passed. A write has no such hazard and a great deal more to get
|
|
23
|
+
* right: `EntryWriterService` already owns field validation, relation-delta
|
|
24
|
+
* application, media-target checks, revision numbering serialized by a per-entry
|
|
25
|
+
* advisory lock, the transactional outbox, and the i18n extension's sibling sync
|
|
26
|
+
* (which demotes rewritten published translations and re-validates them). A
|
|
27
|
+
* second implementation of any of that is how you get duplicate version numbers
|
|
28
|
+
* and translations that quietly drift. So this class adds exactly three things
|
|
29
|
+
* the writer does not know about — the locator, the grant gate (in the
|
|
30
|
+
* controller), and the public wire shape — and delegates the rest.
|
|
31
|
+
*
|
|
32
|
+
* **Responses are re-read through the public projection** rather than mapped
|
|
33
|
+
* from the writer's `EntryRecord`. It costs one SELECT per write and buys the
|
|
34
|
+
* guarantee that what a write returns is byte-identical in shape to what a read
|
|
35
|
+
* returns — including the reference fields the public `values` omits, which a
|
|
36
|
+
* hand-written mapper would have to remember to strip every time the field
|
|
37
|
+
* vocabulary grows.
|
|
38
|
+
*
|
|
39
|
+
* **No `OriginGuard`.** The admin's write controllers carry it because they are
|
|
40
|
+
* cookie-authenticated and therefore CSRF-able. A bearer token is not sent
|
|
41
|
+
* ambiently by a browser, so there is nothing to forge here — and requiring an
|
|
42
|
+
* `Origin` header would break every non-browser client, which is all of them.
|
|
43
|
+
*/
|
|
44
|
+
let PublicEntryWritesService = class PublicEntryWritesService {
|
|
45
|
+
writer;
|
|
46
|
+
entries;
|
|
47
|
+
publishEntry;
|
|
48
|
+
unpublishEntry;
|
|
49
|
+
bulkPublishEntries;
|
|
50
|
+
bulkUnpublishEntries;
|
|
51
|
+
constructor(writer, entries, publishEntry, unpublishEntry, bulkPublishEntries, bulkUnpublishEntries) {
|
|
52
|
+
this.writer = writer;
|
|
53
|
+
this.entries = entries;
|
|
54
|
+
this.publishEntry = publishEntry;
|
|
55
|
+
this.unpublishEntry = unpublishEntry;
|
|
56
|
+
this.bulkPublishEntries = bulkPublishEntries;
|
|
57
|
+
this.bulkUnpublishEntries = bulkUnpublishEntries;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create an entry. It lands as a **draft** on publishable types — the same
|
|
61
|
+
* as the editor's "new record", and the reason publish is its own call: a
|
|
62
|
+
* create that went live immediately would have no reviewable state.
|
|
63
|
+
*
|
|
64
|
+
* With `localeGroupId` the row joins an existing translation group instead
|
|
65
|
+
* of starting one, which is how a new locale of an existing record is
|
|
66
|
+
* written.
|
|
67
|
+
*/
|
|
68
|
+
async create(type, body, workspaceId, grantedTypes) {
|
|
69
|
+
const record = await this.writer.create(type, body.values, workspaceId, body.relations, body.locale, body.localeGroupId,
|
|
70
|
+
// Revisions record a *user* id, and a token is not a user. Writing
|
|
71
|
+
// the token id into that column would make the history resolve it to
|
|
72
|
+
// a nonexistent person; null is the honest answer for "not a user".
|
|
73
|
+
// Attributing a token's writes is a real gap — it wants its own
|
|
74
|
+
// column, not a misused one.
|
|
75
|
+
null);
|
|
76
|
+
return this.readBack(type, record.id, workspaceId, grantedTypes, body);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Update an entry's values and apply its relation deltas. On a publishable
|
|
80
|
+
* type this moves a published row back to `draft` while its published
|
|
81
|
+
* version stays live — the admin's **Modified** state — so an edit never
|
|
82
|
+
* silently changes what the world is reading.
|
|
83
|
+
*
|
|
84
|
+
* **A genuine partial update**, and this is a deliberate divergence from the
|
|
85
|
+
* admin's `PATCH`, which replaces the whole values bag because the editor
|
|
86
|
+
* always submits the full document. An API client does not: it sends the two
|
|
87
|
+
* fields it changed. Under replace semantics that silently nulls everything
|
|
88
|
+
* else — and since `required` only bites at publish time, the damage is
|
|
89
|
+
* invisible until a later publish fails on fields the caller never touched.
|
|
90
|
+
* A method named PATCH that destroys unsent data is a data-loss footgun, so
|
|
91
|
+
* the stored values are merged under the submitted ones here.
|
|
92
|
+
*
|
|
93
|
+
* Merge is **by key presence**, so nulling a field explicitly still works:
|
|
94
|
+
* `{"excerpt": null}` clears it, while omitting `excerpt` leaves it alone.
|
|
95
|
+
*
|
|
96
|
+
* `locale` here is the **addressing** locale — the query string's, which
|
|
97
|
+
* picks the row out of a translation group. It is deliberately not
|
|
98
|
+
* `body.locale`: that field is create-only (it stamps a new row's locale),
|
|
99
|
+
* and reading it here would let a body that omits it silently retarget a
|
|
100
|
+
* group write at the default-locale row. Which is exactly what it did, until
|
|
101
|
+
* a live check caught a German update rewriting the English article.
|
|
102
|
+
*/
|
|
103
|
+
async update(type, locator, body, workspaceId, grantedTypes, locale) {
|
|
104
|
+
const row = await this.entries.resolveWritableRow(type, locator, workspaceId, locale);
|
|
105
|
+
const id = row['id'];
|
|
106
|
+
await this.writer.update(type, id,
|
|
107
|
+
// `toRecord` yields the stored column-backed values — scalars, media
|
|
108
|
+
// ids, and owning single-relation FKs. Join-backed relations have no
|
|
109
|
+
// column and are absent from it, which is correct: those move only
|
|
110
|
+
// through `relations` deltas, and a delta left unsent changes
|
|
111
|
+
// nothing by construction.
|
|
112
|
+
this.mergedValues(type, row, body), workspaceId, body.relations, null);
|
|
113
|
+
return this.readBack(type, id, workspaceId, grantedTypes, { locale });
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Save many entries of one type in a single request — the batch form of
|
|
117
|
+
* {@link create} and {@link update}, and the reason it is one method rather
|
|
118
|
+
* than two: an import is a list of records, some of which the caller already
|
|
119
|
+
* has and some of which it does not, and splitting that into two calls would
|
|
120
|
+
* make the client sort them first.
|
|
121
|
+
*
|
|
122
|
+
* **Each item is dispatched to the very same `create` / `update` above.**
|
|
123
|
+
* Nothing about what a write means is restated here — validation, relation
|
|
124
|
+
* deltas, media checks, revision numbering, the outbox, i18n's sibling sync
|
|
125
|
+
* all come from the one pipeline, so "saved in bulk" and "saved one at a
|
|
126
|
+
* time" cannot mean different things.
|
|
127
|
+
*
|
|
128
|
+
* **Partial success, item by item.** The items are written one transaction
|
|
129
|
+
* at a time (`EntryWriterService` opens its own per write, taking the
|
|
130
|
+
* workspace's shared content lock), so there is no batch to roll back even
|
|
131
|
+
* in principle — and rejecting forty-nine good entries because the fiftieth
|
|
132
|
+
* names a relation that does not exist would defeat the point of the
|
|
133
|
+
* endpoint. Every item therefore gets a verdict, failures carry the status
|
|
134
|
+
* and message their single-entry call would have produced, and the caller
|
|
135
|
+
* retries the ones it can fix. This is the same contract the bulk-publish
|
|
136
|
+
* commit already reports through its `skipped` list.
|
|
137
|
+
*
|
|
138
|
+
* **Written in order, one after another** rather than concurrently. Every
|
|
139
|
+
* write takes the workspace's shared content lock and (on a localized type)
|
|
140
|
+
* an advisory lock over the translation group, so a batch touching siblings
|
|
141
|
+
* would spend a fan-out contending with itself; sequential also makes the
|
|
142
|
+
* per-item results deterministic, which a caller diffing two runs relies on.
|
|
143
|
+
*/
|
|
144
|
+
async bulkSave(type, items, workspaceId, grantedTypes) {
|
|
145
|
+
const results = [];
|
|
146
|
+
for (const [index, item] of items.entries()) {
|
|
147
|
+
// Resolved inside the loop, and inside the try: a malformed
|
|
148
|
+
// locator is this item's problem, not the request's. Failing the
|
|
149
|
+
// whole call on it would make one mis-addressed row cost every
|
|
150
|
+
// other row's write — the very thing the per-item contract exists
|
|
151
|
+
// to prevent.
|
|
152
|
+
let op = public_bulk_1.BULK_SAVE_OP.Create;
|
|
153
|
+
try {
|
|
154
|
+
const resolved = (0, bulk_save_op_1.resolveBulkSaveOp)(item);
|
|
155
|
+
op = resolved.op;
|
|
156
|
+
const entry = resolved.op === public_bulk_1.BULK_SAVE_OP.Create
|
|
157
|
+
? await this.create(type, item, workspaceId, grantedTypes)
|
|
158
|
+
: await this.update(type, resolved.locator, item, workspaceId, grantedTypes, resolved.locale);
|
|
159
|
+
results.push({ index, op: resolved.op, ok: true, entry });
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
results.push({
|
|
163
|
+
index,
|
|
164
|
+
op,
|
|
165
|
+
ok: false,
|
|
166
|
+
error: toBulkSaveError(error)
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
items: results,
|
|
172
|
+
created: results.filter((result) => result.ok && result.op === public_bulk_1.BULK_SAVE_OP.Create).length,
|
|
173
|
+
updated: results.filter((result) => result.ok && result.op === public_bulk_1.BULK_SAVE_OP.Update).length,
|
|
174
|
+
failed: results.filter((result) => !result.ok).length
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The values bag an update submits: the stored values with the caller's
|
|
179
|
+
* merged over them (see {@link update}), minus any single relation the
|
|
180
|
+
* caller is setting through `relations`.
|
|
181
|
+
*
|
|
182
|
+
* That subtraction is what makes the two channels coexist. `toRecord`
|
|
183
|
+
* re-supplies every column-backed field including single-relation FKs, so
|
|
184
|
+
* without it a `relations: { author: { set } }` would always arrive
|
|
185
|
+
* alongside a stored `values.author` and be rejected as "sent in both
|
|
186
|
+
* places" — a collision this method created rather than the caller.
|
|
187
|
+
*/
|
|
188
|
+
mergedValues(type, row, body) {
|
|
189
|
+
const merged = { ...(0, entry_row_1.toRecord)(type, row).values, ...body.values };
|
|
190
|
+
for (const [field, delta] of Object.entries(body.relations ?? {})) {
|
|
191
|
+
// Only the STORED value is dropped. A field the caller put in both
|
|
192
|
+
// bags themselves stays, so the writer still sees the contradiction
|
|
193
|
+
// and rejects it — the point is to hide this method's own
|
|
194
|
+
// re-supplied value, not to paper over an ambiguous request.
|
|
195
|
+
if (delta.set !== undefined &&
|
|
196
|
+
!Object.hasOwn(body.values ?? {}, field)) {
|
|
197
|
+
delete merged[field];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return merged;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Publish many entries at once.
|
|
204
|
+
*
|
|
205
|
+
* **Delegates to `BulkPublishEntriesUseCase` — the admin's own** — rather
|
|
206
|
+
* than looping over {@link publish}. That use-case selects the candidate
|
|
207
|
+
* rows `FOR UPDATE` and re-validates them inside **one** transaction, which
|
|
208
|
+
* is the only thing that closes the window between "this draft validates"
|
|
209
|
+
* and "publish it"; a loop of single publishes would reopen it once per id.
|
|
210
|
+
* It also reports the ids it skipped and why (already published, blocked by
|
|
211
|
+
* validation, not found in this workspace), so a partial batch is legible
|
|
212
|
+
* rather than a flat count.
|
|
213
|
+
*
|
|
214
|
+
* The grant gate has already run in the caller, and the use-case is
|
|
215
|
+
* workspace-scoped, so an id from another workspace is reported as
|
|
216
|
+
* `not-found` — the same answer an id that never existed gets.
|
|
217
|
+
*/
|
|
218
|
+
bulkPublish(type, ids, workspaceId) {
|
|
219
|
+
return this.bulkPublishEntries.execute(type, ids, workspaceId);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Take many entries off the air, reverting each to a draft.
|
|
223
|
+
*
|
|
224
|
+
* `{ count }` rather than a per-id verdict, because unpublishing has no
|
|
225
|
+
* gate to fail: an id that is not a live published row of this workspace is
|
|
226
|
+
* simply not one of the rows that changed. The count is the number that
|
|
227
|
+
* actually transitioned, and one `entry.unpublished` event is emitted per
|
|
228
|
+
* transition — never per submitted id.
|
|
229
|
+
*/
|
|
230
|
+
bulkUnpublish(type, ids, workspaceId) {
|
|
231
|
+
return this.bulkUnpublishEntries.execute(type, ids, workspaceId);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Remove many entries — soft-deleting on a `paranoid` type (recoverable
|
|
235
|
+
* from the admin's trash) and hard-deleting otherwise, exactly as
|
|
236
|
+
* {@link remove} does for one.
|
|
237
|
+
*
|
|
238
|
+
* Rows, not records: on a localized type this deletes the listed locales
|
|
239
|
+
* and leaves their siblings live.
|
|
240
|
+
*/
|
|
241
|
+
bulkRemove(type, ids, workspaceId) {
|
|
242
|
+
return this.writer.bulkRemove(type, ids, workspaceId);
|
|
243
|
+
}
|
|
244
|
+
/** Take an entry live. 422 when the stored draft no longer validates. */
|
|
245
|
+
async publish(type, locator, workspaceId, grantedTypes, locale) {
|
|
246
|
+
const row = await this.entries.resolveWritableRow(type, locator, workspaceId, locale);
|
|
247
|
+
const id = row['id'];
|
|
248
|
+
await this.publishEntry.execute(type, id, workspaceId);
|
|
249
|
+
return this.readBack(type, id, workspaceId, grantedTypes, { locale });
|
|
250
|
+
}
|
|
251
|
+
/** Take an entry back off the air, reverting it to a draft. */
|
|
252
|
+
async unpublish(type, locator, workspaceId, grantedTypes, locale) {
|
|
253
|
+
const row = await this.entries.resolveWritableRow(type, locator, workspaceId, locale);
|
|
254
|
+
const id = row['id'];
|
|
255
|
+
await this.unpublishEntry.execute(type, id, workspaceId);
|
|
256
|
+
return this.readBack(type, id, workspaceId, grantedTypes, { locale });
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Remove an entry — soft delete on a `paranoid` type (recoverable from the
|
|
260
|
+
* admin's trash), a hard delete otherwise. Either way it leaves the public
|
|
261
|
+
* API immediately.
|
|
262
|
+
*
|
|
263
|
+
* Only **this** row: a translation is deleted on its own, so removing the
|
|
264
|
+
* German article leaves the English one live. Deleting a whole group means
|
|
265
|
+
* deleting each locale.
|
|
266
|
+
*/
|
|
267
|
+
async remove(type, locator, workspaceId, locale) {
|
|
268
|
+
const row = await this.entries.resolveWritableRow(type, locator, workspaceId, locale);
|
|
269
|
+
await this.writer.remove(type, row['id'], workspaceId);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Re-read a just-written row through the public projection.
|
|
273
|
+
*
|
|
274
|
+
* `status: 'any'` because the row is usually a draft — the published-only
|
|
275
|
+
* default would 404 the thing that was just created. The locale is carried
|
|
276
|
+
* through so a sibling translation resolves to itself rather than to the
|
|
277
|
+
* default-locale row of its group.
|
|
278
|
+
*/
|
|
279
|
+
readBack(type, id, workspaceId, grantedTypes, source) {
|
|
280
|
+
return this.entries.getOne(type, { id }, workspaceId, { locale: source.locale, status: 'any' }, grantedTypes);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
exports.PublicEntryWritesService = PublicEntryWritesService;
|
|
284
|
+
exports.PublicEntryWritesService = PublicEntryWritesService = tslib_1.__decorate([
|
|
285
|
+
(0, common_1.Injectable)(),
|
|
286
|
+
tslib_1.__metadata("design:paramtypes", [entry_writer_service_1.EntryWriterService,
|
|
287
|
+
public_entries_query_1.PublicEntriesQuery,
|
|
288
|
+
publish_entry_use_case_1.PublishEntryUseCase,
|
|
289
|
+
unpublish_entry_use_case_1.UnpublishEntryUseCase,
|
|
290
|
+
bulk_publish_entries_use_case_1.BulkPublishEntriesUseCase,
|
|
291
|
+
bulk_unpublish_entries_use_case_1.BulkUnpublishEntriesUseCase])
|
|
292
|
+
], PublicEntryWritesService);
|
|
293
|
+
/**
|
|
294
|
+
* One item's failure, in the vocabulary the single-entry routes already speak.
|
|
295
|
+
*
|
|
296
|
+
* A Nest `HttpException` carries both halves of what the caller needs — the
|
|
297
|
+
* status the same write would have failed with, and the body it would have been
|
|
298
|
+
* given, `issues` and all — so a 422's per-field detail survives being one row
|
|
299
|
+
* of a batch instead of being flattened to a sentence. Anything else is a bug
|
|
300
|
+
* rather than a rejection, and is reported as a 500 with its message so the item
|
|
301
|
+
* is still diagnosable; the batch keeps going either way, because one item
|
|
302
|
+
* throwing unexpectedly is not a reason to abandon the rest.
|
|
303
|
+
*/
|
|
304
|
+
function toBulkSaveError(error) {
|
|
305
|
+
if (error instanceof common_1.HttpException) {
|
|
306
|
+
const response = error.getResponse();
|
|
307
|
+
const body = typeof response === 'object' && response !== null
|
|
308
|
+
? response
|
|
309
|
+
: {};
|
|
310
|
+
const message = body['message'];
|
|
311
|
+
return {
|
|
312
|
+
status: error.getStatus(),
|
|
313
|
+
message: typeof message === 'string' ? message : error.message,
|
|
314
|
+
...(body['issues'] === undefined ? {} : { issues: body['issues'] })
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
status: common_1.HttpStatus.INTERNAL_SERVER_ERROR,
|
|
319
|
+
message: error instanceof Error ? error.message : String(error)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { type Database } from '@orthacms/database';
|
|
2
|
+
import { type AnyContentType } from '../../types/content-type';
|
|
3
|
+
import { type MediaAssetResolver } from '../../extension/media-asset-resolver';
|
|
4
|
+
import { RelationLinkService } from '../../entries/infrastructure/persistence/relation-link.service';
|
|
5
|
+
import type { PublicMediaFieldView, PublicRelationFieldView } from '../types/public-expansion';
|
|
6
|
+
/** A generated content row seen as a bag of values by property name. */
|
|
7
|
+
type Row = Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* Upper bound on how many fields one request may expand, per kind. The cost of
|
|
10
|
+
* a preview scales with the number of **fields**, not rows (each relation field
|
|
11
|
+
* is one or two queries spanning the whole page), so this is the knob that
|
|
12
|
+
* actually bounds a request — the admin caps only the raw string length, which
|
|
13
|
+
* on a public endpoint would leave a type with a dozen relations open to a
|
|
14
|
+
* request asking for all of them.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_EXPANDED_FIELDS = 10;
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the public API's opt-in **relation** and **media** expansions for a
|
|
19
|
+
* page of entries.
|
|
20
|
+
*
|
|
21
|
+
* Both are batched across the whole page — one bounded set of queries per
|
|
22
|
+
* *field*, never one per row. Relations delegate to
|
|
23
|
+
* `RelationLinkService.previewForEntries` (windowed `row_number()` for the cap,
|
|
24
|
+
* `count(*) over` for the total, batched title resolution). Media deliberately
|
|
25
|
+
* does **not** reuse `MediaRefsQuery.forValues`: that method takes one entry's
|
|
26
|
+
* values, so calling it per row would be exactly the N+1 the relation preview
|
|
27
|
+
* was built to avoid. Instead every media id on the page is collected and
|
|
28
|
+
* resolved in a single batched lookup.
|
|
29
|
+
*/
|
|
30
|
+
export declare class PublicExpansionQuery {
|
|
31
|
+
private readonly db;
|
|
32
|
+
private readonly relationLinks;
|
|
33
|
+
private readonly media?;
|
|
34
|
+
constructor(db: Database, relationLinks: RelationLinkService, media?: MediaAssetResolver | undefined);
|
|
35
|
+
/**
|
|
36
|
+
* Validate a `?relationFields=` list: every name must be a relation field
|
|
37
|
+
* on the type, and its target must be a content type the workspace was
|
|
38
|
+
* granted. An ungranted target is a **400**, matching how `?filter=` treats
|
|
39
|
+
* a traversal into one — a public caller should not be able to expand into
|
|
40
|
+
* content the workspace does not expose, and saying so beats silently
|
|
41
|
+
* returning an empty field.
|
|
42
|
+
*/
|
|
43
|
+
parseRelationFields(type: AnyContentType, raw: string | undefined, granted: ReadonlySet<string>): string[];
|
|
44
|
+
/** Validate a `?mediaFields=` list: every name must be a media field. */
|
|
45
|
+
parseMediaFields(type: AnyContentType, raw: string | undefined): string[];
|
|
46
|
+
/**
|
|
47
|
+
* One capped page of links per requested relation field, for every row on
|
|
48
|
+
* the page, keyed by entry id.
|
|
49
|
+
*
|
|
50
|
+
* `publishedOnly` is the public difference: a draft target is neither shown
|
|
51
|
+
* nor counted, so `total` is the number of links a caller can actually
|
|
52
|
+
* reach.
|
|
53
|
+
*/
|
|
54
|
+
relationsForRows(type: AnyContentType, fields: string[], rows: Row[], workspaceId: string, limit?: number): Promise<Map<string, Record<string, PublicRelationFieldView>>>;
|
|
55
|
+
/**
|
|
56
|
+
* Hydrate already-read link views (the `/relations` sibling routes' output
|
|
57
|
+
* from `readAll` / `readField`) into the same full-entry shape the list
|
|
58
|
+
* preview returns, so both surfaces speak one contract.
|
|
59
|
+
*/
|
|
60
|
+
hydrateRelationViews(type: AnyContentType, views: Record<string, {
|
|
61
|
+
items: {
|
|
62
|
+
id: string;
|
|
63
|
+
}[];
|
|
64
|
+
total: number;
|
|
65
|
+
}>, workspaceId: string): Promise<Record<string, PublicRelationFieldView>>;
|
|
66
|
+
/**
|
|
67
|
+
* Load every linked entry named by a preview, keyed by field and then by
|
|
68
|
+
* id. One `IN (…)` read per relation field's target type, projected through
|
|
69
|
+
* the same {@link toPublicEntry} the entry routes use — so a linked record
|
|
70
|
+
* has exactly the shape a consumer already handles.
|
|
71
|
+
*
|
|
72
|
+
* Linked entries are returned **unexpanded**: nothing here recurses, which
|
|
73
|
+
* is what bounds a request to one level of the graph.
|
|
74
|
+
*/
|
|
75
|
+
private linkedEntriesById;
|
|
76
|
+
/**
|
|
77
|
+
* The assets of each requested media field, for every row on the page,
|
|
78
|
+
* keyed by entry id. One batched resolver lookup for the whole page.
|
|
79
|
+
*
|
|
80
|
+
* A no-op when no media plugin is registered (nothing binds the resolver),
|
|
81
|
+
* exactly like the admin's media read — content boots without media.
|
|
82
|
+
*/
|
|
83
|
+
mediaForRows(type: AnyContentType, fields: string[], rows: Row[], workspaceId: string, limit?: number): Promise<Map<string, Record<string, PublicMediaFieldView>>>;
|
|
84
|
+
/**
|
|
85
|
+
* Every field of one kind — what `?relations=preview` / `?media=preview`
|
|
86
|
+
* expand when the caller names no fields.
|
|
87
|
+
*
|
|
88
|
+
* A type with more expandable fields than the cap is a **400** telling the
|
|
89
|
+
* caller to name the ones they want, rather than a silently truncated
|
|
90
|
+
* response: quietly dropping fields would read as "this entry has no
|
|
91
|
+
* links" and be near-impossible to notice.
|
|
92
|
+
*/
|
|
93
|
+
private allFieldsOfKind;
|
|
94
|
+
/** Shared parse: split, trim, de-duplicate, bound, then per-kind checks. */
|
|
95
|
+
private parseFields;
|
|
96
|
+
}
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=public-expansion.query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-expansion.query.d.ts","sourceRoot":"","sources":["../../../../src/lib/public-api/infrastructure/public-expansion.query.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE7E,OAAO,EAEH,KAAK,kBAAkB,EAC1B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gEAAgE,CAAC;AAErG,OAAO,KAAK,EACR,oBAAoB,EAEpB,uBAAuB,EAC1B,MAAM,2BAA2B,CAAC;AAInC,wEAAwE;AACxE,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAKnC;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAwCtC;;;;;;;;;;;;GAYG;AACH,qBACa,oBAAoB;IAEP,OAAO,CAAC,QAAQ,CAAC,EAAE;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAG9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAJY,EAAE,EAAE,QAAQ,EAC9B,aAAa,EAAE,mBAAmB,EAGlC,KAAK,CAAC,EAAE,kBAAkB,YAAA;IAG/C;;;;;;;OAOG;IACH,mBAAmB,CACf,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC7B,MAAM,EAAE;IA8BX,yEAAyE;IACzE,gBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE;IAiBzE;;;;;;;OAOG;IACG,gBAAgB,CAClB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,GAAG,EAAE,EACX,WAAW,EAAE,MAAM,EACnB,KAAK,SAA0B,GAChC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAyChE;;;;OAIG;IACG,oBAAoB,CACtB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EACjE,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IAqBnD;;;;;;;;OAQG;YACW,iBAAiB;IAsD/B;;;;;;OAMG;IACG,YAAY,CACd,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,GAAG,EAAE,EACX,WAAW,EAAE,MAAM,EACnB,KAAK,SAA0B,GAChC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;IA+F7D;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAgBvB,4EAA4E;IAC5E,OAAO,CAAC,WAAW;CA+BtB"}
|