UNPKG

6.55 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.FollowPromiseMany = exports.FollowPromiseOne = void 0;
4const link_1 = require("./link");
5const uri_1 = require("./util/uri");
6const uri_template_1 = require("./util/uri-template");
7/**
8 * Base interface for both FollowOne and FollowAll
9 */
10class FollowPromise {
11 constructor() {
12 this.prefetchEnabled = false;
13 this.preferPushEnabled = false;
14 this.preferTranscludeEnabled = false;
15 this.useHeadEnabled = false;
16 }
17 preFetch() {
18 this.prefetchEnabled = true;
19 return this;
20 }
21 preferPush() {
22 this.preferPushEnabled = true;
23 return this;
24 }
25 preferTransclude() {
26 this.preferTranscludeEnabled = true;
27 return this;
28 }
29 /**
30 * Use a HTTP HEAD request to fetch the links.
31 *
32 * This is useful when interacting with servers that embed links in Link
33 * Headers.
34 */
35 useHead() {
36 this.useHeadEnabled = true;
37 return this;
38 }
39}
40/**
41 * The FollowPromise class is what's being returned from follow() functions.
42 *
43 * It's 'PromiseLike', which means you can treat it like a Promise, and it
44 * can be awaited. When used as a Promise, it resolves to the Resource object
45 * that was followed.
46 *
47 * In addition to being a Promise<Resource> stand-in, it also exposes other
48 * functions, namely:
49 *
50 * * `follow()` to allow a user to chain several follow() functions to do
51 * several 'hops' all at once.
52 * * `followAll()`, allowing a user to call `followAll()` at the end of a
53 * chain.
54 */
55class FollowPromiseOne extends FollowPromise {
56 constructor(resource, rel, variables) {
57 super();
58 this.resource = resource;
59 this.rel = rel;
60 this.variables = variables;
61 }
62 /**
63 * This 'then' function behaves like a Promise then() function.
64 *
65 * This method signature is pretty crazy, but trust that it's pretty much
66 * like any then() method on a promise.
67 */
68 then(onfulfilled, onrejected) {
69 return this.fetchLinkedResource().then(onfulfilled, onrejected);
70 }
71 /**
72 * This 'catch' function behaves like a Promise catch() function.
73 */
74 catch(onrejected) {
75 return this.fetchLinkedResource().then(undefined, onrejected);
76 }
77 /**
78 * Implementation of a Promise.finally function
79 */
80 finally(onfinally) {
81 return this.then(() => onfinally(), () => onfinally());
82 }
83 /**
84 * Follow another link immediately after following this link.
85 *
86 * This allows you to follow several hops of links in one go.
87 *
88 * For example: resource.follow('foo').follow('bar');
89 */
90 follow(rel, variables) {
91 return new FollowPromiseOne(this.fetchLinkedResource(), rel, variables);
92 }
93 /**
94 * Follows a set of links immediately after following this link.
95 *
96 * For example: resource.follow('foo').followAll('item');
97 */
98 followAll(rel) {
99 return new FollowPromiseMany(this.fetchLinkedResource(), rel);
100 }
101 /**
102 * This function does the actual fetching of the linked
103 * resource.
104 */
105 async fetchLinkedResource() {
106 const resource = await this.resource;
107 const headers = {};
108 if (this.preferPushEnabled) {
109 headers['Prefer-Push'] = this.rel;
110 }
111 if (!this.useHeadEnabled && this.preferTranscludeEnabled) {
112 headers.Prefer = 'transclude=' + this.rel;
113 }
114 let state;
115 if (this.useHeadEnabled) {
116 state = await resource.head({ headers });
117 }
118 else {
119 state = await resource.get({
120 headers
121 });
122 }
123 const link = state.links.get(this.rel);
124 if (!link)
125 throw new link_1.LinkNotFound(`Link with rel ${this.rel} on ${state.uri} not found`);
126 let href;
127 if (link.templated && this.variables) {
128 href = uri_template_1.expand(link, this.variables);
129 }
130 else {
131 href = uri_1.resolve(link);
132 }
133 const newResource = resource.go(href);
134 if (this.prefetchEnabled) {
135 newResource.get().catch(err => {
136 // eslint-disable-next-line no-console
137 console.warn('Error while prefetching linked resource', err);
138 });
139 }
140 return newResource;
141 }
142}
143exports.FollowPromiseOne = FollowPromiseOne;
144/**
145 */
146class FollowPromiseMany extends FollowPromise {
147 constructor(resource, rel) {
148 super();
149 this.resource = resource;
150 this.rel = rel;
151 }
152 /**
153 * This 'then' function behaves like a Promise then() function.
154 */
155 then(onfulfilled, onrejected) {
156 return this.fetchLinkedResources().then(onfulfilled, onrejected);
157 }
158 /**
159 * This 'catch' function behaves like a Promise catch() function.
160 */
161 catch(onrejected) {
162 return this.fetchLinkedResources().then(undefined, onrejected);
163 }
164 /**
165 * Implementation of a Promise.finally function
166 */
167 finally(onfinally) {
168 return this.then(() => onfinally(), () => onfinally());
169 }
170 /**
171 * This function does the actual fetching, to obtained the url
172 * of the linked resource. It returns the Resource object.
173 */
174 async fetchLinkedResources() {
175 const resource = await this.resource;
176 const headers = {};
177 if (this.preferPushEnabled) {
178 headers['Prefer-Push'] = this.rel;
179 }
180 if (!this.useHeadEnabled && this.preferTranscludeEnabled) {
181 headers.Prefer = 'transclude=' + this.rel;
182 }
183 let state;
184 if (this.useHeadEnabled) {
185 state = await resource.head({ headers });
186 }
187 else {
188 state = await resource.get({
189 headers
190 });
191 }
192 const links = state.links.getMany(this.rel);
193 let href;
194 const result = [];
195 for (const link of links) {
196 href = uri_1.resolve(link);
197 const newResource = resource.go(href);
198 result.push(newResource);
199 if (this.prefetchEnabled) {
200 newResource.get().catch(err => {
201 // eslint-disable-next-line no-console
202 console.warn('Error while prefetching linked resource', err);
203 });
204 }
205 }
206 return result;
207 }
208}
209exports.FollowPromiseMany = FollowPromiseMany;
210//# sourceMappingURL=follow-promise.js.map
\No newline at end of file