@megabudino/stack-utils 1.4.0 → 1.5.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/README.md +13 -8
- package/dist/images/config.d.ts +1 -0
- package/dist/images/config.js +1 -0
- package/dist/images/generator.js +123 -1
- package/dist/proxy/handle.js +23 -5
- package/dist/proxy/index.d.ts +1 -1
- package/dist/proxy/rewrite.d.ts +6 -2
- package/dist/proxy/rewrite.js +37 -3
- package/dist/proxy/types.d.ts +33 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -49,8 +49,7 @@ Proxy config:
|
|
|
49
49
|
| --- | --- | --- | --- |
|
|
50
50
|
| `originUrl` | `string` | required | Origin server URL |
|
|
51
51
|
| `siteUrl` | `string` | required | Public site URL |
|
|
52
|
-
| `
|
|
53
|
-
| `sitemapCacheSeconds` | `number` | `3600` | Cache max-age for sitemap responses |
|
|
52
|
+
| `sitemap` | `SitemapConfig` | `{}` | Sitemap index injection and origin URL filtering |
|
|
54
53
|
| `proxyAssets` | `boolean` | `false` | Rewrite origin asset URLs to relative paths |
|
|
55
54
|
| `additionalOrigins` | `string[]` | `[]` | Additional absolute origins to rewrite like `originUrl` |
|
|
56
55
|
| `domActions` | `DomAction[]` | `[]` | Declarative DOM transformations for proxied HTML |
|
|
@@ -61,7 +60,7 @@ What it does:
|
|
|
61
60
|
- lets SvelteKit routes resolve normally when a route exists
|
|
62
61
|
- proxies unmatched requests to the configured origin
|
|
63
62
|
- rewrites redirects back to the public domain
|
|
64
|
-
- rewrites HTML, applies optional DOM actions, and
|
|
63
|
+
- rewrites HTML, applies optional DOM actions, and rewrites sitemap XML when configured
|
|
65
64
|
|
|
66
65
|
### `images`
|
|
67
66
|
|
|
@@ -99,18 +98,24 @@ What it does:
|
|
|
99
98
|
- reads source images from `static/images`
|
|
100
99
|
- writes responsive variants to `static/_image-variants`
|
|
101
100
|
- writes its generated manifest to `.stack-utils/image-variants.generated.json`
|
|
101
|
+
- writes cache metadata to `.stack-utils/image-variants.cache.json`
|
|
102
102
|
- rewrites compatible `<img src="/images/...">` tags to an internal `StaticImage` component
|
|
103
103
|
- keeps `StaticImage` internal to the package so the public API stays small
|
|
104
104
|
|
|
105
|
-
###
|
|
105
|
+
### Reusing built images
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
If you want to build the image variants once and reuse them across machines or CI runs, commit:
|
|
108
108
|
|
|
109
|
-
```
|
|
110
|
-
static/_image-variants
|
|
111
|
-
.stack-utils
|
|
109
|
+
```text
|
|
110
|
+
static/_image-variants/
|
|
111
|
+
.stack-utils/image-variants.generated.json
|
|
112
|
+
.stack-utils/image-variants.cache.json
|
|
112
113
|
```
|
|
113
114
|
|
|
115
|
+
The pipeline will hash the current source images, verify the referenced generated files still exist, and skip regeneration when everything matches.
|
|
116
|
+
|
|
117
|
+
If you prefer generated artifacts to stay local, you can still ignore those paths in the consuming app.
|
|
118
|
+
|
|
114
119
|
## Publishing Notes
|
|
115
120
|
|
|
116
121
|
The package is intended to be consumed via subpath imports:
|
package/dist/images/config.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare const staticImagePipelineConfig: {
|
|
|
4
4
|
readonly publicSourcePrefix: "/images";
|
|
5
5
|
readonly publicOutputPrefix: "/_image-variants";
|
|
6
6
|
readonly manifestPath: ".stack-utils/image-variants.generated.json";
|
|
7
|
+
readonly cachePath: ".stack-utils/image-variants.cache.json";
|
|
7
8
|
readonly supportedExtensions: readonly [".jpg", ".jpeg", ".png", ".webp"];
|
|
8
9
|
readonly widths: readonly [320, 480, 640, 768, 960, 1280, 1536, 1920];
|
|
9
10
|
readonly quality: {
|
package/dist/images/config.js
CHANGED
|
@@ -4,6 +4,7 @@ export const staticImagePipelineConfig = {
|
|
|
4
4
|
publicSourcePrefix: '/images',
|
|
5
5
|
publicOutputPrefix: '/_image-variants',
|
|
6
6
|
manifestPath: '.stack-utils/image-variants.generated.json',
|
|
7
|
+
cachePath: '.stack-utils/image-variants.cache.json',
|
|
7
8
|
supportedExtensions: ['.jpg', '.jpeg', '.png', '.webp'],
|
|
8
9
|
widths: [320, 480, 640, 768, 960, 1280, 1536, 1920],
|
|
9
10
|
quality: {
|
package/dist/images/generator.js
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createReadStream, promises as fs } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import sharp from 'sharp';
|
|
4
5
|
import { staticImagePipelineConfig } from './config.js';
|
|
5
6
|
const SUPPORTED_EXTENSIONS = new Set(staticImagePipelineConfig.supportedExtensions);
|
|
7
|
+
const IMAGE_VARIANT_CACHE_VERSION = 1;
|
|
6
8
|
export async function generateStaticImageVariants({ root, log = () => undefined }) {
|
|
7
9
|
const sourceDirectory = path.join(root, staticImagePipelineConfig.sourceDirectory);
|
|
8
10
|
const outputDirectory = path.join(root, staticImagePipelineConfig.outputDirectory);
|
|
9
11
|
const manifestPath = path.join(root, staticImagePipelineConfig.manifestPath);
|
|
12
|
+
const cachePath = path.join(root, staticImagePipelineConfig.cachePath);
|
|
10
13
|
await fs.mkdir(path.dirname(manifestPath), { recursive: true });
|
|
14
|
+
await fs.mkdir(path.dirname(cachePath), { recursive: true });
|
|
11
15
|
const sourceFiles = await collectSourceImages(sourceDirectory);
|
|
16
|
+
const cacheMetadata = await buildCacheMetadata(sourceFiles);
|
|
12
17
|
if (sourceFiles.length === 0) {
|
|
13
18
|
await fs.mkdir(outputDirectory, { recursive: true });
|
|
14
19
|
await fs.writeFile(manifestPath, '{}\n');
|
|
20
|
+
await fs.writeFile(cachePath, `${JSON.stringify(cacheMetadata, null, 2)}\n`);
|
|
15
21
|
return {};
|
|
16
22
|
}
|
|
17
23
|
await fs.mkdir(outputDirectory, { recursive: true });
|
|
24
|
+
const reusableManifest = await readReusableManifest({
|
|
25
|
+
root,
|
|
26
|
+
sourceFiles,
|
|
27
|
+
manifestPath,
|
|
28
|
+
cachePath,
|
|
29
|
+
cacheMetadata
|
|
30
|
+
});
|
|
31
|
+
if (reusableManifest) {
|
|
32
|
+
log(`[static-image-pipeline] reused ${Object.keys(reusableManifest).length} manifest entries`);
|
|
33
|
+
return reusableManifest;
|
|
34
|
+
}
|
|
18
35
|
const manifest = {};
|
|
19
36
|
for (const file of sourceFiles) {
|
|
20
37
|
const entry = await generateManifestEntry({ file, outputDirectory });
|
|
@@ -23,9 +40,34 @@ export async function generateStaticImageVariants({ root, log = () => undefined
|
|
|
23
40
|
}
|
|
24
41
|
}
|
|
25
42
|
await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
43
|
+
await fs.writeFile(cachePath, `${JSON.stringify(cacheMetadata, null, 2)}\n`);
|
|
26
44
|
log(`[static-image-pipeline] generated ${Object.keys(manifest).length} manifest entries`);
|
|
27
45
|
return manifest;
|
|
28
46
|
}
|
|
47
|
+
async function readReusableManifest({ root, sourceFiles, manifestPath, cachePath, cacheMetadata }) {
|
|
48
|
+
const [manifest, persistedCache] = await Promise.all([
|
|
49
|
+
readJsonFile(manifestPath),
|
|
50
|
+
readJsonFile(cachePath)
|
|
51
|
+
]);
|
|
52
|
+
if (!manifest || !persistedCache) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (persistedCache.version !== IMAGE_VARIANT_CACHE_VERSION) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (persistedCache.configHash !== cacheMetadata.configHash) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (!areStringRecordsEqual(persistedCache.sources, cacheMetadata.sources)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (!manifestMatchesSources(manifest, sourceFiles)) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const outputFiles = getOutputFilesFromManifest(root, manifest);
|
|
68
|
+
const outputFilesExist = await allFilesExist(outputFiles);
|
|
69
|
+
return outputFilesExist ? manifest : null;
|
|
70
|
+
}
|
|
29
71
|
async function generateManifestEntry({ file, outputDirectory }) {
|
|
30
72
|
const image = sharp(file.absolutePath, { failOn: 'none' });
|
|
31
73
|
const metadata = await image.metadata();
|
|
@@ -146,6 +188,22 @@ async function collectSourceImages(directory) {
|
|
|
146
188
|
files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
147
189
|
return files;
|
|
148
190
|
}
|
|
191
|
+
async function buildCacheMetadata(sourceFiles) {
|
|
192
|
+
const sources = Object.fromEntries(await Promise.all(sourceFiles.map(async (file) => [toSourcePath(file.relativePath), await hashFile(file.absolutePath)])));
|
|
193
|
+
return {
|
|
194
|
+
version: IMAGE_VARIANT_CACHE_VERSION,
|
|
195
|
+
configHash: createHash('sha1')
|
|
196
|
+
.update(JSON.stringify({
|
|
197
|
+
outputDirectory: staticImagePipelineConfig.outputDirectory,
|
|
198
|
+
publicOutputPrefix: staticImagePipelineConfig.publicOutputPrefix,
|
|
199
|
+
widths: staticImagePipelineConfig.widths,
|
|
200
|
+
quality: staticImagePipelineConfig.quality,
|
|
201
|
+
supportedExtensions: staticImagePipelineConfig.supportedExtensions
|
|
202
|
+
}))
|
|
203
|
+
.digest('hex'),
|
|
204
|
+
sources
|
|
205
|
+
};
|
|
206
|
+
}
|
|
149
207
|
async function walkDirectory(directory, onFile, baseDirectory = directory) {
|
|
150
208
|
let entries;
|
|
151
209
|
try {
|
|
@@ -179,10 +237,71 @@ async function needsWrite(outputPath, sourceMtimeMs) {
|
|
|
179
237
|
throw error;
|
|
180
238
|
}
|
|
181
239
|
}
|
|
240
|
+
async function hashFile(filePath) {
|
|
241
|
+
return new Promise((resolve, reject) => {
|
|
242
|
+
const hash = createHash('sha1');
|
|
243
|
+
const stream = createReadStream(filePath);
|
|
244
|
+
stream.on('data', (chunk) => hash.update(chunk));
|
|
245
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
246
|
+
stream.on('error', reject);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async function readJsonFile(filePath) {
|
|
250
|
+
try {
|
|
251
|
+
const fileContents = await fs.readFile(filePath, 'utf8');
|
|
252
|
+
return JSON.parse(fileContents);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (error.code === 'ENOENT') {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function areStringRecordsEqual(left, right) {
|
|
262
|
+
const leftEntries = Object.entries(left).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey));
|
|
263
|
+
const rightEntries = Object.entries(right).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey));
|
|
264
|
+
if (leftEntries.length !== rightEntries.length) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
return leftEntries.every(([leftKey, leftValue], index) => leftKey === rightEntries[index]?.[0] && leftValue === rightEntries[index]?.[1]);
|
|
268
|
+
}
|
|
269
|
+
function manifestMatchesSources(manifest, sourceFiles) {
|
|
270
|
+
const manifestSources = Object.keys(manifest).sort((left, right) => left.localeCompare(right));
|
|
271
|
+
const expectedSources = sourceFiles
|
|
272
|
+
.map((file) => toSourcePath(file.relativePath))
|
|
273
|
+
.sort((left, right) => left.localeCompare(right));
|
|
274
|
+
if (manifestSources.length !== expectedSources.length) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
return manifestSources.every((sourcePath, index) => sourcePath === expectedSources[index]);
|
|
278
|
+
}
|
|
279
|
+
async function allFilesExist(filePaths) {
|
|
280
|
+
for (const filePath of filePaths) {
|
|
281
|
+
try {
|
|
282
|
+
await fs.stat(filePath);
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
if (error.code === 'ENOENT') {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
182
293
|
function getTargetWidths(originalWidth) {
|
|
183
294
|
const widths = staticImagePipelineConfig.widths.filter((width) => width < originalWidth).map(Number);
|
|
184
295
|
return Array.from(new Set([...widths, originalWidth])).sort((left, right) => left - right);
|
|
185
296
|
}
|
|
297
|
+
function getOutputFilesFromManifest(root, manifest) {
|
|
298
|
+
return Object.values(manifest).flatMap((entry) => Object.values(entry.formats).flatMap((variants) => variants.map((variant) => {
|
|
299
|
+
const outputRelativePath = variant.url
|
|
300
|
+
.replace(`${staticImagePipelineConfig.publicOutputPrefix}/`, '')
|
|
301
|
+
.replace(`${staticImagePipelineConfig.publicOutputPrefix}`, '');
|
|
302
|
+
return path.join(root, staticImagePipelineConfig.outputDirectory, outputRelativePath);
|
|
303
|
+
})));
|
|
304
|
+
}
|
|
186
305
|
function getFallbackExtension(extension) {
|
|
187
306
|
if (extension === '.png') {
|
|
188
307
|
return 'png';
|
|
@@ -201,3 +320,6 @@ function getGeneratedExtension(format, fallbackExtension) {
|
|
|
201
320
|
function toPosixPath(filePath) {
|
|
202
321
|
return filePath.split(path.sep).join('/');
|
|
203
322
|
}
|
|
323
|
+
function toSourcePath(relativePath) {
|
|
324
|
+
return `${staticImagePipelineConfig.publicSourcePrefix}/${toPosixPath(relativePath)}`;
|
|
325
|
+
}
|
package/dist/proxy/handle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { applyDomActions } from './dom-actions.js';
|
|
2
|
-
import { injectIntoSitemapIndex, rewriteHtml } from './rewrite.js';
|
|
2
|
+
import { injectIntoSitemapIndex, normalizeSitemapPath, removeExcludedUrlsFromSitemap, rewriteHtml } from './rewrite.js';
|
|
3
3
|
/**
|
|
4
4
|
* Create a SvelteKit `Handle` hook that reverse-proxies requests to an origin.
|
|
5
5
|
*
|
|
@@ -7,9 +7,13 @@ import { injectIntoSitemapIndex, rewriteHtml } from './rewrite.js';
|
|
|
7
7
|
* everything else is forwarded to the origin.
|
|
8
8
|
*/
|
|
9
9
|
export function createProxyHandle(config) {
|
|
10
|
+
assertNoDeprecatedSitemapConfig(config);
|
|
10
11
|
const { originUrl, siteUrl } = config;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
12
|
+
const sitemap = config.sitemap ?? {};
|
|
13
|
+
const sitemapIndexPath = sitemap.indexPath ? normalizeSitemapPath(sitemap.indexPath) : undefined;
|
|
14
|
+
const sitemapIncludeFiles = sitemap.includeFiles ?? [];
|
|
15
|
+
const sitemapExcludePaths = sitemap.excludePaths ?? [];
|
|
16
|
+
const sitemapCache = `public, max-age=${sitemap.cacheSeconds ?? 3600}`;
|
|
13
17
|
const proxyAssets = config.proxyAssets ?? false;
|
|
14
18
|
const additionalOrigins = config.additionalOrigins ?? [];
|
|
15
19
|
const maxRedirects = config.maxInternalRedirects ?? 5;
|
|
@@ -72,8 +76,14 @@ export function createProxyHandle(config) {
|
|
|
72
76
|
if (contentType.includes('text/xml') || contentType.includes('application/xml')) {
|
|
73
77
|
let xml = await upstreamResponse.text();
|
|
74
78
|
xml = xml.replace(/<\?xml-stylesheet[^?]*\?>\s*/g, '');
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
const isConfiguredSitemapIndex = sitemapIndexPath !== undefined && normalizeSitemapPath(pathname) === sitemapIndexPath;
|
|
80
|
+
if (isConfiguredSitemapIndex &&
|
|
81
|
+
sitemapIncludeFiles.length > 0 &&
|
|
82
|
+
xml.includes('</sitemapindex>')) {
|
|
83
|
+
xml = injectIntoSitemapIndex(xml, siteUrl, sitemapIncludeFiles);
|
|
84
|
+
}
|
|
85
|
+
else if (sitemapExcludePaths.length > 0 && xml.includes('</urlset>')) {
|
|
86
|
+
xml = removeExcludedUrlsFromSitemap(xml, sitemapExcludePaths);
|
|
77
87
|
}
|
|
78
88
|
cleanHeaders.set('cache-control', sitemapCache);
|
|
79
89
|
return new Response(xml, {
|
|
@@ -103,3 +113,11 @@ export function createProxyHandle(config) {
|
|
|
103
113
|
}
|
|
104
114
|
};
|
|
105
115
|
}
|
|
116
|
+
function assertNoDeprecatedSitemapConfig(config) {
|
|
117
|
+
if ('sitemaps' in config) {
|
|
118
|
+
throw new Error('`sitemaps` is deprecated and no longer supported. Use `sitemap.includeFiles` with `sitemap.indexPath` instead.');
|
|
119
|
+
}
|
|
120
|
+
if ('sitemapCacheSeconds' in config) {
|
|
121
|
+
throw new Error('`sitemapCacheSeconds` is deprecated and no longer supported. Use `sitemap.cacheSeconds` instead.');
|
|
122
|
+
}
|
|
123
|
+
}
|
package/dist/proxy/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { createProxyHandle } from './handle.js';
|
|
2
|
-
export type { DomAction, ProxyConfig } from './types.js';
|
|
2
|
+
export type { DomAction, ProxyConfig, SitemapConfig } from './types.js';
|
package/dist/proxy/rewrite.d.ts
CHANGED
|
@@ -9,5 +9,9 @@
|
|
|
9
9
|
* - When `proxyAssets` is false, absolute origin URLs are left as-is.
|
|
10
10
|
*/
|
|
11
11
|
export declare function rewriteHtml(html: string, originUrl: string, siteUrl: string, proxyAssets?: boolean, additionalOrigins?: string[]): string;
|
|
12
|
-
/**
|
|
13
|
-
export declare function
|
|
12
|
+
/** Normalize URL-like values to comparable sitemap pathnames. */
|
|
13
|
+
export declare function normalizeSitemapPath(value: string): string;
|
|
14
|
+
/** Inject local sitemap references into a configured `<sitemapindex>`. */
|
|
15
|
+
export declare function injectIntoSitemapIndex(xml: string, siteUrl: string, includeFiles: string[]): string;
|
|
16
|
+
/** Remove `<url>` entries whose `<loc>` pathname is listed in `excludePaths`. */
|
|
17
|
+
export declare function removeExcludedUrlsFromSitemap(xml: string, excludePaths: string[]): string;
|
package/dist/proxy/rewrite.js
CHANGED
|
@@ -41,10 +41,44 @@ export function rewriteHtml(html, originUrl, siteUrl, proxyAssets = false, addit
|
|
|
41
41
|
}
|
|
42
42
|
return html;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
export function
|
|
46
|
-
|
|
44
|
+
/** Normalize URL-like values to comparable sitemap pathnames. */
|
|
45
|
+
export function normalizeSitemapPath(value) {
|
|
46
|
+
let pathname = value.trim();
|
|
47
|
+
try {
|
|
48
|
+
pathname = new URL(pathname, 'https://stack-utils.local').pathname;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Fall back to the raw value below; invalid URL-like values should not crash rewriting.
|
|
52
|
+
}
|
|
53
|
+
if (!pathname.startsWith('/')) {
|
|
54
|
+
pathname = `/${pathname}`;
|
|
55
|
+
}
|
|
56
|
+
if (pathname === '/') {
|
|
57
|
+
return pathname;
|
|
58
|
+
}
|
|
59
|
+
return pathname.replace(/\/+$/g, '') || '/';
|
|
60
|
+
}
|
|
61
|
+
/** Inject local sitemap references into a configured `<sitemapindex>`. */
|
|
62
|
+
export function injectIntoSitemapIndex(xml, siteUrl, includeFiles) {
|
|
63
|
+
if (includeFiles.length === 0) {
|
|
64
|
+
return xml;
|
|
65
|
+
}
|
|
66
|
+
const entries = includeFiles
|
|
47
67
|
.map((url) => `\t<sitemap>\n\t\t<loc>${siteUrl}${url}</loc>\n\t</sitemap>`)
|
|
48
68
|
.join('\n');
|
|
49
69
|
return xml.replace('</sitemapindex>', `${entries}\n</sitemapindex>`);
|
|
50
70
|
}
|
|
71
|
+
/** Remove `<url>` entries whose `<loc>` pathname is listed in `excludePaths`. */
|
|
72
|
+
export function removeExcludedUrlsFromSitemap(xml, excludePaths) {
|
|
73
|
+
if (excludePaths.length === 0) {
|
|
74
|
+
return xml;
|
|
75
|
+
}
|
|
76
|
+
const excluded = new Set(excludePaths.map(normalizeSitemapPath));
|
|
77
|
+
return xml.replace(/<url\b[\s\S]*?<\/url>/g, (entry) => {
|
|
78
|
+
const loc = entry.match(/<loc\b[^>]*>\s*([\s\S]*?)\s*<\/loc>/i)?.[1];
|
|
79
|
+
if (!loc) {
|
|
80
|
+
return entry;
|
|
81
|
+
}
|
|
82
|
+
return excluded.has(normalizeSitemapPath(loc)) ? '' : entry;
|
|
83
|
+
});
|
|
84
|
+
}
|
package/dist/proxy/types.d.ts
CHANGED
|
@@ -34,22 +34,44 @@ export interface DomAction {
|
|
|
34
34
|
*/
|
|
35
35
|
remove?: boolean;
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
type DeprecatedProxyOption<Message extends string> = {
|
|
38
|
+
readonly __deprecatedProxyOption: Message;
|
|
39
|
+
};
|
|
40
|
+
/** Sitemap rewriting configuration for proxied XML responses. */
|
|
41
|
+
export interface SitemapConfig {
|
|
42
|
+
/**
|
|
43
|
+
* Origin sitemap index path that can receive `includeFiles`.
|
|
44
|
+
* Example: `/sitemap_index.xml`.
|
|
45
|
+
*/
|
|
46
|
+
indexPath?: string;
|
|
43
47
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
48
|
+
* Local sitemap files to add to the configured sitemap index.
|
|
49
|
+
* Example: [`/sveltekit-sitemap.xml`].
|
|
46
50
|
*/
|
|
47
|
-
|
|
51
|
+
includeFiles?: string[];
|
|
52
|
+
/**
|
|
53
|
+
* URL paths to remove from origin `<urlset>` sitemap responses.
|
|
54
|
+
* Matching compares normalized pathnames only.
|
|
55
|
+
*/
|
|
56
|
+
excludePaths?: string[];
|
|
48
57
|
/**
|
|
49
58
|
* Cache-Control max-age (in seconds) for sitemap responses.
|
|
50
59
|
* Defaults to 3600 (1 hour).
|
|
51
60
|
*/
|
|
52
|
-
|
|
61
|
+
cacheSeconds?: number;
|
|
62
|
+
}
|
|
63
|
+
/** Configuration for the reverse proxy. */
|
|
64
|
+
export interface ProxyConfig {
|
|
65
|
+
/** The origin URL (not publicly exposed). */
|
|
66
|
+
originUrl: string;
|
|
67
|
+
/** The public site URL (used in sitemaps and link rewriting). */
|
|
68
|
+
siteUrl: string;
|
|
69
|
+
/** Explicit sitemap rewriting configuration for proxied XML responses. */
|
|
70
|
+
sitemap?: SitemapConfig;
|
|
71
|
+
/** @deprecated Use `sitemap.includeFiles` with `sitemap.indexPath` instead. */
|
|
72
|
+
sitemaps?: DeprecatedProxyOption<'`sitemaps` is deprecated. Use `sitemap.includeFiles` with `sitemap.indexPath` instead.'>;
|
|
73
|
+
/** @deprecated Use `sitemap.cacheSeconds` instead. */
|
|
74
|
+
sitemapCacheSeconds?: DeprecatedProxyOption<'`sitemapCacheSeconds` is deprecated. Use `sitemap.cacheSeconds` instead.'>;
|
|
53
75
|
/**
|
|
54
76
|
* If true, absolute origin URLs in HTML are rewritten to relative paths
|
|
55
77
|
* so assets are served through the SvelteKit proxy and the origin is never exposed.
|
|
@@ -79,3 +101,4 @@ export interface ProxyConfig {
|
|
|
79
101
|
*/
|
|
80
102
|
domActions?: DomAction[];
|
|
81
103
|
}
|
|
104
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@megabudino/stack-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Reusable utilities for a SvelteKit stack, including a reverse proxy and static image pipeline",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "node ./scripts/build.mjs",
|
|
31
|
-
"test": "node --test
|
|
31
|
+
"test": "node --test *.test.mjs",
|
|
32
32
|
"typecheck": "tsc --noEmit"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|