decap-cms-lib-util 3.1.0 → 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 +6 -0
- package/dist/esm/API.js +21 -37
- 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/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.
|
|
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/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
|
+
}
|