@fluid-topics/ft-reader-context 1.0.53 → 1.0.55
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/build/ft-reader-context.d.ts +4 -40
- package/build/ft-reader-context.js +24 -51
- package/build/ft-reader-context.light.js +7 -7
- package/build/ft-reader-context.min.js +11 -11
- package/build/index.d.ts +4 -0
- package/build/index.js +4 -0
- package/build/models.d.ts +39 -0
- package/build/models.js +30 -0
- package/build/store/FtReaderStateManager.d.ts +3 -3
- package/build/store/FtReaderStateManager.js +3 -3
- package/build/store/utils/FtOfficialReaderService.d.ts +18 -0
- package/build/store/utils/FtOfficialReaderService.js +48 -0
- package/build/store/utils/FtReaderConverter.js +2 -1
- package/build/store/utils/FtReaderService.d.ts +29 -8
- package/build/store/utils/FtReaderService.js +36 -22
- package/package.json +5 -5
package/build/models.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class MapLoadedEvent extends CustomEvent {
|
|
2
|
+
constructor(map, toc) {
|
|
3
|
+
super("ft-reader-map-loaded", { detail: { map, toc } });
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
export class VisibleTopicsChangeEvent extends CustomEvent {
|
|
7
|
+
constructor(map, visibleTopics) {
|
|
8
|
+
super("ft-reader-visible-topics-change", { detail: { visibleTopics } });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class NavigationDoneEvent extends CustomEvent {
|
|
12
|
+
constructor(data) {
|
|
13
|
+
super("ft-reader-navigation-done", { detail: data });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class LoadErrorEvent extends CustomEvent {
|
|
17
|
+
constructor(e) {
|
|
18
|
+
super("ft-reader-load-error", { detail: e });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class NotFoundErrorEvent extends Event {
|
|
22
|
+
constructor() {
|
|
23
|
+
super("ft-reader-map-not-found");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class UnauthorizedErrorEvent extends Event {
|
|
27
|
+
constructor() {
|
|
28
|
+
super("ft-reader-map-unauthorized");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { FtReaderState } from "./model";
|
|
2
2
|
import { FtReduxStore } from "@fluid-topics/ft-wc-utils";
|
|
3
3
|
import { FtReaderStateReducers } from "./redux";
|
|
4
|
-
import { FtReaderService } from "./utils/FtReaderService";
|
|
4
|
+
import type { FtReaderService } from "./utils/FtReaderService";
|
|
5
5
|
export declare class FtReaderStateManager {
|
|
6
6
|
store: FtReduxStore<FtReaderState, FtReaderStateReducers>;
|
|
7
|
-
static build(id: string): FtReaderStateManager;
|
|
7
|
+
static build(id: string, serviceProvider?: () => Promise<FtReaderService>): FtReaderStateManager;
|
|
8
8
|
service?: FtReaderService;
|
|
9
9
|
errorHandler?: (e: Error) => void;
|
|
10
10
|
constructor(store: FtReduxStore<FtReaderState, FtReaderStateReducers>, serviceProvider: () => Promise<FtReaderService>);
|
|
11
|
-
|
|
11
|
+
initService(serviceProvider: () => Promise<FtReaderService>): Promise<void>;
|
|
12
12
|
setMapId(mapId?: string): Promise<void>;
|
|
13
13
|
setVisibleTopics(tocIds: string[]): void;
|
|
14
14
|
awaitService(): Promise<FtReaderService>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { CanceledPromiseError, Debouncer, delay } from "@fluid-topics/ft-wc-utils";
|
|
2
2
|
import { createReaderStore } from "./redux";
|
|
3
|
-
import {
|
|
3
|
+
import { FtOfficialReaderService } from "./utils/FtOfficialReaderService";
|
|
4
4
|
export class FtReaderStateManager {
|
|
5
|
-
static build(id) {
|
|
6
|
-
return new FtReaderStateManager(createReaderStore(id.trim() || "context"),
|
|
5
|
+
static build(id, serviceProvider) {
|
|
6
|
+
return new FtReaderStateManager(createReaderStore(id.trim() || "context"), serviceProvider !== null && serviceProvider !== void 0 ? serviceProvider : FtOfficialReaderService.build);
|
|
7
7
|
}
|
|
8
8
|
constructor(store, serviceProvider) {
|
|
9
9
|
this.store = store;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FtMap, FtPaginatedToc, FtRatingSummary, FtRatingType, FtRatingTypeKeys, FtTocNode, FtTopic } from "@fluid-topics/public-api";
|
|
2
|
+
import { FtReaderFeatures, FtReaderService } from "./FtReaderService";
|
|
3
|
+
export declare class FtOfficialReaderService extends FtReaderService {
|
|
4
|
+
static build(mapId?: string): Promise<FtOfficialReaderService>;
|
|
5
|
+
protected fetchMap(mapId: string): Promise<FtMap>;
|
|
6
|
+
protected fetchMapRating(mapId: string): Promise<FtRatingSummary>;
|
|
7
|
+
protected fetchPages(mapId: string): Promise<FtPaginatedToc>;
|
|
8
|
+
protected fetchTopicHTMLContent(tocNode: FtTocNode): Promise<string>;
|
|
9
|
+
protected fetchTopicMetadata(tocNode: FtTocNode): Promise<FtTopic>;
|
|
10
|
+
protected makeMapFeedbackRequest(message: string, from?: string): Promise<void>;
|
|
11
|
+
protected makeRateMapRequest(type: FtRatingType | FtRatingTypeKeys, value: number): Promise<void>;
|
|
12
|
+
protected makeRateTopicRequest(tocId: string, type: FtRatingType | FtRatingTypeKeys, value: number): Promise<void>;
|
|
13
|
+
protected makeTopicFeedbackRequest(tocId: string, message: string, from?: string): Promise<void>;
|
|
14
|
+
protected makeUnrateMapRequest(): Promise<void>;
|
|
15
|
+
protected makeUnrateTopicRequest(tocId: string): Promise<void>;
|
|
16
|
+
protected getReaderLinkPrefix(): string;
|
|
17
|
+
isFeatureAccessible(feature: FtReaderFeatures): boolean;
|
|
18
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CacheRegistry } from "@fluid-topics/ft-wc-utils";
|
|
2
|
+
import { FtReaderConverter } from "./FtReaderConverter";
|
|
3
|
+
import { FtReaderService } from "./FtReaderService";
|
|
4
|
+
import { FluidTopicsApiProvider } from "@fluid-topics/ft-app-context";
|
|
5
|
+
export class FtOfficialReaderService extends FtReaderService {
|
|
6
|
+
static async build(mapId) {
|
|
7
|
+
return new FtOfficialReaderService(new FtReaderConverter(), await FluidTopicsApiProvider.await(), new CacheRegistry(), mapId);
|
|
8
|
+
}
|
|
9
|
+
fetchMap(mapId) {
|
|
10
|
+
return this.api.getMap(mapId);
|
|
11
|
+
}
|
|
12
|
+
fetchMapRating(mapId) {
|
|
13
|
+
return this.api.getMapRating(mapId);
|
|
14
|
+
}
|
|
15
|
+
fetchPages(mapId) {
|
|
16
|
+
return this.api.getPages(mapId);
|
|
17
|
+
}
|
|
18
|
+
fetchTopicHTMLContent(tocNode) {
|
|
19
|
+
return this.api.getTopicHTMLContent(this.mapId, tocNode.contentId, "DESIGNED_READER");
|
|
20
|
+
}
|
|
21
|
+
fetchTopicMetadata(tocNode) {
|
|
22
|
+
return this.api.getTopic(this.mapId, tocNode.contentId);
|
|
23
|
+
}
|
|
24
|
+
makeMapFeedbackRequest(message, from) {
|
|
25
|
+
return this.api.sendMapFeedback(this.mapId, message, from);
|
|
26
|
+
}
|
|
27
|
+
makeRateMapRequest(type, value) {
|
|
28
|
+
return this.api.rateMap(this.mapId, type, value);
|
|
29
|
+
}
|
|
30
|
+
makeRateTopicRequest(tocId, type, value) {
|
|
31
|
+
return this.api.rateTopic(this.mapId, tocId, type, value);
|
|
32
|
+
}
|
|
33
|
+
makeTopicFeedbackRequest(tocId, message, from) {
|
|
34
|
+
return this.api.sendTopicFeedback(this.mapId, tocId, message, from);
|
|
35
|
+
}
|
|
36
|
+
makeUnrateMapRequest() {
|
|
37
|
+
return this.api.unrateMap(this.mapId);
|
|
38
|
+
}
|
|
39
|
+
makeUnrateTopicRequest(tocId) {
|
|
40
|
+
return this.api.unrateTopic(this.mapId, tocId);
|
|
41
|
+
}
|
|
42
|
+
getReaderLinkPrefix() {
|
|
43
|
+
return "r/";
|
|
44
|
+
}
|
|
45
|
+
isFeatureAccessible(feature) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -51,6 +51,7 @@ class EnrichedTocBuilder {
|
|
|
51
51
|
contentId: node.contentId,
|
|
52
52
|
title: node.title,
|
|
53
53
|
hasRating: node.hasRating,
|
|
54
|
+
origin: node.origin,
|
|
54
55
|
prettyUrl,
|
|
55
56
|
depth,
|
|
56
57
|
parentTocId: parentTocId,
|
|
@@ -88,7 +89,7 @@ class EnrichedTocBuilder {
|
|
|
88
89
|
number: this.pages.length + 1,
|
|
89
90
|
title: (_a = node.title) !== null && _a !== void 0 ? _a : "",
|
|
90
91
|
rootTocId: (_b = node.tocId) !== null && _b !== void 0 ? _b : "root",
|
|
91
|
-
toc: ((_c = node.pageToc) !== null && _c !== void 0 ? _c : []).map(n => this.nodeByTocId[n.tocId]),
|
|
92
|
+
toc: ((_c = node.pageToc) !== null && _c !== void 0 ? _c : []).map((n) => this.nodeByTocId[n.tocId]),
|
|
92
93
|
topics: [],
|
|
93
94
|
hiddenTopics: [],
|
|
94
95
|
breadcrumb: parentTocId ? this.buildBreadcrumb(this.nodeByTocId[parentTocId]) : []
|
|
@@ -1,12 +1,32 @@
|
|
|
1
|
-
import type { FluidTopicsApi, FtMap, FtPaginationConfiguration, FtPublicationRatingSummary, FtRatingType, FtRatingTypeKeys, FtReaderConfiguration, FtTopic } from "@fluid-topics/public-api";
|
|
1
|
+
import type { FluidTopicsApi, FtMap, FtPaginatedToc, FtPaginationConfiguration, FtPublicationRatingSummary, FtRatingSummary, FtRatingType, FtRatingTypeKeys, FtReaderConfiguration, FtTocNode, FtTopic } from "@fluid-topics/public-api";
|
|
2
2
|
import { CacheRegistry } from "@fluid-topics/ft-wc-utils";
|
|
3
3
|
import { FtPagesTocNode, FtReaderNavigationData, FtReaderPage, FtReaderTocNode, FtTopicRatingSummary } from "../model";
|
|
4
4
|
import { EnrichedToc, FtReaderConverter } from "./FtReaderConverter";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import { FtReaderBreadcrumb } from "../../models";
|
|
6
|
+
export declare enum FtReaderFeatures {
|
|
7
|
+
FEEDBACK = "FEEDBACK",
|
|
8
|
+
RATING = "RATING",
|
|
9
|
+
PRINT = "PRINT",
|
|
10
|
+
BOOKMARK = "BOOKMARK",
|
|
11
|
+
COLLECTIONS = "COLLECTIONS"
|
|
12
|
+
}
|
|
13
|
+
export declare abstract class FtReaderService {
|
|
14
|
+
protected converter: FtReaderConverter;
|
|
15
|
+
protected api: FluidTopicsApi;
|
|
16
|
+
protected cache: CacheRegistry;
|
|
17
|
+
protected abstract fetchMap(mapId: string): Promise<FtMap>;
|
|
18
|
+
protected abstract fetchPages(mapId: string): Promise<FtPaginatedToc>;
|
|
19
|
+
protected abstract fetchMapRating(mapId: string): Promise<FtRatingSummary>;
|
|
20
|
+
protected abstract fetchTopicMetadata(tocNode: FtTocNode): Promise<FtTopic>;
|
|
21
|
+
protected abstract fetchTopicHTMLContent(tocNode: FtTocNode): Promise<string>;
|
|
22
|
+
protected abstract makeRateMapRequest(type: FtRatingType | FtRatingTypeKeys, value: number): Promise<void>;
|
|
23
|
+
protected abstract makeUnrateMapRequest(): Promise<void>;
|
|
24
|
+
protected abstract makeMapFeedbackRequest(message: string, from?: string): Promise<void>;
|
|
25
|
+
protected abstract makeRateTopicRequest(tocId: string, type: FtRatingType | FtRatingTypeKeys, value: number): Promise<void>;
|
|
26
|
+
protected abstract makeUnrateTopicRequest(tocId: string): Promise<void>;
|
|
27
|
+
protected abstract makeTopicFeedbackRequest(tocId: string, message: string, from?: string): Promise<void>;
|
|
28
|
+
protected abstract getReaderLinkPrefix(): string;
|
|
29
|
+
abstract isFeatureAccessible(feature: FtReaderFeatures): boolean;
|
|
10
30
|
private _mapId;
|
|
11
31
|
get mapId(): string;
|
|
12
32
|
set mapId(value: string | undefined);
|
|
@@ -25,8 +45,8 @@ export declare class FtReaderService {
|
|
|
25
45
|
getTocNodeNow(tocId: string): FtReaderTocNode | undefined;
|
|
26
46
|
getPage(number: number): Promise<FtReaderPage>;
|
|
27
47
|
getPageByTocId(tocId: string): Promise<FtReaderPage>;
|
|
28
|
-
getTopicInfo(
|
|
29
|
-
getTopicContent(
|
|
48
|
+
getTopicInfo(tocNode: FtTocNode): Promise<FtTopic>;
|
|
49
|
+
getTopicContent(tocNode: FtTocNode): Promise<string>;
|
|
30
50
|
getLink(tocId?: string, pageNumber?: number, section?: string): string;
|
|
31
51
|
getNavigationData(tocId?: string, pageNumber?: number, section?: string): FtReaderNavigationData;
|
|
32
52
|
getAccurateNavigationData(tocId?: string, pageNumber?: number, section?: string): Promise<FtReaderNavigationData>;
|
|
@@ -42,4 +62,5 @@ export declare class FtReaderService {
|
|
|
42
62
|
sendTopicFeedback(tocId: string, message: string, from?: string): Promise<void>;
|
|
43
63
|
fontsStylesheet(): string;
|
|
44
64
|
themeStylesheet(): string;
|
|
65
|
+
buildBreadcrumb(tocId: string): Array<FtReaderBreadcrumb>;
|
|
45
66
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { userHasRole } from "@fluid-topics/public-api";
|
|
2
|
+
import { ftAppInfoStore } from "@fluid-topics/ft-app-context";
|
|
3
|
+
export var FtReaderFeatures;
|
|
4
|
+
(function (FtReaderFeatures) {
|
|
5
|
+
FtReaderFeatures["FEEDBACK"] = "FEEDBACK";
|
|
6
|
+
FtReaderFeatures["RATING"] = "RATING";
|
|
7
|
+
FtReaderFeatures["PRINT"] = "PRINT";
|
|
8
|
+
FtReaderFeatures["BOOKMARK"] = "BOOKMARK";
|
|
9
|
+
FtReaderFeatures["COLLECTIONS"] = "COLLECTIONS";
|
|
10
|
+
})(FtReaderFeatures || (FtReaderFeatures = {}));
|
|
4
11
|
export class FtReaderService {
|
|
5
|
-
static async build(mapId) {
|
|
6
|
-
return new FtReaderService(new FtReaderConverter(), await FluidTopicsApiProvider.await(), new CacheRegistry(), mapId);
|
|
7
|
-
}
|
|
8
12
|
get mapId() {
|
|
9
13
|
return this._mapId;
|
|
10
14
|
}
|
|
@@ -26,12 +30,12 @@ export class FtReaderService {
|
|
|
26
30
|
}
|
|
27
31
|
initCache() {
|
|
28
32
|
this.cache.registerFinal("configuration", () => this.api.getReaderConfiguration());
|
|
29
|
-
this.cache.register("map", () => this.withMapId(mapId => this.
|
|
30
|
-
this.cache.register("enrichedToc", () => this.withMapId(mapId => this.
|
|
33
|
+
this.cache.register("map", () => this.withMapId(mapId => this.fetchMap(mapId)));
|
|
34
|
+
this.cache.register("enrichedToc", () => this.withMapId(mapId => this.fetchPages(mapId).then(toc => this.converter.convertPaginatedToc(toc))));
|
|
31
35
|
this.cache.register("ratingSummary", () => {
|
|
32
|
-
var _a;
|
|
33
|
-
const userHaveRatingRole = (_a = ftAppInfoStore.getState().session) === null || _a === void 0 ? void 0 : _a.profile.roles
|
|
34
|
-
return userHaveRatingRole ? this.withMapId(mapId => this.
|
|
36
|
+
var _a, _b;
|
|
37
|
+
const userHaveRatingRole = userHasRole((_b = (_a = ftAppInfoStore.getState().session) === null || _a === void 0 ? void 0 : _a.profile.roles) !== null && _b !== void 0 ? _b : [], "RATING_USER");
|
|
38
|
+
return userHaveRatingRole ? this.withMapId(mapId => this.fetchMapRating(mapId)) : Promise.resolve();
|
|
35
39
|
});
|
|
36
40
|
}
|
|
37
41
|
clear() {
|
|
@@ -77,15 +81,15 @@ export class FtReaderService {
|
|
|
77
81
|
const enrichedToc = await this.getEnrichedToc();
|
|
78
82
|
return enrichedToc.pageByTocId[tocId] || enrichedToc.pages[0];
|
|
79
83
|
}
|
|
80
|
-
getTopicInfo(
|
|
81
|
-
return this.cache.get("topic-" +
|
|
84
|
+
getTopicInfo(tocNode) {
|
|
85
|
+
return this.cache.get("topic-" + tocNode.tocId, () => this.fetchTopicMetadata(tocNode));
|
|
82
86
|
}
|
|
83
|
-
getTopicContent(
|
|
84
|
-
return this.cache.get("topic-content-" +
|
|
87
|
+
getTopicContent(tocNode) {
|
|
88
|
+
return this.cache.get("topic-content-" + tocNode.tocId, () => this.fetchTopicHTMLContent(tocNode));
|
|
85
89
|
}
|
|
86
90
|
getLink(tocId, pageNumber, section) {
|
|
87
91
|
const navigationData = this.getNavigationData(tocId, pageNumber, section);
|
|
88
|
-
const path =
|
|
92
|
+
const path = this.getReaderLinkPrefix() + (navigationData.prettyUrl ? navigationData.prettyUrl : `${navigationData.mapId}/${navigationData.tocId}`);
|
|
89
93
|
const url = new URL(path, this.api.tenantBaseUrl);
|
|
90
94
|
if (navigationData.page != null) {
|
|
91
95
|
url.searchParams.set("page", `${navigationData.page}`);
|
|
@@ -135,19 +139,19 @@ export class FtReaderService {
|
|
|
135
139
|
});
|
|
136
140
|
}
|
|
137
141
|
rateMap(type, value) {
|
|
138
|
-
return this.
|
|
142
|
+
return this.makeRateMapRequest(type, value).then(() => this.clearRatingIfNecessary());
|
|
139
143
|
}
|
|
140
144
|
unrateMap() {
|
|
141
|
-
return this.
|
|
145
|
+
return this.makeUnrateMapRequest().then(() => this.clearRatingIfNecessary());
|
|
142
146
|
}
|
|
143
147
|
sendMapFeedback(message, from) {
|
|
144
|
-
return this.
|
|
148
|
+
return this.makeMapFeedbackRequest(message, from);
|
|
145
149
|
}
|
|
146
150
|
rateTopic(tocId, type, value) {
|
|
147
|
-
return this.
|
|
151
|
+
return this.makeRateTopicRequest(tocId, type, value).then(() => this.clearRatingIfNecessary());
|
|
148
152
|
}
|
|
149
153
|
unrateTopic(tocId) {
|
|
150
|
-
return this.
|
|
154
|
+
return this.makeUnrateTopicRequest(tocId).then(() => this.clearRatingIfNecessary());
|
|
151
155
|
}
|
|
152
156
|
async clearRatingIfNecessary() {
|
|
153
157
|
const session = ftAppInfoStore.getState().session;
|
|
@@ -156,7 +160,7 @@ export class FtReaderService {
|
|
|
156
160
|
}
|
|
157
161
|
}
|
|
158
162
|
sendTopicFeedback(tocId, message, from) {
|
|
159
|
-
return this.
|
|
163
|
+
return this.makeTopicFeedbackRequest(tocId, message, from);
|
|
160
164
|
}
|
|
161
165
|
fontsStylesheet() {
|
|
162
166
|
return this.api.makeAbsolute(this.api.endpoints.stylesheets.fonts);
|
|
@@ -164,4 +168,14 @@ export class FtReaderService {
|
|
|
164
168
|
themeStylesheet() {
|
|
165
169
|
return this.api.makeAbsolute(this.api.endpoints.stylesheets.theme);
|
|
166
170
|
}
|
|
171
|
+
buildBreadcrumb(tocId) {
|
|
172
|
+
let target = this.getTocNodeNow(tocId);
|
|
173
|
+
let breadcrumb = [{ tocId: target.tocId, title: target.title }];
|
|
174
|
+
let parent = this.getTocNodeNow(target.parentTocId);
|
|
175
|
+
while (parent) {
|
|
176
|
+
breadcrumb.unshift({ tocId: parent.tocId, title: parent.title });
|
|
177
|
+
parent = this.getTocNodeNow(parent.parentTocId);
|
|
178
|
+
}
|
|
179
|
+
return breadcrumb;
|
|
180
|
+
}
|
|
167
181
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-topics/ft-reader-context",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.55",
|
|
4
4
|
"description": "Context block for integrated reader components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Lit"
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"url": "ssh://git@scm.mrs.antidot.net:2222/fluidtopics/ft-web-components.git"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@fluid-topics/ft-app-context": "1.0.
|
|
23
|
-
"@fluid-topics/ft-wc-utils": "1.0.
|
|
22
|
+
"@fluid-topics/ft-app-context": "1.0.55",
|
|
23
|
+
"@fluid-topics/ft-wc-utils": "1.0.55",
|
|
24
24
|
"@reduxjs/toolkit": "^1.6.2",
|
|
25
25
|
"lit": "2.7.2"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@fluid-topics/public-api": "1.0.
|
|
28
|
+
"@fluid-topics/public-api": "1.0.45"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "4eca32ed1fec3fd25b6d76b26895a6318cfa8ca2"
|
|
31
31
|
}
|