@prezly/theme-kit-nextjs 10.9.0 → 10.9.1
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/README.md +73 -0
- package/build/adapters/app-helper/server.cjs +2 -4
- package/build/adapters/app-helper/server.mjs +2 -4
- package/build/adapters/intl/server.cjs +2 -3
- package/build/adapters/intl/server.mjs +2 -3
- package/build/adapters/metadata/lib/generateCategoryPageMetadata.cjs +5 -8
- package/build/adapters/metadata/lib/generateCategoryPageMetadata.mjs +5 -8
- package/build/adapters/metadata/lib/generateMediaGalleryPageMetadata.cjs +13 -8
- package/build/adapters/metadata/lib/generateMediaGalleryPageMetadata.mjs +13 -8
- package/build/adapters/metadata/lib/generateMediaPageMetadata.cjs +3 -5
- package/build/adapters/metadata/lib/generateMediaPageMetadata.mjs +3 -5
- package/build/adapters/metadata/lib/generateRootMetadata.cjs +14 -5
- package/build/adapters/metadata/lib/generateRootMetadata.mjs +14 -5
- package/build/adapters/metadata/lib/generateSearchPageMetadata.cjs +3 -5
- package/build/adapters/metadata/lib/generateSearchPageMetadata.mjs +3 -5
- package/build/adapters/metadata/lib/generateStoryPageMetadata.cjs +17 -10
- package/build/adapters/metadata/lib/generateStoryPageMetadata.mjs +17 -10
- package/build/adapters/metadata/lib/utils/generatePageMetadata.cjs +16 -7
- package/build/adapters/metadata/lib/utils/generatePageMetadata.mjs +16 -7
- package/build/adapters/metadata/server.cjs +1 -3
- package/build/adapters/metadata/server.mjs +1 -3
- package/build/adapters/prezly/cache/configure.d.ts +2 -0
- package/build/adapters/prezly/cache/redis.cjs +54 -22
- package/build/adapters/prezly/cache/redis.mjs +54 -22
- package/build/adapters/prezly/cache/redis.test.cjs +103 -0
- package/build/adapters/prezly/cache/redis.test.mjs +101 -0
- package/build/adapters/prezly/requests.cjs +152 -0
- package/build/adapters/prezly/requests.d.ts +17 -0
- package/build/adapters/prezly/requests.mjs +144 -0
- package/build/adapters/prezly/requests.test.cjs +201 -0
- package/build/adapters/prezly/requests.test.mjs +199 -0
- package/build/adapters/prezly/server.cjs +42 -18
- package/build/adapters/prezly/server.mjs +41 -18
- package/build/adapters/prezly/server.test.cjs +304 -0
- package/build/adapters/prezly/server.test.mjs +302 -0
- package/build/adapters/routing/lib/RoutingAdapter.client.cjs +5 -9
- package/build/adapters/routing/lib/RoutingAdapter.client.mjs +5 -9
- package/build/adapters/routing/lib/RoutingAdapter.server.cjs +5 -6
- package/build/adapters/routing/lib/RoutingAdapter.server.mjs +5 -6
- package/build/adapters/theme-settings/server.cjs +10 -1
- package/build/adapters/theme-settings/server.mjs +10 -1
- package/build/middleware/intl-middleware.cjs +22 -11
- package/build/middleware/intl-middleware.mjs +22 -11
- package/build/next-config/index.cjs +5 -7
- package/build/next-config/index.mjs +5 -7
- package/build/playwright/config.cjs +5 -5
- package/build/playwright/config.mjs +5 -5
- package/build/robots/server.cjs +1 -3
- package/build/robots/server.mjs +1 -3
- package/build/sentry/common.cjs +3 -7
- package/build/sentry/common.mjs +3 -7
- package/package.json +112 -109
|
@@ -8,52 +8,84 @@ var _jsonStableStringify = _interopRequireDefault(require("json-stable-stringify
|
|
|
8
8
|
var _redis = require("redis");
|
|
9
9
|
var _excluded = ["ttl", "prefix"];
|
|
10
10
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
-
function
|
|
12
|
-
function
|
|
11
|
+
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; }
|
|
12
|
+
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; }
|
|
13
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
14
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
15
|
+
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); }
|
|
13
16
|
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
14
17
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
18
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
19
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
20
|
+
var COMMAND_TIMEOUT = 1000;
|
|
15
21
|
var CONNECTIONS = new Map();
|
|
22
|
+
function command(_x) {
|
|
23
|
+
return _command.apply(this, arguments);
|
|
24
|
+
}
|
|
25
|
+
function _command() {
|
|
26
|
+
_command = _asyncToGenerator(function* (invoke) {
|
|
27
|
+
var timer;
|
|
28
|
+
try {
|
|
29
|
+
// node-redis 4 can corrupt its command queue when aborting a command
|
|
30
|
+
// already sent to the socket. Leave that operation tracked by the client;
|
|
31
|
+
// commandsQueueMaxLength bounds all outstanding replies, including these.
|
|
32
|
+
return yield Promise.race([Promise.resolve().then(invoke), new Promise((_, reject) => {
|
|
33
|
+
timer = setTimeout(() => reject(new Error('Content cache command timed out.')), COMMAND_TIMEOUT);
|
|
34
|
+
})]);
|
|
35
|
+
} finally {
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return _command.apply(this, arguments);
|
|
40
|
+
}
|
|
16
41
|
function createRedisCache(_ref) {
|
|
17
|
-
var
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
prefix = ''
|
|
21
|
-
} = _ref,
|
|
42
|
+
var ttl = _ref.ttl,
|
|
43
|
+
_ref$prefix = _ref.prefix,
|
|
44
|
+
prefix = _ref$prefix === void 0 ? '' : _ref$prefix,
|
|
22
45
|
options = _objectWithoutProperties(_ref, _excluded);
|
|
23
46
|
var connectionKey = (0, _jsonStableStringify.default)(options);
|
|
24
|
-
var
|
|
25
|
-
|
|
47
|
+
var client = CONNECTIONS.get(connectionKey);
|
|
48
|
+
if (!client) {
|
|
49
|
+
client = (0, _redis.createClient)(_objectSpread(_objectSpread({}, options), {}, {
|
|
50
|
+
// Do not accumulate application commands during a disconnected period.
|
|
51
|
+
disableOfflineQueue: true,
|
|
52
|
+
commandsQueueMaxLength: 128,
|
|
53
|
+
socket: _objectSpread(_objectSpread({}, options.socket), {}, {
|
|
54
|
+
connectTimeout: 1000
|
|
55
|
+
})
|
|
56
|
+
})).on('error', error => console.error(error));
|
|
57
|
+
CONNECTIONS.set(connectionKey, client);
|
|
58
|
+
// Reconnection belongs to this one shared client. Requests never wait on
|
|
59
|
+
// its potentially long-lived connection promise when Redis is unavailable.
|
|
60
|
+
void client.connect().catch(() => undefined);
|
|
61
|
+
}
|
|
62
|
+
var connection = client;
|
|
26
63
|
function createCache() {
|
|
27
64
|
var namespacePrefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
28
65
|
return {
|
|
29
66
|
get(key, latestVersion) {
|
|
30
67
|
return _asyncToGenerator(function* () {
|
|
31
|
-
|
|
32
|
-
var cached = yield
|
|
33
|
-
if (!cached)
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
68
|
+
if (!connection.isReady) return undefined;
|
|
69
|
+
var cached = yield command(() => connection.get("".concat(namespacePrefix).concat(key)));
|
|
70
|
+
if (!cached) return undefined;
|
|
36
71
|
var entry = JSON.parse(cached);
|
|
37
|
-
if (entry.version < latestVersion)
|
|
38
|
-
// the entry is stale and cannot be used anymore
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
72
|
+
if (entry.version < latestVersion) return undefined;
|
|
41
73
|
if (ttl) {
|
|
42
|
-
|
|
74
|
+
void command(() => connection.expire("".concat(namespacePrefix).concat(key), ttl)).catch(() => undefined);
|
|
43
75
|
}
|
|
44
76
|
return entry.value;
|
|
45
77
|
})();
|
|
46
78
|
},
|
|
47
79
|
set(key, value, version) {
|
|
48
80
|
return _asyncToGenerator(function* () {
|
|
49
|
-
|
|
81
|
+
if (!connection.isReady) return;
|
|
50
82
|
var entry = {
|
|
51
83
|
value,
|
|
52
84
|
version
|
|
53
85
|
};
|
|
54
|
-
yield
|
|
86
|
+
yield command(() => connection.set("".concat(namespacePrefix).concat(key), JSON.stringify(entry), {
|
|
55
87
|
EX: ttl
|
|
56
|
-
});
|
|
88
|
+
}));
|
|
57
89
|
})();
|
|
58
90
|
},
|
|
59
91
|
namespace(namespace) {
|
|
@@ -1,52 +1,84 @@
|
|
|
1
1
|
var _excluded = ["ttl", "prefix"];
|
|
2
|
-
function
|
|
3
|
-
function
|
|
2
|
+
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; }
|
|
3
|
+
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; }
|
|
4
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
6
|
+
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); }
|
|
4
7
|
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
|
|
5
8
|
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
9
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
10
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
6
11
|
import stableStringify from 'json-stable-stringify';
|
|
7
12
|
import { createClient } from 'redis';
|
|
13
|
+
var COMMAND_TIMEOUT = 1000;
|
|
8
14
|
var CONNECTIONS = new Map();
|
|
15
|
+
function command(_x) {
|
|
16
|
+
return _command.apply(this, arguments);
|
|
17
|
+
}
|
|
18
|
+
function _command() {
|
|
19
|
+
_command = _asyncToGenerator(function* (invoke) {
|
|
20
|
+
var timer;
|
|
21
|
+
try {
|
|
22
|
+
// node-redis 4 can corrupt its command queue when aborting a command
|
|
23
|
+
// already sent to the socket. Leave that operation tracked by the client;
|
|
24
|
+
// commandsQueueMaxLength bounds all outstanding replies, including these.
|
|
25
|
+
return yield Promise.race([Promise.resolve().then(invoke), new Promise((_, reject) => {
|
|
26
|
+
timer = setTimeout(() => reject(new Error('Content cache command timed out.')), COMMAND_TIMEOUT);
|
|
27
|
+
})]);
|
|
28
|
+
} finally {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return _command.apply(this, arguments);
|
|
33
|
+
}
|
|
9
34
|
export function createRedisCache(_ref) {
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
prefix = ''
|
|
14
|
-
} = _ref,
|
|
35
|
+
var ttl = _ref.ttl,
|
|
36
|
+
_ref$prefix = _ref.prefix,
|
|
37
|
+
prefix = _ref$prefix === void 0 ? '' : _ref$prefix,
|
|
15
38
|
options = _objectWithoutProperties(_ref, _excluded);
|
|
16
39
|
var connectionKey = stableStringify(options);
|
|
17
|
-
var
|
|
18
|
-
|
|
40
|
+
var client = CONNECTIONS.get(connectionKey);
|
|
41
|
+
if (!client) {
|
|
42
|
+
client = createClient(_objectSpread(_objectSpread({}, options), {}, {
|
|
43
|
+
// Do not accumulate application commands during a disconnected period.
|
|
44
|
+
disableOfflineQueue: true,
|
|
45
|
+
commandsQueueMaxLength: 128,
|
|
46
|
+
socket: _objectSpread(_objectSpread({}, options.socket), {}, {
|
|
47
|
+
connectTimeout: 1000
|
|
48
|
+
})
|
|
49
|
+
})).on('error', error => console.error(error));
|
|
50
|
+
CONNECTIONS.set(connectionKey, client);
|
|
51
|
+
// Reconnection belongs to this one shared client. Requests never wait on
|
|
52
|
+
// its potentially long-lived connection promise when Redis is unavailable.
|
|
53
|
+
void client.connect().catch(() => undefined);
|
|
54
|
+
}
|
|
55
|
+
var connection = client;
|
|
19
56
|
function createCache() {
|
|
20
57
|
var namespacePrefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
21
58
|
return {
|
|
22
59
|
get(key, latestVersion) {
|
|
23
60
|
return _asyncToGenerator(function* () {
|
|
24
|
-
|
|
25
|
-
var cached = yield
|
|
26
|
-
if (!cached)
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
61
|
+
if (!connection.isReady) return undefined;
|
|
62
|
+
var cached = yield command(() => connection.get("".concat(namespacePrefix).concat(key)));
|
|
63
|
+
if (!cached) return undefined;
|
|
29
64
|
var entry = JSON.parse(cached);
|
|
30
|
-
if (entry.version < latestVersion)
|
|
31
|
-
// the entry is stale and cannot be used anymore
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
65
|
+
if (entry.version < latestVersion) return undefined;
|
|
34
66
|
if (ttl) {
|
|
35
|
-
|
|
67
|
+
void command(() => connection.expire("".concat(namespacePrefix).concat(key), ttl)).catch(() => undefined);
|
|
36
68
|
}
|
|
37
69
|
return entry.value;
|
|
38
70
|
})();
|
|
39
71
|
},
|
|
40
72
|
set(key, value, version) {
|
|
41
73
|
return _asyncToGenerator(function* () {
|
|
42
|
-
|
|
74
|
+
if (!connection.isReady) return;
|
|
43
75
|
var entry = {
|
|
44
76
|
value,
|
|
45
77
|
version
|
|
46
78
|
};
|
|
47
|
-
yield
|
|
79
|
+
yield command(() => connection.set("".concat(namespacePrefix).concat(key), JSON.stringify(entry), {
|
|
48
80
|
EX: ttl
|
|
49
|
-
});
|
|
81
|
+
}));
|
|
50
82
|
})();
|
|
51
83
|
},
|
|
52
84
|
namespace(namespace) {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _redis = require("redis");
|
|
4
|
+
var _redis2 = require("./redis.cjs");
|
|
5
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
6
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
7
|
+
jest.mock('redis', () => ({
|
|
8
|
+
createClient: jest.fn()
|
|
9
|
+
}));
|
|
10
|
+
var sequence = 0;
|
|
11
|
+
function connection() {
|
|
12
|
+
var ready = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
13
|
+
var client = {
|
|
14
|
+
isReady: ready,
|
|
15
|
+
on: jest.fn().mockReturnThis(),
|
|
16
|
+
connect: jest.fn(() => ready ? Promise.resolve(undefined) : new Promise(() => {})),
|
|
17
|
+
get: jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
18
|
+
return JSON.stringify({
|
|
19
|
+
version: 2,
|
|
20
|
+
value: 'cached'
|
|
21
|
+
});
|
|
22
|
+
})),
|
|
23
|
+
set: jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
24
|
+
return 'OK';
|
|
25
|
+
})),
|
|
26
|
+
expire: jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
27
|
+
return true;
|
|
28
|
+
}))
|
|
29
|
+
};
|
|
30
|
+
_redis.createClient.mockReturnValue(client);
|
|
31
|
+
var options = {
|
|
32
|
+
url: "redis://test-".concat(sequence++, ":6379"),
|
|
33
|
+
ttl: 300,
|
|
34
|
+
prefix: 'test:'
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
client,
|
|
38
|
+
options,
|
|
39
|
+
cache: (0, _redis2.createRedisCache)(options)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
afterEach(() => jest.useRealTimers());
|
|
43
|
+
it('fails open while disconnected without waiting for connection establishment', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
44
|
+
var _connection = connection(false),
|
|
45
|
+
client = _connection.client,
|
|
46
|
+
cache = _connection.cache,
|
|
47
|
+
options = _connection.options;
|
|
48
|
+
expect(yield cache.get('key', 1)).toBeUndefined();
|
|
49
|
+
yield cache.set('key', 'value', 1);
|
|
50
|
+
expect(client.get).not.toHaveBeenCalled();
|
|
51
|
+
expect(client.set).not.toHaveBeenCalled();
|
|
52
|
+
(0, _redis2.createRedisCache)(options);
|
|
53
|
+
expect(client.connect).toHaveBeenCalledTimes(1);
|
|
54
|
+
expect(_redis.createClient).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
55
|
+
disableOfflineQueue: true,
|
|
56
|
+
commandsQueueMaxLength: 128,
|
|
57
|
+
socket: expect.objectContaining({
|
|
58
|
+
connectTimeout: 1000
|
|
59
|
+
})
|
|
60
|
+
}));
|
|
61
|
+
client.isReady = true;
|
|
62
|
+
expect(yield cache.get('key', 1)).toBe('cached');
|
|
63
|
+
}));
|
|
64
|
+
it('preserves namespace, version and sliding expiry and observes expiry rejection', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
65
|
+
var _connection2 = connection(),
|
|
66
|
+
client = _connection2.client,
|
|
67
|
+
cache = _connection2.cache;
|
|
68
|
+
client.expire.mockRejectedValueOnce(new Error('expiry failed'));
|
|
69
|
+
expect(yield cache.namespace('room').get('key', 2)).toBe('cached');
|
|
70
|
+
expect(client.get).toHaveBeenLastCalledWith('test:room:key');
|
|
71
|
+
expect(yield cache.get('key', 3)).toBeUndefined();
|
|
72
|
+
expect(client.expire).toHaveBeenCalledTimes(1);
|
|
73
|
+
yield cache.set('key', 'updated', 3);
|
|
74
|
+
expect(client.set).toHaveBeenLastCalledWith('test:key', JSON.stringify({
|
|
75
|
+
value: 'updated',
|
|
76
|
+
version: 3
|
|
77
|
+
}), {
|
|
78
|
+
EX: 300
|
|
79
|
+
});
|
|
80
|
+
}));
|
|
81
|
+
it.each(['get', 'set'])('times out stalled %s commands without unsafe cancellation and recovers', /*#__PURE__*/function () {
|
|
82
|
+
var _ref6 = _asyncToGenerator(function* (method) {
|
|
83
|
+
jest.useFakeTimers();
|
|
84
|
+
var _connection3 = connection(),
|
|
85
|
+
client = _connection3.client,
|
|
86
|
+
cache = _connection3.cache;
|
|
87
|
+
var rejectOperation;
|
|
88
|
+
client[method].mockImplementationOnce(() => new Promise((_, reject) => {
|
|
89
|
+
rejectOperation = reject;
|
|
90
|
+
}));
|
|
91
|
+
var pending = method === 'get' ? cache.get('key', 1) : cache.set('key', 'value', 1);
|
|
92
|
+
var failed = expect(pending).rejects.toThrow('timed out');
|
|
93
|
+
yield jest.advanceTimersByTimeAsync(1000);
|
|
94
|
+
yield failed;
|
|
95
|
+
rejectOperation(new Error('late failure'));
|
|
96
|
+
expect(yield cache.get('key', 1)).toBe('cached');
|
|
97
|
+
yield jest.advanceTimersByTimeAsync(0);
|
|
98
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
99
|
+
});
|
|
100
|
+
return function (_x) {
|
|
101
|
+
return _ref6.apply(this, arguments);
|
|
102
|
+
};
|
|
103
|
+
}());
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
2
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
3
|
+
import { createClient } from 'redis';
|
|
4
|
+
import { createRedisCache } from "./redis.mjs";
|
|
5
|
+
jest.mock('redis', () => ({
|
|
6
|
+
createClient: jest.fn()
|
|
7
|
+
}));
|
|
8
|
+
var sequence = 0;
|
|
9
|
+
function connection() {
|
|
10
|
+
var ready = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
11
|
+
var client = {
|
|
12
|
+
isReady: ready,
|
|
13
|
+
on: jest.fn().mockReturnThis(),
|
|
14
|
+
connect: jest.fn(() => ready ? Promise.resolve(undefined) : new Promise(() => {})),
|
|
15
|
+
get: jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
16
|
+
return JSON.stringify({
|
|
17
|
+
version: 2,
|
|
18
|
+
value: 'cached'
|
|
19
|
+
});
|
|
20
|
+
})),
|
|
21
|
+
set: jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
22
|
+
return 'OK';
|
|
23
|
+
})),
|
|
24
|
+
expire: jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
25
|
+
return true;
|
|
26
|
+
}))
|
|
27
|
+
};
|
|
28
|
+
createClient.mockReturnValue(client);
|
|
29
|
+
var options = {
|
|
30
|
+
url: "redis://test-".concat(sequence++, ":6379"),
|
|
31
|
+
ttl: 300,
|
|
32
|
+
prefix: 'test:'
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
client,
|
|
36
|
+
options,
|
|
37
|
+
cache: createRedisCache(options)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
afterEach(() => jest.useRealTimers());
|
|
41
|
+
it('fails open while disconnected without waiting for connection establishment', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
42
|
+
var _connection = connection(false),
|
|
43
|
+
client = _connection.client,
|
|
44
|
+
cache = _connection.cache,
|
|
45
|
+
options = _connection.options;
|
|
46
|
+
expect(yield cache.get('key', 1)).toBeUndefined();
|
|
47
|
+
yield cache.set('key', 'value', 1);
|
|
48
|
+
expect(client.get).not.toHaveBeenCalled();
|
|
49
|
+
expect(client.set).not.toHaveBeenCalled();
|
|
50
|
+
createRedisCache(options);
|
|
51
|
+
expect(client.connect).toHaveBeenCalledTimes(1);
|
|
52
|
+
expect(createClient).toHaveBeenLastCalledWith(expect.objectContaining({
|
|
53
|
+
disableOfflineQueue: true,
|
|
54
|
+
commandsQueueMaxLength: 128,
|
|
55
|
+
socket: expect.objectContaining({
|
|
56
|
+
connectTimeout: 1000
|
|
57
|
+
})
|
|
58
|
+
}));
|
|
59
|
+
client.isReady = true;
|
|
60
|
+
expect(yield cache.get('key', 1)).toBe('cached');
|
|
61
|
+
}));
|
|
62
|
+
it('preserves namespace, version and sliding expiry and observes expiry rejection', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
63
|
+
var _connection2 = connection(),
|
|
64
|
+
client = _connection2.client,
|
|
65
|
+
cache = _connection2.cache;
|
|
66
|
+
client.expire.mockRejectedValueOnce(new Error('expiry failed'));
|
|
67
|
+
expect(yield cache.namespace('room').get('key', 2)).toBe('cached');
|
|
68
|
+
expect(client.get).toHaveBeenLastCalledWith('test:room:key');
|
|
69
|
+
expect(yield cache.get('key', 3)).toBeUndefined();
|
|
70
|
+
expect(client.expire).toHaveBeenCalledTimes(1);
|
|
71
|
+
yield cache.set('key', 'updated', 3);
|
|
72
|
+
expect(client.set).toHaveBeenLastCalledWith('test:key', JSON.stringify({
|
|
73
|
+
value: 'updated',
|
|
74
|
+
version: 3
|
|
75
|
+
}), {
|
|
76
|
+
EX: 300
|
|
77
|
+
});
|
|
78
|
+
}));
|
|
79
|
+
it.each(['get', 'set'])('times out stalled %s commands without unsafe cancellation and recovers', /*#__PURE__*/function () {
|
|
80
|
+
var _ref6 = _asyncToGenerator(function* (method) {
|
|
81
|
+
jest.useFakeTimers();
|
|
82
|
+
var _connection3 = connection(),
|
|
83
|
+
client = _connection3.client,
|
|
84
|
+
cache = _connection3.cache;
|
|
85
|
+
var rejectOperation;
|
|
86
|
+
client[method].mockImplementationOnce(() => new Promise((_, reject) => {
|
|
87
|
+
rejectOperation = reject;
|
|
88
|
+
}));
|
|
89
|
+
var pending = method === 'get' ? cache.get('key', 1) : cache.set('key', 'value', 1);
|
|
90
|
+
var failed = expect(pending).rejects.toThrow('timed out');
|
|
91
|
+
yield jest.advanceTimersByTimeAsync(1000);
|
|
92
|
+
yield failed;
|
|
93
|
+
rejectOperation(new Error('late failure'));
|
|
94
|
+
expect(yield cache.get('key', 1)).toBe('cached');
|
|
95
|
+
yield jest.advanceTimersByTimeAsync(0);
|
|
96
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
97
|
+
});
|
|
98
|
+
return function (_x) {
|
|
99
|
+
return _ref6.apply(this, arguments);
|
|
100
|
+
};
|
|
101
|
+
}());
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.FetchScheduler = void 0;
|
|
7
|
+
exports.createBoundedFetch = createBoundedFetch;
|
|
8
|
+
var _registry$key;
|
|
9
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
10
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
11
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
12
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
13
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
14
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
15
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
16
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
17
|
+
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; }
|
|
18
|
+
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; }
|
|
19
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
20
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
21
|
+
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); }
|
|
22
|
+
function abortReason(signal) {
|
|
23
|
+
if (signal.reason instanceof Error) return signal.reason;
|
|
24
|
+
var error = new Error('Content request aborted.');
|
|
25
|
+
error.name = 'AbortError';
|
|
26
|
+
return error;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Limit actual HTTP requests, not aggregate ContentDelivery methods. */
|
|
30
|
+
class FetchScheduler {
|
|
31
|
+
constructor() {
|
|
32
|
+
var limits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
33
|
+
_defineProperty(this, "active", 0);
|
|
34
|
+
_defineProperty(this, "queue", []);
|
|
35
|
+
_defineProperty(this, "limits", void 0);
|
|
36
|
+
this.limits = _objectSpread({
|
|
37
|
+
concurrency: 32,
|
|
38
|
+
queueLimit: 256,
|
|
39
|
+
queueTimeout: 1000,
|
|
40
|
+
requestTimeout: 30000
|
|
41
|
+
}, limits);
|
|
42
|
+
for (var _ref3 of Object.entries(this.limits)) {
|
|
43
|
+
var _ref2 = _slicedToArray(_ref3, 2);
|
|
44
|
+
var name = _ref2[0];
|
|
45
|
+
var value = _ref2[1];
|
|
46
|
+
if (!Number.isInteger(value) || value < (name === 'queueLimit' ? 0 : 1)) {
|
|
47
|
+
throw new RangeError('Invalid content request limits.');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
run(invoke, signal) {
|
|
52
|
+
if (signal !== null && signal !== void 0 && signal.aborted) return Promise.reject(abortReason(signal));
|
|
53
|
+
return new Promise((_resolve, reject) => {
|
|
54
|
+
var task = {
|
|
55
|
+
invoke,
|
|
56
|
+
resolve: value => _resolve(value),
|
|
57
|
+
reject,
|
|
58
|
+
signal,
|
|
59
|
+
clearQueueWait: () => {}
|
|
60
|
+
};
|
|
61
|
+
if (this.active < this.limits.concurrency) {
|
|
62
|
+
this.start(task);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (this.queue.length >= this.limits.queueLimit) {
|
|
66
|
+
reject(new Error('Content request queue is full.'));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
var cancel = error => {
|
|
70
|
+
var index = this.queue.indexOf(task);
|
|
71
|
+
if (index < 0) return;
|
|
72
|
+
this.queue.splice(index, 1);
|
|
73
|
+
task.clearQueueWait();
|
|
74
|
+
reject(error);
|
|
75
|
+
};
|
|
76
|
+
var onAbort = () => cancel(abortReason(signal));
|
|
77
|
+
var timer = setTimeout(() => cancel(new Error('Content request queue wait timed out.')), this.limits.queueTimeout);
|
|
78
|
+
task.clearQueueWait = () => {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
signal === null || signal === void 0 || signal.removeEventListener('abort', onAbort);
|
|
81
|
+
};
|
|
82
|
+
signal === null || signal === void 0 || signal.addEventListener('abort', onAbort, {
|
|
83
|
+
once: true
|
|
84
|
+
});
|
|
85
|
+
this.queue.push(task);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
start(task) {
|
|
89
|
+
var _task$signal2;
|
|
90
|
+
task.clearQueueWait();
|
|
91
|
+
this.active += 1;
|
|
92
|
+
var controller = new AbortController();
|
|
93
|
+
var rejectAbort = () => task.reject(abortReason(controller.signal));
|
|
94
|
+
var forwardAbort = () => {
|
|
95
|
+
var _task$signal;
|
|
96
|
+
return controller.abort((_task$signal = task.signal) === null || _task$signal === void 0 ? void 0 : _task$signal.reason);
|
|
97
|
+
};
|
|
98
|
+
controller.signal.addEventListener('abort', rejectAbort, {
|
|
99
|
+
once: true
|
|
100
|
+
});
|
|
101
|
+
(_task$signal2 = task.signal) === null || _task$signal2 === void 0 || _task$signal2.addEventListener('abort', forwardAbort, {
|
|
102
|
+
once: true
|
|
103
|
+
});
|
|
104
|
+
var timer = setTimeout(() => controller.abort(new Error('Content request timed out.')), this.limits.requestTimeout);
|
|
105
|
+
void Promise.resolve().then(() => {
|
|
106
|
+
if (controller.signal.aborted) throw abortReason(controller.signal);
|
|
107
|
+
return task.invoke(controller.signal);
|
|
108
|
+
}).then(value => {
|
|
109
|
+
if (!controller.signal.aborted) task.resolve(value);
|
|
110
|
+
}, task.reject).finally(() => {
|
|
111
|
+
var _task$signal3;
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
controller.signal.removeEventListener('abort', rejectAbort);
|
|
114
|
+
(_task$signal3 = task.signal) === null || _task$signal3 === void 0 || _task$signal3.removeEventListener('abort', forwardAbort);
|
|
115
|
+
// A custom fetch that ignores abort keeps its slot until it really
|
|
116
|
+
// settles. Timeouts must not create unlimited overlapping requests.
|
|
117
|
+
this.active -= 1;
|
|
118
|
+
while (this.active < this.limits.concurrency && this.queue.length > 0) {
|
|
119
|
+
this.start(this.queue.shift());
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.FetchScheduler = FetchScheduler;
|
|
125
|
+
var key = Symbol.for('@prezly/theme-kit-nextjs/content-fetch-scheduler/v1');
|
|
126
|
+
var registry = globalThis;
|
|
127
|
+
var sharedContentFetches = (_registry$key = registry[key]) !== null && _registry$key !== void 0 ? _registry$key : registry[key] = new FetchScheduler();
|
|
128
|
+
function createBoundedFetch(fetchImpl) {
|
|
129
|
+
var scheduler = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : sharedContentFetches;
|
|
130
|
+
return (input, init) => {
|
|
131
|
+
var _init$signal;
|
|
132
|
+
var signal = (_init$signal = init === null || init === void 0 ? void 0 : init.signal) !== null && _init$signal !== void 0 ? _init$signal : input instanceof Request ? input.signal : undefined;
|
|
133
|
+
return scheduler.run(/*#__PURE__*/function () {
|
|
134
|
+
var _ref4 = _asyncToGenerator(function* (requestSignal) {
|
|
135
|
+
var response = yield fetchImpl(input, _objectSpread(_objectSpread({}, init), {}, {
|
|
136
|
+
signal: requestSignal
|
|
137
|
+
}));
|
|
138
|
+
// Keep the deadline and slot through body consumption, not just headers.
|
|
139
|
+
// Only the internal content SDK uses this wrapper; the raw SDK is unchanged.
|
|
140
|
+
var body = response.body === null ? null : yield response.arrayBuffer();
|
|
141
|
+
return new Response(body, {
|
|
142
|
+
status: response.status,
|
|
143
|
+
statusText: response.statusText,
|
|
144
|
+
headers: response.headers
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
return function (_x) {
|
|
148
|
+
return _ref4.apply(this, arguments);
|
|
149
|
+
};
|
|
150
|
+
}(), signal);
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface Limits {
|
|
2
|
+
concurrency: number;
|
|
3
|
+
queueLimit: number;
|
|
4
|
+
queueTimeout: number;
|
|
5
|
+
requestTimeout: number;
|
|
6
|
+
}
|
|
7
|
+
/** Limit actual HTTP requests, not aggregate ContentDelivery methods. */
|
|
8
|
+
export declare class FetchScheduler {
|
|
9
|
+
private active;
|
|
10
|
+
private readonly queue;
|
|
11
|
+
private readonly limits;
|
|
12
|
+
constructor(limits?: Partial<Limits>);
|
|
13
|
+
run<T>(invoke: (signal: AbortSignal) => Promise<T>, signal?: AbortSignal | null): Promise<T>;
|
|
14
|
+
private start;
|
|
15
|
+
}
|
|
16
|
+
export declare function createBoundedFetch(fetchImpl: typeof fetch, scheduler?: FetchScheduler): typeof fetch;
|
|
17
|
+
export {};
|