@orthacms/content-server 0.4.3 → 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 +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/lib/content.module.d.ts.map +1 -1
- package/dist/lib/content.module.js +8 -0
- 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/infrastructure/persistence/entry-writer.service.d.ts +22 -6
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts.map +1 -1
- package/dist/lib/entries/infrastructure/persistence/entry-writer.service.js +71 -17
- 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-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/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 +12 -1
- 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/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.map +1 -1
- package/dist/lib/utils/content-views-plugin.js +8 -0
- package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts +4 -1
- package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts.map +1 -1
- package/dist/lib/views/application/use-cases/create-saved-view.use-case.js +33 -9
- package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts +4 -1
- package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts.map +1 -1
- package/dist/lib/views/application/use-cases/delete-saved-view.use-case.js +24 -3
- package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts +4 -1
- package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts.map +1 -1
- package/dist/lib/views/application/use-cases/update-saved-view.use-case.js +32 -3
- 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/infrastructure/persistence/drizzle-saved-view.repository.d.ts +12 -3
- package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.d.ts.map +1 -1
- package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.js +15 -5
- package/package.json +11 -11
- 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/entry-access-source.d.ts +0 -112
- package/dist/lib/extension/entry-access-source.d.ts.map +0 -1
- package/dist/lib/extension/entry-write-hook.d.ts +0 -98
- package/dist/lib/extension/entry-write-hook.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,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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-unpublish-entries.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/bulk-unpublish-entries.use-case.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"bulk-unpublish-entries.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/bulk-unpublish-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,kBAAkB,EAAE,MAAM,uDAAuD,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAIjE;;;;;;;;;;GAUG;AACH,qBACa,2BAA2B;IAEhC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,kBAAkB;IAGzC,OAAO,CACT,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,gBAAgB,CAAC;CA6D/B"}
|
|
@@ -7,6 +7,7 @@ const database_1 = require("@orthacms/database");
|
|
|
7
7
|
const content_domain_1 = require("@orthacms/content-domain");
|
|
8
8
|
const entry_writer_service_1 = require("../../infrastructure/persistence/entry-writer.service");
|
|
9
9
|
const entry_1 = require("../../domain/entry");
|
|
10
|
+
const entry_row_1 = require("../../infrastructure/persistence/entry-row");
|
|
10
11
|
/**
|
|
11
12
|
* Revert a set of live entries to draft. Runs inside one {@link UnitOfWork}, and
|
|
12
13
|
* **one set of ids runs through all three effects**: the `{ count }` reported,
|
|
@@ -27,7 +28,7 @@ let BulkUnpublishEntriesUseCase = class BulkUnpublishEntriesUseCase {
|
|
|
27
28
|
this.outbox = outbox;
|
|
28
29
|
this.writer = writer;
|
|
29
30
|
}
|
|
30
|
-
async execute(type, ids, workspaceId) {
|
|
31
|
+
async execute(type, ids, workspaceId, actor) {
|
|
31
32
|
if (!type.publishable) {
|
|
32
33
|
throw new common_1.BadRequestException(`Content type "${type.name}" is not publishable.`);
|
|
33
34
|
}
|
|
@@ -38,21 +39,30 @@ let BulkUnpublishEntriesUseCase = class BulkUnpublishEntriesUseCase {
|
|
|
38
39
|
// The set that actually transitions (published → draft), captured
|
|
39
40
|
// before the write so we emit one event per real change.
|
|
40
41
|
const publishedIds = await this.writer.publishedIdsAmong(exec, type, ids, workspaceId);
|
|
42
|
+
// Read the labels **before** the write: an audit row carries a
|
|
43
|
+
// frozen title, and after `markDraftBulk` the rows are still there
|
|
44
|
+
// but a later kind (a purge) would have taken them, so reading them
|
|
45
|
+
// up front is the habit that keeps every bulk path the same shape.
|
|
46
|
+
const byId = await this.writer.loadLiveByIds(type, publishedIds, workspaceId, exec);
|
|
41
47
|
const count = await this.writer.markDraftBulk(exec, type, ids, workspaceId);
|
|
42
48
|
// Revert each really-transitioned entry's published revision back to
|
|
43
49
|
// draft — the same set the outbox emits an event for.
|
|
44
50
|
for (const id of publishedIds) {
|
|
45
51
|
await this.writer.markRevisionUnpublished(exec, id, workspaceId);
|
|
46
52
|
}
|
|
47
|
-
|
|
53
|
+
const events = publishedIds.flatMap((id) => {
|
|
54
|
+
const row = byId.get(id);
|
|
48
55
|
const entry = entry_1.Entry.rehydrate({
|
|
49
56
|
id,
|
|
50
57
|
contentType: type.name,
|
|
51
|
-
status: content_domain_1.ENTRY_STATUS.Published
|
|
58
|
+
status: content_domain_1.ENTRY_STATUS.Published,
|
|
59
|
+
workspaceId,
|
|
60
|
+
title: row ? (0, entry_row_1.entryTitle)(type, row) : null
|
|
52
61
|
});
|
|
53
62
|
entry.unpublish();
|
|
54
63
|
return entry.pullEvents();
|
|
55
|
-
})
|
|
64
|
+
});
|
|
65
|
+
await this.outbox.append(actor ? (0, database_1.attachActor)(events, actor) : events);
|
|
56
66
|
return { count };
|
|
57
67
|
});
|
|
58
68
|
}
|
|
@@ -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';
|
|
@@ -23,14 +23,12 @@ export declare class PublishEntryUseCase {
|
|
|
23
23
|
constructor(uow: UnitOfWork, outbox: OutboxWriter, writer: EntryWriterService, validation: EntryValidationService);
|
|
24
24
|
execute(type: AnyContentType, id: string, workspaceId: string,
|
|
25
25
|
/**
|
|
26
|
-
* The acting
|
|
27
|
-
* name who did this. Without it every content row in the activity
|
|
28
|
-
* read "System"
|
|
26
|
+
* The acting principal, merged onto the outbox events so the audit log
|
|
27
|
+
* can name who did this. Without it every content row in the activity
|
|
28
|
+
* log read "System" — which is also what a token-authenticated publish
|
|
29
|
+
* read as until `actor_type` let a credential be named.
|
|
29
30
|
*/
|
|
30
|
-
actor?:
|
|
31
|
-
id: string;
|
|
32
|
-
email: string | null;
|
|
33
|
-
},
|
|
31
|
+
actor?: EventActor,
|
|
34
32
|
/**
|
|
35
33
|
* The version to mark live, when publishing a **specific** earlier one
|
|
36
34
|
* whose content the caller already re-applied to the live row. Omitted
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-entry.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/publish-entry.use-case.ts"],"names":[],"mappings":"AAMA,OAAO,
|
|
1
|
+
{"version":3,"file":"publish-entry.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/publish-entry.use-case.ts"],"names":[],"mappings":"AAMA,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,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAK/D;;;;;;;;;;;GAWG;AACH,qBACa,mBAAmB;IAExB,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,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,WAAW,CAAC;IA+EvB,OAAO,CAAC,QAAQ;CAKnB"}
|
|
@@ -7,6 +7,7 @@ const database_1 = require("@orthacms/database");
|
|
|
7
7
|
const entry_validation_service_1 = require("../../../validation/services/entry-validation.service");
|
|
8
8
|
const entry_writer_service_1 = require("../../infrastructure/persistence/entry-writer.service");
|
|
9
9
|
const entry_row_1 = require("../../infrastructure/persistence/entry-row");
|
|
10
|
+
const entry_row_2 = require("../../infrastructure/persistence/entry-row");
|
|
10
11
|
const entry_1 = require("../../domain/entry");
|
|
11
12
|
const entry_publish_blocked_error_1 = require("../../domain/entry-publish-blocked.error");
|
|
12
13
|
/**
|
|
@@ -34,9 +35,10 @@ let PublishEntryUseCase = class PublishEntryUseCase {
|
|
|
34
35
|
}
|
|
35
36
|
async execute(type, id, workspaceId,
|
|
36
37
|
/**
|
|
37
|
-
* The acting
|
|
38
|
-
* name who did this. Without it every content row in the activity
|
|
39
|
-
* read "System"
|
|
38
|
+
* The acting principal, merged onto the outbox events so the audit log
|
|
39
|
+
* can name who did this. Without it every content row in the activity
|
|
40
|
+
* log read "System" — which is also what a token-authenticated publish
|
|
41
|
+
* read as until `actor_type` let a credential be named.
|
|
40
42
|
*/
|
|
41
43
|
actor,
|
|
42
44
|
/**
|
|
@@ -63,7 +65,11 @@ let PublishEntryUseCase = class PublishEntryUseCase {
|
|
|
63
65
|
const entry = entry_1.Entry.rehydrate({
|
|
64
66
|
id,
|
|
65
67
|
contentType: type.name,
|
|
66
|
-
status: current['status']
|
|
68
|
+
status: current['status'],
|
|
69
|
+
workspaceId,
|
|
70
|
+
// The stored row is already loaded, so the audit row gets a
|
|
71
|
+
// readable name for free rather than a bare uuid.
|
|
72
|
+
title: (0, entry_row_2.entryTitle)(type, current)
|
|
67
73
|
});
|
|
68
74
|
try {
|
|
69
75
|
entry.publish({ valid: issues.length === 0, issues });
|
|
@@ -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 { EntryRecord } from '../../types/entry-list-view';
|
|
@@ -21,10 +21,7 @@ export declare class UnpublishEntryUseCase {
|
|
|
21
21
|
* name who did this. Without it every content row in the activity log
|
|
22
22
|
* read "System".
|
|
23
23
|
*/
|
|
24
|
-
actor?:
|
|
25
|
-
id: string;
|
|
26
|
-
email: string | null;
|
|
27
|
-
}): Promise<EntryRecord>;
|
|
24
|
+
actor?: EventActor): Promise<EntryRecord>;
|
|
28
25
|
private notFound;
|
|
29
26
|
}
|
|
30
27
|
//# sourceMappingURL=unpublish-entry.use-case.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unpublish-entry.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/unpublish-entry.use-case.ts"],"names":[],"mappings":"AAKA,OAAO,
|
|
1
|
+
{"version":3,"file":"unpublish-entry.use-case.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/application/use-cases/unpublish-entry.use-case.ts"],"names":[],"mappings":"AAKA,OAAO,EAEH,KAAK,UAAU,EACf,YAAY,EACZ,UAAU,EACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uDAAuD,CAAC;AAE3F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAI/D;;;;;;;GAOG;AACH,qBACa,qBAAqB;IAE1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,kBAAkB;IAGzC,OAAO,CACT,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,WAAW,CAAC;IA8CvB,OAAO,CAAC,QAAQ;CAKnB"}
|
|
@@ -6,6 +6,7 @@ const common_1 = require("@nestjs/common");
|
|
|
6
6
|
const database_1 = require("@orthacms/database");
|
|
7
7
|
const entry_writer_service_1 = require("../../infrastructure/persistence/entry-writer.service");
|
|
8
8
|
const entry_row_1 = require("../../infrastructure/persistence/entry-row");
|
|
9
|
+
const entry_row_2 = require("../../infrastructure/persistence/entry-row");
|
|
9
10
|
const entry_1 = require("../../domain/entry");
|
|
10
11
|
/**
|
|
11
12
|
* Revert one entry to draft. Runs inside a {@link UnitOfWork} so the status
|
|
@@ -42,7 +43,11 @@ let UnpublishEntryUseCase = class UnpublishEntryUseCase {
|
|
|
42
43
|
const entry = entry_1.Entry.rehydrate({
|
|
43
44
|
id,
|
|
44
45
|
contentType: type.name,
|
|
45
|
-
status: current['status']
|
|
46
|
+
status: current['status'],
|
|
47
|
+
workspaceId,
|
|
48
|
+
// The stored row is already loaded, so the audit row gets a
|
|
49
|
+
// readable name for free rather than a bare uuid.
|
|
50
|
+
title: (0, entry_row_2.entryTitle)(type, current)
|
|
46
51
|
});
|
|
47
52
|
entry.unpublish();
|
|
48
53
|
const updated = await this.writer.markDraft(exec, type, id, workspaceId);
|
|
@@ -8,6 +8,13 @@ export interface EntryState {
|
|
|
8
8
|
contentType: string;
|
|
9
9
|
/** Current publish status of the stored row. */
|
|
10
10
|
status: EntryStatus;
|
|
11
|
+
/** The workspace the row lives in — carried only for the event payload. */
|
|
12
|
+
workspaceId?: string | null;
|
|
13
|
+
/**
|
|
14
|
+
* A human label for the entry, carried only for the event payload so the
|
|
15
|
+
* audit row names something a reader recognises instead of a uuid.
|
|
16
|
+
*/
|
|
17
|
+
title?: string | null;
|
|
11
18
|
}
|
|
12
19
|
/**
|
|
13
20
|
* The outcome of the publish precondition, computed by the use-case from the
|
|
@@ -37,7 +44,7 @@ export interface PublishGate {
|
|
|
37
44
|
*/
|
|
38
45
|
export declare class Entry {
|
|
39
46
|
private readonly _id;
|
|
40
|
-
private readonly
|
|
47
|
+
private readonly _subject;
|
|
41
48
|
private _status;
|
|
42
49
|
private readonly events;
|
|
43
50
|
private constructor();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../../../src/lib/entries/domain/entry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAGH,KAAK,WAAW,EAChB,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../../../src/lib/entries/domain/entry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAGH,KAAK,WAAW,EAChB,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;AASlC,oFAAoF;AACpF,MAAM,WAAW,UAAU;IACvB,gBAAgB;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,MAAM,EAAE,WAAW,CAAC;IACpB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IACxB,qCAAqC;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,KAAK;IAIV,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,OAAO;IALnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,OAAO;IAMP,gEAAgE;IAChE,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK;IAY1C;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAYhC;;;;;OAKG;IACH,SAAS,IAAI,IAAI;IASjB,oBAAoB;IACpB,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,kCAAkC;IAClC,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,gEAAgE;IAChE,UAAU,IAAI,WAAW,EAAE;IAI3B,OAAO,CAAC,KAAK;CAKhB"}
|
|
@@ -21,17 +21,21 @@ const entry_publish_blocked_error_1 = require("./entry-publish-blocked.error");
|
|
|
21
21
|
*/
|
|
22
22
|
class Entry {
|
|
23
23
|
_id;
|
|
24
|
-
|
|
24
|
+
_subject;
|
|
25
25
|
_status;
|
|
26
26
|
events = [];
|
|
27
|
-
constructor(_id,
|
|
27
|
+
constructor(_id, _subject, _status) {
|
|
28
28
|
this._id = _id;
|
|
29
|
-
this.
|
|
29
|
+
this._subject = _subject;
|
|
30
30
|
this._status = _status;
|
|
31
31
|
}
|
|
32
32
|
/** Reconstructs an entry from its persisted lifecycle state. */
|
|
33
33
|
static rehydrate(state) {
|
|
34
|
-
return new Entry(state.id,
|
|
34
|
+
return new Entry(state.id, {
|
|
35
|
+
contentType: state.contentType,
|
|
36
|
+
workspaceId: state.workspaceId ?? null,
|
|
37
|
+
title: state.title ?? null
|
|
38
|
+
}, state.status);
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
41
|
* Publish the entry. Rejects with {@link EntryPublishBlockedError} when the
|
|
@@ -51,9 +55,7 @@ class Entry {
|
|
|
51
55
|
}
|
|
52
56
|
(0, content_domain_1.assertTransition)(this._status, content_domain_1.ENTRY_STATUS.Published);
|
|
53
57
|
this._status = content_domain_1.ENTRY_STATUS.Published;
|
|
54
|
-
this.raise(entry_events_1.ENTRY_EVENT_KINDS.PUBLISHED
|
|
55
|
-
contentType: this._contentType
|
|
56
|
-
});
|
|
58
|
+
this.raise(entry_events_1.ENTRY_EVENT_KINDS.PUBLISHED);
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* Revert the entry to draft. A `published` row transitions to `draft` via
|
|
@@ -67,9 +69,7 @@ class Entry {
|
|
|
67
69
|
}
|
|
68
70
|
(0, content_domain_1.assertTransition)(this._status, content_domain_1.ENTRY_STATUS.Draft);
|
|
69
71
|
this._status = content_domain_1.ENTRY_STATUS.Draft;
|
|
70
|
-
this.raise(entry_events_1.ENTRY_EVENT_KINDS.UNPUBLISHED
|
|
71
|
-
contentType: this._contentType
|
|
72
|
-
});
|
|
72
|
+
this.raise(entry_events_1.ENTRY_EVENT_KINDS.UNPUBLISHED);
|
|
73
73
|
}
|
|
74
74
|
/** The entry id. */
|
|
75
75
|
get id() {
|
|
@@ -83,8 +83,8 @@ class Entry {
|
|
|
83
83
|
pullEvents() {
|
|
84
84
|
return this.events.splice(0, this.events.length);
|
|
85
85
|
}
|
|
86
|
-
raise(kind
|
|
87
|
-
this.events.push((0, entry_events_1.entryEvent)(kind, this._id,
|
|
86
|
+
raise(kind) {
|
|
87
|
+
this.events.push((0, entry_events_1.entryEvent)(kind, this._id, (0, entry_events_1.entrySubjectPayload)(this._subject)));
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
exports.Entry = Entry;
|
|
@@ -26,6 +26,48 @@ export declare const ENTRY_EVENT_KINDS: {
|
|
|
26
26
|
readonly RESTORED: "entry.restored";
|
|
27
27
|
readonly PURGED: "entry.purged";
|
|
28
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* What an entry event says about **which entry** it is about, beyond the id.
|
|
31
|
+
*
|
|
32
|
+
* Both extra fields exist because of what an audit row can and cannot recover
|
|
33
|
+
* afterwards:
|
|
34
|
+
*
|
|
35
|
+
* - `workspaceId` is the only way the trail can answer a workspace-shaped
|
|
36
|
+
* question. `activity_events` grew a nullable `workspace_id` for it, filled
|
|
37
|
+
* from the event payload, and an entry is the most-produced audited fact in
|
|
38
|
+
* the product — so an entry event that omitted it would leave the column
|
|
39
|
+
* mostly empty and the question mostly unanswerable.
|
|
40
|
+
* - `title` is what makes the row **readable**. An audit row keeps no FK and no
|
|
41
|
+
* denormalised name; its whole handle on the subject is `subject_id`. That is
|
|
42
|
+
* survivable for an entry that still exists and unrecoverable for one that
|
|
43
|
+
* does not — and `entry.purged` is precisely the case where this row is the
|
|
44
|
+
* only remaining record of the thing. A uuid is not an answer to "what did
|
|
45
|
+
* they delete".
|
|
46
|
+
*
|
|
47
|
+
* Both are optional so a caller that genuinely has neither (a unit test, an
|
|
48
|
+
* event minted outside the write path) is not forced to invent them.
|
|
49
|
+
*/
|
|
50
|
+
export interface EntrySubject {
|
|
51
|
+
/** The content type's machine name. */
|
|
52
|
+
contentType: string;
|
|
53
|
+
/** The workspace the entry lives in. */
|
|
54
|
+
workspaceId?: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* A human label for the entry **at the time of the event** — a frozen
|
|
57
|
+
* snapshot, like `actor_email`, not a lookup. The point is to survive the
|
|
58
|
+
* subject.
|
|
59
|
+
*/
|
|
60
|
+
title?: string | null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The subject fields every entry event's payload carries.
|
|
64
|
+
*
|
|
65
|
+
* Exported because the {@link Entry} model raises its two events through the
|
|
66
|
+
* envelope builder directly rather than the per-kind helpers — those two are
|
|
67
|
+
* status transitions the model owns, and it must produce the same payload shape
|
|
68
|
+
* as the five the write path mints.
|
|
69
|
+
*/
|
|
70
|
+
export declare function entrySubjectPayload(subject: EntrySubject): Record<string, unknown>;
|
|
29
71
|
/**
|
|
30
72
|
* Builds an entry {@link DomainEvent} of `kind` for `entryId`, carrying
|
|
31
73
|
* `payload`. Keeps the domain model free of the event envelope's plumbing — the
|
|
@@ -33,7 +75,7 @@ export declare const ENTRY_EVENT_KINDS: {
|
|
|
33
75
|
*/
|
|
34
76
|
export declare function entryEvent(kind: string, entryId: string, payload: Record<string, unknown>): DomainEvent;
|
|
35
77
|
/** A new entry exists. `contentType` is what lets the log name *what* was made. */
|
|
36
|
-
export declare function entryCreated(entryId: string,
|
|
78
|
+
export declare function entryCreated(entryId: string, subject: EntrySubject): DomainEvent;
|
|
37
79
|
/**
|
|
38
80
|
* An entry's stored values changed, naming **which fields** did.
|
|
39
81
|
*
|
|
@@ -46,16 +88,16 @@ export declare function entryCreated(entryId: string, contentType: string): Doma
|
|
|
46
88
|
* and therefore no event at all, so the log records editorial changes rather
|
|
47
89
|
* than round trips.
|
|
48
90
|
*/
|
|
49
|
-
export declare function entryUpdated(entryId: string,
|
|
91
|
+
export declare function entryUpdated(entryId: string, subject: EntrySubject, fields: readonly string[]): DomainEvent;
|
|
50
92
|
/**
|
|
51
93
|
* An entry was deleted. `soft` distinguishes the two very different facts the
|
|
52
94
|
* one route produces: a **paranoid** type is tombstoned and can be restored,
|
|
53
95
|
* while any other type is gone from the table the moment this commits, and a
|
|
54
96
|
* reviewer reading the log needs to know which happened.
|
|
55
97
|
*/
|
|
56
|
-
export declare function entryDeleted(entryId: string,
|
|
98
|
+
export declare function entryDeleted(entryId: string, subject: EntrySubject, soft: boolean): DomainEvent;
|
|
57
99
|
/** A soft-deleted entry's tombstone was cleared. */
|
|
58
|
-
export declare function entryRestored(entryId: string,
|
|
100
|
+
export declare function entryRestored(entryId: string, subject: EntrySubject): DomainEvent;
|
|
59
101
|
/**
|
|
60
102
|
* A tombstoned entry was destroyed permanently.
|
|
61
103
|
*
|
|
@@ -63,5 +105,5 @@ export declare function entryRestored(entryId: string, contentType: string): Dom
|
|
|
63
105
|
* action with nothing left behind to inspect afterwards, so it is exactly the
|
|
64
106
|
* one an audit trail has to record distinctly.
|
|
65
107
|
*/
|
|
66
|
-
export declare function entryPurged(entryId: string,
|
|
108
|
+
export declare function entryPurged(entryId: string, subject: EntrySubject): DomainEvent;
|
|
67
109
|
//# sourceMappingURL=entry-events.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-events.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/domain/events/entry-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEzE;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;CAQpB,CAAC;AAKX;;;;GAIG;AACH,wBAAgB,UAAU,CACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,WAAW,CAOb;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CACxB,OAAO,EAAE,MAAM,EACf,
|
|
1
|
+
{"version":3,"file":"entry-events.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/domain/events/entry-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEzE;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;CAQpB,CAAC;AAKX;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,YAAY;IACzB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,YAAY,GACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,WAAW,CAOb;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,GACtB,WAAW,CAMb;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,SAAS,MAAM,EAAE,GAC1B,WAAW,CAKb;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,OAAO,GACd,WAAW,CAKb;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,GACtB,WAAW,CAMb;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,GACtB,WAAW,CAMb"}
|