@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.
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +41 -1
- package/dist/lib/content.module.d.ts.map +1 -1
- package/dist/lib/content.module.js +31 -1
- package/dist/lib/copilot/entry-proposal.applier.d.ts.map +1 -1
- package/dist/lib/copilot/entry-proposal.applier.js +5 -4
- package/dist/lib/docs/describe-content-api.d.ts +7 -0
- package/dist/lib/docs/describe-content-api.d.ts.map +1 -1
- package/dist/lib/docs/describe-content-api.js +177 -53
- package/dist/lib/docs/describe-views-api.d.ts +19 -0
- package/dist/lib/docs/describe-views-api.d.ts.map +1 -0
- package/dist/lib/docs/describe-views-api.js +76 -0
- package/dist/lib/docs/field-schema.d.ts.map +1 -1
- package/dist/lib/docs/field-schema.js +36 -2
- package/dist/lib/docs/openapi-writer.d.ts +49 -0
- package/dist/lib/docs/openapi-writer.d.ts.map +1 -0
- package/dist/lib/docs/openapi-writer.js +76 -0
- package/dist/lib/docs/public-api-schemas.d.ts +45 -0
- package/dist/lib/docs/public-api-schemas.d.ts.map +1 -0
- package/dist/lib/docs/public-api-schemas.js +377 -0
- package/dist/lib/docs/views-schemas.d.ts +22 -0
- package/dist/lib/docs/views-schemas.d.ts.map +1 -0
- package/dist/lib/docs/views-schemas.js +156 -0
- package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.d.ts +8 -2
- package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.d.ts.map +1 -1
- package/dist/lib/entries/application/use-cases/bulk-publish-entries.use-case.js +19 -4
- package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.d.ts +2 -2
- package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.d.ts.map +1 -1
- package/dist/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.js +14 -4
- package/dist/lib/entries/application/use-cases/publish-entry.use-case.d.ts +6 -8
- package/dist/lib/entries/application/use-cases/publish-entry.use-case.d.ts.map +1 -1
- package/dist/lib/entries/application/use-cases/publish-entry.use-case.js +10 -4
- package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.d.ts +2 -5
- package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.d.ts.map +1 -1
- package/dist/lib/entries/application/use-cases/unpublish-entry.use-case.js +6 -1
- package/dist/lib/entries/domain/entry.d.ts +8 -1
- package/dist/lib/entries/domain/entry.d.ts.map +1 -1
- package/dist/lib/entries/domain/entry.js +12 -12
- package/dist/lib/entries/domain/events/entry-events.d.ts +47 -5
- package/dist/lib/entries/domain/events/entry-events.d.ts.map +1 -1
- package/dist/lib/entries/domain/events/entry-events.js +26 -10
- package/dist/lib/entries/entries.constants.d.ts +10 -2
- package/dist/lib/entries/entries.constants.d.ts.map +1 -1
- package/dist/lib/entries/entries.constants.js +11 -2
- package/dist/lib/entries/http/controllers/bulk-entries.controller.d.ts +2 -2
- package/dist/lib/entries/http/controllers/bulk-entries.controller.d.ts.map +1 -1
- package/dist/lib/entries/http/controllers/bulk-entries.controller.js +8 -6
- package/dist/lib/entries/http/controllers/create-entry.controller.d.ts.map +1 -1
- package/dist/lib/entries/http/controllers/create-entry.controller.js +1 -1
- package/dist/lib/entries/http/controllers/update-entry.controller.d.ts.map +1 -1
- package/dist/lib/entries/http/controllers/update-entry.controller.js +1 -1
- package/dist/lib/entries/http/dto/extension-bag.validator.d.ts +14 -0
- package/dist/lib/entries/http/dto/extension-bag.validator.d.ts.map +1 -0
- package/dist/lib/entries/http/dto/extension-bag.validator.js +42 -0
- package/dist/lib/entries/http/dto/save-entry.dto.d.ts +16 -0
- package/dist/lib/entries/http/dto/save-entry.dto.d.ts.map +1 -1
- package/dist/lib/entries/http/dto/save-entry.dto.js +30 -0
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts +50 -8
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts.map +1 -1
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.js +183 -32
- package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts +38 -5
- package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts.map +1 -1
- package/dist/lib/entries/infrastructure/persistence/relation-link.service.js +38 -6
- package/dist/lib/entries/infrastructure/queries/entries.service.d.ts +3 -1
- package/dist/lib/entries/infrastructure/queries/entries.service.d.ts.map +1 -1
- package/dist/lib/entries/infrastructure/queries/entries.service.js +17 -5
- package/dist/lib/entries/infrastructure/queries/entry-match.query.d.ts +82 -0
- package/dist/lib/entries/infrastructure/queries/entry-match.query.d.ts.map +1 -0
- package/dist/lib/entries/infrastructure/queries/entry-match.query.js +152 -0
- package/dist/lib/extension/entry-filter-provider.d.ts +79 -0
- package/dist/lib/extension/entry-filter-provider.d.ts.map +1 -0
- package/dist/lib/extension/entry-filter-provider.js +149 -0
- package/dist/lib/extension/entry-write-extension.d.ts +185 -0
- package/dist/lib/extension/entry-write-extension.d.ts.map +1 -0
- package/dist/lib/extension/entry-write-extension.js +182 -0
- package/dist/lib/extension/read-scope.d.ts +107 -0
- package/dist/lib/extension/read-scope.d.ts.map +1 -0
- package/dist/lib/extension/read-scope.js +131 -0
- package/dist/lib/insights/docs/describe-content-insights-api.d.ts +23 -0
- package/dist/lib/insights/docs/describe-content-insights-api.d.ts.map +1 -0
- package/dist/lib/insights/docs/describe-content-insights-api.js +329 -0
- package/dist/lib/mcp/content-tools.provider.d.ts.map +1 -1
- package/dist/lib/mcp/content-tools.provider.js +10 -9
- package/dist/lib/public-api/http/controllers/public-entry-writes.controller.d.ts +14 -13
- package/dist/lib/public-api/http/controllers/public-entry-writes.controller.d.ts.map +1 -1
- package/dist/lib/public-api/http/controllers/public-entry-writes.controller.js +62 -46
- package/dist/lib/public-api/http/guards/api-token-workspace.guard.d.ts.map +1 -1
- package/dist/lib/public-api/http/guards/api-token-workspace.guard.js +8 -0
- package/dist/lib/public-api/infrastructure/public-entries.query.d.ts +14 -1
- package/dist/lib/public-api/infrastructure/public-entries.query.d.ts.map +1 -1
- package/dist/lib/public-api/infrastructure/public-entries.query.js +20 -3
- package/dist/lib/public-api/infrastructure/public-entry-writes.service.d.ts +10 -9
- package/dist/lib/public-api/infrastructure/public-entry-writes.service.d.ts.map +1 -1
- package/dist/lib/public-api/infrastructure/public-entry-writes.service.js +23 -24
- package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts +16 -4
- package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts.map +1 -1
- package/dist/lib/public-api/infrastructure/public-expansion.query.js +29 -9
- package/dist/lib/public-api/infrastructure/token-actor.d.ts +37 -0
- package/dist/lib/public-api/infrastructure/token-actor.d.ts.map +1 -0
- package/dist/lib/public-api/infrastructure/token-actor.js +58 -0
- package/dist/lib/revisions/application/use-cases/restore-revision.use-case.d.ts.map +1 -1
- package/dist/lib/revisions/application/use-cases/restore-revision.use-case.js +23 -1
- package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts +7 -1
- package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts.map +1 -1
- package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.js +9 -2
- package/dist/lib/revisions/infrastructure/purge/revisions-workspace.purger.d.ts +28 -0
- package/dist/lib/revisions/infrastructure/purge/revisions-workspace.purger.d.ts.map +1 -0
- package/dist/lib/revisions/infrastructure/purge/revisions-workspace.purger.js +52 -0
- package/dist/lib/revisions/types/revision-view.d.ts +11 -0
- package/dist/lib/revisions/types/revision-view.d.ts.map +1 -1
- package/dist/lib/utils/content-plugin.d.ts.map +1 -1
- package/dist/lib/utils/content-plugin.js +10 -1
- package/dist/lib/utils/content-views-plugin.d.ts +28 -0
- package/dist/lib/utils/content-views-plugin.d.ts.map +1 -0
- package/dist/lib/utils/content-views-plugin.js +37 -0
- package/dist/lib/views/application/queries/saved-views.query.d.ts +16 -0
- package/dist/lib/views/application/queries/saved-views.query.d.ts.map +1 -0
- package/dist/lib/views/application/queries/saved-views.query.js +46 -0
- package/dist/lib/views/application/saved-view-access.service.d.ts +30 -0
- package/dist/lib/views/application/saved-view-access.service.d.ts.map +1 -0
- package/dist/lib/views/application/saved-view-access.service.js +55 -0
- package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts +30 -0
- package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts.map +1 -0
- package/dist/lib/views/application/use-cases/create-saved-view.use-case.js +81 -0
- package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts +18 -0
- package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts.map +1 -0
- package/dist/lib/views/application/use-cases/delete-saved-view.use-case.js +56 -0
- package/dist/lib/views/application/use-cases/set-default-view.use-case.d.ts +23 -0
- package/dist/lib/views/application/use-cases/set-default-view.use-case.d.ts.map +1 -0
- package/dist/lib/views/application/use-cases/set-default-view.use-case.js +51 -0
- package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts +24 -0
- package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts.map +1 -0
- package/dist/lib/views/application/use-cases/update-saved-view.use-case.js +90 -0
- package/dist/lib/views/application/view-scope.service.d.ts +25 -0
- package/dist/lib/views/application/view-scope.service.d.ts.map +1 -0
- package/dist/lib/views/application/view-scope.service.js +45 -0
- package/dist/lib/views/content-views.module.d.ts +22 -0
- package/dist/lib/views/content-views.module.d.ts.map +1 -0
- package/dist/lib/views/content-views.module.js +63 -0
- package/dist/lib/views/domain/errors/index.d.ts +6 -0
- package/dist/lib/views/domain/errors/index.d.ts.map +1 -0
- package/dist/lib/views/domain/errors/index.js +12 -0
- package/dist/lib/views/domain/errors/saved-view-forbidden.error.d.ts +10 -0
- package/dist/lib/views/domain/errors/saved-view-forbidden.error.d.ts.map +1 -0
- package/dist/lib/views/domain/errors/saved-view-forbidden.error.js +16 -0
- package/dist/lib/views/domain/errors/saved-view-limit-error.d.ts +9 -0
- package/dist/lib/views/domain/errors/saved-view-limit-error.d.ts.map +1 -0
- package/dist/lib/views/domain/errors/saved-view-limit-error.js +16 -0
- package/dist/lib/views/domain/errors/saved-view-name-taken.error.d.ts +5 -0
- package/dist/lib/views/domain/errors/saved-view-name-taken.error.d.ts.map +1 -0
- package/dist/lib/views/domain/errors/saved-view-name-taken.error.js +11 -0
- package/dist/lib/views/domain/errors/saved-view-not-found.error.d.ts +9 -0
- package/dist/lib/views/domain/errors/saved-view-not-found.error.d.ts.map +1 -0
- package/dist/lib/views/domain/errors/saved-view-not-found.error.js +15 -0
- package/dist/lib/views/domain/events/saved-view-events.d.ts +28 -0
- package/dist/lib/views/domain/events/saved-view-events.d.ts.map +1 -0
- package/dist/lib/views/domain/events/saved-view-events.js +38 -0
- package/dist/lib/views/domain/saved-view.d.ts +66 -0
- package/dist/lib/views/domain/saved-view.d.ts.map +1 -0
- package/dist/lib/views/domain/saved-view.js +18 -0
- package/dist/lib/views/domain/saved-view.repository.d.ts +77 -0
- package/dist/lib/views/domain/saved-view.repository.d.ts.map +1 -0
- package/dist/lib/views/domain/saved-view.repository.js +5 -0
- package/dist/lib/views/http/controllers/saved-views.controller.d.ts +45 -0
- package/dist/lib/views/http/controllers/saved-views.controller.d.ts.map +1 -0
- package/dist/lib/views/http/controllers/saved-views.controller.js +216 -0
- package/dist/lib/views/http/controllers/to-http-error.d.ts +10 -0
- package/dist/lib/views/http/controllers/to-http-error.d.ts.map +1 -0
- package/dist/lib/views/http/controllers/to-http-error.js +26 -0
- package/dist/lib/views/http/dto/save-view.dto.d.ts +54 -0
- package/dist/lib/views/http/dto/save-view.dto.d.ts.map +1 -0
- package/dist/lib/views/http/dto/save-view.dto.js +230 -0
- package/dist/lib/views/http/dto/view-payload.validator.d.ts +20 -0
- package/dist/lib/views/http/dto/view-payload.validator.d.ts.map +1 -0
- package/dist/lib/views/http/dto/view-payload.validator.js +52 -0
- package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.d.ts +26 -0
- package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.d.ts.map +1 -0
- package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.js +135 -0
- package/dist/lib/views/infrastructure/schema/external-refs.d.ts +68 -0
- package/dist/lib/views/infrastructure/schema/external-refs.d.ts.map +1 -0
- package/dist/lib/views/infrastructure/schema/external-refs.js +29 -0
- package/dist/lib/views/infrastructure/schema/index.d.ts +8 -0
- package/dist/lib/views/infrastructure/schema/index.d.ts.map +1 -0
- package/dist/lib/views/infrastructure/schema/index.js +13 -0
- package/dist/lib/views/infrastructure/schema/saved-views.d.ts +275 -0
- package/dist/lib/views/infrastructure/schema/saved-views.d.ts.map +1 -0
- package/dist/lib/views/infrastructure/schema/saved-views.js +87 -0
- package/dist/lib/views/views.constants.d.ts +26 -0
- package/dist/lib/views/views.constants.d.ts.map +1 -0
- package/dist/lib/views/views.constants.js +28 -0
- package/dist/lib/views/views.tokens.d.ts +17 -0
- package/dist/lib/views/views.tokens.d.ts.map +1 -0
- package/dist/lib/views/views.tokens.js +21 -0
- package/migrations/0000_saved_views.sql +29 -0
- package/migrations/meta/0000_snapshot.json +256 -0
- package/migrations/meta/_journal.json +13 -0
- package/package.json +13 -12
- package/dist/lib/entries/controllers/bulk-entries.controller.d.ts +0 -26
- package/dist/lib/entries/controllers/bulk-entries.controller.d.ts.map +0 -1
- package/dist/lib/entries/controllers/create-entry.controller.d.ts +0 -20
- package/dist/lib/entries/controllers/create-entry.controller.d.ts.map +0 -1
- package/dist/lib/entries/controllers/delete-entry.controller.d.ts +0 -24
- package/dist/lib/entries/controllers/delete-entry.controller.d.ts.map +0 -1
- package/dist/lib/entries/controllers/get-entry.controller.d.ts +0 -34
- package/dist/lib/entries/controllers/get-entry.controller.d.ts.map +0 -1
- package/dist/lib/entries/controllers/list-entries.controller.d.ts +0 -20
- package/dist/lib/entries/controllers/list-entries.controller.d.ts.map +0 -1
- package/dist/lib/entries/controllers/publish-entry.controller.d.ts +0 -19
- package/dist/lib/entries/controllers/publish-entry.controller.d.ts.map +0 -1
- package/dist/lib/entries/controllers/resolve-type.d.ts +0 -9
- package/dist/lib/entries/controllers/resolve-type.d.ts.map +0 -1
- package/dist/lib/entries/controllers/update-entry.controller.d.ts +0 -18
- package/dist/lib/entries/controllers/update-entry.controller.d.ts.map +0 -1
- package/dist/lib/entries/dto/bulk-ids.dto.d.ts +0 -11
- package/dist/lib/entries/dto/bulk-ids.dto.d.ts.map +0 -1
- package/dist/lib/entries/dto/list-entries-query.dto.d.ts +0 -46
- package/dist/lib/entries/dto/list-entries-query.dto.d.ts.map +0 -1
- package/dist/lib/entries/dto/relation-delta-map.validator.d.ts +0 -19
- package/dist/lib/entries/dto/relation-delta-map.validator.d.ts.map +0 -1
- package/dist/lib/entries/dto/save-entry.dto.d.ts +0 -46
- package/dist/lib/entries/dto/save-entry.dto.d.ts.map +0 -1
- package/dist/lib/entries/infrastructure/persistence/field-selection.d.ts +0 -24
- package/dist/lib/entries/infrastructure/persistence/field-selection.d.ts.map +0 -1
- package/dist/lib/entries/infrastructure/queries/entry-filter-schema.d.ts +0 -40
- package/dist/lib/entries/infrastructure/queries/entry-filter-schema.d.ts.map +0 -1
- package/dist/lib/entries/services/entries.service.d.ts +0 -59
- package/dist/lib/entries/services/entries.service.d.ts.map +0 -1
- package/dist/lib/entries/services/entry-counter.service.d.ts +0 -31
- package/dist/lib/entries/services/entry-counter.service.d.ts.map +0 -1
- package/dist/lib/entries/services/entry-filter-schema.d.ts +0 -32
- package/dist/lib/entries/services/entry-filter-schema.d.ts.map +0 -1
- package/dist/lib/entries/services/entry-row.d.ts +0 -56
- package/dist/lib/entries/services/entry-row.d.ts.map +0 -1
- package/dist/lib/entries/services/entry-writer.service.d.ts +0 -193
- package/dist/lib/entries/services/entry-writer.service.d.ts.map +0 -1
- package/dist/lib/entries/services/relation-link.service.d.ts +0 -115
- package/dist/lib/entries/services/relation-link.service.d.ts.map +0 -1
- package/dist/lib/extension/per-locale-relation.d.ts +0 -18
- package/dist/lib/extension/per-locale-relation.d.ts.map +0 -1
- package/dist/lib/public/controllers/get-public-entry.controller.d.ts +0 -26
- package/dist/lib/public/controllers/get-public-entry.controller.d.ts.map +0 -1
- package/dist/lib/public/controllers/list-public-entries.controller.d.ts +0 -24
- package/dist/lib/public/controllers/list-public-entries.controller.d.ts.map +0 -1
- package/dist/lib/public/controllers/public-schema.controller.d.ts +0 -18
- package/dist/lib/public/controllers/public-schema.controller.d.ts.map +0 -1
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The OpenAPI schemas of the **public** content API (`/api/v1/...`), kept apart
|
|
4
|
+
* from the admin's for the same reason `types/public-entry.ts` is kept apart
|
|
5
|
+
* from `EntryRecord`: this is a published contract an external site builds
|
|
6
|
+
* against, and it must be free to stay still while the admin's shape moves.
|
|
7
|
+
*
|
|
8
|
+
* Splitting them is not tidiness. Until this module existed the document
|
|
9
|
+
* described `/api/v1/content/{typeName}/{id}` with the **admin** entry schema,
|
|
10
|
+
* because the route pattern in `describe-content-api.ts` matched both spellings
|
|
11
|
+
* — so a consumer reading the reference was told a relation preview carries
|
|
12
|
+
* `RelationRef`s (id + title) when the public API returns whole entries, and
|
|
13
|
+
* that a media read is `Record<string, MediaRef[]>` when it is
|
|
14
|
+
* `Record<string, { items, total }>`. A wrong schema is worse than an absent
|
|
15
|
+
* one: the absent one sends you to curl, the wrong one does not.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.publicSchemaNamesOf = publicSchemaNamesOf;
|
|
19
|
+
exports.publicUnionOf = publicUnionOf;
|
|
20
|
+
exports.buildPublicApiSchemas = buildPublicApiSchemas;
|
|
21
|
+
const fields_1 = require("../types/fields");
|
|
22
|
+
const content_type_1 = require("../types/content-type");
|
|
23
|
+
const field_schema_1 = require("./field-schema");
|
|
24
|
+
const content_schemas_1 = require("./content-schemas");
|
|
25
|
+
const UUID = { type: 'string', format: 'uuid' };
|
|
26
|
+
const DATE_TIME = { type: 'string', format: 'date-time' };
|
|
27
|
+
/** The three schema names a content type contributes to the public document. */
|
|
28
|
+
function publicSchemaNamesOf(type) {
|
|
29
|
+
const base = `Public${(0, content_schemas_1.pascalCase)(type.name)}`;
|
|
30
|
+
return {
|
|
31
|
+
values: `${base}Values`,
|
|
32
|
+
entry: `${base}Entry`,
|
|
33
|
+
listPage: `${base}ListPage`
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether a field's value appears in a public entry's `values` bag.
|
|
38
|
+
*
|
|
39
|
+
* Mirrors `public-api/infrastructure/public-entry-row.ts`'s
|
|
40
|
+
* `isPureValueField` — **every** reference kind is dropped, `relation` in all
|
|
41
|
+
* four cardinalities and `media` alike, because the public read resolves
|
|
42
|
+
* neither in `values` and a bare uuid is an identifier with nothing to follow.
|
|
43
|
+
* Deliberately stricter than the admin's `isValueField`, which keeps an owning
|
|
44
|
+
* single relation's FK.
|
|
45
|
+
*/
|
|
46
|
+
function isPublicValueField(field) {
|
|
47
|
+
return (field.type !== fields_1.CONTENT_FIELD_TYPE.Relation &&
|
|
48
|
+
field.type !== fields_1.CONTENT_FIELD_TYPE.Media);
|
|
49
|
+
}
|
|
50
|
+
/** The `values` bag of one content type, as the public API serves it. */
|
|
51
|
+
function publicValuesSchema(type) {
|
|
52
|
+
const fields = type.fields.filter(isPublicValueField);
|
|
53
|
+
const omitted = type.fields
|
|
54
|
+
.filter((field) => !isPublicValueField(field))
|
|
55
|
+
.map((field) => field.name);
|
|
56
|
+
const properties = {};
|
|
57
|
+
for (const field of fields) {
|
|
58
|
+
properties[field.name] = (0, field_schema_1.fieldSchema)(field);
|
|
59
|
+
}
|
|
60
|
+
const notes = [
|
|
61
|
+
`Field values keyed by field name, for one \`${type.name}\` entry.`,
|
|
62
|
+
'An unset field reads back as `null`, never as a missing key — except under a `?fields=` sparse fieldset, where only the requested keys are present at all.',
|
|
63
|
+
omitted.length
|
|
64
|
+
? `Reference fields (${omitted
|
|
65
|
+
.map((name) => `\`${name}\``)
|
|
66
|
+
.join(', ')}) are **not** in this bag: read relations through \`?relations=preview\` or the \`/relations/{field}\` route, and media through \`?media=preview\` or \`/media\`. The schema endpoint still describes them, because they are part of the real model.`
|
|
67
|
+
: ''
|
|
68
|
+
].filter(Boolean);
|
|
69
|
+
return {
|
|
70
|
+
type: 'object',
|
|
71
|
+
title: `${type.label} values (public)`,
|
|
72
|
+
description: notes.join(' '),
|
|
73
|
+
properties,
|
|
74
|
+
// No `required`: a sparse fieldset may narrow the bag to one key, and a
|
|
75
|
+
// publishable type's "required" fields are required to publish rather
|
|
76
|
+
// than to exist.
|
|
77
|
+
additionalProperties: false
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** One entry of a content type, as the public API serves it. */
|
|
81
|
+
function publicEntrySchema(type, names) {
|
|
82
|
+
const properties = {
|
|
83
|
+
id: { ...UUID, description: 'Entry id.' },
|
|
84
|
+
...(type.publishable
|
|
85
|
+
? {
|
|
86
|
+
status: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
enum: Object.values(content_type_1.ENTRY_STATUS),
|
|
89
|
+
description: 'Publish state. A read-only token only ever sees `published`; a write-scoped one can create a draft and read it back with `?status=`.'
|
|
90
|
+
},
|
|
91
|
+
publishedAt: {
|
|
92
|
+
...DATE_TIME,
|
|
93
|
+
nullable: true,
|
|
94
|
+
description: 'When this entry last went live. `draft` together with a non-null `publishedAt` is live content carrying unpublished edits — neither field alone tells those apart.'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
: {}),
|
|
98
|
+
createdAt: DATE_TIME,
|
|
99
|
+
updatedAt: DATE_TIME,
|
|
100
|
+
...(type.i18n
|
|
101
|
+
? {
|
|
102
|
+
locale: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: 'Locale slug of this row.'
|
|
105
|
+
},
|
|
106
|
+
localeGroupId: {
|
|
107
|
+
...UUID,
|
|
108
|
+
description: 'Translation group — this row’s siblings in other locales share it, and it is the id the `/group/{localeGroupId}` routes take.'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
: {}),
|
|
112
|
+
values: (0, content_schemas_1.ref)(names.values),
|
|
113
|
+
relations: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
additionalProperties: (0, content_schemas_1.ref)('PublicRelationFieldPage'),
|
|
116
|
+
description: 'Relation links keyed by field name — present only with `?relations=preview`. Each linked record is a whole entry, not a ref.'
|
|
117
|
+
},
|
|
118
|
+
media: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
additionalProperties: (0, content_schemas_1.ref)('PublicMediaFieldPage'),
|
|
121
|
+
description: 'Media assets keyed by field name — present only with `?media=preview`.'
|
|
122
|
+
},
|
|
123
|
+
...(type.i18n
|
|
124
|
+
? {
|
|
125
|
+
translations: {
|
|
126
|
+
type: 'array',
|
|
127
|
+
items: (0, content_schemas_1.ref)(names.entry),
|
|
128
|
+
description: 'The entry’s **other** published locale rows — present only with `?translations=preview`. The entry itself is not repeated, and the siblings are not themselves expanded.'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
: {})
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
type: 'object',
|
|
135
|
+
title: `${type.label} (public)`,
|
|
136
|
+
description: `One \`${type.name}\` entry on the public API.`,
|
|
137
|
+
properties,
|
|
138
|
+
required: [
|
|
139
|
+
'id',
|
|
140
|
+
'createdAt',
|
|
141
|
+
'updatedAt',
|
|
142
|
+
'values',
|
|
143
|
+
...(type.publishable ? ['status', 'publishedAt'] : []),
|
|
144
|
+
...(type.i18n ? ['locale', 'localeGroupId'] : [])
|
|
145
|
+
]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** One page of public entries of a content type. */
|
|
149
|
+
function publicListPageSchema(type, names) {
|
|
150
|
+
return {
|
|
151
|
+
type: 'object',
|
|
152
|
+
title: `${type.label} page (public)`,
|
|
153
|
+
description: `One page of \`${type.name}\` entries. \`total\` counts every match, ignoring pagination.`,
|
|
154
|
+
properties: {
|
|
155
|
+
items: { type: 'array', items: (0, content_schemas_1.ref)(names.entry) },
|
|
156
|
+
total: { type: 'integer' },
|
|
157
|
+
page: {
|
|
158
|
+
type: 'integer',
|
|
159
|
+
description: '1-based page number this page represents.'
|
|
160
|
+
},
|
|
161
|
+
pageSize: {
|
|
162
|
+
type: 'integer',
|
|
163
|
+
description: 'Rows per page actually applied.'
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
required: ['items', 'total', 'page', 'pageSize']
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The public per-type entry schemas as alternatives — the response shape
|
|
171
|
+
* depends on `typeName`, which OpenAPI cannot express as a dependency. One
|
|
172
|
+
* registered type needs no union; none at all leaves an open object, since
|
|
173
|
+
* every one of these routes 404s anyway.
|
|
174
|
+
*
|
|
175
|
+
* `anyOf` rather than `oneOf`, for the reason `unionOf` in
|
|
176
|
+
* `describe-content-api.ts` spells out: the alternatives overlap, and *exactly
|
|
177
|
+
* one* is a promise the API does not keep.
|
|
178
|
+
*/
|
|
179
|
+
function publicUnionOf(names) {
|
|
180
|
+
if (!names.length)
|
|
181
|
+
return { type: 'object', additionalProperties: true };
|
|
182
|
+
return names.length === 1
|
|
183
|
+
? (0, content_schemas_1.ref)(names[0])
|
|
184
|
+
: { anyOf: names.map((name) => (0, content_schemas_1.ref)(name)) };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The public API's fixed shapes — the ones that do not vary by content type,
|
|
188
|
+
* except where an entry appears inside them.
|
|
189
|
+
*
|
|
190
|
+
* `entryUnion` is threaded in rather than referenced by name because a relation
|
|
191
|
+
* page and a translation list carry whole entries, and which entries those are
|
|
192
|
+
* is the registry's answer, not this module's.
|
|
193
|
+
*/
|
|
194
|
+
function publicSharedSchemas(entryUnion) {
|
|
195
|
+
return {
|
|
196
|
+
PublicMediaTrack: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
description: 'One timed-text track on a published video or audio asset — everything a `<track>` element needs.',
|
|
199
|
+
properties: {
|
|
200
|
+
kind: {
|
|
201
|
+
type: 'string',
|
|
202
|
+
description: '`captions` / `subtitles` / `descriptions` / `chapters`.'
|
|
203
|
+
},
|
|
204
|
+
srclang: {
|
|
205
|
+
type: 'string',
|
|
206
|
+
description: 'BCP-47 tag of the track’s language.'
|
|
207
|
+
},
|
|
208
|
+
label: {
|
|
209
|
+
type: 'string',
|
|
210
|
+
description: 'What a player shows in its track menu.'
|
|
211
|
+
},
|
|
212
|
+
src: {
|
|
213
|
+
type: 'string',
|
|
214
|
+
description: 'Route the WebVTT bytes stream from — same bearer token as the read.'
|
|
215
|
+
},
|
|
216
|
+
default: {
|
|
217
|
+
type: 'boolean',
|
|
218
|
+
description: 'Whether a player should enable this track by default.'
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
required: ['kind', 'srclang', 'label', 'src']
|
|
222
|
+
},
|
|
223
|
+
PublicMediaRef: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
description: 'One attached asset. The URLs take the **same bearer token** as the read that produced them, so a server-side consumer can fetch the bytes with the credential it already holds — but a browser `<img src>` sends no `Authorization` header and will not load one. Proxy them from whatever holds the token.',
|
|
226
|
+
properties: {
|
|
227
|
+
id: UUID,
|
|
228
|
+
name: {
|
|
229
|
+
type: 'string',
|
|
230
|
+
description: 'Display name — the original file name.'
|
|
231
|
+
},
|
|
232
|
+
url: {
|
|
233
|
+
type: 'string',
|
|
234
|
+
description: 'Route the original bytes stream from.'
|
|
235
|
+
},
|
|
236
|
+
thumbUrl: {
|
|
237
|
+
type: 'string',
|
|
238
|
+
description: '~320px derivative, when one was generated (absent for non-images, SVGs, and images too small to derive).'
|
|
239
|
+
},
|
|
240
|
+
previewUrl: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
description: '~1280px derivative, same caveats.'
|
|
243
|
+
},
|
|
244
|
+
kind: {
|
|
245
|
+
type: 'string',
|
|
246
|
+
description: 'Coarse kind — image / video / audio / document / archive.'
|
|
247
|
+
},
|
|
248
|
+
mimeType: { type: 'string' },
|
|
249
|
+
alt: {
|
|
250
|
+
type: 'string',
|
|
251
|
+
nullable: true,
|
|
252
|
+
description: 'The text alternative for **this usage**. `""` and `null` mean different things: `""` is “render `alt=""`, this image says nothing”, `null` is “nobody supplied one” — a gap to report, not an instruction to hide the image from assistive tech.'
|
|
253
|
+
},
|
|
254
|
+
decorative: {
|
|
255
|
+
type: 'boolean',
|
|
256
|
+
enum: [true],
|
|
257
|
+
description: 'Present, and always `true`, when the author marked this usage purely presentational.'
|
|
258
|
+
},
|
|
259
|
+
tracks: {
|
|
260
|
+
type: 'array',
|
|
261
|
+
items: (0, content_schemas_1.ref)('PublicMediaTrack'),
|
|
262
|
+
description: 'Empty for an image, and for a video nobody has captioned.'
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
required: ['id', 'name', 'url', 'kind', 'mimeType', 'alt', 'tracks']
|
|
266
|
+
},
|
|
267
|
+
PublicMediaFieldPage: {
|
|
268
|
+
type: 'object',
|
|
269
|
+
description: 'One media field’s assets, in their stored order.',
|
|
270
|
+
properties: {
|
|
271
|
+
items: { type: 'array', items: (0, content_schemas_1.ref)('PublicMediaRef') },
|
|
272
|
+
total: {
|
|
273
|
+
type: 'integer',
|
|
274
|
+
description: 'Assets attached to this field.'
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
required: ['items', 'total']
|
|
278
|
+
},
|
|
279
|
+
PublicEntryMedia: {
|
|
280
|
+
type: 'object',
|
|
281
|
+
description: 'Every media field of the entry, keyed by field name.',
|
|
282
|
+
properties: {
|
|
283
|
+
media: {
|
|
284
|
+
type: 'object',
|
|
285
|
+
additionalProperties: (0, content_schemas_1.ref)('PublicMediaFieldPage')
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
required: ['media']
|
|
289
|
+
},
|
|
290
|
+
PublicRelationFieldPage: {
|
|
291
|
+
type: 'object',
|
|
292
|
+
description: 'One relation field’s links: a capped page plus the true total. Each item is a **whole entry**, envelope and `values` alike — not a ref — so a consumer renders a linked record with the code it already has. Linked entries are not themselves expanded. A target the caller may not see is omitted **and not counted**, so `total` is the number of links actually reachable.',
|
|
293
|
+
properties: {
|
|
294
|
+
items: { type: 'array', items: entryUnion },
|
|
295
|
+
total: {
|
|
296
|
+
type: 'integer',
|
|
297
|
+
description: 'Visible links for this field, ignoring the page cap.'
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
required: ['items', 'total']
|
|
301
|
+
},
|
|
302
|
+
PublicEntryTranslations: {
|
|
303
|
+
type: 'object',
|
|
304
|
+
description: 'The entry’s other published locale rows, ordered by locale slug. The entry itself is not repeated, and a locale that exists only as a draft is absent.',
|
|
305
|
+
properties: {
|
|
306
|
+
translations: { type: 'array', items: entryUnion }
|
|
307
|
+
},
|
|
308
|
+
required: ['translations']
|
|
309
|
+
},
|
|
310
|
+
PublicContentTypeList: {
|
|
311
|
+
type: 'object',
|
|
312
|
+
description: 'Every content type granted to the requested workspace — exactly the set of `typeName`s that will not 404.',
|
|
313
|
+
properties: {
|
|
314
|
+
items: { type: 'array', items: (0, content_schemas_1.ref)('ContentTypeSummary') }
|
|
315
|
+
},
|
|
316
|
+
required: ['items']
|
|
317
|
+
},
|
|
318
|
+
PublicBulkError: {
|
|
319
|
+
type: 'object',
|
|
320
|
+
description: 'Why one item of a batch did not go through, carrying **the status the same write would have failed with on its own** (404, 422, 400) rather than a bulk-specific vocabulary.',
|
|
321
|
+
properties: {
|
|
322
|
+
status: { type: 'integer' },
|
|
323
|
+
message: { type: 'string' },
|
|
324
|
+
issues: {
|
|
325
|
+
type: 'array',
|
|
326
|
+
items: (0, content_schemas_1.ref)('ValidationIssue'),
|
|
327
|
+
description: 'The per-field issues of a 422.'
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
required: ['status', 'message']
|
|
331
|
+
},
|
|
332
|
+
PublicBulkSaveItemResult: {
|
|
333
|
+
type: 'object',
|
|
334
|
+
description: 'What became of one submitted item, positionally matched to the request.',
|
|
335
|
+
properties: {
|
|
336
|
+
index: {
|
|
337
|
+
type: 'integer',
|
|
338
|
+
description: '0-based position in the submitted `items`.'
|
|
339
|
+
},
|
|
340
|
+
op: { type: 'string', enum: ['create', 'update'] },
|
|
341
|
+
ok: { type: 'boolean' },
|
|
342
|
+
entry: entryUnion,
|
|
343
|
+
error: (0, content_schemas_1.ref)('PublicBulkError')
|
|
344
|
+
},
|
|
345
|
+
required: ['index', 'op', 'ok']
|
|
346
|
+
},
|
|
347
|
+
PublicBulkSaveResult: {
|
|
348
|
+
type: 'object',
|
|
349
|
+
description: '**Partial success is the contract**, not a degraded mode: items are written one transaction at a time, so the call is a 200 with per-item verdicts whether or not every item saved, and a caller retries the failures it can fix.',
|
|
350
|
+
properties: {
|
|
351
|
+
items: {
|
|
352
|
+
type: 'array',
|
|
353
|
+
items: (0, content_schemas_1.ref)('PublicBulkSaveItemResult')
|
|
354
|
+
},
|
|
355
|
+
created: { type: 'integer' },
|
|
356
|
+
updated: { type: 'integer' },
|
|
357
|
+
failed: { type: 'integer' }
|
|
358
|
+
},
|
|
359
|
+
required: ['items', 'created', 'updated', 'failed']
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Every schema the public API contributes: its fixed shapes plus, per
|
|
365
|
+
* registered content type, the `Values` / `Entry` / `ListPage` trio.
|
|
366
|
+
*/
|
|
367
|
+
function buildPublicApiSchemas(types) {
|
|
368
|
+
const entryUnion = publicUnionOf(types.map((type) => publicSchemaNamesOf(type).entry));
|
|
369
|
+
const schemas = publicSharedSchemas(entryUnion);
|
|
370
|
+
for (const type of types) {
|
|
371
|
+
const names = publicSchemaNamesOf(type);
|
|
372
|
+
schemas[names.values] = publicValuesSchema(type);
|
|
373
|
+
schemas[names.entry] = publicEntrySchema(type, names);
|
|
374
|
+
schemas[names.listPage] = publicListPageSchema(type, names);
|
|
375
|
+
}
|
|
376
|
+
return schemas;
|
|
377
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OpenAPI schemas the **saved views** plugin contributes.
|
|
3
|
+
*
|
|
4
|
+
* `SavedView` is a plain TypeScript `interface` in `views/domain/`, which the
|
|
5
|
+
* swagger scanner cannot see and ADR-0003 forbids decorating — so the shape is
|
|
6
|
+
* written out here instead, the same way the content types are.
|
|
7
|
+
*
|
|
8
|
+
* One thing about it is runtime data rather than a fixed contract: `scope` is
|
|
9
|
+
* `content:<typeName>`, and which type names exist is what the host registered.
|
|
10
|
+
* {@link buildViewsSchemas} therefore takes the serialized registry and turns
|
|
11
|
+
* the scope into an enum of the real lists, so a reader of the reference sees
|
|
12
|
+
* `content:article`, not "some string".
|
|
13
|
+
*/
|
|
14
|
+
import type { SerializedContentType } from '../registry/content-type-registry';
|
|
15
|
+
import type { OpenApiSchema } from './field-schema';
|
|
16
|
+
/** Schema name of one saved view. */
|
|
17
|
+
export declare const SAVED_VIEW_SCHEMA = "SavedView";
|
|
18
|
+
/** Schema name of the slice a view restores. */
|
|
19
|
+
export declare const SAVED_VIEW_PAYLOAD_SCHEMA = "SavedViewPayload";
|
|
20
|
+
/** The saved-view schemas, keyed by schema name. */
|
|
21
|
+
export declare function buildViewsSchemas(types: readonly SerializedContentType[]): Record<string, OpenApiSchema>;
|
|
22
|
+
//# sourceMappingURL=views-schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"views-schemas.d.ts","sourceRoot":"","sources":["../../../src/lib/docs/views-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAepD,qCAAqC;AACrC,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAC7C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,qBAAqB,CAAC;AA8I5D,oDAAoD;AACpD,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,SAAS,qBAAqB,EAAE,GACxC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAK/B"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The OpenAPI schemas the **saved views** plugin contributes.
|
|
4
|
+
*
|
|
5
|
+
* `SavedView` is a plain TypeScript `interface` in `views/domain/`, which the
|
|
6
|
+
* swagger scanner cannot see and ADR-0003 forbids decorating — so the shape is
|
|
7
|
+
* written out here instead, the same way the content types are.
|
|
8
|
+
*
|
|
9
|
+
* One thing about it is runtime data rather than a fixed contract: `scope` is
|
|
10
|
+
* `content:<typeName>`, and which type names exist is what the host registered.
|
|
11
|
+
* {@link buildViewsSchemas} therefore takes the serialized registry and turns
|
|
12
|
+
* the scope into an enum of the real lists, so a reader of the reference sees
|
|
13
|
+
* `content:article`, not "some string".
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.SAVED_VIEW_PAYLOAD_SCHEMA = exports.SAVED_VIEW_SCHEMA = void 0;
|
|
17
|
+
exports.buildViewsSchemas = buildViewsSchemas;
|
|
18
|
+
const content_schemas_1 = require("./content-schemas");
|
|
19
|
+
const saved_view_1 = require("../views/domain/saved-view");
|
|
20
|
+
const views_constants_1 = require("../views/views.constants");
|
|
21
|
+
const entries_constants_1 = require("../entries/entries.constants");
|
|
22
|
+
/** Schema name of one saved view. */
|
|
23
|
+
exports.SAVED_VIEW_SCHEMA = 'SavedView';
|
|
24
|
+
/** Schema name of the slice a view restores. */
|
|
25
|
+
exports.SAVED_VIEW_PAYLOAD_SCHEMA = 'SavedViewPayload';
|
|
26
|
+
/**
|
|
27
|
+
* The `scope` values the API will accept and return.
|
|
28
|
+
*
|
|
29
|
+
* An enum when the host registered types, a plain bounded string when it
|
|
30
|
+
* registered none — an empty `enum` would be a schema nothing can satisfy, and
|
|
31
|
+
* "no views are reachable" is better said by the absence of types than by a
|
|
32
|
+
* response schema that rejects every answer.
|
|
33
|
+
*/
|
|
34
|
+
function scopeSchema(types) {
|
|
35
|
+
const base = {
|
|
36
|
+
type: 'string',
|
|
37
|
+
maxLength: views_constants_1.VIEW_SCOPE_MAX_LENGTH,
|
|
38
|
+
description: `The list this view belongs to, as \`${views_constants_1.CONTENT_SCOPE_PREFIX}<typeName>\`. A scope naming a type this workspace was not granted is a 404 — the same answer an unknown type gives, so the endpoint cannot be used to discover which types exist elsewhere.`
|
|
39
|
+
};
|
|
40
|
+
if (types.length === 0) {
|
|
41
|
+
return base;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
...base,
|
|
45
|
+
enum: types.map((type) => `${views_constants_1.CONTENT_SCOPE_PREFIX}${type.name}`)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The stored slice.
|
|
50
|
+
*
|
|
51
|
+
* `filter` and `sort` are the records page's own `?filter=` / `?sort=` strings
|
|
52
|
+
* carried verbatim, so this describes them as the strings they are rather than
|
|
53
|
+
* re-modelling a filter tree that would then have two spellings. `search` and
|
|
54
|
+
* `page` are absent by design — see {@link SavedViewPayload}.
|
|
55
|
+
*/
|
|
56
|
+
function payloadSchema() {
|
|
57
|
+
return {
|
|
58
|
+
type: 'object',
|
|
59
|
+
title: 'Saved view payload',
|
|
60
|
+
description: 'The filter/sort/columns/page-size slice a view restores. Mirrors the records page’s URL params, so a saved view and a hand-edited link replay through one code path. A one-off `search` and the reading position `page` are deliberately not part of a view.',
|
|
61
|
+
properties: {
|
|
62
|
+
filter: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
maxLength: entries_constants_1.FILTER_MAX_LENGTH,
|
|
65
|
+
description: 'The filter tree as a JSON string, byte-identical to `?filter=`. Stored opaquely and re-validated against the type when replayed.'
|
|
66
|
+
},
|
|
67
|
+
sort: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
maxLength: 255,
|
|
70
|
+
description: 'Sort spec: a column id (ascending) or `-`-prefixed (descending).'
|
|
71
|
+
},
|
|
72
|
+
pageSize: {
|
|
73
|
+
type: 'integer',
|
|
74
|
+
minimum: 1,
|
|
75
|
+
maximum: entries_constants_1.MAX_PAGE_SIZE,
|
|
76
|
+
description: 'Rows per page this view opens with.'
|
|
77
|
+
},
|
|
78
|
+
columns: {
|
|
79
|
+
type: 'array',
|
|
80
|
+
maxItems: views_constants_1.VIEW_MAX_COLUMNS,
|
|
81
|
+
items: { type: 'string', maxLength: views_constants_1.VIEW_COLUMN_MAX_LENGTH },
|
|
82
|
+
description: 'Visible column ids **in display order**. Ids the type no longer has are dropped when the view is applied.'
|
|
83
|
+
},
|
|
84
|
+
extra: {
|
|
85
|
+
type: 'object',
|
|
86
|
+
additionalProperties: { type: 'string' },
|
|
87
|
+
description: 'Slot-contributed list params (the i18n plugin’s `locale`, and whatever a later plugin adds), as a flat string map. Opaque on purpose — the keys come from plugin-registered toolbar items at runtime.'
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
// Every field is optional: a view over an unfiltered, unsorted,
|
|
91
|
+
// default-column list stores `{}`, and that is a legitimate view.
|
|
92
|
+
additionalProperties: false
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** One saved view, as every one of these routes answers it. */
|
|
96
|
+
function savedViewSchema(types) {
|
|
97
|
+
const properties = {
|
|
98
|
+
id: { type: 'string', format: 'uuid', description: 'View id.' },
|
|
99
|
+
scope: scopeSchema(types),
|
|
100
|
+
name: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
minLength: 1,
|
|
103
|
+
maxLength: views_constants_1.VIEW_NAME_MAX_LENGTH,
|
|
104
|
+
description: 'Display name. Unique per person within one workspace and scope.'
|
|
105
|
+
},
|
|
106
|
+
visibility: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
enum: [...saved_view_1.VIEW_VISIBILITY_VALUES],
|
|
109
|
+
description: '`private` (only its owner) or `workspace` (every member). Sharing needs `views:share`.'
|
|
110
|
+
},
|
|
111
|
+
ownerId: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
format: 'uuid',
|
|
114
|
+
description: 'The user who created it.'
|
|
115
|
+
},
|
|
116
|
+
isOwn: {
|
|
117
|
+
type: 'boolean',
|
|
118
|
+
description: 'Whether the **caller** owns it. Computed per request, so the same row answers differently to two people — the admin gates Save and Delete on it.'
|
|
119
|
+
},
|
|
120
|
+
isDefault: {
|
|
121
|
+
type: 'boolean',
|
|
122
|
+
description: 'Whether it is the caller’s default for this scope. Also per-caller: a default is a personal landing choice, not a property of the view.'
|
|
123
|
+
},
|
|
124
|
+
payload: (0, content_schemas_1.ref)(exports.SAVED_VIEW_PAYLOAD_SCHEMA),
|
|
125
|
+
updatedAt: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
format: 'date-time',
|
|
128
|
+
description: 'Last modified, ISO-8601.'
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
return {
|
|
132
|
+
type: 'object',
|
|
133
|
+
title: 'Saved view',
|
|
134
|
+
description: 'A named slice of one content list. The stored payload is **not** a grant: it is replayed through the ordinary list query with the reader’s own permissions, workspace scope and content grants, so a shared view shows a narrower reader fewer rows — never more.',
|
|
135
|
+
properties,
|
|
136
|
+
required: [
|
|
137
|
+
'id',
|
|
138
|
+
'scope',
|
|
139
|
+
'name',
|
|
140
|
+
'visibility',
|
|
141
|
+
'ownerId',
|
|
142
|
+
'isOwn',
|
|
143
|
+
'isDefault',
|
|
144
|
+
'payload',
|
|
145
|
+
'updatedAt'
|
|
146
|
+
],
|
|
147
|
+
additionalProperties: false
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** The saved-view schemas, keyed by schema name. */
|
|
151
|
+
function buildViewsSchemas(types) {
|
|
152
|
+
return {
|
|
153
|
+
[exports.SAVED_VIEW_PAYLOAD_SCHEMA]: payloadSchema(),
|
|
154
|
+
[exports.SAVED_VIEW_SCHEMA]: savedViewSchema(types)
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutboxWriter, UnitOfWork } from '@orthacms/database';
|
|
1
|
+
import { type EventActor, OutboxWriter, UnitOfWork } from '@orthacms/database';
|
|
2
2
|
import type { AnyContentType } from '../../../types/content-type';
|
|
3
3
|
import { EntryValidationService } from '../../../validation/services/entry-validation.service';
|
|
4
4
|
import { EntryWriterService } from '../../infrastructure/persistence/entry-writer.service';
|
|
@@ -11,6 +11,12 @@ import { type BulkPublishResult } from '../../types/bulk-publish';
|
|
|
11
11
|
* `publishable` ids, reporting which were skipped and why (partial success),
|
|
12
12
|
* and appends one `entry.published` event per newly-published id to the outbox —
|
|
13
13
|
* atomically with the batch.
|
|
14
|
+
*
|
|
15
|
+
* `actor` is stamped onto every one of those events. It used to be missing here
|
|
16
|
+
* while the single-entry publish and the bulk *delete* beside it both carried
|
|
17
|
+
* one, so publishing fifty entries from the records toolbar wrote fifty audit
|
|
18
|
+
* rows attributed to "System" — the one operation in the content lifecycle that
|
|
19
|
+
* silently lost the person who performed it.
|
|
14
20
|
*/
|
|
15
21
|
export declare class BulkPublishEntriesUseCase {
|
|
16
22
|
private readonly uow;
|
|
@@ -18,6 +24,6 @@ export declare class BulkPublishEntriesUseCase {
|
|
|
18
24
|
private readonly writer;
|
|
19
25
|
private readonly validation;
|
|
20
26
|
constructor(uow: UnitOfWork, outbox: OutboxWriter, writer: EntryWriterService, validation: EntryValidationService);
|
|
21
|
-
execute(type: AnyContentType, ids: string[], workspaceId: string): Promise<BulkPublishResult>;
|
|
27
|
+
execute(type: AnyContentType, ids: string[], workspaceId: string, actor?: EventActor): Promise<BulkPublishResult>;
|
|
22
28
|
}
|
|
23
29
|
//# sourceMappingURL=bulk-publish-entries.use-case.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-publish-entries.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/bulk-publish-entries.use-case.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"bulk-publish-entries.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/bulk-publish-entries.use-case.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,KAAK,UAAU,EACf,YAAY,EACZ,UAAU,EACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uDAAuD,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,uDAAuD,CAAC;AAE3F,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAIhF;;;;;;;;;;;;;;GAcG;AACH,qBACa,yBAAyB;IAE9B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAHV,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,kBAAkB,EAC1B,UAAU,EAAE,sBAAsB;IAGjD,OAAO,CACT,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,iBAAiB,CAAC;CAqEhC"}
|
|
@@ -10,6 +10,7 @@ const entry_writer_service_1 = require("../../infrastructure/persistence/entry-w
|
|
|
10
10
|
const bulk_publish_verdicts_1 = require("../../infrastructure/persistence/bulk-publish-verdicts");
|
|
11
11
|
const bulk_publish_1 = require("../../types/bulk-publish");
|
|
12
12
|
const entry_1 = require("../../domain/entry");
|
|
13
|
+
const entry_row_1 = require("../../infrastructure/persistence/entry-row");
|
|
13
14
|
/**
|
|
14
15
|
* Commit a bulk publish. Preserves the original single **locked** transaction:
|
|
15
16
|
* the candidate rows are selected `FOR UPDATE` and re-validated inside one
|
|
@@ -18,6 +19,12 @@ const entry_1 = require("../../domain/entry");
|
|
|
18
19
|
* `publishable` ids, reporting which were skipped and why (partial success),
|
|
19
20
|
* and appends one `entry.published` event per newly-published id to the outbox —
|
|
20
21
|
* atomically with the batch.
|
|
22
|
+
*
|
|
23
|
+
* `actor` is stamped onto every one of those events. It used to be missing here
|
|
24
|
+
* while the single-entry publish and the bulk *delete* beside it both carried
|
|
25
|
+
* one, so publishing fifty entries from the records toolbar wrote fifty audit
|
|
26
|
+
* rows attributed to "System" — the one operation in the content lifecycle that
|
|
27
|
+
* silently lost the person who performed it.
|
|
21
28
|
*/
|
|
22
29
|
let BulkPublishEntriesUseCase = class BulkPublishEntriesUseCase {
|
|
23
30
|
uow;
|
|
@@ -30,7 +37,7 @@ let BulkPublishEntriesUseCase = class BulkPublishEntriesUseCase {
|
|
|
30
37
|
this.writer = writer;
|
|
31
38
|
this.validation = validation;
|
|
32
39
|
}
|
|
33
|
-
async execute(type, ids, workspaceId) {
|
|
40
|
+
async execute(type, ids, workspaceId, actor) {
|
|
34
41
|
if (!type.publishable) {
|
|
35
42
|
throw new common_1.BadRequestException(`Content type "${type.name}" is not publishable.`);
|
|
36
43
|
}
|
|
@@ -50,15 +57,23 @@ let BulkPublishEntriesUseCase = class BulkPublishEntriesUseCase {
|
|
|
50
57
|
for (const id of published) {
|
|
51
58
|
await this.writer.markRevisionPublished(exec, id, workspaceId);
|
|
52
59
|
}
|
|
53
|
-
|
|
60
|
+
const events = published.flatMap((id) => {
|
|
54
61
|
const entry = entry_1.Entry.rehydrate({
|
|
55
62
|
id,
|
|
56
63
|
contentType: type.name,
|
|
57
|
-
status: content_domain_1.ENTRY_STATUS.Draft
|
|
64
|
+
status: content_domain_1.ENTRY_STATUS.Draft,
|
|
65
|
+
workspaceId,
|
|
66
|
+
// The candidate rows were already locked and loaded for
|
|
67
|
+
// re-validation, so each row's own label is to hand.
|
|
68
|
+
title: (() => {
|
|
69
|
+
const row = byId.get(id);
|
|
70
|
+
return row ? (0, entry_row_1.entryTitle)(type, row) : null;
|
|
71
|
+
})()
|
|
58
72
|
});
|
|
59
73
|
entry.publish({ valid: true, issues: [] });
|
|
60
74
|
return entry.pullEvents();
|
|
61
|
-
})
|
|
75
|
+
});
|
|
76
|
+
await this.outbox.append(actor ? (0, database_1.attachActor)(events, actor) : events);
|
|
62
77
|
}
|
|
63
78
|
const skipped = items
|
|
64
79
|
.filter((item) => item.verdict !== bulk_publish_1.BULK_VERDICT.Publishable)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutboxWriter, UnitOfWork } from '@orthacms/database';
|
|
1
|
+
import { type EventActor, OutboxWriter, UnitOfWork } from '@orthacms/database';
|
|
2
2
|
import type { AnyContentType } from '../../../types/content-type';
|
|
3
3
|
import { EntryWriterService } from '../../infrastructure/persistence/entry-writer.service';
|
|
4
4
|
import type { BulkActionResult } from '../../types/bulk-publish';
|
|
@@ -18,6 +18,6 @@ export declare class BulkUnpublishEntriesUseCase {
|
|
|
18
18
|
private readonly outbox;
|
|
19
19
|
private readonly writer;
|
|
20
20
|
constructor(uow: UnitOfWork, outbox: OutboxWriter, writer: EntryWriterService);
|
|
21
|
-
execute(type: AnyContentType, ids: string[], workspaceId: string): Promise<BulkActionResult>;
|
|
21
|
+
execute(type: AnyContentType, ids: string[], workspaceId: string, actor?: EventActor): Promise<BulkActionResult>;
|
|
22
22
|
}
|
|
23
23
|
//# sourceMappingURL=bulk-unpublish-entries.use-case.d.ts.map
|