decap-cms-lib-util 3.0.4 → 3.2.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/CHANGELOG.md +12 -0
- package/dist/decap-cms-lib-util.js +1 -1
- package/dist/decap-cms-lib-util.js.map +1 -1
- package/dist/esm/API.js +28 -41
- package/dist/esm/APIError.js +3 -10
- package/dist/esm/APIUtils.js +10 -23
- package/dist/esm/AccessTokenError.js +3 -10
- package/dist/esm/Cursor.js +12 -19
- package/dist/esm/EditorialWorkflowError.js +3 -10
- package/dist/esm/asyncLock.js +5 -12
- package/dist/esm/backendUtil.js +19 -31
- package/dist/esm/getBlobSHA.js +4 -11
- package/dist/esm/git-lfs.js +17 -28
- package/dist/esm/implementation.js +21 -37
- package/dist/esm/index.js +71 -409
- package/dist/esm/loadScript.js +1 -7
- package/dist/esm/localForage.js +4 -11
- package/dist/esm/path.js +4 -13
- package/dist/esm/promise.js +5 -14
- package/dist/esm/stega.js +129 -0
- package/dist/esm/types/semaphore.d.js +0 -1
- package/dist/esm/types.js +7 -0
- package/dist/esm/unsentRequest.js +13 -20
- package/package.json +2 -2
- package/src/API.ts +6 -4
- package/src/stega.ts +134 -0
- package/src/types.ts +9 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { vercelStegaEncode } from '@vercel/stega';
|
|
2
|
+
import { isImmutableMap, isImmutableList } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Context passed to encode functions, containing the current state of the encoding process
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the fields that should be used for encoding nested values
|
|
10
|
+
*/
|
|
11
|
+
function getNestedFields(f) {
|
|
12
|
+
if (f) {
|
|
13
|
+
if ('types' in f) {
|
|
14
|
+
var _f$types;
|
|
15
|
+
return (_f$types = f.types) !== null && _f$types !== void 0 ? _f$types : [];
|
|
16
|
+
}
|
|
17
|
+
if ('fields' in f) {
|
|
18
|
+
var _f$fields;
|
|
19
|
+
return (_f$fields = f.fields) !== null && _f$fields !== void 0 ? _f$fields : [];
|
|
20
|
+
}
|
|
21
|
+
if ('field' in f) {
|
|
22
|
+
return f.field ? [f.field] : [];
|
|
23
|
+
}
|
|
24
|
+
return [f];
|
|
25
|
+
}
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Encode a string value by appending steganographic data
|
|
31
|
+
* For markdown fields, encode each paragraph separately
|
|
32
|
+
*/
|
|
33
|
+
function encodeString(value, {
|
|
34
|
+
fields,
|
|
35
|
+
path
|
|
36
|
+
}) {
|
|
37
|
+
var _fields$;
|
|
38
|
+
const stega = vercelStegaEncode({
|
|
39
|
+
decap: path
|
|
40
|
+
});
|
|
41
|
+
const isMarkdown = ((_fields$ = fields[0]) === null || _fields$ === void 0 ? void 0 : _fields$.widget) === 'markdown';
|
|
42
|
+
if (isMarkdown && value.includes('\n\n')) {
|
|
43
|
+
const blocks = value.split(/(\n\n+)/);
|
|
44
|
+
return blocks.map(block => block.trim() ? block + stega : block).join('');
|
|
45
|
+
}
|
|
46
|
+
return value + stega;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Encode a list of values, handling both simple values and nested objects/lists
|
|
51
|
+
* For typed lists, use the type field to determine which fields to use
|
|
52
|
+
*/
|
|
53
|
+
function encodeList(list, ctx) {
|
|
54
|
+
let newList = list;
|
|
55
|
+
for (let i = 0; i < newList.size; i++) {
|
|
56
|
+
const item = newList.get(i);
|
|
57
|
+
if (isImmutableMap(item)) {
|
|
58
|
+
const itemType = item.get('type');
|
|
59
|
+
if (typeof itemType === 'string') {
|
|
60
|
+
// For typed items, look up fields based on type
|
|
61
|
+
const field = ctx.fields.find(f => f.name === itemType);
|
|
62
|
+
const newItem = ctx.visit(item, getNestedFields(field), `${ctx.path}.${i}`);
|
|
63
|
+
newList = newList.set(i, newItem);
|
|
64
|
+
} else {
|
|
65
|
+
// For untyped items, use current fields
|
|
66
|
+
const newItem = ctx.visit(item, ctx.fields, `${ctx.path}.${i}`);
|
|
67
|
+
newList = newList.set(i, newItem);
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
// For simple values, use first field if available
|
|
71
|
+
const field = ctx.fields[0];
|
|
72
|
+
const newItem = ctx.visit(item, field ? [field] : [], `${ctx.path}.${i}`);
|
|
73
|
+
if (newItem !== item) {
|
|
74
|
+
newList = newList.set(i, newItem);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return newList;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Encode a map of values, looking up the appropriate field for each key
|
|
83
|
+
* and recursively encoding nested values
|
|
84
|
+
*/
|
|
85
|
+
function encodeMap(map, ctx) {
|
|
86
|
+
let newMap = map;
|
|
87
|
+
for (const [key, val] of newMap.entrySeq().toArray()) {
|
|
88
|
+
const field = ctx.fields.find(f => f.name === key);
|
|
89
|
+
if (field) {
|
|
90
|
+
const fields = getNestedFields(field);
|
|
91
|
+
const newVal = ctx.visit(val, fields, ctx.path ? `${ctx.path}.${key}` : key);
|
|
92
|
+
if (newVal !== val) {
|
|
93
|
+
newMap = newMap.set(key, newVal);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return newMap;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Main entry point for encoding steganographic data into entry values
|
|
102
|
+
* Uses a visitor pattern with caching to handle recursive structures
|
|
103
|
+
*/
|
|
104
|
+
export function encodeEntry(value, fields) {
|
|
105
|
+
const plainFields = fields.toJS();
|
|
106
|
+
const cache = new Map();
|
|
107
|
+
function visit(value, fields, path = '') {
|
|
108
|
+
const cached = cache.get(path);
|
|
109
|
+
if (cached === value) return value;
|
|
110
|
+
const ctx = {
|
|
111
|
+
fields,
|
|
112
|
+
path,
|
|
113
|
+
visit
|
|
114
|
+
};
|
|
115
|
+
let result;
|
|
116
|
+
if (isImmutableList(value)) {
|
|
117
|
+
result = encodeList(value, ctx);
|
|
118
|
+
} else if (isImmutableMap(value)) {
|
|
119
|
+
result = encodeMap(value, ctx);
|
|
120
|
+
} else if (typeof value === 'string') {
|
|
121
|
+
result = encodeString(value, ctx);
|
|
122
|
+
} else {
|
|
123
|
+
result = value;
|
|
124
|
+
}
|
|
125
|
+
cache.set(path, result);
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
return visit(value, plainFields);
|
|
129
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = void 0;
|
|
7
|
-
var _immutable = require("immutable");
|
|
8
|
-
var _curry = _interopRequireDefault(require("lodash/curry"));
|
|
9
|
-
var _flow = _interopRequireDefault(require("lodash/flow"));
|
|
10
|
-
var _isString = _interopRequireDefault(require("lodash/isString"));
|
|
11
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
1
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
13
2
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
14
3
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
15
4
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
|
|
16
5
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
6
|
+
import { fromJS, List, Map } from 'immutable';
|
|
7
|
+
import curry from 'lodash/curry';
|
|
8
|
+
import flow from 'lodash/flow';
|
|
9
|
+
import isString from 'lodash/isString';
|
|
17
10
|
function isAbortControllerSupported() {
|
|
18
11
|
if (typeof window !== 'undefined') {
|
|
19
12
|
return !!window.AbortController;
|
|
@@ -40,18 +33,18 @@ function fetchWithTimeout(input, init) {
|
|
|
40
33
|
});
|
|
41
34
|
}
|
|
42
35
|
function decodeParams(paramsString) {
|
|
43
|
-
return
|
|
36
|
+
return List(paramsString.split('&')).map(s => List(s.split('=')).map(decodeURIComponent)).update(Map);
|
|
44
37
|
}
|
|
45
38
|
function fromURL(wholeURL) {
|
|
46
39
|
const [url, allParamsString] = wholeURL.split('?');
|
|
47
|
-
return
|
|
40
|
+
return Map(_objectSpread({
|
|
48
41
|
url
|
|
49
42
|
}, allParamsString ? {
|
|
50
43
|
params: decodeParams(allParamsString)
|
|
51
44
|
} : {}));
|
|
52
45
|
}
|
|
53
46
|
function fromFetchArguments(wholeURL, options) {
|
|
54
|
-
return fromURL(wholeURL).merge((options ?
|
|
47
|
+
return fromURL(wholeURL).merge((options ? fromJS(options) : Map()).remove('url').remove('params'));
|
|
55
48
|
}
|
|
56
49
|
function encodeParams(params) {
|
|
57
50
|
return params.entrySeq().map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&');
|
|
@@ -63,13 +56,13 @@ function toFetchArguments(req) {
|
|
|
63
56
|
return [toURL(req), req.remove('url').remove('params').toJS()];
|
|
64
57
|
}
|
|
65
58
|
function maybeRequestArg(req) {
|
|
66
|
-
if ((
|
|
59
|
+
if (isString(req)) {
|
|
67
60
|
return fromURL(req);
|
|
68
61
|
}
|
|
69
62
|
if (req) {
|
|
70
|
-
return
|
|
63
|
+
return fromJS(req);
|
|
71
64
|
}
|
|
72
|
-
return
|
|
65
|
+
return Map();
|
|
73
66
|
}
|
|
74
67
|
function ensureRequestArg(func) {
|
|
75
68
|
return req => func(maybeRequestArg(req));
|
|
@@ -86,12 +79,12 @@ const performRequest = ensureRequestArg(req => {
|
|
|
86
79
|
|
|
87
80
|
// Each of the following functions takes options and returns another
|
|
88
81
|
// function that performs the requested action on a request.
|
|
89
|
-
const getCurriedRequestProcessor = (
|
|
82
|
+
const getCurriedRequestProcessor = flow([ensureRequestArg2, curry]);
|
|
90
83
|
function getPropSetFunction(path) {
|
|
91
84
|
return getCurriedRequestProcessor((val, req) => req.setIn(path, val));
|
|
92
85
|
}
|
|
93
86
|
function getPropMergeFunction(path) {
|
|
94
|
-
return getCurriedRequestProcessor((obj, req) => req.updateIn(path, (p =
|
|
87
|
+
return getCurriedRequestProcessor((obj, req) => req.updateIn(path, (p = Map()) => p.merge(obj)));
|
|
95
88
|
}
|
|
96
89
|
const withMethod = getPropSetFunction(['method']);
|
|
97
90
|
const withBody = getPropSetFunction(['body']);
|
|
@@ -107,7 +100,7 @@ const withRoot = getCurriedRequestProcessor((root, req) => req.update('url', p =
|
|
|
107
100
|
}
|
|
108
101
|
return root && p && p[0] !== '/' && root[root.length - 1] !== '/' ? `${root}/${p}` : `${root}${p}`;
|
|
109
102
|
}));
|
|
110
|
-
|
|
103
|
+
export default {
|
|
111
104
|
toURL,
|
|
112
105
|
fromURL,
|
|
113
106
|
fromFetchArguments,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decap-cms-lib-util",
|
|
3
3
|
"description": "Shared utilities for Decap CMS.",
|
|
4
|
-
"version": "3.0
|
|
4
|
+
"version": "3.2.0",
|
|
5
5
|
"repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-lib-util",
|
|
6
6
|
"bugs": "https://github.com/decaporg/decap-cms/issues",
|
|
7
7
|
"module": "dist/esm/index.js",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
"immutable": "^3.7.6",
|
|
26
26
|
"lodash": "^4.17.11"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "e2165450e5963b2a8d76d4cf331319bf91f05d45"
|
|
29
29
|
}
|
package/src/API.ts
CHANGED
|
@@ -142,6 +142,7 @@ type Backend = 'github' | 'gitlab' | 'bitbucket';
|
|
|
142
142
|
type RequestConfig = Omit<RequestInit, 'headers'> &
|
|
143
143
|
HeaderConfig & {
|
|
144
144
|
backend: Backend;
|
|
145
|
+
apiRoot?: string;
|
|
145
146
|
params?: ParamObject;
|
|
146
147
|
};
|
|
147
148
|
|
|
@@ -182,7 +183,7 @@ async function constructRequestHeaders(headerConfig: HeaderConfig) {
|
|
|
182
183
|
const { token, headers } = headerConfig;
|
|
183
184
|
const baseHeaders: HeaderObj = { 'Content-Type': 'application/json; charset=utf-8', ...headers };
|
|
184
185
|
if (token) {
|
|
185
|
-
baseHeaders['Authorization'] = `
|
|
186
|
+
baseHeaders['Authorization'] = `Bearer ${token}`;
|
|
186
187
|
}
|
|
187
188
|
return Promise.resolve(baseHeaders);
|
|
188
189
|
}
|
|
@@ -199,7 +200,7 @@ export async function apiRequest(
|
|
|
199
200
|
const { token, backend, ...props } = config;
|
|
200
201
|
const options = { cache: 'no-cache', ...props };
|
|
201
202
|
const headers = await constructRequestHeaders({ headers: options.headers || {}, token });
|
|
202
|
-
const baseUrl = apiRoots[backend];
|
|
203
|
+
const baseUrl = config.apiRoot ?? apiRoots[backend];
|
|
203
204
|
const url = constructUrlWithParams(`${baseUrl}${path}`, options.params);
|
|
204
205
|
let responseStatus = 500;
|
|
205
206
|
try {
|
|
@@ -220,9 +221,10 @@ export async function getDefaultBranchName(configs: {
|
|
|
220
221
|
backend: Backend;
|
|
221
222
|
repo: string;
|
|
222
223
|
token?: string;
|
|
224
|
+
apiRoot?: string;
|
|
223
225
|
}) {
|
|
224
226
|
let apiPath;
|
|
225
|
-
const { token, backend, repo } = configs;
|
|
227
|
+
const { token, backend, repo, apiRoot } = configs;
|
|
226
228
|
switch (backend) {
|
|
227
229
|
case 'gitlab': {
|
|
228
230
|
apiPath = `/projects/${encodeURIComponent(repo)}`;
|
|
@@ -236,7 +238,7 @@ export async function getDefaultBranchName(configs: {
|
|
|
236
238
|
apiPath = `/repos/${repo}`;
|
|
237
239
|
}
|
|
238
240
|
}
|
|
239
|
-
const repoInfo = await apiRequest(apiPath, { token, backend });
|
|
241
|
+
const repoInfo = await apiRequest(apiPath, { token, backend, apiRoot });
|
|
240
242
|
let defaultBranchName;
|
|
241
243
|
if (backend === 'bitbucket') {
|
|
242
244
|
const {
|
package/src/stega.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { vercelStegaEncode } from '@vercel/stega';
|
|
2
|
+
|
|
3
|
+
import { isImmutableMap, isImmutableList } from './types';
|
|
4
|
+
|
|
5
|
+
import type { Map as ImmutableMap, List } from 'immutable';
|
|
6
|
+
import type { CmsField } from 'decap-cms-core';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Context passed to encode functions, containing the current state of the encoding process
|
|
10
|
+
*/
|
|
11
|
+
interface EncodeContext {
|
|
12
|
+
fields: CmsField[]; // Available CMS fields at current level
|
|
13
|
+
path: string; // Path to current value in object tree
|
|
14
|
+
visit: (value: unknown, fields: CmsField[], path: string) => unknown; // Visitor for recursive traversal
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the fields that should be used for encoding nested values
|
|
19
|
+
*/
|
|
20
|
+
function getNestedFields(f?: CmsField): CmsField[] {
|
|
21
|
+
if (f) {
|
|
22
|
+
if ('types' in f) {
|
|
23
|
+
return f.types ?? [];
|
|
24
|
+
}
|
|
25
|
+
if ('fields' in f) {
|
|
26
|
+
return f.fields ?? [];
|
|
27
|
+
}
|
|
28
|
+
if ('field' in f) {
|
|
29
|
+
return f.field ? [f.field] : [];
|
|
30
|
+
}
|
|
31
|
+
return [f];
|
|
32
|
+
}
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Encode a string value by appending steganographic data
|
|
38
|
+
* For markdown fields, encode each paragraph separately
|
|
39
|
+
*/
|
|
40
|
+
function encodeString(value: string, { fields, path }: EncodeContext): string {
|
|
41
|
+
const stega = vercelStegaEncode({ decap: path });
|
|
42
|
+
const isMarkdown = fields[0]?.widget === 'markdown';
|
|
43
|
+
|
|
44
|
+
if (isMarkdown && value.includes('\n\n')) {
|
|
45
|
+
const blocks = value.split(/(\n\n+)/);
|
|
46
|
+
return blocks.map(block => (block.trim() ? block + stega : block)).join('');
|
|
47
|
+
}
|
|
48
|
+
return value + stega;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Encode a list of values, handling both simple values and nested objects/lists
|
|
53
|
+
* For typed lists, use the type field to determine which fields to use
|
|
54
|
+
*/
|
|
55
|
+
function encodeList(list: List<unknown>, ctx: EncodeContext): List<unknown> {
|
|
56
|
+
let newList = list;
|
|
57
|
+
for (let i = 0; i < newList.size; i++) {
|
|
58
|
+
const item = newList.get(i);
|
|
59
|
+
if (isImmutableMap(item)) {
|
|
60
|
+
const itemType = item.get('type');
|
|
61
|
+
if (typeof itemType === 'string') {
|
|
62
|
+
// For typed items, look up fields based on type
|
|
63
|
+
const field = ctx.fields.find(f => f.name === itemType);
|
|
64
|
+
const newItem = ctx.visit(item, getNestedFields(field), `${ctx.path}.${i}`);
|
|
65
|
+
newList = newList.set(i, newItem);
|
|
66
|
+
} else {
|
|
67
|
+
// For untyped items, use current fields
|
|
68
|
+
const newItem = ctx.visit(item, ctx.fields, `${ctx.path}.${i}`);
|
|
69
|
+
newList = newList.set(i, newItem);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
// For simple values, use first field if available
|
|
73
|
+
const field = ctx.fields[0];
|
|
74
|
+
const newItem = ctx.visit(item, field ? [field] : [], `${ctx.path}.${i}`);
|
|
75
|
+
if (newItem !== item) {
|
|
76
|
+
newList = newList.set(i, newItem);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return newList;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Encode a map of values, looking up the appropriate field for each key
|
|
85
|
+
* and recursively encoding nested values
|
|
86
|
+
*/
|
|
87
|
+
function encodeMap(
|
|
88
|
+
map: ImmutableMap<string, unknown>,
|
|
89
|
+
ctx: EncodeContext,
|
|
90
|
+
): ImmutableMap<string, unknown> {
|
|
91
|
+
let newMap = map;
|
|
92
|
+
for (const [key, val] of newMap.entrySeq().toArray()) {
|
|
93
|
+
const field = ctx.fields.find(f => f.name === key);
|
|
94
|
+
if (field) {
|
|
95
|
+
const fields = getNestedFields(field);
|
|
96
|
+
const newVal = ctx.visit(val, fields, ctx.path ? `${ctx.path}.${key}` : key);
|
|
97
|
+
if (newVal !== val) {
|
|
98
|
+
newMap = newMap.set(key, newVal);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return newMap;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Main entry point for encoding steganographic data into entry values
|
|
107
|
+
* Uses a visitor pattern with caching to handle recursive structures
|
|
108
|
+
*/
|
|
109
|
+
export function encodeEntry(value: unknown, fields: List<ImmutableMap<string, unknown>>) {
|
|
110
|
+
const plainFields = fields.toJS() as CmsField[];
|
|
111
|
+
const cache = new Map();
|
|
112
|
+
|
|
113
|
+
function visit(value: unknown, fields: CmsField[], path = '') {
|
|
114
|
+
const cached = cache.get(path);
|
|
115
|
+
if (cached === value) return value;
|
|
116
|
+
|
|
117
|
+
const ctx: EncodeContext = { fields, path, visit };
|
|
118
|
+
let result;
|
|
119
|
+
if (isImmutableList(value)) {
|
|
120
|
+
result = encodeList(value, ctx);
|
|
121
|
+
} else if (isImmutableMap(value)) {
|
|
122
|
+
result = encodeMap(value, ctx);
|
|
123
|
+
} else if (typeof value === 'string') {
|
|
124
|
+
result = encodeString(value, ctx);
|
|
125
|
+
} else {
|
|
126
|
+
result = value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
cache.set(path, result);
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return visit(value, plainFields);
|
|
134
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Map as ImmutableMap, List } from 'immutable';
|
|
2
|
+
|
|
3
|
+
export function isImmutableMap(value: unknown): value is ImmutableMap<string, unknown> {
|
|
4
|
+
return ImmutableMap.isMap(value);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isImmutableList(value: unknown): value is List<unknown> {
|
|
8
|
+
return List.isList(value);
|
|
9
|
+
}
|