From f5b093f50bd226a9d477cca8b5c5c6954d3196ce Mon Sep 17 00:00:00 2001 From: Christian Westgaard Date: Fri, 20 Sep 2024 09:59:01 +0200 Subject: [PATCH] Remove old documentation, and document requestHandler --- docs/api/buildGetter.adoc | 32 --- docs/api/get.adoc | 21 -- docs/api/getStaticUrl.adoc | 59 ------ docs/api/index.adoc | 209 -------------------- docs/api/requestHandler.adoc | 100 ++++++++++ docs/{concepts/index.adoc => concepts.adoc} | 0 docs/concepts/mutability.adoc | 2 + docs/concepts/why.adoc | 4 +- docs/index.adoc | 48 ++--- docs/todo.adoc | 25 --- 10 files changed, 120 insertions(+), 380 deletions(-) delete mode 100644 docs/api/buildGetter.adoc delete mode 100644 docs/api/get.adoc delete mode 100644 docs/api/getStaticUrl.adoc delete mode 100644 docs/api/index.adoc create mode 100644 docs/api/requestHandler.adoc rename docs/{concepts/index.adoc => concepts.adoc} (100%) delete mode 100644 docs/todo.adoc diff --git a/docs/api/buildGetter.adoc b/docs/api/buildGetter.adoc deleted file mode 100644 index 33a754e6..00000000 --- a/docs/api/buildGetter.adoc +++ /dev/null @@ -1,32 +0,0 @@ -[[api-buildgetter]] -= .buildGetter - -Sets up and returns a reusable resource-getter function. - -Can be used in three ways: - -`const getStatic = libStatic.buildGetter(root);` - -`const getStatic = libStatic.buildGetter(root, options);` - -`const getStatic = libStatic.buildGetter(optionsWithRoot);` - -The getter function (`getStatic`) takes the link:https://developer.enonic.com/docs/xp/stable/framework/http#http-request[XP request object] as argument. `request` is used to determine the asset path, and to check the `If-None-Match` header. It then returns a link:../api/index#behaviour[response object] for the asset: - -`const response = getStatic(request);` - -An ETag value is generated and cached for the requested asset. If that matches the `If-None-Match` header in the request, the response will only contain: `{status: 304}`, signifying the asset hasn't changed and the cache can be used instead of downloading the asset. If there's no match, the asset will be read out and returned in the link:../api/index#behaviour[response] under `body`, with a `status` 200. - - -[[buildgetter-params]] -== Params: -- `root` (string): path to a root folder where resources are found. This string points to a root folder in the built JAR. - > NOTE: The phrase _"a root folder in the built JAR"_ is accurate, but if you think JAR's can be a bit obscure here's an easier mental model: `root` points to a folder below and relative to the _build/resources/main_. This is where all assets are collected when building the JAR. And when running XP in link:https://developer.enonic.com/docs/enonic-cli/master/dev#start[dev mode], it actually IS where assets are served from. Depending on specific build setups, you can also think of `root` as being relative to _src/main/resources/_. -- `options` (object): add an link:../api/index#options[options object] after `path` to control behaviour for all responses from the returned getter function. -- `optionsWithRoot` (object): same as above: an link:../api/index#options[options object]. But when used as the first and only argument, this object _must_ also include a `{ root: ..., }` attribute too - a root string same as above. This is simply for convenience if you prefer named parameters instead of a positional `root` argument. If both are supplied, the positional `root` argument is used. - -If `root` (either as a string argument or as an attribute in a `options` object) resolves to (or outside) the JAR root, contains `..` or any of the characters `: | < > ' " ยด * ?` or backslash or backtick, or is missing or empty, an error is thrown. - -Again, you need to call the returned getter function to actually get a response. - -๐Ÿ‘‰ link:../examples/service#example-service[Usage examples] diff --git a/docs/api/get.adoc b/docs/api/get.adoc deleted file mode 100644 index 68a89632..00000000 --- a/docs/api/get.adoc +++ /dev/null @@ -1,21 +0,0 @@ -[[api-get]] -= .get -A specific-recource getter method, returns a link:../api/index#behaviour[response object] for the particular asset that's named in the argument string. - -Three optional and equivalent syntaxes: - -`const response = libStatic.get(path);` - -`const response = libStatic.get(path, options);` - -`const response = libStatic.get(optionsWithPath);` - - -== Params: -- `path` (string): path and full file name to an asset file, relative to the JAR root (or relative to _build/resources/main_ in XP dev mode, see link:buildGetter#buildgetter-params[the 'root' param explanation] above. Cannot contain `..` or any of the characters `: | < > ' " ยด * ?` or backslash or backtick. -- `options` (object): add an link:../api/index#options[options object] after `path` to control behaviour for this specific response. -- `optionsWithPath` (object): same as above, an link:../api/index#options[options object] but when used as the first and only argument, this object _must_ include a `{ path: ..., }` attribute too - a path string same as above. This is simply for convenience if you prefer named parameters instead of a positional `path` argument. If both are supplied, the positional `path` argument is used. - -If `path` (either as a string argument or as an attribute in a `options` object) resolves to (or outside) the JAR root, contains `..` or any of the characters `: | < > ' " ยด * ?` or backslash or backtick, or is missing or empty, an error is thrown. - -๐Ÿ‘‰ link:../examples/get#example-get[Usage examples] diff --git a/docs/api/getStaticUrl.adoc b/docs/api/getStaticUrl.adoc deleted file mode 100644 index 82ce6441..00000000 --- a/docs/api/getStaticUrl.adoc +++ /dev/null @@ -1,59 +0,0 @@ -= getStaticUrl - -This function generates a URL pointing to a static file. - -== Parameters - -[%header,cols="1%,1%,98%a"] -[frame="none"] -[grid="none"] -|=== -| Name | Type | Description -| params | object | Input parameters as JSON - -[%header,cols="1%,1%,1%,1%,96%a"] -[frame="topbot"] -[grid="none"] -[caption=""] -.Properties -!=== -! Name ! Type ! Attributes ! Default ! Description -! path ! string ! ! ! Path to the asset, relative to the root -! type ! string ! ! server ! URL type. Either server (server-relative URL) or absolute -! params ! object ! ! ! Custom parameters to append to the url -!=== -|=== - -[.lead] -Returns - -*string* : The generated URL. - -[.lead] -Example - -[source, TypeScript] ----- -import { getStaticUrl } from '/lib/enonic/static'; - -export function get(_request) { - - const url = getStaticUrl({ - path: 'styles/main.css' - }); - - return { - body: ` - - - - - -

Hello, world!

- - -`, - contentType: 'text/html' - }; -} ----- diff --git a/docs/api/index.adoc b/docs/api/index.adoc deleted file mode 100644 index a10bc231..00000000 --- a/docs/api/index.adoc +++ /dev/null @@ -1,209 +0,0 @@ -[[api]] -== API: functions - -Three controller functions are exposed. - -- The first, link:buildgetter#api-buildgetter[buildgetter], is a broad configure-once/catch-all approach that's based on the relative path in the request. This is the one you usually want. -- The second, link:get#api-get[get], specifically gets an asset based on a path string and options for each particular call. -- The third, link:getstaticurl[getStaticUrl], is a helper function that generates a URL pointing to a static file. - -๐Ÿ‘‰ link:get#example-get[Similarities and differences] - -* <> -* <> -* <> - -[[behaviour]] -== API: response and default behaviour -Unless some of these aspects are overriden by an link:../api/index#options[options parameter], the returned object (from both `.get` and the getter function created by `.buildGetter`) is a standard link:https://developer.enonic.com/docs/xp/stable/framework/http#http-response[XP response object] ready to be returned from an XP controller. - -**Response signature:** - ----- -{ status, body, contentType, headers } ----- - -For example: - ----- -{ - status: 200, - body: "I am some content", - contentType: "text/plain", - headers: { - 'Cache-Control': 'public, max-age=31536000, immutable', - ETag: '"12a39b87c43d7e4f5"' - } -} ----- - -๐Ÿ‘‰ link:#example-output[Output: intro/example] - -{zwsp} + - -[[index-fallback]] -==== Index fallback - -If the URL points to a folder instead of a file, and that folder contains a fallback file (`index.html`), the fallback file is served with the appropriate contentType and a cache-busting Cache-Control header. - -If the folder-name URL does not end with a trailing slash, this slash is automatically added via a redirect. This is to ensure that later relative links will work. - -This is a feature in link:#api-buildgetter[.buildGetter], but not link:#api-get[.get] - if you use .get you must implement it yourself. - -[[osgi-bug]] -NOTE: A workaround for a link:https://issues.apache.org/jira/browse/FELIX-6294[a bug in the underlying OSGi system] causes the following behaviour in current versions of lib-static: directories can be referenced to get an index fallback both with and without a trailing slash - but **empty files cannot be served and will cause a status `404` response instead**. When a fix for the underlying bug is available, lib-static will be updated to support both empty files and directories with index fallback. - - - -{zwsp} + - -[[status]] -==== status - -Standard link:https://en.wikipedia.org/wiki/List_of_HTTP_status_codes[HTTP error codes]: - -- `200` (OK): successful, resource fetched. Either the resource path pointed to a readable file, or to a folder where a link:#index-fallback[index fallback] file was found (index fallback is an automatic feature of link:#api-buildgetter[.buildGetter], but not link:#api-get[.get]). -- `303` (Redirect): resource path hit a folder with an index fallback file in it, but the path doesnt end with a slash. It needs the slash, so make a redirect to add it. This is an automatic feature of link:#api-buildgetter[.buildGetter], but not link:#api-get[.get]. -- `304` (Not Modified): matching ETag - the requested resource hasn't changed since a previous download. So a response with this status only is a signal to browsers to reuse their locally cached resource instead of downloading it again. This is an automatic feature of link:#api-buildgetter[.buildGetter], but not link:#api-get[.get]. -- `400` (Bad Request): the resource path is illegal, that is, resolves to an empty path or contains illegal characters: `: | < > ' " ยด * ?` or backslash or backtick. -- `404` (Not Found): a valid resource path, but it doesn't point to a readable file or a directory with an index fallback in it. Currently, it can also signify an link:#osgi-bug[empty file]. -- `500` (Error): a server-side error happened. Details will be found in the server log, but not returned to the user. - -{zwsp} + - -[[body]] -==== body - -On status-`200` responses, this is the content of the requested asset. Can be text or binary, depending on the file and type. May also carry error messages. - -Empty on status-`304`. - -Interally in XP (before returning it to the browser), this content is not a string but a **resource stream** from link:https://developer.enonic.com/docs/xp/stable/api/lib-io[ioLib] (see resource.getStream). This works seamlessly for returning both binary and non-binary files in the response directly to browsers. But might be less straightforward when writing tests or otherwise intercepting the output. - -In link:https://developer.enonic.com/docs/enonic-cli/master/dev#start[XP dev mode], `400`- and and `404`-status errors will have the requested asset path in the body. - -{zwsp} + - -[[content-type]] -==== contentType - -link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types[MIME type] string, after best-effort-automatically determining it from the requested asset. Will be `text/plain` on error messages. - -{zwsp} + - -[[headers]] -==== headers - -**Default headers** optimized for immutable and link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#private_browser_caches[browser cached] resources. - -Typically, there's an `ETag` and a `Cache-Control` attribute, but this may depend on whether they are active in link:../api/index#options[options], and on XP runtime mode: ETag is usually switched off in dev mode. - -[NOTE] -==== -**Important:** mutable assets should not be served with the default 'Cache-Control' header: `'public, max-age=31536000, immutable'`. - -๐Ÿ‘‰ link:#mutable-headers[Handling mutable assets] -==== - - - -{zwsp} + -{zwsp} + -{zwsp} + - - -[[options]] -== API: options and overrides - -As described above, an options object can be added with optional attributes to **override** the link:../api/index#behaviour[default behaviour]: - -.For .buildGetter: ----- -{ cacheControl, contentType, etag, getCleanPath, throwErrors } ----- - -.For .get: ----- -{ cacheControl, contentType, etag, throwErrors } ----- - -{zwsp} + - -[[option-cachecontrol]] -==== cacheControl - -(boolean/string/function) Override the default `Cache-Control` header value (`'public, max-age=31536000, immutable'`). - - - if set as a `false` boolean, no `Cache-Control` headers are sent. A `true` boolean is just ignored. - - if set as a string, always use that value. An empty string will act as `false` and switch off cacheControl. - - if set as a function: `(filePathAndName, resource, mimeType) => cacheControl`. For fine-grained control which can use resource path, resolved MIMEtype string, or file content if needed. _filePathAndName_ is the asset's file path and name (relative to the JAR root, or `build/resources/main/` in dev mode). File content is by resource object: _resource_ is the output from link:https://developer.enonic.com/docs/xp/stable/api/lib-io#getresource[ioLib getResource], so your function should handle this if used. This function and the string it returns is meant to replace the default header handling. -+ -NOTE: A trick: if a _cacheControl_ function returns `null`, lib-static's default Cache-Control header will be used. - -An output _cacheControl_ string is used directly in the response. - -๐Ÿ‘‰ link:#example-cache[Usage example] - -{zwsp} + - -[[option-contenttype]] -==== contentType - -(string/boolean/object/function) Override the built-in link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types[MIME type] detection. - - - if set as a boolean, switches MIME type handling on/off. `true` is basically ignored (keep using built-in type detection), `false` skips processing and removes the content-type header (same as an empty string) - - if set as a non-empty string, assets will not be processed to try and find the MIME content type. Instead this value will always be preselected and returned. - - if set as an object, keys are file types (the extensions of the asset file names _after compilation_, case-insensitive and will ignore dots), and values are Content-Type strings - for example, `{"json": "application/json", ".mp3": "audio/mpeg", "TTF": "font/ttf"}`. For files with extensions that are not among the keys in the object, the handling will fall back to the built-in handling. - - if set as a function: `(filePathAndName, resource) => contentType`. _filePathAndName_ is the asset file path and name (relative to the JAR root, or `build/resources/main/` in dev mode). File content is by resource object: _resource_ is the output from link:https://developer.enonic.com/docs/xp/stable/api/lib-io#getresource[ioLib getResource], so your function should handle this if used. -+ -NOTE: Same trick as for the _cacheControl_ function above: if a _contentType_ function returns `null`, the processing falls back to the default: built-in MIME type detection. - -An output _contentType_ string is used directly in the response. - -๐Ÿ‘‰ link:#example-content[Usage example] - -{zwsp} + - -[[option-etag]] -==== etag - -(boolean) The default behaviour of lib-static is to generate/handle ETag in prod, while skipping it entirely in dev mode. - - Setting the etag parameter to `false` will turn **off** etag processing (runtime content processing, headers and handling) in **prod** too. - - Setting it to `true` will turn it **on in dev mode** too. - -๐Ÿ‘‰ link:#example-etag[Usage example] - -{zwsp} + - -[[option-getcleanpath]] -==== getCleanPath - -(function) Only used in link:#api-buildgetter[.buildGetter]. The default behaviour of the returned `getStatic` function is to take a request object, and compare the beginning of the current requested path (`request.rawPath`) to the endpoint's own root path (`request.contextPath`) and get a relative asset path below `root` (so that later, prefixing the `root` value to that relative path will give the absolute full path to the resource in the JAR). In cases where this default behaviour is not enough, you can override it by adding a `getCleanPath` param: `(request) => ''`. Emphasis: the returned 'clean' path from this function should be _relative to the `root` folder_, not an absolute path in the JAR. - - - **For example:** if a controller _getAnyStatic.es6_ is accessed with a link:https://developer.enonic.com/docs/xp/stable/cms/mappings[controller mapping] at _https://someDomain.com/resources/public_, then that's an endpoint with the path `resources/public` - but that can't be determined from the request. So the automatic extraction of a relative path needs a `getCleanPath` override. Super simplified here: -+ ----- - const getStatic = libStatic.buildGetter( - 'my-resources', - { - getCleanPath: (request) => { - if (!request.rawPath.startsWith('resources/public')) { throw Error('Ooops'); } - return request.rawPath.substring('resources/public'.length); - } - } - ); ----- -+ -Now, since `request.rawPath` doesn't include the protocol or domain, the URL https://someDomain.com/resources/public/subfolder/target-resource.xml will give `request.rawPath` this value: `"resources/public/subfolder/target-resource.xml"`. So the `getCleanPath` function will return `"/subfolder/target-resource.xml"`, which together with the root, `"my-resources"`, will look up the resource _/my-resources/subfolder/target-resource.xml_ in the JAR (or in XP dev mode: _build/resources/main/my-resources/subfolder/target-resource.xml_). - -๐Ÿ‘‰ link:path#example-getcleanpath[Another usage example] - -[[option-throwerrors]] -==== throwErrors - -(boolean, default value is `false`) By default, the `.get` method should not throw errors when used correctly. Instead, it internally server-logs (and hash-ID-tags) errors and automatically outputs a 500 error response. - - - Setting `throwErrors` to `true` overrides this: the 500-response generation is skipped, and the error is re-thrown down to the calling context, to be handled there. - - This does not apply to 400-bad-request and 404-not-found type "errors", they will always generate a 404-response either way. 200 and 304 are also untouched, of course. - -๐Ÿ‘‰ link:#example-errors[Usage example] diff --git a/docs/api/requestHandler.adoc b/docs/api/requestHandler.adoc new file mode 100644 index 00000000..4e3ccfa8 --- /dev/null +++ b/docs/api/requestHandler.adoc @@ -0,0 +1,100 @@ += requestHandler + +This function + +== Parameters + +[%header,cols="1%,1%,98%a"] +[frame="none"] +[grid="none"] +|=== +| Name | Type | Description +| params | object | Input parameters as JSON + +[%header,cols="1%,1%,1%,1%,96%a"] +[frame="topbot"] +[grid="none"] +[caption=""] +.Properties +!=== +! Name ! Type ! Attributes ! Default ! Description +! request ! Request ! ! null ! The request object +! cacheControl ! ({contentType?: string, path?: string, resource?: Resource}) => string \| null ! webAppCacheControl ! ! Function that returns the cache control header value +! contentType ! (path?: string, resource?: Resource) => string \| null ! ! getMimeType() ! Function that returns the content type header value +! etag ! boolean ! ! true ! Whether to include ETag header +! index ! string ! ! 'index.html' ! Default file to serve when requesting a directory +! relativePath ! (Request) => string ! ! getRelativeResourcePath() ! Function that returns the relative path to the resource +! root ! string ! ! '/static' ! Root path to serve files from +! throwErrors ! boolean ! ! true ! Whether to throw errors or return a response object +!=== +|=== + +[.lead] +Returns + +*Response* : The response object. + +[.lead] +Example + +.src/main/resources/webapp/webapp.ts +[source, TypeScript] +---- +import type {Request, Response} from '/lib/enonic/static'; +import {requestHandler} from '/lib/enonic/static'; + +import Router from '/lib/router'; +const router = Router(); + +router.get('{path:.*}', (request: Request): Response => requestHandler({request})); + +export const all = (request: Request) => router.dispatch(request); +---- + +== Full example +.src/main/resources/webapp/webapp.ts +[source, TypeScript] +---- +import type {Request, Response} from '/lib/enonic/static'; +import { + RESPONSE_CACHE_CONTROL, + requestHandler, + webAppCacheControl + } from '/lib/enonic/static'; + +import Router from '/lib/router'; +const router = Router(); + +router.get('{path:.*}', (request: Request): Response => requestHandler({ + request, + cacheControl: ({contentType, path, resource}) => { + // First do custom cache control: + if (path.startsWith('/favicon')) { + // Currently: public, max-age=10, s-maxage=3600, stale-while-revalidate=50 + return RESPONSE_CACHE_CONTROL.SAFE; + } + // Then fallback to the default cache control + return webAppCacheControl({ + contentType, + path, + resource + }); + }, + contentType: ({path, resource: _resource}) => { + if (path.endsWith('.css')) { + return 'text/css'; + } + return 'application/octet-stream'; + }, + etag: true, // This is the default + index: 'index.html', // This is the default + relativePath: (request) => { + // Simplification of the default implementation: + return request.rawPath.substring(request.contextPath.length); + }, + root: '/static', + throwErrors: true +})); + +export const all = (request: Request) => router.dispatch(request); +---- diff --git a/docs/concepts/index.adoc b/docs/concepts.adoc similarity index 100% rename from docs/concepts/index.adoc rename to docs/concepts.adoc diff --git a/docs/concepts/mutability.adoc b/docs/concepts/mutability.adoc index 40921952..ad6b5b03 100644 --- a/docs/concepts/mutability.adoc +++ b/docs/concepts/mutability.adoc @@ -5,6 +5,8 @@ **Mutable assets** on the other hand are any files whose content _may_ change and still keep the same filename/path/URL. +Some relevant sources: link:https://web.dev/http-cache/[web.dev], link:https://engineering.fb.com/2017/01/26/web/this-browser-tweak-saved-60-of-requests-to-facebook/[facebook], link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching[mozilla], link:https://imagekit.io/blog/ultimate-guide-to-http-caching-for-static-assets/[imagekit], link:https://freecontent.manning.com/caching-assets/[freecontent.manning.com]. + [[mutable-headers]] == Headers diff --git a/docs/concepts/why.adoc b/docs/concepts/why.adoc index 8f4aa5e9..918a536b 100644 --- a/docs/concepts/why.adoc +++ b/docs/concepts/why.adoc @@ -3,10 +3,10 @@ Enonic XP already comes with an link:https://developer.enonic.com/docs/xp/stable/runtime/engines/asset-service[asset service], where you can just put resources in the _/assets_ root folder and use `portal.assetUrl(resourcePath)` to generate URLs from where to fetch them. Lib-static basically does the same thing, but with more features and control: -- **Caching behaviour:** With `assetUrl`, you get a URL where the current installation/version of the app is baked in as a hash. It will change whenever the app is updated, forcing browsers to skip their locally cached resources and request new ones, even if the resource wasn't changed during the update. Using lib-static with link:mutability#mutable-assets[immutable assets] retains stable URLs and has several ways to adapt the header to direct browsers' caching behaviour more effectively, even for mutable assets. +- **Caching behaviour:** With `assetUrl`, you get a URL where the current installation/version of the app is baked in as a fingerprint. It will change whenever the app is updated, forcing browsers to skip their locally cached resources and request new ones, even if the resource wasn't changed during the update. Using lib-static with link:mutability#mutable-assets[immutable assets] retains stable URLs and has several ways to adapt the header to direct browsers' caching behaviour more effectively, even for mutable assets. - **Endpoint URLs:** make your resource endpoints anywhere, - **Response headers**: override and control the MIME-type resolution, or the Cache-Control headers more specifically - **Control resource folders:** As long as the resources are built into the app JAR, resources can be served from anywhere - even with multiple lib-static instances at once: serve from multiple specific-purpose folders, or use multi-instances to specify multiple rules from the same folder. - - Security issues around this are handled in the standard usage: a set root folder is required (and not at the JAR root), and URL navigation out from it is prevented. But if you still REALLY want to circumvent this, there is a lower-level API too. + - Security issues around this are handled in the standard usage: a set root folder is required (and not at the JAR root), and URL navigation out from it is prevented. - **Error handling:** 500-type errors can be set to throw instead of returning an error response - leaving the handling to you. - **Index fallback:** A URL that refers to the name of a directory that contains a fallback file (`index.html`), will fetch the fallback file. diff --git a/docs/index.adoc b/docs/index.adoc index 8aa4ad21..df1fff0d 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -8,17 +8,10 @@ [[intro]] == Intro -link:https://enonic.com/developer-tour[Enonic XP] library for serving assets from a folder in the application resource structure. The aim is _"perfect client-side and network caching"_ via response headers - with basic error handling included, and a simple basic usage but highly configurable (modelled akin to link:https://www.npmjs.com/package/serve-static[serve-static]). +link:https://enonic.com/developer-tour[Enonic XP] library for serving assets from a folder in the application resource structure. The aim is _"perfect client-side and network caching"_ via response headers - with basic error handling included. Intended for setting up XP endpoints that serve static files in a cache-optimized way. Optimally, these should be **immutable files** (files whose content aren't meant to change, that is, can be trusted to never change without changing the file name), but lib-static also handles ETags which provide caching with dynamic files too (link:concepts/mutability#mutable-assets[more about handling mutability]). -Some relevant sources: link:https://web.dev/http-cache/[web.dev], link:https://engineering.fb.com/2017/01/26/web/this-browser-tweak-saved-60-of-requests-to-facebook/[facebook], link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching[mozilla], link:https://imagekit.io/blog/ultimate-guide-to-http-caching-for-static-assets/[imagekit], link:https://freecontent.manning.com/caching-assets/[freecontent.manning.com]. - -== Concepts - -* <> -* <> - {zwsp} + [[get-started]] @@ -43,35 +36,26 @@ repositories { === Import In any link:https://developer.enonic.com/docs/xp/stable/framework/controllers[XP controller], import the library: +Ecma/TypeScript: +[source,typescript,options="nowrap"] +---- +import {requestHandler} from '/lib/enonic/static'; +---- + +JavaScript: [source,javascript,options="nowrap"] ---- const libStatic = require('/lib/enonic/static'); ---- -== Service - -* <> -* <> == API -* <> -* <> -* <> - -== Custom examples - -Using low-level API functions <> or <>: - -1. <> -2. <> -3. <> -4. <> -5. <> -6. <> -7. <> -8. <> -9. <> -10. <> -11. <> -12 <> \ No newline at end of file +* <> + +== Examples + +== Concepts + +* <> +* <> diff --git a/docs/todo.adoc b/docs/todo.adoc deleted file mode 100644 index 20f80c2c..00000000 --- a/docs/todo.adoc +++ /dev/null @@ -1,25 +0,0 @@ -= TODO: Later versions - -== Options params -- `indexFallback` (`false`, string, string array, object or function(absolutePath -> stringOrStringarrayOrFalse)): filename(s) (without slashes or path) to fall back to, look for and serve, in cases where the asset path requested is a folder. If not set, requesting a folder will yield an error. Implementaion: before throwing a 404, check if postfixing any of the chosen /index files (with the slash) resolves it. If so, return that. - The rest is up to the developer, and their responsibility how it's used: what htm/html/other they explicitly add in this parameter. And cache headers, just same as if they had asked directly for the index file. Set to `false` (or have the object or function return it) to skip the index fallback. - -== Response -- `'Last-Modified'` header, determined on file modified date - -== Range handling -- `'Accept-Ranges': 'bytes'` header - -== .resolvePath(globPath/regex, root) -Probably not in this lib? Worth mentioning though: - -To save huge complexity (detecting at buildtime what the output and unpredictable hash will be and hooking those references up to output), there should be a function that can resolve a fingerprinted asset filename at XP runtime: `resolvePath(globPath, root)`. - -For example, if a fingerprinted asset _bundle.92d34fd72.js_ is built into _/static_, then resolvePath('bundle.*.js', 'static') will look for matching files within _/static_ and return the string `"bundle.92d34fd72.js"`. We can always later add the functionality that the `globPath` argument can also be a regex pattern. -- `resolvePath` should *never* be part of an asset-serving endpoint service - i.e. it should not be possible to send a glob to the server and get a file response. Instead, itโ€™s meant to be used in controllers to fetch the name of a required asset, e.g: ----- - pageContributions: