@umami/shiso 1.11.0 → 1.13.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/bin/shiso.mjs +20 -1
- package/dist/chunks/App.js +412 -51
- package/dist/chunks/abnfDiagram-VCTEODGH.js +109 -0
- package/dist/chunks/arc.js +130 -0
- package/dist/chunks/architecture-7GRP2DOG.js +4 -0
- package/dist/chunks/architectureDiagram-5GKGNRK7.js +7466 -0
- package/dist/chunks/array.js +8 -0
- package/dist/chunks/blockDiagram-I7D4REHJ.js +2546 -0
- package/dist/chunks/c4Diagram-7LVT6UL2.js +3654 -0
- package/dist/chunks/channel.js +9 -0
- package/dist/chunks/chunk-2Q5K7J3B.js +21 -0
- package/dist/chunks/chunk-4HAMMTFA.js +6106 -0
- package/dist/chunks/chunk-5VM5RSS4.js +21 -0
- package/dist/chunks/chunk-75Z2AOVW.js +2185 -0
- package/dist/chunks/chunk-DU6HZSFF.js +8729 -0
- package/dist/chunks/chunk-F27PBJKO.js +95 -0
- package/dist/chunks/chunk-FOHPRMQF.js +27162 -0
- package/dist/chunks/chunk-GMAD6QVW.js +2369 -0
- package/dist/chunks/chunk-GVQU2GXP.js +58 -0
- package/dist/chunks/chunk-IMKFNOWR.js +2593 -0
- package/dist/chunks/chunk-JWPE2WC7.js +12 -0
- package/dist/chunks/chunk-L3NEJ4N5.js +420 -0
- package/dist/chunks/chunk-OSK3NFVY.js +1029 -0
- package/dist/chunks/chunk-P2QGCYS3.js +95 -0
- package/dist/chunks/chunk-POPQ4Y6H.js +33 -0
- package/dist/chunks/chunk-PWAF6VOD.js +467 -0
- package/dist/chunks/chunk-SHT3W25Y.js +4813 -0
- package/dist/chunks/chunk-SVP7TREG.js +686 -0
- package/dist/chunks/chunk-TICWLB2K.js +3800 -0
- package/dist/chunks/chunk-XXDRQBXY.js +12 -0
- package/dist/chunks/chunk-Y2CYZVJY.js +15 -0
- package/dist/chunks/classDiagram-ZZMXUADV.js +33 -0
- package/dist/chunks/classDiagram-v2-VYDZK3BY.js +33 -0
- package/dist/chunks/cose-bilkent-JH36ORCC.js +4268 -0
- package/dist/chunks/cynefin-OW5HDTMX.js +4 -0
- package/dist/chunks/cynefinDiagram-5FMLGOSQ.js +482 -0
- package/dist/chunks/cytoscape.esm.js +28282 -0
- package/dist/chunks/dagre-GXQ25YYZ.js +583 -0
- package/dist/chunks/dagre.js +4235 -0
- package/dist/chunks/defaultLocale.js +246 -0
- package/dist/chunks/diagram-S7CK7UJ4.js +402 -0
- package/dist/chunks/diagram-UQ7AKVKN.js +291 -0
- package/dist/chunks/diagram-VSXAHHWV.js +553 -0
- package/dist/chunks/diagram-VX7I27RA.js +798 -0
- package/dist/chunks/diagram-Z3DM3KII.js +186 -0
- package/dist/chunks/dist.js +92 -0
- package/dist/chunks/docs.js +989 -93
- package/dist/chunks/ebnfDiagram-PWID7BFC.js +123 -0
- package/dist/chunks/erDiagram-RLTQ6QDP.js +2295 -0
- package/dist/chunks/eventmodeling-NTZA5JFV.js +4 -0
- package/dist/chunks/flowDiagram-HODETNUW.js +17 -0
- package/dist/chunks/ganttDiagram-EL5Y4UJY.js +3928 -0
- package/dist/chunks/gitGraph-4MIJSDKK.js +4 -0
- package/dist/chunks/gitGraphDiagram-WWUBYQGX.js +1153 -0
- package/dist/chunks/graphlib.js +4194 -0
- package/dist/chunks/info-A6RAGUB7.js +4 -0
- package/dist/chunks/infoDiagram-27XIBGKW.js +25 -0
- package/dist/chunks/init.js +14 -0
- package/dist/chunks/ishikawaDiagram-5VMMS53U.js +971 -0
- package/dist/chunks/journeyDiagram-3NMN7TZE.js +1226 -0
- package/dist/chunks/kanban-definition-UXKFOSKX.js +1241 -0
- package/dist/chunks/katex.js +26463 -0
- package/dist/chunks/line.js +48 -0
- package/dist/chunks/linear.js +391 -0
- package/dist/chunks/mermaid-parser.core.js +754 -0
- package/dist/chunks/mermaid.core.js +4898 -0
- package/dist/chunks/mindmap-definition-YA3MSWOX.js +1309 -0
- package/dist/chunks/ordinal.js +84 -0
- package/dist/chunks/packet-AYTQ26CC.js +4 -0
- package/dist/chunks/path.js +106 -0
- package/dist/chunks/pegDiagram-XKGWAZYB.js +115 -0
- package/dist/chunks/pie-WAS4IAKB.js +4 -0
- package/dist/chunks/pieDiagram-E7YTZNPT.js +297 -0
- package/dist/chunks/quadrantDiagram-AXDQQJYC.js +2234 -0
- package/dist/chunks/radar-RG4KPBEZ.js +4 -0
- package/dist/chunks/railroad-74A4TZTK.js +4 -0
- package/dist/chunks/railroad-abnf-HS5TGJTU.js +4 -0
- package/dist/chunks/railroad-ebnf-LZEXJU2U.js +4 -0
- package/dist/chunks/railroad-peg-WCYAUIDC.js +4 -0
- package/dist/chunks/railroadDiagram-O6MQD6OU.js +89 -0
- package/dist/chunks/requirementDiagram-BXWQKSXE.js +2464 -0
- package/dist/chunks/rough.esm.js +1396 -0
- package/dist/chunks/sankeyDiagram-P5KCCOFB.js +1308 -0
- package/dist/chunks/sequenceDiagram-WJ2MYXX4.js +5445 -0
- package/dist/chunks/sizeCapture-INFHLROL.js +56 -0
- package/dist/chunks/src.js +2653 -0
- package/dist/chunks/stateDiagram-D77RDMKH.js +368 -0
- package/dist/chunks/stateDiagram-v2-MP3YSRHH.js +33 -0
- package/dist/chunks/swimlanes-42K2YHIH.js +6925 -0
- package/dist/chunks/swimlanesDiagram-VR7AAH4N.js +32 -0
- package/dist/chunks/timeline-definition-24CTP7MA.js +1507 -0
- package/dist/chunks/treeView-Q6P3EWNA.js +4 -0
- package/dist/chunks/treemap-WGGIJYW6.js +4 -0
- package/dist/chunks/vennDiagram-4TSXK5OY.js +2803 -0
- package/dist/chunks/wardley-WFR3VGLG.js +4 -0
- package/dist/chunks/wardleyDiagram-VM6X3IG4.js +877 -0
- package/dist/chunks/xychartDiagram-S5SC5T6Z.js +2698 -0
- package/dist/components.js +2 -2
- package/dist/entry-client.js +2 -2
- package/dist/entry-server.js +31 -3
- package/docs.schema.json +90 -0
- package/mdx.config.ts +13 -97
- package/package.json +6 -4
- package/scripts/build-runtime.mjs +1 -0
- package/scripts/check-content.mjs +358 -0
- package/scripts/expand-navigation-globs.mjs +277 -0
- package/scripts/expand-openapi-navigation.mjs +94 -0
- package/scripts/generate-openapi.mjs +117 -0
- package/scripts/generate-search-index.mjs +31 -1
- package/scripts/lib/openapi.mjs +653 -0
- package/scripts/load-docs-config.mjs +52 -6
- package/scripts/load-shiso-config.mjs +45 -3
- package/scripts/prerender.mjs +83 -3
- package/scripts/vite-docs-config.mjs +27 -6
- package/src/App.tsx +0 -1
- package/src/components/CodeBlock.tsx +108 -8
- package/src/components/DocContent.tsx +18 -0
- package/src/components/Docs.tsx +45 -17
- package/src/components/OpenApiOperation.tsx +205 -0
- package/src/components/SideNav.tsx +13 -2
- package/src/components/docs/Changelog.tsx +163 -0
- package/src/components/docs/CodeGroup.tsx +6 -2
- package/src/components/docs/Mermaid.tsx +164 -0
- package/src/components/docs/Panel.tsx +23 -0
- package/src/components/docs/ParamField.tsx +17 -5
- package/src/components/docs/PropertiesTable.tsx +2 -2
- package/src/components/docs/ResponseField.tsx +17 -5
- package/src/components/docs/Tiles.tsx +59 -0
- package/src/components/docs/Tree.tsx +359 -0
- package/src/components/docs/Update.tsx +72 -0
- package/src/components/docs/index.ts +7 -0
- package/src/components/docs/panel-context.tsx +25 -0
- package/src/components/docs/styles.ts +49 -0
- package/src/components/icons/index.ts +4 -0
- package/src/entry-server.tsx +50 -0
- package/src/lib/code-blocks.ts +18 -0
- package/src/lib/code-meta.ts +87 -0
- package/src/lib/docs-config.ts +4 -1
- package/src/lib/openapi.generated.ts +4 -0
- package/src/lib/openapi.ts +63 -0
- package/src/lib/rehype-shiki.ts +213 -0
- package/src/lib/site-model.ts +2 -0
- package/src/lib/types.ts +116 -2
- package/src/styles/global.css +63 -73
- package/types/components.d.ts +79 -0
- package/types/config.d.ts +11 -4
- package/vite.config.ts +49 -3
- package/CHANGELOG.md +0 -171
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** Expands { openapi } navigation entries into ordinary { page } entries. */
|
|
2
|
+
|
|
3
|
+
const OPENAPI_KEYS = new Set(['openapi']);
|
|
4
|
+
|
|
5
|
+
function isOpenApiItem(value) {
|
|
6
|
+
return !!value && typeof value === 'object' && !Array.isArray(value) && 'openapi' in value;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function assertOpenApiItem(item) {
|
|
10
|
+
const unknown = Object.keys(item).filter(key => !OPENAPI_KEYS.has(key));
|
|
11
|
+
if (unknown.length) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`Invalid navigation openapi entry: unknown ${unknown.length === 1 ? 'key' : 'keys'} ${unknown.map(key => `"${key}"`).join(', ')}.`,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
if (item.openapi !== true && (typeof item.openapi !== 'string' || !item.openapi.trim())) {
|
|
17
|
+
throw new Error('Invalid navigation openapi entry: use true or a non-empty tag name.');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** True when a navigation tree contains at least one { openapi } page entry. */
|
|
22
|
+
export function hasOpenApiItems(navigation) {
|
|
23
|
+
if (Array.isArray(navigation)) return navigation.some(hasOpenApiItems);
|
|
24
|
+
if (!navigation || typeof navigation !== 'object') return false;
|
|
25
|
+
if (isOpenApiItem(navigation)) return true;
|
|
26
|
+
return Object.values(navigation).some(hasOpenApiItems);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function pageEntry(operation, directory) {
|
|
30
|
+
return {
|
|
31
|
+
page: `${directory}/${operation.id}`,
|
|
32
|
+
title: operation.summary || `${operation.method} ${operation.path}`,
|
|
33
|
+
method: operation.method,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sortOperations(operations) {
|
|
38
|
+
return [...operations].sort(
|
|
39
|
+
(left, right) => left.path.localeCompare(right.path) || left.method.localeCompare(right.method),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function expandItem(item, { operations, directory }) {
|
|
44
|
+
assertOpenApiItem(item);
|
|
45
|
+
|
|
46
|
+
if (item.openapi === true) {
|
|
47
|
+
const tags = [];
|
|
48
|
+
for (const operation of operations) {
|
|
49
|
+
for (const tag of operation.tags) {
|
|
50
|
+
if (!tags.includes(tag)) tags.push(tag);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return tags.map(tag => ({
|
|
54
|
+
group: tag,
|
|
55
|
+
pages: sortOperations(operations.filter(operation => operation.tags.includes(tag))).map(
|
|
56
|
+
operation => pageEntry(operation, directory),
|
|
57
|
+
),
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const tag = item.openapi.trim();
|
|
62
|
+
const matched = sortOperations(operations.filter(operation => operation.tags.includes(tag)));
|
|
63
|
+
if (!matched.length) {
|
|
64
|
+
throw new Error(`Navigation openapi entry "${tag}" matched no operations in the API spec.`);
|
|
65
|
+
}
|
|
66
|
+
return matched.map(operation => pageEntry(operation, directory));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Returns a config copy whose { openapi } entries are ordinary page objects. */
|
|
70
|
+
export function expandOpenApiNavigation(config, { operations, directory }) {
|
|
71
|
+
function expandObject(value) {
|
|
72
|
+
if (Array.isArray(value)) return value.map(expandObject);
|
|
73
|
+
if (!value || typeof value !== 'object') return value;
|
|
74
|
+
|
|
75
|
+
return Object.fromEntries(
|
|
76
|
+
Object.entries(value).map(([key, child]) => {
|
|
77
|
+
if (key === 'pages' && Array.isArray(child)) {
|
|
78
|
+
const expanded = [];
|
|
79
|
+
for (const item of child) {
|
|
80
|
+
if (isOpenApiItem(item)) {
|
|
81
|
+
expanded.push(...expandItem(item, { operations, directory }));
|
|
82
|
+
} else {
|
|
83
|
+
expanded.push(expandObject(item));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return [key, expanded];
|
|
87
|
+
}
|
|
88
|
+
return [key, expandObject(child)];
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { ...config, navigation: expandObject(config.navigation) };
|
|
94
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emits .shiso/openapi.generated.ts: every normalized operation from the
|
|
3
|
+
* project's OpenAPI spec, with code samples and response examples highlighted
|
|
4
|
+
* by Shiki at build time so no highlighter ships to the browser.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { createHighlighter, hastToHtml } from 'shiki';
|
|
10
|
+
import { loadOpenApiSpec, normalizeOperations } from './lib/openapi.mjs';
|
|
11
|
+
|
|
12
|
+
// Keep in sync with DEFAULT_CODE_THEME in src/lib/code-blocks.ts (asserted by
|
|
13
|
+
// tests/openapi.test.mjs).
|
|
14
|
+
export const DEFAULT_OPENAPI_THEME = { light: 'github-light', dark: 'github-dark' };
|
|
15
|
+
|
|
16
|
+
const EMPTY_MODULE = `// Generated by @umami/shiso. Do not edit by hand.
|
|
17
|
+
import type { NormalizedOperation } from '@/lib/types';
|
|
18
|
+
|
|
19
|
+
export const OPENAPI_OPERATIONS: Record<string, NormalizedOperation> = {};
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
let highlighterPromise;
|
|
23
|
+
|
|
24
|
+
async function getHighlighter(theme) {
|
|
25
|
+
if (!highlighterPromise) {
|
|
26
|
+
highlighterPromise = createHighlighter({ themes: [theme.light, theme.dark], langs: [] });
|
|
27
|
+
}
|
|
28
|
+
return highlighterPromise;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function highlight(highlighter, source, lang, theme) {
|
|
32
|
+
if (!highlighter.getLoadedLanguages().includes(lang)) {
|
|
33
|
+
await highlighter.loadLanguage(lang);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const hast = highlighter.codeToHast(source, {
|
|
37
|
+
lang,
|
|
38
|
+
themes: theme,
|
|
39
|
+
defaultColor: false,
|
|
40
|
+
});
|
|
41
|
+
const code = hast.children[0]?.children?.[0];
|
|
42
|
+
const children = code?.children || [];
|
|
43
|
+
const lineCount = children.filter(node => node.tagName === 'span').length;
|
|
44
|
+
|
|
45
|
+
return { html: hastToHtml({ type: 'root', children }), lineCount };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function highlightOperation(operation, highlighter, theme) {
|
|
49
|
+
const samples = [];
|
|
50
|
+
for (const sample of operation.samples) {
|
|
51
|
+
samples.push({
|
|
52
|
+
...sample,
|
|
53
|
+
...(await highlight(highlighter, sample.source, sample.language, theme)),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const responses = [];
|
|
58
|
+
for (const response of operation.responses) {
|
|
59
|
+
responses.push(
|
|
60
|
+
response.example
|
|
61
|
+
? {
|
|
62
|
+
...response,
|
|
63
|
+
exampleHtml: (await highlight(highlighter, response.example, 'json', theme)).html,
|
|
64
|
+
}
|
|
65
|
+
: response,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const requestBody =
|
|
70
|
+
operation.requestBody?.example !== undefined
|
|
71
|
+
? {
|
|
72
|
+
...operation.requestBody,
|
|
73
|
+
exampleHtml: (await highlight(highlighter, operation.requestBody.example, 'json', theme))
|
|
74
|
+
.html,
|
|
75
|
+
}
|
|
76
|
+
: operation.requestBody;
|
|
77
|
+
|
|
78
|
+
return { ...operation, samples, responses, requestBody };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function writeIfChanged(output, contents) {
|
|
82
|
+
const existing = await fs.readFile(output, 'utf8').catch(() => undefined);
|
|
83
|
+
if (existing === contents) return;
|
|
84
|
+
await fs.mkdir(path.dirname(output), { recursive: true });
|
|
85
|
+
await fs.writeFile(output, contents);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Generates the openapi data module for the project, or an empty fallback. */
|
|
89
|
+
export async function generateOpenApiModule({
|
|
90
|
+
root,
|
|
91
|
+
config,
|
|
92
|
+
theme = DEFAULT_OPENAPI_THEME,
|
|
93
|
+
output,
|
|
94
|
+
}) {
|
|
95
|
+
if (!config?.api?.spec) {
|
|
96
|
+
await writeIfChanged(output, EMPTY_MODULE);
|
|
97
|
+
return { operations: 0 };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const { spec } = await loadOpenApiSpec({ root, specPath: config.api.spec });
|
|
101
|
+
const operations = normalizeOperations(spec);
|
|
102
|
+
const highlighter = await getHighlighter(theme);
|
|
103
|
+
const record = {};
|
|
104
|
+
|
|
105
|
+
for (const operation of operations) {
|
|
106
|
+
record[operation.key] = await highlightOperation(operation, highlighter, theme);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const contents = `// Generated by @umami/shiso. Do not edit by hand.
|
|
110
|
+
import type { NormalizedOperation } from '@/lib/types';
|
|
111
|
+
|
|
112
|
+
export const OPENAPI_OPERATIONS: Record<string, NormalizedOperation> = ${JSON.stringify(record, null, 2)};
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
await writeIfChanged(output, contents);
|
|
116
|
+
return { operations: operations.length };
|
|
117
|
+
}
|
|
@@ -22,6 +22,12 @@ import remarkMdx from 'remark-mdx';
|
|
|
22
22
|
import remarkParse from 'remark-parse';
|
|
23
23
|
import { unified } from 'unified';
|
|
24
24
|
import { headingText } from './lib/mdast.mjs';
|
|
25
|
+
import {
|
|
26
|
+
loadOpenApiSpec,
|
|
27
|
+
normalizeOperationKey,
|
|
28
|
+
normalizeOperations,
|
|
29
|
+
operationSearchSections,
|
|
30
|
+
} from './lib/openapi.mjs';
|
|
25
31
|
import { createSlugger, slugifyId } from './lib/slug.mjs';
|
|
26
32
|
import { loadDocsConfig } from './load-docs-config.mjs';
|
|
27
33
|
import { loadShisoConfig } from './load-shiso-config.mjs';
|
|
@@ -141,7 +147,13 @@ function collectVisiblePages(container, pages = [], hidden = false) {
|
|
|
141
147
|
return pages;
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
/** Frontmatter is YAML, but search only needs
|
|
150
|
+
/** Frontmatter is YAML, but search only needs single lines from it. */
|
|
151
|
+
function frontmatterOpenApi(tree) {
|
|
152
|
+
const yaml = tree.children?.find(node => node.type === 'yaml');
|
|
153
|
+
const match = yaml?.value?.match(/^openapi:\s*(.+)$/m);
|
|
154
|
+
return match ? match[1].trim() : undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
145
157
|
function frontmatterTitle(tree) {
|
|
146
158
|
const yaml = tree.children?.find(node => node.type === 'yaml');
|
|
147
159
|
const match = yaml?.value?.match(/^title:\s*(.+)$/m);
|
|
@@ -211,6 +223,16 @@ export async function generateSearchIndex({
|
|
|
211
223
|
const seen = new Set();
|
|
212
224
|
const records = [];
|
|
213
225
|
|
|
226
|
+
// Pages bound to an API operation get synthesized sections from the spec, so
|
|
227
|
+
// parameters and responses are searchable even though they render from data.
|
|
228
|
+
let operationsByKey;
|
|
229
|
+
if (docsJson.api?.spec) {
|
|
230
|
+
const { spec } = await loadOpenApiSpec({ root, specPath: docsJson.api.spec });
|
|
231
|
+
operationsByKey = new Map(
|
|
232
|
+
normalizeOperations(spec).map(operation => [operation.key, operation]),
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
214
236
|
for (const scope of collectScopes(docsJson.navigation || {})) {
|
|
215
237
|
// Single-scope sites omit scope fields so their index stays unchanged.
|
|
216
238
|
const scopeFields =
|
|
@@ -249,6 +271,14 @@ export async function generateSearchIndex({
|
|
|
249
271
|
for (const { heading, id, text } of collectSections(tree)) {
|
|
250
272
|
records.push({ url, page, heading, id, text, ...scopeFields });
|
|
251
273
|
}
|
|
274
|
+
|
|
275
|
+
const operationKey = normalizeOperationKey(frontmatterOpenApi(tree));
|
|
276
|
+
const operation = operationKey ? operationsByKey?.get(operationKey) : undefined;
|
|
277
|
+
if (operation) {
|
|
278
|
+
for (const { heading, id, text } of operationSearchSections(operation)) {
|
|
279
|
+
records.push({ url, page, heading, id, text, ...scopeFields });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
252
282
|
}
|
|
253
283
|
}
|
|
254
284
|
|