@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,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_EXTENSION_KEYS = void 0;
|
|
4
|
+
exports.MaxExtensionKeys = MaxExtensionKeys;
|
|
5
|
+
const class_validator_1 = require("class-validator");
|
|
6
|
+
/**
|
|
7
|
+
* Most extension keys one save may carry.
|
|
8
|
+
*
|
|
9
|
+
* A key names an installed plugin, so the real ceiling is single digits; this is
|
|
10
|
+
* a guard against a client posting an object with thousands of keys, each of
|
|
11
|
+
* which the registry would otherwise walk on every write. Deliberately not a
|
|
12
|
+
* bound on the *values* — those are the extensions' own contracts, and
|
|
13
|
+
* content-server declares the bag opaque.
|
|
14
|
+
*/
|
|
15
|
+
exports.MAX_EXTENSION_KEYS = 16;
|
|
16
|
+
/** Rejects an extension bag with more keys than {@link MAX_EXTENSION_KEYS}. */
|
|
17
|
+
function MaxExtensionKeys(options) {
|
|
18
|
+
return function (target, propertyName) {
|
|
19
|
+
(0, class_validator_1.registerDecorator)({
|
|
20
|
+
name: 'maxExtensionKeys',
|
|
21
|
+
target: target.constructor,
|
|
22
|
+
propertyName,
|
|
23
|
+
options,
|
|
24
|
+
validator: {
|
|
25
|
+
validate(value) {
|
|
26
|
+
if (value === undefined || value === null)
|
|
27
|
+
return true;
|
|
28
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
29
|
+
// `@IsObject()` owns that message; saying it twice
|
|
30
|
+
// would put two sentences about one field in the 400.
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return (Object.keys(value).length <=
|
|
34
|
+
exports.MAX_EXTENSION_KEYS);
|
|
35
|
+
},
|
|
36
|
+
defaultMessage() {
|
|
37
|
+
return `extensions may name at most ${exports.MAX_EXTENSION_KEYS} plugins`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -44,5 +44,21 @@ export declare class SaveEntryDto {
|
|
|
44
44
|
* the workspace. Read on create only.
|
|
45
45
|
*/
|
|
46
46
|
localeGroupId?: string;
|
|
47
|
+
/**
|
|
48
|
+
* State a **different plugin** owns about this entry, keyed by that plugin's
|
|
49
|
+
* extension key — opaque here, exactly like {@link locale}: content-server
|
|
50
|
+
* forwards the bag to the bound entry-write extensions and never looks
|
|
51
|
+
* inside it. `@orthacms/segments-server` reads `access` from it.
|
|
52
|
+
*
|
|
53
|
+
* It rides the save body rather than a second request so the entry, its
|
|
54
|
+
* links, the plugin's state and the version recording all of them commit in
|
|
55
|
+
* one transaction — and so the version records what the save applied instead
|
|
56
|
+
* of what it replaced.
|
|
57
|
+
*
|
|
58
|
+
* A key **omitted** is left alone; a key present is written. An **unknown**
|
|
59
|
+
* key is ignored rather than refused, because the same client may be talking
|
|
60
|
+
* to a deployment without that plugin installed.
|
|
61
|
+
*/
|
|
62
|
+
extensions?: Record<string, unknown>;
|
|
47
63
|
}
|
|
48
64
|
//# sourceMappingURL=save-entry.dto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"save-entry.dto.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/http/dto/save-entry.dto.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"save-entry.dto.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/http/dto/save-entry.dto.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAqBjE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAY;IACrB,wCAAwC;IASxC,MAAM,EAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjC;;;OAGG;IAwBH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAE1C;;;;;OAKG;IAWH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;OAKG;IASH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;;;;;;;;OAcG;IAYH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC"}
|
|
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const swagger_1 = require("@nestjs/swagger");
|
|
6
6
|
const class_validator_1 = require("class-validator");
|
|
7
7
|
const relation_delta_map_validator_1 = require("./relation-delta-map.validator");
|
|
8
|
+
const extension_bag_validator_1 = require("./extension-bag.validator");
|
|
8
9
|
/** Longest accepted locale slug (BCP-47's practical bound). */
|
|
9
10
|
const LOCALE_MAX_LENGTH = 35;
|
|
10
11
|
/** OpenAPI schema of one uuid-array leg of a relation delta. */
|
|
@@ -58,6 +59,22 @@ class SaveEntryDto {
|
|
|
58
59
|
* the workspace. Read on create only.
|
|
59
60
|
*/
|
|
60
61
|
localeGroupId;
|
|
62
|
+
/**
|
|
63
|
+
* State a **different plugin** owns about this entry, keyed by that plugin's
|
|
64
|
+
* extension key — opaque here, exactly like {@link locale}: content-server
|
|
65
|
+
* forwards the bag to the bound entry-write extensions and never looks
|
|
66
|
+
* inside it. `@orthacms/segments-server` reads `access` from it.
|
|
67
|
+
*
|
|
68
|
+
* It rides the save body rather than a second request so the entry, its
|
|
69
|
+
* links, the plugin's state and the version recording all of them commit in
|
|
70
|
+
* one transaction — and so the version records what the save applied instead
|
|
71
|
+
* of what it replaced.
|
|
72
|
+
*
|
|
73
|
+
* A key **omitted** is left alone; a key present is written. An **unknown**
|
|
74
|
+
* key is ignored rather than refused, because the same client may be talking
|
|
75
|
+
* to a deployment without that plugin installed.
|
|
76
|
+
*/
|
|
77
|
+
extensions;
|
|
61
78
|
}
|
|
62
79
|
exports.SaveEntryDto = SaveEntryDto;
|
|
63
80
|
tslib_1.__decorate([
|
|
@@ -118,3 +135,16 @@ tslib_1.__decorate([
|
|
|
118
135
|
(0, class_validator_1.IsUUID)(),
|
|
119
136
|
tslib_1.__metadata("design:type", String)
|
|
120
137
|
], SaveEntryDto.prototype, "localeGroupId", void 0);
|
|
138
|
+
tslib_1.__decorate([
|
|
139
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
140
|
+
type: 'object',
|
|
141
|
+
maxProperties: extension_bag_validator_1.MAX_EXTENSION_KEYS,
|
|
142
|
+
additionalProperties: true,
|
|
143
|
+
example: { access: { allow: [], deny: [] } },
|
|
144
|
+
description: 'Per-plugin state stored alongside the entry, keyed by extension (e.g. `access`). Opaque to content-server: forwarded to the bound entry-write extensions, written inside the save’s transaction, and captured by the revision. An omitted key is left unchanged; an unknown key is ignored.'
|
|
145
|
+
}),
|
|
146
|
+
(0, class_validator_1.IsOptional)(),
|
|
147
|
+
(0, class_validator_1.IsObject)(),
|
|
148
|
+
(0, extension_bag_validator_1.MaxExtensionKeys)(),
|
|
149
|
+
tslib_1.__metadata("design:type", Object)
|
|
150
|
+
], SaveEntryDto.prototype, "extensions", void 0);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OutboxWriter, UnitOfWork, type Database, type EventActor } from '@orthacms/database';
|
|
2
2
|
import { type ContentEntryExtension } from '../../../extension/entry-extension';
|
|
3
3
|
import { type MediaAssetResolver } from '../../../extension/media-asset-resolver';
|
|
4
|
+
import { EntryWriteExtensionRegistry } from '../../../extension/entry-write-extension';
|
|
4
5
|
import type { AnyContentType } from '../../../types/content-type';
|
|
5
6
|
import { EntryValidationService, type ValidationIssue } from '../../../validation/services/entry-validation.service';
|
|
6
7
|
import type { EntryRecord, RelationDelta, RelationFieldView } from '../../types/entry-list-view';
|
|
@@ -35,9 +36,10 @@ export declare class EntryWriterService {
|
|
|
35
36
|
private readonly validation;
|
|
36
37
|
private readonly relations;
|
|
37
38
|
private readonly revisionStore;
|
|
39
|
+
private readonly writeExtensions;
|
|
38
40
|
private readonly extension?;
|
|
39
41
|
private readonly mediaResolver?;
|
|
40
|
-
constructor(db: Database, uow: UnitOfWork, outbox: OutboxWriter, validation: EntryValidationService, relations: RelationLinkService, revisionStore: RevisionStore, extension?: ContentEntryExtension | undefined, mediaResolver?: MediaAssetResolver | undefined);
|
|
42
|
+
constructor(db: Database, uow: UnitOfWork, outbox: OutboxWriter, validation: EntryValidationService, relations: RelationLinkService, revisionStore: RevisionStore, writeExtensions: EntryWriteExtensionRegistry, extension?: ContentEntryExtension | undefined, mediaResolver?: MediaAssetResolver | undefined);
|
|
41
43
|
/**
|
|
42
44
|
* The active unit-of-work transaction, typed as the handle drizzle hands a
|
|
43
45
|
* `db.transaction` callback.
|
|
@@ -49,16 +51,32 @@ export declare class EntryWriterService {
|
|
|
49
51
|
* `uow.run(...)`, where the two are the same object.
|
|
50
52
|
*/
|
|
51
53
|
private tx;
|
|
52
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* One event per affected row, built from each row's own id **and the row
|
|
56
|
+
* itself** — the latter because an entry event carries a frozen `title`
|
|
57
|
+
* snapshot, and a title is per row rather than per batch.
|
|
58
|
+
*/
|
|
53
59
|
private eventsFor;
|
|
60
|
+
/**
|
|
61
|
+
* What an entry event says about its subject: the type, the workspace, and
|
|
62
|
+
* a **frozen** human label read off the row being written.
|
|
63
|
+
*
|
|
64
|
+
* Frozen rather than looked up, for the same reason `actor_email` is: the
|
|
65
|
+
* audit row has to outlive its subject. `entry.purged` destroys the only
|
|
66
|
+
* other copy of the name, so a row that stored just the uuid could never
|
|
67
|
+
* answer "what did they delete" — not even in principle.
|
|
68
|
+
*/
|
|
69
|
+
private subjectOf;
|
|
54
70
|
/**
|
|
55
71
|
* Append `events` to the transactional outbox, stamped with the acting
|
|
56
|
-
*
|
|
72
|
+
* principal, from inside the active unit of work.
|
|
57
73
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
74
|
+
* A **token** may be the actor here, and that is the whole difference from
|
|
75
|
+
* {@link revisionActorId} below: the audit row now carries an `actor_type`,
|
|
76
|
+
* so naming a credential is a fact rather than a misused user column. A
|
|
77
|
+
* write made with a bearer token used to reach the log as "System", which
|
|
78
|
+
* left every write over the public REST API, GraphQL and MCP unattributable
|
|
79
|
+
* to the credential that made it.
|
|
62
80
|
*/
|
|
63
81
|
private emit;
|
|
64
82
|
/**
|
|
@@ -119,6 +137,14 @@ export declare class EntryWriterService {
|
|
|
119
137
|
* two of them from deadlocking on the pair.
|
|
120
138
|
*/
|
|
121
139
|
private appendRevisionsFor;
|
|
140
|
+
/**
|
|
141
|
+
* The rows behind a set of ids, on this transaction.
|
|
142
|
+
*
|
|
143
|
+
* Soft-deleted rows are **included**: a locale in the trash is still a row
|
|
144
|
+
* whose state an extension just changed, and it comes back on restore
|
|
145
|
+
* expecting a timeline that says so.
|
|
146
|
+
*/
|
|
147
|
+
private rowsByIds;
|
|
122
148
|
/**
|
|
123
149
|
* Create an entry **owned by `workspaceId`** (stamped onto the row so the
|
|
124
150
|
* reader's workspace filter and every later scoped write see it). A
|
|
@@ -136,8 +162,13 @@ export declare class EntryWriterService {
|
|
|
136
162
|
* extension validates them and stamps the envelope columns (a
|
|
137
163
|
* `localeGroupId` makes the row a **sibling** in that translation group). A
|
|
138
164
|
* duplicate `(group, locale)` surfaces as a **409** via {@link uniqueGuarded}.
|
|
165
|
+
*
|
|
166
|
+
* `extensions` is the same kind of thing one plugin further out: an opaque
|
|
167
|
+
* bag forwarded to the bound {@link EntryWriteExtensionRegistry}, so a plugin
|
|
168
|
+
* that owns state *about* an entry (segments' audiences) writes it inside
|
|
169
|
+
* this transaction and has it captured by the same version.
|
|
139
170
|
*/
|
|
140
|
-
create(type: AnyContentType, values: Record<string, unknown>, workspaceId: string, relations?: Record<string, RelationDelta>, locale?: string, localeGroupId?: string, actor?: EventActor | null): Promise<EntryRecord>;
|
|
171
|
+
create(type: AnyContentType, values: Record<string, unknown>, workspaceId: string, relations?: Record<string, RelationDelta>, locale?: string, localeGroupId?: string, actor?: EventActor | null, extensions?: Record<string, unknown>): Promise<EntryRecord>;
|
|
141
172
|
/**
|
|
142
173
|
* First page (+ total) of every relation field's links for one live entry,
|
|
143
174
|
* keyed by field name — what the editor loads on open. Each field is
|
|
@@ -180,6 +211,17 @@ export declare class EntryWriterService {
|
|
|
180
211
|
* really did change.
|
|
181
212
|
*/
|
|
182
213
|
appendRevision?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Opaque per-plugin state to store alongside the entry, forwarded to
|
|
216
|
+
* the bound {@link EntryWriteExtensionRegistry} inside this write's
|
|
217
|
+
* transaction. A key the caller omits is left alone — a save that
|
|
218
|
+
* said nothing about audiences must not clear them.
|
|
219
|
+
*
|
|
220
|
+
* A **restore** passes the restored version's `snapshot.extra` here,
|
|
221
|
+
* which is what makes going back to Tuesday put Tuesday's audiences
|
|
222
|
+
* back too rather than leaving today's readers on Tuesday's words.
|
|
223
|
+
*/
|
|
224
|
+
extensions?: Record<string, unknown>;
|
|
183
225
|
}): Promise<EntryRecord>;
|
|
184
226
|
/**
|
|
185
227
|
* Stamp `status='published'` + `published_at`/`updated_at` on one live row,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-writer.service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/infrastructure/persistence/entry-writer.service.ts"],"names":[],"mappings":"AAoBA,OAAO,
|
|
1
|
+
{"version":3,"file":"entry-writer.service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/entries/infrastructure/persistence/entry-writer.service.ts"],"names":[],"mappings":"AAoBA,OAAO,EAIH,YAAY,EACZ,UAAU,EACV,KAAK,QAAQ,EAEb,KAAK,UAAU,EAClB,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EAEH,KAAK,qBAAqB,EAC7B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAEH,KAAK,kBAAkB,EAC1B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AAEvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGlE,OAAO,EACH,sBAAsB,EACtB,KAAK,eAAe,EACvB,MAAM,uDAAuD,CAAC;AAC/D,OAAO,KAAK,EACR,WAAW,EACX,aAAa,EACb,iBAAiB,EACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAQjE,OAAO,EAEH,mBAAmB,EACnB,KAAK,aAAa,EACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEH,KAAK,aAAa,EACrB,MAAM,qDAAqD,CAAC;AAY7D,oFAAoF;AACpF,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAenC;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,kBAAkB;IAEL,OAAO,CAAC,QAAQ,CAAC,EAAE;IAIrC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAI1B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAO9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAKhC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAO3B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;gBA9BI,EAAE,EAAE,QAAQ,EAI9B,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,sBAAsB,EAClC,SAAS,EAAE,mBAAmB,EAI9B,aAAa,EAAE,aAAa,EAO5B,eAAe,EAAE,2BAA2B,EAK5C,SAAS,CAAC,EAAE,qBAAqB,YAAA,EAOjC,aAAa,CAAC,EAAE,kBAAkB,YAAA;IAGvD;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE;IAIV;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAOjB;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IAYjB;;;;;;;;;;OAUG;YACW,IAAI;IASlB;;;;;;;;;OASG;IACH,OAAO,CAAC,aAAa;IAarB;;;;;;;;;;;;;;;OAeG;YACW,sBAAsB;IAwDpC;;;;;;;OAOG;YACW,QAAQ;IAuBtB;;;;;;;OAOG;YACW,cAAc;IAmC5B;;;;;;;;;;OAUG;YACW,kBAAkB;IA0ChC;;;;;;OAMG;YACW,SAAS;IAsBvB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,MAAM,CACR,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACzC,MAAM,CAAC,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,EACtB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,EACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,OAAO,CAAC,WAAW,CAAC;IA6LvB;;;;;OAKG;IACG,YAAY,CACd,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAM7C;;;;OAIG;IACG,gBAAgB,CAClB,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC;IAe7B;;;;;;OAMG;YACW,mBAAmB;IAuCjC,yEAAyE;IACzE,OAAO,CAAC,YAAY;IAUpB,oDAAoD;IAC9C,MAAM,CACR,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,CAAC;IAMvB;;;;;OAKG;IACG,MAAM,CACR,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACzC,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,EACzB,OAAO,CAAC,EAAE;QACN;;;;;;;;WAQG;QACH,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB;;;;;;;;;WASG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACxC,GACF,OAAO,CAAC,WAAW,CAAC;IAmLvB;;;OAGG;IACG,aAAa,CACf,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC;IAa3B;;;;;;;;;;OAUG;IACG,qBAAqB,CACvB,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IAShB;;;OAGG;IACG,uBAAuB,CACzB,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IAIhB,mFAAmF;IAC7E,SAAS,CACX,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC;IAa3B;;;;;OAKG;IACG,sBAAsB,CACxB,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAkB5B,sFAAsF;IAChF,iBAAiB,CACnB,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IAoBhB;;;;OAIG;IACG,iBAAiB,CACnB,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC;IAkBpB;;;;;;;;;;;;OAYG;IACG,aAAa,CACf,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC;IAsBlB;;;;;OAKG;IACG,MAAM,CACR,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC;IA8ChB;;;;;OAKG;IACG,OAAO,CACT,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAC1B,OAAO,CAAC,WAAW,CAAC;IAqCvB,2EAA2E;IACrE,KAAK,CACP,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAC1B,OAAO,CAAC,IAAI,CAAC;IAkChB,wEAAwE;IAClE,UAAU,CACZ,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAC1B,OAAO,CAAC,gBAAgB,CAAC;IA6C5B,4EAA4E;IACtE,SAAS,CACX,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAC1B,OAAO,CAAC,gBAAgB,CAAC;IA8B5B;;;;;OAKG;IACG,WAAW,CACb,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,GAC1B,OAAO,CAAC,gBAAgB,CAAC;IAkC5B;;;;;OAKG;IACH;;;;;;;;;;;;;;OAcG;YACW,eAAe;YAcf,aAAa;IA8C3B,2EAA2E;IAC3E,OAAO,CAAC,OAAO;IAIf;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAIb;;;OAGG;IACH,OAAO,CAAC,SAAS;IAajB;;;;OAIG;IACG,QAAQ,CACV,IAAI,EAAE,cAAc,EACpB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,QAAQ,GAAG,aAAuB,GACzC,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC;IAS3B;;;;OAIG;IACG,aAAa,CACf,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,QAAQ,GAAG,aAAuB,GACzC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAgB5B;;;;;;;;;;;;;OAaG;IACH;;;;;;;;;;;;;;;;;;OAkBG;YACW,qBAAqB;YA4DrB,qBAAqB;IAiHnC;;;;;;;;;;;;;;OAcG;YACW,kBAAkB;IAoDhC;;;;;;;;;;OAUG;YACW,uBAAuB;IAoBrC;;;;;;;;OAQG;IACG,sBAAsB,CACxB,IAAI,EAAE,QAAQ,GAAG,aAAa,EAC9B,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC;IA8B7B,qEAAqE;IACrE,OAAO,CAAC,WAAW;IAanB,0DAA0D;IAC1D,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,QAAQ;CAKnB"}
|
|
@@ -10,6 +10,7 @@ const utils_server_1 = require("@orthacms/utils-server");
|
|
|
10
10
|
const content_domain_1 = require("@orthacms/content-domain");
|
|
11
11
|
const entry_extension_1 = require("../../../extension/entry-extension");
|
|
12
12
|
const media_asset_resolver_1 = require("../../../extension/media-asset-resolver");
|
|
13
|
+
const entry_write_extension_1 = require("../../../extension/entry-write-extension");
|
|
13
14
|
const media_accept_1 = require("./media-accept");
|
|
14
15
|
const content_type_1 = require("../../../types/content-type");
|
|
15
16
|
const fields_1 = require("../../../types/fields");
|
|
@@ -58,19 +59,28 @@ let EntryWriterService = class EntryWriterService {
|
|
|
58
59
|
validation;
|
|
59
60
|
relations;
|
|
60
61
|
revisionStore;
|
|
62
|
+
writeExtensions;
|
|
61
63
|
extension;
|
|
62
64
|
mediaResolver;
|
|
63
65
|
constructor(db,
|
|
64
66
|
// The transaction boundary every write now runs inside, and the outbox
|
|
65
67
|
// that joins it. Together they are what makes an entry's domain event
|
|
66
68
|
// commit with — and never without — the row it describes.
|
|
67
|
-
uow, outbox, validation, relations, revisionStore,
|
|
69
|
+
uow, outbox, validation, relations, revisionStore,
|
|
70
|
+
// Every bound entry-write extension (segments' reader entitlements
|
|
71
|
+
// today) — state another plugin owns *about* the entry, written on this
|
|
72
|
+
// save's transaction and captured by its revision. A registry rather
|
|
73
|
+
// than a token because Nest cannot merge two bindings of one token, and
|
|
74
|
+
// an extension that silently stopped being called would silently stop
|
|
75
|
+
// restricting content.
|
|
76
|
+
writeExtensions, extension, mediaResolver) {
|
|
68
77
|
this.db = db;
|
|
69
78
|
this.uow = uow;
|
|
70
79
|
this.outbox = outbox;
|
|
71
80
|
this.validation = validation;
|
|
72
81
|
this.relations = relations;
|
|
73
82
|
this.revisionStore = revisionStore;
|
|
83
|
+
this.writeExtensions = writeExtensions;
|
|
74
84
|
this.extension = extension;
|
|
75
85
|
this.mediaResolver = mediaResolver;
|
|
76
86
|
}
|
|
@@ -87,18 +97,40 @@ let EntryWriterService = class EntryWriterService {
|
|
|
87
97
|
tx() {
|
|
88
98
|
return this.uow.current();
|
|
89
99
|
}
|
|
90
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* One event per affected row, built from each row's own id **and the row
|
|
102
|
+
* itself** — the latter because an entry event carries a frozen `title`
|
|
103
|
+
* snapshot, and a title is per row rather than per batch.
|
|
104
|
+
*/
|
|
91
105
|
eventsFor(rows, build) {
|
|
92
|
-
return rows.map((row) => build(row['id']));
|
|
106
|
+
return rows.map((row) => build(row['id'], row));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* What an entry event says about its subject: the type, the workspace, and
|
|
110
|
+
* a **frozen** human label read off the row being written.
|
|
111
|
+
*
|
|
112
|
+
* Frozen rather than looked up, for the same reason `actor_email` is: the
|
|
113
|
+
* audit row has to outlive its subject. `entry.purged` destroys the only
|
|
114
|
+
* other copy of the name, so a row that stored just the uuid could never
|
|
115
|
+
* answer "what did they delete" — not even in principle.
|
|
116
|
+
*/
|
|
117
|
+
subjectOf(type, workspaceId, row) {
|
|
118
|
+
return {
|
|
119
|
+
contentType: type.name,
|
|
120
|
+
workspaceId,
|
|
121
|
+
title: row ? (0, entry_row_1.entryTitle)(type, row) : null
|
|
122
|
+
};
|
|
93
123
|
}
|
|
94
124
|
/**
|
|
95
125
|
* Append `events` to the transactional outbox, stamped with the acting
|
|
96
|
-
*
|
|
126
|
+
* principal, from inside the active unit of work.
|
|
97
127
|
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
128
|
+
* A **token** may be the actor here, and that is the whole difference from
|
|
129
|
+
* {@link revisionActorId} below: the audit row now carries an `actor_type`,
|
|
130
|
+
* so naming a credential is a fact rather than a misused user column. A
|
|
131
|
+
* write made with a bearer token used to reach the log as "System", which
|
|
132
|
+
* left every write over the public REST API, GraphQL and MCP unattributable
|
|
133
|
+
* to the credential that made it.
|
|
102
134
|
*/
|
|
103
135
|
async emit(events, actor) {
|
|
104
136
|
if (events.length === 0)
|
|
@@ -216,6 +248,17 @@ let EntryWriterService = class EntryWriterService {
|
|
|
216
248
|
async appendRevision(tx, type, row, workspaceId, actorId) {
|
|
217
249
|
const id = row['id'];
|
|
218
250
|
const links = await this.relations.snapshotLinks(tx, type, row);
|
|
251
|
+
// Whatever the bound extensions hold for this row *now* — read on the
|
|
252
|
+
// save's own transaction, after any `apply` has run, so the version
|
|
253
|
+
// records what committed. Captured unconditionally, including for a
|
|
254
|
+
// sibling row this save only touched indirectly: a version that omitted
|
|
255
|
+
// the state it did not change would restore as a version that had none.
|
|
256
|
+
const extra = await this.writeExtensions.captureAll({
|
|
257
|
+
executor: tx,
|
|
258
|
+
type,
|
|
259
|
+
entryId: id,
|
|
260
|
+
workspaceId
|
|
261
|
+
});
|
|
219
262
|
await this.revisionStore.lockEntry(tx, id);
|
|
220
263
|
const number = await this.revisionStore.nextNumber(tx, id);
|
|
221
264
|
const revision = revision_1.Revision.createDraft({
|
|
@@ -225,7 +268,7 @@ let EntryWriterService = class EntryWriterService {
|
|
|
225
268
|
localeGroupId: row['localeGroupId'] ?? null,
|
|
226
269
|
locale: row['locale'] ?? null,
|
|
227
270
|
number,
|
|
228
|
-
snapshot: (0, revision_snapshot_1.buildSnapshot)(type, row, links),
|
|
271
|
+
snapshot: (0, revision_snapshot_1.buildSnapshot)(type, row, links, extra),
|
|
229
272
|
createdBy: actorId
|
|
230
273
|
});
|
|
231
274
|
await this.revisionStore.append(tx, revision);
|
|
@@ -241,14 +284,50 @@ let EntryWriterService = class EntryWriterService {
|
|
|
241
284
|
* advisory lock, and a stable order across concurrent savers is what keeps
|
|
242
285
|
* two of them from deadlocking on the pair.
|
|
243
286
|
*/
|
|
244
|
-
async appendRevisionsFor(tx, type, rows, workspaceId, actorId
|
|
245
|
-
|
|
287
|
+
async appendRevisionsFor(tx, type, rows, workspaceId, actorId,
|
|
288
|
+
/**
|
|
289
|
+
* Ids of further rows an **entry-write extension** changed, which it
|
|
290
|
+
* reports rather than returns whole — it wrote a table of its own, not
|
|
291
|
+
* these ones, so it has no row to hand back. Merged with `rows` and
|
|
292
|
+
* deduplicated by id: one save can reach a sibling through both paths
|
|
293
|
+
* (a shared field *and* an audience), and appending two revisions for it
|
|
294
|
+
* would read as two edits.
|
|
295
|
+
*/
|
|
296
|
+
alsoChangedIds = []) {
|
|
297
|
+
const byId = new Map();
|
|
298
|
+
for (const row of rows ?? [])
|
|
299
|
+
byId.set(String(row['id']), row);
|
|
300
|
+
const missing = alsoChangedIds.filter((id) => !byId.has(id));
|
|
301
|
+
for (const row of await this.rowsByIds(tx, type, missing, workspaceId)) {
|
|
302
|
+
byId.set(String(row['id']), row);
|
|
303
|
+
}
|
|
304
|
+
if (!byId.size)
|
|
246
305
|
return;
|
|
247
|
-
const ordered = [...
|
|
306
|
+
const ordered = [...byId.values()].sort((a, b) => String(a['id']).localeCompare(String(b['id'])));
|
|
248
307
|
for (const row of ordered) {
|
|
249
308
|
await this.appendRevision(tx, type, row, workspaceId, actorId);
|
|
250
309
|
}
|
|
251
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* The rows behind a set of ids, on this transaction.
|
|
313
|
+
*
|
|
314
|
+
* Soft-deleted rows are **included**: a locale in the trash is still a row
|
|
315
|
+
* whose state an extension just changed, and it comes back on restore
|
|
316
|
+
* expecting a timeline that says so.
|
|
317
|
+
*/
|
|
318
|
+
async rowsByIds(tx, type, ids, workspaceId) {
|
|
319
|
+
if (!ids.length)
|
|
320
|
+
return [];
|
|
321
|
+
const columns = type.table;
|
|
322
|
+
const id = columns['id'];
|
|
323
|
+
const workspace = columns['workspaceId'];
|
|
324
|
+
if (!id || !workspace)
|
|
325
|
+
return [];
|
|
326
|
+
return (await tx
|
|
327
|
+
.select()
|
|
328
|
+
.from(type.table)
|
|
329
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.inArray)(id, [...ids]), (0, drizzle_orm_1.eq)(workspace, workspaceId))));
|
|
330
|
+
}
|
|
252
331
|
/**
|
|
253
332
|
* Create an entry **owned by `workspaceId`** (stamped onto the row so the
|
|
254
333
|
* reader's workspace filter and every later scoped write see it). A
|
|
@@ -266,9 +345,14 @@ let EntryWriterService = class EntryWriterService {
|
|
|
266
345
|
* extension validates them and stamps the envelope columns (a
|
|
267
346
|
* `localeGroupId` makes the row a **sibling** in that translation group). A
|
|
268
347
|
* duplicate `(group, locale)` surfaces as a **409** via {@link uniqueGuarded}.
|
|
348
|
+
*
|
|
349
|
+
* `extensions` is the same kind of thing one plugin further out: an opaque
|
|
350
|
+
* bag forwarded to the bound {@link EntryWriteExtensionRegistry}, so a plugin
|
|
351
|
+
* that owns state *about* an entry (segments' audiences) writes it inside
|
|
352
|
+
* this transaction and has it captured by the same version.
|
|
269
353
|
*/
|
|
270
|
-
async create(type, values, workspaceId, relations, locale, localeGroupId, actor) {
|
|
271
|
-
const actorId = actor
|
|
354
|
+
async create(type, values, workspaceId, relations, locale, localeGroupId, actor, extensions) {
|
|
355
|
+
const actorId = revisionActorId(actor);
|
|
272
356
|
// `coerceValues` stamps every declared field onto the bag, so the
|
|
273
357
|
// "sent in both places" check below has to read the RAW body — after
|
|
274
358
|
// coercion every field looks present.
|
|
@@ -342,16 +426,42 @@ let EntryWriterService = class EntryWriterService {
|
|
|
342
426
|
// group must land consistent with the group's shared values.
|
|
343
427
|
// A no-op for a fresh, sibling-less group.
|
|
344
428
|
const touched = await this.extension?.afterUpdate(tx, type, inserted, coerced, workspaceId, { created: true });
|
|
429
|
+
// Anything a bound plugin was asked to store about this
|
|
430
|
+
// entry — the row exists now, so it finally has something to
|
|
431
|
+
// hang off, and it lands before the snapshot below reads it
|
|
432
|
+
// back. A create is the case that forced this into the write
|
|
433
|
+
// at all: until the insert there is no id to write against.
|
|
434
|
+
const alsoChanged = await this.writeExtensions.applyAll({
|
|
435
|
+
executor: tx,
|
|
436
|
+
type,
|
|
437
|
+
entryId: id,
|
|
438
|
+
workspaceId
|
|
439
|
+
}, extensions);
|
|
440
|
+
// …and, for a row joining an existing locale group, whatever
|
|
441
|
+
// that group already holds. `applyAll` runs only for keys
|
|
442
|
+
// the caller sent, and "create a translation" sends none —
|
|
443
|
+
// so without this a translation of a restricted article was
|
|
444
|
+
// a public copy of it. After `applyAll`, so an extension the
|
|
445
|
+
// caller just told what to store is not overwritten.
|
|
446
|
+
await this.writeExtensions.inheritAll({
|
|
447
|
+
executor: tx,
|
|
448
|
+
type,
|
|
449
|
+
entryId: id,
|
|
450
|
+
workspaceId
|
|
451
|
+
});
|
|
345
452
|
// Snapshot the just-created document as its first revision,
|
|
346
453
|
// inside this same transaction.
|
|
347
454
|
await this.appendRevision(tx, type, inserted, workspaceId, actorId ?? null);
|
|
348
|
-
// …and one for every sibling
|
|
349
|
-
// shared value landing on them is in
|
|
350
|
-
|
|
455
|
+
// …and one for every sibling either extension path rewrote,
|
|
456
|
+
// so a shared value — or an audience — landing on them is in
|
|
457
|
+
// their history too.
|
|
458
|
+
await this.appendRevisionsFor(tx, type, touched, workspaceId, actorId ?? null, alsoChanged);
|
|
351
459
|
// Inside the transaction, so the fact and the row it
|
|
352
460
|
// describes commit together or not at all — the whole point
|
|
353
461
|
// of the outbox.
|
|
354
|
-
await this.emit([
|
|
462
|
+
await this.emit([
|
|
463
|
+
(0, entry_events_1.entryCreated)(id, this.subjectOf(type, workspaceId, inserted))
|
|
464
|
+
], actor);
|
|
355
465
|
return inserted;
|
|
356
466
|
}), type);
|
|
357
467
|
return (0, entry_row_1.toRecord)(type, row);
|
|
@@ -431,7 +541,7 @@ let EntryWriterService = class EntryWriterService {
|
|
|
431
541
|
* type validates now.
|
|
432
542
|
*/
|
|
433
543
|
async update(type, id, values, workspaceId, relations, actor, options) {
|
|
434
|
-
const actorId = actor
|
|
544
|
+
const actorId = revisionActorId(actor);
|
|
435
545
|
let coerced = (0, entry_row_1.coerceValues)(type, values);
|
|
436
546
|
// The row's own locale is what a relation link must match. Read up front
|
|
437
547
|
// (one indexed lookup, and only on localized types) so the pre-transaction
|
|
@@ -504,19 +614,32 @@ let EntryWriterService = class EntryWriterService {
|
|
|
504
614
|
// locale siblings) run inside the same transaction — a failure
|
|
505
615
|
// rolls the whole save back.
|
|
506
616
|
const touched = await this.extension?.afterUpdate(tx, type, updated, coerced, workspaceId, { created: false });
|
|
617
|
+
// Whatever a bound plugin was asked to store about this entry, on
|
|
618
|
+
// this transaction and before the snapshot reads it back — so the
|
|
619
|
+
// version records the audiences the save actually applied rather
|
|
620
|
+
// than the ones it replaced. This runs even when no version is
|
|
621
|
+
// appended (publish-a-version): the state is the entry's, not the
|
|
622
|
+
// revision's.
|
|
623
|
+
const alsoChanged = await this.writeExtensions.applyAll({ executor: tx, type, entryId: id, workspaceId }, options?.extensions);
|
|
507
624
|
// Snapshot the updated document as a new draft revision, inside this
|
|
508
625
|
// same transaction — unless the caller is re-applying a version that
|
|
509
626
|
// already exists in the timeline (publish-a-version).
|
|
510
627
|
if (options?.appendRevision ?? true) {
|
|
511
628
|
await this.appendRevision(tx, type, updated, workspaceId, actorId ?? null);
|
|
512
629
|
}
|
|
513
|
-
// …and one for every sibling
|
|
514
|
-
|
|
630
|
+
// …and one for every sibling either extension path rewrote — the
|
|
631
|
+
// rows i18n synced a shared field to, and the rows a write extension
|
|
632
|
+
// reports it also changed (segments writes an entry's audiences to
|
|
633
|
+
// its whole locale group). Both are the same defect if skipped: a
|
|
634
|
+
// row whose state moved while its timeline did not.
|
|
635
|
+
await this.appendRevisionsFor(tx, type, touched, workspaceId, actorId ?? null, alsoChanged);
|
|
515
636
|
// A save that changed no field value raises nothing: it is a round
|
|
516
637
|
// trip, not an editorial change, and the log is read by people.
|
|
517
638
|
const fields = this.changedFields(type, before, updated);
|
|
518
639
|
if (fields.length > 0) {
|
|
519
|
-
await this.emit([
|
|
640
|
+
await this.emit([
|
|
641
|
+
(0, entry_events_1.entryUpdated)(id, this.subjectOf(type, workspaceId, updated), fields)
|
|
642
|
+
], actor);
|
|
520
643
|
}
|
|
521
644
|
return updated;
|
|
522
645
|
});
|
|
@@ -685,7 +808,9 @@ let EntryWriterService = class EntryWriterService {
|
|
|
685
808
|
.returning(), type);
|
|
686
809
|
if (!row)
|
|
687
810
|
throw this.notFound(type, id);
|
|
688
|
-
await this.emit([
|
|
811
|
+
await this.emit([
|
|
812
|
+
(0, entry_events_1.entryDeleted)(id, this.subjectOf(type, workspaceId, row), Boolean(type.paranoid))
|
|
813
|
+
], actor);
|
|
689
814
|
});
|
|
690
815
|
}
|
|
691
816
|
/**
|
|
@@ -709,7 +834,9 @@ let EntryWriterService = class EntryWriterService {
|
|
|
709
834
|
.returning(), type);
|
|
710
835
|
if (!row)
|
|
711
836
|
throw this.notFound(type, id);
|
|
712
|
-
await this.emit([
|
|
837
|
+
await this.emit([
|
|
838
|
+
(0, entry_events_1.entryRestored)(id, this.subjectOf(type, workspaceId, row))
|
|
839
|
+
], actor);
|
|
713
840
|
return (0, entry_row_1.toRecord)(type, row);
|
|
714
841
|
});
|
|
715
842
|
}
|
|
@@ -727,7 +854,9 @@ let EntryWriterService = class EntryWriterService {
|
|
|
727
854
|
throw this.notFound(type, id);
|
|
728
855
|
// The one content action that leaves nothing behind to inspect, so
|
|
729
856
|
// the event is the only remaining record that it happened.
|
|
730
|
-
await this.emit([
|
|
857
|
+
await this.emit([
|
|
858
|
+
(0, entry_events_1.entryPurged)(id, this.subjectOf(type, workspaceId, row))
|
|
859
|
+
], actor);
|
|
731
860
|
});
|
|
732
861
|
}
|
|
733
862
|
/** Delete a set of entries: soft for paranoid types, hard otherwise. */
|
|
@@ -753,7 +882,7 @@ let EntryWriterService = class EntryWriterService {
|
|
|
753
882
|
.returning(), type);
|
|
754
883
|
// One event per row actually removed, not one per id asked for:
|
|
755
884
|
// batching is a way of asking, and the log records what happened.
|
|
756
|
-
await this.emit(this.eventsFor(rows, (id) => (0, entry_events_1.entryDeleted)(id, type
|
|
885
|
+
await this.emit(this.eventsFor(rows, (id, row) => (0, entry_events_1.entryDeleted)(id, this.subjectOf(type, workspaceId, row), Boolean(type.paranoid))), actor);
|
|
757
886
|
return { count: rows.length };
|
|
758
887
|
});
|
|
759
888
|
}
|
|
@@ -769,7 +898,7 @@ let EntryWriterService = class EntryWriterService {
|
|
|
769
898
|
.delete(type.table)
|
|
770
899
|
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.inArray)(t['id'], ids), this.scope(type, workspaceId), (0, drizzle_orm_1.isNotNull)(t['deletedAt'])))
|
|
771
900
|
.returning(), type);
|
|
772
|
-
await this.emit(this.eventsFor(rows, (id) => (0, entry_events_1.entryPurged)(id, type
|
|
901
|
+
await this.emit(this.eventsFor(rows, (id, row) => (0, entry_events_1.entryPurged)(id, this.subjectOf(type, workspaceId, row))), actor);
|
|
773
902
|
return { count: rows.length };
|
|
774
903
|
});
|
|
775
904
|
}
|
|
@@ -794,7 +923,7 @@ let EntryWriterService = class EntryWriterService {
|
|
|
794
923
|
})
|
|
795
924
|
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.inArray)(t['id'], ids), this.scope(type, workspaceId), (0, drizzle_orm_1.isNotNull)(t['deletedAt'])))
|
|
796
925
|
.returning(), type);
|
|
797
|
-
await this.emit(this.eventsFor(rows, (id) => (0, entry_events_1.entryRestored)(id, type
|
|
926
|
+
await this.emit(this.eventsFor(rows, (id, row) => (0, entry_events_1.entryRestored)(id, this.subjectOf(type, workspaceId, row))), actor);
|
|
798
927
|
return { count: rows.length };
|
|
799
928
|
});
|
|
800
929
|
}
|
|
@@ -1221,12 +1350,34 @@ exports.EntryWriterService = EntryWriterService = tslib_1.__decorate([
|
|
|
1221
1350
|
(0, common_1.Injectable)(),
|
|
1222
1351
|
tslib_1.__param(0, (0, database_1.InjectDatabase)()),
|
|
1223
1352
|
tslib_1.__param(5, (0, revision_store_1.InjectRevisionStore)()),
|
|
1224
|
-
tslib_1.__param(6, (0, common_1.Optional)()),
|
|
1225
|
-
tslib_1.__param(6, (0, common_1.Inject)(entry_extension_1.CONTENT_ENTRY_EXTENSION)),
|
|
1226
1353
|
tslib_1.__param(7, (0, common_1.Optional)()),
|
|
1227
|
-
tslib_1.__param(7, (0,
|
|
1354
|
+
tslib_1.__param(7, (0, common_1.Inject)(entry_extension_1.CONTENT_ENTRY_EXTENSION)),
|
|
1355
|
+
tslib_1.__param(8, (0, common_1.Optional)()),
|
|
1356
|
+
tslib_1.__param(8, (0, media_asset_resolver_1.InjectMediaAssetResolver)()),
|
|
1228
1357
|
tslib_1.__metadata("design:paramtypes", [Object, database_1.UnitOfWork,
|
|
1229
1358
|
database_1.OutboxWriter,
|
|
1230
1359
|
entry_validation_service_1.EntryValidationService,
|
|
1231
|
-
relation_link_service_1.RelationLinkService, Object, Object, Object])
|
|
1360
|
+
relation_link_service_1.RelationLinkService, Object, entry_write_extension_1.EntryWriteExtensionRegistry, Object, Object])
|
|
1232
1361
|
], EntryWriterService);
|
|
1362
|
+
/**
|
|
1363
|
+
* The **user** id to stamp on a revision, or `null` when the writer is not a
|
|
1364
|
+
* person.
|
|
1365
|
+
*
|
|
1366
|
+
* `content_entry_revisions.created_by` records a `users` row. A token is not
|
|
1367
|
+
* one, so writing its id there would make the history resolve every version to
|
|
1368
|
+
* a nonexistent person — which is why the public API used to pass no actor at
|
|
1369
|
+
* all rather than pass the token. The audit path has since gained an
|
|
1370
|
+
* `actor_type` and can name a credential honestly, so the two answers had to be
|
|
1371
|
+
* separated: {@link EntryWriterService.emit} gets the real principal, and this
|
|
1372
|
+
* gets a user id or nothing.
|
|
1373
|
+
*
|
|
1374
|
+
* An actor with no `type` is a person — every actor predates the field except
|
|
1375
|
+
* the token ones that introduced it.
|
|
1376
|
+
*/
|
|
1377
|
+
function revisionActorId(actor) {
|
|
1378
|
+
if (!actor)
|
|
1379
|
+
return null;
|
|
1380
|
+
return (actor.type ?? database_1.EVENT_ACTOR_TYPE.User) === database_1.EVENT_ACTOR_TYPE.User
|
|
1381
|
+
? actor.id
|
|
1382
|
+
: null;
|
|
1383
|
+
}
|