article-content-renderer-vue2 0.3.0 → 0.4.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/README.md +127 -20
- package/dist/article-content-renderer-vue2.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +423 -312
- package/dist/types/components/ArticleContentRenderer.vue.d.ts +6 -12
- package/dist/types/index.d.ts +1 -1
- package/dist/types/protocols/types.d.ts +2 -1
- package/dist/types/types.d.ts +28 -5
- package/package.json +1 -1
- package/protocol/article-content-protocol-v1.json +94 -20
package/README.md
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
- 支持 Article Content Protocol v1 的全部节点和 marks。
|
|
10
10
|
- 使用 Vue 2 `CreateElement` 和 `VNodeData`,不是 Vue 3 兼容层。
|
|
11
11
|
- 支持严格模式和非严格容错渲染。
|
|
12
|
-
- 拦截危险链接和图片 URL。
|
|
12
|
+
- 拦截危险链接和图片 URL。
|
|
13
|
+
- `link` mark 支持 `href` 和 `custom` 两种类型,custom 链接由使用者回调生成完整地址。
|
|
13
14
|
- articleButton 的 text/button 完整 href 由使用者回调生成。
|
|
14
15
|
- articleButton 的 link 样式直接使用协议节点中的 href,不调用 resolver。
|
|
15
16
|
- 支持 Vue 2 SSR。
|
|
@@ -172,7 +173,117 @@ export default Vue.extend({
|
|
|
172
173
|
广告组件也通过具名插槽传入,并自行负责 SDK 加载、广告请求、空广告回退、唯一 DOM ID 和卸载清理。
|
|
173
174
|
|
|
174
175
|
`imageBaseUrl` 默认为 `https://www.doitme.link/`;传入新地址时,只替换文档图片中该默认前缀,其他图片 URL 保持不变。
|
|
175
|
-
##
|
|
176
|
+
## link mark:href 与 custom
|
|
177
|
+
|
|
178
|
+
协议中的 `link` mark 现在有两种链接来源:
|
|
179
|
+
|
|
180
|
+
- `type` 省略或为 `"href"`:直接使用 `attrs.href`,兼容旧文档,不调用 custom resolver。
|
|
181
|
+
- `type: "custom"`:协议只保存业务属性 `id`、`title` 和 `target`,通过 `resolveCustomLink` 生成最终 `href`。
|
|
182
|
+
|
|
183
|
+
普通 href 链接可以继续使用旧格式:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const article: ArticleDocument = {
|
|
187
|
+
type: 'doc',
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: 'paragraph',
|
|
191
|
+
content: [
|
|
192
|
+
{
|
|
193
|
+
type: 'text',
|
|
194
|
+
text: '直接链接',
|
|
195
|
+
marks: [
|
|
196
|
+
{
|
|
197
|
+
type: 'link',
|
|
198
|
+
attrs: { href: '/help', target: '_self' },
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
custom 链接不在协议中提供 `href`:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const article: ArticleDocument = {
|
|
212
|
+
type: 'doc',
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: 'paragraph',
|
|
216
|
+
content: [
|
|
217
|
+
{
|
|
218
|
+
type: 'text',
|
|
219
|
+
text: '查看文章详情',
|
|
220
|
+
marks: [
|
|
221
|
+
{
|
|
222
|
+
type: 'link',
|
|
223
|
+
attrs: {
|
|
224
|
+
type: 'custom',
|
|
225
|
+
id: 'article-42',
|
|
226
|
+
title: 'memory signs',
|
|
227
|
+
target: '_self',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
由使用者读取 `id + title` 并返回完整地址:
|
|
239
|
+
|
|
240
|
+
```vue
|
|
241
|
+
<script lang="ts">
|
|
242
|
+
import Vue from 'vue'
|
|
243
|
+
import type { ResolveCustomLink } from 'article-content-renderer-vue2'
|
|
244
|
+
|
|
245
|
+
export default Vue.extend({
|
|
246
|
+
methods: {
|
|
247
|
+
resolveCustomLink: ((attrs) => {
|
|
248
|
+
return `/detail/${encodeURIComponent(attrs.id)}/${encodeURIComponent(attrs.title)}`
|
|
249
|
+
}) as ResolveCustomLink,
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
</script>
|
|
253
|
+
|
|
254
|
+
<template>
|
|
255
|
+
<ArticleContentRenderer
|
|
256
|
+
:document="article"
|
|
257
|
+
:resolve-custom-link="resolveCustomLink"
|
|
258
|
+
/>
|
|
259
|
+
</template>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
也可以返回对象来覆盖 `target` 或补充 `rel`:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const resolveCustomLink: ResolveCustomLink = (attrs) => ({
|
|
266
|
+
href: `/detail/${encodeURIComponent(attrs.id)}?title=${encodeURIComponent(attrs.title)}`,
|
|
267
|
+
target: '_blank',
|
|
268
|
+
rel: 'external',
|
|
269
|
+
})
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
最终 custom DOM 会保留协议绑定属性,方便样式或事件委托:
|
|
273
|
+
|
|
274
|
+
```html
|
|
275
|
+
<a
|
|
276
|
+
href="/detail/article-42/memory%20signs"
|
|
277
|
+
data-link-type="custom"
|
|
278
|
+
data-link-id="article-42"
|
|
279
|
+
title="memory signs"
|
|
280
|
+
target="_self"
|
|
281
|
+
>查看文章详情</a>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
`resolveCustomLink` **只会**为 `type: "custom"` 的 link mark 调用。href 类型始终直接读取 `attrs.href`。传给回调的 `attrs` 和 `mark` 是只读、冻结的快照。回调缺失、返回 `null`、抛出异常或返回不安全 URL 时,组件渲染不带 `href` 的禁用态 `<a>`,并通过 `render-error` 报告 `LINK_RESOLUTION_FAILED` 或 `UNSAFE_URL`。
|
|
285
|
+
|
|
286
|
+
## articleButton 链接
|
|
176
287
|
|
|
177
288
|
`style: "button"`、`style: "text"` 和 `style: "link"` 都使用 `<a>` 渲染。它们的链接来源不同:
|
|
178
289
|
|
|
@@ -298,8 +409,9 @@ function handleArticleButtonClick(payload: ArticleButtonClickPayload): void {
|
|
|
298
409
|
| `protocolVersion` | `number` | `1` | 协议适配器版本 |
|
|
299
410
|
| `strict` | `boolean` | `false` | 校验失败时是否停止整篇正文渲染 |
|
|
300
411
|
| `customSlots` | `CustomSlot[]` | `[]` | 配置一个或多个具名插槽的顶层正文插入位置 |
|
|
301
|
-
| `imageBaseUrl` | `string` | `"https://www.doitme.link/"` | 替换文档图片的默认地址前缀 |
|
|
412
|
+
| `imageBaseUrl` | `string` | `"https://www.doitme.link/"` | 替换文档图片的默认地址前缀 |
|
|
302
413
|
| `resolveArticleButtonLink` | `ResolveArticleButtonLink` | `undefined` | 为 text/button 生成完整链接;link 类型不调用 |
|
|
414
|
+
| `resolveCustomLink` | `ResolveCustomLink` | `undefined` | 仅为 `type: "custom"` 的 link mark 生成完整安全链接 |
|
|
303
415
|
|
|
304
416
|
## Events
|
|
305
417
|
|
|
@@ -338,23 +450,18 @@ interface RenderIssue {
|
|
|
338
450
|
|
|
339
451
|
普通链接和 articleButton 允许 `http:`、`https:`、`mailto:`、`tel:`、相对路径和页面锚点。图片允许 `http:`、`https:`、`blob:` 和相对路径。危险协议会被拦截。
|
|
340
452
|
|
|
341
|
-
## Vue 2
|
|
342
|
-
|
|
343
|
-
Vue 2
|
|
344
|
-
|
|
345
|
-
```html
|
|
346
|
-
<
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
display: contents;
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
它不会增加额外布局盒。Vue 3 版本使用 Fragment,不需要此兼容根节点。
|
|
453
|
+
## Vue 2 顶层渲染结构
|
|
454
|
+
|
|
455
|
+
组件使用 Vue 2 函数式多根渲染,正文节点会直接进入使用方已有的容器,不会额外输出 `.acp-document`、`data-node-type="doc"` 或 `data-protocol-version`:
|
|
456
|
+
|
|
457
|
+
```html
|
|
458
|
+
<article class="article-content">
|
|
459
|
+
<h1 class="acp-heading">...</h1>
|
|
460
|
+
<p class="acp-paragraph">...</p>
|
|
461
|
+
</article>
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Vue 2 应用本身仍要求单一根节点。因此,不要把包含多个正文节点的 `ArticleContentRenderer` 直接作为整个 Vue 实例 render 函数的唯一根;请像上例一样放入页面已有的 `<main>`、`<article>` 或其他业务容器中。这个容器由使用方控制,不属于组件输出。
|
|
358
465
|
|
|
359
466
|
## 样式覆盖
|
|
360
467
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--acp-color-text: inherit;--acp-color-muted: #667085;--acp-color-border: #d0d5dd;--acp-color-surface: #f8fafc;--acp-color-accent: #2563eb;--acp-color-accent-hover: #1d4ed8;--acp-color-on-accent: #ffffff;--acp-radius: .375rem;--acp-spacing-block: 1rem;--acp-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace}.acp-
|
|
1
|
+
:root{--acp-color-text: inherit;--acp-color-muted: #667085;--acp-color-border: #d0d5dd;--acp-color-surface: #f8fafc;--acp-color-accent: #2563eb;--acp-color-accent-hover: #1d4ed8;--acp-color-on-accent: #ffffff;--acp-radius: .375rem;--acp-spacing-block: 1rem;--acp-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace}.acp-paragraph,.acp-heading,.acp-blockquote,.acp-list,.acp-code-block,.acp-horizontal-rule,.acp-image,.acp-article-button,.acp-table-wrapper{box-sizing:border-box}.acp-paragraph,.acp-heading,.acp-blockquote,.acp-list,.acp-code-block,.acp-horizontal-rule,.acp-image,.acp-table-wrapper{margin-block:0 var(--acp-spacing-block)}.acp-paragraph:first-child,.acp-heading:first-child,.acp-blockquote:first-child,.acp-list:first-child,.acp-code-block:first-child,.acp-horizontal-rule:first-child,.acp-image:first-child,.acp-table-wrapper:first-child{margin-block-start:0}.acp-paragraph{color:var(--acp-color-text)}.acp-blockquote{margin-inline:0;padding-inline-start:1rem;border-inline-start:.25rem solid var(--acp-color-border);color:var(--acp-color-muted)}.acp-list{padding-inline-start:1.5rem}.acp-list-item>:last-child{margin-block-end:0}.acp-code-block{overflow-x:auto;padding:1rem;border-radius:var(--acp-radius);background:var(--acp-color-surface);font-family:var(--acp-font-mono);white-space:pre}.acp-mark--code{padding:.1em .25em;border-radius:calc(var(--acp-radius) / 2);background:var(--acp-color-surface);font-family:var(--acp-font-mono)}.acp-horizontal-rule{border:0;border-block-start:1px solid var(--acp-color-border)}.acp-image{display:flex;width:100%}.acp-image--left{justify-content:flex-start}.acp-image--center{justify-content:center}.acp-image--right{justify-content:flex-end}.acp-image__element{display:block;max-width:100%;height:auto}.acp-link,.acp-article-button--text,.acp-article-button--link{color:var(--acp-color-accent);text-decoration:underline;text-underline-offset:.15em}.acp-link:hover,.acp-article-button--text:hover,.acp-article-button--link:hover{color:var(--acp-color-accent-hover)}.acp-link--disabled{cursor:not-allowed;opacity:.55}.acp-article-button{display:inline-flex;align-items:center;justify-content:center;margin-block:0 var(--acp-spacing-block);cursor:pointer}.acp-article-button--button{min-height:2.5rem;padding:.5rem 1rem;border-radius:var(--acp-radius);background:var(--acp-color-accent);color:var(--acp-color-on-accent);text-decoration:none}.acp-article-button--button:hover{background:var(--acp-color-accent-hover)}.acp-article-button--disabled{cursor:not-allowed;opacity:.55}.acp-table-wrapper{width:100%;overflow-x:auto}.acp-table{width:100%;border-collapse:collapse}.acp-table-cell{padding:.625rem .75rem;border:1px solid var(--acp-color-border);vertical-align:top}.acp-table-cell>:last-child{margin-block-end:0}.acp-render-error{padding:.75rem 1rem;border:1px solid #fda29b;border-radius:var(--acp-radius);background:#fffbfa;color:#b42318}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var M=Object.defineProperty,Y=Object.defineProperties;var F=Object.getOwnPropertyDescriptors;var B=Object.getOwnPropertySymbols;var K=Object.prototype.hasOwnProperty,G=Object.prototype.propertyIsEnumerable;var R=(i,r,t)=>r in i?M(i,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[r]=t,m=(i,r)=>{for(var t in r||(r={}))K.call(r,t)&&R(i,t,r[t]);if(B)for(var t of B(r))G.call(r,t)&&R(i,t,r[t]);return i},A=(i,r)=>Y(i,F(r));var w=(i,r,t)=>R(i,typeof r!="symbol"?r+"":r,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const W=require("vue"),H=new Set(["http:","https:","mailto:","tel:"]),X=new Set(["http:","https:","blob:"]),J="https://article-content-renderer.invalid/",N="https://www.doitme.link/";function Q(i,r){if(typeof i!="string")return i;const t=i.trim();if(!t.startsWith(N))return t;const n=(r.trim()||N).replace(/\/+$/u,""),s=t.slice(N.length).replace(/^\/+/,"");return`${n}/${s}`}function T(i,r){if(typeof i!="string")return null;const t=i.trim();if(!t||/[\u0000-\u001F\u007F]/u.test(t))return null;try{const e=new URL(t,J);return(r==="link"?H:X).has(e.protocol)?t:null}catch(e){return null}}function v(i,r){const t=new Set(typeof i=="string"?i.trim().split(/\s+/u).filter(Boolean):[]);return r==="_blank"&&(t.add("noopener"),t.add("noreferrer")),t.size>0?[...t].join(" "):void 0}const Z=new Set(["paragraph","heading","blockquote","bulletList","orderedList","codeBlock","horizontalRule","image","articleButton","table"]);function p(i){return typeof i=="object"&&i!==null&&!Array.isArray(i)}function _(i){return p(i)?i:{}}function f(i,r,t,e){var c;const n=p(t)?t:{},s=p(t)?e:t,o={},l={};for(const[u,h]of Object.entries(n))if(u==="class")o.class=h;else if(u==="style")o.style=h;else if(u.startsWith("on")&&typeof h=="function"){const y=u.slice(2).toLowerCase();o.on=A(m({},(c=o.on)!=null?c:{}),{[y]:h})}else h!==void 0&&h!==!1&&(l[u]=h);return Object.keys(l).length>0&&(o.attrs=l),i.createElement(r,o,s)}function k(i){return Array.isArray(i)?i:[]}function g(i,...r){return`${i}/${r.map(String).join("/")}`}function V(i){return i==="left"||i==="center"||i==="right"||i==="justify"}function tt(i){return i==="left"||i==="center"||i==="right"}function E(i){return i==="_blank"||i==="_self"}function b(i,r){i.reportIssue(r)}function et(i,r,t){if(!p(i)||i.type!=="text"||typeof i.text!="string"||!i.text)return null;let e=i.text;const n=k(i.marks);for(let s=0;s<n.length;s+=1){const o=n[s];if(!p(o)||typeof o.type!="string")continue;const l=g(r,"marks",s);switch(o.type){case"bold":e=f(t,"strong",{class:"acp-mark acp-mark--bold"},[e]);break;case"italic":e=f(t,"em",{class:"acp-mark acp-mark--italic"},[e]);break;case"strike":e=f(t,"s",{class:"acp-mark acp-mark--strike"},[e]);break;case"underline":e=f(t,"u",{class:"acp-mark acp-mark--underline"},[e]);break;case"code":e=f(t,"code",{class:"acp-mark acp-mark--code"},[e]);break;case"link":{const c=_(o.attrs),u=T(c.href,"link");if(!u){b(t,{code:"UNSAFE_URL",path:g(l,"attrs","href"),message:"The link URL is empty, malformed, or uses a disallowed protocol.",nodeType:"link"});break}const h=E(c.target)?c.target:"_blank";e=f(t,"a",{class:"acp-link",href:u,target:h,rel:v(void 0,h)},[e]);break}}}return e}function C(i,r,t){return k(i).map((e,n)=>et(e,g(r,n),t)).filter(e=>e!==null)}function q(i,r,t){return!p(i)||i.type!=="listItem"?null:f(t,"li",{class:"acp-list-item","data-node-type":"listItem"},P(i.content,g(r,"content"),t))}function rt(i,r,t){return!p(i)||i.type!=="tableCell"?null:f(t,"td",{class:"acp-table-cell","data-node-type":"tableCell"},P(i.content,g(r,"content"),t))}function it(i,r,t){if(!p(i)||i.type!=="tableRow")return null;const e=k(i.content).map((n,s)=>rt(n,g(r,"content",s),t)).filter(n=>n!==null);return f(t,"tr",{class:"acp-table-row","data-node-type":"tableRow"},e)}function U(i){var n;if(p(i)&&i.target!==void 0&&!E(i.target)||p(i)&&i.rel!==void 0&&typeof i.rel!="string")return null;const r=typeof i=="string"?{href:i}:p(i)&&typeof i.href=="string"?m(m({href:i.href},E(i.target)?{target:i.target}:{}),typeof i.rel=="string"?{rel:i.rel}:{}):null;if(!r)return null;const t=T(r.href,"link");if(!t)return null;const e=(n=r.target)!=null?n:"_self";return{href:t,target:e,rel:v(r.rel,e)}}function nt(i,r,t){var h;const e=_(i.attrs),n=e.style==="text"||e.style==="button",s=e.style==="link";if(typeof e.text!="string"||!e.text||!n&&!s||n&&(typeof e.id!="string"||!e.id)||s&&e.id!==void 0&&(typeof e.id!="string"||!e.id))return null;const o=Object.freeze(s?m(A(m(m({},typeof e.id=="string"?{id:e.id}:{}),typeof e.title=="string"?{title:e.title}:{}),{text:e.text,style:"link"}),typeof e.href=="string"?{href:e.href}:{}):A(m({id:e.id},typeof e.title=="string"?{title:e.title}:{}),{text:e.text,style:e.style})),l=Object.freeze({type:"articleButton",attrs:o});let c=null;if(o.style==="link")o.href!==void 0&&(c=U(o.href),c||b(t,{code:"UNSAFE_URL",path:`${r}/attrs/href`,message:"The articleButton link href is not a usable safe URL.",nodeType:"articleButton"}));else if(!t.resolveArticleButtonLink)b(t,{code:"LINK_RESOLUTION_FAILED",path:r,message:"No resolveArticleButtonLink callback was provided for an articleButton node.",nodeType:"articleButton"});else try{const y=Object.freeze({type:"articleButton",attrs:o}),L=t.resolveArticleButtonLink(o,y);c=U(L),c||b(t,{code:typeof L=="string"||p(L)&&typeof L.href=="string"?"UNSAFE_URL":"LINK_RESOLUTION_FAILED",path:r,message:"The articleButton link resolver returned no usable safe URL.",nodeType:"articleButton"})}catch(y){b(t,{code:"LINK_RESOLUTION_FAILED",path:r,message:`The articleButton link resolver threw an error: ${y instanceof Error?y.message:String(y)}`,nodeType:"articleButton"})}const u=(h=c==null?void 0:c.href)!=null?h:null;return f(t,"a",{class:["acp-article-button",`acp-article-button--${o.style}`,!u&&"acp-article-button--disabled"],href:u!=null?u:void 0,target:c==null?void 0:c.target,rel:c==null?void 0:c.rel,title:o.title,"data-node-type":"articleButton","data-article-button-id":o.id,"data-article-button-style":o.style,"aria-disabled":u?void 0:"true",onClick:y=>{u||y.preventDefault(),t.emitArticleButtonClick({attrs:o,node:l,href:u,event:y})}},o.text)}function $(i,r,t){if(!p(i)||typeof i.type!="string"||!Z.has(i.type))return null;const e=_(i.attrs);switch(i.type){case"paragraph":{const n=V(e.textAlign)?e.textAlign:void 0;return f(t,"p",{class:"acp-paragraph","data-node-type":"paragraph",style:n?{textAlign:n}:void 0},C(i.content,g(r,"content"),t))}case"heading":{const n=Number.isInteger(e.level)&&Number(e.level)>=1&&Number(e.level)<=6?Number(e.level):1,s=V(e.textAlign)?e.textAlign:void 0;return f(t,`h${n}`,{class:["acp-heading",`acp-heading--${n}`],"data-node-type":"heading",style:s?{textAlign:s}:void 0},C(i.content,g(r,"content"),t))}case"blockquote":return f(t,"blockquote",{class:"acp-blockquote","data-node-type":"blockquote"},P(i.content,g(r,"content"),t));case"bulletList":{const n=k(i.content).map((s,o)=>q(s,g(r,"content",o),t)).filter(s=>s!==null);return f(t,"ul",{class:"acp-list acp-list--bullet","data-node-type":"bulletList"},n)}case"orderedList":{const n=Number.isInteger(e.start)&&Number(e.start)>=1?Number(e.start):1,s=k(i.content).map((o,l)=>q(o,g(r,"content",l),t)).filter(o=>o!==null);return f(t,"ol",{class:"acp-list acp-list--ordered","data-node-type":"orderedList",start:n},s)}case"codeBlock":{const n=typeof e.language=="string"&&e.language?e.language:void 0,s=k(i.content).filter(o=>p(o)&&o.type==="text"&&typeof o.text=="string").map(o=>o.text).join("");return f(t,"pre",{class:"acp-code-block","data-node-type":"codeBlock","data-language":n},[f(t,"code",{class:n?`language-${n}`:void 0},s)])}case"horizontalRule":return f(t,"hr",{class:"acp-horizontal-rule","data-node-type":"horizontalRule"});case"image":{const n=T(Q(e.src,t.imageBaseUrl),"image");if(!n)return b(t,{code:"UNSAFE_URL",path:g(r,"attrs","src"),message:"The image URL is empty, malformed, or uses a disallowed protocol.",nodeType:"image"}),null;const s=tt(e.imageAlign)?e.imageAlign:"center",o=Number.isInteger(e.width)&&Number(e.width)>=1&&Number(e.width)<=1e4?Number(e.width):void 0,l=Number.isInteger(e.height)&&Number(e.height)>=1&&Number(e.height)<=1e4?Number(e.height):void 0;return f(t,"div",{class:["acp-image",`acp-image--${s}`],"data-node-type":"image","data-image-align":s},[f(t,"img",{class:"acp-image__element",src:n,alt:typeof e.alt=="string"?e.alt:"",title:typeof e.title=="string"?e.title:void 0,width:o,height:l,"data-image-align":s})])}case"articleButton":return nt(i,r,t);case"table":{const n=k(i.content).map((s,o)=>it(s,g(r,"content",o),t)).filter(s=>s!==null);return f(t,"div",{class:"acp-table-wrapper","data-node-type":"table"},[f(t,"table",{class:"acp-table"},[f(t,"tbody",n)])])}}return null}function P(i,r,t){return k(i).map((e,n)=>$(e,g(r,n),t)).filter(e=>e!==null)}function st(i,r){const t=k(i.content),e=new Map;r.customSlots.forEach(s=>{var l;if(!Number.isInteger(s.location)||s.location<1||s.location>t.length)return;const o=(l=e.get(s.location))!=null?l:[];o.push(s),e.set(s.location,o)});const n=[];return t.forEach((s,o)=>{var u;const l=o+1;(u=e.get(l))==null||u.forEach(h=>n.push(...h.content));const c=$(s,g("/content",o),r);c&&typeof c!="string"&&n.push(c)}),n}function ot(i,r){return!p(i)||i.type!=="doc"?[]:st(i,r)}const at=new Set(["paragraph","heading","blockquote","bulletList","orderedList","codeBlock","horizontalRule","image","articleButton","table"]),lt=new Set(["left","center","right","justify"]),ct=new Set(["left","center","right"]),dt=new Set(["bold","italic","strike","underline","code"]);function I(i){return typeof i=="object"&&i!==null&&!Array.isArray(i)}function a(i,r){return`${i}/${String(r).replaceAll("~","~0").replaceAll("/","~1")}`}function d(i,...r){let t=i;for(const e of r)t=a(t,e);return t}class ut{constructor(){w(this,"issues",[])}validate(r){if(!I(r))return this.add("INVALID_ROOT","","The document must be an object."),this.result();this.checkProperties(r,"",["type","content"],["type","content"]),r.type!=="doc"&&this.add("INVALID_ROOT","/type",'The root node type must be "doc".');const t=this.requireArray(r.content,"/content");return t==null||t.forEach((e,n)=>this.validateBlock(e,a("/content",n))),this.result()}result(){return{valid:this.issues.length===0,issues:this.issues}}add(r,t,e,n){this.issues.push(m({code:r,path:t,message:e},n?{nodeType:n}:{}))}checkProperties(r,t,e,n=[],s){const o=new Set(e);for(const l of Object.keys(r))o.has(l)||this.add("UNKNOWN_PROPERTY",a(t,l),`Property "${l}" is not allowed.`,s);for(const l of n)l in r||this.add("MISSING_PROPERTY",a(t,l),`Required property "${l}" is missing.`,s)}requireArray(r,t,e){return Array.isArray(r)?r:(this.add("INVALID_TYPE",t,"Expected an array.",e),null)}optionalArray(r,t,e){return r===void 0?[]:this.requireArray(r,t,e)}requireNonEmptyArray(r,t,e){const n=this.requireArray(r,t,e);return n&&n.length===0&&this.add("INVALID_CONTENT",t,`Node "${e}" must contain at least one child.`,e),n}requireRecord(r,t,e){return I(r)?r:(this.add("INVALID_TYPE",t,"Expected an object.",e),null)}optionalRecord(r,t,e){return r===void 0?{}:this.requireRecord(r,t,e)}requireString(r,t,e,n={}){return typeof r!="string"?(this.add("INVALID_TYPE",t,"Expected a string.",e),!1):!n.allowEmpty&&r.length===0?(this.add("INVALID_VALUE",t,"The string must not be empty.",e),!1):n.maxLength!==void 0&&r.length>n.maxLength?(this.add("INVALID_VALUE",t,`The string must contain at most ${n.maxLength} characters.`,e),!1):!0}optionalString(r,t,e,n={}){r!==void 0&&this.requireString(r,t,e,n)}requireInteger(r,t,e,n,s){if(!Number.isInteger(r))return this.add("INVALID_TYPE",t,"Expected an integer.",e),!1;const o=r;return n!==void 0&&o<n?(this.add("INVALID_VALUE",t,`Value must be at least ${n}.`,e),!1):s!==void 0&&o>s?(this.add("INVALID_VALUE",t,`Value must be at most ${s}.`,e),!1):!0}validateBlock(r,t){if(!I(r)){this.add("INVALID_TYPE",t,"A block node must be an object.");return}const e=r.type;if(typeof e!="string"){this.add("MISSING_PROPERTY",a(t,"type"),"A block node requires a type.");return}if(!at.has(e)){this.add("UNKNOWN_NODE",a(t,"type"),`Unknown block node "${e}".`,e);return}switch(e){case"paragraph":this.validateParagraph(r,t);break;case"heading":this.validateHeading(r,t);break;case"blockquote":this.validateBlockquote(r,t);break;case"bulletList":this.validateBulletList(r,t);break;case"orderedList":this.validateOrderedList(r,t);break;case"codeBlock":this.validateCodeBlock(r,t);break;case"horizontalRule":this.checkProperties(r,t,["type"],["type"],e);break;case"image":this.validateImage(r,t);break;case"articleButton":this.validateArticleButton(r,t);break;case"table":this.validateTable(r,t);break}}validateParagraph(r,t){const e="paragraph";this.checkProperties(r,t,["type","attrs","content"],["type"],e),this.validateTextAlignAttrs(r.attrs,a(t,"attrs"),e,!1),this.validateInlineContent(r.content,a(t,"content"),e)}validateHeading(r,t){const e="heading";this.checkProperties(r,t,["type","attrs","content"],["type","attrs"],e);const n=this.requireRecord(r.attrs,a(t,"attrs"),e);n&&(this.checkProperties(n,a(t,"attrs"),["level","textAlign"],["level"],e),this.requireInteger(n.level,d(t,"attrs","level"),e,1,6),this.validateTextAlign(n.textAlign,d(t,"attrs","textAlign"),e)),this.validateInlineContent(r.content,a(t,"content"),e)}validateTextAlignAttrs(r,t,e,n){const s=n?this.requireRecord(r,t,e):this.optionalRecord(r,t,e);s&&(this.checkProperties(s,t,["textAlign"],[],e),this.validateTextAlign(s.textAlign,a(t,"textAlign"),e))}validateTextAlign(r,t,e){r!==void 0&&(typeof r!="string"?this.add("INVALID_TYPE",t,"Text alignment must be a string.",e):lt.has(r)||this.add("INVALID_VALUE",t,`Unsupported text alignment "${r}".`,e))}validateInlineContent(r,t,e){const n=this.optionalArray(r,t,e);n==null||n.forEach((s,o)=>this.validateText(s,a(t,o),!0))}validateText(r,t,e){if(!I(r)){this.add("INVALID_TYPE",t,"A text node must be an object.","text");return}const n=e?["type","text","marks"]:["type","text"];if(this.checkProperties(r,t,n,["type","text"],"text"),r.type!=="text"&&this.add("INVALID_CONTENT",a(t,"type"),"Expected a text node.","text"),this.requireString(r.text,a(t,"text"),"text"),e&&r.marks!==void 0){const s=this.requireArray(r.marks,a(t,"marks"),"text");s==null||s.forEach((o,l)=>this.validateMark(o,d(t,"marks",l)))}}validateMark(r,t){if(!I(r)){this.add("INVALID_TYPE",t,"A mark must be an object.");return}if(typeof r.type!="string"){this.add("MISSING_PROPERTY",a(t,"type"),"A mark requires a type.");return}if(dt.has(r.type)){this.checkProperties(r,t,["type"],["type"],r.type);return}if(r.type!=="link"){this.add("UNKNOWN_MARK",a(t,"type"),`Unknown mark "${r.type}".`,r.type);return}this.checkProperties(r,t,["type","attrs"],["type","attrs"],"link");const e=this.requireRecord(r.attrs,a(t,"attrs"),"link");e&&(this.checkProperties(e,a(t,"attrs"),["href","target"],["href"],"link"),this.requireString(e.href,d(t,"attrs","href"),"link"),e.target!==void 0&&e.target!=="_blank"&&e.target!=="_self"&&this.add("INVALID_VALUE",d(t,"attrs","target"),'Link target must be "_blank" or "_self".',"link"))}validateBlockquote(r,t){const e="blockquote";this.checkProperties(r,t,["type","content"],["type","content"],e);const n=this.requireNonEmptyArray(r.content,a(t,"content"),e);n==null||n.forEach((s,o)=>this.validateBlock(s,d(t,"content",o)))}validateBulletList(r,t){const e="bulletList";this.checkProperties(r,t,["type","content"],["type","content"],e);const n=this.requireNonEmptyArray(r.content,a(t,"content"),e);n==null||n.forEach((s,o)=>this.validateListItem(s,d(t,"content",o)))}validateOrderedList(r,t){const e="orderedList";this.checkProperties(r,t,["type","attrs","content"],["type","content"],e);const n=this.optionalRecord(r.attrs,a(t,"attrs"),e);n&&(this.checkProperties(n,a(t,"attrs"),["start"],[],e),n.start!==void 0&&this.requireInteger(n.start,d(t,"attrs","start"),e,1));const s=this.requireNonEmptyArray(r.content,a(t,"content"),e);s==null||s.forEach((o,l)=>this.validateListItem(o,d(t,"content",l)))}validateListItem(r,t){const e="listItem",n=this.requireRecord(r,t,e);if(!n)return;this.checkProperties(n,t,["type","content"],["type","content"],e),n.type!==e&&this.add("INVALID_CONTENT",a(t,"type"),"Expected a listItem node.",e);const s=this.requireNonEmptyArray(n.content,a(t,"content"),e);s==null||s.forEach((o,l)=>this.validateBlock(o,d(t,"content",l)))}validateCodeBlock(r,t){const e="codeBlock";this.checkProperties(r,t,["type","attrs","content"],["type"],e);const n=this.optionalRecord(r.attrs,a(t,"attrs"),e);n&&(this.checkProperties(n,a(t,"attrs"),["language"],[],e),this.optionalString(n.language,d(t,"attrs","language"),e,{maxLength:32}));const s=this.optionalArray(r.content,a(t,"content"),e);s&&(s.length>1&&this.add("INVALID_CONTENT",a(t,"content"),"A codeBlock may contain at most one text node.",e),s.forEach((o,l)=>this.validateText(o,d(t,"content",l),!1)))}validateImage(r,t){const e="image";this.checkProperties(r,t,["type","attrs"],["type","attrs"],e);const n=this.requireRecord(r.attrs,a(t,"attrs"),e);n&&(this.checkProperties(n,a(t,"attrs"),["src","alt","title","width","height","imageAlign"],["src"],e),this.requireString(n.src,d(t,"attrs","src"),e),this.optionalString(n.alt,d(t,"attrs","alt"),e,{allowEmpty:!0}),this.optionalString(n.title,d(t,"attrs","title"),e,{allowEmpty:!0}),n.width!==void 0&&this.requireInteger(n.width,d(t,"attrs","width"),e,1,1e4),n.height!==void 0&&this.requireInteger(n.height,d(t,"attrs","height"),e,1,1e4),n.imageAlign!==void 0&&(typeof n.imageAlign!="string"?this.add("INVALID_TYPE",d(t,"attrs","imageAlign"),"Image alignment must be a string.",e):ct.has(n.imageAlign)||this.add("INVALID_VALUE",d(t,"attrs","imageAlign"),`Unsupported image alignment "${n.imageAlign}".`,e)))}validateArticleButton(r,t){const e="articleButton";this.checkProperties(r,t,["type","attrs"],["type","attrs"],e);const n=this.requireRecord(r.attrs,a(t,"attrs"),e);n&&(this.checkProperties(n,a(t,"attrs"),["id","title","text","style","href"],["text","style"],e),n.style==="text"||n.style==="button"?"id"in n?this.requireString(n.id,d(t,"attrs","id"),e):this.add("MISSING_PROPERTY",d(t,"attrs","id"),'Required property "id" is missing for an articleButton with text or button style.',e):this.optionalString(n.id,d(t,"attrs","id"),e),this.optionalString(n.title,d(t,"attrs","title"),e),this.requireString(n.text,d(t,"attrs","text"),e),this.optionalString(n.href,d(t,"attrs","href"),e),n.style!=="text"&&n.style!=="button"&&n.style!=="link"&&this.add("INVALID_VALUE",d(t,"attrs","style"),'Article button style must be "text", "button", or "link".',e),n.href!==void 0&&n.style!=="link"&&this.add("INVALID_VALUE",d(t,"attrs","href"),'Article button href may only be present when style is "link".',e))}validateTable(r,t){const e="table";this.checkProperties(r,t,["type","content"],["type","content"],e);const n=this.requireNonEmptyArray(r.content,a(t,"content"),e);if(!n)return;let s=null;n.forEach((o,l)=>{const c=d(t,"content",l),u=this.validateTableRow(o,c);u!==null&&(s===null?s=u:u!==s&&this.add("TABLE_COLUMN_MISMATCH",a(c,"content"),`Expected ${s} table cells but received ${u}.`,"tableRow"))})}validateTableRow(r,t){var o;const e="tableRow",n=this.requireRecord(r,t,e);if(!n)return null;this.checkProperties(n,t,["type","content"],["type","content"],e),n.type!==e&&this.add("INVALID_CONTENT",a(t,"type"),"Expected a tableRow node.",e);const s=this.requireNonEmptyArray(n.content,a(t,"content"),e);return s==null||s.forEach((l,c)=>this.validateTableCell(l,d(t,"content",c))),(o=s==null?void 0:s.length)!=null?o:null}validateTableCell(r,t){const e="tableCell",n=this.requireRecord(r,t,e);if(!n)return;this.checkProperties(n,t,["type","content"],["type","content"],e),n.type!==e&&this.add("INVALID_CONTENT",a(t,"type"),"Expected a tableCell node.",e);const s=this.requireNonEmptyArray(n.content,a(t,"content"),e);s==null||s.forEach((o,l)=>this.validateBlock(o,d(t,"content",l)))}}function ft(i){return new ut().validate(i)}const D=Object.freeze({version:1,validate:ft,render:ot}),x=new Map([[D.version,D]]),ht=Object.freeze([...x.keys()]),S=1;function j(i){return x.get(i)}function z(i,r={}){var s;const t=(s=r.protocolVersion)!=null?s:S,e=j(t);return e?e.validate(i):{valid:!1,issues:[{code:"UNSUPPORTED_PROTOCOL",path:"",message:`Article Content Protocol version ${t} is not supported.`}]}}function gt(){return[]}function pt(i){return`${i.code}:${i.path}:${i.message}`}const yt=W.extend({name:"ArticleContentRenderer",props:{document:{type:null,required:!0},protocolVersion:{type:Number,default:S},strict:{type:Boolean,default:!1},customSlots:{type:Array,default:gt},imageBaseUrl:{type:String,default:N},resolveArticleButtonLink:{type:Function,default:void 0}},data(){return{reportedRuntimeIssues:new Set}},computed:{validation(){return z(this.document,{protocolVersion:this.protocolVersion})}},watch:{validation:{immediate:!0,deep:!0,handler(i){i.issues.forEach(r=>this.$emit("render-error",r))}},document:{deep:!0,handler(){this.reportedRuntimeIssues.clear()}},protocolVersion(){this.reportedRuntimeIssues.clear()},resolveArticleButtonLink(){this.reportedRuntimeIssues.clear()},imageBaseUrl(){this.reportedRuntimeIssues.clear()}},methods:{resolveCustomSlots(){return this.customSlots.map(i=>{var e,n,s,o;const r={id:i.id,location:i.location},t=(o=(s=(n=(e=this.$scopedSlots)[i.id])==null?void 0:n.call(e,r))!=null?s:this.$slots[i.id])!=null?o:[];return A(m({},i),{content:t})})},reportRuntimeIssue(i){const r=pt(i);this.reportedRuntimeIssues.has(r)||(this.reportedRuntimeIssues.add(r),this.$nextTick(()=>this.$emit("render-error",i)))}},render(i){const r=j(this.protocolVersion);if(!r||this.strict&&!this.validation.valid)return i("div",{class:"acp-render-error",attrs:{role:"alert","data-render-error":"true"}},"Invalid article content");const t=r.render(this.document,{createElement:i,customSlots:this.resolveCustomSlots(),imageBaseUrl:this.imageBaseUrl,resolveArticleButtonLink:this.resolveArticleButtonLink,emitArticleButtonClick:e=>this.$emit("article-button-click",e),reportIssue:this.reportRuntimeIssue});return i("div",{class:"acp-document",attrs:{"data-node-type":"doc","data-protocol-version":this.protocolVersion}},t)}});function mt(i,r,t,e,n,s,o,l){var c=typeof i=="function"?i.options:i;return{exports:i,options:c}}var kt=mt(yt);const O=kt.exports,bt=Object.freeze({fileFormat:"article-content-protocol",fileFormatVersion:1,mediaType:"application/vnd.article-content-protocol+json",id:"article-content-v1",version:1,name:"Article Content Protocol v1",status:"draft",rootNode:"doc",nodeTypes:Object.freeze(["doc","paragraph","heading","blockquote","bulletList","orderedList","listItem","codeBlock","horizontalRule","image","articleButton","table","tableRow","tableCell","text"]),markTypes:Object.freeze(["bold","italic","strike","underline","code","link"])}),At={install(i){i.component("ArticleContentRenderer",O)}};exports.ARTICLE_CONTENT_PROTOCOL_V1=bt;exports.ArticleContentRenderer=O;exports.ArticleContentRendererPlugin=At;exports.CURRENT_PROTOCOL_VERSION=S;exports.SUPPORTED_PROTOCOL_VERSIONS=ht;exports.default=O;exports.validateArticleDocument=z;
|
|
1
|
+
"use strict";var Z=Object.defineProperty,tt=Object.defineProperties;var et=Object.getOwnPropertyDescriptors;var j=Object.getOwnPropertySymbols;var rt=Object.prototype.hasOwnProperty,it=Object.prototype.propertyIsEnumerable;var O=(i,r,t)=>r in i?Z(i,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[r]=t,k=(i,r)=>{for(var t in r||(r={}))rt.call(r,t)&&O(i,t,r[t]);if(j)for(var t of j(r))it.call(r,t)&&O(i,t,r[t]);return i},E=(i,r)=>tt(i,et(r));var x=(i,r,t)=>O(i,typeof r!="symbol"?r+"":r,t);Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const G=require("vue"),nt=new Set(["http:","https:","mailto:","tel:"]),st=new Set(["http:","https:","blob:"]),ot="https://article-content-renderer.invalid/",_="https://www.doitme.link/";function at(i,r){if(typeof i!="string")return i;const t=i.trim();if(!t.startsWith(_))return t;const n=(r.trim()||_).replace(/\/+$/u,""),s=t.slice(_.length).replace(/^\/+/,"");return`${n}/${s}`}function V(i,r){if(typeof i!="string")return null;const t=i.trim();if(!t||/[\u0000-\u001F\u007F]/u.test(t))return null;try{const e=new URL(t,ot);return(r==="link"?nt:st).has(e.protocol)?t:null}catch(e){return null}}function P(i,r){const t=new Set(typeof i=="string"?i.trim().split(/\s+/u).filter(Boolean):[]);return r==="_blank"&&(t.add("noopener"),t.add("noreferrer")),t.size>0?[...t].join(" "):void 0}const lt=new Set(["paragraph","heading","blockquote","bulletList","orderedList","codeBlock","horizontalRule","image","articleButton","table"]);function y(i){return typeof i=="object"&&i!==null&&!Array.isArray(i)}function w(i){return y(i)?i:{}}function p(i,r,t,e){var c;const n=y(t)?t:{},s=y(t)?e:t,o={},a={};for(const[u,g]of Object.entries(n))if(u==="class")o.class=g;else if(u==="style")o.style=g;else if(u.startsWith("on")&&typeof g=="function"){const f=u.slice(2).toLowerCase();o.on=E(k({},(c=o.on)!=null?c:{}),{[f]:g})}else g!==void 0&&g!==!1&&(a[u]=g);return Object.keys(a).length>0&&(o.attrs=a),i.createElement(r,o,s)}function I(i){return Array.isArray(i)?i:[]}function h(i,...r){return`${i}/${r.map(String).join("/")}`}function M(i){return i==="left"||i==="center"||i==="right"||i==="justify"}function ct(i){return i==="left"||i==="center"||i==="right"}function S(i){return i==="_blank"||i==="_self"}function L(i,r){i.reportIssue(r)}function dt(i,r,t){var s,o,a;if(!y(i)||i.type!=="text"||typeof i.text!="string"||!i.text)return null;let e=i.text;const n=I(i.marks);for(let c=0;c<n.length;c+=1){const u=n[c];if(!y(u)||typeof u.type!="string")continue;const g=h(r,"marks",c);switch(u.type){case"bold":e=p(t,"strong",{class:"acp-mark acp-mark--bold"},[e]);break;case"italic":e=p(t,"em",{class:"acp-mark acp-mark--italic"},[e]);break;case"strike":e=p(t,"s",{class:"acp-mark acp-mark--strike"},[e]);break;case"underline":e=p(t,"u",{class:"acp-mark acp-mark--underline"},[e]);break;case"code":e=p(t,"code",{class:"acp-mark acp-mark--code"},[e]);break;case"link":{const f=w(u.attrs),b=S(f.target)?f.target:"_blank";if(f.type==="custom"){if(typeof f.id!="string"||!f.id||typeof f.title!="string"||!f.title)break;const T=Object.freeze(k({type:"custom",id:f.id,title:f.title},S(f.target)?{target:f.target}:{})),Q=Object.freeze({type:"link",attrs:T});let m=null;if(!t.resolveCustomLink)L(t,{code:"LINK_RESOLUTION_FAILED",path:g,message:"No resolveCustomLink callback was provided for a custom link mark.",nodeType:"link"});else try{const A=t.resolveCustomLink(T,Q);m=B(A,b),m||L(t,{code:typeof A=="string"||y(A)&&typeof A.href=="string"?"UNSAFE_URL":"LINK_RESOLUTION_FAILED",path:g,message:"The custom link resolver returned no usable safe URL.",nodeType:"link"})}catch(A){L(t,{code:"LINK_RESOLUTION_FAILED",path:g,message:`The custom link resolver threw an error: ${A instanceof Error?A.message:String(A)}`,nodeType:"link"})}const N=(s=m==null?void 0:m.href)!=null?s:null,$=(o=m==null?void 0:m.target)!=null?o:b;e=p(t,"a",{class:`acp-link acp-link--custom${N?"":" acp-link--disabled"}`,href:N!=null?N:void 0,target:$,rel:(a=m==null?void 0:m.rel)!=null?a:P(void 0,$),title:T.title,"data-link-type":"custom","data-link-id":T.id,"aria-disabled":N?void 0:"true",onClick:N?void 0:A=>A.preventDefault()},[e]);break}if(f.type!==void 0&&f.type!=="href")break;const D=V(f.href,"link");if(!D){L(t,{code:"UNSAFE_URL",path:h(g,"attrs","href"),message:"The link URL is empty, malformed, or uses a disallowed protocol.",nodeType:"link"});break}e=p(t,"a",{class:"acp-link",href:D,target:b,rel:P(void 0,b),"data-link-type":"href"},[e]);break}}}return e}function z(i,r,t){return I(i).map((e,n)=>dt(e,h(r,n),t)).filter(e=>e!==null)}function F(i,r,t){return!y(i)||i.type!=="listItem"?null:p(t,"li",{class:"acp-list-item","data-node-type":"listItem"},U(i.content,h(r,"content"),t))}function ut(i,r,t){return!y(i)||i.type!=="tableCell"?null:p(t,"td",{class:"acp-table-cell","data-node-type":"tableCell"},U(i.content,h(r,"content"),t))}function ft(i,r,t){if(!y(i)||i.type!=="tableRow")return null;const e=I(i.content).map((n,s)=>ut(n,h(r,"content",s),t)).filter(n=>n!==null);return p(t,"tr",{class:"acp-table-row","data-node-type":"tableRow"},e)}function B(i,r="_self"){var s;if(y(i)&&i.target!==void 0&&!S(i.target)||y(i)&&i.rel!==void 0&&typeof i.rel!="string")return null;const t=typeof i=="string"?{href:i}:y(i)&&typeof i.href=="string"?k(k({href:i.href},S(i.target)?{target:i.target}:{}),typeof i.rel=="string"?{rel:i.rel}:{}):null;if(!t)return null;const e=V(t.href,"link");if(!e)return null;const n=(s=t.target)!=null?s:r;return{href:e,target:n,rel:P(t.rel,n)}}function pt(i,r,t){var g;const e=w(i.attrs),n=e.style==="text"||e.style==="button",s=e.style==="link";if(typeof e.text!="string"||!e.text||!n&&!s||n&&(typeof e.id!="string"||!e.id)||s&&e.id!==void 0&&(typeof e.id!="string"||!e.id))return null;const o=Object.freeze(s?k(E(k(k({},typeof e.id=="string"?{id:e.id}:{}),typeof e.title=="string"?{title:e.title}:{}),{text:e.text,style:"link"}),typeof e.href=="string"?{href:e.href}:{}):E(k({id:e.id},typeof e.title=="string"?{title:e.title}:{}),{text:e.text,style:e.style})),a=Object.freeze({type:"articleButton",attrs:o});let c=null;if(o.style==="link")o.href!==void 0&&(c=B(o.href),c||L(t,{code:"UNSAFE_URL",path:`${r}/attrs/href`,message:"The articleButton link href is not a usable safe URL.",nodeType:"articleButton"}));else if(!t.resolveArticleButtonLink)L(t,{code:"LINK_RESOLUTION_FAILED",path:r,message:"No resolveArticleButtonLink callback was provided for an articleButton node.",nodeType:"articleButton"});else try{const f=Object.freeze({type:"articleButton",attrs:o}),b=t.resolveArticleButtonLink(o,f);c=B(b),c||L(t,{code:typeof b=="string"||y(b)&&typeof b.href=="string"?"UNSAFE_URL":"LINK_RESOLUTION_FAILED",path:r,message:"The articleButton link resolver returned no usable safe URL.",nodeType:"articleButton"})}catch(f){L(t,{code:"LINK_RESOLUTION_FAILED",path:r,message:`The articleButton link resolver threw an error: ${f instanceof Error?f.message:String(f)}`,nodeType:"articleButton"})}const u=(g=c==null?void 0:c.href)!=null?g:null;return p(t,"a",{class:["acp-article-button",`acp-article-button--${o.style}`,!u&&"acp-article-button--disabled"],href:u!=null?u:void 0,target:c==null?void 0:c.target,rel:c==null?void 0:c.rel,title:o.title,"data-node-type":"articleButton","data-article-button-id":o.id,"data-article-button-style":o.style,"aria-disabled":u?void 0:"true",onClick:f=>{u||f.preventDefault(),t.emitArticleButtonClick({attrs:o,node:a,href:u,event:f})}},o.text)}function W(i,r,t){if(!y(i)||typeof i.type!="string"||!lt.has(i.type))return null;const e=w(i.attrs);switch(i.type){case"paragraph":{const n=M(e.textAlign)?e.textAlign:void 0;return p(t,"p",{class:"acp-paragraph","data-node-type":"paragraph",style:n?{textAlign:n}:void 0},z(i.content,h(r,"content"),t))}case"heading":{const n=Number.isInteger(e.level)&&Number(e.level)>=1&&Number(e.level)<=6?Number(e.level):1,s=M(e.textAlign)?e.textAlign:void 0;return p(t,`h${n}`,{class:["acp-heading",`acp-heading--${n}`],"data-node-type":"heading",style:s?{textAlign:s}:void 0},z(i.content,h(r,"content"),t))}case"blockquote":return p(t,"blockquote",{class:"acp-blockquote","data-node-type":"blockquote"},U(i.content,h(r,"content"),t));case"bulletList":{const n=I(i.content).map((s,o)=>F(s,h(r,"content",o),t)).filter(s=>s!==null);return p(t,"ul",{class:"acp-list acp-list--bullet","data-node-type":"bulletList"},n)}case"orderedList":{const n=Number.isInteger(e.start)&&Number(e.start)>=1?Number(e.start):1,s=I(i.content).map((o,a)=>F(o,h(r,"content",a),t)).filter(o=>o!==null);return p(t,"ol",{class:"acp-list acp-list--ordered","data-node-type":"orderedList",start:n},s)}case"codeBlock":{const n=typeof e.language=="string"&&e.language?e.language:void 0,s=I(i.content).filter(o=>y(o)&&o.type==="text"&&typeof o.text=="string").map(o=>o.text).join("");return p(t,"pre",{class:"acp-code-block","data-node-type":"codeBlock","data-language":n},[p(t,"code",{class:n?`language-${n}`:void 0},s)])}case"horizontalRule":return p(t,"hr",{class:"acp-horizontal-rule","data-node-type":"horizontalRule"});case"image":{const n=V(at(e.src,t.imageBaseUrl),"image");if(!n)return L(t,{code:"UNSAFE_URL",path:h(r,"attrs","src"),message:"The image URL is empty, malformed, or uses a disallowed protocol.",nodeType:"image"}),null;const s=ct(e.imageAlign)?e.imageAlign:"center",o=Number.isInteger(e.width)&&Number(e.width)>=1&&Number(e.width)<=1e4?Number(e.width):void 0,a=Number.isInteger(e.height)&&Number(e.height)>=1&&Number(e.height)<=1e4?Number(e.height):void 0;return p(t,"div",{class:["acp-image",`acp-image--${s}`],"data-node-type":"image","data-image-align":s},[p(t,"img",{class:"acp-image__element",src:n,alt:typeof e.alt=="string"?e.alt:"",title:typeof e.title=="string"?e.title:void 0,width:o,height:a,"data-image-align":s})])}case"articleButton":return pt(i,r,t);case"table":{const n=I(i.content).map((s,o)=>ft(s,h(r,"content",o),t)).filter(s=>s!==null);return p(t,"div",{class:"acp-table-wrapper","data-node-type":"table"},[p(t,"table",{class:"acp-table"},[p(t,"tbody",n)])])}}return null}function U(i,r,t){return I(i).map((e,n)=>W(e,h(r,n),t)).filter(e=>e!==null)}function gt(i,r){const t=I(i.content),e=new Map;r.customSlots.forEach(s=>{var a;if(!Number.isInteger(s.location)||s.location<1||s.location>t.length)return;const o=(a=e.get(s.location))!=null?a:[];o.push(s),e.set(s.location,o)});const n=[];return t.forEach((s,o)=>{var u;const a=o+1;(u=e.get(a))==null||u.forEach(g=>n.push(...g.content));const c=W(s,h("/content",o),r);c&&typeof c!="string"&&n.push(c)}),n}function yt(i,r){return!y(i)||i.type!=="doc"?[]:gt(i,r)}const ht=new Set(["paragraph","heading","blockquote","bulletList","orderedList","codeBlock","horizontalRule","image","articleButton","table"]),mt=new Set(["left","center","right","justify"]),kt=new Set(["left","center","right"]),At=new Set(["bold","italic","strike","underline","code"]);function R(i){return typeof i=="object"&&i!==null&&!Array.isArray(i)}function l(i,r){return`${i}/${String(r).replaceAll("~","~0").replaceAll("/","~1")}`}function d(i,...r){let t=i;for(const e of r)t=l(t,e);return t}class bt{constructor(){x(this,"issues",[])}validate(r){if(!R(r))return this.add("INVALID_ROOT","","The document must be an object."),this.result();this.checkProperties(r,"",["type","content"],["type","content"]),r.type!=="doc"&&this.add("INVALID_ROOT","/type",'The root node type must be "doc".');const t=this.requireArray(r.content,"/content");return t==null||t.forEach((e,n)=>this.validateBlock(e,l("/content",n))),this.result()}result(){return{valid:this.issues.length===0,issues:this.issues}}add(r,t,e,n){this.issues.push(k({code:r,path:t,message:e},n?{nodeType:n}:{}))}checkProperties(r,t,e,n=[],s){const o=new Set(e);for(const a of Object.keys(r))o.has(a)||this.add("UNKNOWN_PROPERTY",l(t,a),`Property "${a}" is not allowed.`,s);for(const a of n)a in r||this.add("MISSING_PROPERTY",l(t,a),`Required property "${a}" is missing.`,s)}requireArray(r,t,e){return Array.isArray(r)?r:(this.add("INVALID_TYPE",t,"Expected an array.",e),null)}optionalArray(r,t,e){return r===void 0?[]:this.requireArray(r,t,e)}requireNonEmptyArray(r,t,e){const n=this.requireArray(r,t,e);return n&&n.length===0&&this.add("INVALID_CONTENT",t,`Node "${e}" must contain at least one child.`,e),n}requireRecord(r,t,e){return R(r)?r:(this.add("INVALID_TYPE",t,"Expected an object.",e),null)}optionalRecord(r,t,e){return r===void 0?{}:this.requireRecord(r,t,e)}requireString(r,t,e,n={}){return typeof r!="string"?(this.add("INVALID_TYPE",t,"Expected a string.",e),!1):!n.allowEmpty&&r.length===0?(this.add("INVALID_VALUE",t,"The string must not be empty.",e),!1):n.maxLength!==void 0&&r.length>n.maxLength?(this.add("INVALID_VALUE",t,`The string must contain at most ${n.maxLength} characters.`,e),!1):!0}optionalString(r,t,e,n={}){r!==void 0&&this.requireString(r,t,e,n)}requireInteger(r,t,e,n,s){if(!Number.isInteger(r))return this.add("INVALID_TYPE",t,"Expected an integer.",e),!1;const o=r;return n!==void 0&&o<n?(this.add("INVALID_VALUE",t,`Value must be at least ${n}.`,e),!1):s!==void 0&&o>s?(this.add("INVALID_VALUE",t,`Value must be at most ${s}.`,e),!1):!0}validateBlock(r,t){if(!R(r)){this.add("INVALID_TYPE",t,"A block node must be an object.");return}const e=r.type;if(typeof e!="string"){this.add("MISSING_PROPERTY",l(t,"type"),"A block node requires a type.");return}if(!ht.has(e)){this.add("UNKNOWN_NODE",l(t,"type"),`Unknown block node "${e}".`,e);return}switch(e){case"paragraph":this.validateParagraph(r,t);break;case"heading":this.validateHeading(r,t);break;case"blockquote":this.validateBlockquote(r,t);break;case"bulletList":this.validateBulletList(r,t);break;case"orderedList":this.validateOrderedList(r,t);break;case"codeBlock":this.validateCodeBlock(r,t);break;case"horizontalRule":this.checkProperties(r,t,["type"],["type"],e);break;case"image":this.validateImage(r,t);break;case"articleButton":this.validateArticleButton(r,t);break;case"table":this.validateTable(r,t);break}}validateParagraph(r,t){const e="paragraph";this.checkProperties(r,t,["type","attrs","content"],["type"],e),this.validateTextAlignAttrs(r.attrs,l(t,"attrs"),e,!1),this.validateInlineContent(r.content,l(t,"content"),e)}validateHeading(r,t){const e="heading";this.checkProperties(r,t,["type","attrs","content"],["type","attrs"],e);const n=this.requireRecord(r.attrs,l(t,"attrs"),e);n&&(this.checkProperties(n,l(t,"attrs"),["level","textAlign"],["level"],e),this.requireInteger(n.level,d(t,"attrs","level"),e,1,6),this.validateTextAlign(n.textAlign,d(t,"attrs","textAlign"),e)),this.validateInlineContent(r.content,l(t,"content"),e)}validateTextAlignAttrs(r,t,e,n){const s=n?this.requireRecord(r,t,e):this.optionalRecord(r,t,e);s&&(this.checkProperties(s,t,["textAlign"],[],e),this.validateTextAlign(s.textAlign,l(t,"textAlign"),e))}validateTextAlign(r,t,e){r!==void 0&&(typeof r!="string"?this.add("INVALID_TYPE",t,"Text alignment must be a string.",e):mt.has(r)||this.add("INVALID_VALUE",t,`Unsupported text alignment "${r}".`,e))}validateInlineContent(r,t,e){const n=this.optionalArray(r,t,e);n==null||n.forEach((s,o)=>this.validateText(s,l(t,o),!0))}validateText(r,t,e){if(!R(r)){this.add("INVALID_TYPE",t,"A text node must be an object.","text");return}const n=e?["type","text","marks"]:["type","text"];if(this.checkProperties(r,t,n,["type","text"],"text"),r.type!=="text"&&this.add("INVALID_CONTENT",l(t,"type"),"Expected a text node.","text"),this.requireString(r.text,l(t,"text"),"text"),e&&r.marks!==void 0){const s=this.requireArray(r.marks,l(t,"marks"),"text");s==null||s.forEach((o,a)=>this.validateMark(o,d(t,"marks",a)))}}validateMark(r,t){var s;if(!R(r)){this.add("INVALID_TYPE",t,"A mark must be an object.");return}if(typeof r.type!="string"){this.add("MISSING_PROPERTY",l(t,"type"),"A mark requires a type.");return}if(At.has(r.type)){this.checkProperties(r,t,["type"],["type"],r.type);return}if(r.type!=="link"){this.add("UNKNOWN_MARK",l(t,"type"),`Unknown mark "${r.type}".`,r.type);return}this.checkProperties(r,t,["type","attrs"],["type","attrs"],"link");const e=this.requireRecord(r.attrs,l(t,"attrs"),"link");if(!e)return;this.checkProperties(e,l(t,"attrs"),["type","href","id","title","target"],[],"link"),e.type!==void 0&&e.type!=="href"&&e.type!=="custom"&&this.add("INVALID_VALUE",d(t,"attrs","type"),'Link type must be "href" or "custom".',"link");const n=(s=e.type)!=null?s:"href";if(n==="custom"){for(const o of["id","title"])o in e?this.requireString(e[o],d(t,"attrs",o),"link"):this.add("MISSING_PROPERTY",d(t,"attrs",o),`Required property "${o}" is missing for a custom link.`,"link");e.href!==void 0&&this.add("INVALID_VALUE",d(t,"attrs","href"),"A custom link must not contain href.","link")}else if(n==="href"){"href"in e?this.requireString(e.href,d(t,"attrs","href"),"link"):this.add("MISSING_PROPERTY",d(t,"attrs","href"),'Required property "href" is missing for an href link.',"link");for(const o of["id","title"])e[o]!==void 0&&this.add("INVALID_VALUE",d(t,"attrs",o),`An href link must not contain ${o}.`,"link")}e.target!==void 0&&e.target!=="_blank"&&e.target!=="_self"&&this.add("INVALID_VALUE",d(t,"attrs","target"),'Link target must be "_blank" or "_self".',"link")}validateBlockquote(r,t){const e="blockquote";this.checkProperties(r,t,["type","content"],["type","content"],e);const n=this.requireNonEmptyArray(r.content,l(t,"content"),e);n==null||n.forEach((s,o)=>this.validateBlock(s,d(t,"content",o)))}validateBulletList(r,t){const e="bulletList";this.checkProperties(r,t,["type","content"],["type","content"],e);const n=this.requireNonEmptyArray(r.content,l(t,"content"),e);n==null||n.forEach((s,o)=>this.validateListItem(s,d(t,"content",o)))}validateOrderedList(r,t){const e="orderedList";this.checkProperties(r,t,["type","attrs","content"],["type","content"],e);const n=this.optionalRecord(r.attrs,l(t,"attrs"),e);n&&(this.checkProperties(n,l(t,"attrs"),["start"],[],e),n.start!==void 0&&this.requireInteger(n.start,d(t,"attrs","start"),e,1));const s=this.requireNonEmptyArray(r.content,l(t,"content"),e);s==null||s.forEach((o,a)=>this.validateListItem(o,d(t,"content",a)))}validateListItem(r,t){const e="listItem",n=this.requireRecord(r,t,e);if(!n)return;this.checkProperties(n,t,["type","content"],["type","content"],e),n.type!==e&&this.add("INVALID_CONTENT",l(t,"type"),"Expected a listItem node.",e);const s=this.requireNonEmptyArray(n.content,l(t,"content"),e);s==null||s.forEach((o,a)=>this.validateBlock(o,d(t,"content",a)))}validateCodeBlock(r,t){const e="codeBlock";this.checkProperties(r,t,["type","attrs","content"],["type"],e);const n=this.optionalRecord(r.attrs,l(t,"attrs"),e);n&&(this.checkProperties(n,l(t,"attrs"),["language"],[],e),this.optionalString(n.language,d(t,"attrs","language"),e,{maxLength:32}));const s=this.optionalArray(r.content,l(t,"content"),e);s&&(s.length>1&&this.add("INVALID_CONTENT",l(t,"content"),"A codeBlock may contain at most one text node.",e),s.forEach((o,a)=>this.validateText(o,d(t,"content",a),!1)))}validateImage(r,t){const e="image";this.checkProperties(r,t,["type","attrs"],["type","attrs"],e);const n=this.requireRecord(r.attrs,l(t,"attrs"),e);n&&(this.checkProperties(n,l(t,"attrs"),["src","alt","title","width","height","imageAlign"],["src"],e),this.requireString(n.src,d(t,"attrs","src"),e),this.optionalString(n.alt,d(t,"attrs","alt"),e,{allowEmpty:!0}),this.optionalString(n.title,d(t,"attrs","title"),e,{allowEmpty:!0}),n.width!==void 0&&this.requireInteger(n.width,d(t,"attrs","width"),e,1,1e4),n.height!==void 0&&this.requireInteger(n.height,d(t,"attrs","height"),e,1,1e4),n.imageAlign!==void 0&&(typeof n.imageAlign!="string"?this.add("INVALID_TYPE",d(t,"attrs","imageAlign"),"Image alignment must be a string.",e):kt.has(n.imageAlign)||this.add("INVALID_VALUE",d(t,"attrs","imageAlign"),`Unsupported image alignment "${n.imageAlign}".`,e)))}validateArticleButton(r,t){const e="articleButton";this.checkProperties(r,t,["type","attrs"],["type","attrs"],e);const n=this.requireRecord(r.attrs,l(t,"attrs"),e);n&&(this.checkProperties(n,l(t,"attrs"),["id","title","text","style","href"],["text","style"],e),n.style==="text"||n.style==="button"?"id"in n?this.requireString(n.id,d(t,"attrs","id"),e):this.add("MISSING_PROPERTY",d(t,"attrs","id"),'Required property "id" is missing for an articleButton with text or button style.',e):this.optionalString(n.id,d(t,"attrs","id"),e),this.optionalString(n.title,d(t,"attrs","title"),e),this.requireString(n.text,d(t,"attrs","text"),e),this.optionalString(n.href,d(t,"attrs","href"),e),n.style!=="text"&&n.style!=="button"&&n.style!=="link"&&this.add("INVALID_VALUE",d(t,"attrs","style"),'Article button style must be "text", "button", or "link".',e),n.href!==void 0&&n.style!=="link"&&this.add("INVALID_VALUE",d(t,"attrs","href"),'Article button href may only be present when style is "link".',e))}validateTable(r,t){const e="table";this.checkProperties(r,t,["type","content"],["type","content"],e);const n=this.requireNonEmptyArray(r.content,l(t,"content"),e);if(!n)return;let s=null;n.forEach((o,a)=>{const c=d(t,"content",a),u=this.validateTableRow(o,c);u!==null&&(s===null?s=u:u!==s&&this.add("TABLE_COLUMN_MISMATCH",l(c,"content"),`Expected ${s} table cells but received ${u}.`,"tableRow"))})}validateTableRow(r,t){var o;const e="tableRow",n=this.requireRecord(r,t,e);if(!n)return null;this.checkProperties(n,t,["type","content"],["type","content"],e),n.type!==e&&this.add("INVALID_CONTENT",l(t,"type"),"Expected a tableRow node.",e);const s=this.requireNonEmptyArray(n.content,l(t,"content"),e);return s==null||s.forEach((a,c)=>this.validateTableCell(a,d(t,"content",c))),(o=s==null?void 0:s.length)!=null?o:null}validateTableCell(r,t){const e="tableCell",n=this.requireRecord(r,t,e);if(!n)return;this.checkProperties(n,t,["type","content"],["type","content"],e),n.type!==e&&this.add("INVALID_CONTENT",l(t,"type"),"Expected a tableCell node.",e);const s=this.requireNonEmptyArray(n.content,l(t,"content"),e);s==null||s.forEach((o,a)=>this.validateBlock(o,d(t,"content",a)))}}function It(i){return new bt().validate(i)}const Y=Object.freeze({version:1,validate:It,render:yt}),H=new Map([[Y.version,Y]]),Lt=Object.freeze([...H.keys()]),q=1;function X(i){return H.get(i)}function J(i,r={}){var s;const t=(s=r.protocolVersion)!=null?s:q,e=X(t);return e?e.validate(i):{valid:!1,issues:[{code:"UNSUPPORTED_PROTOCOL",path:"",message:`Article Content Protocol version ${t} is not supported.`}]}}function Nt(){return[]}function C(i){return`${i.code}:${i.path}:${i.message}`}function Et(i){const r=i.slots();return i.props.customSlots.map(t=>{var s,o,a,c;const e={id:t.id,location:t.location},n=(c=(a=(o=(s=i.scopedSlots)[t.id])==null?void 0:o.call(s,e))!=null?a:r[t.id])!=null?c:[];return E(k({},t),{content:n})})}function Rt(i,r){Array.isArray(i)?i.forEach(t=>t(r)):i==null||i(r)}const Tt=G.extend({name:"ArticleContentRenderIssueReporter",props:{issues:{type:Array,required:!0}},data(){return{reportedFingerprint:""}},watch:{issues:{immediate:!0,deep:!0,handler(i){const r=i.map(C).join("|");r!==this.reportedFingerprint&&(this.reportedFingerprint=r,this.$nextTick(()=>i.forEach(t=>this.$emit("render-error",t))))}}},render(i){var r,t;return(t=(r=this.$slots.default)==null?void 0:r[0])!=null?t:i()}});function K(i,r,t,e){var a;const n=(a=t[0])!=null?a:i(),s=r.listeners["render-error"],o=i(Tt,{props:{issues:e},on:s?{"render-error":s}:void 0},[n]);return t.length>1?[o,...t.slice(1)]:o}const _t={name:"ArticleContentRenderer",functional:!0,props:{document:{type:null,required:!0},protocolVersion:{type:Number,default:q},strict:{type:Boolean,default:!1},customSlots:{type:Array,default:Nt},imageBaseUrl:{type:String,default:_},resolveArticleButtonLink:{type:Function,default:void 0},resolveCustomLink:{type:Function,default:void 0}},render(i,r){const{props:t}=r,e=J(t.document,{protocolVersion:t.protocolVersion}),n=X(t.protocolVersion),s=[];if(!n||t.strict&&!e.valid){const a=i("div",{class:"acp-render-error",attrs:{role:"alert","data-render-error":"true"}},"Invalid article content");return K(i,r,[a],e.issues)}const o=n.render(t.document,{createElement:i,customSlots:Et(r),imageBaseUrl:t.imageBaseUrl,resolveArticleButtonLink:t.resolveArticleButtonLink,resolveCustomLink:t.resolveCustomLink,emitArticleButtonClick:a=>Rt(r.listeners["article-button-click"],a),reportIssue:a=>s.push(a)});return K(i,r,o,[...e.issues,...s.filter((a,c,u)=>u.findIndex(g=>C(g)===C(a))===c)])}},St=G.extend(_t);function Ot(i,r,t,e,n,s,o,a){var c=typeof i=="function"?i.options:i;return{exports:i,options:c}}var Pt=Ot(St);const v=Pt.exports,Bt=Object.freeze({fileFormat:"article-content-protocol",fileFormatVersion:1,mediaType:"application/vnd.article-content-protocol+json",id:"article-content-v1",version:1,name:"Article Content Protocol v1",status:"draft",rootNode:"doc",nodeTypes:Object.freeze(["doc","paragraph","heading","blockquote","bulletList","orderedList","listItem","codeBlock","horizontalRule","image","articleButton","table","tableRow","tableCell","text"]),markTypes:Object.freeze(["bold","italic","strike","underline","code","link"])}),Ct={install(i){i.component("ArticleContentRenderer",v)}};exports.ARTICLE_CONTENT_PROTOCOL_V1=Bt;exports.ArticleContentRenderer=v;exports.ArticleContentRendererPlugin=Ct;exports.CURRENT_PROTOCOL_VERSION=q;exports.SUPPORTED_PROTOCOL_VERSIONS=Lt;exports.default=v;exports.validateArticleDocument=J;
|