@cmssy/astro 9.10.0 → 10.1.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.cjs +11 -115
- package/dist/index.d.cts +2 -31
- package/dist/index.d.ts +2 -31
- package/dist/index.js +4 -74
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@cmssy/core');
|
|
4
|
+
var internal = require('@cmssy/core/internal');
|
|
4
5
|
|
|
5
6
|
// src/middleware.ts
|
|
6
7
|
var CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
|
|
@@ -8,9 +9,9 @@ function cmssyMiddleware(config, options = {}) {
|
|
|
8
9
|
return async function onRequest(context, next) {
|
|
9
10
|
const { pathname } = context.url;
|
|
10
11
|
context.request.headers.delete(core.CMSSY_EDIT_HEADER);
|
|
11
|
-
context.request.headers.delete(
|
|
12
|
-
const locale = await
|
|
13
|
-
context.request.headers.set(
|
|
12
|
+
context.request.headers.delete(internal.CMSSY_LOCALE_HEADER);
|
|
13
|
+
const locale = await internal.localeForPathname(config, pathname);
|
|
14
|
+
context.request.headers.set(internal.CMSSY_LOCALE_HEADER, locale);
|
|
14
15
|
const editRequested = context.url.searchParams.getAll(core.CMSSY_EDIT_QUERY_PARAM).includes("1");
|
|
15
16
|
if (editRequested) {
|
|
16
17
|
const verified = await core.isVerifiedEditUrl(context.url, config);
|
|
@@ -21,7 +22,7 @@ function cmssyMiddleware(config, options = {}) {
|
|
|
21
22
|
core.applyCmssyCsp(response, { editorOrigin: config.editorOrigin });
|
|
22
23
|
return response;
|
|
23
24
|
}
|
|
24
|
-
if (!verified &&
|
|
25
|
+
if (!verified && internal.isDevelopment()) {
|
|
25
26
|
const { collectEditDiagnostics, renderEditDiagnosticsDocument } = await import('@cmssy/core/preflight');
|
|
26
27
|
const diagnostics = await collectEditDiagnostics({
|
|
27
28
|
config,
|
|
@@ -37,7 +38,7 @@ function cmssyMiddleware(config, options = {}) {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
if (options.stripLocalePrefix && pathname.startsWith(`/${locale}`)) {
|
|
40
|
-
const { defaultLocale } = await
|
|
41
|
+
const { defaultLocale } = await internal.resolveSiteLocales(config);
|
|
41
42
|
if (locale !== defaultLocale) {
|
|
42
43
|
const stripped = pathname.slice(locale.length + 1) || "/";
|
|
43
44
|
return context.rewrite(`${stripped}${context.url.search}`);
|
|
@@ -48,15 +49,15 @@ function cmssyMiddleware(config, options = {}) {
|
|
|
48
49
|
}
|
|
49
50
|
async function loadCmssyPage(config, request, url) {
|
|
50
51
|
const isEdit = request.headers.get(core.CMSSY_EDIT_HEADER) === "1";
|
|
51
|
-
const siteLocales = await
|
|
52
|
+
const siteLocales = await internal.resolveSiteLocales(config);
|
|
52
53
|
const segments = url.pathname.replace(/^\/cmssy-edit/, "").split("/").filter(Boolean);
|
|
53
|
-
const fromPath =
|
|
54
|
-
const locale = request.headers.get(
|
|
54
|
+
const fromPath = internal.splitLocaleFromPath(segments, siteLocales);
|
|
55
|
+
const locale = request.headers.get(internal.CMSSY_LOCALE_HEADER) ?? fromPath.locale;
|
|
55
56
|
const path = fromPath.path;
|
|
56
57
|
const previewSecret = isEdit ? config.draftSecret : void 0;
|
|
57
58
|
const [page, layouts] = await Promise.all([
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
internal.fetchPage(config, path, { previewSecret }),
|
|
60
|
+
internal.fetchLayouts(config, path, { previewSecret })
|
|
60
61
|
]);
|
|
61
62
|
return {
|
|
62
63
|
page,
|
|
@@ -67,77 +68,6 @@ async function loadCmssyPage(config, request, url) {
|
|
|
67
68
|
isEdit
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
|
-
function xmlEscape(value) {
|
|
71
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
72
|
-
}
|
|
73
|
-
function baseUrlFor(config, url, request) {
|
|
74
|
-
if (config.siteUrl) return config.siteUrl.replace(/\/+$/, "");
|
|
75
|
-
const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host");
|
|
76
|
-
if (!host) return url.origin.replace(/\/+$/, "");
|
|
77
|
-
const protocol = request.headers.get("x-forwarded-proto") ?? (host.startsWith("localhost") || host.startsWith("127.0.0.1") ? "http" : "https");
|
|
78
|
-
return `${protocol}://${host}`.replace(/\/+$/, "");
|
|
79
|
-
}
|
|
80
|
-
function createCmssySitemap(config, options = {}) {
|
|
81
|
-
return async function GET({
|
|
82
|
-
url,
|
|
83
|
-
request
|
|
84
|
-
}) {
|
|
85
|
-
const baseUrl = baseUrlFor(config, url, request);
|
|
86
|
-
const { defaultLocale, locales } = await core.resolveSiteLocales(config);
|
|
87
|
-
const pages = await core.fetchPages(config);
|
|
88
|
-
const entries = [];
|
|
89
|
-
for (const page of pages) {
|
|
90
|
-
const slug = core.normalizeSlug(page.slug);
|
|
91
|
-
for (const locale of locales) {
|
|
92
|
-
entries.push({
|
|
93
|
-
url: `${baseUrl}${core.localizedPath(slug, locale, defaultLocale)}`,
|
|
94
|
-
lastModified: page.updatedAt ?? void 0
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (options.extra) {
|
|
99
|
-
entries.push(
|
|
100
|
-
...await options.extra({
|
|
101
|
-
baseUrl,
|
|
102
|
-
defaultLocale,
|
|
103
|
-
locales
|
|
104
|
-
})
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
const body = [
|
|
108
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
109
|
-
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
110
|
-
...entries.map(
|
|
111
|
-
(entry) => [
|
|
112
|
-
" <url>",
|
|
113
|
-
` <loc>${xmlEscape(entry.url)}</loc>`,
|
|
114
|
-
entry.lastModified ? ` <lastmod>${xmlEscape(entry.lastModified)}</lastmod>` : "",
|
|
115
|
-
" </url>"
|
|
116
|
-
].filter(Boolean).join("\n")
|
|
117
|
-
),
|
|
118
|
-
"</urlset>"
|
|
119
|
-
].join("\n");
|
|
120
|
-
return new Response(body, {
|
|
121
|
-
headers: { "content-type": "application/xml" }
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
function createCmssyRobots(config) {
|
|
126
|
-
return async function GET({
|
|
127
|
-
url,
|
|
128
|
-
request
|
|
129
|
-
}) {
|
|
130
|
-
const baseUrl = baseUrlFor(config, url, request);
|
|
131
|
-
const body = [
|
|
132
|
-
"User-agent: *",
|
|
133
|
-
"Allow: /",
|
|
134
|
-
"Disallow: /cmssy-edit",
|
|
135
|
-
`Sitemap: ${baseUrl}/sitemap.xml`,
|
|
136
|
-
""
|
|
137
|
-
].join("\n");
|
|
138
|
-
return new Response(body, { headers: { "content-type": "text/plain" } });
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
71
|
|
|
142
72
|
Object.defineProperty(exports, "CMSSY_EDIT_HEADER", {
|
|
143
73
|
enumerable: true,
|
|
@@ -147,10 +77,6 @@ Object.defineProperty(exports, "CMSSY_LOCALE_HEADER", {
|
|
|
147
77
|
enumerable: true,
|
|
148
78
|
get: function () { return core.CMSSY_LOCALE_HEADER; }
|
|
149
79
|
});
|
|
150
|
-
Object.defineProperty(exports, "buildBlockContext", {
|
|
151
|
-
enumerable: true,
|
|
152
|
-
get: function () { return core.buildBlockContext; }
|
|
153
|
-
});
|
|
154
80
|
Object.defineProperty(exports, "createCmssyClient", {
|
|
155
81
|
enumerable: true,
|
|
156
82
|
get: function () { return core.createCmssyClient; }
|
|
@@ -159,40 +85,10 @@ Object.defineProperty(exports, "defineCmssyConfig", {
|
|
|
159
85
|
enumerable: true,
|
|
160
86
|
get: function () { return core.defineCmssyConfig; }
|
|
161
87
|
});
|
|
162
|
-
Object.defineProperty(exports, "fetchLayouts", {
|
|
163
|
-
enumerable: true,
|
|
164
|
-
get: function () { return core.fetchLayouts; }
|
|
165
|
-
});
|
|
166
|
-
Object.defineProperty(exports, "fetchPage", {
|
|
167
|
-
enumerable: true,
|
|
168
|
-
get: function () { return core.fetchPage; }
|
|
169
|
-
});
|
|
170
|
-
Object.defineProperty(exports, "fetchPageMeta", {
|
|
171
|
-
enumerable: true,
|
|
172
|
-
get: function () { return core.fetchPageMeta; }
|
|
173
|
-
});
|
|
174
|
-
Object.defineProperty(exports, "fetchPages", {
|
|
175
|
-
enumerable: true,
|
|
176
|
-
get: function () { return core.fetchPages; }
|
|
177
|
-
});
|
|
178
|
-
Object.defineProperty(exports, "fetchProduct", {
|
|
179
|
-
enumerable: true,
|
|
180
|
-
get: function () { return core.fetchProduct; }
|
|
181
|
-
});
|
|
182
|
-
Object.defineProperty(exports, "fetchProducts", {
|
|
183
|
-
enumerable: true,
|
|
184
|
-
get: function () { return core.fetchProducts; }
|
|
185
|
-
});
|
|
186
88
|
Object.defineProperty(exports, "localizeHref", {
|
|
187
89
|
enumerable: true,
|
|
188
90
|
get: function () { return core.localizeHref; }
|
|
189
91
|
});
|
|
190
|
-
Object.defineProperty(exports, "resolveSiteLocales", {
|
|
191
|
-
enumerable: true,
|
|
192
|
-
get: function () { return core.resolveSiteLocales; }
|
|
193
|
-
});
|
|
194
92
|
exports.CMSSY_EDIT_PATH_PREFIX = CMSSY_EDIT_PATH_PREFIX;
|
|
195
93
|
exports.cmssyMiddleware = cmssyMiddleware;
|
|
196
|
-
exports.createCmssyRobots = createCmssyRobots;
|
|
197
|
-
exports.createCmssySitemap = createCmssySitemap;
|
|
198
94
|
exports.loadCmssyPage = loadCmssyPage;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CmssyConfig, CmssyPageData, CmssyLayoutGroup } from '@cmssy/core';
|
|
2
|
-
export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData,
|
|
2
|
+
export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData, createCmssyClient, defineCmssyConfig, localizeHref } from '@cmssy/core';
|
|
3
3
|
|
|
4
4
|
declare const CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
|
|
5
5
|
interface CmssyMiddlewareOptions {
|
|
@@ -50,33 +50,4 @@ interface CmssyPageResult {
|
|
|
50
50
|
*/
|
|
51
51
|
declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL): Promise<CmssyPageResult>;
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
url: string;
|
|
55
|
-
lastModified?: string;
|
|
56
|
-
}
|
|
57
|
-
interface CmssySitemapOptions {
|
|
58
|
-
/**
|
|
59
|
-
* Records are not pages, so the sitemap cannot know about your products or
|
|
60
|
-
* categories. Add them here; you get the same base URL and locales the page
|
|
61
|
-
* entries use, so the two cannot disagree.
|
|
62
|
-
*/
|
|
63
|
-
extra?: (context: {
|
|
64
|
-
baseUrl: string;
|
|
65
|
-
defaultLocale: string;
|
|
66
|
-
locales: string[];
|
|
67
|
-
}) => Promise<CmssySitemapEntry[]> | CmssySitemapEntry[];
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* One `<url>` per language, plus x-default - a translated page is not a
|
|
71
|
-
* duplicate, and telling Google it is keeps the translation out of the index.
|
|
72
|
-
*/
|
|
73
|
-
declare function createCmssySitemap(config: CmssyConfig, options?: CmssySitemapOptions): ({ url, request, }: {
|
|
74
|
-
url: URL;
|
|
75
|
-
request: Request;
|
|
76
|
-
}) => Promise<Response>;
|
|
77
|
-
declare function createCmssyRobots(config: CmssyConfig): ({ url, request, }: {
|
|
78
|
-
url: URL;
|
|
79
|
-
request: Request;
|
|
80
|
-
}) => Promise<Response>;
|
|
81
|
-
|
|
82
|
-
export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, type CmssySitemapEntry, type CmssySitemapOptions, cmssyMiddleware, createCmssyRobots, createCmssySitemap, loadCmssyPage };
|
|
53
|
+
export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, cmssyMiddleware, loadCmssyPage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CmssyConfig, CmssyPageData, CmssyLayoutGroup } from '@cmssy/core';
|
|
2
|
-
export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData,
|
|
2
|
+
export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData, createCmssyClient, defineCmssyConfig, localizeHref } from '@cmssy/core';
|
|
3
3
|
|
|
4
4
|
declare const CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
|
|
5
5
|
interface CmssyMiddlewareOptions {
|
|
@@ -50,33 +50,4 @@ interface CmssyPageResult {
|
|
|
50
50
|
*/
|
|
51
51
|
declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL): Promise<CmssyPageResult>;
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
url: string;
|
|
55
|
-
lastModified?: string;
|
|
56
|
-
}
|
|
57
|
-
interface CmssySitemapOptions {
|
|
58
|
-
/**
|
|
59
|
-
* Records are not pages, so the sitemap cannot know about your products or
|
|
60
|
-
* categories. Add them here; you get the same base URL and locales the page
|
|
61
|
-
* entries use, so the two cannot disagree.
|
|
62
|
-
*/
|
|
63
|
-
extra?: (context: {
|
|
64
|
-
baseUrl: string;
|
|
65
|
-
defaultLocale: string;
|
|
66
|
-
locales: string[];
|
|
67
|
-
}) => Promise<CmssySitemapEntry[]> | CmssySitemapEntry[];
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* One `<url>` per language, plus x-default - a translated page is not a
|
|
71
|
-
* duplicate, and telling Google it is keeps the translation out of the index.
|
|
72
|
-
*/
|
|
73
|
-
declare function createCmssySitemap(config: CmssyConfig, options?: CmssySitemapOptions): ({ url, request, }: {
|
|
74
|
-
url: URL;
|
|
75
|
-
request: Request;
|
|
76
|
-
}) => Promise<Response>;
|
|
77
|
-
declare function createCmssyRobots(config: CmssyConfig): ({ url, request, }: {
|
|
78
|
-
url: URL;
|
|
79
|
-
request: Request;
|
|
80
|
-
}) => Promise<Response>;
|
|
81
|
-
|
|
82
|
-
export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, type CmssySitemapEntry, type CmssySitemapOptions, cmssyMiddleware, createCmssyRobots, createCmssySitemap, loadCmssyPage };
|
|
53
|
+
export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, cmssyMiddleware, loadCmssyPage };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { CMSSY_EDIT_HEADER,
|
|
2
|
-
export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER,
|
|
1
|
+
import { CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, isVerifiedEditUrl, applyCmssyCsp, CMSSY_SECRET_QUERY_PARAM } from '@cmssy/core';
|
|
2
|
+
export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, createCmssyClient, defineCmssyConfig, localizeHref } from '@cmssy/core';
|
|
3
|
+
import { CMSSY_LOCALE_HEADER, localeForPathname, isDevelopment, resolveSiteLocales, splitLocaleFromPath, fetchPage, fetchLayouts } from '@cmssy/core/internal';
|
|
3
4
|
|
|
4
5
|
// src/middleware.ts
|
|
5
6
|
var CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
|
|
@@ -66,76 +67,5 @@ async function loadCmssyPage(config, request, url) {
|
|
|
66
67
|
isEdit
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
|
-
function xmlEscape(value) {
|
|
70
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
71
|
-
}
|
|
72
|
-
function baseUrlFor(config, url, request) {
|
|
73
|
-
if (config.siteUrl) return config.siteUrl.replace(/\/+$/, "");
|
|
74
|
-
const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host");
|
|
75
|
-
if (!host) return url.origin.replace(/\/+$/, "");
|
|
76
|
-
const protocol = request.headers.get("x-forwarded-proto") ?? (host.startsWith("localhost") || host.startsWith("127.0.0.1") ? "http" : "https");
|
|
77
|
-
return `${protocol}://${host}`.replace(/\/+$/, "");
|
|
78
|
-
}
|
|
79
|
-
function createCmssySitemap(config, options = {}) {
|
|
80
|
-
return async function GET({
|
|
81
|
-
url,
|
|
82
|
-
request
|
|
83
|
-
}) {
|
|
84
|
-
const baseUrl = baseUrlFor(config, url, request);
|
|
85
|
-
const { defaultLocale, locales } = await resolveSiteLocales(config);
|
|
86
|
-
const pages = await fetchPages(config);
|
|
87
|
-
const entries = [];
|
|
88
|
-
for (const page of pages) {
|
|
89
|
-
const slug = normalizeSlug(page.slug);
|
|
90
|
-
for (const locale of locales) {
|
|
91
|
-
entries.push({
|
|
92
|
-
url: `${baseUrl}${localizedPath(slug, locale, defaultLocale)}`,
|
|
93
|
-
lastModified: page.updatedAt ?? void 0
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (options.extra) {
|
|
98
|
-
entries.push(
|
|
99
|
-
...await options.extra({
|
|
100
|
-
baseUrl,
|
|
101
|
-
defaultLocale,
|
|
102
|
-
locales
|
|
103
|
-
})
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
const body = [
|
|
107
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
108
|
-
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
109
|
-
...entries.map(
|
|
110
|
-
(entry) => [
|
|
111
|
-
" <url>",
|
|
112
|
-
` <loc>${xmlEscape(entry.url)}</loc>`,
|
|
113
|
-
entry.lastModified ? ` <lastmod>${xmlEscape(entry.lastModified)}</lastmod>` : "",
|
|
114
|
-
" </url>"
|
|
115
|
-
].filter(Boolean).join("\n")
|
|
116
|
-
),
|
|
117
|
-
"</urlset>"
|
|
118
|
-
].join("\n");
|
|
119
|
-
return new Response(body, {
|
|
120
|
-
headers: { "content-type": "application/xml" }
|
|
121
|
-
});
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
function createCmssyRobots(config) {
|
|
125
|
-
return async function GET({
|
|
126
|
-
url,
|
|
127
|
-
request
|
|
128
|
-
}) {
|
|
129
|
-
const baseUrl = baseUrlFor(config, url, request);
|
|
130
|
-
const body = [
|
|
131
|
-
"User-agent: *",
|
|
132
|
-
"Allow: /",
|
|
133
|
-
"Disallow: /cmssy-edit",
|
|
134
|
-
`Sitemap: ${baseUrl}/sitemap.xml`,
|
|
135
|
-
""
|
|
136
|
-
].join("\n");
|
|
137
|
-
return new Response(body, { headers: { "content-type": "text/plain" } });
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
70
|
|
|
141
|
-
export { CMSSY_EDIT_PATH_PREFIX, cmssyMiddleware,
|
|
71
|
+
export { CMSSY_EDIT_PATH_PREFIX, cmssyMiddleware, loadCmssyPage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/astro",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.1.0",
|
|
4
4
|
"description": "Astro bindings for cmssy headless sites: middleware, page loader, sitemap and robots.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cmssy",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"vitest": "^2.1.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@cmssy/core": "
|
|
51
|
+
"@cmssy/core": "10.1.0"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "tsup",
|