@happyvertical/smrt-content 0.30.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/AGENTS.md +194 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +634 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/asset-associable.d.ts +115 -0
- package/dist/asset-associable.d.ts.map +1 -0
- package/dist/body-format.d.ts +29 -0
- package/dist/body-format.d.ts.map +1 -0
- package/dist/body-format.js +604 -0
- package/dist/body-format.js.map +1 -0
- package/dist/content-asset.d.ts +17 -0
- package/dist/content-asset.d.ts.map +1 -0
- package/dist/content-assets.d.ts +10 -0
- package/dist/content-assets.d.ts.map +1 -0
- package/dist/content-chat-handlers.d.ts +115 -0
- package/dist/content-chat-handlers.d.ts.map +1 -0
- package/dist/content-chat-prompts.d.ts +3 -0
- package/dist/content-chat-prompts.d.ts.map +1 -0
- package/dist/content-chat-session.d.ts +26 -0
- package/dist/content-chat-session.d.ts.map +1 -0
- package/dist/content-contribution-attachment.d.ts +42 -0
- package/dist/content-contribution-attachment.d.ts.map +1 -0
- package/dist/content-contribution-attachments.d.ts +8 -0
- package/dist/content-contribution-attachments.d.ts.map +1 -0
- package/dist/content-contribution-config.d.ts +84 -0
- package/dist/content-contribution-config.d.ts.map +1 -0
- package/dist/content-contribution-revision.d.ts +38 -0
- package/dist/content-contribution-revision.d.ts.map +1 -0
- package/dist/content-contribution-revisions.d.ts +8 -0
- package/dist/content-contribution-revisions.d.ts.map +1 -0
- package/dist/content-contribution-type.d.ts +51 -0
- package/dist/content-contribution-type.d.ts.map +1 -0
- package/dist/content-contribution-types.d.ts +7 -0
- package/dist/content-contribution-types.d.ts.map +1 -0
- package/dist/content-contribution.d.ts +161 -0
- package/dist/content-contribution.d.ts.map +1 -0
- package/dist/content-contributions.d.ts +53 -0
- package/dist/content-contributions.d.ts.map +1 -0
- package/dist/content-contributor.d.ts +30 -0
- package/dist/content-contributor.d.ts.map +1 -0
- package/dist/content-contributors.d.ts +13 -0
- package/dist/content-contributors.d.ts.map +1 -0
- package/dist/content-correction.d.ts +39 -0
- package/dist/content-correction.d.ts.map +1 -0
- package/dist/content-corrections.d.ts +9 -0
- package/dist/content-corrections.d.ts.map +1 -0
- package/dist/content-editor-assistant.d.ts +68 -0
- package/dist/content-editor-assistant.d.ts.map +1 -0
- package/dist/content-editor-assistant.js +97 -0
- package/dist/content-editor-assistant.js.map +1 -0
- package/dist/content-feed-parser.d.ts +19 -0
- package/dist/content-feed-parser.d.ts.map +1 -0
- package/dist/content-feed-source.d.ts +52 -0
- package/dist/content-feed-source.d.ts.map +1 -0
- package/dist/content-feed-sources.d.ts +11 -0
- package/dist/content-feed-sources.d.ts.map +1 -0
- package/dist/content-feed-sync.d.ts +23 -0
- package/dist/content-feed-sync.d.ts.map +1 -0
- package/dist/content-governance-assignment.d.ts +42 -0
- package/dist/content-governance-assignment.d.ts.map +1 -0
- package/dist/content-governance-assignments.d.ts +11 -0
- package/dist/content-governance-assignments.d.ts.map +1 -0
- package/dist/content-governance-policies.d.ts +7 -0
- package/dist/content-governance-policies.d.ts.map +1 -0
- package/dist/content-governance-policy.d.ts +29 -0
- package/dist/content-governance-policy.d.ts.map +1 -0
- package/dist/content-governance-profile.d.ts +31 -0
- package/dist/content-governance-profile.d.ts.map +1 -0
- package/dist/content-governance-profiles.d.ts +7 -0
- package/dist/content-governance-profiles.d.ts.map +1 -0
- package/dist/content-governance.d.ts +188 -0
- package/dist/content-governance.d.ts.map +1 -0
- package/dist/content-prompts.d.ts +10 -0
- package/dist/content-prompts.d.ts.map +1 -0
- package/dist/content-reference.d.ts +17 -0
- package/dist/content-reference.d.ts.map +1 -0
- package/dist/content-references.d.ts +55 -0
- package/dist/content-references.d.ts.map +1 -0
- package/dist/content-review.d.ts +34 -0
- package/dist/content-review.d.ts.map +1 -0
- package/dist/content-reviews.d.ts +21 -0
- package/dist/content-reviews.d.ts.map +1 -0
- package/dist/content-transparency.d.ts +72 -0
- package/dist/content-transparency.d.ts.map +1 -0
- package/dist/content-types.d.ts +51 -0
- package/dist/content-types.d.ts.map +1 -0
- package/dist/content-version.d.ts +38 -0
- package/dist/content-version.d.ts.map +1 -0
- package/dist/content-versions.d.ts +16 -0
- package/dist/content-versions.d.ts.map +1 -0
- package/dist/content.d.ts +736 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/contents.d.ts +292 -0
- package/dist/contents.d.ts.map +1 -0
- package/dist/database-utils.d.ts +3 -0
- package/dist/database-utils.d.ts.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11602 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +12308 -0
- package/dist/mock-smrt-client.d.ts +493 -0
- package/dist/mock-smrt-client.d.ts.map +1 -0
- package/dist/mock-smrt-client.js +390 -0
- package/dist/mock-smrt-client.js.map +1 -0
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +454 -0
- package/dist/playground.js.map +1 -0
- package/dist/publish-readiness.d.ts +30 -0
- package/dist/publish-readiness.d.ts.map +1 -0
- package/dist/publish-readiness.js +74 -0
- package/dist/publish-readiness.js.map +1 -0
- package/dist/safe-remote-url.d.ts +52 -0
- package/dist/safe-remote-url.d.ts.map +1 -0
- package/dist/serialization.d.ts +78 -0
- package/dist/serialization.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +6130 -0
- package/dist/svelte/api.d.ts +3 -0
- package/dist/svelte/api.d.ts.map +1 -0
- package/dist/svelte/api.js +10 -0
- package/dist/svelte/components/ArticleCard.svelte +159 -0
- package/dist/svelte/components/ArticleCard.svelte.d.ts +17 -0
- package/dist/svelte/components/ArticleCard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ArticleList.svelte +75 -0
- package/dist/svelte/components/ArticleList.svelte.d.ts +21 -0
- package/dist/svelte/components/ArticleList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentAgentChat.svelte +652 -0
- package/dist/svelte/components/ContentAgentChat.svelte.d.ts +17 -0
- package/dist/svelte/components/ContentAgentChat.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentBodyEditor.svelte +1446 -0
- package/dist/svelte/components/ContentBodyEditor.svelte.d.ts +25 -0
- package/dist/svelte/components/ContentBodyEditor.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentBodyRenderer.svelte +152 -0
- package/dist/svelte/components/ContentBodyRenderer.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentBodyRenderer.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentClaimAuditTool.svelte +441 -0
- package/dist/svelte/components/ContentClaimAuditTool.svelte.d.ts +12 -0
- package/dist/svelte/components/ContentClaimAuditTool.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentContributionForm.svelte +226 -0
- package/dist/svelte/components/ContentContributionForm.svelte.d.ts +23 -0
- package/dist/svelte/components/ContentContributionForm.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentContributionInbox.svelte +322 -0
- package/dist/svelte/components/ContentContributionInbox.svelte.d.ts +22 -0
- package/dist/svelte/components/ContentContributionInbox.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentContributionPortal.svelte +182 -0
- package/dist/svelte/components/ContentContributionPortal.svelte.d.ts +12 -0
- package/dist/svelte/components/ContentContributionPortal.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentContributionTypeManager.svelte +281 -0
- package/dist/svelte/components/ContentContributionTypeManager.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentContributionTypeManager.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentContributorManager.svelte +140 -0
- package/dist/svelte/components/ContentContributorManager.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentContributorManager.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentCorrectionsTool.svelte +361 -0
- package/dist/svelte/components/ContentCorrectionsTool.svelte.d.ts +11 -0
- package/dist/svelte/components/ContentCorrectionsTool.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentEditor.svelte +2166 -0
- package/dist/svelte/components/ContentEditor.svelte.d.ts +26 -0
- package/dist/svelte/components/ContentEditor.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentGovernanceAssignmentEditor.svelte +199 -0
- package/dist/svelte/components/ContentGovernanceAssignmentEditor.svelte.d.ts +11 -0
- package/dist/svelte/components/ContentGovernanceAssignmentEditor.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentGovernanceManager.svelte +340 -0
- package/dist/svelte/components/ContentGovernanceManager.svelte.d.ts +11 -0
- package/dist/svelte/components/ContentGovernanceManager.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentGovernancePanel.svelte +2244 -0
- package/dist/svelte/components/ContentGovernancePanel.svelte.d.ts +26 -0
- package/dist/svelte/components/ContentGovernancePanel.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentGovernancePolicyEditor.svelte +110 -0
- package/dist/svelte/components/ContentGovernancePolicyEditor.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentGovernancePolicyEditor.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentGovernanceProfileEditor.svelte +185 -0
- package/dist/svelte/components/ContentGovernanceProfileEditor.svelte.d.ts +11 -0
- package/dist/svelte/components/ContentGovernanceProfileEditor.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentGovernanceTool.svelte +56 -0
- package/dist/svelte/components/ContentGovernanceTool.svelte.d.ts +13 -0
- package/dist/svelte/components/ContentGovernanceTool.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentImageBrowser.svelte +243 -0
- package/dist/svelte/components/ContentImageBrowser.svelte.d.ts +18 -0
- package/dist/svelte/components/ContentImageBrowser.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentImageChooser.svelte +134 -0
- package/dist/svelte/components/ContentImageChooser.svelte.d.ts +11 -0
- package/dist/svelte/components/ContentImageChooser.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentList.svelte +906 -0
- package/dist/svelte/components/ContentList.svelte.d.ts +16 -0
- package/dist/svelte/components/ContentList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentMetadataFields.svelte +107 -0
- package/dist/svelte/components/ContentMetadataFields.svelte.d.ts +8 -0
- package/dist/svelte/components/ContentMetadataFields.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentReferencesPanel.svelte +221 -0
- package/dist/svelte/components/ContentReferencesPanel.svelte.d.ts +20 -0
- package/dist/svelte/components/ContentReferencesPanel.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentReviewStatusTray.svelte +151 -0
- package/dist/svelte/components/ContentReviewStatusTray.svelte.d.ts +20 -0
- package/dist/svelte/components/ContentReviewStatusTray.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentStatusFields.svelte +85 -0
- package/dist/svelte/components/ContentStatusFields.svelte.d.ts +8 -0
- package/dist/svelte/components/ContentStatusFields.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentTitleField.svelte +54 -0
- package/dist/svelte/components/ContentTitleField.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentTitleField.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentTransparencyReport.svelte +322 -0
- package/dist/svelte/components/ContentTransparencyReport.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentTransparencyReport.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentTransparencyTool.svelte +314 -0
- package/dist/svelte/components/ContentTransparencyTool.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentTransparencyTool.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ContentVersionsTool.svelte +291 -0
- package/dist/svelte/components/ContentVersionsTool.svelte.d.ts +10 -0
- package/dist/svelte/components/ContentVersionsTool.svelte.d.ts.map +1 -0
- package/dist/svelte/components/GovernedContentEditor.svelte +409 -0
- package/dist/svelte/components/GovernedContentEditor.svelte.d.ts +35 -0
- package/dist/svelte/components/GovernedContentEditor.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ImageThumbnail.cache.d.ts +14 -0
- package/dist/svelte/components/ImageThumbnail.cache.d.ts.map +1 -0
- package/dist/svelte/components/ImageThumbnail.cache.js +36 -0
- package/dist/svelte/components/ImageThumbnail.svelte +159 -0
- package/dist/svelte/components/ImageThumbnail.svelte.d.ts +8 -0
- package/dist/svelte/components/ImageThumbnail.svelte.d.ts.map +1 -0
- package/dist/svelte/components/Markdown.svelte +125 -0
- package/dist/svelte/components/Markdown.svelte.d.ts +11 -0
- package/dist/svelte/components/Markdown.svelte.d.ts.map +1 -0
- package/dist/svelte/content-editor-form.d.ts +63 -0
- package/dist/svelte/content-editor-form.d.ts.map +1 -0
- package/dist/svelte/content-editor-form.js +94 -0
- package/dist/svelte/content-editor-media.d.ts +12 -0
- package/dist/svelte/content-editor-media.d.ts.map +1 -0
- package/dist/svelte/content-editor-media.js +84 -0
- package/dist/svelte/content-editor-state.svelte.d.ts +35 -0
- package/dist/svelte/content-editor-state.svelte.d.ts.map +1 -0
- package/dist/svelte/content-editor-state.svelte.js +141 -0
- package/dist/svelte/governance-manager-client.d.ts +22 -0
- package/dist/svelte/governance-manager-client.d.ts.map +1 -0
- package/dist/svelte/governance-manager-client.js +1 -0
- package/dist/svelte/i18n.contribution.d.ts +57 -0
- package/dist/svelte/i18n.contribution.d.ts.map +1 -0
- package/dist/svelte/i18n.contribution.js +64 -0
- package/dist/svelte/i18n.editor.d.ts +71 -0
- package/dist/svelte/i18n.editor.d.ts.map +1 -0
- package/dist/svelte/i18n.editor.js +87 -0
- package/dist/svelte/i18n.governance.d.ts +66 -0
- package/dist/svelte/i18n.governance.d.ts.map +1 -0
- package/dist/svelte/i18n.governance.js +66 -0
- package/dist/svelte/i18n.routes.d.ts +66 -0
- package/dist/svelte/i18n.routes.d.ts.map +1 -0
- package/dist/svelte/i18n.routes.js +75 -0
- package/dist/svelte/i18n.tools.d.ts +81 -0
- package/dist/svelte/i18n.tools.d.ts.map +1 -0
- package/dist/svelte/i18n.tools.js +90 -0
- package/dist/svelte/index.d.ts +101 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +63 -0
- package/dist/svelte/playground.d.ts +281 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +438 -0
- package/dist/svelte/routes/ContentContributionsRoute.svelte +809 -0
- package/dist/svelte/routes/ContentContributionsRoute.svelte.d.ts +10 -0
- package/dist/svelte/routes/ContentContributionsRoute.svelte.d.ts.map +1 -0
- package/dist/svelte/routes/ContentFactsRoute.svelte +612 -0
- package/dist/svelte/routes/ContentFactsRoute.svelte.d.ts +11 -0
- package/dist/svelte/routes/ContentFactsRoute.svelte.d.ts.map +1 -0
- package/dist/svelte/routes/ContentGovernanceRoute.svelte +218 -0
- package/dist/svelte/routes/ContentGovernanceRoute.svelte.d.ts +10 -0
- package/dist/svelte/routes/ContentGovernanceRoute.svelte.d.ts.map +1 -0
- package/dist/svelte/routes/ContentWorkspaceRoute.svelte +431 -0
- package/dist/svelte/routes/ContentWorkspaceRoute.svelte.d.ts +12 -0
- package/dist/svelte/routes/ContentWorkspaceRoute.svelte.d.ts.map +1 -0
- package/dist/svelte/routes/PublishedArticleRoute.svelte +194 -0
- package/dist/svelte/routes/PublishedArticleRoute.svelte.d.ts +10 -0
- package/dist/svelte/routes/PublishedArticleRoute.svelte.d.ts.map +1 -0
- package/dist/svelte/routes/index.d.ts +8 -0
- package/dist/svelte/routes/index.d.ts.map +1 -0
- package/dist/svelte/routes/index.js +6 -0
- package/dist/svelte/routes/shared.d.ts +90 -0
- package/dist/svelte/routes/shared.d.ts.map +1 -0
- package/dist/svelte/routes/shared.js +104 -0
- package/dist/svelte/types.d.ts +69 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +6 -0
- package/dist/thumbnail-generator.d.ts +174 -0
- package/dist/thumbnail-generator.d.ts.map +1 -0
- package/dist/ui.d.ts +10 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +42 -0
- package/dist/ui.js.map +1 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { CONTENT_MODULE_META } from "./ui.js";
|
|
2
|
+
const DEFAULT_CONTENT_PLAYGROUND_API_BASE_URL = "/api/v1";
|
|
3
|
+
function resolveContentPlaygroundApiBaseUrl() {
|
|
4
|
+
const configuredViaGlobal = globalThis.__SMRT_CONTENT_PLAYGROUND_API_BASE_URL__;
|
|
5
|
+
if (configuredViaGlobal) {
|
|
6
|
+
return configuredViaGlobal;
|
|
7
|
+
}
|
|
8
|
+
const browserLocation = globalThis.location;
|
|
9
|
+
if (browserLocation) {
|
|
10
|
+
const configuredViaQuery = new URLSearchParams(browserLocation.search).get(
|
|
11
|
+
"smrtContentApiBaseUrl"
|
|
12
|
+
);
|
|
13
|
+
if (configuredViaQuery) {
|
|
14
|
+
return configuredViaQuery;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return DEFAULT_CONTENT_PLAYGROUND_API_BASE_URL;
|
|
18
|
+
}
|
|
19
|
+
const sampleArticles = [
|
|
20
|
+
{
|
|
21
|
+
id: "article-aurora-kitchen",
|
|
22
|
+
slug: "aurora-kitchen-notes",
|
|
23
|
+
title: "Aurora Kitchen Notes",
|
|
24
|
+
description: "A reference article card preview showing how editorial metadata lands in the package playground.",
|
|
25
|
+
publish_date: "2026-03-14T12:00:00.000Z",
|
|
26
|
+
author: "Editorial Systems",
|
|
27
|
+
tags: ["release", "editorial", "qa"]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "article-governance-habits",
|
|
31
|
+
slug: "governance-habits",
|
|
32
|
+
title: "Governance Habits That Scale",
|
|
33
|
+
description: "Teams can move fast when quality gates are visible, lightweight, and shared with contributors.",
|
|
34
|
+
publish_date: "2026-03-19T09:30:00.000Z",
|
|
35
|
+
author: "Content Ops",
|
|
36
|
+
tags: ["governance", "quality", "workflows"]
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
const sampleContributions = [
|
|
40
|
+
{
|
|
41
|
+
id: "contribution-1",
|
|
42
|
+
title: "Spring buyer guide",
|
|
43
|
+
contributionTypeKey: "article",
|
|
44
|
+
status: "needs_changes",
|
|
45
|
+
revisionCount: 2,
|
|
46
|
+
editorNotes: "Please tighten the sourcing notes in the opening section.",
|
|
47
|
+
contributorName: "Taylor Rowan",
|
|
48
|
+
contributorEmail: "taylor@example.com",
|
|
49
|
+
intakeDecision: "needs_changes",
|
|
50
|
+
body: "The spring buyer guide draft is attached, with sourcing notes for the opening section and the product comparison appendix.",
|
|
51
|
+
updatedAt: "2026-03-20T15:18:00.000Z"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "contribution-2",
|
|
55
|
+
title: "Field report: Pacific logistics",
|
|
56
|
+
contributionTypeKey: "report",
|
|
57
|
+
status: "submitted",
|
|
58
|
+
revisionCount: 1,
|
|
59
|
+
contributorName: "Jordan Lee",
|
|
60
|
+
contributorEmail: "jordan@example.com",
|
|
61
|
+
intakeDecision: "submitted",
|
|
62
|
+
body: "Field notes from the Pacific corridor include updated shipping windows, route constraints, and operator interviews.",
|
|
63
|
+
updatedAt: "2026-03-18T10:40:00.000Z"
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
const sampleGovernanceDefinitions = {
|
|
67
|
+
effective: {
|
|
68
|
+
policies: [
|
|
69
|
+
{
|
|
70
|
+
id: "policy-facts",
|
|
71
|
+
key: "facts",
|
|
72
|
+
label: "Facts review",
|
|
73
|
+
kind: "facts",
|
|
74
|
+
instructions: "Compare claims against linked facts before publication.",
|
|
75
|
+
enabled: true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "policy-style",
|
|
79
|
+
key: "style",
|
|
80
|
+
label: "Style review",
|
|
81
|
+
kind: "custom",
|
|
82
|
+
instructions: "Apply newsroom tone, structure, and clarity guidelines.",
|
|
83
|
+
enabled: true
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
profiles: [
|
|
87
|
+
{
|
|
88
|
+
id: "profile-publication",
|
|
89
|
+
key: "publication",
|
|
90
|
+
label: "Publication",
|
|
91
|
+
description: "Required before governed content can be published.",
|
|
92
|
+
enabled: true,
|
|
93
|
+
requirements: [
|
|
94
|
+
{
|
|
95
|
+
policyKey: "facts",
|
|
96
|
+
label: "Facts review",
|
|
97
|
+
blocking: true,
|
|
98
|
+
acceptedStatuses: ["passed"]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
policyKey: "style",
|
|
102
|
+
label: "Style review",
|
|
103
|
+
blocking: false,
|
|
104
|
+
acceptedStatuses: ["passed", "warning"]
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
assignments: [
|
|
110
|
+
{
|
|
111
|
+
id: "assignment-article",
|
|
112
|
+
key: "article",
|
|
113
|
+
label: "Articles",
|
|
114
|
+
contentType: "article",
|
|
115
|
+
contentVariant: null,
|
|
116
|
+
enabled: true,
|
|
117
|
+
factLinkingEnabled: true,
|
|
118
|
+
transparencyEnabled: true,
|
|
119
|
+
publicationProfileKey: "publication",
|
|
120
|
+
correctionProfileKey: null,
|
|
121
|
+
enforcePublishReadiness: true,
|
|
122
|
+
defaultFactRelationship: "supports"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
persisted: {
|
|
127
|
+
policies: [
|
|
128
|
+
{
|
|
129
|
+
id: "policy-style",
|
|
130
|
+
key: "style",
|
|
131
|
+
label: "Style review",
|
|
132
|
+
kind: "custom",
|
|
133
|
+
instructions: "Apply newsroom tone, structure, and clarity guidelines.",
|
|
134
|
+
enabled: true
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
profiles: [
|
|
138
|
+
{
|
|
139
|
+
id: "profile-publication",
|
|
140
|
+
key: "publication",
|
|
141
|
+
label: "Publication",
|
|
142
|
+
description: "Required before governed content can be published.",
|
|
143
|
+
enabled: true,
|
|
144
|
+
requirements: [
|
|
145
|
+
{
|
|
146
|
+
policyKey: "facts",
|
|
147
|
+
label: "Facts review",
|
|
148
|
+
blocking: true,
|
|
149
|
+
acceptedStatuses: ["passed"]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
policyKey: "style",
|
|
153
|
+
label: "Style review",
|
|
154
|
+
blocking: false,
|
|
155
|
+
acceptedStatuses: ["passed", "warning"]
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
assignments: [
|
|
161
|
+
{
|
|
162
|
+
id: "assignment-article",
|
|
163
|
+
key: "article",
|
|
164
|
+
label: "Articles",
|
|
165
|
+
contentType: "article",
|
|
166
|
+
contentVariant: null,
|
|
167
|
+
enabled: true,
|
|
168
|
+
factLinkingEnabled: true,
|
|
169
|
+
transparencyEnabled: true,
|
|
170
|
+
publicationProfileKey: "publication",
|
|
171
|
+
correctionProfileKey: null,
|
|
172
|
+
enforcePublishReadiness: true,
|
|
173
|
+
defaultFactRelationship: "supports"
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const sampleEditorContent = {
|
|
179
|
+
id: "content-playground-editor",
|
|
180
|
+
slug: "playground-editor-reference",
|
|
181
|
+
title: "Reference editorial draft",
|
|
182
|
+
description: "A working editor preview with copy, references, and assets already populated.",
|
|
183
|
+
body: `# Editorial Draft
|
|
184
|
+
|
|
185
|
+
This preview is meant to feel like a real authoring surface.
|
|
186
|
+
|
|
187
|
+
- tighten the lede
|
|
188
|
+
- verify product claims
|
|
189
|
+
- attach supporting references`,
|
|
190
|
+
author: "Editorial Systems",
|
|
191
|
+
type: "article",
|
|
192
|
+
status: "draft",
|
|
193
|
+
state: "active",
|
|
194
|
+
source: "manual",
|
|
195
|
+
tags: ["editorial", "playground"],
|
|
196
|
+
referenceIds: ["fact-aurora", "source-governance"],
|
|
197
|
+
assetIds: [],
|
|
198
|
+
assets: []
|
|
199
|
+
};
|
|
200
|
+
const noop = () => {
|
|
201
|
+
};
|
|
202
|
+
function cloneValue(value) {
|
|
203
|
+
return JSON.parse(JSON.stringify(value));
|
|
204
|
+
}
|
|
205
|
+
function buildPlaygroundResponse(data) {
|
|
206
|
+
return {
|
|
207
|
+
data,
|
|
208
|
+
success: true
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function upsertByIdOrKey(items, value, prefix) {
|
|
212
|
+
const id = value.id || `${prefix}-${value.key || items.length + 1}`;
|
|
213
|
+
const nextValue = {
|
|
214
|
+
...value,
|
|
215
|
+
id
|
|
216
|
+
};
|
|
217
|
+
const index = items.findIndex(
|
|
218
|
+
(item) => item.id === id || !!value.key && item.key === value.key
|
|
219
|
+
);
|
|
220
|
+
if (index === -1) {
|
|
221
|
+
return [...items, nextValue];
|
|
222
|
+
}
|
|
223
|
+
const nextItems = [...items];
|
|
224
|
+
nextItems[index] = {
|
|
225
|
+
...nextItems[index],
|
|
226
|
+
...nextValue
|
|
227
|
+
};
|
|
228
|
+
return nextItems;
|
|
229
|
+
}
|
|
230
|
+
function createGovernancePlaygroundClient(seed = sampleGovernanceDefinitions) {
|
|
231
|
+
let policies = cloneValue(seed.effective.policies);
|
|
232
|
+
let profiles = cloneValue(seed.effective.profiles);
|
|
233
|
+
let assignments = cloneValue(seed.effective.assignments);
|
|
234
|
+
const getDefinitions = () => ({
|
|
235
|
+
effective: {
|
|
236
|
+
policies: cloneValue(policies),
|
|
237
|
+
profiles: cloneValue(profiles),
|
|
238
|
+
assignments: cloneValue(assignments)
|
|
239
|
+
},
|
|
240
|
+
persisted: {
|
|
241
|
+
policies: cloneValue(policies),
|
|
242
|
+
profiles: cloneValue(profiles),
|
|
243
|
+
assignments: cloneValue(assignments)
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
contents: {
|
|
248
|
+
getGovernanceDefinitions: async () => buildPlaygroundResponse(getDefinitions())
|
|
249
|
+
},
|
|
250
|
+
contentGovernancePolicies: {
|
|
251
|
+
create: async (policy) => {
|
|
252
|
+
policies = upsertByIdOrKey(policies, policy, "policy");
|
|
253
|
+
return buildPlaygroundResponse(policies[policies.length - 1]);
|
|
254
|
+
},
|
|
255
|
+
update: async (id, policy) => {
|
|
256
|
+
policies = upsertByIdOrKey(policies, { ...policy, id }, "policy");
|
|
257
|
+
return buildPlaygroundResponse(
|
|
258
|
+
policies.find((item) => item.id === id)
|
|
259
|
+
);
|
|
260
|
+
},
|
|
261
|
+
delete: async (id) => {
|
|
262
|
+
policies = policies.filter((item) => item.id !== id);
|
|
263
|
+
return buildPlaygroundResponse(void 0);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
contentGovernanceProfiles: {
|
|
267
|
+
create: async (profile) => {
|
|
268
|
+
profiles = upsertByIdOrKey(profiles, profile, "profile");
|
|
269
|
+
return buildPlaygroundResponse(profiles[profiles.length - 1]);
|
|
270
|
+
},
|
|
271
|
+
update: async (id, profile) => {
|
|
272
|
+
profiles = upsertByIdOrKey(profiles, { ...profile, id }, "profile");
|
|
273
|
+
return buildPlaygroundResponse(
|
|
274
|
+
profiles.find((item) => item.id === id)
|
|
275
|
+
);
|
|
276
|
+
},
|
|
277
|
+
delete: async (id) => {
|
|
278
|
+
profiles = profiles.filter((item) => item.id !== id);
|
|
279
|
+
return buildPlaygroundResponse(void 0);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
contentGovernanceAssignments: {
|
|
283
|
+
create: async (assignment) => {
|
|
284
|
+
assignments = upsertByIdOrKey(assignments, assignment, "assignment");
|
|
285
|
+
return buildPlaygroundResponse(assignments[assignments.length - 1]);
|
|
286
|
+
},
|
|
287
|
+
update: async (id, assignment) => {
|
|
288
|
+
assignments = upsertByIdOrKey(
|
|
289
|
+
assignments,
|
|
290
|
+
{ ...assignment, id },
|
|
291
|
+
"assignment"
|
|
292
|
+
);
|
|
293
|
+
return buildPlaygroundResponse(
|
|
294
|
+
assignments.find((item) => item.id === id)
|
|
295
|
+
);
|
|
296
|
+
},
|
|
297
|
+
delete: async (id) => {
|
|
298
|
+
assignments = assignments.filter((item) => item.id !== id);
|
|
299
|
+
return buildPlaygroundResponse(void 0);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const markdownExample = `# Content Playground
|
|
305
|
+
|
|
306
|
+
This preview module lives in \`src/svelte/playground.ts\`.
|
|
307
|
+
|
|
308
|
+
- Package-owned previews stay close to the real components
|
|
309
|
+
- The shared host simply discovers and renders them
|
|
310
|
+
- Live entries can still point at the package's generated API routes`;
|
|
311
|
+
const loadArticleCard = () => import("./svelte/components/ArticleCard.svelte");
|
|
312
|
+
const loadArticleList = () => import("./svelte/components/ArticleList.svelte");
|
|
313
|
+
const loadContentEditor = () => import("./svelte/components/ContentEditor.svelte");
|
|
314
|
+
const loadContentContributionInbox = () => import("./svelte/components/ContentContributionInbox.svelte");
|
|
315
|
+
const loadContentContributionPortal = () => import("./svelte/components/ContentContributionPortal.svelte");
|
|
316
|
+
const loadContentGovernanceManager = () => import("./svelte/components/ContentGovernanceManager.svelte");
|
|
317
|
+
const loadMarkdown = () => import("./svelte/components/Markdown.svelte");
|
|
318
|
+
const governancePlaygroundClient = createGovernancePlaygroundClient();
|
|
319
|
+
const playground = {
|
|
320
|
+
packageName: "@happyvertical/smrt-content",
|
|
321
|
+
displayName: "Content",
|
|
322
|
+
description: CONTENT_MODULE_META.description,
|
|
323
|
+
moduleMeta: CONTENT_MODULE_META,
|
|
324
|
+
entries: [
|
|
325
|
+
{
|
|
326
|
+
id: "article-card",
|
|
327
|
+
slotId: "article-card",
|
|
328
|
+
title: "Article Card",
|
|
329
|
+
description: "Editorial teaser card with tags and metadata.",
|
|
330
|
+
loadComponent: loadArticleCard,
|
|
331
|
+
order: 1,
|
|
332
|
+
props: {
|
|
333
|
+
article: sampleArticles[0],
|
|
334
|
+
showTags: true
|
|
335
|
+
},
|
|
336
|
+
modes: {
|
|
337
|
+
mock: {
|
|
338
|
+
label: "Mock"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: "article-list",
|
|
344
|
+
slotId: "article-list",
|
|
345
|
+
title: "Article List",
|
|
346
|
+
description: "Reference list/grid layout for published content.",
|
|
347
|
+
loadComponent: loadArticleList,
|
|
348
|
+
order: 2,
|
|
349
|
+
props: {
|
|
350
|
+
articles: sampleArticles,
|
|
351
|
+
showTags: true
|
|
352
|
+
},
|
|
353
|
+
modes: {
|
|
354
|
+
mock: {
|
|
355
|
+
label: "Mock"
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
id: "markdown",
|
|
361
|
+
slotId: "markdown",
|
|
362
|
+
title: "Markdown Renderer",
|
|
363
|
+
description: "Safe markdown rendering with a small editorial snippet.",
|
|
364
|
+
loadComponent: loadMarkdown,
|
|
365
|
+
order: 3,
|
|
366
|
+
props: {
|
|
367
|
+
content: markdownExample
|
|
368
|
+
},
|
|
369
|
+
modes: {
|
|
370
|
+
mock: {
|
|
371
|
+
label: "Mock"
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
id: "content-editor",
|
|
377
|
+
title: "Content Editor",
|
|
378
|
+
description: "Authoring surface for body copy, references, and media.",
|
|
379
|
+
loadComponent: loadContentEditor,
|
|
380
|
+
order: 4,
|
|
381
|
+
props: {
|
|
382
|
+
apiBaseUrl: resolveContentPlaygroundApiBaseUrl(),
|
|
383
|
+
content: sampleEditorContent,
|
|
384
|
+
contentId: sampleEditorContent.id,
|
|
385
|
+
agentChatEnabled: false,
|
|
386
|
+
agentChatNotice: "The shared playground keeps the editor preview local. Run the content package app when you want the live agent chat routes too.",
|
|
387
|
+
onSave: noop,
|
|
388
|
+
onCancel: noop
|
|
389
|
+
},
|
|
390
|
+
modes: {
|
|
391
|
+
mock: {
|
|
392
|
+
label: "Mock"
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
id: "contribution-portal",
|
|
398
|
+
title: "Contribution Portal",
|
|
399
|
+
description: "Contributor-facing inbox and submission status view.",
|
|
400
|
+
loadComponent: loadContentContributionPortal,
|
|
401
|
+
order: 5,
|
|
402
|
+
props: {
|
|
403
|
+
contributions: sampleContributions
|
|
404
|
+
},
|
|
405
|
+
modes: {
|
|
406
|
+
mock: {
|
|
407
|
+
label: "Mock"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
id: "contribution-inbox",
|
|
413
|
+
title: "Contribution Inbox",
|
|
414
|
+
description: "Editorial review queue for approving, rejecting, or requesting changes.",
|
|
415
|
+
loadComponent: loadContentContributionInbox,
|
|
416
|
+
order: 6,
|
|
417
|
+
props: {
|
|
418
|
+
contributions: sampleContributions
|
|
419
|
+
},
|
|
420
|
+
modes: {
|
|
421
|
+
mock: {
|
|
422
|
+
label: "Mock"
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
id: "governance-manager",
|
|
428
|
+
title: "Governance Manager",
|
|
429
|
+
description: "Administrative view with in-memory mock data and optional live package routes.",
|
|
430
|
+
loadComponent: loadContentGovernanceManager,
|
|
431
|
+
order: 7,
|
|
432
|
+
modes: {
|
|
433
|
+
mock: {
|
|
434
|
+
label: "Mock",
|
|
435
|
+
description: "Uses an in-memory governance client so the shared playground works without a package-local dev server.",
|
|
436
|
+
props: {
|
|
437
|
+
client: governancePlaygroundClient
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
live: {
|
|
441
|
+
label: "Live",
|
|
442
|
+
description: "Requires the content package dev server and generated routes. Override the base URL with ?smrtContentApiBaseUrl=... or window.__SMRT_CONTENT_PLAYGROUND_API_BASE_URL__ when needed.",
|
|
443
|
+
props: {
|
|
444
|
+
apiBaseUrl: resolveContentPlaygroundApiBaseUrl()
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
]
|
|
450
|
+
};
|
|
451
|
+
export {
|
|
452
|
+
playground as default
|
|
453
|
+
};
|
|
454
|
+
//# sourceMappingURL=playground.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playground.js","sources":["../src/svelte/playground.ts"],"sourcesContent":["import type {\n ApiResponse,\n ContentGovernanceAssignmentData,\n ContentGovernanceDefinitionsData,\n ContentGovernanceProfileData,\n ContentReviewPolicyData,\n} from '../mock-smrt-client';\nimport { CONTENT_MODULE_META } from '../ui.js';\nimport type { ContentGovernanceManagerClient } from './governance-manager-client';\n\nconst DEFAULT_CONTENT_PLAYGROUND_API_BASE_URL = '/api/v1';\n\ntype ContentPlaygroundGlobal = typeof globalThis & {\n __SMRT_CONTENT_PLAYGROUND_API_BASE_URL__?: string;\n location?: {\n search: string;\n };\n};\n\nfunction resolveContentPlaygroundApiBaseUrl(): string {\n const configuredViaGlobal = (globalThis as ContentPlaygroundGlobal)\n .__SMRT_CONTENT_PLAYGROUND_API_BASE_URL__;\n if (configuredViaGlobal) {\n return configuredViaGlobal;\n }\n\n const browserLocation = (globalThis as ContentPlaygroundGlobal).location;\n if (browserLocation) {\n const configuredViaQuery = new URLSearchParams(browserLocation.search).get(\n 'smrtContentApiBaseUrl',\n );\n\n if (configuredViaQuery) {\n return configuredViaQuery;\n }\n }\n\n return DEFAULT_CONTENT_PLAYGROUND_API_BASE_URL;\n}\n\nconst sampleArticles = [\n {\n id: 'article-aurora-kitchen',\n slug: 'aurora-kitchen-notes',\n title: 'Aurora Kitchen Notes',\n description:\n 'A reference article card preview showing how editorial metadata lands in the package playground.',\n publish_date: '2026-03-14T12:00:00.000Z',\n author: 'Editorial Systems',\n tags: ['release', 'editorial', 'qa'],\n },\n {\n id: 'article-governance-habits',\n slug: 'governance-habits',\n title: 'Governance Habits That Scale',\n description:\n 'Teams can move fast when quality gates are visible, lightweight, and shared with contributors.',\n publish_date: '2026-03-19T09:30:00.000Z',\n author: 'Content Ops',\n tags: ['governance', 'quality', 'workflows'],\n },\n];\n\nconst sampleContributions = [\n {\n id: 'contribution-1',\n title: 'Spring buyer guide',\n contributionTypeKey: 'article',\n status: 'needs_changes',\n revisionCount: 2,\n editorNotes: 'Please tighten the sourcing notes in the opening section.',\n contributorName: 'Taylor Rowan',\n contributorEmail: 'taylor@example.com',\n intakeDecision: 'needs_changes',\n body: 'The spring buyer guide draft is attached, with sourcing notes for the opening section and the product comparison appendix.',\n updatedAt: '2026-03-20T15:18:00.000Z',\n },\n {\n id: 'contribution-2',\n title: 'Field report: Pacific logistics',\n contributionTypeKey: 'report',\n status: 'submitted',\n revisionCount: 1,\n contributorName: 'Jordan Lee',\n contributorEmail: 'jordan@example.com',\n intakeDecision: 'submitted',\n body: 'Field notes from the Pacific corridor include updated shipping windows, route constraints, and operator interviews.',\n updatedAt: '2026-03-18T10:40:00.000Z',\n },\n];\n\nconst sampleGovernanceDefinitions: ContentGovernanceDefinitionsData = {\n effective: {\n policies: [\n {\n id: 'policy-facts',\n key: 'facts',\n label: 'Facts review',\n kind: 'facts',\n instructions: 'Compare claims against linked facts before publication.',\n enabled: true,\n },\n {\n id: 'policy-style',\n key: 'style',\n label: 'Style review',\n kind: 'custom',\n instructions: 'Apply newsroom tone, structure, and clarity guidelines.',\n enabled: true,\n },\n ],\n profiles: [\n {\n id: 'profile-publication',\n key: 'publication',\n label: 'Publication',\n description: 'Required before governed content can be published.',\n enabled: true,\n requirements: [\n {\n policyKey: 'facts',\n label: 'Facts review',\n blocking: true,\n acceptedStatuses: ['passed'],\n },\n {\n policyKey: 'style',\n label: 'Style review',\n blocking: false,\n acceptedStatuses: ['passed', 'warning'],\n },\n ],\n },\n ],\n assignments: [\n {\n id: 'assignment-article',\n key: 'article',\n label: 'Articles',\n contentType: 'article',\n contentVariant: null,\n enabled: true,\n factLinkingEnabled: true,\n transparencyEnabled: true,\n publicationProfileKey: 'publication',\n correctionProfileKey: null,\n enforcePublishReadiness: true,\n defaultFactRelationship: 'supports',\n },\n ],\n },\n persisted: {\n policies: [\n {\n id: 'policy-style',\n key: 'style',\n label: 'Style review',\n kind: 'custom',\n instructions: 'Apply newsroom tone, structure, and clarity guidelines.',\n enabled: true,\n },\n ],\n profiles: [\n {\n id: 'profile-publication',\n key: 'publication',\n label: 'Publication',\n description: 'Required before governed content can be published.',\n enabled: true,\n requirements: [\n {\n policyKey: 'facts',\n label: 'Facts review',\n blocking: true,\n acceptedStatuses: ['passed'],\n },\n {\n policyKey: 'style',\n label: 'Style review',\n blocking: false,\n acceptedStatuses: ['passed', 'warning'],\n },\n ],\n },\n ],\n assignments: [\n {\n id: 'assignment-article',\n key: 'article',\n label: 'Articles',\n contentType: 'article',\n contentVariant: null,\n enabled: true,\n factLinkingEnabled: true,\n transparencyEnabled: true,\n publicationProfileKey: 'publication',\n correctionProfileKey: null,\n enforcePublishReadiness: true,\n defaultFactRelationship: 'supports',\n },\n ],\n },\n};\n\nconst sampleEditorContent = {\n id: 'content-playground-editor',\n slug: 'playground-editor-reference',\n title: 'Reference editorial draft',\n description:\n 'A working editor preview with copy, references, and assets already populated.',\n body: `# Editorial Draft\n\nThis preview is meant to feel like a real authoring surface.\n\n- tighten the lede\n- verify product claims\n- attach supporting references`,\n author: 'Editorial Systems',\n type: 'article',\n status: 'draft',\n state: 'active',\n source: 'manual',\n tags: ['editorial', 'playground'],\n referenceIds: ['fact-aurora', 'source-governance'],\n assetIds: [],\n assets: [],\n};\n\nconst noop = () => {};\n\nfunction cloneValue<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction buildPlaygroundResponse<T>(data: T): ApiResponse<T> {\n return {\n data,\n success: true,\n };\n}\n\nfunction upsertByIdOrKey<\n T extends {\n id?: string;\n key?: string;\n },\n>(items: T[], value: Partial<T>, prefix: string): T[] {\n const id = value.id || `${prefix}-${value.key || items.length + 1}`;\n const nextValue = {\n ...value,\n id,\n } as T;\n\n const index = items.findIndex(\n (item) => item.id === id || (!!value.key && item.key === value.key),\n );\n\n if (index === -1) {\n return [...items, nextValue];\n }\n\n const nextItems = [...items];\n nextItems[index] = {\n ...nextItems[index],\n ...nextValue,\n };\n return nextItems;\n}\n\nfunction createGovernancePlaygroundClient(\n seed = sampleGovernanceDefinitions,\n): ContentGovernanceManagerClient {\n let policies = cloneValue(seed.effective.policies);\n let profiles = cloneValue(seed.effective.profiles);\n let assignments = cloneValue(seed.effective.assignments);\n\n const getDefinitions = (): ContentGovernanceDefinitionsData => ({\n effective: {\n policies: cloneValue(policies),\n profiles: cloneValue(profiles),\n assignments: cloneValue(assignments),\n },\n persisted: {\n policies: cloneValue(policies),\n profiles: cloneValue(profiles),\n assignments: cloneValue(assignments),\n },\n });\n\n return {\n contents: {\n getGovernanceDefinitions: async () =>\n buildPlaygroundResponse(getDefinitions()),\n },\n contentGovernancePolicies: {\n create: async (policy: Partial<ContentReviewPolicyData>) => {\n policies = upsertByIdOrKey(policies, policy, 'policy');\n return buildPlaygroundResponse(policies[policies.length - 1]);\n },\n update: async (id: string, policy: Partial<ContentReviewPolicyData>) => {\n policies = upsertByIdOrKey(policies, { ...policy, id }, 'policy');\n return buildPlaygroundResponse(\n policies.find((item) => item.id === id)!,\n );\n },\n delete: async (id: string) => {\n policies = policies.filter((item) => item.id !== id);\n return buildPlaygroundResponse(undefined);\n },\n },\n contentGovernanceProfiles: {\n create: async (profile: Partial<ContentGovernanceProfileData>) => {\n profiles = upsertByIdOrKey(profiles, profile, 'profile');\n return buildPlaygroundResponse(profiles[profiles.length - 1]);\n },\n update: async (\n id: string,\n profile: Partial<ContentGovernanceProfileData>,\n ) => {\n profiles = upsertByIdOrKey(profiles, { ...profile, id }, 'profile');\n return buildPlaygroundResponse(\n profiles.find((item) => item.id === id)!,\n );\n },\n delete: async (id: string) => {\n profiles = profiles.filter((item) => item.id !== id);\n return buildPlaygroundResponse(undefined);\n },\n },\n contentGovernanceAssignments: {\n create: async (assignment: Partial<ContentGovernanceAssignmentData>) => {\n assignments = upsertByIdOrKey(assignments, assignment, 'assignment');\n return buildPlaygroundResponse(assignments[assignments.length - 1]);\n },\n update: async (\n id: string,\n assignment: Partial<ContentGovernanceAssignmentData>,\n ) => {\n assignments = upsertByIdOrKey(\n assignments,\n { ...assignment, id },\n 'assignment',\n );\n return buildPlaygroundResponse(\n assignments.find((item) => item.id === id)!,\n );\n },\n delete: async (id: string) => {\n assignments = assignments.filter((item) => item.id !== id);\n return buildPlaygroundResponse(undefined);\n },\n },\n };\n}\n\nconst markdownExample = `# Content Playground\n\nThis preview module lives in \\`src/svelte/playground.ts\\`.\n\n- Package-owned previews stay close to the real components\n- The shared host simply discovers and renders them\n- Live entries can still point at the package's generated API routes`;\n\n// Keep the published playground module importable from Node so `smrt\n// playground list` can inspect entry metadata without needing a Svelte loader.\nconst loadArticleCard = () => import('./components/ArticleCard.svelte');\nconst loadArticleList = () => import('./components/ArticleList.svelte');\nconst loadContentEditor = () => import('./components/ContentEditor.svelte');\nconst loadContentContributionInbox = () =>\n import('./components/ContentContributionInbox.svelte');\nconst loadContentContributionPortal = () =>\n import('./components/ContentContributionPortal.svelte');\nconst loadContentGovernanceManager = () =>\n import('./components/ContentGovernanceManager.svelte');\nconst loadMarkdown = () => import('./components/Markdown.svelte');\nconst governancePlaygroundClient = createGovernancePlaygroundClient();\n\nexport default {\n packageName: '@happyvertical/smrt-content',\n displayName: 'Content',\n description: CONTENT_MODULE_META.description,\n moduleMeta: CONTENT_MODULE_META,\n entries: [\n {\n id: 'article-card',\n slotId: 'article-card',\n title: 'Article Card',\n description: 'Editorial teaser card with tags and metadata.',\n loadComponent: loadArticleCard,\n order: 1,\n props: {\n article: sampleArticles[0],\n showTags: true,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'article-list',\n slotId: 'article-list',\n title: 'Article List',\n description: 'Reference list/grid layout for published content.',\n loadComponent: loadArticleList,\n order: 2,\n props: {\n articles: sampleArticles,\n showTags: true,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'markdown',\n slotId: 'markdown',\n title: 'Markdown Renderer',\n description: 'Safe markdown rendering with a small editorial snippet.',\n loadComponent: loadMarkdown,\n order: 3,\n props: {\n content: markdownExample,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'content-editor',\n title: 'Content Editor',\n description: 'Authoring surface for body copy, references, and media.',\n loadComponent: loadContentEditor,\n order: 4,\n props: {\n apiBaseUrl: resolveContentPlaygroundApiBaseUrl(),\n content: sampleEditorContent,\n contentId: sampleEditorContent.id,\n agentChatEnabled: false,\n agentChatNotice:\n 'The shared playground keeps the editor preview local. Run the content package app when you want the live agent chat routes too.',\n onSave: noop,\n onCancel: noop,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'contribution-portal',\n title: 'Contribution Portal',\n description: 'Contributor-facing inbox and submission status view.',\n loadComponent: loadContentContributionPortal,\n order: 5,\n props: {\n contributions: sampleContributions,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'contribution-inbox',\n title: 'Contribution Inbox',\n description:\n 'Editorial review queue for approving, rejecting, or requesting changes.',\n loadComponent: loadContentContributionInbox,\n order: 6,\n props: {\n contributions: sampleContributions,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'governance-manager',\n title: 'Governance Manager',\n description:\n 'Administrative view with in-memory mock data and optional live package routes.',\n loadComponent: loadContentGovernanceManager,\n order: 7,\n modes: {\n mock: {\n label: 'Mock',\n description:\n 'Uses an in-memory governance client so the shared playground works without a package-local dev server.',\n props: {\n client: governancePlaygroundClient,\n },\n },\n live: {\n label: 'Live',\n description:\n 'Requires the content package dev server and generated routes. Override the base URL with ?smrtContentApiBaseUrl=... or window.__SMRT_CONTENT_PLAYGROUND_API_BASE_URL__ when needed.',\n props: {\n apiBaseUrl: resolveContentPlaygroundApiBaseUrl(),\n },\n },\n },\n },\n ],\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface PublishReadinessRequirement {
|
|
2
|
+
label: string;
|
|
3
|
+
blocking: boolean;
|
|
4
|
+
missing: boolean;
|
|
5
|
+
stale?: boolean;
|
|
6
|
+
satisfied: boolean;
|
|
7
|
+
latestStatus: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface PublishReadinessProfile {
|
|
10
|
+
profileKey: string;
|
|
11
|
+
ready: boolean;
|
|
12
|
+
complete: boolean;
|
|
13
|
+
requirements: PublishReadinessRequirement[];
|
|
14
|
+
}
|
|
15
|
+
export interface ContentPublishReadinessState {
|
|
16
|
+
level: 'ready' | 'advisory' | 'blocked';
|
|
17
|
+
title: string;
|
|
18
|
+
message: string;
|
|
19
|
+
details: string[];
|
|
20
|
+
disableSave: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface EvaluateContentPublishReadinessOptions {
|
|
23
|
+
status?: string | null;
|
|
24
|
+
contentId?: string | null;
|
|
25
|
+
reviewProfileKey: string;
|
|
26
|
+
reviewProfiles?: PublishReadinessProfile[] | null;
|
|
27
|
+
enforce?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function evaluateContentPublishReadiness(options: EvaluateContentPublishReadinessOptions): ContentPublishReadinessState | null;
|
|
30
|
+
//# sourceMappingURL=publish-readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-readiness.d.ts","sourceRoot":"","sources":["../src/publish-readiness.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,2BAA2B,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,sCAAsC;IACrD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,uBAAuB,EAAE,GAAG,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAkBD,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C,4BAA4B,GAAG,IAAI,CAwErC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
function describeRequirement(requirement) {
|
|
2
|
+
if (requirement.missing) {
|
|
3
|
+
return `${requirement.label}: not run yet`;
|
|
4
|
+
}
|
|
5
|
+
if (requirement.stale) {
|
|
6
|
+
return `${requirement.label}: stale, rerun required`;
|
|
7
|
+
}
|
|
8
|
+
if (requirement.latestStatus) {
|
|
9
|
+
return `${requirement.label}: ${requirement.latestStatus}`;
|
|
10
|
+
}
|
|
11
|
+
return `${requirement.label}: not satisfied`;
|
|
12
|
+
}
|
|
13
|
+
function evaluateContentPublishReadiness(options) {
|
|
14
|
+
if (options.status !== "published") {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const enforce = options.enforce === true;
|
|
18
|
+
if (!options.contentId || options.contentId === "new") {
|
|
19
|
+
return {
|
|
20
|
+
level: enforce ? "blocked" : "advisory",
|
|
21
|
+
title: "Publication Needs A Saved Draft",
|
|
22
|
+
message: "Save this governed content first so reviews can run before publication.",
|
|
23
|
+
details: [],
|
|
24
|
+
disableSave: enforce
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const profile = (options.reviewProfiles || []).find(
|
|
28
|
+
(candidate) => candidate.profileKey === options.reviewProfileKey
|
|
29
|
+
);
|
|
30
|
+
if (!profile) {
|
|
31
|
+
return {
|
|
32
|
+
level: "ready",
|
|
33
|
+
title: "No Publish Profile Configured",
|
|
34
|
+
message: "No publication review profile is configured, so publish readiness is not being enforced here.",
|
|
35
|
+
details: [],
|
|
36
|
+
disableSave: false
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const unsatisfiedBlocking = profile.requirements.filter(
|
|
40
|
+
(requirement) => requirement.blocking && !requirement.satisfied
|
|
41
|
+
);
|
|
42
|
+
if (unsatisfiedBlocking.length > 0) {
|
|
43
|
+
return {
|
|
44
|
+
level: enforce ? "blocked" : "advisory",
|
|
45
|
+
title: enforce ? "Blocking Reviews Must Pass" : "Blocking Reviews Are Not Satisfied",
|
|
46
|
+
message: enforce ? "Resolve the blocking review requirements before publishing this content." : "This content can still be published, but blocking review requirements are not yet satisfied.",
|
|
47
|
+
details: unsatisfiedBlocking.map(describeRequirement),
|
|
48
|
+
disableSave: enforce
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const incompleteRequirements = profile.requirements.filter(
|
|
52
|
+
(requirement) => !requirement.satisfied
|
|
53
|
+
);
|
|
54
|
+
if (incompleteRequirements.length > 0 || !profile.complete) {
|
|
55
|
+
return {
|
|
56
|
+
level: "advisory",
|
|
57
|
+
title: "Some Reviews Are Still Pending",
|
|
58
|
+
message: "Blocking requirements are satisfied, but some advisory reviews are still missing or need follow-up.",
|
|
59
|
+
details: incompleteRequirements.map(describeRequirement),
|
|
60
|
+
disableSave: false
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
level: "ready",
|
|
65
|
+
title: "Ready To Publish",
|
|
66
|
+
message: "All configured review requirements for this publication profile are satisfied.",
|
|
67
|
+
details: [],
|
|
68
|
+
disableSave: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
evaluateContentPublishReadiness
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=publish-readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-readiness.js","sources":["../src/publish-readiness.ts"],"sourcesContent":["export interface PublishReadinessRequirement {\n label: string;\n blocking: boolean;\n missing: boolean;\n stale?: boolean;\n satisfied: boolean;\n latestStatus: string | null;\n}\n\nexport interface PublishReadinessProfile {\n profileKey: string;\n ready: boolean;\n complete: boolean;\n requirements: PublishReadinessRequirement[];\n}\n\nexport interface ContentPublishReadinessState {\n level: 'ready' | 'advisory' | 'blocked';\n title: string;\n message: string;\n details: string[];\n disableSave: boolean;\n}\n\nexport interface EvaluateContentPublishReadinessOptions {\n status?: string | null;\n contentId?: string | null;\n reviewProfileKey: string;\n reviewProfiles?: PublishReadinessProfile[] | null;\n enforce?: boolean;\n}\n\nfunction describeRequirement(requirement: PublishReadinessRequirement): string {\n if (requirement.missing) {\n return `${requirement.label}: not run yet`;\n }\n\n if (requirement.stale) {\n return `${requirement.label}: stale, rerun required`;\n }\n\n if (requirement.latestStatus) {\n return `${requirement.label}: ${requirement.latestStatus}`;\n }\n\n return `${requirement.label}: not satisfied`;\n}\n\nexport function evaluateContentPublishReadiness(\n options: EvaluateContentPublishReadinessOptions,\n): ContentPublishReadinessState | null {\n if (options.status !== 'published') {\n return null;\n }\n\n const enforce = options.enforce === true;\n\n if (!options.contentId || options.contentId === 'new') {\n return {\n level: enforce ? 'blocked' : 'advisory',\n title: 'Publication Needs A Saved Draft',\n message:\n 'Save this governed content first so reviews can run before publication.',\n details: [],\n disableSave: enforce,\n };\n }\n\n const profile = (options.reviewProfiles || []).find(\n (candidate) => candidate.profileKey === options.reviewProfileKey,\n );\n\n if (!profile) {\n return {\n level: 'ready',\n title: 'No Publish Profile Configured',\n message:\n 'No publication review profile is configured, so publish readiness is not being enforced here.',\n details: [],\n disableSave: false,\n };\n }\n\n const unsatisfiedBlocking = profile.requirements.filter(\n (requirement) => requirement.blocking && !requirement.satisfied,\n );\n if (unsatisfiedBlocking.length > 0) {\n return {\n level: enforce ? 'blocked' : 'advisory',\n title: enforce\n ? 'Blocking Reviews Must Pass'\n : 'Blocking Reviews Are Not Satisfied',\n message: enforce\n ? 'Resolve the blocking review requirements before publishing this content.'\n : 'This content can still be published, but blocking review requirements are not yet satisfied.',\n details: unsatisfiedBlocking.map(describeRequirement),\n disableSave: enforce,\n };\n }\n\n const incompleteRequirements = profile.requirements.filter(\n (requirement) => !requirement.satisfied,\n );\n if (incompleteRequirements.length > 0 || !profile.complete) {\n return {\n level: 'advisory',\n title: 'Some Reviews Are Still Pending',\n message:\n 'Blocking requirements are satisfied, but some advisory reviews are still missing or need follow-up.',\n details: incompleteRequirements.map(describeRequirement),\n disableSave: false,\n };\n }\n\n return {\n level: 'ready',\n title: 'Ready To Publish',\n message:\n 'All configured review requirements for this publication profile are satisfied.',\n details: [],\n disableSave: false,\n };\n}\n"],"names":[],"mappings":"AAgCA,SAAS,oBAAoB,aAAkD;AAC7E,MAAI,YAAY,SAAS;AACvB,WAAO,GAAG,YAAY,KAAK;AAAA,EAC7B;AAEA,MAAI,YAAY,OAAO;AACrB,WAAO,GAAG,YAAY,KAAK;AAAA,EAC7B;AAEA,MAAI,YAAY,cAAc;AAC5B,WAAO,GAAG,YAAY,KAAK,KAAK,YAAY,YAAY;AAAA,EAC1D;AAEA,SAAO,GAAG,YAAY,KAAK;AAC7B;AAEO,SAAS,gCACd,SACqC;AACrC,MAAI,QAAQ,WAAW,aAAa;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,QAAQ,YAAY;AAEpC,MAAI,CAAC,QAAQ,aAAa,QAAQ,cAAc,OAAO;AACrD,WAAO;AAAA,MACL,OAAO,UAAU,YAAY;AAAA,MAC7B,OAAO;AAAA,MACP,SACE;AAAA,MACF,SAAS,CAAA;AAAA,MACT,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,WAAW,QAAQ,kBAAkB,CAAA,GAAI;AAAA,IAC7C,CAAC,cAAc,UAAU,eAAe,QAAQ;AAAA,EAAA;AAGlD,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,SACE;AAAA,MACF,SAAS,CAAA;AAAA,MACT,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,sBAAsB,QAAQ,aAAa;AAAA,IAC/C,CAAC,gBAAgB,YAAY,YAAY,CAAC,YAAY;AAAA,EAAA;AAExD,MAAI,oBAAoB,SAAS,GAAG;AAClC,WAAO;AAAA,MACL,OAAO,UAAU,YAAY;AAAA,MAC7B,OAAO,UACH,+BACA;AAAA,MACJ,SAAS,UACL,6EACA;AAAA,MACJ,SAAS,oBAAoB,IAAI,mBAAmB;AAAA,MACpD,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,yBAAyB,QAAQ,aAAa;AAAA,IAClD,CAAC,gBAAgB,CAAC,YAAY;AAAA,EAAA;AAEhC,MAAI,uBAAuB,SAAS,KAAK,CAAC,QAAQ,UAAU;AAC1D,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,SACE;AAAA,MACF,SAAS,uBAAuB,IAAI,mBAAmB;AAAA,MACvD,aAAa;AAAA,IAAA;AAAA,EAEjB;AAEA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,SACE;AAAA,IACF,SAAS,CAAA;AAAA,IACT,aAAa;AAAA,EAAA;AAEjB;"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type ResolvedAddress = {
|
|
2
|
+
address: string;
|
|
3
|
+
family?: number;
|
|
4
|
+
};
|
|
5
|
+
export type ResolveHostname = (hostname: string) => Promise<ResolvedAddress[]>;
|
|
6
|
+
export interface SafeRemoteUrlOptions {
|
|
7
|
+
/** Skip the private-network checks (trusted callers only, e.g. local dev). */
|
|
8
|
+
allowPrivateNetworkHosts?: boolean;
|
|
9
|
+
/** Injectable resolver for tests; defaults to {@link defaultResolveHostname}. */
|
|
10
|
+
resolveHostname?: ResolveHostname;
|
|
11
|
+
}
|
|
12
|
+
export declare function defaultResolveHostname(hostname: string): Promise<ResolvedAddress[]>;
|
|
13
|
+
export declare function isBlockedIPv4(address: string): boolean;
|
|
14
|
+
export declare function isBlockedIPv6(address: string): boolean;
|
|
15
|
+
export declare function isBlockedAddress(address: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Parse and validate a remote URL for outbound fetching. Rejects non-http(s)
|
|
18
|
+
* schemes, embedded credentials, and hosts that resolve to non-public ranges.
|
|
19
|
+
* Returns the parsed {@link URL} on success; throws a descriptive `Error`
|
|
20
|
+
* otherwise.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertSafeRemoteUrl(rawUrl: string, options?: SafeRemoteUrlOptions): Promise<URL>;
|
|
23
|
+
export interface SafeRedirectOptions extends SafeRemoteUrlOptions {
|
|
24
|
+
/** Maximum redirect hops to follow before failing. Default 5. */
|
|
25
|
+
maxRedirects?: number;
|
|
26
|
+
/** Per-hop timeout in ms. Default 10s. */
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
/** Injectable fetch (primarily for tests). Defaults to global `fetch`. */
|
|
29
|
+
fetchImpl?: typeof fetch;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a URL's redirect chain and return the final safe {@link URL}, with
|
|
33
|
+
* EVERY hop re-validated through {@link assertSafeRemoteUrl}.
|
|
34
|
+
*
|
|
35
|
+
* Use this before handing a URL to a fetcher that follows redirects on its own
|
|
36
|
+
* (e.g. `fetchDocument`): the up-front {@link assertSafeRemoteUrl} check alone
|
|
37
|
+
* can't stop an allowed public host from `30x`-redirecting into an internal /
|
|
38
|
+
* loopback / metadata host, which would defeat the SSRF guard (review #1562).
|
|
39
|
+
*
|
|
40
|
+
* Redirects are followed with `GET` + `redirect: 'manual'` to match downstream
|
|
41
|
+
* GET-based fetchers; response bodies are discarded (the redirect bodies are
|
|
42
|
+
* empty and the terminal body is left for the caller to re-fetch), so this does
|
|
43
|
+
* not double-download content.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveSafeFinalUrl(rawUrl: string, options?: SafeRedirectOptions): Promise<URL>;
|
|
46
|
+
/**
|
|
47
|
+
* Strip userinfo (`user:pass@`) from a URL so it can be safely logged or echoed
|
|
48
|
+
* in an error message. Returns a placeholder for unparseable input. Never let a
|
|
49
|
+
* credential-bearing URL reach logs/errors verbatim (review #1562).
|
|
50
|
+
*/
|
|
51
|
+
export declare function redactUrlCredentials(raw: string): string;
|
|
52
|
+
//# sourceMappingURL=safe-remote-url.d.ts.map
|