@happyvertical/smrt-content 0.43.9 → 0.43.10
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/agents/content-list.md +69 -0
- package/dist/chunks/content-corrections-BxfA3tW1.js +107 -0
- package/dist/chunks/content-corrections-BxfA3tW1.js.map +1 -0
- package/dist/chunks/content-query-4w62u5_0.js +4092 -0
- package/dist/chunks/content-query-4w62u5_0.js.map +1 -0
- package/dist/chunks/content-reviews-BWrYEQc6.js +124 -0
- package/dist/chunks/content-reviews-BWrYEQc6.js.map +1 -0
- package/dist/chunks/content-versions-Di3vcTgb.js +284 -0
- package/dist/chunks/content-versions-Di3vcTgb.js.map +1 -0
- package/dist/chunks/contents-BV8RoP_R.js +612 -0
- package/dist/chunks/contents-BV8RoP_R.js.map +1 -0
- package/dist/chunks/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/content-chat-handlers.d.ts +1 -1
- package/dist/content-chat-handlers.d.ts.map +1 -1
- package/dist/content-governance.d.ts +2 -0
- package/dist/content-governance.d.ts.map +1 -1
- package/dist/content.d.ts +2 -2
- package/dist/content.d.ts.map +1 -1
- package/dist/index.js +142 -4717
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +142 -5
- package/dist/server/content-list-actions.d.ts +123 -0
- package/dist/server/content-list-actions.d.ts.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +874 -0
- package/dist/server.js.map +1 -0
- package/dist/smrt-knowledge.json +96 -9
- package/dist/svelte/components/ContentList.svelte +1028 -24
- package/dist/svelte/components/ContentList.svelte.d.ts +3 -0
- package/dist/svelte/components/ContentList.svelte.d.ts.map +1 -1
- package/dist/svelte/content-list-controller.d.ts +3 -1
- package/dist/svelte/content-list-controller.d.ts.map +1 -1
- package/dist/svelte/content-list-controller.js +15 -1
- package/dist/svelte/content-list-workflows.d.ts +87 -0
- package/dist/svelte/content-list-workflows.d.ts.map +1 -0
- package/dist/svelte/content-list-workflows.js +199 -0
- package/dist/svelte/i18n.contribution.d.ts +109 -0
- package/dist/svelte/i18n.contribution.d.ts.map +1 -1
- package/dist/svelte/i18n.contribution.js +109 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +2 -0
- package/package.json +19 -14
- package/dist/chunks/content-transparency-Bh6axaZj.js +0 -606
- package/dist/chunks/content-transparency-Bh6axaZj.js.map +0 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { SmrtCollection, SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
3
|
+
import { TenantScoped, tenantId } from "@happyvertical/smrt-tenancy";
|
|
4
|
+
//#region src/content-review.ts
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
8
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
9
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
10
|
+
if (kind && result) __defProp(target, key, result);
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
13
|
+
var ContentReview = class extends SmrtObject {
|
|
14
|
+
contentId = "";
|
|
15
|
+
contentVersionId = "";
|
|
16
|
+
kind = "custom";
|
|
17
|
+
policyKey = "";
|
|
18
|
+
status = "pending";
|
|
19
|
+
summary = "";
|
|
20
|
+
findings = "[]";
|
|
21
|
+
reviewer = "system";
|
|
22
|
+
metadata = "";
|
|
23
|
+
tenantId = null;
|
|
24
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
25
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
super(options);
|
|
28
|
+
if (options.contentId) this.contentId = options.contentId;
|
|
29
|
+
if (options.contentVersionId !== void 0) this.contentVersionId = options.contentVersionId;
|
|
30
|
+
if (options.kind !== void 0) this.kind = options.kind;
|
|
31
|
+
if (options.policyKey !== void 0) this.policyKey = options.policyKey;
|
|
32
|
+
if (options.status !== void 0) this.status = options.status;
|
|
33
|
+
if (options.summary !== void 0) this.summary = options.summary;
|
|
34
|
+
if (options.reviewer !== void 0) this.reviewer = options.reviewer;
|
|
35
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
36
|
+
if (options.createdAt) this.createdAt = options.createdAt;
|
|
37
|
+
if (options.updatedAt) this.updatedAt = options.updatedAt;
|
|
38
|
+
if (options.findings !== void 0) this.findings = typeof options.findings === "string" ? options.findings : JSON.stringify(options.findings);
|
|
39
|
+
if (options.metadata !== void 0) this.metadata = typeof options.metadata === "string" ? options.metadata : JSON.stringify(options.metadata);
|
|
40
|
+
}
|
|
41
|
+
getFindings() {
|
|
42
|
+
if (Array.isArray(this.findings)) return this.findings;
|
|
43
|
+
try {
|
|
44
|
+
return this.findings ? JSON.parse(this.findings) : [];
|
|
45
|
+
} catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
getMetadata() {
|
|
50
|
+
if (this.metadata && typeof this.metadata === "object") return { ...this.metadata };
|
|
51
|
+
try {
|
|
52
|
+
return this.metadata ? JSON.parse(this.metadata) : {};
|
|
53
|
+
} catch {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
__decorateClass([foreignKey("Content", { required: true })], ContentReview.prototype, "contentId", 2);
|
|
59
|
+
__decorateClass([foreignKey("ContentVersion")], ContentReview.prototype, "contentVersionId", 2);
|
|
60
|
+
__decorateClass([tenantId({ nullable: true })], ContentReview.prototype, "tenantId", 2);
|
|
61
|
+
ContentReview = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
|
|
62
|
+
tableName: "content_reviews",
|
|
63
|
+
api: { include: [
|
|
64
|
+
"list",
|
|
65
|
+
"get",
|
|
66
|
+
"create",
|
|
67
|
+
"update"
|
|
68
|
+
] },
|
|
69
|
+
mcp: { include: [
|
|
70
|
+
"list",
|
|
71
|
+
"get",
|
|
72
|
+
"create",
|
|
73
|
+
"update"
|
|
74
|
+
] },
|
|
75
|
+
cli: true
|
|
76
|
+
})], ContentReview);
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/content-reviews.ts
|
|
79
|
+
var content_reviews_exports = /* @__PURE__ */ __exportAll({ ContentReviewCollection: () => ContentReviewCollection });
|
|
80
|
+
var ContentReviewCollection = class extends SmrtCollection {
|
|
81
|
+
static _itemClass = ContentReview;
|
|
82
|
+
async listForContent(contentId, kind) {
|
|
83
|
+
return this.list({
|
|
84
|
+
where: kind ? {
|
|
85
|
+
contentId,
|
|
86
|
+
kind
|
|
87
|
+
} : { contentId },
|
|
88
|
+
orderBy: "created_at DESC"
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async getLatestForContent(contentId, kind) {
|
|
92
|
+
return (await this.listForContent(contentId, kind))[0] ?? null;
|
|
93
|
+
}
|
|
94
|
+
async listForContentByPolicyKey(contentId, policyKey) {
|
|
95
|
+
return this.list({
|
|
96
|
+
where: {
|
|
97
|
+
contentId,
|
|
98
|
+
policyKey
|
|
99
|
+
},
|
|
100
|
+
orderBy: "created_at DESC"
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async getLatestForPolicyKey(contentId, policyKey) {
|
|
104
|
+
return (await this.listForContentByPolicyKey(contentId, policyKey))[0] ?? null;
|
|
105
|
+
}
|
|
106
|
+
async createFromResult(options) {
|
|
107
|
+
return this.create({
|
|
108
|
+
contentId: options.contentId,
|
|
109
|
+
contentVersionId: options.contentVersionId || "",
|
|
110
|
+
kind: options.kind,
|
|
111
|
+
policyKey: options.policyKey,
|
|
112
|
+
status: options.result.status,
|
|
113
|
+
summary: options.result.summary,
|
|
114
|
+
findings: JSON.stringify(options.result.findings),
|
|
115
|
+
reviewer: options.reviewer || "system",
|
|
116
|
+
metadata: JSON.stringify(options.metadata || {}),
|
|
117
|
+
tenantId: options.tenantId ?? null
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
//#endregion
|
|
122
|
+
export { content_reviews_exports as n, ContentReview as r, ContentReviewCollection as t };
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=content-reviews-BWrYEQc6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-reviews-BWrYEQc6.js","names":[],"sources":["../../src/content-review.ts","../../src/content-reviews.ts"],"sourcesContent":["import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ContentReviewFinding,\n ContentReviewKind,\n ContentReviewStatus,\n} from './content-governance';\n\nexport interface ContentReviewOptions extends SmrtObjectOptions {\n contentId?: string;\n contentVersionId?: string;\n kind?: ContentReviewKind;\n policyKey?: string;\n status?: ContentReviewStatus;\n summary?: string;\n findings?: string | ContentReviewFinding[];\n reviewer?: string;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_reviews',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentReview extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n @foreignKey('ContentVersion')\n contentVersionId = '';\n kind: ContentReviewKind = 'custom';\n policyKey = '';\n status: ContentReviewStatus = 'pending';\n summary = '';\n findings = '[]';\n reviewer = 'system';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentReviewOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.contentVersionId !== undefined)\n this.contentVersionId = options.contentVersionId;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.policyKey !== undefined) this.policyKey = options.policyKey;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.reviewer !== undefined) this.reviewer = options.reviewer;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.findings !== undefined) {\n this.findings =\n typeof options.findings === 'string'\n ? options.findings\n : JSON.stringify(options.findings);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getFindings(): ContentReviewFinding[] {\n if (Array.isArray(this.findings)) {\n return this.findings as ContentReviewFinding[];\n }\n\n try {\n return this.findings ? JSON.parse(this.findings) : [];\n } catch {\n return [];\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport type {\n ContentReviewKind,\n ContentReviewResult,\n} from './content-governance';\nimport { ContentReview } from './content-review';\n\nexport class ContentReviewCollection extends SmrtCollection<ContentReview> {\n static readonly _itemClass = ContentReview;\n\n async listForContent(\n contentId: string,\n kind?: ContentReviewKind,\n ): Promise<ContentReview[]> {\n return this.list({\n where: kind ? { contentId, kind } : { contentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async getLatestForContent(\n contentId: string,\n kind?: ContentReviewKind,\n ): Promise<ContentReview | null> {\n const reviews = await this.listForContent(contentId, kind);\n return reviews[0] ?? null;\n }\n\n async listForContentByPolicyKey(\n contentId: string,\n policyKey: string,\n ): Promise<ContentReview[]> {\n return this.list({\n where: { contentId, policyKey },\n orderBy: 'created_at DESC',\n });\n }\n\n async getLatestForPolicyKey(\n contentId: string,\n policyKey: string,\n ): Promise<ContentReview | null> {\n const reviews = await this.listForContentByPolicyKey(contentId, policyKey);\n return reviews[0] ?? null;\n }\n\n async createFromResult(options: {\n contentId: string;\n contentVersionId?: string | null;\n kind: ContentReviewKind;\n policyKey: string;\n reviewer?: string;\n result: ContentReviewResult;\n metadata?: Record<string, unknown>;\n tenantId?: string | null;\n }): Promise<ContentReview> {\n return this.create({\n contentId: options.contentId,\n contentVersionId: options.contentVersionId || '',\n kind: options.kind,\n policyKey: options.policyKey,\n status: options.result.status,\n summary: options.result.summary,\n findings: JSON.stringify(options.result.findings),\n reviewer: options.reviewer || 'system',\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: options.tenantId ?? null,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { A as normalizeContentTransparency, nt as resolveEffectiveContentGovernance } from "./content-query-4w62u5_0.js";
|
|
3
|
+
import { SmrtCollection, SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
4
|
+
import { TenantScoped, tenantId } from "@happyvertical/smrt-tenancy";
|
|
5
|
+
//#region src/content-version.ts
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
9
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
10
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
11
|
+
if (kind && result) __defProp(target, key, result);
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
var ContentVersion = class extends SmrtObject {
|
|
15
|
+
contentId = "";
|
|
16
|
+
version = 1;
|
|
17
|
+
kind = "manual";
|
|
18
|
+
title = "";
|
|
19
|
+
description = "";
|
|
20
|
+
body = "";
|
|
21
|
+
status = "draft";
|
|
22
|
+
summary = "";
|
|
23
|
+
snapshot = "{}";
|
|
24
|
+
metadata = "";
|
|
25
|
+
tenantId = null;
|
|
26
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
27
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
super(options);
|
|
30
|
+
if (options.contentId) this.contentId = options.contentId;
|
|
31
|
+
if (options.version !== void 0) this.version = options.version;
|
|
32
|
+
if (options.kind !== void 0) this.kind = options.kind;
|
|
33
|
+
if (options.title !== void 0) this.title = options.title;
|
|
34
|
+
if (options.description !== void 0) this.description = options.description;
|
|
35
|
+
if (options.body !== void 0) this.body = options.body;
|
|
36
|
+
if (options.status !== void 0) this.status = options.status;
|
|
37
|
+
if (options.summary !== void 0) this.summary = options.summary;
|
|
38
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
39
|
+
if (options.createdAt) this.createdAt = options.createdAt;
|
|
40
|
+
if (options.updatedAt) this.updatedAt = options.updatedAt;
|
|
41
|
+
if (options.snapshot !== void 0) this.snapshot = typeof options.snapshot === "string" ? options.snapshot : JSON.stringify(options.snapshot);
|
|
42
|
+
if (options.metadata !== void 0) this.metadata = typeof options.metadata === "string" ? options.metadata : JSON.stringify(options.metadata);
|
|
43
|
+
}
|
|
44
|
+
getSnapshot() {
|
|
45
|
+
try {
|
|
46
|
+
return this.snapshot ? JSON.parse(this.snapshot) : {};
|
|
47
|
+
} catch {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
getMetadata() {
|
|
52
|
+
try {
|
|
53
|
+
return this.metadata ? JSON.parse(this.metadata) : {};
|
|
54
|
+
} catch {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
getTransparency() {
|
|
59
|
+
const metadata = this.getMetadata();
|
|
60
|
+
const snapshot = this.getSnapshot();
|
|
61
|
+
return normalizeContentTransparency(metadata.transparency, {
|
|
62
|
+
snapshotKind: this.kind === "publication" ? "published" : "preview",
|
|
63
|
+
contentId: this.contentId || snapshot.contentId || null,
|
|
64
|
+
currentContentStatus: this.status || snapshot.status || "draft",
|
|
65
|
+
publicationVersion: {
|
|
66
|
+
id: this.id || null,
|
|
67
|
+
version: this.version ?? null,
|
|
68
|
+
kind: this.kind || null,
|
|
69
|
+
summary: this.summary || "",
|
|
70
|
+
createdAt: this.createdAt instanceof Date ? this.createdAt.toISOString() : null
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async getTransparencyAction() {
|
|
75
|
+
return this.getTransparency();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
__decorateClass([foreignKey("Content", { required: true })], ContentVersion.prototype, "contentId", 2);
|
|
79
|
+
__decorateClass([tenantId({ nullable: true })], ContentVersion.prototype, "tenantId", 2);
|
|
80
|
+
ContentVersion = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
|
|
81
|
+
tableName: "content_versions",
|
|
82
|
+
conflictColumns: ["content_id", "version"],
|
|
83
|
+
api: {
|
|
84
|
+
include: [
|
|
85
|
+
"list",
|
|
86
|
+
"get",
|
|
87
|
+
"create",
|
|
88
|
+
"getTransparencyAction"
|
|
89
|
+
],
|
|
90
|
+
routes: { getTransparencyAction: {
|
|
91
|
+
method: "GET",
|
|
92
|
+
path: "transparency"
|
|
93
|
+
} }
|
|
94
|
+
},
|
|
95
|
+
mcp: { include: [
|
|
96
|
+
"list",
|
|
97
|
+
"get",
|
|
98
|
+
"create"
|
|
99
|
+
] },
|
|
100
|
+
cli: true
|
|
101
|
+
})], ContentVersion);
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/content-versions.ts
|
|
104
|
+
var content_versions_exports = /* @__PURE__ */ __exportAll({ ContentVersionCollection: () => ContentVersionCollection });
|
|
105
|
+
var ContentVersionCollection = class extends SmrtCollection {
|
|
106
|
+
static _itemClass = ContentVersion;
|
|
107
|
+
buildSnapshotFactRelationships(snapshot, defaultRelationship) {
|
|
108
|
+
const byRelationship = /* @__PURE__ */ new Map();
|
|
109
|
+
const rawLinks = Array.isArray(snapshot.factLinks) ? snapshot.factLinks : [];
|
|
110
|
+
for (const rawLink of rawLinks) {
|
|
111
|
+
const link = rawLink;
|
|
112
|
+
const factId = typeof link?.factId === "string" && link.factId.length > 0 ? link.factId : null;
|
|
113
|
+
const relationship = typeof link?.relationship === "string" && link.relationship.length > 0 ? link.relationship : defaultRelationship;
|
|
114
|
+
if (!factId) continue;
|
|
115
|
+
byRelationship.set(relationship, [...byRelationship.get(relationship) || [], factId]);
|
|
116
|
+
}
|
|
117
|
+
if (byRelationship.size === 0 && Array.isArray(snapshot.factIds) && snapshot.factIds.length > 0) byRelationship.set(defaultRelationship, snapshot.factIds.filter((factId) => typeof factId === "string" && factId.length > 0));
|
|
118
|
+
return byRelationship;
|
|
119
|
+
}
|
|
120
|
+
async listForContent(contentId) {
|
|
121
|
+
return this.list({
|
|
122
|
+
where: { contentId },
|
|
123
|
+
orderBy: "version ASC"
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async getLatestForContent(contentId) {
|
|
127
|
+
const versions = await this.listForContent(contentId);
|
|
128
|
+
return versions.length > 0 ? versions[versions.length - 1] : null;
|
|
129
|
+
}
|
|
130
|
+
async getLatestPublishedForContent(contentId) {
|
|
131
|
+
return (await this.list({
|
|
132
|
+
where: {
|
|
133
|
+
contentId,
|
|
134
|
+
kind: "publication"
|
|
135
|
+
},
|
|
136
|
+
orderBy: "version DESC"
|
|
137
|
+
}))[0] || null;
|
|
138
|
+
}
|
|
139
|
+
async getVersion(contentId, versionNumber) {
|
|
140
|
+
return this.get({
|
|
141
|
+
contentId,
|
|
142
|
+
version: versionNumber
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async getNextVersionNumber(contentId) {
|
|
146
|
+
const latest = await this.getLatestForContent(contentId);
|
|
147
|
+
return latest ? latest.version + 1 : 1;
|
|
148
|
+
}
|
|
149
|
+
async createSnapshot(content, options = {}) {
|
|
150
|
+
if (!content.id) throw new Error("Cannot create a version for unsaved content");
|
|
151
|
+
const version = await this.getNextVersionNumber(content.id);
|
|
152
|
+
const governance = await resolveEffectiveContentGovernance({
|
|
153
|
+
contentType: content.type,
|
|
154
|
+
contentVariant: content.variant,
|
|
155
|
+
db: this.db,
|
|
156
|
+
tenantId: content.tenantId ?? null
|
|
157
|
+
});
|
|
158
|
+
const [references, referenceEdges, assets, factsState] = await Promise.all([
|
|
159
|
+
typeof content.getReferences === "function" ? content.getReferences() : [],
|
|
160
|
+
typeof content.getReferenceEdges === "function" ? content.getReferenceEdges() : Promise.resolve([]),
|
|
161
|
+
typeof content.getAssets === "function" ? content.getAssets() : [],
|
|
162
|
+
typeof content.getFactsState === "function" && governance.factLinkingEnabled ? content.getFactsState() : {
|
|
163
|
+
factIds: [],
|
|
164
|
+
facts: [],
|
|
165
|
+
factLinks: []
|
|
166
|
+
}
|
|
167
|
+
]);
|
|
168
|
+
const snapshot = {
|
|
169
|
+
id: content.id,
|
|
170
|
+
slug: content.slug,
|
|
171
|
+
context: content.context,
|
|
172
|
+
name: content.name,
|
|
173
|
+
type: content.type,
|
|
174
|
+
variant: content.variant,
|
|
175
|
+
fileKey: content.fileKey,
|
|
176
|
+
author: content.author,
|
|
177
|
+
title: content.title,
|
|
178
|
+
description: content.description,
|
|
179
|
+
body: content.body,
|
|
180
|
+
bodyFormat: content.bodyFormat,
|
|
181
|
+
publish_date: content.publish_date,
|
|
182
|
+
url: content.url,
|
|
183
|
+
source: content.source,
|
|
184
|
+
original_url: content.original_url,
|
|
185
|
+
language: content.language,
|
|
186
|
+
tags: [...content.tags],
|
|
187
|
+
category: content.category,
|
|
188
|
+
status: content.status,
|
|
189
|
+
state: content.state,
|
|
190
|
+
metadata: content.metadata,
|
|
191
|
+
thumbnailAssetId: content.thumbnailAssetId,
|
|
192
|
+
referenceIds: references.map((reference) => reference.id).filter(Boolean),
|
|
193
|
+
referenceEdges: referenceEdges.filter((edge) => Boolean(edge.targetId)),
|
|
194
|
+
assetIds: assets.map((asset) => asset.id).filter(Boolean),
|
|
195
|
+
factIds: factsState.factIds,
|
|
196
|
+
factLinks: factsState.factLinks,
|
|
197
|
+
tenantId: content.tenantId,
|
|
198
|
+
_meta_type: content.toJSON()._meta_type,
|
|
199
|
+
...options.snapshot || {}
|
|
200
|
+
};
|
|
201
|
+
const versionSlug = `${snapshot.slug || content.slug || content.name || content.title || content.id}-v${version}`;
|
|
202
|
+
return this.create({
|
|
203
|
+
slug: versionSlug,
|
|
204
|
+
context: content.context || "",
|
|
205
|
+
contentId: content.id,
|
|
206
|
+
version,
|
|
207
|
+
kind: options.kind || "manual",
|
|
208
|
+
title: snapshot.title || "",
|
|
209
|
+
description: snapshot.description || "",
|
|
210
|
+
body: snapshot.body || "",
|
|
211
|
+
status: snapshot.status || "draft",
|
|
212
|
+
summary: options.summary || "",
|
|
213
|
+
snapshot: JSON.stringify(snapshot),
|
|
214
|
+
metadata: JSON.stringify(options.metadata || {}),
|
|
215
|
+
tenantId: content.tenantId
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async restoreIntoContent(content, versionNumber) {
|
|
219
|
+
if (!content.id) throw new Error("Cannot restore an unsaved content item");
|
|
220
|
+
const version = await this.getVersion(content.id, versionNumber);
|
|
221
|
+
if (!version) throw new Error(`Content version ${versionNumber} not found for content ${content.id}`);
|
|
222
|
+
const snapshot = version.getSnapshot();
|
|
223
|
+
const keysToRestore = [
|
|
224
|
+
"name",
|
|
225
|
+
"type",
|
|
226
|
+
"variant",
|
|
227
|
+
"fileKey",
|
|
228
|
+
"author",
|
|
229
|
+
"title",
|
|
230
|
+
"description",
|
|
231
|
+
"body",
|
|
232
|
+
"bodyFormat",
|
|
233
|
+
"publish_date",
|
|
234
|
+
"url",
|
|
235
|
+
"source",
|
|
236
|
+
"original_url",
|
|
237
|
+
"language",
|
|
238
|
+
"tags",
|
|
239
|
+
"category",
|
|
240
|
+
"status",
|
|
241
|
+
"state",
|
|
242
|
+
"metadata",
|
|
243
|
+
"thumbnailAssetId"
|
|
244
|
+
];
|
|
245
|
+
const writableContent = content;
|
|
246
|
+
for (const key of keysToRestore) if (snapshot[key] !== void 0) writableContent[key] = snapshot[key];
|
|
247
|
+
const snapshotEdges = Array.isArray(snapshot.referenceEdges) ? snapshot.referenceEdges.filter((edge) => !!edge && typeof edge === "object" && typeof edge.targetId === "string" && edge.targetId.length > 0).map((edge) => ({
|
|
248
|
+
targetId: edge.targetId,
|
|
249
|
+
targetVersion: typeof edge.targetVersion === "number" ? edge.targetVersion : null
|
|
250
|
+
})) : Array.isArray(snapshot.referenceIds) ? snapshot.referenceIds.filter((id) => typeof id === "string" && id.length > 0).map((targetId) => ({
|
|
251
|
+
targetId,
|
|
252
|
+
targetVersion: null
|
|
253
|
+
})) : [];
|
|
254
|
+
if (Array.isArray(snapshot.referenceEdges) || Array.isArray(snapshot.referenceIds)) writableContent.referenceIds = snapshotEdges.map((edge) => edge.targetId);
|
|
255
|
+
if (Array.isArray(snapshot.assetIds)) writableContent.assetIds = [...snapshot.assetIds];
|
|
256
|
+
await content.save();
|
|
257
|
+
if (snapshotEdges.length > 0 && typeof content.getReferences === "function" && typeof content.addReference === "function") {
|
|
258
|
+
const resolvedReferences = await content.getReferences();
|
|
259
|
+
const resolvedById = new Map(resolvedReferences.filter((reference) => reference.id).map((reference) => [reference.id, reference]));
|
|
260
|
+
for (const edge of snapshotEdges) {
|
|
261
|
+
const target = resolvedById.get(edge.targetId);
|
|
262
|
+
if (target) await content.addReference(target, { targetVersion: edge.targetVersion });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const governance = await resolveEffectiveContentGovernance({
|
|
266
|
+
contentType: content.type,
|
|
267
|
+
contentVariant: content.variant,
|
|
268
|
+
db: this.db,
|
|
269
|
+
tenantId: content.tenantId ?? null
|
|
270
|
+
});
|
|
271
|
+
if (governance.isGoverned && governance.factLinkingEnabled && typeof content.getFactLinks === "function" && typeof content.syncFacts === "function") {
|
|
272
|
+
const desiredByRelationship = this.buildSnapshotFactRelationships(snapshot, governance.defaultFactRelationship);
|
|
273
|
+
const currentLinks = await content.getFactLinks();
|
|
274
|
+
const currentRelationships = new Set(currentLinks.map((link) => link.relationship || governance.defaultFactRelationship));
|
|
275
|
+
const relationshipsToSync = /* @__PURE__ */ new Set([...currentRelationships, ...desiredByRelationship.keys()]);
|
|
276
|
+
for (const relationship of relationshipsToSync) await content.syncFacts(desiredByRelationship.get(relationship) || [], relationship);
|
|
277
|
+
}
|
|
278
|
+
return content;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
//#endregion
|
|
282
|
+
export { content_versions_exports as n, ContentVersion as r, ContentVersionCollection as t };
|
|
283
|
+
|
|
284
|
+
//# sourceMappingURL=content-versions-Di3vcTgb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-versions-Di3vcTgb.js","names":[],"sources":["../../src/content-version.ts","../../src/content-versions.ts"],"sourcesContent":["import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentVersionKind } from './content-governance';\nimport { normalizeContentTransparency } from './content-transparency';\n\n/**\n * Shape of the loosely-structured `metadata` JSON bag stored on a content\n * version. Known fields are typed; unrecognized keys remain accessible via the\n * index signature.\n */\nexport interface ContentVersionMetadata {\n /**\n * Fingerprint of the snapshot captured for the latest publication version,\n * used to detect post-publication drift.\n */\n publicationSnapshotFingerprint?: string;\n [key: string]: unknown;\n}\n\nexport interface ContentVersionOptions extends SmrtObjectOptions {\n contentId?: string;\n version?: number;\n kind?: ContentVersionKind;\n title?: string;\n description?: string;\n body?: string;\n status?: string;\n summary?: string;\n snapshot?: string | Record<string, unknown>;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_versions',\n conflictColumns: ['content_id', 'version'],\n api: {\n include: ['list', 'get', 'create', 'getTransparencyAction'],\n routes: {\n getTransparencyAction: { method: 'GET', path: 'transparency' },\n },\n },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class ContentVersion extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n version = 1;\n kind: ContentVersionKind = 'manual';\n title = '';\n description = '';\n body = '';\n status = 'draft';\n summary = '';\n snapshot = '{}';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentVersionOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.version !== undefined) this.version = options.version;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.body !== undefined) this.body = options.body;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.snapshot !== undefined) {\n this.snapshot =\n typeof options.snapshot === 'string'\n ? options.snapshot\n : JSON.stringify(options.snapshot);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getSnapshot(): Record<string, unknown> {\n try {\n return this.snapshot ? JSON.parse(this.snapshot) : {};\n } catch {\n return {};\n }\n }\n\n getMetadata(): ContentVersionMetadata {\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n getTransparency() {\n const metadata = this.getMetadata();\n const snapshot = this.getSnapshot();\n\n return normalizeContentTransparency(metadata.transparency, {\n snapshotKind: this.kind === 'publication' ? 'published' : 'preview',\n contentId: this.contentId || (snapshot.contentId as string) || null,\n currentContentStatus:\n this.status || (snapshot.status as string) || 'draft',\n publicationVersion: {\n id: (this.id as string) || null,\n version: this.version ?? null,\n kind: this.kind || null,\n summary: this.summary || '',\n createdAt:\n this.createdAt instanceof Date ? this.createdAt.toISOString() : null,\n },\n });\n }\n\n async getTransparencyAction() {\n return this.getTransparency();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport type { FactContentRelationship } from '@happyvertical/smrt-facts';\nimport type { Content } from './content';\nimport type { CreateContentVersionOptions } from './content-governance';\nimport { resolveEffectiveContentGovernance } from './content-governance';\nimport { ContentVersion } from './content-version';\n\nexport class ContentVersionCollection extends SmrtCollection<ContentVersion> {\n static readonly _itemClass = ContentVersion;\n\n private buildSnapshotFactRelationships(\n snapshot: Record<string, unknown>,\n defaultRelationship: FactContentRelationship,\n ): Map<FactContentRelationship, string[]> {\n const byRelationship = new Map<FactContentRelationship, string[]>();\n const rawLinks: unknown[] = Array.isArray(snapshot.factLinks)\n ? snapshot.factLinks\n : [];\n\n for (const rawLink of rawLinks) {\n const link = rawLink as {\n factId?: unknown;\n relationship?: unknown;\n } | null;\n const factId =\n typeof link?.factId === 'string' && link.factId.length > 0\n ? link.factId\n : null;\n const relationship =\n typeof link?.relationship === 'string' && link.relationship.length > 0\n ? (link.relationship as FactContentRelationship)\n : defaultRelationship;\n\n if (!factId) {\n continue;\n }\n\n byRelationship.set(relationship, [\n ...(byRelationship.get(relationship) || []),\n factId,\n ]);\n }\n\n if (\n byRelationship.size === 0 &&\n Array.isArray(snapshot.factIds) &&\n snapshot.factIds.length > 0\n ) {\n byRelationship.set(\n defaultRelationship,\n snapshot.factIds.filter(\n (factId: unknown): factId is string =>\n typeof factId === 'string' && factId.length > 0,\n ),\n );\n }\n\n return byRelationship;\n }\n\n async listForContent(contentId: string): Promise<ContentVersion[]> {\n return this.list({\n where: { contentId },\n orderBy: 'version ASC',\n });\n }\n\n async getLatestForContent(contentId: string): Promise<ContentVersion | null> {\n const versions = await this.listForContent(contentId);\n return versions.length > 0 ? versions[versions.length - 1] : null;\n }\n\n async getLatestPublishedForContent(\n contentId: string,\n ): Promise<ContentVersion | null> {\n const versions = await this.list({\n where: {\n contentId,\n kind: 'publication',\n },\n orderBy: 'version DESC',\n });\n\n return versions[0] || null;\n }\n\n async getVersion(\n contentId: string,\n versionNumber: number,\n ): Promise<ContentVersion | null> {\n return this.get({\n contentId,\n version: versionNumber,\n });\n }\n\n async getNextVersionNumber(contentId: string): Promise<number> {\n const latest = await this.getLatestForContent(contentId);\n return latest ? latest.version + 1 : 1;\n }\n\n async createSnapshot(\n content: Content,\n options: CreateContentVersionOptions = {},\n ): Promise<ContentVersion> {\n if (!content.id) {\n throw new Error('Cannot create a version for unsaved content');\n }\n\n const version = await this.getNextVersionNumber(content.id as string);\n const governance = await resolveEffectiveContentGovernance({\n contentType: content.type,\n contentVariant: content.variant,\n db: this.db,\n tenantId: content.tenantId ?? null,\n });\n const [references, referenceEdges, assets, factsState] = await Promise.all([\n typeof content.getReferences === 'function'\n ? content.getReferences()\n : [],\n // Capture per-edge citation pins so restore can reconstruct them.\n // `getReferences()` resolves to Content objects and loses targetVersion.\n typeof content.getReferenceEdges === 'function'\n ? content.getReferenceEdges()\n : Promise.resolve([]),\n typeof content.getAssets === 'function' ? content.getAssets() : [],\n typeof content.getFactsState === 'function' &&\n governance.factLinkingEnabled\n ? content.getFactsState()\n : {\n factIds: [],\n facts: [],\n factLinks: [],\n },\n ]);\n\n const baseSnapshot = {\n id: content.id,\n slug: content.slug,\n context: content.context,\n name: content.name,\n type: content.type,\n variant: content.variant,\n fileKey: content.fileKey,\n author: content.author,\n title: content.title,\n description: content.description,\n body: content.body,\n bodyFormat: content.bodyFormat,\n publish_date: content.publish_date,\n url: content.url,\n source: content.source,\n original_url: content.original_url,\n language: content.language,\n tags: [...content.tags],\n category: content.category,\n status: content.status,\n state: content.state,\n metadata: content.metadata,\n thumbnailAssetId: content.thumbnailAssetId,\n referenceIds: references.map((reference) => reference.id).filter(Boolean),\n // Full edges with citation pins; `referenceIds` retained for back-compat\n // with snapshots written before pin-aware restore (#1387 #3).\n referenceEdges: referenceEdges.filter((edge) => Boolean(edge.targetId)),\n assetIds: assets.map((asset) => asset.id).filter(Boolean),\n factIds: factsState.factIds,\n factLinks: factsState.factLinks,\n tenantId: content.tenantId,\n _meta_type: content.toJSON()._meta_type,\n };\n const snapshot = {\n ...baseSnapshot,\n ...(options.snapshot || {}),\n };\n const versionSlugBase =\n snapshot.slug ||\n content.slug ||\n content.name ||\n content.title ||\n content.id;\n const versionSlug = `${versionSlugBase}-v${version}`;\n\n return this.create({\n slug: versionSlug,\n context: content.context || '',\n contentId: content.id as string,\n version,\n kind: options.kind || 'manual',\n title: snapshot.title || '',\n description: snapshot.description || '',\n body: snapshot.body || '',\n status: snapshot.status || 'draft',\n summary: options.summary || '',\n snapshot: JSON.stringify(snapshot),\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: content.tenantId,\n });\n }\n\n async restoreIntoContent(\n content: Content,\n versionNumber: number,\n ): Promise<Content> {\n if (!content.id) {\n throw new Error('Cannot restore an unsaved content item');\n }\n\n const version = await this.getVersion(content.id as string, versionNumber);\n if (!version) {\n throw new Error(\n `Content version ${versionNumber} not found for content ${content.id}`,\n );\n }\n\n const snapshot = version.getSnapshot();\n const keysToRestore = [\n 'name',\n 'type',\n 'variant',\n 'fileKey',\n 'author',\n 'title',\n 'description',\n 'body',\n 'bodyFormat',\n 'publish_date',\n 'url',\n 'source',\n 'original_url',\n 'language',\n 'tags',\n 'category',\n 'status',\n 'state',\n 'metadata',\n 'thumbnailAssetId',\n ];\n\n // Restore snapshot values onto the live Content instance by field name.\n // Indexing a class instance by an arbitrary string key requires a record\n // view; the keys are a fixed, known set of Content fields.\n const writableContent = content as unknown as Record<string, unknown>;\n for (const key of keysToRestore) {\n if (snapshot[key] !== undefined) {\n writableContent[key] = snapshot[key];\n }\n }\n\n // Reference edges with citation pins (#1387 #3). Newer snapshots carry\n // `referenceEdges` ({ targetId, targetVersion }); older ones only have\n // `referenceIds`. Either way, seed the pending `referenceIds` from the\n // target ids so `save()` reconciles the set (adds missing, removes extra),\n // then re-apply the saved pins below so restoring \"to vN\" reconstructs the\n // citation pins that existed at vN instead of dropping them to unpinned.\n const snapshotEdges: Array<{\n targetId: string;\n targetVersion: number | null;\n }> = Array.isArray(snapshot.referenceEdges)\n ? (snapshot.referenceEdges as unknown[])\n .filter(\n (edge): edge is { targetId: string; targetVersion?: unknown } =>\n !!edge &&\n typeof edge === 'object' &&\n typeof (edge as { targetId?: unknown }).targetId === 'string' &&\n (edge as { targetId: string }).targetId.length > 0,\n )\n .map((edge) => ({\n targetId: edge.targetId,\n targetVersion:\n typeof edge.targetVersion === 'number'\n ? edge.targetVersion\n : null,\n }))\n : Array.isArray(snapshot.referenceIds)\n ? snapshot.referenceIds\n .filter(\n (id: unknown): id is string =>\n typeof id === 'string' && id.length > 0,\n )\n .map((targetId: string) => ({ targetId, targetVersion: null }))\n : [];\n\n if (\n Array.isArray(snapshot.referenceEdges) ||\n Array.isArray(snapshot.referenceIds)\n ) {\n writableContent.referenceIds = snapshotEdges.map((edge) => edge.targetId);\n }\n\n if (Array.isArray(snapshot.assetIds)) {\n writableContent.assetIds = [...snapshot.assetIds];\n }\n\n await content.save();\n\n // Re-apply the citation pin of EVERY snapshot edge — including UNPINNED\n // ones (`targetVersion: null`). `save()` only reconciles the target-id set\n // (adds missing / removes extra edges) and leaves the pin of an edge that\n // already existed untouched. So restoring an *unpinned* snapshot over an\n // edge that is currently *pinned* must explicitly clear that pin, otherwise\n // the live pin survives the restore and drift never resets. Passing\n // `addReference(target, { targetVersion: null })` clears the pin in place\n // (the junction's `attach` updates the row when `null !== existing`), while\n // a non-null value (re)sets it — so \"restore to vN\" reconstructs exactly\n // the pins that existed at vN.\n //\n // We pass the resolved Content object (not the raw id) because\n // `addReference(string)` treats the string as a URL, not a content id.\n // `addReference` is idempotent on (source, target) and only adjusts\n // targetVersion.\n if (\n snapshotEdges.length > 0 &&\n typeof content.getReferences === 'function' &&\n typeof content.addReference === 'function'\n ) {\n const resolvedReferences = await content.getReferences();\n const resolvedById = new Map(\n resolvedReferences\n .filter((reference) => reference.id)\n .map((reference) => [reference.id as string, reference]),\n );\n for (const edge of snapshotEdges) {\n const target = resolvedById.get(edge.targetId);\n if (target) {\n await content.addReference(target, {\n targetVersion: edge.targetVersion,\n });\n }\n }\n }\n\n const governance = await resolveEffectiveContentGovernance({\n contentType: content.type,\n contentVariant: content.variant,\n db: this.db,\n tenantId: content.tenantId ?? null,\n });\n\n if (\n governance.isGoverned &&\n governance.factLinkingEnabled &&\n typeof content.getFactLinks === 'function' &&\n typeof content.syncFacts === 'function'\n ) {\n const desiredByRelationship = this.buildSnapshotFactRelationships(\n snapshot,\n governance.defaultFactRelationship,\n );\n const currentLinks = await content.getFactLinks();\n const currentRelationships = new Set(\n currentLinks.map(\n (link) =>\n (link.relationship as FactContentRelationship) ||\n governance.defaultFactRelationship,\n ),\n );\n const relationshipsToSync = new Set<FactContentRelationship>([\n ...currentRelationships,\n ...desiredByRelationship.keys(),\n ]);\n\n for (const relationship of relationshipsToSync) {\n await content.syncFacts(\n desiredByRelationship.get(relationship) || [],\n relationship,\n );\n }\n }\n\n return content;\n }\n}\n"],"mappings":"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"}
|