@prezly/theme-kit-nextjs 10.9.0 → 10.9.2
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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
var _registry$key;
|
|
2
|
+
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); }
|
|
3
|
+
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); }); }; }
|
|
4
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
5
|
+
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."); }
|
|
6
|
+
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; } }
|
|
7
|
+
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; }
|
|
8
|
+
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; } }
|
|
9
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
10
|
+
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; }
|
|
11
|
+
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; }
|
|
12
|
+
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; }
|
|
13
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
14
|
+
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); }
|
|
15
|
+
function abortReason(signal) {
|
|
16
|
+
if (signal.reason instanceof Error) return signal.reason;
|
|
17
|
+
var error = new Error('Content request aborted.');
|
|
18
|
+
error.name = 'AbortError';
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Limit actual HTTP requests, not aggregate ContentDelivery methods. */
|
|
23
|
+
export class FetchScheduler {
|
|
24
|
+
constructor() {
|
|
25
|
+
var limits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
26
|
+
_defineProperty(this, "active", 0);
|
|
27
|
+
_defineProperty(this, "queue", []);
|
|
28
|
+
_defineProperty(this, "limits", void 0);
|
|
29
|
+
this.limits = _objectSpread({
|
|
30
|
+
concurrency: 32,
|
|
31
|
+
queueLimit: 256,
|
|
32
|
+
queueTimeout: 1000,
|
|
33
|
+
requestTimeout: 30000
|
|
34
|
+
}, limits);
|
|
35
|
+
for (var _ref3 of Object.entries(this.limits)) {
|
|
36
|
+
var _ref2 = _slicedToArray(_ref3, 2);
|
|
37
|
+
var name = _ref2[0];
|
|
38
|
+
var value = _ref2[1];
|
|
39
|
+
if (!Number.isInteger(value) || value < (name === 'queueLimit' ? 0 : 1)) {
|
|
40
|
+
throw new RangeError('Invalid content request limits.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
run(invoke, signal) {
|
|
45
|
+
if (signal !== null && signal !== void 0 && signal.aborted) return Promise.reject(abortReason(signal));
|
|
46
|
+
return new Promise((_resolve, reject) => {
|
|
47
|
+
var task = {
|
|
48
|
+
invoke,
|
|
49
|
+
resolve: value => _resolve(value),
|
|
50
|
+
reject,
|
|
51
|
+
signal,
|
|
52
|
+
clearQueueWait: () => {}
|
|
53
|
+
};
|
|
54
|
+
if (this.active < this.limits.concurrency) {
|
|
55
|
+
this.start(task);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (this.queue.length >= this.limits.queueLimit) {
|
|
59
|
+
reject(new Error('Content request queue is full.'));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
var cancel = error => {
|
|
63
|
+
var index = this.queue.indexOf(task);
|
|
64
|
+
if (index < 0) return;
|
|
65
|
+
this.queue.splice(index, 1);
|
|
66
|
+
task.clearQueueWait();
|
|
67
|
+
reject(error);
|
|
68
|
+
};
|
|
69
|
+
var onAbort = () => cancel(abortReason(signal));
|
|
70
|
+
var timer = setTimeout(() => cancel(new Error('Content request queue wait timed out.')), this.limits.queueTimeout);
|
|
71
|
+
task.clearQueueWait = () => {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
signal === null || signal === void 0 || signal.removeEventListener('abort', onAbort);
|
|
74
|
+
};
|
|
75
|
+
signal === null || signal === void 0 || signal.addEventListener('abort', onAbort, {
|
|
76
|
+
once: true
|
|
77
|
+
});
|
|
78
|
+
this.queue.push(task);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
start(task) {
|
|
82
|
+
var _task$signal2;
|
|
83
|
+
task.clearQueueWait();
|
|
84
|
+
this.active += 1;
|
|
85
|
+
var controller = new AbortController();
|
|
86
|
+
var rejectAbort = () => task.reject(abortReason(controller.signal));
|
|
87
|
+
var forwardAbort = () => {
|
|
88
|
+
var _task$signal;
|
|
89
|
+
return controller.abort((_task$signal = task.signal) === null || _task$signal === void 0 ? void 0 : _task$signal.reason);
|
|
90
|
+
};
|
|
91
|
+
controller.signal.addEventListener('abort', rejectAbort, {
|
|
92
|
+
once: true
|
|
93
|
+
});
|
|
94
|
+
(_task$signal2 = task.signal) === null || _task$signal2 === void 0 || _task$signal2.addEventListener('abort', forwardAbort, {
|
|
95
|
+
once: true
|
|
96
|
+
});
|
|
97
|
+
var timer = setTimeout(() => controller.abort(new Error('Content request timed out.')), this.limits.requestTimeout);
|
|
98
|
+
void Promise.resolve().then(() => {
|
|
99
|
+
if (controller.signal.aborted) throw abortReason(controller.signal);
|
|
100
|
+
return task.invoke(controller.signal);
|
|
101
|
+
}).then(value => {
|
|
102
|
+
if (!controller.signal.aborted) task.resolve(value);
|
|
103
|
+
}, task.reject).finally(() => {
|
|
104
|
+
var _task$signal3;
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
controller.signal.removeEventListener('abort', rejectAbort);
|
|
107
|
+
(_task$signal3 = task.signal) === null || _task$signal3 === void 0 || _task$signal3.removeEventListener('abort', forwardAbort);
|
|
108
|
+
// A custom fetch that ignores abort keeps its slot until it really
|
|
109
|
+
// settles. Timeouts must not create unlimited overlapping requests.
|
|
110
|
+
this.active -= 1;
|
|
111
|
+
while (this.active < this.limits.concurrency && this.queue.length > 0) {
|
|
112
|
+
this.start(this.queue.shift());
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
var key = Symbol.for('@prezly/theme-kit-nextjs/content-fetch-scheduler/v1');
|
|
118
|
+
var registry = globalThis;
|
|
119
|
+
var sharedContentFetches = (_registry$key = registry[key]) !== null && _registry$key !== void 0 ? _registry$key : registry[key] = new FetchScheduler();
|
|
120
|
+
export function createBoundedFetch(fetchImpl) {
|
|
121
|
+
var scheduler = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : sharedContentFetches;
|
|
122
|
+
return (input, init) => {
|
|
123
|
+
var _init$signal;
|
|
124
|
+
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;
|
|
125
|
+
return scheduler.run(/*#__PURE__*/function () {
|
|
126
|
+
var _ref4 = _asyncToGenerator(function* (requestSignal) {
|
|
127
|
+
var response = yield fetchImpl(input, _objectSpread(_objectSpread({}, init), {}, {
|
|
128
|
+
signal: requestSignal
|
|
129
|
+
}));
|
|
130
|
+
// Keep the deadline and slot through body consumption, not just headers.
|
|
131
|
+
// Only the internal content SDK uses this wrapper; the raw SDK is unchanged.
|
|
132
|
+
var body = response.body === null ? null : yield response.arrayBuffer();
|
|
133
|
+
return new Response(body, {
|
|
134
|
+
status: response.status,
|
|
135
|
+
statusText: response.statusText,
|
|
136
|
+
headers: response.headers
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
return function (_x) {
|
|
140
|
+
return _ref4.apply(this, arguments);
|
|
141
|
+
};
|
|
142
|
+
}(), signal);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _nodeEvents = require("node:events");
|
|
4
|
+
var _nodeHttp = require("node:http");
|
|
5
|
+
var _requests = require("./requests.cjs");
|
|
6
|
+
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); }
|
|
7
|
+
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); }); }; }
|
|
8
|
+
function deferred() {
|
|
9
|
+
var resolve;
|
|
10
|
+
var promise = new Promise(yes => {
|
|
11
|
+
resolve = yes;
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
promise,
|
|
15
|
+
resolve
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
var tick = /*#__PURE__*/function () {
|
|
19
|
+
var _ref = _asyncToGenerator(function* () {
|
|
20
|
+
for (var i = 0; i < 10; i++) yield Promise.resolve();
|
|
21
|
+
});
|
|
22
|
+
return function tick() {
|
|
23
|
+
return _ref.apply(this, arguments);
|
|
24
|
+
};
|
|
25
|
+
}();
|
|
26
|
+
describe('Content HTTP admission', () => {
|
|
27
|
+
beforeEach(() => jest.useFakeTimers());
|
|
28
|
+
afterEach(() => jest.useRealTimers());
|
|
29
|
+
it('limits active work, serves queued work in order, and rejects overflow', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
30
|
+
var scheduler = new _requests.FetchScheduler({
|
|
31
|
+
concurrency: 1,
|
|
32
|
+
queueLimit: 2
|
|
33
|
+
});
|
|
34
|
+
var first = deferred();
|
|
35
|
+
var started = [];
|
|
36
|
+
var a = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
37
|
+
started.push(1);
|
|
38
|
+
return first.promise;
|
|
39
|
+
}));
|
|
40
|
+
var b = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
41
|
+
started.push(2);
|
|
42
|
+
return 2;
|
|
43
|
+
}));
|
|
44
|
+
var c = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
45
|
+
started.push(3);
|
|
46
|
+
return 3;
|
|
47
|
+
}));
|
|
48
|
+
var overflow = jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
49
|
+
return 4;
|
|
50
|
+
}));
|
|
51
|
+
yield expect(scheduler.run(overflow)).rejects.toThrow('queue is full');
|
|
52
|
+
expect(started).toEqual([1]);
|
|
53
|
+
expect(overflow).not.toHaveBeenCalled();
|
|
54
|
+
first.resolve(1);
|
|
55
|
+
expect(yield Promise.all([a, b, c])).toEqual([1, 2, 3]);
|
|
56
|
+
expect(started).toEqual([1, 2, 3]);
|
|
57
|
+
yield tick();
|
|
58
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
59
|
+
}));
|
|
60
|
+
it('removes expired and canceled queued work before it reaches the origin', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
61
|
+
var scheduler = new _requests.FetchScheduler({
|
|
62
|
+
concurrency: 1,
|
|
63
|
+
queueLimit: 1
|
|
64
|
+
});
|
|
65
|
+
var first = deferred();
|
|
66
|
+
var a = scheduler.run(() => first.promise);
|
|
67
|
+
var queued = jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
68
|
+
return 2;
|
|
69
|
+
}));
|
|
70
|
+
var expired = expect(scheduler.run(queued)).rejects.toThrow('queue wait timed out');
|
|
71
|
+
yield jest.advanceTimersByTimeAsync(1000);
|
|
72
|
+
yield expired;
|
|
73
|
+
var controller = new AbortController();
|
|
74
|
+
var canceled = expect(scheduler.run(queued, controller.signal)).rejects.toThrow('cancel');
|
|
75
|
+
controller.abort(new Error('cancel'));
|
|
76
|
+
yield canceled;
|
|
77
|
+
var recovered = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
78
|
+
return 3;
|
|
79
|
+
}));
|
|
80
|
+
first.resolve(1);
|
|
81
|
+
expect(yield Promise.all([a, recovered])).toEqual([1, 3]);
|
|
82
|
+
expect(queued).not.toHaveBeenCalled();
|
|
83
|
+
}));
|
|
84
|
+
it('aborts a timed out transport and admits a subsequent retry', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
85
|
+
var scheduler = new _requests.FetchScheduler({
|
|
86
|
+
concurrency: 1,
|
|
87
|
+
requestTimeout: 100
|
|
88
|
+
});
|
|
89
|
+
var signal;
|
|
90
|
+
var timedOut = expect(scheduler.run(s => {
|
|
91
|
+
signal = s;
|
|
92
|
+
return new Promise((_, reject) => s.addEventListener('abort', () => reject(s.reason)));
|
|
93
|
+
})).rejects.toThrow('timed out');
|
|
94
|
+
yield jest.advanceTimersByTimeAsync(100);
|
|
95
|
+
yield timedOut;
|
|
96
|
+
expect(signal.aborted).toBe(true);
|
|
97
|
+
expect(yield scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
98
|
+
return 'recovered';
|
|
99
|
+
}))).toBe('recovered');
|
|
100
|
+
}));
|
|
101
|
+
it('retains the physical slot if custom work ignores abort', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
102
|
+
var scheduler = new _requests.FetchScheduler({
|
|
103
|
+
concurrency: 1,
|
|
104
|
+
queueLimit: 0,
|
|
105
|
+
requestTimeout: 100
|
|
106
|
+
});
|
|
107
|
+
var pending = deferred();
|
|
108
|
+
var timedOut = expect(scheduler.run(() => pending.promise)).rejects.toThrow('timed out');
|
|
109
|
+
yield jest.advanceTimersByTimeAsync(100);
|
|
110
|
+
yield timedOut;
|
|
111
|
+
yield expect(scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
112
|
+
return 2;
|
|
113
|
+
}))).rejects.toThrow('queue is full');
|
|
114
|
+
pending.resolve(1);
|
|
115
|
+
yield tick();
|
|
116
|
+
expect(yield scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
117
|
+
return 3;
|
|
118
|
+
}))).toBe(3);
|
|
119
|
+
}));
|
|
120
|
+
it('forwards active cancellation and never starts an already canceled call', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
121
|
+
var scheduler = new _requests.FetchScheduler({
|
|
122
|
+
concurrency: 1
|
|
123
|
+
});
|
|
124
|
+
var controller = new AbortController();
|
|
125
|
+
var signal;
|
|
126
|
+
var canceled = expect(scheduler.run(s => {
|
|
127
|
+
signal = s;
|
|
128
|
+
return new Promise((_, reject) => s.addEventListener('abort', () => reject(s.reason)));
|
|
129
|
+
}, controller.signal)).rejects.toThrow('cancel');
|
|
130
|
+
yield tick();
|
|
131
|
+
controller.abort(new Error('cancel'));
|
|
132
|
+
yield canceled;
|
|
133
|
+
expect(signal.aborted).toBe(true);
|
|
134
|
+
var invoke = jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
135
|
+
return 1;
|
|
136
|
+
}));
|
|
137
|
+
yield expect(scheduler.run(invoke, controller.signal)).rejects.toThrow('cancel');
|
|
138
|
+
expect(invoke).not.toHaveBeenCalled();
|
|
139
|
+
yield tick();
|
|
140
|
+
expect(yield scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
141
|
+
return 'ok';
|
|
142
|
+
}))).toBe('ok');
|
|
143
|
+
}));
|
|
144
|
+
it('keeps the deadline through response body consumption', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
145
|
+
var scheduler = new _requests.FetchScheduler({
|
|
146
|
+
requestTimeout: 100
|
|
147
|
+
});
|
|
148
|
+
var transport = /*#__PURE__*/function () {
|
|
149
|
+
var _ref17 = _asyncToGenerator(function* (_input, init) {
|
|
150
|
+
return new Response(new ReadableStream({
|
|
151
|
+
start(controller) {
|
|
152
|
+
init.signal.addEventListener('abort', () => controller.error(init.signal.reason));
|
|
153
|
+
}
|
|
154
|
+
}));
|
|
155
|
+
});
|
|
156
|
+
return function transport(_x, _x2) {
|
|
157
|
+
return _ref17.apply(this, arguments);
|
|
158
|
+
};
|
|
159
|
+
}();
|
|
160
|
+
var bounded = (0, _requests.createBoundedFetch)(transport, scheduler);
|
|
161
|
+
var timedOut = expect(bounded('https://example.test')).rejects.toThrow('timed out');
|
|
162
|
+
yield jest.advanceTimersByTimeAsync(100);
|
|
163
|
+
yield timedOut;
|
|
164
|
+
var empty = (0, _requests.createBoundedFetch)(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
165
|
+
return new Response(null, {
|
|
166
|
+
status: 204
|
|
167
|
+
});
|
|
168
|
+
}), scheduler);
|
|
169
|
+
expect((yield empty('https://example.test')).status).toBe(204);
|
|
170
|
+
}));
|
|
171
|
+
});
|
|
172
|
+
it('aborts a real HTTP response stalled after headers and then recovers', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
173
|
+
jest.useRealTimers();
|
|
174
|
+
var server = (0, _nodeHttp.createServer)((request, response) => {
|
|
175
|
+
response.writeHead(200, {
|
|
176
|
+
'Content-Type': 'application/json'
|
|
177
|
+
});
|
|
178
|
+
if (request.url === '/stall') {
|
|
179
|
+
response.write('{');
|
|
180
|
+
} else {
|
|
181
|
+
response.end('{"ok":true}');
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
server.listen(0, '127.0.0.1');
|
|
185
|
+
yield (0, _nodeEvents.once)(server, 'listening');
|
|
186
|
+
var url = "http://127.0.0.1:".concat(server.address().port);
|
|
187
|
+
var fetch = (0, _requests.createBoundedFetch)(globalThis.fetch, new _requests.FetchScheduler({
|
|
188
|
+
concurrency: 1,
|
|
189
|
+
requestTimeout: 200
|
|
190
|
+
}));
|
|
191
|
+
try {
|
|
192
|
+
yield expect(fetch("".concat(url, "/stall"))).rejects.toThrow('timed out');
|
|
193
|
+
var result = yield fetch("".concat(url, "/ok"));
|
|
194
|
+
expect(yield result.json()).toEqual({
|
|
195
|
+
ok: true
|
|
196
|
+
});
|
|
197
|
+
} finally {
|
|
198
|
+
server.closeAllConnections();
|
|
199
|
+
yield new Promise(resolve => server.close(() => resolve()));
|
|
200
|
+
}
|
|
201
|
+
}));
|
|
@@ -0,0 +1,199 @@
|
|
|
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 { once } from 'node:events';
|
|
4
|
+
import { createServer } from 'node:http';
|
|
5
|
+
import { createBoundedFetch, FetchScheduler } from "./requests.mjs";
|
|
6
|
+
function deferred() {
|
|
7
|
+
var resolve;
|
|
8
|
+
var promise = new Promise(yes => {
|
|
9
|
+
resolve = yes;
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
promise,
|
|
13
|
+
resolve
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
var tick = /*#__PURE__*/function () {
|
|
17
|
+
var _ref = _asyncToGenerator(function* () {
|
|
18
|
+
for (var i = 0; i < 10; i++) yield Promise.resolve();
|
|
19
|
+
});
|
|
20
|
+
return function tick() {
|
|
21
|
+
return _ref.apply(this, arguments);
|
|
22
|
+
};
|
|
23
|
+
}();
|
|
24
|
+
describe('Content HTTP admission', () => {
|
|
25
|
+
beforeEach(() => jest.useFakeTimers());
|
|
26
|
+
afterEach(() => jest.useRealTimers());
|
|
27
|
+
it('limits active work, serves queued work in order, and rejects overflow', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
28
|
+
var scheduler = new FetchScheduler({
|
|
29
|
+
concurrency: 1,
|
|
30
|
+
queueLimit: 2
|
|
31
|
+
});
|
|
32
|
+
var first = deferred();
|
|
33
|
+
var started = [];
|
|
34
|
+
var a = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
35
|
+
started.push(1);
|
|
36
|
+
return first.promise;
|
|
37
|
+
}));
|
|
38
|
+
var b = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
39
|
+
started.push(2);
|
|
40
|
+
return 2;
|
|
41
|
+
}));
|
|
42
|
+
var c = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
43
|
+
started.push(3);
|
|
44
|
+
return 3;
|
|
45
|
+
}));
|
|
46
|
+
var overflow = jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
47
|
+
return 4;
|
|
48
|
+
}));
|
|
49
|
+
yield expect(scheduler.run(overflow)).rejects.toThrow('queue is full');
|
|
50
|
+
expect(started).toEqual([1]);
|
|
51
|
+
expect(overflow).not.toHaveBeenCalled();
|
|
52
|
+
first.resolve(1);
|
|
53
|
+
expect(yield Promise.all([a, b, c])).toEqual([1, 2, 3]);
|
|
54
|
+
expect(started).toEqual([1, 2, 3]);
|
|
55
|
+
yield tick();
|
|
56
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
57
|
+
}));
|
|
58
|
+
it('removes expired and canceled queued work before it reaches the origin', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
59
|
+
var scheduler = new FetchScheduler({
|
|
60
|
+
concurrency: 1,
|
|
61
|
+
queueLimit: 1
|
|
62
|
+
});
|
|
63
|
+
var first = deferred();
|
|
64
|
+
var a = scheduler.run(() => first.promise);
|
|
65
|
+
var queued = jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
66
|
+
return 2;
|
|
67
|
+
}));
|
|
68
|
+
var expired = expect(scheduler.run(queued)).rejects.toThrow('queue wait timed out');
|
|
69
|
+
yield jest.advanceTimersByTimeAsync(1000);
|
|
70
|
+
yield expired;
|
|
71
|
+
var controller = new AbortController();
|
|
72
|
+
var canceled = expect(scheduler.run(queued, controller.signal)).rejects.toThrow('cancel');
|
|
73
|
+
controller.abort(new Error('cancel'));
|
|
74
|
+
yield canceled;
|
|
75
|
+
var recovered = scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
76
|
+
return 3;
|
|
77
|
+
}));
|
|
78
|
+
first.resolve(1);
|
|
79
|
+
expect(yield Promise.all([a, recovered])).toEqual([1, 3]);
|
|
80
|
+
expect(queued).not.toHaveBeenCalled();
|
|
81
|
+
}));
|
|
82
|
+
it('aborts a timed out transport and admits a subsequent retry', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
83
|
+
var scheduler = new FetchScheduler({
|
|
84
|
+
concurrency: 1,
|
|
85
|
+
requestTimeout: 100
|
|
86
|
+
});
|
|
87
|
+
var signal;
|
|
88
|
+
var timedOut = expect(scheduler.run(s => {
|
|
89
|
+
signal = s;
|
|
90
|
+
return new Promise((_, reject) => s.addEventListener('abort', () => reject(s.reason)));
|
|
91
|
+
})).rejects.toThrow('timed out');
|
|
92
|
+
yield jest.advanceTimersByTimeAsync(100);
|
|
93
|
+
yield timedOut;
|
|
94
|
+
expect(signal.aborted).toBe(true);
|
|
95
|
+
expect(yield scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
96
|
+
return 'recovered';
|
|
97
|
+
}))).toBe('recovered');
|
|
98
|
+
}));
|
|
99
|
+
it('retains the physical slot if custom work ignores abort', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
100
|
+
var scheduler = new FetchScheduler({
|
|
101
|
+
concurrency: 1,
|
|
102
|
+
queueLimit: 0,
|
|
103
|
+
requestTimeout: 100
|
|
104
|
+
});
|
|
105
|
+
var pending = deferred();
|
|
106
|
+
var timedOut = expect(scheduler.run(() => pending.promise)).rejects.toThrow('timed out');
|
|
107
|
+
yield jest.advanceTimersByTimeAsync(100);
|
|
108
|
+
yield timedOut;
|
|
109
|
+
yield expect(scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
110
|
+
return 2;
|
|
111
|
+
}))).rejects.toThrow('queue is full');
|
|
112
|
+
pending.resolve(1);
|
|
113
|
+
yield tick();
|
|
114
|
+
expect(yield scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
115
|
+
return 3;
|
|
116
|
+
}))).toBe(3);
|
|
117
|
+
}));
|
|
118
|
+
it('forwards active cancellation and never starts an already canceled call', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
119
|
+
var scheduler = new FetchScheduler({
|
|
120
|
+
concurrency: 1
|
|
121
|
+
});
|
|
122
|
+
var controller = new AbortController();
|
|
123
|
+
var signal;
|
|
124
|
+
var canceled = expect(scheduler.run(s => {
|
|
125
|
+
signal = s;
|
|
126
|
+
return new Promise((_, reject) => s.addEventListener('abort', () => reject(s.reason)));
|
|
127
|
+
}, controller.signal)).rejects.toThrow('cancel');
|
|
128
|
+
yield tick();
|
|
129
|
+
controller.abort(new Error('cancel'));
|
|
130
|
+
yield canceled;
|
|
131
|
+
expect(signal.aborted).toBe(true);
|
|
132
|
+
var invoke = jest.fn(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
133
|
+
return 1;
|
|
134
|
+
}));
|
|
135
|
+
yield expect(scheduler.run(invoke, controller.signal)).rejects.toThrow('cancel');
|
|
136
|
+
expect(invoke).not.toHaveBeenCalled();
|
|
137
|
+
yield tick();
|
|
138
|
+
expect(yield scheduler.run(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
139
|
+
return 'ok';
|
|
140
|
+
}))).toBe('ok');
|
|
141
|
+
}));
|
|
142
|
+
it('keeps the deadline through response body consumption', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
143
|
+
var scheduler = new FetchScheduler({
|
|
144
|
+
requestTimeout: 100
|
|
145
|
+
});
|
|
146
|
+
var transport = /*#__PURE__*/function () {
|
|
147
|
+
var _ref17 = _asyncToGenerator(function* (_input, init) {
|
|
148
|
+
return new Response(new ReadableStream({
|
|
149
|
+
start(controller) {
|
|
150
|
+
init.signal.addEventListener('abort', () => controller.error(init.signal.reason));
|
|
151
|
+
}
|
|
152
|
+
}));
|
|
153
|
+
});
|
|
154
|
+
return function transport(_x, _x2) {
|
|
155
|
+
return _ref17.apply(this, arguments);
|
|
156
|
+
};
|
|
157
|
+
}();
|
|
158
|
+
var bounded = createBoundedFetch(transport, scheduler);
|
|
159
|
+
var timedOut = expect(bounded('https://example.test')).rejects.toThrow('timed out');
|
|
160
|
+
yield jest.advanceTimersByTimeAsync(100);
|
|
161
|
+
yield timedOut;
|
|
162
|
+
var empty = createBoundedFetch(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
163
|
+
return new Response(null, {
|
|
164
|
+
status: 204
|
|
165
|
+
});
|
|
166
|
+
}), scheduler);
|
|
167
|
+
expect((yield empty('https://example.test')).status).toBe(204);
|
|
168
|
+
}));
|
|
169
|
+
});
|
|
170
|
+
it('aborts a real HTTP response stalled after headers and then recovers', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
171
|
+
jest.useRealTimers();
|
|
172
|
+
var server = createServer((request, response) => {
|
|
173
|
+
response.writeHead(200, {
|
|
174
|
+
'Content-Type': 'application/json'
|
|
175
|
+
});
|
|
176
|
+
if (request.url === '/stall') {
|
|
177
|
+
response.write('{');
|
|
178
|
+
} else {
|
|
179
|
+
response.end('{"ok":true}');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
server.listen(0, '127.0.0.1');
|
|
183
|
+
yield once(server, 'listening');
|
|
184
|
+
var url = "http://127.0.0.1:".concat(server.address().port);
|
|
185
|
+
var fetch = createBoundedFetch(globalThis.fetch, new FetchScheduler({
|
|
186
|
+
concurrency: 1,
|
|
187
|
+
requestTimeout: 200
|
|
188
|
+
}));
|
|
189
|
+
try {
|
|
190
|
+
yield expect(fetch("".concat(url, "/stall"))).rejects.toThrow('timed out');
|
|
191
|
+
var result = yield fetch("".concat(url, "/ok"));
|
|
192
|
+
expect(yield result.json()).toEqual({
|
|
193
|
+
ok: true
|
|
194
|
+
});
|
|
195
|
+
} finally {
|
|
196
|
+
server.closeAllConnections();
|
|
197
|
+
yield new Promise(resolve => server.close(() => resolve()));
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
@@ -4,9 +4,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.PrezlyAdapter = void 0;
|
|
7
|
+
var _sha = require("@noble/hashes/sha256");
|
|
8
|
+
var _utils = require("@noble/hashes/utils");
|
|
9
|
+
var _jsonStableStringify = _interopRequireDefault(require("json-stable-stringify"));
|
|
7
10
|
var _sdk = require("@prezly/sdk");
|
|
8
11
|
var _themeKitCore = require("@prezly/theme-kit-core");
|
|
9
12
|
var _index = require("./cache/index.cjs");
|
|
13
|
+
var _requests = require("./requests.cjs");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
15
|
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; }
|
|
11
16
|
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; }
|
|
12
17
|
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; }
|
|
@@ -15,32 +20,51 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
15
20
|
var PrezlyAdapter;
|
|
16
21
|
(function (_PrezlyAdapter) {
|
|
17
22
|
function connect(config) {
|
|
18
|
-
var {
|
|
19
|
-
cache
|
|
20
|
-
fetch
|
|
21
|
-
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
23
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
24
|
+
cacheConfig = _ref.cache,
|
|
25
|
+
fetch = _ref.fetch;
|
|
22
26
|
function usePrezlyClient() {
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
accessToken,
|
|
26
|
-
baseUrl,
|
|
27
|
-
headers,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
formats
|
|
32
|
-
} = _themeKitCore.Resolvable.resolve(config);
|
|
27
|
+
var _cacheConfig$namespac, _cacheConfig$memory;
|
|
28
|
+
var _Resolvable$resolve = _themeKitCore.Resolvable.resolve(config),
|
|
29
|
+
accessToken = _Resolvable$resolve.accessToken,
|
|
30
|
+
baseUrl = _Resolvable$resolve.baseUrl,
|
|
31
|
+
headers = _Resolvable$resolve.headers,
|
|
32
|
+
newsroom = _Resolvable$resolve.newsroom,
|
|
33
|
+
theme = _Resolvable$resolve.theme,
|
|
34
|
+
formats = _Resolvable$resolve.formats;
|
|
33
35
|
var client = (0, _sdk.createPrezlyClient)({
|
|
34
36
|
fetch,
|
|
35
37
|
accessToken,
|
|
36
38
|
baseUrl,
|
|
37
39
|
headers
|
|
38
40
|
});
|
|
39
|
-
var
|
|
41
|
+
var cache = cacheConfig ? (0, _index.configure)(_objectSpread({
|
|
42
|
+
namespace: 'content:'
|
|
43
|
+
}, cacheConfig)) : undefined;
|
|
44
|
+
// Custom fetches may depend on hidden request context. Preserve their
|
|
45
|
+
// existing cache behavior unless the caller declares that identity.
|
|
46
|
+
var scope = cache && (!fetch || (cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.requestScope) !== undefined) ? (0, _utils.bytesToHex)((0, _sha.sha256)((0, _jsonStableStringify.default)({
|
|
47
|
+
baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : 'https://api.prezly.com',
|
|
48
|
+
accessToken,
|
|
49
|
+
headers: headers !== null && headers !== void 0 ? headers : {},
|
|
50
|
+
cache: {
|
|
51
|
+
namespace: (_cacheConfig$namespac = cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.namespace) !== null && _cacheConfig$namespac !== void 0 ? _cacheConfig$namespac : 'content:',
|
|
52
|
+
memory: (_cacheConfig$memory = cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.memory) !== null && _cacheConfig$memory !== void 0 ? _cacheConfig$memory : false,
|
|
53
|
+
redis: cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.redis
|
|
54
|
+
},
|
|
55
|
+
requestScope: cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.requestScope
|
|
56
|
+
}))) : undefined;
|
|
57
|
+
var contentClient = cache ? (0, _sdk.createPrezlyClient)({
|
|
58
|
+
accessToken,
|
|
59
|
+
baseUrl,
|
|
60
|
+
headers,
|
|
61
|
+
fetch: (0, _requests.createBoundedFetch)(fetch !== null && fetch !== void 0 ? fetch : globalThis.fetch)
|
|
62
|
+
}) : client;
|
|
63
|
+
var contentDelivery = _themeKitCore.ContentDelivery.createClient(contentClient, newsroom, theme, {
|
|
40
64
|
formats,
|
|
41
|
-
cache:
|
|
42
|
-
|
|
43
|
-
}
|
|
65
|
+
cache: cache ? _objectSpread(_objectSpread({}, cache), {}, {
|
|
66
|
+
scope
|
|
67
|
+
}) : undefined
|
|
44
68
|
});
|
|
45
69
|
return {
|
|
46
70
|
client,
|