@quintype/framework 6.1.3-amp-logo.3 → 6.1.5-webpack5.2
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/CHANGELOG.md +4 -0
- package/package.json +46 -47
- package/server/amp/handlers/infinite-scroll.js +3 -13
- package/server/amp/handlers/story-page.js +1 -3
- package/server/amp/handlers/visual-stories-bookend.js +5 -25
- package/server/amp/helpers/get-domain-specific-opts.js +1 -3
- package/server/amp/helpers/infinite-scroll.js +4 -6
- package/server/amp/helpers/optimize-amp-html.js +1 -3
- package/server/handlers/isomorphic-handler.js +3 -11
- package/server/publisher-config.js +1 -1
- package/server/static-configuration.js +1 -1
- package/server/utils/apm.js +3 -21
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [6.1.4](https://github.com/quintype/quintype-node-framework/compare/v6.1.2...v6.1.4) (2021-10-04)
|
|
6
|
+
|
|
7
|
+
### [6.1.3](https://github.com/quintype/quintype-node-framework/compare/v6.1.2...v6.1.3) (2021-10-04)
|
|
8
|
+
|
|
5
9
|
### [6.1.2](https://github.com/quintype/quintype-node-framework/compare/v6.1.1...v6.1.2) (2021-09-17)
|
|
6
10
|
|
|
7
11
|
### [6.1.1](https://github.com/quintype/quintype-node-framework/compare/v6.1.0...v6.1.1) (2021-09-08)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quintype/framework",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.5-webpack5.2",
|
|
4
4
|
"description": "Libraries to help build Quintype Node.js apps",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,76 +26,75 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/quintype/quintype-node-framework#readme",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ampproject/toolbox-optimizer": "2.8.
|
|
30
|
-
"@quintype/amp": "^2.4.12
|
|
29
|
+
"@ampproject/toolbox-optimizer": "2.8.3",
|
|
30
|
+
"@quintype/amp": "^2.4.12",
|
|
31
31
|
"@quintype/backend": "^2.1.0",
|
|
32
|
-
"@quintype/components": "^2.31.
|
|
32
|
+
"@quintype/components": "^2.31.8-webpack5.1",
|
|
33
33
|
"@quintype/prerender-node": "^3.2.24",
|
|
34
34
|
"@quintype/seo": "^1.38.1",
|
|
35
35
|
"atob": "^2.1.2",
|
|
36
36
|
"cluster": "^0.7.7",
|
|
37
37
|
"compression": "^1.7.4",
|
|
38
|
-
"ejs": "^
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"firebase": "^6.0.2",
|
|
38
|
+
"ejs": "^3.1.6",
|
|
39
|
+
"express": "^4.17.1",
|
|
40
|
+
"firebase": "^9.1.1",
|
|
42
41
|
"get-youtube-id": "^1.0.1",
|
|
43
42
|
"grpc": "^1.21.1",
|
|
44
|
-
"http-proxy": "^1.
|
|
45
|
-
"js-yaml": "^
|
|
46
|
-
"lodash": "^4.17.
|
|
43
|
+
"http-proxy": "^1.18.1",
|
|
44
|
+
"js-yaml": "^4.1.0",
|
|
45
|
+
"lodash": "^4.17.21",
|
|
47
46
|
"mocha-snapshots": "^4.2.0",
|
|
48
|
-
"morgan": "^1.
|
|
47
|
+
"morgan": "^1.10.0",
|
|
49
48
|
"path-to-regexp": "^6.2.0",
|
|
50
|
-
"react": "^
|
|
51
|
-
"react-dom": "^
|
|
52
|
-
"react-redux": "^7.
|
|
53
|
-
"react-router": "^5.
|
|
54
|
-
"redux": "^4.
|
|
49
|
+
"react": "^17.0.2",
|
|
50
|
+
"react-dom": "^17.0.2",
|
|
51
|
+
"react-redux": "^7.2.5",
|
|
52
|
+
"react-router": "^5.2.1",
|
|
53
|
+
"redux": "^4.1.1",
|
|
55
54
|
"request-promise": "^4.2.6",
|
|
56
|
-
"sleep-promise": "^
|
|
57
|
-
"winston": "3.
|
|
55
|
+
"sleep-promise": "^9.1.0",
|
|
56
|
+
"winston": "3.3.3"
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
60
|
-
"@babel/eslint-parser": "^7.
|
|
61
|
-
"@loadable/component": "^5.
|
|
62
|
-
"@loadable/server": "^5.
|
|
63
|
-
"@quintype/build": "^3.13.
|
|
59
|
+
"@babel/eslint-parser": "^7.15.7",
|
|
60
|
+
"@loadable/component": "^5.15.0",
|
|
61
|
+
"@loadable/server": "^5.15.1",
|
|
62
|
+
"@quintype/build": "^3.13.1",
|
|
64
63
|
"babel-plugin-quintype-assets": "^1.1.1",
|
|
65
|
-
"babel-plugin-react-css-modules": "^5.2.
|
|
64
|
+
"babel-plugin-react-css-modules": "^5.2.6",
|
|
66
65
|
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
|
67
66
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
|
68
67
|
"babel-preset-es2015": "^6.24.1",
|
|
69
68
|
"babel-preset-es2015-tree-shaking": "^1.0.1",
|
|
70
69
|
"babel-preset-react": "^6.24.1",
|
|
71
70
|
"babel-register": "^6.26.0",
|
|
72
|
-
"better-docs": "^
|
|
73
|
-
"eslint": "^
|
|
74
|
-
"eslint-config-prettier": "^
|
|
75
|
-
"eslint-config-standard": "^
|
|
76
|
-
"eslint-plugin-import": "^2.
|
|
77
|
-
"eslint-plugin-node": "^
|
|
78
|
-
"eslint-plugin-prettier": "^
|
|
79
|
-
"eslint-plugin-promise": "^
|
|
80
|
-
"eslint-plugin-react": "^7.
|
|
81
|
-
"eslint-plugin-standard": "^4.0
|
|
82
|
-
"gh-pages": "^2.
|
|
83
|
-
"history": "^
|
|
84
|
-
"husky": "^
|
|
85
|
-
"jsdoc": "^3.6.
|
|
86
|
-
"jsdom": "^
|
|
71
|
+
"better-docs": "^2.3.2",
|
|
72
|
+
"eslint": "^7.32.0",
|
|
73
|
+
"eslint-config-prettier": "^8.3.0",
|
|
74
|
+
"eslint-config-standard": "^16.0.3",
|
|
75
|
+
"eslint-plugin-import": "^2.24.2",
|
|
76
|
+
"eslint-plugin-node": "^11.1.0",
|
|
77
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
78
|
+
"eslint-plugin-promise": "^5.1.0",
|
|
79
|
+
"eslint-plugin-react": "^7.26.1",
|
|
80
|
+
"eslint-plugin-standard": "^4.1.0",
|
|
81
|
+
"gh-pages": "^3.2.3",
|
|
82
|
+
"history": "^5.0.1",
|
|
83
|
+
"husky": "^7.0.2",
|
|
84
|
+
"jsdoc": "^3.6.7",
|
|
85
|
+
"jsdom": "^17.0.0",
|
|
87
86
|
"jsdom-global": "3.0.2",
|
|
88
|
-
"lint-staged": "^
|
|
89
|
-
"mocha": "^
|
|
90
|
-
"nyc": "^
|
|
91
|
-
"onchange": "^
|
|
87
|
+
"lint-staged": "^11.2.0",
|
|
88
|
+
"mocha": "^9.1.2",
|
|
89
|
+
"nyc": "^15.1.0",
|
|
90
|
+
"onchange": "^7.1.0",
|
|
92
91
|
"path": "^0.12.7",
|
|
93
|
-
"prettier": "^2.
|
|
94
|
-
"standard-version": "^
|
|
95
|
-
"supertest": "^
|
|
92
|
+
"prettier": "^2.4.1",
|
|
93
|
+
"standard-version": "^9.3.1",
|
|
94
|
+
"supertest": "^6.1.6"
|
|
96
95
|
},
|
|
97
96
|
"peerDependencies": {
|
|
98
|
-
"@quintype/seo": "^1.
|
|
97
|
+
"@quintype/seo": "^1.39.0"
|
|
99
98
|
},
|
|
100
99
|
"nyc": {
|
|
101
100
|
"exclude": [
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
const { AmpConfig } = require("../../impl/api-client-impl");
|
|
2
2
|
const InfiniteScrollAmp = require("../helpers/infinite-scroll");
|
|
3
3
|
const { setCorsHeaders } = require("../helpers");
|
|
4
|
-
const { handleSpanInstance } = require("../../utils/apm");
|
|
5
4
|
|
|
6
5
|
// eslint-disable-next-line consistent-return
|
|
7
|
-
async function storyPageInfiniteScrollHandler(
|
|
8
|
-
|
|
9
|
-
res,
|
|
10
|
-
next,
|
|
11
|
-
{ client, config }
|
|
12
|
-
) {
|
|
13
|
-
const apmInstance = handleSpanInstance({ isStart: true, title: "storyPageInfiniteScrollHandler" });
|
|
14
|
-
const ampConfig = await config.memoizeAsync(
|
|
15
|
-
"amp-config",
|
|
16
|
-
async () => await AmpConfig.getAmpConfig(client)
|
|
17
|
-
);
|
|
6
|
+
async function storyPageInfiniteScrollHandler(req, res, next, { client, config }) {
|
|
7
|
+
const ampConfig = await config.memoizeAsync("amp-config", async () => await AmpConfig.getAmpConfig(client));
|
|
18
8
|
|
|
19
9
|
const infiniteScrollAmp = new InfiniteScrollAmp({
|
|
20
10
|
ampConfig,
|
|
@@ -26,7 +16,7 @@ async function storyPageInfiniteScrollHandler(
|
|
|
26
16
|
if (jsonResponse instanceof Error) return next(jsonResponse);
|
|
27
17
|
res.set("Content-Type", "application/json; charset=utf-8");
|
|
28
18
|
setCorsHeaders({ req, res, next, publisherConfig: config });
|
|
29
|
-
|
|
19
|
+
|
|
30
20
|
if (!res.headersSent) return res.send(jsonResponse);
|
|
31
21
|
}
|
|
32
22
|
|
|
@@ -7,7 +7,6 @@ const { Story, AmpConfig } = require("../../impl/api-client-impl");
|
|
|
7
7
|
const { optimize, getDomainSpecificOpts } = require("../helpers");
|
|
8
8
|
const { storyToCacheKey } = require("../../caching");
|
|
9
9
|
const { addCacheHeadersToResult } = require("../../handlers/cdn-caching");
|
|
10
|
-
const { handleSpanInstance } = require("../../utils/apm");
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* ampStoryPageHandler gets all the things needed and calls "ampifyStory" function (which comes from ampLib)
|
|
@@ -37,7 +36,6 @@ async function ampStoryPageHandler(
|
|
|
37
36
|
}
|
|
38
37
|
) {
|
|
39
38
|
try {
|
|
40
|
-
const apmInstance = handleSpanInstance({ isStart: true, title: "ampStoryPageHandler" });
|
|
41
39
|
const opts = cloneDeep(rest);
|
|
42
40
|
const domainSpecificOpts = getDomainSpecificOpts(opts, domainSlug);
|
|
43
41
|
const url = urlLib.parse(req.url, true);
|
|
@@ -120,7 +118,7 @@ async function ampStoryPageHandler(
|
|
|
120
118
|
|
|
121
119
|
const optimizeAmpHtml = get(domainSpecificOpts, ["featureConfig", "optimizeAmpHtml"], true);
|
|
122
120
|
const finalResponse = optimizeAmpHtml ? await optimize(ampHtml) : ampHtml;
|
|
123
|
-
|
|
121
|
+
|
|
124
122
|
return res.send(finalResponse);
|
|
125
123
|
} catch (e) {
|
|
126
124
|
return next(e);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const get = require("lodash/get");
|
|
2
|
-
const { handleSpanInstance } = require("../../utils/apm");
|
|
3
2
|
|
|
4
3
|
function getStoryUrl(story, config) {
|
|
5
4
|
if (get(story, ["story-template"]) === "news-elsewhere") {
|
|
@@ -8,16 +7,7 @@ function getStoryUrl(story, config) {
|
|
|
8
7
|
return `${config["sketches-host"]}/${story.slug}`;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
async function bookendHandler(
|
|
12
|
-
req,
|
|
13
|
-
res,
|
|
14
|
-
next,
|
|
15
|
-
{ config, client, sMaxAge = "900" }
|
|
16
|
-
) {
|
|
17
|
-
const apmInstance = handleSpanInstance({
|
|
18
|
-
isStart: true,
|
|
19
|
-
title: "bookendHandler",
|
|
20
|
-
});
|
|
10
|
+
async function bookendHandler(req, res, next, { config, client, sMaxAge = "900" }) {
|
|
21
11
|
const { storyId, sectionId } = req.query;
|
|
22
12
|
if (!storyId || !sectionId) {
|
|
23
13
|
res.status(400).json({
|
|
@@ -28,10 +18,7 @@ async function bookendHandler(
|
|
|
28
18
|
return;
|
|
29
19
|
}
|
|
30
20
|
|
|
31
|
-
const relatedStoriesResponse = await client.getRelatedStories(
|
|
32
|
-
storyId,
|
|
33
|
-
sectionId
|
|
34
|
-
);
|
|
21
|
+
const relatedStoriesResponse = await client.getRelatedStories(storyId, sectionId);
|
|
35
22
|
const relatedStories = relatedStoriesResponse["related-stories"];
|
|
36
23
|
|
|
37
24
|
if (!relatedStories.length) {
|
|
@@ -39,11 +26,7 @@ async function bookendHandler(
|
|
|
39
26
|
return;
|
|
40
27
|
}
|
|
41
28
|
|
|
42
|
-
const fbAppId = get(
|
|
43
|
-
config,
|
|
44
|
-
["public-integrations", "facebook", "app-id"],
|
|
45
|
-
""
|
|
46
|
-
);
|
|
29
|
+
const fbAppId = get(config, ["public-integrations", "facebook", "app-id"], "");
|
|
47
30
|
|
|
48
31
|
const jsonPayLoad = {
|
|
49
32
|
bookendVersion: "v1.0",
|
|
@@ -85,12 +68,9 @@ async function bookendHandler(
|
|
|
85
68
|
),
|
|
86
69
|
};
|
|
87
70
|
|
|
88
|
-
res.header(
|
|
89
|
-
"Cache-Control",
|
|
90
|
-
`public,max-age=15,s-maxage=${sMaxAge},stale-while-revalidate=1000,stale-if-error=14400`
|
|
91
|
-
);
|
|
71
|
+
res.header("Cache-Control", `public,max-age=15,s-maxage=${sMaxAge},stale-while-revalidate=1000,stale-if-error=14400`);
|
|
92
72
|
res.header("Vary", "Accept-Encoding");
|
|
93
|
-
|
|
73
|
+
|
|
94
74
|
res.json(jsonPayLoad);
|
|
95
75
|
}
|
|
96
76
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const merge = require("lodash/merge");
|
|
2
2
|
const cloneDeep = require("lodash/cloneDeep");
|
|
3
|
-
const { handleSpanInstance } = require("../../utils/apm");
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Pick the correct opts for the subdomain
|
|
@@ -14,12 +13,11 @@ const { handleSpanInstance } = require("../../utils/apm");
|
|
|
14
13
|
function getDomainSpecificOpts(opts = {}, domainSlug = null) {
|
|
15
14
|
if (!domainSlug || !opts.domains || !opts.domains[domainSlug]) return opts;
|
|
16
15
|
|
|
17
|
-
const apmInstance = handleSpanInstance({ isStart: true, title: "getDomainSpecificOpts" });
|
|
18
16
|
const clone = cloneDeep(opts);
|
|
19
17
|
delete clone.domains;
|
|
20
18
|
const domainSpecificOpts = opts.domains[domainSlug];
|
|
21
19
|
const mergedOptions = merge(clone, domainSpecificOpts);
|
|
22
|
-
|
|
20
|
+
|
|
23
21
|
return mergedOptions;
|
|
24
22
|
}
|
|
25
23
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const { handleSpanInstance } = require("../../utils/apm");
|
|
2
|
-
|
|
3
1
|
class InfiniteScrollAmp {
|
|
4
2
|
constructor({ ampConfig, client, publisherConfig, queryParams }) {
|
|
5
3
|
this.client = client;
|
|
@@ -42,20 +40,20 @@ class InfiniteScrollAmp {
|
|
|
42
40
|
async getResponse({ itemsTaken }) {
|
|
43
41
|
const { "story-id": storyId } = this.queryParams;
|
|
44
42
|
if (!storyId) return new Error(`Query param "story-id" missing`);
|
|
45
|
-
|
|
43
|
+
|
|
46
44
|
const collection = await this.client.getCollectionBySlug(this.collSlug);
|
|
47
45
|
if (!collection || collection.error)
|
|
48
46
|
return new Error(`Infinite scroll collection ${this.collSlug} returned falsy value`);
|
|
49
47
|
const filteredItems = this.getFilteredCollItems(collection, storyId);
|
|
50
48
|
const slicedItems = filteredItems.slice(itemsTaken);
|
|
51
49
|
const formattedData = this.formatData({ itemsArr: slicedItems });
|
|
52
|
-
|
|
50
|
+
|
|
53
51
|
return JSON.stringify(formattedData);
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
async getInitialInlineConfig({ itemsToTake, storyId }) {
|
|
57
55
|
if (!itemsToTake || !storyId) return new Error("Required params for getInitialInlineConfig missing");
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
const collection = await this.client.getCollectionBySlug(this.collSlug);
|
|
60
58
|
if (!collection || (collection.items && !collection.items.length) || collection.error) return null;
|
|
61
59
|
const filteredItems = this.getFilteredCollItems(collection, storyId);
|
|
@@ -64,7 +62,7 @@ class InfiniteScrollAmp {
|
|
|
64
62
|
itemsArr: slicedItems,
|
|
65
63
|
type: "inline",
|
|
66
64
|
});
|
|
67
|
-
|
|
65
|
+
|
|
68
66
|
return JSON.stringify(formattedData);
|
|
69
67
|
}
|
|
70
68
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const AmpOptimizer = require("@ampproject/toolbox-optimizer");
|
|
2
|
-
const { handleSpanInstance } = require("../../utils/apm");
|
|
3
2
|
|
|
4
3
|
const ampOptimizer = AmpOptimizer.create({
|
|
5
4
|
autoAddMandatoryTags: false,
|
|
@@ -8,9 +7,8 @@ const ampOptimizer = AmpOptimizer.create({
|
|
|
8
7
|
});
|
|
9
8
|
|
|
10
9
|
async function optimize(ampHtml) {
|
|
11
|
-
const apmInstance = handleSpanInstance({ isStart: true, title: "optimize amp html" });
|
|
12
10
|
const optimizedAmp = ampOptimizer.transformHtml(ampHtml);
|
|
13
|
-
|
|
11
|
+
|
|
14
12
|
return optimizedAmp;
|
|
15
13
|
}
|
|
16
14
|
|
|
@@ -15,7 +15,7 @@ const { customUrlToCacheKey } = require("../caching");
|
|
|
15
15
|
const { addLightPageHeaders } = require("../impl/light-page-impl");
|
|
16
16
|
const { getOneSignalScript } = require("./onesignal-script");
|
|
17
17
|
const { getRedirectUrl } = require("../redirect-url-helper");
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
const ABORT_HANDLER = "__ABORT__";
|
|
20
20
|
function abortHandler() {
|
|
21
21
|
return Promise.resolve({ pageType: ABORT_HANDLER, [ABORT_HANDLER]: true });
|
|
@@ -420,10 +420,6 @@ exports.handleIsomorphicRoute = function handleIsomorphicRoute(
|
|
|
420
420
|
sMaxAge,
|
|
421
421
|
}
|
|
422
422
|
) {
|
|
423
|
-
const apmInstance = handleSpanInstance({
|
|
424
|
-
isStart: true,
|
|
425
|
-
title: "handleIsomorphicRoute",
|
|
426
|
-
});
|
|
427
423
|
const url = urlLib.parse(req.url, true);
|
|
428
424
|
|
|
429
425
|
function writeResponse(result) {
|
|
@@ -487,7 +483,7 @@ exports.handleIsomorphicRoute = function handleIsomorphicRoute(
|
|
|
487
483
|
if (typeof redirectUrls === "function" || (redirectUrls && Object.keys(redirectUrls).length > 0)) {
|
|
488
484
|
getRedirectUrl(req, res, next, { redirectUrls, config });
|
|
489
485
|
}
|
|
490
|
-
|
|
486
|
+
|
|
491
487
|
return loadDataForIsomorphicRoute(loadData, loadErrorData, url, generateRoutes(config, domainSlug), {
|
|
492
488
|
config,
|
|
493
489
|
client,
|
|
@@ -538,13 +534,9 @@ exports.handleStaticRoute = function handleStaticRoute(
|
|
|
538
534
|
sMaxAge,
|
|
539
535
|
}
|
|
540
536
|
) {
|
|
541
|
-
const apmInstance = handleSpanInstance({
|
|
542
|
-
isStart: true,
|
|
543
|
-
title: "handleStaticRoute",
|
|
544
|
-
});
|
|
545
537
|
const url = urlLib.parse(path);
|
|
546
538
|
pageType = pageType || "static-page";
|
|
547
|
-
|
|
539
|
+
|
|
548
540
|
return loadDataForPageType(loadData, loadErrorData, pageType, renderParams, {
|
|
549
541
|
config,
|
|
550
542
|
client,
|
package/server/utils/apm.js
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* * © [2015 - 2020] Quintype Technologies India Private Limited
|
|
4
|
-
* * All Rights Reserved.
|
|
5
|
-
* *************************************************************************
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const apm = require("elastic-apm-node");
|
|
9
|
-
|
|
10
|
-
const handleSpanInstance = ({ apmInstance, isStart, title }) => {
|
|
11
|
-
if (!process.env.APM_SERVICE_NAME && !process.env.APM_SECRET_TOKEN) {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (isStart && !apmInstance) {
|
|
16
|
-
return apm.startSpan(title);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (apmInstance) apmInstance.end();
|
|
20
|
-
return true;
|
|
1
|
+
const handleSpanInstance = (_) => {
|
|
2
|
+
return true;
|
|
21
3
|
};
|
|
22
4
|
|
|
23
5
|
module.exports = {
|
|
24
|
-
|
|
6
|
+
handleSpanInstance
|
|
25
7
|
};
|