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.
@@ -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";
@@ -0,0 +1,7 @@
1
+ import { Map as ImmutableMap, List } from 'immutable';
2
+ export function isImmutableMap(value) {
3
+ return ImmutableMap.isMap(value);
4
+ }
5
+ export function isImmutableList(value) {
6
+ return List.isList(value);
7
+ }
@@ -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 (0, _immutable.List)(paramsString.split('&')).map(s => (0, _immutable.List)(s.split('=')).map(decodeURIComponent)).update(_immutable.Map);
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 (0, _immutable.Map)(_objectSpread({
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 ? (0, _immutable.fromJS)(options) : (0, _immutable.Map)()).remove('url').remove('params'));
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 ((0, _isString.default)(req)) {
59
+ if (isString(req)) {
67
60
  return fromURL(req);
68
61
  }
69
62
  if (req) {
70
- return (0, _immutable.fromJS)(req);
63
+ return fromJS(req);
71
64
  }
72
- return (0, _immutable.Map)();
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 = (0, _flow.default)([ensureRequestArg2, _curry.default]);
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 = (0, _immutable.Map)()) => p.merge(obj)));
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
- var _default = exports.default = {
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",
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": "d3678b0ad1b9f93b841c85e4ee2de48167aa8906"
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'] = `token ${token}`;
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
+ }