@hollware/nuxt 0.6.0 → 0.9.0
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/dist/index.d.mts +15 -0
- package/dist/index.mjs +23 -2
- package/dist/runtime/composables.d.mts +1 -5
- package/dist/runtime/composables.mjs +32 -6
- package/dist/runtime/server/cache.d.mts +10 -0
- package/dist/runtime/server/cache.mjs +17 -0
- package/dist/runtime/server/reader.mjs +33 -3
- package/dist/runtime/server/routes/entries.d.mts +1 -1
- package/dist/runtime/server/routes/entries.mjs +2 -1
- package/dist/runtime/server/routes/site.d.mts +1 -0
- package/dist/runtime/server/routes/translations.d.mts +1 -1
- package/dist/runtime/server/routes/translations.mjs +2 -1
- package/dist/runtime/server/routes/webhook.d.mts +6 -0
- package/dist/runtime/server/routes/webhook.mjs +38 -0
- package/dist/runtime/tags.d.mts +26 -0
- package/dist/runtime/tags.mjs +55 -0
- package/package.json +3 -2
package/dist/index.d.mts
CHANGED
|
@@ -29,6 +29,21 @@ export interface ModuleOptions {
|
|
|
29
29
|
* No views are counted when it is not set.
|
|
30
30
|
*/
|
|
31
31
|
beacon?: string;
|
|
32
|
+
/**
|
|
33
|
+
* How the pages that read Hollware are kept (S8):
|
|
34
|
+
*
|
|
35
|
+
* - `'purge'`: in Cloudflare's Workers Cache, which serves them without running the Worker, each
|
|
36
|
+
* tagged with what it showed, until Hollware's webhook to `/_hollware/webhook` (signed with
|
|
37
|
+
* `NUXT_HOLLWARE_WEBHOOK_SECRET`) purges what changed: a publish, an edit or an unpublish shows
|
|
38
|
+
* in seconds everywhere. It needs `"cache": { "enabled": true }` in the site's wrangler.jsonc.
|
|
39
|
+
* - a number of seconds: kept by Nitro (`swr`) in each isolate, and rendered again in the
|
|
40
|
+
* background once that old; a change shows within that time.
|
|
41
|
+
*
|
|
42
|
+
* Errors are never kept. Off when not set.
|
|
43
|
+
*/
|
|
44
|
+
cache?: number | 'purge';
|
|
45
|
+
/** The secret of Hollware's webhook to this site, for `cache: 'purge'`; prefer `NUXT_HOLLWARE_WEBHOOK_SECRET`. */
|
|
46
|
+
webhookSecret?: string;
|
|
32
47
|
}
|
|
33
48
|
export declare const name = "@hollware/nuxt";
|
|
34
49
|
/** The routes of the site that read Hollware for its pages, on the server. */
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addComponent, addImports, addPlugin, addServerHandler, addServerImports, createResolver, defineNuxtModule } from "@nuxt/kit";
|
|
1
|
+
import { addComponent, addImports, addPlugin, addServerHandler, addServerImports, addServerPlugin, createResolver, defineNuxtModule } from "@nuxt/kit";
|
|
2
2
|
//#region src/index.ts
|
|
3
3
|
const name = "@hollware/nuxt";
|
|
4
4
|
/** The routes of the site that read Hollware for its pages, on the server. */
|
|
@@ -27,7 +27,8 @@ var src_default = defineNuxtModule({
|
|
|
27
27
|
site: options.site,
|
|
28
28
|
binding: options.binding,
|
|
29
29
|
url: options.url ?? "",
|
|
30
|
-
token: options.token ?? ""
|
|
30
|
+
token: options.token ?? "",
|
|
31
|
+
webhookSecret: options.webhookSecret ?? ""
|
|
31
32
|
};
|
|
32
33
|
addServerImports([{
|
|
33
34
|
name: "useHollware",
|
|
@@ -52,6 +53,26 @@ var src_default = defineNuxtModule({
|
|
|
52
53
|
filePath: resolve("./runtime/components/HollwareContent")
|
|
53
54
|
});
|
|
54
55
|
if (options.generator) addPlugin(resolve("./runtime/generator"));
|
|
56
|
+
if (options.cache === "purge") {
|
|
57
|
+
addServerPlugin(resolve("./runtime/server/cache"));
|
|
58
|
+
addServerHandler({
|
|
59
|
+
route: "/_hollware/webhook",
|
|
60
|
+
method: "post",
|
|
61
|
+
handler: resolve("./runtime/server/routes/webhook")
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (typeof options.cache === "number" && options.cache > 0) {
|
|
65
|
+
const pages = { swr: options.cache };
|
|
66
|
+
const routes = {
|
|
67
|
+
swr: false,
|
|
68
|
+
cache: false
|
|
69
|
+
};
|
|
70
|
+
nuxt.options.routeRules = {
|
|
71
|
+
"/**": pages,
|
|
72
|
+
"/_hollware/**": routes,
|
|
73
|
+
...nuxt.options.routeRules
|
|
74
|
+
};
|
|
75
|
+
}
|
|
55
76
|
if (options.beacon !== void 0 && options.beacon !== "") {
|
|
56
77
|
nuxt.options.runtimeConfig.public["hollware"] = {
|
|
57
78
|
site: options.site,
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { PageEntry } from "./types.mjs";
|
|
2
2
|
import { MaybeRefOrGetter } from "vue";
|
|
3
3
|
//#region src/runtime/composables.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* The published content of the site, for a page: each reads the site's own `/_hollware/` route,
|
|
6
|
-
* which reads Hollware on the server, so no token reaches the browser. They are `useFetch`, so the
|
|
7
|
-
* page renders on the server with the answer and the browser does not ask again.
|
|
8
|
-
*/
|
|
9
4
|
/** An entry, with its Comark document; `data` is null while it loads, `error` a 404 when it is not published. */
|
|
10
5
|
export declare function useHollwareEntry(collection: MaybeRefOrGetter<string>, locale: MaybeRefOrGetter<string>, slug: MaybeRefOrGetter<string>): import("nuxt/app").AsyncData<{
|
|
11
6
|
outcome: 'ok';
|
|
@@ -38,6 +33,7 @@ export declare function useHollwareSite(): import("nuxt/app").AsyncData<{
|
|
|
38
33
|
key: string;
|
|
39
34
|
locales: string[];
|
|
40
35
|
defaultLocale: string;
|
|
36
|
+
url: string | null;
|
|
41
37
|
styleGuide: string | null;
|
|
42
38
|
collections: {
|
|
43
39
|
name: string;
|
|
@@ -1,32 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { entryTags, groupTags, listTags, recordTags, siteTags } from "./tags.mjs";
|
|
2
|
+
import { useFetch, useRequestEvent } from "nuxt/app";
|
|
2
3
|
import { computed, toValue } from "vue";
|
|
3
4
|
//#region src/runtime/composables.ts
|
|
4
5
|
/**
|
|
5
6
|
* The published content of the site, for a page: each reads the site's own `/_hollware/` route,
|
|
6
7
|
* which reads Hollware on the server, so no token reaches the browser. They are `useFetch`, so the
|
|
7
|
-
* page renders on the server with the answer and the browser does not ask again.
|
|
8
|
+
* page renders on the server with the answer and the browser does not ask again. On the server,
|
|
9
|
+
* each records on the page's request the cache tags of what it read (S8).
|
|
8
10
|
*/
|
|
11
|
+
/** The request of the page being rendered, on the server; none in the browser. */
|
|
12
|
+
function pageRequest() {
|
|
13
|
+
return import.meta.server ? useRequestEvent() : void 0;
|
|
14
|
+
}
|
|
9
15
|
/** An entry, with its Comark document; `data` is null while it loads, `error` a 404 when it is not published. */
|
|
10
16
|
function useHollwareEntry(collection, locale, slug) {
|
|
11
17
|
const path = computed(() => `/_hollware/entry/${encodeURIComponent(toValue(collection))}/${encodeURIComponent(toValue(locale))}/${encodeURIComponent(toValue(slug))}`);
|
|
12
|
-
|
|
18
|
+
const event = pageRequest();
|
|
19
|
+
return useFetch(path, {
|
|
20
|
+
key: path.value,
|
|
21
|
+
transform: (answer) => {
|
|
22
|
+
recordTags(event, entryTags(answer.entry));
|
|
23
|
+
return answer;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
13
26
|
}
|
|
14
27
|
/** A page of the published entries of a collection, newest first. */
|
|
15
28
|
function useHollwareEntries(collection, options = {}, fetching = {}) {
|
|
16
29
|
const path = computed(() => `/_hollware/entries/${encodeURIComponent(toValue(collection))}`);
|
|
30
|
+
const event = pageRequest();
|
|
17
31
|
return useFetch(path, {
|
|
18
32
|
query: computed(() => toValue(options)),
|
|
19
|
-
...fetching
|
|
33
|
+
...fetching,
|
|
34
|
+
transform: (answer) => {
|
|
35
|
+
recordTags(event, listTags(toValue(collection), toValue(options).locale, answer.entries));
|
|
36
|
+
return answer;
|
|
37
|
+
}
|
|
20
38
|
});
|
|
21
39
|
}
|
|
22
40
|
/** The entries of a translation group, one per locale. */
|
|
23
41
|
function useHollwareTranslations(collection, group) {
|
|
24
42
|
const path = computed(() => `/_hollware/translations/${encodeURIComponent(toValue(collection))}/${encodeURIComponent(toValue(group))}`);
|
|
25
|
-
|
|
43
|
+
const event = pageRequest();
|
|
44
|
+
return useFetch(path, { transform: (answer) => {
|
|
45
|
+
recordTags(event, groupTags(toValue(collection), toValue(group), answer.entries));
|
|
46
|
+
return answer;
|
|
47
|
+
} });
|
|
26
48
|
}
|
|
27
49
|
/** The site: its locales, its collections and their schemas. */
|
|
28
50
|
function useHollwareSite() {
|
|
29
|
-
|
|
51
|
+
const event = pageRequest();
|
|
52
|
+
return useFetch("/_hollware/site", { transform: (answer) => {
|
|
53
|
+
recordTags(event, siteTags());
|
|
54
|
+
return answer;
|
|
55
|
+
} });
|
|
30
56
|
}
|
|
31
57
|
//#endregion
|
|
32
58
|
export { useHollwareEntries, useHollwareEntry, useHollwareSite, useHollwareTranslations };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/runtime/server/cache.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The cache of `hollware.cache: 'purge'` (S8): a response that read Hollware and answered 200 is
|
|
4
|
+
* kept by Cloudflare's Workers Cache, which serves it again without running the Worker, tagged with
|
|
5
|
+
* what it showed, until an event of Hollware purges one of its tags (`/_hollware/webhook`). The
|
|
6
|
+
* browser keeps nothing: only the edge does (`cloudflare-cdn-cache-control`).
|
|
7
|
+
*/
|
|
8
|
+
declare const _default: import("nitropack").NitroAppPlugin;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { _default as default };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { edgeHeaders, tagsOf } from "../tags.mjs";
|
|
2
|
+
import { defineNitroPlugin } from "nitropack/runtime";
|
|
3
|
+
//#region src/runtime/server/cache.ts
|
|
4
|
+
/**
|
|
5
|
+
* The cache of `hollware.cache: 'purge'` (S8): a response that read Hollware and answered 200 is
|
|
6
|
+
* kept by Cloudflare's Workers Cache, which serves it again without running the Worker, tagged with
|
|
7
|
+
* what it showed, until an event of Hollware purges one of its tags (`/_hollware/webhook`). The
|
|
8
|
+
* browser keeps nothing: only the edge does (`cloudflare-cdn-cache-control`).
|
|
9
|
+
*/
|
|
10
|
+
var cache_default = defineNitroPlugin((nitroApp) => {
|
|
11
|
+
nitroApp.hooks.hook("beforeResponse", (event) => {
|
|
12
|
+
const headers = edgeHeaders(tagsOf(event), event.node.res.statusCode, event.method);
|
|
13
|
+
for (const [name, value] of Object.entries(headers ?? {})) event.node.res.setHeader(name, value);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
//#endregion
|
|
17
|
+
export { cache_default as default };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { entryTags, groupTags, listTags, recordTags, siteTags } from "../tags.mjs";
|
|
1
2
|
import { createClient } from "@hollware/client";
|
|
2
3
|
import { createError } from "h3";
|
|
3
4
|
import { useRuntimeConfig } from "nitropack/runtime";
|
|
@@ -16,19 +17,48 @@ function useHollware(event) {
|
|
|
16
17
|
const binding = typeof env === "object" && env !== null ? Reflect.get(env, config.binding) : void 0;
|
|
17
18
|
if (isReader(binding)) return {
|
|
18
19
|
site: config.site,
|
|
19
|
-
reader: createClient({ binding })
|
|
20
|
+
reader: tagging(event, createClient({ binding }))
|
|
20
21
|
};
|
|
21
22
|
if (config.url !== "" && config.token !== "") return {
|
|
22
23
|
site: config.site,
|
|
23
|
-
reader: createClient({
|
|
24
|
+
reader: tagging(event, createClient({
|
|
24
25
|
url: config.url,
|
|
25
26
|
token: config.token
|
|
26
|
-
})
|
|
27
|
+
}))
|
|
27
28
|
};
|
|
28
29
|
throw createError({
|
|
29
30
|
statusCode: 500,
|
|
30
31
|
statusMessage: `Hollware: the Worker has no binding ${config.binding}, and hollware.url with a read token (NUXT_HOLLWARE_TOKEN) is not set.`
|
|
31
32
|
});
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* The reader, recording on the request the cache tags of what each read answered (S8): a route of
|
|
36
|
+
* the site's own that reads through `useHollware` is tagged with no work of its own.
|
|
37
|
+
*/
|
|
38
|
+
function tagging(event, reader) {
|
|
39
|
+
return {
|
|
40
|
+
...reader,
|
|
41
|
+
getEntry: async (...args) => {
|
|
42
|
+
const answer = await reader.getEntry(...args);
|
|
43
|
+
recordTags(event, entryTags(answer.entry));
|
|
44
|
+
return answer;
|
|
45
|
+
},
|
|
46
|
+
listEntries: async (...args) => {
|
|
47
|
+
const answer = await reader.listEntries(...args);
|
|
48
|
+
recordTags(event, listTags(args[1], args[2]?.locale, answer.entries));
|
|
49
|
+
return answer;
|
|
50
|
+
},
|
|
51
|
+
getTranslations: async (...args) => {
|
|
52
|
+
const answer = await reader.getTranslations(...args);
|
|
53
|
+
recordTags(event, groupTags(args[1], args[2], answer.entries ?? []));
|
|
54
|
+
return answer;
|
|
55
|
+
},
|
|
56
|
+
describeSite: async (...args) => {
|
|
57
|
+
const answer = await reader.describeSite(...args);
|
|
58
|
+
recordTags(event, siteTags());
|
|
59
|
+
return answer;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
33
63
|
//#endregion
|
|
34
64
|
export { useHollware };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/runtime/server/routes/entries.d.ts
|
|
2
2
|
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
3
3
|
outcome: "ok";
|
|
4
|
-
entries:
|
|
4
|
+
entries: Record<string, unknown>[];
|
|
5
5
|
nextCursor: string | null;
|
|
6
6
|
}>>;
|
|
7
7
|
//#endregion
|
|
@@ -19,9 +19,10 @@ var entries_default = defineEventHandler(async (event) => {
|
|
|
19
19
|
...limit === void 0 ? {} : { limit: Number(limit) }
|
|
20
20
|
};
|
|
21
21
|
const page = await answer(event, () => reader.listEntries(site, getRouterParam(event, "collection") ?? "", options));
|
|
22
|
+
const entries = page.entries.map(forPage).flatMap((entry) => entry === void 0 ? [] : [entry]);
|
|
22
23
|
return {
|
|
23
24
|
outcome: page.outcome,
|
|
24
|
-
entries
|
|
25
|
+
entries,
|
|
25
26
|
nextCursor: page.nextCursor
|
|
26
27
|
};
|
|
27
28
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/runtime/server/routes/translations.d.ts
|
|
2
2
|
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
3
3
|
outcome: "not_found" | "ok";
|
|
4
|
-
entries:
|
|
4
|
+
entries: Record<string, unknown>[];
|
|
5
5
|
}>>;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { _default as default };
|
|
@@ -5,9 +5,10 @@ import { defineEventHandler, getRouterParam } from "h3";
|
|
|
5
5
|
var translations_default = defineEventHandler(async (event) => {
|
|
6
6
|
const { site, reader } = useHollware(event);
|
|
7
7
|
const group = await answer(event, () => reader.getTranslations(site, getRouterParam(event, "collection") ?? "", getRouterParam(event, "group") ?? ""));
|
|
8
|
+
const entries = (group.entries ?? []).map(forPage).flatMap((entry) => entry === void 0 ? [] : [entry]);
|
|
8
9
|
return {
|
|
9
10
|
outcome: group.outcome,
|
|
10
|
-
entries
|
|
11
|
+
entries
|
|
11
12
|
};
|
|
12
13
|
});
|
|
13
14
|
//#endregion
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { verifyWebhook } from "@hollware/core";
|
|
2
|
+
import { createError, defineEventHandler, getRequestHeaders, readRawBody } from "h3";
|
|
3
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
4
|
+
//#region src/runtime/server/routes/webhook.ts
|
|
5
|
+
function cacheOf(context) {
|
|
6
|
+
const cloudflare = typeof context === "object" && context !== null ? Reflect.get(context, "cloudflare") : void 0;
|
|
7
|
+
const ctx = typeof cloudflare === "object" && cloudflare !== null ? Reflect.get(cloudflare, "context") : void 0;
|
|
8
|
+
const cache = typeof ctx === "object" && ctx !== null ? Reflect.get(ctx, "cache") : void 0;
|
|
9
|
+
return typeof cache === "object" && cache !== null && typeof Reflect.get(cache, "purge") === "function" ? { purge: (options) => Reflect.apply(Reflect.get(cache, "purge"), cache, [options]) } : void 0;
|
|
10
|
+
}
|
|
11
|
+
var webhook_default = defineEventHandler(async (event) => {
|
|
12
|
+
const config = useRuntimeConfig(event);
|
|
13
|
+
const secret = String(Reflect.get(Reflect.get(config, "hollware") ?? {}, "webhookSecret") ?? "");
|
|
14
|
+
const body = await readRawBody(event, "utf8") ?? "";
|
|
15
|
+
const headers = new Headers(Object.entries(getRequestHeaders(event)).flatMap(([name, value]) => value === void 0 ? [] : [[name, value]]));
|
|
16
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
17
|
+
if (secret === "" || !await verifyWebhook({
|
|
18
|
+
secret,
|
|
19
|
+
body,
|
|
20
|
+
headers,
|
|
21
|
+
now
|
|
22
|
+
})) throw createError({
|
|
23
|
+
statusCode: 401,
|
|
24
|
+
statusMessage: "The signature of the event does not hold"
|
|
25
|
+
});
|
|
26
|
+
const payload = JSON.parse(body);
|
|
27
|
+
const found = typeof payload === "object" && payload !== null ? Reflect.get(payload, "cacheTags") : void 0;
|
|
28
|
+
const tags = Array.isArray(found) ? found.filter((tag) => typeof tag === "string") : [];
|
|
29
|
+
const cache = cacheOf(event.context);
|
|
30
|
+
if (cache === void 0) throw createError({
|
|
31
|
+
statusCode: 501,
|
|
32
|
+
statusMessage: "This Worker has no cache to purge: set \"cache\": { \"enabled\": true } in wrangler.jsonc"
|
|
33
|
+
});
|
|
34
|
+
if (tags.length > 0) await cache.purge({ tags });
|
|
35
|
+
return { purged: tags };
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
export { webhook_default as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/runtime/tags.d.ts
|
|
2
|
+
/** What a request holds of its own, whether Nuxt's or h3's: its context. */
|
|
3
|
+
interface Tagged {
|
|
4
|
+
readonly context: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
export declare function recordTags(event: Tagged | undefined, tags: readonly string[]): void;
|
|
7
|
+
export declare function tagsOf(event: Tagged): string[];
|
|
8
|
+
interface Entryish {
|
|
9
|
+
readonly id?: unknown;
|
|
10
|
+
}
|
|
11
|
+
/** The tags of an entry read alone. */
|
|
12
|
+
export declare function entryTags(entry: Entryish | undefined): string[];
|
|
13
|
+
/** The tags of a list: the list itself, in its locale or every one, and each entry it shows. */
|
|
14
|
+
export declare function listTags(collection: string, locale: string | undefined, entries: readonly Entryish[]): string[];
|
|
15
|
+
/** The tags of the translations of a group. */
|
|
16
|
+
export declare function groupTags(collection: string, group: string, entries: readonly Entryish[]): string[];
|
|
17
|
+
export declare function siteTags(): string[];
|
|
18
|
+
/** How long the edge keeps a page no event purges: a year, the most it keeps anything. */
|
|
19
|
+
export declare const EDGE_SECONDS = 31536000;
|
|
20
|
+
/**
|
|
21
|
+
* The headers that let the edge keep a response, tagged with what it showed: only a GET that
|
|
22
|
+
* answered 200 and read Hollware. An error, a redirect or a write is never kept, nor a response
|
|
23
|
+
* that read nothing to purge it by. The browser keeps nothing; only the edge does.
|
|
24
|
+
*/
|
|
25
|
+
export declare function edgeHeaders(tags: readonly string[], status: number, method: string): Readonly<Record<string, string>> | null;
|
|
26
|
+
//#endregion
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { EVERY_LOCALE, SITE_TAG, entryTag, groupTag, listTag } from "@hollware/core";
|
|
2
|
+
//#region src/runtime/tags.ts
|
|
3
|
+
/**
|
|
4
|
+
* The cache tags of a response (S8): what it showed of Hollware, in the names of the contract of
|
|
5
|
+
* `@hollware/core`, gathered on its request as each read answers, and written as `Cache-Tag` once
|
|
6
|
+
* it is rendered. A page gathers the tags of every read it made; a route of `/_hollware/` those of
|
|
7
|
+
* its own.
|
|
8
|
+
*/
|
|
9
|
+
const KEY = "hollwareTags";
|
|
10
|
+
function recordTags(event, tags) {
|
|
11
|
+
if (event === void 0 || tags.length === 0) return;
|
|
12
|
+
const found = event.context[KEY];
|
|
13
|
+
const set = found instanceof Set ? found : /* @__PURE__ */ new Set();
|
|
14
|
+
for (const tag of tags) set.add(tag);
|
|
15
|
+
event.context[KEY] = set;
|
|
16
|
+
}
|
|
17
|
+
function tagsOf(event) {
|
|
18
|
+
const found = event.context[KEY];
|
|
19
|
+
return found instanceof Set ? [...found].map(String) : [];
|
|
20
|
+
}
|
|
21
|
+
function idsOf(entries) {
|
|
22
|
+
return entries.flatMap((entry) => typeof entry.id === "string" ? [entryTag(entry.id)] : []);
|
|
23
|
+
}
|
|
24
|
+
/** The tags of an entry read alone. */
|
|
25
|
+
function entryTags(entry) {
|
|
26
|
+
return entry === void 0 ? [] : idsOf([entry]);
|
|
27
|
+
}
|
|
28
|
+
/** The tags of a list: the list itself, in its locale or every one, and each entry it shows. */
|
|
29
|
+
function listTags(collection, locale, entries) {
|
|
30
|
+
return [listTag(collection, locale ?? EVERY_LOCALE), ...idsOf(entries)];
|
|
31
|
+
}
|
|
32
|
+
/** The tags of the translations of a group. */
|
|
33
|
+
function groupTags(collection, group, entries) {
|
|
34
|
+
return [groupTag(collection, group), ...idsOf(entries)];
|
|
35
|
+
}
|
|
36
|
+
function siteTags() {
|
|
37
|
+
return [SITE_TAG];
|
|
38
|
+
}
|
|
39
|
+
/** How long the edge keeps a page no event purges: a year, the most it keeps anything. */
|
|
40
|
+
const EDGE_SECONDS = 31536e3;
|
|
41
|
+
/**
|
|
42
|
+
* The headers that let the edge keep a response, tagged with what it showed: only a GET that
|
|
43
|
+
* answered 200 and read Hollware. An error, a redirect or a write is never kept, nor a response
|
|
44
|
+
* that read nothing to purge it by. The browser keeps nothing; only the edge does.
|
|
45
|
+
*/
|
|
46
|
+
function edgeHeaders(tags, status, method) {
|
|
47
|
+
if (tags.length === 0 || status !== 200 || method !== "GET") return null;
|
|
48
|
+
return {
|
|
49
|
+
"cache-tag": tags.join(","),
|
|
50
|
+
"cache-control": "public, max-age=0, must-revalidate",
|
|
51
|
+
"cloudflare-cdn-cache-control": `max-age=${EDGE_SECONDS}`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { EDGE_SECONDS, edgeHeaders, entryTags, groupTags, listTags, recordTags, siteTags, tagsOf };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hollware/nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Read Hollware from a Nuxt site, over a service binding or HTTP.",
|
|
5
5
|
"homepage": "https://hollware.com",
|
|
6
6
|
"bugs": {
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@comark/vue": "0.7.0",
|
|
32
|
-
"@hollware/client": "0.
|
|
32
|
+
"@hollware/client": "0.9.0",
|
|
33
|
+
"@hollware/core": "0.9.0",
|
|
33
34
|
"@nuxt/kit": "4.6.1"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|