@elastic/isomer-sdk 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/dist/cjs/composition/error.js +1 -0
- package/dist/cjs/composition/error.js.map +1 -1
- package/dist/cjs/define/index.js.map +1 -1
- package/dist/cjs/define/primitive_module.js.map +1 -1
- package/dist/cjs/define/slack_blocks.js.map +1 -1
- package/dist/cjs/entries/index.js +3 -1
- package/dist/cjs/entries/index.js.map +1 -1
- package/dist/cjs/entries/markdown.js +2 -1
- package/dist/cjs/entries/markdown.js.map +1 -1
- package/dist/cjs/entries/react.js +2 -1
- package/dist/cjs/entries/react.js.map +1 -1
- package/dist/cjs/entries/slack.js +5 -1
- package/dist/cjs/entries/slack.js.map +1 -1
- package/dist/cjs/pack/enhancements.js.map +1 -1
- package/dist/cjs/render/markdown/blocks.js +82 -0
- package/dist/cjs/render/markdown/blocks.js.map +1 -0
- package/dist/cjs/render/markdown/index.js +3 -1
- package/dist/cjs/render/markdown/index.js.map +1 -1
- package/dist/cjs/render/primitive_dispatch.js +36 -6
- package/dist/cjs/render/primitive_dispatch.js.map +1 -1
- package/dist/cjs/render/react/index.js +3 -1
- package/dist/cjs/render/react/index.js.map +1 -1
- package/dist/cjs/render/react/markdown_content.js +143 -0
- package/dist/cjs/render/react/markdown_content.js.map +1 -0
- package/dist/cjs/render/slack/blocks.js +7 -0
- package/dist/cjs/render/slack/blocks.js.map +1 -1
- package/dist/cjs/render/slack/container.js +62 -0
- package/dist/cjs/render/slack/container.js.map +1 -0
- package/dist/cjs/render/slack/envelope.js +70 -14
- package/dist/cjs/render/slack/envelope.js.map +1 -1
- package/dist/cjs/render/slack/format.js +56 -6
- package/dist/cjs/render/slack/format.js.map +1 -1
- package/dist/cjs/render/slack/index.js +7 -1
- package/dist/cjs/render/slack/index.js.map +1 -1
- package/dist/cjs/render/slack/markdown_content.js +29 -76
- package/dist/cjs/render/slack/markdown_content.js.map +1 -1
- package/dist/cjs/render/slack/unmodeled.js +38 -0
- package/dist/cjs/render/slack/unmodeled.js.map +1 -0
- package/dist/cjs/testing/conformance.js +15 -1
- package/dist/cjs/testing/conformance.js.map +1 -1
- package/dist/cjs/testing/index.js +2 -1
- package/dist/cjs/testing/index.js.map +1 -1
- package/dist/composition/error.d.ts +1 -0
- package/dist/composition/error.d.ts.map +1 -1
- package/dist/composition/error.js +1 -0
- package/dist/composition/error.js.map +1 -1
- package/dist/define/index.d.ts +1 -1
- package/dist/define/index.d.ts.map +1 -1
- package/dist/define/index.js.map +1 -1
- package/dist/define/primitive_module.d.ts +7 -5
- package/dist/define/primitive_module.d.ts.map +1 -1
- package/dist/define/primitive_module.js.map +1 -1
- package/dist/define/slack_blocks.d.ts +31 -1
- package/dist/define/slack_blocks.d.ts.map +1 -1
- package/dist/define/slack_blocks.js.map +1 -1
- package/dist/entries/index.d.ts +1 -1
- package/dist/entries/index.d.ts.map +1 -1
- package/dist/entries/index.js +1 -1
- package/dist/entries/index.js.map +1 -1
- package/dist/entries/markdown.d.ts +1 -1
- package/dist/entries/markdown.d.ts.map +1 -1
- package/dist/entries/markdown.js +1 -1
- package/dist/entries/markdown.js.map +1 -1
- package/dist/entries/react.d.ts +1 -1
- package/dist/entries/react.d.ts.map +1 -1
- package/dist/entries/react.js +1 -1
- package/dist/entries/react.js.map +1 -1
- package/dist/entries/slack.d.ts +1 -1
- package/dist/entries/slack.d.ts.map +1 -1
- package/dist/entries/slack.js +1 -1
- package/dist/entries/slack.js.map +1 -1
- package/dist/pack/enhancements.d.ts +4 -0
- package/dist/pack/enhancements.d.ts.map +1 -1
- package/dist/pack/enhancements.js.map +1 -1
- package/dist/render/markdown/blocks.d.ts +6 -0
- package/dist/render/markdown/blocks.d.ts.map +1 -0
- package/dist/render/markdown/blocks.js +77 -0
- package/dist/render/markdown/blocks.js.map +1 -0
- package/dist/render/markdown/index.d.ts +1 -0
- package/dist/render/markdown/index.d.ts.map +1 -1
- package/dist/render/markdown/index.js +1 -0
- package/dist/render/markdown/index.js.map +1 -1
- package/dist/render/primitive_dispatch.d.ts +4 -2
- package/dist/render/primitive_dispatch.d.ts.map +1 -1
- package/dist/render/primitive_dispatch.js +36 -6
- package/dist/render/primitive_dispatch.js.map +1 -1
- package/dist/render/react/index.d.ts +1 -0
- package/dist/render/react/index.d.ts.map +1 -1
- package/dist/render/react/index.js +1 -0
- package/dist/render/react/index.js.map +1 -1
- package/dist/render/react/markdown_content.d.ts +9 -0
- package/dist/render/react/markdown_content.d.ts.map +1 -0
- package/dist/render/react/markdown_content.js +139 -0
- package/dist/render/react/markdown_content.js.map +1 -0
- package/dist/render/slack/blocks.d.ts +8 -1
- package/dist/render/slack/blocks.d.ts.map +1 -1
- package/dist/render/slack/blocks.js +7 -0
- package/dist/render/slack/blocks.js.map +1 -1
- package/dist/render/slack/container.d.ts +25 -0
- package/dist/render/slack/container.d.ts.map +1 -0
- package/dist/render/slack/container.js +55 -0
- package/dist/render/slack/container.js.map +1 -0
- package/dist/render/slack/envelope.d.ts +6 -4
- package/dist/render/slack/envelope.d.ts.map +1 -1
- package/dist/render/slack/envelope.js +71 -15
- package/dist/render/slack/envelope.js.map +1 -1
- package/dist/render/slack/format.d.ts +15 -3
- package/dist/render/slack/format.d.ts.map +1 -1
- package/dist/render/slack/format.js +54 -5
- package/dist/render/slack/format.js.map +1 -1
- package/dist/render/slack/index.d.ts +4 -2
- package/dist/render/slack/index.d.ts.map +1 -1
- package/dist/render/slack/index.js +2 -0
- package/dist/render/slack/index.js.map +1 -1
- package/dist/render/slack/markdown_content.d.ts +3 -2
- package/dist/render/slack/markdown_content.d.ts.map +1 -1
- package/dist/render/slack/markdown_content.js +20 -67
- package/dist/render/slack/markdown_content.js.map +1 -1
- package/dist/render/slack/unmodeled.d.ts +16 -0
- package/dist/render/slack/unmodeled.d.ts.map +1 -0
- package/dist/render/slack/unmodeled.js +32 -0
- package/dist/render/slack/unmodeled.js.map +1 -0
- package/dist/testing/conformance.d.ts +3 -1
- package/dist/testing/conformance.d.ts.map +1 -1
- package/dist/testing/conformance.js +14 -0
- package/dist/testing/conformance.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -60,7 +60,30 @@ const createPrimitiveDispatcher = (definitions, options = {}) => {
|
|
|
60
60
|
// Assigned after helpers that close over this binding.
|
|
61
61
|
// eslint-disable-next-line prefer-const
|
|
62
62
|
let self;
|
|
63
|
-
const
|
|
63
|
+
const scopeFor = (surface, extras) => {
|
|
64
|
+
const parent = self;
|
|
65
|
+
if (surface === 'slack') {
|
|
66
|
+
const { baseUrl } = extras;
|
|
67
|
+
return baseUrl === undefined
|
|
68
|
+
? parent
|
|
69
|
+
: {
|
|
70
|
+
...parent,
|
|
71
|
+
renderOn: (target, node, targetExtras) => parent.renderOn(target, node, target === 'slack' ? { baseUrl, ...targetExtras } : targetExtras),
|
|
72
|
+
renderSlack: (node, collector) => self.renderSlack(node, collector, { baseUrl }),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (surface !== 'snapshot') {
|
|
76
|
+
return parent;
|
|
77
|
+
}
|
|
78
|
+
const { theme } = extras;
|
|
79
|
+
return {
|
|
80
|
+
...parent,
|
|
81
|
+
renderReact: (node, ...context) => parent.renderOn('snapshot', node, {
|
|
82
|
+
context: context[0] ?? {},
|
|
83
|
+
theme,
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
};
|
|
64
87
|
const renderOn = (surface, node, extras) => {
|
|
65
88
|
if (!(0, body_node_base_1.isVisibleOnSurface)(node, surface)) {
|
|
66
89
|
return undefined;
|
|
@@ -74,7 +97,10 @@ const createPrimitiveDispatcher = (definitions, options = {}) => {
|
|
|
74
97
|
// `renderOn` for a node its sanitizer has already run on.
|
|
75
98
|
const renderSanitized = (surface, definition, safeNode, extras) => {
|
|
76
99
|
const renderer = definition.renderers[RENDERER_FOR_SURFACE[surface]];
|
|
77
|
-
return renderer?.(safeNode, {
|
|
100
|
+
return renderer?.(safeNode, {
|
|
101
|
+
...extras,
|
|
102
|
+
scope: scopeFor(surface, extras),
|
|
103
|
+
});
|
|
78
104
|
};
|
|
79
105
|
const renderMarkdownContent = (node) => renderOn('markdown', node, {}) ?? [];
|
|
80
106
|
const renderMarkdown = (node) => (0, builder_1.serializeMarkdown)(renderMarkdownContent(node));
|
|
@@ -104,7 +130,7 @@ const createPrimitiveDispatcher = (definitions, options = {}) => {
|
|
|
104
130
|
renderText: (node) => renderOn('text', node, {}) ?? '',
|
|
105
131
|
renderMarkdown,
|
|
106
132
|
renderMarkdownContent,
|
|
107
|
-
renderSlack: (node, collector) => {
|
|
133
|
+
renderSlack: (node, collector, options = {}) => {
|
|
108
134
|
if (!(0, body_node_base_1.isVisibleOnSurface)(node, 'slack')) {
|
|
109
135
|
return [];
|
|
110
136
|
}
|
|
@@ -132,15 +158,19 @@ const createPrimitiveDispatcher = (definitions, options = {}) => {
|
|
|
132
158
|
// Degrading here rather than in the envelope is what reaches nested
|
|
133
159
|
// children: a container's `slack` renderer recurses through this
|
|
134
160
|
// method, and the envelope only ever sees the container's own output.
|
|
135
|
-
return asSlackPayload((0, markdown_content_1.markdownContentToSlackBlocks)(renderMarkdownContent(node)));
|
|
161
|
+
return asSlackPayload((0, markdown_content_1.markdownContentToSlackBlocks)(renderMarkdownContent(node), options));
|
|
136
162
|
}
|
|
137
163
|
const safeNode = sanitizeNode(definition, node);
|
|
138
164
|
if (safeNode === null) {
|
|
139
165
|
return [];
|
|
140
166
|
}
|
|
141
|
-
const
|
|
167
|
+
const extras = {
|
|
142
168
|
...(collector === undefined ? {} : { collector }),
|
|
143
|
-
|
|
169
|
+
...(options.baseUrl === undefined ? {} : { baseUrl: options.baseUrl }),
|
|
170
|
+
};
|
|
171
|
+
const rendered = slackRenderer(safeNode, {
|
|
172
|
+
...extras,
|
|
173
|
+
scope: scopeFor('slack', extras),
|
|
144
174
|
});
|
|
145
175
|
return isReadonlyArray(rendered) ? rendered : [rendered];
|
|
146
176
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitive_dispatch.js","sourceRoot":"","sources":["../../../src/render/primitive_dispatch.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,iCAAqE;AAErE,kEAGuC;AACvC,gDAAmD;AAcnD,yDAAmE;AAEnE,gDAAuD;AAGvD,+DAAwE;AAwExE;;;;;;GAMG;AACH,MAAM,oBAAoB,GAAG;IAC3B,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,OAAO;IACjB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;CAIf,CAAC;AAcF;;;;;;GAMG;AACI,MAAM,yBAAyB,GAAG,CAIvC,WAA8C,EAC9C,UAAsC,EAAE,EACT,EAAE;IACjC,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC;IACrC,MAAM,YAAY,GAAG,IAAA,sCAAwB,EAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkC,CAAC;IACzD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,mBAAW,CACnB,0BAA0B,EAC1B,GAAG,KAAK,6BAA6B,UAAU,CAAC,IAAI,cAAc,CACnE,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,IAAW,EAA0B,EAAE;QAC5D,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,mBAAW,CACnB,wBAAwB,EACxB,2BAA2B,IAAI,CAAC,IAAI,GAAG,CACxC,CAAC;QACJ,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CACnB,UAAkC,EAClC,IAAW,EACG,EAAE;QAChB,MAAM,QAAQ,GAAG,UAAU,CAAC,QACiB,CAAC;QAC9C,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1C,CAAC,CAAC;IAEF,uDAAuD;IACvD,wCAAwC;IACxC,IAAI,IAAmC,CAAC;IAExC,MAAM,KAAK,GAAG,GAAmB,EAAE,CAAC,IAAI,CAAC;IAEzC,MAAM,QAAQ,GAAG,CACf,OAAU,EACV,IAAW,EACX,MAA8C,EACN,EAAE;QAC1C,IAAI,CAAC,IAAA,mCAAkB,EAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YACvC,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAChD,OAAO,QAAQ,KAAK,IAAI;YACtB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC,CAAC;IAEF,0DAA0D;IAC1D,MAAM,eAAe,GAAG,CACtB,OAAU,EACV,UAAkC,EAClC,QAAe,EACf,MAA8C,EACN,EAAE;QAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAChC,CAAC;QACpC,OAAO,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC;IAEF,MAAM,qBAAqB,GAAG,CAAC,IAAW,EAAmB,EAAE,CAC7D,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IAEvC,MAAM,cAAc,GAAG,CAAC,IAAW,EAAU,EAAE,CAC7C,IAAA,2BAAiB,EAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjD,IAAI,GAAG;QACL,WAAW;QACX,aAAa;QACb,QAAQ;QACR,4DAA4D;QAC5D,oEAAoE;QACpE,WAAW,EAAE,CAAC,IAAI,EAAE,GAAG,OAAO,EAAE,EAAE,CAChC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE;YACtB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE;SAC1B,CAAC;QACJ,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;YACvC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,aAAa,GAAG,UAAU,CAAC,aAQpB,CAAC;YACd,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC5C,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAChE,wEAAwE;YACxE,qEAAqE;YACrE,uCAAuC;YACvC,OAAO,IAAA,sBAAc,EAAC,QAAQ,CAAC;gBAC7B,CAAC,CAAC,IAAA,oBAAY,EAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC;gBACjC,CAAC,CAAC,QAAQ,CAAC;QACf,CAAC;QACD,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;QACtD,cAAc;QACd,qBAAqB;QACrB,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE;YAC/B,IAAI,CAAC,IAAA,mCAAkB,EAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;gBACvC,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YACxE,IAAI,MAAM,IAAI,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5C,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;gBACvC,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;gBAChD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;oBACtB,OAAO,EAAE,CAAC;gBACZ,CAAC;gBACD,MAAM,OAAO,GACX,CAAC,IAAA,mCAAkB,EAAC,QAAQ,EAAE,MAAM,CAAC;oBACnC,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;oBACpD,IAAI,CAAC,IAAI,CAAC;gBACZ,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC/C,MAAM,KAAK,GAAG;oBACZ,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,OAAO;oBACjB,UAAU,EAAE,EAAE,GAAG,EAAE;iBACM,CAAC;gBAC5B,OAAO,cAAc,CAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;YAClD,CAAC;YACD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,aAAa,GAAG,UAAU,CAAC,SAAS,CAAC,KACF,CAAC;YAC1C,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,oEAAoE;gBACpE,iEAAiE;gBACjE,sEAAsE;gBACtE,OAAO,cAAc,CACnB,IAAA,+CAA4B,EAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAC1D,CAAC;YACJ,CAAC;YACD,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YAChD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACtB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE;gBACvC,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;gBACjD,KAAK,EAAE,KAAK,EAAE;aACf,CAAC,CAAC;YACH,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC3D,CAAC;QACD,sBAAsB,EAAE,CAAC,IAAI,EAAE,EAAE;YAC/B,IAAI,CAAC,IAAA,mCAAkB,EAAC,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC;gBAC1C,OAAO,CAAC,CAAC;YACX,CAAC;YACD,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,cACb,CAAC;YACxC,OAAO,cAAc,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QACD,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;YAC/B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE;gBACxD,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;KACF,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAlLW,QAAA,yBAAyB,6BAkLpC;AAEF,MAAM,eAAe,GAAG,CAAI,KAAuB,EAAyB,EAAE,CAC5E,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvB,+FAA+F;AAC/F,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAAgC,EAAE,CAC7E,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,OAAQ,KAAgC,CAAC,QAAQ,KAAK,UAAU,CAAC;AAEnE,iEAAiE;AACjE,MAAM,cAAc,GAAG,CACrB,MAA6B,EACL,EAAE,CAAC,MAA2C,CAAC","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\nimport { cloneElement, isValidElement, type ReactNode } from 'react';\n\nimport {\n isVisibleOnSurface,\n type SurfaceName,\n} from '../composition/body_node_base';\nimport { IsomerError } from '../composition/error';\nimport type { ValidationError } from '../composition/validation_error';\nimport type { MarkdownContent } from '../define/markdown_content';\nimport {\n type AnyPrimitiveDefinition,\n type DefaultPackTypes,\n type PackTypes,\n type PrimitiveNode,\n type PrimitiveStyleCollectionContext,\n type ReactContextArg,\n type Renderer,\n type RenderScope,\n type SurfaceMap,\n} from '../define/primitive_module';\nimport { createNodeIssueFormatter } from '../validate/node_issues';\n\nimport { serializeMarkdown } from './markdown/builder';\nimport type { SlackAssetCollector } from './slack/assets';\nimport type { SlackBlock, SlackImageBlock } from './slack/blocks';\nimport { markdownContentToSlackBlocks } from './slack/markdown_content';\n\n/**\n * Renders a node on any surface by looking its `type` up in one flattened\n * inventory.\n *\n * The seam that makes packs composable: a container recurses through the\n * {@link RenderScope} it is passed rather than through its own pack, so a node\n * owned by another pack still renders.\n */\nexport interface PrimitiveDispatcher<\n TNode extends PrimitiveNode,\n T extends PackTypes = DefaultPackTypes,\n> {\n /** Every primitive this dispatcher can reach, across all composed packs. */\n readonly definitions: readonly AnyPrimitiveDefinition[];\n /** Throws for an unknown node type; a missing primitive is a bug, not a degradation. */\n getDefinition(node: TNode): AnyPrimitiveDefinition;\n /** `undefined` when the node is hidden from `surface`, `sanitize` drops it, or the primitive declares no renderer for `surface`. */\n renderOn<S extends SurfaceName>(\n surface: S,\n node: TNode,\n extras: Omit<SurfaceMap<T>[S]['env'], 'scope'>\n ): SurfaceMap<T>[S]['output'] | undefined;\n /** The node's `react` renderer, or `undefined` when it is hidden or has none. `context` may be omitted only when `{}` is a complete context for the pack. */\n renderReact(\n node: TNode,\n ...context: ReactContextArg<T['context']>\n ): ReactNode;\n /** Mutates `styles` with this node's contribution. */\n collectStyles(\n node: TNode,\n styles: T['collector'],\n context: PrimitiveStyleCollectionContext\n ): void;\n /**\n * The node's `react` renderer, gated on `snapshot` visibility and keyed for a\n * frame's sibling list. `key` must be unique among those siblings.\n * `theme` is the frame's resolved palette for this render, or `undefined`\n * outside a frame.\n */\n renderSnapshot(\n node: TNode,\n context: T['context'],\n theme: T['theme'] | undefined,\n key: string\n ): ReactNode;\n /** `''` when the node is hidden from `text` or its primitive declares no `text` renderer. */\n renderText(node: TNode): string;\n /** The node's content serialized as GFM. */\n renderMarkdown(node: TNode): string;\n /** Empty when the node is hidden from `markdown`. */\n renderMarkdownContent(node: TNode): MarkdownContent;\n /**\n * Empty when the node is hidden from `slack`, `sanitize` drops it, or its\n * renderer returns nothing. A node with no `slack` renderer degrades through\n * its markdown; one `isSlackAssetType` names becomes an `image` block when\n * `collector` can allocate.\n */\n renderSlack(\n node: TNode,\n collector?: T['slackCollector']\n ): readonly T['slackBlock'][];\n /** `0` when the node is hidden from `snapshot` or its primitive declares no `metrics.snapshotHeight`. */\n estimateSnapshotHeight(node: TNode): number;\n /**\n * Appends schema failures under `path` to `errors`, each naming `node`'s\n * type, and an unknown key on `node` listing the fields it declares.\n */\n validate(node: TNode, path: string, errors: ValidationError[]): void;\n}\n\n/**\n * Which of a definition's renderers each surface dispatches to.\n *\n * `snapshot` maps to `react`: a rasterizer lays out the pack's DOM tree and\n * stylesheet, so the two surfaces differ only in visibility and in what\n * surrounds them.\n */\nconst RENDERER_FOR_SURFACE = {\n react: 'react',\n snapshot: 'react',\n text: 'text',\n markdown: 'markdown',\n slack: 'slack',\n} as const satisfies Record<\n SurfaceName,\n keyof AnyPrimitiveDefinition['renderers']\n>;\n\n/** Runtime facts {@link createPrimitiveDispatcher} cannot derive from the inventory. */\nexport interface PrimitiveDispatcherOptions {\n /** Names this dispatcher in error messages. */\n label?: string;\n /**\n * Which node types render as a picture rather than as text, so the Slack\n * surface uploads an image instead of degrading to markdown. Supplied by the\n * runtime from each pack's `slackAssetTypes`.\n */\n isSlackAssetType?: ((type: string) => boolean) | undefined;\n}\n\n/**\n * Flattens `definitions` into one type-keyed inventory.\n *\n * Throws on a duplicate `type`: two packs claiming one node type has no correct\n * resolution, so it fails at construction rather than resolving arbitrarily per\n * render.\n */\nexport const createPrimitiveDispatcher = <\n TNode extends PrimitiveNode,\n T extends PackTypes = DefaultPackTypes,\n>(\n definitions: readonly AnyPrimitiveDefinition[],\n options: PrimitiveDispatcherOptions = {}\n): PrimitiveDispatcher<TNode, T> => {\n const { isSlackAssetType } = options;\n const formatIssues = createNodeIssueFormatter(definitions);\n const byType = new Map<string, AnyPrimitiveDefinition>();\n for (const definition of definitions) {\n const existing = byType.get(definition.type);\n if (existing) {\n const label = options.label ? `${options.label}: ` : '';\n throw new IsomerError(\n 'DUPLICATE_PRIMITIVE_TYPE',\n `${label}duplicate primitive type \"${definition.type}\" registered`\n );\n }\n byType.set(definition.type, definition);\n }\n\n const getDefinition = (node: TNode): AnyPrimitiveDefinition => {\n const definition = byType.get(node.type);\n if (!definition) {\n throw new IsomerError(\n 'UNKNOWN_PRIMITIVE_TYPE',\n `Unknown primitive type \"${node.type}\"`\n );\n }\n return definition;\n };\n\n const sanitizeNode = (\n definition: AnyPrimitiveDefinition,\n node: TNode\n ): TNode | null => {\n const sanitize = definition.sanitize as\n ((node: TNode) => TNode | null) | undefined;\n return sanitize ? sanitize(node) : node;\n };\n\n // Assigned after helpers that close over this binding.\n // eslint-disable-next-line prefer-const\n let self: PrimitiveDispatcher<TNode, T>;\n\n const scope = (): RenderScope<T> => self;\n\n const renderOn = <S extends SurfaceName>(\n surface: S,\n node: TNode,\n extras: Omit<SurfaceMap<T>[S]['env'], 'scope'>\n ): SurfaceMap<T>[S]['output'] | undefined => {\n if (!isVisibleOnSurface(node, surface)) {\n return undefined;\n }\n const definition = getDefinition(node);\n const safeNode = sanitizeNode(definition, node);\n return safeNode === null\n ? undefined\n : renderSanitized(surface, definition, safeNode, extras);\n };\n\n // `renderOn` for a node its sanitizer has already run on.\n const renderSanitized = <S extends SurfaceName>(\n surface: S,\n definition: AnyPrimitiveDefinition,\n safeNode: TNode,\n extras: Omit<SurfaceMap<T>[S]['env'], 'scope'>\n ): SurfaceMap<T>[S]['output'] | undefined => {\n const renderer = definition.renderers[RENDERER_FOR_SURFACE[surface]] as\n Renderer<TNode, S, T> | undefined;\n return renderer?.(safeNode, { ...extras, scope: scope() });\n };\n\n const renderMarkdownContent = (node: TNode): MarkdownContent =>\n renderOn('markdown', node, {}) ?? [];\n\n const renderMarkdown = (node: TNode): string =>\n serializeMarkdown(renderMarkdownContent(node));\n\n self = {\n definitions,\n getDefinition,\n renderOn,\n // `{}` only ever runs when the argument was optional, which\n // `ReactContextArg` allows exactly when `{}` is a complete context.\n renderReact: (node, ...context) =>\n renderOn('react', node, {\n context: context[0] ?? {},\n }),\n collectStyles: (node, styles, context) => {\n const definition = getDefinition(node);\n const collectStyles = definition.collectStyles as\n | ((\n node: TNode,\n env: {\n styles: T['collector'];\n context: PrimitiveStyleCollectionContext;\n }\n ) => void)\n | undefined;\n collectStyles?.(node, { styles, context });\n },\n renderSnapshot: (node, context, theme, key) => {\n const rendered = renderOn('snapshot', node, { context, theme });\n // The frame hands these to React as an array; a `key` has to sit on the\n // element itself, since wrapping in a Fragment would hide the single\n // root an image backend lays out from.\n return isValidElement(rendered)\n ? cloneElement(rendered, { key })\n : rendered;\n },\n renderText: (node) => renderOn('text', node, {}) ?? '',\n renderMarkdown,\n renderMarkdownContent,\n renderSlack: (node, collector) => {\n if (!isVisibleOnSurface(node, 'slack')) {\n return [];\n }\n const assets = isSlackAssetCollector(collector) ? collector : undefined;\n if (assets && isSlackAssetType?.(node.type)) {\n const definition = getDefinition(node);\n const safeNode = sanitizeNode(definition, node);\n if (safeNode === null) {\n return [];\n }\n const altText =\n (isVisibleOnSurface(safeNode, 'text') &&\n renderSanitized('text', definition, safeNode, {})) ||\n node.type;\n const ref = assets.allocate(safeNode, altText);\n const block = {\n type: 'image',\n alt_text: altText,\n slack_file: { ref },\n } satisfies SlackImageBlock;\n return asSlackPayload<T['slackBlock']>([block]);\n }\n const definition = getDefinition(node);\n const slackRenderer = definition.renderers.slack as\n Renderer<TNode, 'slack', T> | undefined;\n if (!slackRenderer) {\n // Degrading here rather than in the envelope is what reaches nested\n // children: a container's `slack` renderer recurses through this\n // method, and the envelope only ever sees the container's own output.\n return asSlackPayload<T['slackBlock']>(\n markdownContentToSlackBlocks(renderMarkdownContent(node))\n );\n }\n const safeNode = sanitizeNode(definition, node);\n if (safeNode === null) {\n return [];\n }\n const rendered = slackRenderer(safeNode, {\n ...(collector === undefined ? {} : { collector }),\n scope: scope(),\n });\n return isReadonlyArray(rendered) ? rendered : [rendered];\n },\n estimateSnapshotHeight: (node) => {\n if (!isVisibleOnSurface(node, 'snapshot')) {\n return 0;\n }\n const snapshotHeight = getDefinition(node).metrics?.snapshotHeight as\n ((node: TNode) => number) | undefined;\n return snapshotHeight?.(node) ?? 0;\n },\n validate: (node, path, errors) => {\n const result = getDefinition(node).schema.safeParse(node, {\n reportInput: true,\n });\n if (!result.success) {\n errors.push(...formatIssues([[node, path]], result.error.issues, path));\n }\n },\n };\n return self;\n};\n\nconst isReadonlyArray = <T>(value: T | readonly T[]): value is readonly T[] =>\n Array.isArray(value);\n\n/** The picture swap needs a collector that can allocate; any other collector type skips it. */\nconst isSlackAssetCollector = (value: unknown): value is SlackAssetCollector =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { allocate?: unknown }).allocate === 'function';\n\n/** Rebinds checked Block Kit to the erased payload parameter. */\nconst asSlackPayload = <TSlackBlock>(\n blocks: readonly SlackBlock[]\n): readonly TSlackBlock[] => blocks as unknown as readonly TSlackBlock[];\n"]}
|
|
1
|
+
{"version":3,"file":"primitive_dispatch.js","sourceRoot":"","sources":["../../../src/render/primitive_dispatch.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\nimport { cloneElement, isValidElement, type ReactNode } from 'react';\n\nimport {\n isVisibleOnSurface,\n type SurfaceName,\n} from '../composition/body_node_base';\nimport { IsomerError } from '../composition/error';\nimport type { ValidationError } from '../composition/validation_error';\nimport type { MarkdownContent } from '../define/markdown_content';\nimport {\n type AnyPrimitiveDefinition,\n type DefaultPackTypes,\n type PackTypes,\n type PrimitiveNode,\n type PrimitiveStyleCollectionContext,\n type ReactContextArg,\n type Renderer,\n type RenderScope,\n type SurfaceMap,\n} from '../define/primitive_module';\nimport { createNodeIssueFormatter } from '../validate/node_issues';\n\nimport { serializeMarkdown } from './markdown/builder';\nimport type { SlackAssetCollector } from './slack/assets';\nimport type { SlackBlock, SlackImageBlock } from './slack/blocks';\nimport type { SlackLinkOptions } from './slack/format';\nimport { markdownContentToSlackBlocks } from './slack/markdown_content';\n\n/**\n * Renders a node on any surface by looking its `type` up in one flattened\n * inventory.\n *\n * The seam that makes packs composable: a container recurses through the\n * {@link RenderScope} it is passed rather than through its own pack, so a node\n * owned by another pack still renders.\n */\nexport interface PrimitiveDispatcher<\n TNode extends PrimitiveNode,\n T extends PackTypes = DefaultPackTypes,\n> {\n /** Every primitive this dispatcher can reach, across all composed packs. */\n readonly definitions: readonly AnyPrimitiveDefinition[];\n /** Throws for an unknown node type; a missing primitive is a bug, not a degradation. */\n getDefinition(node: TNode): AnyPrimitiveDefinition;\n /** `undefined` when the node is hidden from `surface`, `sanitize` drops it, or the primitive declares no renderer for `surface`. */\n renderOn<S extends SurfaceName>(\n surface: S,\n node: TNode,\n extras: Omit<SurfaceMap<T>[S]['env'], 'scope'>\n ): SurfaceMap<T>[S]['output'] | undefined;\n /** The node's `react` renderer, or `undefined` when it is hidden or has none. `context` may be omitted only when `{}` is a complete context for the pack. */\n renderReact(\n node: TNode,\n ...context: ReactContextArg<T['context']>\n ): ReactNode;\n /** Mutates `styles` with this node's contribution. */\n collectStyles(\n node: TNode,\n styles: T['collector'],\n context: PrimitiveStyleCollectionContext\n ): void;\n /**\n * The node's `react` renderer, gated on `snapshot` visibility and keyed for a\n * frame's sibling list. `key` must be unique among those siblings.\n * `theme` is the frame's resolved palette for this render, or `undefined`\n * outside a frame.\n */\n renderSnapshot(\n node: TNode,\n context: T['context'],\n theme: T['theme'] | undefined,\n key: string\n ): ReactNode;\n /** `''` when the node is hidden from `text` or its primitive declares no `text` renderer. */\n renderText(node: TNode): string;\n /** The node's content serialized as GFM. */\n renderMarkdown(node: TNode): string;\n /** Empty when the node is hidden from `markdown`. */\n renderMarkdownContent(node: TNode): MarkdownContent;\n /**\n * Empty when the node is hidden from `slack`, `sanitize` drops it, or its\n * renderer returns nothing. A node with no `slack` renderer degrades through\n * its markdown; one `isSlackAssetType` names becomes an `image` block when\n * `collector` can allocate. `options.baseUrl` reaches the renderer's env and\n * every child it renders through `scope`.\n */\n renderSlack(\n node: TNode,\n collector?: T['slackCollector'],\n options?: SlackLinkOptions\n ): readonly T['slackBlock'][];\n /** `0` when the node is hidden from `snapshot` or its primitive declares no `metrics.snapshotHeight`. */\n estimateSnapshotHeight(node: TNode): number;\n /**\n * Appends schema failures under `path` to `errors`, each naming `node`'s\n * type, and an unknown key on `node` listing the fields it declares.\n */\n validate(node: TNode, path: string, errors: ValidationError[]): void;\n}\n\n/**\n * Which of a definition's renderers each surface dispatches to.\n *\n * `snapshot` maps to `react`: a rasterizer lays out the pack's DOM tree and\n * stylesheet, so the two surfaces differ only in visibility and in what\n * surrounds them.\n */\nconst RENDERER_FOR_SURFACE = {\n react: 'react',\n snapshot: 'react',\n text: 'text',\n markdown: 'markdown',\n slack: 'slack',\n} as const satisfies Record<\n SurfaceName,\n keyof AnyPrimitiveDefinition['renderers']\n>;\n\n/** Runtime facts {@link createPrimitiveDispatcher} cannot derive from the inventory. */\nexport interface PrimitiveDispatcherOptions {\n /** Names this dispatcher in error messages. */\n label?: string;\n /**\n * Which node types render as a picture rather than as text, so the Slack\n * surface uploads an image instead of degrading to markdown. Supplied by the\n * runtime from each pack's `slackAssetTypes`.\n */\n isSlackAssetType?: ((type: string) => boolean) | undefined;\n}\n\n/**\n * Flattens `definitions` into one type-keyed inventory.\n *\n * Throws on a duplicate `type`: two packs claiming one node type has no correct\n * resolution, so it fails at construction rather than resolving arbitrarily per\n * render.\n */\nexport const createPrimitiveDispatcher = <\n TNode extends PrimitiveNode,\n T extends PackTypes = DefaultPackTypes,\n>(\n definitions: readonly AnyPrimitiveDefinition[],\n options: PrimitiveDispatcherOptions = {}\n): PrimitiveDispatcher<TNode, T> => {\n const { isSlackAssetType } = options;\n const formatIssues = createNodeIssueFormatter(definitions);\n const byType = new Map<string, AnyPrimitiveDefinition>();\n for (const definition of definitions) {\n const existing = byType.get(definition.type);\n if (existing) {\n const label = options.label ? `${options.label}: ` : '';\n throw new IsomerError(\n 'DUPLICATE_PRIMITIVE_TYPE',\n `${label}duplicate primitive type \"${definition.type}\" registered`\n );\n }\n byType.set(definition.type, definition);\n }\n\n const getDefinition = (node: TNode): AnyPrimitiveDefinition => {\n const definition = byType.get(node.type);\n if (!definition) {\n throw new IsomerError(\n 'UNKNOWN_PRIMITIVE_TYPE',\n `Unknown primitive type \"${node.type}\"`\n );\n }\n return definition;\n };\n\n const sanitizeNode = (\n definition: AnyPrimitiveDefinition,\n node: TNode\n ): TNode | null => {\n const sanitize = definition.sanitize as\n ((node: TNode) => TNode | null) | undefined;\n return sanitize ? sanitize(node) : node;\n };\n\n // Assigned after helpers that close over this binding.\n // eslint-disable-next-line prefer-const\n let self: PrimitiveDispatcher<TNode, T>;\n\n const scopeFor = <S extends SurfaceName>(\n surface: S,\n extras: Omit<SurfaceMap<T>[S]['env'], 'scope'>\n ): RenderScope<T> => {\n const parent: RenderScope<T> = self;\n if (surface === 'slack') {\n const { baseUrl } = extras as Omit<\n SurfaceMap<T>['slack']['env'],\n 'scope'\n >;\n return baseUrl === undefined\n ? parent\n : {\n ...parent,\n renderOn: (target, node, targetExtras) =>\n parent.renderOn(\n target,\n node,\n target === 'slack' ? { baseUrl, ...targetExtras } : targetExtras\n ),\n renderSlack: (node, collector) =>\n self.renderSlack(node as TNode, collector, { baseUrl }),\n };\n }\n if (surface !== 'snapshot') {\n return parent;\n }\n const { theme } = extras as Omit<SurfaceMap<T>['snapshot']['env'], 'scope'>;\n return {\n ...parent,\n renderReact: (node, ...context) =>\n parent.renderOn('snapshot', node, {\n context: context[0] ?? {},\n theme,\n }),\n };\n };\n\n const renderOn = <S extends SurfaceName>(\n surface: S,\n node: TNode,\n extras: Omit<SurfaceMap<T>[S]['env'], 'scope'>\n ): SurfaceMap<T>[S]['output'] | undefined => {\n if (!isVisibleOnSurface(node, surface)) {\n return undefined;\n }\n const definition = getDefinition(node);\n const safeNode = sanitizeNode(definition, node);\n return safeNode === null\n ? undefined\n : renderSanitized(surface, definition, safeNode, extras);\n };\n\n // `renderOn` for a node its sanitizer has already run on.\n const renderSanitized = <S extends SurfaceName>(\n surface: S,\n definition: AnyPrimitiveDefinition,\n safeNode: TNode,\n extras: Omit<SurfaceMap<T>[S]['env'], 'scope'>\n ): SurfaceMap<T>[S]['output'] | undefined => {\n const renderer = definition.renderers[RENDERER_FOR_SURFACE[surface]] as\n Renderer<TNode, S, T> | undefined;\n return renderer?.(safeNode, {\n ...extras,\n scope: scopeFor(surface, extras),\n });\n };\n\n const renderMarkdownContent = (node: TNode): MarkdownContent =>\n renderOn('markdown', node, {}) ?? [];\n\n const renderMarkdown = (node: TNode): string =>\n serializeMarkdown(renderMarkdownContent(node));\n\n self = {\n definitions,\n getDefinition,\n renderOn,\n // `{}` only ever runs when the argument was optional, which\n // `ReactContextArg` allows exactly when `{}` is a complete context.\n renderReact: (node, ...context) =>\n renderOn('react', node, {\n context: context[0] ?? {},\n }),\n collectStyles: (node, styles, context) => {\n const definition = getDefinition(node);\n const collectStyles = definition.collectStyles as\n | ((\n node: TNode,\n env: {\n styles: T['collector'];\n context: PrimitiveStyleCollectionContext;\n }\n ) => void)\n | undefined;\n collectStyles?.(node, { styles, context });\n },\n renderSnapshot: (node, context, theme, key) => {\n const rendered = renderOn('snapshot', node, { context, theme });\n // The frame hands these to React as an array; a `key` has to sit on the\n // element itself, since wrapping in a Fragment would hide the single\n // root an image backend lays out from.\n return isValidElement(rendered)\n ? cloneElement(rendered, { key })\n : rendered;\n },\n renderText: (node) => renderOn('text', node, {}) ?? '',\n renderMarkdown,\n renderMarkdownContent,\n renderSlack: (node, collector, options = {}) => {\n if (!isVisibleOnSurface(node, 'slack')) {\n return [];\n }\n const assets = isSlackAssetCollector(collector) ? collector : undefined;\n if (assets && isSlackAssetType?.(node.type)) {\n const definition = getDefinition(node);\n const safeNode = sanitizeNode(definition, node);\n if (safeNode === null) {\n return [];\n }\n const altText =\n (isVisibleOnSurface(safeNode, 'text') &&\n renderSanitized('text', definition, safeNode, {})) ||\n node.type;\n const ref = assets.allocate(safeNode, altText);\n const block = {\n type: 'image',\n alt_text: altText,\n slack_file: { ref },\n } satisfies SlackImageBlock;\n return asSlackPayload<T['slackBlock']>([block]);\n }\n const definition = getDefinition(node);\n const slackRenderer = definition.renderers.slack as\n Renderer<TNode, 'slack', T> | undefined;\n if (!slackRenderer) {\n // Degrading here rather than in the envelope is what reaches nested\n // children: a container's `slack` renderer recurses through this\n // method, and the envelope only ever sees the container's own output.\n return asSlackPayload<T['slackBlock']>(\n markdownContentToSlackBlocks(renderMarkdownContent(node), options)\n );\n }\n const safeNode = sanitizeNode(definition, node);\n if (safeNode === null) {\n return [];\n }\n const extras = {\n ...(collector === undefined ? {} : { collector }),\n ...(options.baseUrl === undefined ? {} : { baseUrl: options.baseUrl }),\n };\n const rendered = slackRenderer(safeNode, {\n ...extras,\n scope: scopeFor('slack', extras),\n });\n return isReadonlyArray(rendered) ? rendered : [rendered];\n },\n estimateSnapshotHeight: (node) => {\n if (!isVisibleOnSurface(node, 'snapshot')) {\n return 0;\n }\n const snapshotHeight = getDefinition(node).metrics?.snapshotHeight as\n ((node: TNode) => number) | undefined;\n return snapshotHeight?.(node) ?? 0;\n },\n validate: (node, path, errors) => {\n const result = getDefinition(node).schema.safeParse(node, {\n reportInput: true,\n });\n if (!result.success) {\n errors.push(...formatIssues([[node, path]], result.error.issues, path));\n }\n },\n };\n return self;\n};\n\nconst isReadonlyArray = <T>(value: T | readonly T[]): value is readonly T[] =>\n Array.isArray(value);\n\n/** The picture swap needs a collector that can allocate; any other collector type skips it. */\nconst isSlackAssetCollector = (value: unknown): value is SlackAssetCollector =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { allocate?: unknown }).allocate === 'function';\n\n/** Rebinds checked Block Kit to the erased payload parameter. */\nconst asSlackPayload = <TSlackBlock>(\n blocks: readonly SlackBlock[]\n): readonly TSlackBlock[] => blocks as unknown as readonly TSlackBlock[];\n"]}
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* 2.0.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.applyEnhancements = exports.wrapCompositionContent = exports.renderCompositionContent = void 0;
|
|
9
|
+
exports.renderMarkdownContent = exports.applyEnhancements = exports.wrapCompositionContent = exports.renderCompositionContent = void 0;
|
|
10
10
|
var content_1 = require("./content");
|
|
11
11
|
Object.defineProperty(exports, "renderCompositionContent", { enumerable: true, get: function () { return content_1.renderCompositionContent; } });
|
|
12
12
|
Object.defineProperty(exports, "wrapCompositionContent", { enumerable: true, get: function () { return content_1.wrapCompositionContent; } });
|
|
13
13
|
var enhancements_1 = require("./enhancements");
|
|
14
14
|
Object.defineProperty(exports, "applyEnhancements", { enumerable: true, get: function () { return enhancements_1.applyEnhancements; } });
|
|
15
|
+
var markdown_content_1 = require("./markdown_content");
|
|
16
|
+
Object.defineProperty(exports, "renderMarkdownContent", { enumerable: true, get: function () { return markdown_content_1.renderMarkdownContent; } });
|
|
15
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/render/react/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,qCAMmB;AAFjB,mHAAA,wBAAwB,OAAA;AACxB,iHAAA,sBAAsB,OAAA;AAExB,+CAAmD;AAA1C,iHAAA,iBAAiB,OAAA","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\nexport {\n type CompositionWrapperOptions,\n type ReactContentDispatcher,\n type ReactContentOptions,\n renderCompositionContent,\n wrapCompositionContent,\n} from './content';\nexport { applyEnhancements } from './enhancements';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/render/react/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,qCAMmB;AAFjB,mHAAA,wBAAwB,OAAA;AACxB,iHAAA,sBAAsB,OAAA;AAExB,+CAAmD;AAA1C,iHAAA,iBAAiB,OAAA;AAC1B,uDAA2D;AAAlD,yHAAA,qBAAqB,OAAA","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\nexport {\n type CompositionWrapperOptions,\n type ReactContentDispatcher,\n type ReactContentOptions,\n renderCompositionContent,\n wrapCompositionContent,\n} from './content';\nexport { applyEnhancements } from './enhancements';\nexport { renderMarkdownContent } from './markdown_content';\n"]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
4
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
5
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
6
|
+
* 2.0.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.renderMarkdownContent = void 0;
|
|
10
|
+
// Builder content as React elements, read from the blocks the Markdown and
|
|
11
|
+
// Slack surfaces read. Unstyled: a pack's stylesheet owns the look.
|
|
12
|
+
const react_1 = require("react");
|
|
13
|
+
const url_1 = require("../../validate/url");
|
|
14
|
+
const blocks_1 = require("../markdown/blocks");
|
|
15
|
+
const HEADINGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
|
16
|
+
const footnoteLabel = ({ identifier, label, }) => `[^${label ?? identifier}]`;
|
|
17
|
+
const plainText = (node) => {
|
|
18
|
+
if ('value' in node) {
|
|
19
|
+
return node.value;
|
|
20
|
+
}
|
|
21
|
+
if (node.type === 'image') {
|
|
22
|
+
return node.alt ?? '';
|
|
23
|
+
}
|
|
24
|
+
if (node.type === 'footnoteReference') {
|
|
25
|
+
return footnoteLabel(node);
|
|
26
|
+
}
|
|
27
|
+
return 'children' in node ? node.children.map(plainText).join('') : '';
|
|
28
|
+
};
|
|
29
|
+
const keyed = (nodes) => nodes.map((node, index) => (0, react_1.createElement)(react_1.Fragment, { key: index }, node));
|
|
30
|
+
const inlines = (nodes) => keyed(nodes.map(inline));
|
|
31
|
+
const inline = (node) => {
|
|
32
|
+
switch (node.type) {
|
|
33
|
+
case 'strong':
|
|
34
|
+
return (0, react_1.createElement)('strong', null, ...inlines(node.children));
|
|
35
|
+
case 'emphasis':
|
|
36
|
+
return (0, react_1.createElement)('em', null, ...inlines(node.children));
|
|
37
|
+
case 'delete':
|
|
38
|
+
return (0, react_1.createElement)('del', null, ...inlines(node.children));
|
|
39
|
+
case 'inlineCode':
|
|
40
|
+
return (0, react_1.createElement)('code', null, node.value);
|
|
41
|
+
case 'break':
|
|
42
|
+
return (0, react_1.createElement)('br');
|
|
43
|
+
case 'link':
|
|
44
|
+
return (0, react_1.createElement)('a', {
|
|
45
|
+
href: (0, url_1.sanitizeParsedNavigationHref)(node.url) ?? url_1.BLOCKED_HREF,
|
|
46
|
+
...(node.title ? { title: node.title } : {}),
|
|
47
|
+
}, ...inlines(node.children));
|
|
48
|
+
case 'image': {
|
|
49
|
+
const src = (0, url_1.sanitizeParsedAssetUrl)(node.url);
|
|
50
|
+
return src === null
|
|
51
|
+
? (node.alt ?? '')
|
|
52
|
+
: (0, react_1.createElement)('img', {
|
|
53
|
+
src,
|
|
54
|
+
alt: node.alt ?? '',
|
|
55
|
+
...(node.title ? { title: node.title } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
return plainText(node);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const paragraphCount = (item) => item.children.filter(({ type }) => type === 'paragraph').length;
|
|
63
|
+
// A spread list, a spread item, or an item with two paragraphs makes the whole list loose.
|
|
64
|
+
const items = (list) => {
|
|
65
|
+
const tight = list.spread !== true &&
|
|
66
|
+
!list.children.some((item) => item.spread === true || paragraphCount(item) > 1);
|
|
67
|
+
return list.children.map((item, index) => listItem(item, index, tight));
|
|
68
|
+
};
|
|
69
|
+
// A task item's checkbox leads its first paragraph, so a loose item keeps it on the task's line.
|
|
70
|
+
const listItem = (item, key, tight) => {
|
|
71
|
+
const checkbox = typeof item.checked === 'boolean'
|
|
72
|
+
? [
|
|
73
|
+
(0, react_1.createElement)('input', {
|
|
74
|
+
type: 'checkbox',
|
|
75
|
+
checked: item.checked,
|
|
76
|
+
disabled: true,
|
|
77
|
+
}),
|
|
78
|
+
' ',
|
|
79
|
+
]
|
|
80
|
+
: [];
|
|
81
|
+
const [first, ...rest] = item.children;
|
|
82
|
+
const leadsWithParagraph = first?.type === 'paragraph';
|
|
83
|
+
const lead = leadsWithParagraph
|
|
84
|
+
? [...checkbox, ...inlines(first.children)]
|
|
85
|
+
: checkbox;
|
|
86
|
+
const head = leadsWithParagraph && !tight ? [(0, react_1.createElement)('p', null, ...lead)] : lead;
|
|
87
|
+
const body = (leadsWithParagraph ? rest : item.children).flatMap((child) => child.type === 'paragraph' && tight
|
|
88
|
+
? inlines(child.children)
|
|
89
|
+
: [block(child)]);
|
|
90
|
+
return (0, react_1.createElement)('li', { key }, ...keyed([...head, ...body]));
|
|
91
|
+
};
|
|
92
|
+
const tableRows = (table) => {
|
|
93
|
+
const [header, ...body] = table.children;
|
|
94
|
+
const cells = (row, tag) => (0, react_1.createElement)('tr', null, ...row.children.map((cell, index) => {
|
|
95
|
+
const align = table.align?.[index];
|
|
96
|
+
return (0, react_1.createElement)(tag, {
|
|
97
|
+
key: index,
|
|
98
|
+
...(align ? { style: { textAlign: align } } : {}),
|
|
99
|
+
}, ...inlines(cell.children));
|
|
100
|
+
}));
|
|
101
|
+
return (0, react_1.createElement)(react_1.Fragment, null, header
|
|
102
|
+
? (0, react_1.createElement)('thead', { key: 'head' }, cells(header, 'th'))
|
|
103
|
+
: null, body.length > 0
|
|
104
|
+
? (0, react_1.createElement)('tbody', { key: 'body' }, ...body.map((row, index) => (0, react_1.createElement)(react_1.Fragment, { key: index }, cells(row, 'td'))))
|
|
105
|
+
: null);
|
|
106
|
+
};
|
|
107
|
+
const blocks = (nodes) => keyed(nodes.map(block));
|
|
108
|
+
const block = (node) => {
|
|
109
|
+
switch (node.type) {
|
|
110
|
+
case 'paragraph':
|
|
111
|
+
return (0, react_1.createElement)('p', null, ...inlines(node.children));
|
|
112
|
+
case 'heading':
|
|
113
|
+
return (0, react_1.createElement)(HEADINGS[node.depth - 1], null, ...inlines(node.children));
|
|
114
|
+
case 'blockquote':
|
|
115
|
+
return (0, react_1.createElement)('blockquote', null, ...blocks(node.children));
|
|
116
|
+
case 'list':
|
|
117
|
+
return (0, react_1.createElement)(node.ordered ? 'ol' : 'ul', node.ordered && node.start != null && node.start !== 1
|
|
118
|
+
? { start: node.start }
|
|
119
|
+
: null, ...items(node));
|
|
120
|
+
case 'code':
|
|
121
|
+
return (0, react_1.createElement)('pre', null, (0, react_1.createElement)('code', node.lang ? { className: `language-${node.lang}` } : null, node.value));
|
|
122
|
+
case 'table':
|
|
123
|
+
return (0, react_1.createElement)('table', null, tableRows(node));
|
|
124
|
+
case 'thematicBreak':
|
|
125
|
+
return (0, react_1.createElement)('hr');
|
|
126
|
+
case 'html':
|
|
127
|
+
return (0, react_1.createElement)('p', null, node.value);
|
|
128
|
+
case 'footnoteDefinition': {
|
|
129
|
+
const [first, ...rest] = node.children;
|
|
130
|
+
return (0, react_1.createElement)(react_1.Fragment, null, (0, react_1.createElement)('p', null, `${footnoteLabel(node)}: `, ...(first?.type === 'paragraph' ? inlines(first.children) : [])), ...blocks(first?.type === 'paragraph' ? rest : node.children));
|
|
131
|
+
}
|
|
132
|
+
default:
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Builder content as React elements: paragraphs, headings, lists, quotes, code, tables, and thematic breaks, with inline marks, links, and images. Source from `md.authored` is parsed as GFM, once, as the Markdown and Slack surfaces read it.
|
|
138
|
+
*
|
|
139
|
+
* Links and images follow `md`'s URL policy: a blocked link keeps its label on {@link BLOCKED_HREF}, and a blocked image degrades to its alt text.
|
|
140
|
+
*/
|
|
141
|
+
const renderMarkdownContent = (content) => (0, react_1.createElement)(react_1.Fragment, null, ...blocks((0, blocks_1.resolvedMarkdownBlocks)(content)));
|
|
142
|
+
exports.renderMarkdownContent = renderMarkdownContent;
|
|
143
|
+
//# sourceMappingURL=markdown_content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown_content.js","sourceRoot":"","sources":["../../../../src/render/react/markdown_content.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\n// Builder content as React elements, read from the blocks the Markdown and\n// Slack surfaces read. Unstyled: a pack's stylesheet owns the look.\n\nimport { createElement, Fragment, type ReactNode } from 'react';\nimport type {\n FootnoteDefinition,\n List,\n ListItem,\n Nodes,\n PhrasingContent,\n RootContent,\n Table,\n} from 'mdast';\n\nimport type { MarkdownContent } from '../../define/markdown_content';\nimport {\n BLOCKED_HREF,\n sanitizeParsedAssetUrl,\n sanitizeParsedNavigationHref,\n} from '../../validate/url';\nimport { resolvedMarkdownBlocks } from '../markdown/blocks';\n\nconst HEADINGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;\n\nconst footnoteLabel = ({\n identifier,\n label,\n}: Pick<FootnoteDefinition, 'identifier' | 'label'>): string =>\n `[^${label ?? identifier}]`;\n\nconst plainText = (node: Nodes): string => {\n if ('value' in node) {\n return node.value;\n }\n if (node.type === 'image') {\n return node.alt ?? '';\n }\n if (node.type === 'footnoteReference') {\n return footnoteLabel(node);\n }\n return 'children' in node ? node.children.map(plainText).join('') : '';\n};\n\nconst keyed = (nodes: readonly ReactNode[]): ReactNode[] =>\n nodes.map((node, index) => createElement(Fragment, { key: index }, node));\n\nconst inlines = (nodes: readonly PhrasingContent[]): ReactNode[] =>\n keyed(nodes.map(inline));\n\nconst inline = (node: PhrasingContent): ReactNode => {\n switch (node.type) {\n case 'strong':\n return createElement('strong', null, ...inlines(node.children));\n case 'emphasis':\n return createElement('em', null, ...inlines(node.children));\n case 'delete':\n return createElement('del', null, ...inlines(node.children));\n case 'inlineCode':\n return createElement('code', null, node.value);\n case 'break':\n return createElement('br');\n case 'link':\n return createElement(\n 'a',\n {\n href: sanitizeParsedNavigationHref(node.url) ?? BLOCKED_HREF,\n ...(node.title ? { title: node.title } : {}),\n },\n ...inlines(node.children)\n );\n case 'image': {\n const src = sanitizeParsedAssetUrl(node.url);\n return src === null\n ? (node.alt ?? '')\n : createElement('img', {\n src,\n alt: node.alt ?? '',\n ...(node.title ? { title: node.title } : {}),\n });\n }\n default:\n return plainText(node);\n }\n};\n\nconst paragraphCount = (item: ListItem): number =>\n item.children.filter(({ type }) => type === 'paragraph').length;\n\n// A spread list, a spread item, or an item with two paragraphs makes the whole list loose.\nconst items = (list: List): ReactNode[] => {\n const tight =\n list.spread !== true &&\n !list.children.some(\n (item) => item.spread === true || paragraphCount(item) > 1\n );\n return list.children.map((item, index) => listItem(item, index, tight));\n};\n\n// A task item's checkbox leads its first paragraph, so a loose item keeps it on the task's line.\nconst listItem = (item: ListItem, key: number, tight: boolean): ReactNode => {\n const checkbox =\n typeof item.checked === 'boolean'\n ? [\n createElement('input', {\n type: 'checkbox',\n checked: item.checked,\n disabled: true,\n }),\n ' ',\n ]\n : [];\n const [first, ...rest] = item.children;\n const leadsWithParagraph = first?.type === 'paragraph';\n const lead = leadsWithParagraph\n ? [...checkbox, ...inlines(first.children)]\n : checkbox;\n const head =\n leadsWithParagraph && !tight ? [createElement('p', null, ...lead)] : lead;\n const body = (leadsWithParagraph ? rest : item.children).flatMap(\n (child): ReactNode[] =>\n child.type === 'paragraph' && tight\n ? inlines(child.children)\n : [block(child)]\n );\n return createElement('li', { key }, ...keyed([...head, ...body]));\n};\n\nconst tableRows = (table: Table): ReactNode => {\n const [header, ...body] = table.children;\n const cells = (row: Table['children'][number], tag: 'th' | 'td'): ReactNode =>\n createElement(\n 'tr',\n null,\n ...row.children.map((cell, index) => {\n const align = table.align?.[index];\n return createElement(\n tag,\n {\n key: index,\n ...(align ? { style: { textAlign: align } } : {}),\n },\n ...inlines(cell.children)\n );\n })\n );\n return createElement(\n Fragment,\n null,\n header\n ? createElement('thead', { key: 'head' }, cells(header, 'th'))\n : null,\n body.length > 0\n ? createElement(\n 'tbody',\n { key: 'body' },\n ...body.map((row, index) =>\n createElement(Fragment, { key: index }, cells(row, 'td'))\n )\n )\n : null\n );\n};\n\nconst blocks = (nodes: readonly RootContent[]): ReactNode[] =>\n keyed(nodes.map(block));\n\nconst block = (node: RootContent): ReactNode => {\n switch (node.type) {\n case 'paragraph':\n return createElement('p', null, ...inlines(node.children));\n case 'heading':\n return createElement(\n HEADINGS[node.depth - 1]!,\n null,\n ...inlines(node.children)\n );\n case 'blockquote':\n return createElement('blockquote', null, ...blocks(node.children));\n case 'list':\n return createElement(\n node.ordered ? 'ol' : 'ul',\n node.ordered && node.start != null && node.start !== 1\n ? { start: node.start }\n : null,\n ...items(node)\n );\n case 'code':\n return createElement(\n 'pre',\n null,\n createElement(\n 'code',\n node.lang ? { className: `language-${node.lang}` } : null,\n node.value\n )\n );\n case 'table':\n return createElement('table', null, tableRows(node));\n case 'thematicBreak':\n return createElement('hr');\n case 'html':\n return createElement('p', null, node.value);\n case 'footnoteDefinition': {\n const [first, ...rest] = node.children;\n return createElement(\n Fragment,\n null,\n createElement(\n 'p',\n null,\n `${footnoteLabel(node)}: `,\n ...(first?.type === 'paragraph' ? inlines(first.children) : [])\n ),\n ...blocks(first?.type === 'paragraph' ? rest : node.children)\n );\n }\n default:\n return null;\n }\n};\n\n/**\n * Builder content as React elements: paragraphs, headings, lists, quotes, code, tables, and thematic breaks, with inline marks, links, and images. Source from `md.authored` is parsed as GFM, once, as the Markdown and Slack surfaces read it.\n *\n * Links and images follow `md`'s URL policy: a blocked link keeps its label on {@link BLOCKED_HREF}, and a blocked image degrades to its alt text.\n */\nexport const renderMarkdownContent = (content: MarkdownContent): ReactNode =>\n createElement(\n Fragment,\n null,\n ...blocks(resolvedMarkdownBlocks(content) as RootContent[])\n );\n"]}
|
|
@@ -12,7 +12,14 @@ exports.SLACK_LIMITS = void 0;
|
|
|
12
12
|
* cannot silently trip Slack's validation.
|
|
13
13
|
*/
|
|
14
14
|
exports.SLACK_LIMITS = {
|
|
15
|
+
/** Top-level blocks; a `container`'s `child_blocks` do not count. */
|
|
15
16
|
blocksPerMessage: 50,
|
|
17
|
+
/** `container.title`, which is `plain_text`. */
|
|
18
|
+
containerTitleChars: 150,
|
|
19
|
+
/** `container.subtitle`. */
|
|
20
|
+
containerSubtitleChars: 150,
|
|
21
|
+
/** `container.child_blocks`; Slack also requires at least one. */
|
|
22
|
+
containerChildBlocks: 10,
|
|
16
23
|
/** `section.text`. */
|
|
17
24
|
sectionTextChars: 3000,
|
|
18
25
|
/** One entry of `section.fields`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks.js","sourceRoot":"","sources":["../../../../src/render/slack/blocks.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;
|
|
1
|
+
{"version":3,"file":"blocks.js","sourceRoot":"","sources":["../../../../src/render/slack/blocks.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AA+CH;;;GAGG;AACU,QAAA,YAAY,GAAG;IAC1B,qEAAqE;IACrE,gBAAgB,EAAE,EAAE;IACpB,gDAAgD;IAChD,mBAAmB,EAAE,GAAG;IACxB,4BAA4B;IAC5B,sBAAsB,EAAE,GAAG;IAC3B,kEAAkE;IAClE,oBAAoB,EAAE,EAAE;IACxB,sBAAsB;IACtB,gBAAgB,EAAE,IAAI;IACtB,qCAAqC;IACrC,iBAAiB,EAAE,IAAI;IACvB,uCAAuC;IACvC,mBAAmB,EAAE,IAAI;IACzB,4CAA4C;IAC5C,eAAe,EAAE,GAAG;IACpB,gBAAgB,EAAE,EAAE;IACpB,oCAAoC;IACpC,eAAe,EAAE,EAAE;IACnB,iBAAiB,EAAE,EAAE;IACrB,kFAAkF;IAClF,iBAAiB,EAAE,IAAI;IACvB,yEAAyE;IACzE,eAAe,EAAE,EAAE;IACnB,sDAAsD;IACtD,gBAAgB,EAAE,GAAG;IACrB,4DAA4D;IAC5D,kBAAkB,EAAE,CAAC;IACrB,4DAA4D;IAC5D,gBAAgB,EAAE,EAAE;IACpB,+EAA+E;IAC/E,qBAAqB,EAAE,GAAG;IAC1B,yDAAyD;IACzD,gBAAgB,EAAE,GAAG;IACrB,0BAA0B;IAC1B,gBAAgB,EAAE,IAAI;IACtB,6CAA6C;IAC7C,QAAQ,EAAE,IAAI;IACd,4CAA4C;IAC5C,aAAa,EAAE,GAAG;IAClB,6CAA6C;IAC7C,qBAAqB,EAAE,CAAC;IACxB;;;;;OAKG;IACH,wBAAwB,EAAE,MAAM;IAChC,wDAAwD;IACxD,SAAS,EAAE,GAAG;IACd,iDAAiD;IACjD,YAAY,EAAE,EAAE;IAChB,oDAAoD;IACpD,aAAa,EAAE,IAAI;IACnB,mDAAmD;IACnD,iBAAiB,EAAE,IAAI;IACvB,6BAA6B;IAC7B,eAAe,EAAE,IAAI;IACrB,4DAA4D;IAC5D,eAAe,EAAE,GAAG;IACpB,kEAAkE;IAClE,qBAAqB,EAAE,GAAG;IAC1B,iEAAiE;IACjE,oBAAoB,EAAE,EAAE;IACxB,+BAA+B;IAC/B,eAAe,EAAE,EAAE;IACnB,4BAA4B;IAC5B,gBAAgB,EAAE,GAAG;IACrB,+BAA+B;IAC/B,qBAAqB,EAAE,EAAE;CACjB,CAAC","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\nexport type {\n SlackActionElement,\n SlackActionsBlock,\n SlackBlock,\n SlackButtonElement,\n SlackCheckboxesElement,\n SlackContainerBlock,\n SlackContainerChildBlock,\n SlackContextBlock,\n SlackDividerBlock,\n SlackFileReference,\n SlackHeaderBlock,\n SlackHeaderLevel,\n SlackImageBlock,\n SlackImageElement,\n SlackMrkdwnTextObject,\n SlackMultiStaticSelectElement,\n SlackOptionGroup,\n SlackOptionObject,\n SlackOverflowElement,\n SlackPlainTextObject,\n SlackRadioButtonsElement,\n SlackRawTextElement,\n SlackRichTextBlock,\n SlackRichTextBlockElement,\n SlackRichTextInline,\n SlackRichTextLink,\n SlackRichTextList,\n SlackRichTextPreformatted,\n SlackRichTextQuote,\n SlackRichTextSection,\n SlackRichTextStyle,\n SlackRichTextTag,\n SlackRichTextText,\n SlackSectionAccessory,\n SlackSectionBlock,\n SlackStaticSelectElement,\n SlackTableBlock,\n SlackTableCell,\n SlackTableColumnSetting,\n SlackTagColor,\n SlackTextObject,\n SlackVideoBlock,\n} from '../../define/slack_blocks';\n\n/**\n * Slack's published surface limits, enforced by the renderer so a composition\n * cannot silently trip Slack's validation.\n */\nexport const SLACK_LIMITS = {\n /** Top-level blocks; a `container`'s `child_blocks` do not count. */\n blocksPerMessage: 50,\n /** `container.title`, which is `plain_text`. */\n containerTitleChars: 150,\n /** `container.subtitle`. */\n containerSubtitleChars: 150,\n /** `container.child_blocks`; Slack also requires at least one. */\n containerChildBlocks: 10,\n /** `section.text`. */\n sectionTextChars: 3000,\n /** One entry of `section.fields`. */\n sectionFieldChars: 2000,\n /** One entry of `context.elements`. */\n contextElementChars: 2000,\n /** `header.text`, which is `plain_text`. */\n headerTextChars: 150,\n fieldsPerSection: 10,\n /** Elements per `context` block. */\n contextElements: 10,\n buttonsPerActions: 25,\n /** Slack truncates the top-level `text` fallback in notifications around here. */\n fallbackTextChars: 4000,\n /** `plain_text` option labels (selects, radio, checkboxes, overflow). */\n optionTextChars: 75,\n /** Options per select, or per option group of one. */\n optionsPerSelect: 100,\n /** Options per overflow menu (Slack caps overflow at 5). */\n optionsPerOverflow: 5,\n /** Options per radio button group or checkboxes element. */\n optionsPerChoice: 10,\n /** Option groups per select, each holding up to `optionsPerSelect` options. */\n optionGroupsPerSelect: 100,\n /** An option's `value`, which interactions send back. */\n optionValueChars: 150,\n /** A button's `value`. */\n buttonValueChars: 2000,\n /** A button's or overflow option's `url`. */\n urlChars: 3000,\n /** An interactive element's `action_id`. */\n actionIdChars: 255,\n /** Max indent level for `rich_text_list`. */\n richTextListMaxIndent: 8,\n /**\n * Characters across every table cell in one message. Slack publishes the\n * same 10,000 figure per table and per message, so the message-wide\n * aggregate is the binding one.\n * https://docs.slack.dev/reference/block-kit/blocks/table-block/\n */\n tableCellCharsPerMessage: 10_000,\n /** Rows per `table` block (includes the header row). */\n tableRows: 100,\n /** Cells per `table` row / columns per table. */\n tableColumns: 20,\n /** `image`/image-element `image_url` max length. */\n imageUrlChars: 3000,\n /** `image`/image-element `alt_text` max length. */\n imageAltTextChars: 2000,\n /** `image` block `title`. */\n imageTitleChars: 2000,\n /** `video` block `title`; Slack requires fewer than 200. */\n videoTitleChars: 199,\n /** `video` block `description`; Slack requires fewer than 200. */\n videoDescriptionChars: 199,\n /** `video` block `author_name`; Slack requires fewer than 50. */\n videoAuthorNameChars: 49,\n /** `button` element `text`. */\n buttonTextChars: 75,\n /** Select `placeholder`. */\n placeholderChars: 150,\n /** `option_groups[].label`. */\n optionGroupLabelChars: 75,\n} as const;\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
4
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
5
|
+
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
6
|
+
* 2.0.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.slackContainer = exports.containerWithChildren = exports.unwrapContainer = exports.isContainer = void 0;
|
|
10
|
+
const blocks_1 = require("./blocks");
|
|
11
|
+
const format_1 = require("./format");
|
|
12
|
+
const unmodeled_1 = require("./unmodeled");
|
|
13
|
+
/** A container Isomer fits, as opposed to one passed through `unmodeledSlackBlock`. */
|
|
14
|
+
const isContainer = (block) => block.type === 'container' && !(0, unmodeled_1.isUnmodeledSlackBlock)(block);
|
|
15
|
+
exports.isContainer = isContainer;
|
|
16
|
+
/** A container as the blocks it holds, led by its title and subtitle. */
|
|
17
|
+
const unwrapContainer = ({ title, rich_text_title, subtitle, child_blocks }, level) => [
|
|
18
|
+
...(rich_text_title
|
|
19
|
+
? [rich_text_title]
|
|
20
|
+
: title
|
|
21
|
+
? [
|
|
22
|
+
{
|
|
23
|
+
type: 'header',
|
|
24
|
+
text: title,
|
|
25
|
+
...(level === undefined ? {} : { level }),
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
: []),
|
|
29
|
+
...(subtitle ? [{ type: 'context', elements: [subtitle] }] : []),
|
|
30
|
+
...child_blocks,
|
|
31
|
+
];
|
|
32
|
+
exports.unwrapContainer = unwrapContainer;
|
|
33
|
+
/**
|
|
34
|
+
* `container` holding `children`, with any nested container unwrapped, since
|
|
35
|
+
* Slack rejects one. A container left with no children, or more than
|
|
36
|
+
* `containerChildBlocks`, is unwrapped itself.
|
|
37
|
+
*/
|
|
38
|
+
const containerWithChildren = (container, children, level) => {
|
|
39
|
+
const child_blocks = children.flatMap((child) => (0, exports.isContainer)(child) ? (0, exports.unwrapContainer)(child) : [child]);
|
|
40
|
+
const refitted = { ...container, child_blocks };
|
|
41
|
+
return child_blocks.length === 0 ||
|
|
42
|
+
child_blocks.length > blocks_1.SLACK_LIMITS.containerChildBlocks
|
|
43
|
+
? (0, exports.unwrapContainer)(refitted, level)
|
|
44
|
+
: [refitted];
|
|
45
|
+
};
|
|
46
|
+
exports.containerWithChildren = containerWithChildren;
|
|
47
|
+
/**
|
|
48
|
+
* A titled section as one Block Kit `container`, from its title and the
|
|
49
|
+
* blocks its body rendered to, typically through `scope.renderSlack`.
|
|
50
|
+
*
|
|
51
|
+
* A nested container among `children` is unwrapped to its title and blocks.
|
|
52
|
+
* With no children, or more than `SLACK_LIMITS.containerChildBlocks`, the
|
|
53
|
+
* section falls back to a `header` at `fallbackLevel` followed by the blocks.
|
|
54
|
+
*/
|
|
55
|
+
const slackContainer = (title, children, { fallbackLevel, ...fields } = {}) => (0, exports.containerWithChildren)({
|
|
56
|
+
type: 'container',
|
|
57
|
+
title: { type: 'plain_text', text: (0, format_1.formatHeaderText)(title), emoji: true },
|
|
58
|
+
...fields,
|
|
59
|
+
child_blocks: [],
|
|
60
|
+
}, children, fallbackLevel);
|
|
61
|
+
exports.slackContainer = slackContainer;
|
|
62
|
+
//# sourceMappingURL=container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../../../src/render/slack/container.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,qCAMkB;AAClB,qCAA4C;AAC5C,2CAAoD;AAgBpD,uFAAuF;AAChF,MAAM,WAAW,GAAG,CAAC,KAAiB,EAAgC,EAAE,CAC7E,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,IAAA,iCAAqB,EAAC,KAAK,CAAC,CAAC;AADjD,QAAA,WAAW,eACsC;AAE9D,yEAAyE;AAClE,MAAM,eAAe,GAAG,CAC7B,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAuB,EACvE,KAAwB,EACI,EAAE,CAAC;IAC/B,GAAG,CAAC,eAAe;QACjB,CAAC,CAAC,CAAC,eAAe,CAAC;QACnB,CAAC,CAAC,KAAK;YACL,CAAC,CAAC;gBACE;oBACE,IAAI,EAAE,QAAiB;oBACvB,IAAI,EAAE,KAAK;oBACX,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;iBAC1C;aACF;YACH,CAAC,CAAC,EAAE,CAAC;IACT,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAkB,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,GAAG,YAAY;CAChB,CAAC;AAjBW,QAAA,eAAe,mBAiB1B;AAEF;;;;GAIG;AACI,MAAM,qBAAqB,GAAG,CACnC,SAA8B,EAC9B,QAA+B,EAC/B,KAAwB,EACV,EAAE;IAChB,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9C,IAAA,mBAAW,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAA,uBAAe,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CACtD,CAAC;IACF,MAAM,QAAQ,GAAG,EAAE,GAAG,SAAS,EAAE,YAAY,EAAE,CAAC;IAChD,OAAO,YAAY,CAAC,MAAM,KAAK,CAAC;QAC9B,YAAY,CAAC,MAAM,GAAG,qBAAY,CAAC,oBAAoB;QACvD,CAAC,CAAC,IAAA,uBAAe,EAAC,QAAQ,EAAE,KAAK,CAAC;QAClC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AACjB,CAAC,CAAC;AAbW,QAAA,qBAAqB,yBAahC;AAEF;;;;;;;GAOG;AACI,MAAM,cAAc,GAAG,CAC5B,KAAa,EACb,QAA+B,EAC/B,EAAE,aAAa,EAAE,GAAG,MAAM,KAA4B,EAAE,EAC1C,EAAE,CAChB,IAAA,6BAAqB,EACnB;IACE,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,IAAA,yBAAgB,EAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;IACzE,GAAG,MAAM;IACT,YAAY,EAAE,EAAE;CACjB,EACD,QAAQ,EACR,aAAa,CACd,CAAC;AAdS,QAAA,cAAc,kBAcvB","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\nimport {\n SLACK_LIMITS,\n type SlackBlock,\n type SlackContainerBlock,\n type SlackContainerChildBlock,\n type SlackHeaderLevel,\n} from './blocks';\nimport { formatHeaderText } from './format';\nimport { isUnmodeledSlackBlock } from './unmodeled';\n\n/** Options for {@link slackContainer}: the container's optional fields, and the level of the `header` it falls back to. */\nexport type SlackContainerOptions = Partial<\n Pick<\n SlackContainerBlock,\n | 'block_id'\n | 'subtitle'\n | 'icon'\n | 'width'\n | 'is_collapsible'\n | 'default_collapsed'\n | 'has_header_divider'\n >\n> & { fallbackLevel?: SlackHeaderLevel };\n\n/** A container Isomer fits, as opposed to one passed through `unmodeledSlackBlock`. */\nexport const isContainer = (block: SlackBlock): block is SlackContainerBlock =>\n block.type === 'container' && !isUnmodeledSlackBlock(block);\n\n/** A container as the blocks it holds, led by its title and subtitle. */\nexport const unwrapContainer = (\n { title, rich_text_title, subtitle, child_blocks }: SlackContainerBlock,\n level?: SlackHeaderLevel\n): SlackContainerChildBlock[] => [\n ...(rich_text_title\n ? [rich_text_title]\n : title\n ? [\n {\n type: 'header' as const,\n text: title,\n ...(level === undefined ? {} : { level }),\n },\n ]\n : []),\n ...(subtitle ? [{ type: 'context' as const, elements: [subtitle] }] : []),\n ...child_blocks,\n];\n\n/**\n * `container` holding `children`, with any nested container unwrapped, since\n * Slack rejects one. A container left with no children, or more than\n * `containerChildBlocks`, is unwrapped itself.\n */\nexport const containerWithChildren = (\n container: SlackContainerBlock,\n children: readonly SlackBlock[],\n level?: SlackHeaderLevel\n): SlackBlock[] => {\n const child_blocks = children.flatMap((child) =>\n isContainer(child) ? unwrapContainer(child) : [child]\n );\n const refitted = { ...container, child_blocks };\n return child_blocks.length === 0 ||\n child_blocks.length > SLACK_LIMITS.containerChildBlocks\n ? unwrapContainer(refitted, level)\n : [refitted];\n};\n\n/**\n * A titled section as one Block Kit `container`, from its title and the\n * blocks its body rendered to, typically through `scope.renderSlack`.\n *\n * A nested container among `children` is unwrapped to its title and blocks.\n * With no children, or more than `SLACK_LIMITS.containerChildBlocks`, the\n * section falls back to a `header` at `fallbackLevel` followed by the blocks.\n */\nexport const slackContainer = (\n title: string,\n children: readonly SlackBlock[],\n { fallbackLevel, ...fields }: SlackContainerOptions = {}\n): SlackBlock[] =>\n containerWithChildren(\n {\n type: 'container',\n title: { type: 'plain_text', text: formatHeaderText(title), emoji: true },\n ...fields,\n child_blocks: [],\n },\n children,\n fallbackLevel\n );\n"]}
|