kopia lustrzana https://gitlab.com/rysiekpl/libresilient
				
				
				
			
		
			
	
	
		
			102 wiersze
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Markdown
		
	
	
		
		
			
		
	
	
			102 wiersze
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Markdown
		
	
	
|   | # Architecture
 | ||
|  | 
 | ||
|  | Eventually this will document the architecture of LibResilient. | ||
|  | 
 | ||
|  | ## Plugins
 | ||
|  | 
 | ||
|  | There are three kinds of plugins: | ||
|  | 
 | ||
|  | - **Transport plugins**   | ||
|  |   Plugins that *retrieve* website content, e.g. by using regular HTTPS [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), or by going through [IPFS](https://js.ipfs.io/). They *should* also offer a way to *publish* content by website admins (if relevant credentials or encryption keys are provided, depending on the method).   | ||
|  | Methods these plugins implement: | ||
|  |   - `fetch` - fetch content from an external source (e.g., from IPFS) | ||
|  |   - `publish` - publish the content to the external source (e.g., to IPFS) | ||
|  | 
 | ||
|  | - **Stashing plugins**   | ||
|  |   Plugins that *stash* content locally (e.g., in the [browser cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache)) for displaying when no *transport plugin* works, or before content is received via one of them.   | ||
|  | Methods these plugins implement: | ||
|  |   - `fetch` - fetch the locally stored content (e.g., from cache) | ||
|  |   - `stash` - stash the content locally (e.g., in cache) | ||
|  |   - `unstash` - clear the content from the local store (e.g., clear the cache) | ||
|  | 
 | ||
|  | - **Composing plugins**   | ||
|  |   Plugins that *compose* other plugins, for example by running them simultaneously to retrieve content from whichever succeeds first.   | ||
|  | Methods these plugins implement depend on which plugins they compose. Additionally, plugins being composed the `uses` key, providing the configuration for them the same way configuration is provided for plugins in the `plugins` key of `LibResilientConfig`. | ||
|  | 
 | ||
|  | Any plugin needs to add itself to the LibResilientPlugins global variable, using a data structure as follows: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | self.LibResilientPlugins.push({ | ||
|  |     name: 'plugin-name', | ||
|  |     description: 'Plugin description. Just a few words, ideally.', | ||
|  |     version: 'any relevant plugin version information', | ||
|  |     fetch: functionImplementingFetch, | ||
|  |     publish|stash|unstash: functionsImplementingRelevantFunctionality, | ||
|  |     uses: { | ||
|  |         composed-plugin-1: { | ||
|  |             configKey1: "whatever-data-here" | ||
|  |         }, | ||
|  |         composed-plugin-2: { | ||
|  |             configKey2: "whatever-data-here" | ||
|  |         }, | ||
|  |         {...} | ||
|  |     } | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Transport plugins
 | ||
|  | 
 | ||
|  | Transport plugins *must* add `X-LibResilient-Method` and `X-LibResilient-ETag` headers to the response they return, so as to facilitate informing the user about new content after content was displayed using a stashing plugin. | ||
|  | 
 | ||
|  |  - **`X-LibResilient-Method`**:   | ||
|  |    contains the name of the plugin used to fetch the content. | ||
|  | 
 | ||
|  |  - **`X-LibResilient-ETag`**:   | ||
|  |    contains the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) for the content; this can be an actual `ETag` header for HTTPS-based plugins, or some arbitrary string identifying a particular version of the resource (e.g., for IPFS-based plugins this can be the IPFS address, since that is based on content and different content results in a different IPFS address). | ||
|  | 
 | ||
|  | ### Stashing plugins
 | ||
|  | 
 | ||
|  | Stashing plugins *must* stash the request along with the `X-LibResilient-Method` and `X-LibResilient-ETag` headers. | ||
|  | 
 | ||
|  | ### Composing plugins
 | ||
|  | 
 | ||
|  | Composing plugins work by composing other plugins, for example to run them simultaneously and retrieve content from the first one that succeeds. A composing plugin needs to set the `uses` key in it's `LibResilientPlugins`. The key should contain mappings from plugin names to configuration: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | uses: { | ||
|  |     composed-plugin-1: { | ||
|  |         configKey1: "whatever-data-here" | ||
|  |     }, | ||
|  |     composed-plugin-2: { | ||
|  |         configKey2: "whatever-data-here" | ||
|  |     }, | ||
|  |     {...} | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | ## Fetching a resource via LibResilient
 | ||
|  | 
 | ||
|  | Whenever a resource is being fetched on a LibResilient-enabled site, the `service-worker.js` script dispatches plugins in the set order. Currently this order is hard-coded in `service-worker.js`, and is: | ||
|  | 
 | ||
|  | 1. `fetch`, to use the upstream site directly if it is available, | ||
|  | 1. `cache`, to display the site immediately from the cache in case regular `fetch` fails, | ||
|  | 1. `gun-ipfs`, in the background if `cache` call succeeded, otherwise as the active fetch handler. | ||
|  | 
 | ||
|  | If a background plugin `fetch()` succeeds, the result is added to the cache and will be immediately available on page reload. | ||
|  | 
 | ||
|  | ## Stashed versions invalidation
 | ||
|  | 
 | ||
|  | Invalidation heuristic is rather naïve, and boils down to checking if either of `X-LibResilient-Method` or `X-LibResilient-ETag` differs between the response from a transport plugin and whatever has already been stashed by a stashing plugin. If either differs, the transport plugin response is considered "*fresher*". | ||
|  | 
 | ||
|  | This is far from ideal and will need improvements in the long-term. The difficulty is that different transport plugins can provide different ways of determining the "*freshness*" of fetched content -- HTTPS-based requests offer `ETag`, `Date`, `Last-Modified`, and other headers that can help with that; whereas IPFS can't really offer much apart from the address which itself is a hash of the content, so at least we know the content is *different* (but is it *fresher* though?). | ||
|  | 
 | ||
|  | ## Messaging
 | ||
|  | 
 | ||
|  | The ServiceWorker can communicate with the browser window using the [`Client.postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage) to post messages to the browser window context using the relevant [`Client ID`](https://developer.mozilla.org/en-US/docs/Web/API/Client/id), retrieved from the fetch event object. | ||
|  | 
 | ||
|  | When the browser window context wants to message the service worker, it uses the [`Worker.postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) call, with `clientId` field set to the relevant client ID if a response is expected. ServiceWorker then again responds using `Client.postMessage()` using the `clientId` field as source of the `Client ID`. | ||
|  | 
 | ||
|  | ### Messages
 | ||
|  | 
 | ||
|  | This section is a work in progress. |