mirror of
https://github.com/mntno/obsidian-come-down.git
synced 2026-07-22 05:43:15 +00:00
1.1.1
This commit is contained in:
parent
a424f3e48c
commit
d384fb2429
6 changed files with 72 additions and 5 deletions
|
|
@ -29,6 +29,8 @@ pnpm run dev
|
||||||
|
|
||||||
### Production build
|
### Production build
|
||||||
|
|
||||||
|
Use this when verifying changes.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run build
|
pnpm run build
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "come-down",
|
"id": "come-down",
|
||||||
"name": "Come Down",
|
"name": "Come Down",
|
||||||
"version": "1.1.1-rc.1",
|
"version": "1.1.1",
|
||||||
"minAppVersion": "1.8.0",
|
"minAppVersion": "1.8.0",
|
||||||
"description": "Maintains a cache of your notes’ embedded external images.",
|
"description": "Maintains a cache of your notes’ embedded external images.",
|
||||||
"author": "mntno",
|
"author": "mntno",
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ export const Env = {
|
||||||
SPACE: " ",
|
SPACE: " ",
|
||||||
is: (value: unknown): value is string => typeof value === "string",
|
is: (value: unknown): value is string => typeof value === "string",
|
||||||
nonEmpty: (value: unknown): string | undefined => typeof value === "string" && value !== "" ? value : undefined,
|
nonEmpty: (value: unknown): string | undefined => typeof value === "string" && value !== "" ? value : undefined,
|
||||||
|
isNonEmpty: (value: unknown): value is string => typeof value === "string" && value !== "",
|
||||||
} as const,
|
} as const,
|
||||||
|
|
||||||
bool: {
|
bool: {
|
||||||
|
|
|
||||||
11
src/cache/CacheManager.ts
vendored
11
src/cache/CacheManager.ts
vendored
|
|
@ -891,12 +891,15 @@ export class CacheManager {
|
||||||
|
|
||||||
const response = await requestUrl({ url: sourceUrl, method: 'GET', throw: false });
|
const response = await requestUrl({ url: sourceUrl, method: 'GET', throw: false });
|
||||||
const headers = Url.normalizeHeaders(response.headers);
|
const headers = Url.normalizeHeaders(response.headers);
|
||||||
const contentType = headers[Url.RESPONSE_HEADER_LOWERCASE.contentType] ?? undefined;
|
const contentType = headers[Url.RESPONSE_HEADER_LOWERCASE.contentType];
|
||||||
const cacheControl = headers[Url.RESPONSE_HEADER_LOWERCASE.cacheControl] ?? undefined;
|
const cacheControl = headers[Url.RESPONSE_HEADER_LOWERCASE.cacheControl];
|
||||||
|
const etag = Url.parseETag(headers[Url.RESPONSE_HEADER_LOWERCASE.etag]);
|
||||||
|
const lastModifiedHeader = headers[Url.RESPONSE_HEADER_LOWERCASE.lastModified];
|
||||||
|
const lastModified = lastModifiedHeader !== undefined ? new Date(lastModifiedHeader).toISOString() : undefined;
|
||||||
|
|
||||||
//Env.log.cm(Env.dev.icon.CACHE_MANAGER, `CacheManager:download: Got response:\n\tcacheID: ${cacheKey}\n\t${response.status}\n\tcontentType: ${contentType}`);
|
//Env.log.cm(Env.dev.icon.CACHE_MANAGER, `CacheManager:download: Got response:\n\tcacheID: ${cacheKey}\n\t${response.status}\n\tcontentType: ${contentType}`);
|
||||||
|
|
||||||
if (cacheControl == Url.CACHE_CONTROL_LOWERCASE.noStore)
|
if (cacheControl === Url.CACHE_CONTROL_LOWERCASE.noStore)
|
||||||
throw new CacheError(`Caching not allowed on ${sourceUrl}.`, cacheKey);
|
throw new CacheError(`Caching not allowed on ${sourceUrl}.`, cacheKey);
|
||||||
|
|
||||||
if (response.status >= 400)
|
if (response.status >= 400)
|
||||||
|
|
@ -926,6 +929,8 @@ export class CacheManager {
|
||||||
d: nowDateString,
|
d: nowDateString,
|
||||||
l: nowDateString,
|
l: nowDateString,
|
||||||
cc: cacheControl,
|
cc: cacheControl,
|
||||||
|
et: etag !== null ? etag : undefined,
|
||||||
|
m: lastModified,
|
||||||
},
|
},
|
||||||
f: {
|
f: {
|
||||||
s: sourceUrl,
|
s: sourceUrl,
|
||||||
|
|
|
||||||
25
src/cache/CacheMetadata.ts
vendored
25
src/cache/CacheMetadata.ts
vendored
|
|
@ -75,9 +75,32 @@ export interface CacheMetadataTime {
|
||||||
/** Download time */
|
/** Download time */
|
||||||
d: string;
|
d: string;
|
||||||
|
|
||||||
/** Last accessed */
|
/**
|
||||||
|
* "Last time checked".
|
||||||
|
*
|
||||||
|
* Currently set to download time and never updated.
|
||||||
|
* @todo May be used to keep track of when the server was last checked for modified.
|
||||||
|
*/
|
||||||
l: string;
|
l: string;
|
||||||
|
|
||||||
/** Value of the `Cache-Control` HTTP response header. */
|
/** Value of the `Cache-Control` HTTP response header. */
|
||||||
cc?: string;
|
cc?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of the `ETag` HTTP response header.
|
||||||
|
*
|
||||||
|
* In the HTTP spec (RFC 7232), an ETag must be wrapped in double quotes. But here they are not. Add them back when using the ETag in requests.
|
||||||
|
*
|
||||||
|
* @since 1.1.1
|
||||||
|
*/
|
||||||
|
et?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalized value of the `Last-Modified` HTTP response header (ISO 8601).
|
||||||
|
*
|
||||||
|
* Converted from HTTP-date format to ISO for consistency. Convert back to UTC string when using in 'If-Modified-Since' headers.
|
||||||
|
*
|
||||||
|
* @since 1.1.1
|
||||||
|
*/
|
||||||
|
m?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ export class Url {
|
||||||
contentLength: "Content-Length".toLowerCase(),
|
contentLength: "Content-Length".toLowerCase(),
|
||||||
cacheControl: "Cache-Control".toLowerCase(),
|
cacheControl: "Cache-Control".toLowerCase(),
|
||||||
expires: "Expires".toLowerCase(),
|
expires: "Expires".toLowerCase(),
|
||||||
|
etag: "ETag".toLowerCase(),
|
||||||
|
lastModified: "Last-Modified".toLowerCase(),
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
public static readonly CACHE_CONTROL_LOWERCASE = {
|
public static readonly CACHE_CONTROL_LOWERCASE = {
|
||||||
|
|
@ -81,6 +83,40 @@ export class Url {
|
||||||
return url.startsWith("app://") || url.startsWith("capacitor://") || url.startsWith("file://");
|
return url.startsWith("app://") || url.startsWith("capacitor://") || url.startsWith("file://");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an ETag header value into a storage-friendly format.
|
||||||
|
* - `W/"abc"` -> `W/abc`
|
||||||
|
* - `"abc"` -> `abc`
|
||||||
|
* @param value The raw ETag header string.
|
||||||
|
* @returns The tag value stripped of quotes, with "W/" prefix preserved if present.
|
||||||
|
* @since 1.1.1
|
||||||
|
*/
|
||||||
|
public static parseETag(value: string | undefined): string | null {
|
||||||
|
if (!Env.str.isNonEmpty(value))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const match = value.match(/^(W\/)?"(.*)"$/);
|
||||||
|
if (match === null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const prefix = match[1] !== undefined ? match[1] : Env.str.EMPTY;
|
||||||
|
const tag = match[2];
|
||||||
|
return `${prefix}${tag}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a stored ETag back to a format suitable for HTTP headers.
|
||||||
|
* - `W/abc` -> `W/"abc"`
|
||||||
|
* - `abc` -> `"abc"`
|
||||||
|
* @param tag The stored ETag value.
|
||||||
|
* @since 1.1.1
|
||||||
|
*/
|
||||||
|
public static stringifyETag(tag: string): string | null {
|
||||||
|
if (Env.str.nonEmpty(tag) === undefined)
|
||||||
|
return null;
|
||||||
|
return tag.replace(/^(W\/)?(.*)$/, '$1"$2"');
|
||||||
|
}
|
||||||
|
|
||||||
public static trimBackslash(url: string): string {
|
public static trimBackslash(url: string): string {
|
||||||
return url.endsWith("/") ? url.slice(0, -1) : url;
|
return url.endsWith("/") ? url.slice(0, -1) : url;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue