astro 7.0.9 → 7.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/assets/fonts/infra/fs-font-file-content-resolver.js +1 -1
- package/dist/cli/dev/index.d.ts +17 -0
- package/dist/cli/dev/index.js +56 -2
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/content/consts.d.ts +2 -0
- package/dist/content/consts.js +4 -0
- package/dist/content/content-layer.d.ts +0 -6
- package/dist/content/content-layer.js +4 -9
- package/dist/content/data-store-source.d.ts +36 -0
- package/dist/content/data-store-source.js +30 -0
- package/dist/content/data-store-writer.d.ts +67 -0
- package/dist/content/data-store-writer.js +94 -0
- package/dist/content/data-store.d.ts +15 -0
- package/dist/content/data-store.js +33 -3
- package/dist/content/loaders/glob.d.ts +7 -0
- package/dist/content/loaders/glob.js +2 -2
- package/dist/content/mutable-data-store.d.ts +8 -0
- package/dist/content/mutable-data-store.js +52 -15
- package/dist/content/paths.d.ts +14 -0
- package/dist/content/paths.js +11 -0
- package/dist/content/runtime.js +4 -4
- package/dist/content/vite-plugin-content-virtual-mod.js +29 -2
- package/dist/core/app/dev/pipeline.js +2 -1
- package/dist/core/app/types.d.ts +39 -5
- package/dist/core/base-pipeline.d.ts +1 -1
- package/dist/core/base-pipeline.js +6 -2
- package/dist/core/build/index.js +2 -1
- package/dist/core/build/plugins/plugin-manifest.js +23 -8
- package/dist/core/config/schemas/base.d.ts +38 -5
- package/dist/core/config/schemas/base.js +15 -8
- package/dist/core/config/schemas/relative.d.ts +73 -15
- package/dist/core/constants.js +1 -1
- package/dist/core/csp/common.d.ts +7 -7
- package/dist/core/csp/common.js +8 -9
- package/dist/core/csp/config.d.ts +45 -0
- package/dist/core/csp/config.js +44 -2
- package/dist/core/csp/runtime.d.ts +21 -1
- package/dist/core/csp/runtime.js +31 -0
- package/dist/core/dev/dev.js +9 -4
- package/dist/core/dev/restart.js +7 -1
- package/dist/core/errors/zod-error-map.js +7 -1
- package/dist/core/fetch/fetch-state.js +52 -10
- package/dist/core/logger/config.d.ts +1 -1
- package/dist/core/logger/core.d.ts +4 -4
- package/dist/core/logger/impls/console.d.ts +2 -2
- package/dist/core/logger/impls/json.d.ts +2 -2
- package/dist/core/logger/impls/json.js +3 -14
- package/dist/core/logger/impls/node.d.ts +2 -2
- package/dist/core/logger/load.d.ts +2 -2
- package/dist/core/logger/load.js +19 -27
- package/dist/core/messages/runtime.d.ts +8 -0
- package/dist/core/messages/runtime.js +34 -1
- package/dist/core/render/paginate.js +21 -16
- package/dist/core/sync/index.js +23 -8
- package/dist/integrations/hooks.js +8 -0
- package/dist/manifest/serialized.js +21 -6
- package/dist/runtime/server/escape.d.ts +2 -0
- package/dist/runtime/server/escape.js +4 -0
- package/dist/runtime/server/render/csp.js +80 -19
- package/dist/runtime/server/render/server-islands.js +6 -4
- package/dist/runtime/server/transition.js +2 -2
- package/dist/types/astro.d.ts +2 -3
- package/dist/types/public/common.d.ts +13 -0
- package/dist/types/public/config.d.ts +196 -15
- package/dist/types/public/context.d.ts +27 -11
- package/dist/types/public/internal.d.ts +12 -11
- package/dist/vite-plugin-app/pipeline.js +2 -1
- package/dist/vite-plugin-astro-server/route-guard.js +3 -2
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel
|
|
1
|
+
import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel } from '../core.js';
|
|
2
2
|
import type { AstroInlineConfig } from '../../../types/public/index.js';
|
|
3
3
|
export type JsonHandlerConfig = {
|
|
4
4
|
/**
|
|
@@ -11,5 +11,5 @@ export type JsonHandlerConfig = {
|
|
|
11
11
|
level?: AstroLoggerLevel;
|
|
12
12
|
};
|
|
13
13
|
export declare const SGR_REGEX: RegExp;
|
|
14
|
-
export default function jsonLoggerDestination(config?: JsonHandlerConfig): AstroLoggerDestination
|
|
14
|
+
export default function jsonLoggerDestination(config?: JsonHandlerConfig): AstroLoggerDestination;
|
|
15
15
|
export declare function createJsonLoggerFromFlags(config: AstroInlineConfig): AstroLogger;
|
|
@@ -8,24 +8,13 @@ function jsonLoggerDestination(config = {}) {
|
|
|
8
8
|
const { pretty = false, level = "info" } = config;
|
|
9
9
|
return {
|
|
10
10
|
write(event) {
|
|
11
|
-
let dest = process.stderr;
|
|
12
|
-
if (levels[event.level] < levels["error"]) {
|
|
13
|
-
dest = process.stdout;
|
|
14
|
-
}
|
|
15
11
|
if (!matchesLevel(event.level, level)) {
|
|
16
12
|
return;
|
|
17
13
|
}
|
|
18
|
-
|
|
14
|
+
const dest = levels[event.level] >= levels["error"] ? console.error : console.info;
|
|
19
15
|
const message = event.message.replace(SGR_REGEX, "");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
JSON.stringify({ message, label: event.label, level: event.level }, null, 2) + trailingLine
|
|
23
|
-
);
|
|
24
|
-
} else {
|
|
25
|
-
dest.write(
|
|
26
|
-
JSON.stringify({ message, label: event.label, level: event.level }) + trailingLine
|
|
27
|
-
);
|
|
28
|
-
}
|
|
16
|
+
const payload = pretty ? JSON.stringify({ message, label: event.label, level: event.level }, null, 2) : JSON.stringify({ message, label: event.label, level: event.level });
|
|
17
|
+
dest(payload);
|
|
29
18
|
}
|
|
30
19
|
};
|
|
31
20
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel
|
|
1
|
+
import { AstroLogger, type AstroLoggerDestination, type AstroLoggerLevel } from '../core.js';
|
|
2
2
|
import type { AstroInlineConfig } from '../../../types/public/index.js';
|
|
3
3
|
export type NodeHandlerConfig = {
|
|
4
4
|
level?: AstroLoggerLevel;
|
|
5
5
|
};
|
|
6
|
-
export default function (options?: NodeHandlerConfig): AstroLoggerDestination
|
|
6
|
+
export default function (options?: NodeHandlerConfig): AstroLoggerDestination;
|
|
7
7
|
export declare function createNodeLoggerFromFlags(inlineConfig: AstroInlineConfig): AstroLogger;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AstroLogger, type
|
|
1
|
+
import { AstroLogger, type AstroLoggerDestination } from './core.js';
|
|
2
2
|
import type { LoggerHandlerConfig } from './config.js';
|
|
3
3
|
import type { AstroConfig, AstroInlineConfig } from '../../types/public/index.js';
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function loadLoggerDestination(config: LoggerHandlerConfig): Promise<AstroLoggerDestination>;
|
|
5
5
|
/**
|
|
6
6
|
* It attempts to load a logger from the entrypoint.
|
|
7
7
|
* If not provided, it creates a new logger instance on the fly.
|
package/dist/core/logger/load.js
CHANGED
|
@@ -5,27 +5,22 @@ import { default as nodeLoggerCreator, createNodeLoggerFromFlags } from "./impls
|
|
|
5
5
|
import { default as consoleLoggerCreator } from "./impls/console.js";
|
|
6
6
|
import { default as jsonLoggerCreator } from "./impls/json.js";
|
|
7
7
|
import { default as composeLoggerCreator } from "./impls/compose.js";
|
|
8
|
-
|
|
8
|
+
function normalizeEntrypoint(entrypoint) {
|
|
9
|
+
return entrypoint instanceof URL ? entrypoint.href : entrypoint;
|
|
10
|
+
}
|
|
11
|
+
async function loadLoggerDestination(config) {
|
|
9
12
|
let cause = void 0;
|
|
13
|
+
const entrypoint = normalizeEntrypoint(config.entrypoint);
|
|
10
14
|
try {
|
|
11
15
|
switch (config.entrypoint) {
|
|
12
16
|
case "astro/logger/node": {
|
|
13
|
-
return
|
|
14
|
-
destination: nodeLoggerCreator(config.config),
|
|
15
|
-
level
|
|
16
|
-
});
|
|
17
|
+
return nodeLoggerCreator(config.config);
|
|
17
18
|
}
|
|
18
19
|
case "astro/logger/console": {
|
|
19
|
-
return
|
|
20
|
-
destination: consoleLoggerCreator(config.config),
|
|
21
|
-
level
|
|
22
|
-
});
|
|
20
|
+
return consoleLoggerCreator(config.config);
|
|
23
21
|
}
|
|
24
22
|
case "astro/logger/json": {
|
|
25
|
-
return
|
|
26
|
-
destination: jsonLoggerCreator(config.config),
|
|
27
|
-
level
|
|
28
|
-
});
|
|
23
|
+
return jsonLoggerCreator(config.config);
|
|
29
24
|
}
|
|
30
25
|
case "astro/logger/compose": {
|
|
31
26
|
let destinations = [];
|
|
@@ -35,26 +30,20 @@ async function loadLogger(config, level = "info") {
|
|
|
35
30
|
loggers.map(async (loggerConfig) => {
|
|
36
31
|
const logger = await import(
|
|
37
32
|
/* @vite-ignore */
|
|
38
|
-
loggerConfig.entrypoint
|
|
33
|
+
normalizeEntrypoint(loggerConfig.entrypoint)
|
|
39
34
|
);
|
|
40
35
|
return logger.default(loggerConfig.config);
|
|
41
36
|
})
|
|
42
37
|
);
|
|
43
38
|
}
|
|
44
|
-
return
|
|
45
|
-
destination: composeLoggerCreator(destinations),
|
|
46
|
-
level
|
|
47
|
-
});
|
|
39
|
+
return composeLoggerCreator(destinations);
|
|
48
40
|
}
|
|
49
41
|
default: {
|
|
50
|
-
const
|
|
42
|
+
const logger = await import(
|
|
51
43
|
/* @vite-ignore */
|
|
52
|
-
|
|
44
|
+
entrypoint
|
|
53
45
|
);
|
|
54
|
-
return
|
|
55
|
-
destination: nodeLogger.default(config.config),
|
|
56
|
-
level
|
|
57
|
-
});
|
|
46
|
+
return logger.default(config.config);
|
|
58
47
|
}
|
|
59
48
|
}
|
|
60
49
|
} catch (e) {
|
|
@@ -64,7 +53,7 @@ async function loadLogger(config, level = "info") {
|
|
|
64
53
|
}
|
|
65
54
|
const error = new AstroError({
|
|
66
55
|
...UnableToLoadLogger,
|
|
67
|
-
message: UnableToLoadLogger.message(
|
|
56
|
+
message: UnableToLoadLogger.message(entrypoint)
|
|
68
57
|
});
|
|
69
58
|
if (cause) {
|
|
70
59
|
error.cause = cause;
|
|
@@ -75,7 +64,10 @@ async function loadOrCreateNodeLogger(astroConfig, inlineAstroConfig) {
|
|
|
75
64
|
if (inlineAstroConfig._logger) return inlineAstroConfig._logger;
|
|
76
65
|
try {
|
|
77
66
|
if (astroConfig.logger) {
|
|
78
|
-
return
|
|
67
|
+
return new AstroLogger({
|
|
68
|
+
destination: await loadLoggerDestination(astroConfig.logger),
|
|
69
|
+
level: inlineAstroConfig.logLevel ?? "info"
|
|
70
|
+
});
|
|
79
71
|
} else {
|
|
80
72
|
return createNodeLoggerFromFlags(inlineAstroConfig);
|
|
81
73
|
}
|
|
@@ -84,6 +76,6 @@ async function loadOrCreateNodeLogger(astroConfig, inlineAstroConfig) {
|
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
78
|
export {
|
|
87
|
-
|
|
79
|
+
loadLoggerDestination,
|
|
88
80
|
loadOrCreateNodeLogger
|
|
89
81
|
};
|
|
@@ -58,3 +58,11 @@ export declare function printHelp({ commandName, headline, usage, tables, descri
|
|
|
58
58
|
description?: string;
|
|
59
59
|
}): void;
|
|
60
60
|
export declare function warnIfCspWithShiki(config: AstroConfig, logger: AstroLogger): void;
|
|
61
|
+
/**
|
|
62
|
+
* Warns when a `scriptDirective`/`styleDirective` defines `default`-kind resources alongside
|
|
63
|
+
* `element`/`attribute`-kind entries. Because the more specific directive (`*-src-elem`/`*-src-attr`)
|
|
64
|
+
* overrides the generic one for its scope and browsers do not fall back, the generic resources will
|
|
65
|
+
* not apply there. Astro's generated hashes are folded automatically, so this only concerns
|
|
66
|
+
* user-provided resources.
|
|
67
|
+
*/
|
|
68
|
+
export declare function warnIfCspResourceFallbackShadowing(config: AstroConfig, logger: AstroLogger): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import colors from "piccolore";
|
|
2
|
+
import { partitionByKind } from "../csp/runtime.js";
|
|
2
3
|
import { getDocsForError, renderErrorMarkdown } from "../errors/dev/runtime.js";
|
|
3
4
|
import {
|
|
4
5
|
AstroError,
|
|
@@ -269,7 +270,7 @@ function printHelp({
|
|
|
269
270
|
message.push(
|
|
270
271
|
linebreak(),
|
|
271
272
|
` ${bgGreen(black(` ${commandName} `))} ${green(
|
|
272
|
-
`v${"7.0
|
|
273
|
+
`v${"7.1.0"}`
|
|
273
274
|
)} ${headline}`
|
|
274
275
|
);
|
|
275
276
|
}
|
|
@@ -304,6 +305,37 @@ function warnIfCspWithShiki(config, logger) {
|
|
|
304
305
|
);
|
|
305
306
|
}
|
|
306
307
|
}
|
|
308
|
+
function warnIfCspResourceFallbackShadowing(config, logger) {
|
|
309
|
+
const csp = config.security.csp;
|
|
310
|
+
if (typeof csp !== "object") return;
|
|
311
|
+
const families = [
|
|
312
|
+
{ name: "script", directive: csp.scriptDirective },
|
|
313
|
+
{ name: "style", directive: csp.styleDirective }
|
|
314
|
+
];
|
|
315
|
+
for (const { name, directive } of families) {
|
|
316
|
+
const sources = partitionByKind({
|
|
317
|
+
resources: directive?.resources ?? [],
|
|
318
|
+
hashes: directive?.hashes ?? []
|
|
319
|
+
});
|
|
320
|
+
if (sources.default.resources.length === 0) continue;
|
|
321
|
+
const shadowed = [];
|
|
322
|
+
if (sources.element.resources.length > 0 || sources.element.hashes.length > 0) {
|
|
323
|
+
shadowed.push(`\`${name}-src-elem\``);
|
|
324
|
+
}
|
|
325
|
+
if (sources.attribute.resources.length > 0 || sources.attribute.hashes.length > 0) {
|
|
326
|
+
shadowed.push(`\`${name}-src-attr\``);
|
|
327
|
+
}
|
|
328
|
+
if (shadowed.length === 0) continue;
|
|
329
|
+
logger.warn(
|
|
330
|
+
"csp",
|
|
331
|
+
`\`security.csp.${name}Directive\` defines \`${name}-src\` resources (${sources.default.resources.join(
|
|
332
|
+
" "
|
|
333
|
+
)}) as well as ${shadowed.join(" and ")} resources or hashes. Because ${shadowed.join(
|
|
334
|
+
" and "
|
|
335
|
+
)} override \`${name}-src\` for their scope (browsers do not fall back), those \`${name}-src\` resources will not apply there. Add them to the corresponding \`kind\` if needed.`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
307
339
|
export {
|
|
308
340
|
actionRequired,
|
|
309
341
|
cancelled,
|
|
@@ -327,5 +359,6 @@ export {
|
|
|
327
359
|
telemetryEnabled,
|
|
328
360
|
telemetryNotice,
|
|
329
361
|
telemetryReset,
|
|
362
|
+
warnIfCspResourceFallbackShadowing,
|
|
330
363
|
warnIfCspWithShiki
|
|
331
364
|
};
|
|
@@ -4,11 +4,12 @@ import { getRouteGenerator } from "../routing/generator.js";
|
|
|
4
4
|
function generatePaginateFunction(routeMatch, base, trailingSlash) {
|
|
5
5
|
return function paginateUtility(data, args = {}) {
|
|
6
6
|
const generate = getRouteGenerator(routeMatch.segments, trailingSlash);
|
|
7
|
-
let { pageSize: _pageSize, params: _params, props: _props } = args;
|
|
7
|
+
let { pageSize: _pageSize, params: _params, props: _props, format: _format } = args;
|
|
8
8
|
const pageSize = _pageSize || 10;
|
|
9
9
|
const paramName = "page";
|
|
10
10
|
const additionalParams = _params || {};
|
|
11
11
|
const additionalProps = _props || {};
|
|
12
|
+
const formatUrl = _format || ((url) => url);
|
|
12
13
|
let includesFirstPageNumber;
|
|
13
14
|
if (routeMatch.params.includes(`...${paramName}`)) {
|
|
14
15
|
includesFirstPageNumber = false;
|
|
@@ -29,23 +30,27 @@ function generatePaginateFunction(routeMatch, base, trailingSlash) {
|
|
|
29
30
|
...additionalParams,
|
|
30
31
|
[paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : void 0
|
|
31
32
|
};
|
|
32
|
-
const current = addRouteBase(generate({ ...params }), base);
|
|
33
|
-
const next = pageNum === lastPage ? void 0 : addRouteBase(generate({ ...params, page: String(pageNum + 1) }), base);
|
|
34
|
-
const prev = pageNum === 1 ? void 0 :
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
const current = formatUrl(addRouteBase(generate({ ...params }), base));
|
|
34
|
+
const next = pageNum === lastPage ? void 0 : formatUrl(addRouteBase(generate({ ...params, page: String(pageNum + 1) }), base));
|
|
35
|
+
const prev = pageNum === 1 ? void 0 : formatUrl(
|
|
36
|
+
addRouteBase(
|
|
37
|
+
generate({
|
|
38
|
+
...params,
|
|
39
|
+
page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
|
|
40
|
+
}),
|
|
41
|
+
base
|
|
42
|
+
)
|
|
40
43
|
);
|
|
41
|
-
const first = pageNum === 1 ? void 0 :
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const first = pageNum === 1 ? void 0 : formatUrl(
|
|
45
|
+
addRouteBase(
|
|
46
|
+
generate({
|
|
47
|
+
...params,
|
|
48
|
+
page: includesFirstPageNumber ? "1" : void 0
|
|
49
|
+
}),
|
|
50
|
+
base
|
|
51
|
+
)
|
|
47
52
|
);
|
|
48
|
-
const last = pageNum === lastPage ? void 0 : addRouteBase(generate({ ...params, page: String(lastPage) }), base);
|
|
53
|
+
const last = pageNum === lastPage ? void 0 : formatUrl(addRouteBase(generate({ ...params, page: String(lastPage) }), base));
|
|
49
54
|
return {
|
|
50
55
|
params,
|
|
51
56
|
props: {
|
package/dist/core/sync/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import colors from "piccolore";
|
|
|
6
6
|
import { createServer } from "vite";
|
|
7
7
|
import { syncFonts } from "../../assets/fonts/sync.js";
|
|
8
8
|
import { CONTENT_TYPES_FILE } from "../../content/consts.js";
|
|
9
|
-
import { getDataStoreFile } from "../../content/
|
|
9
|
+
import { getDataStoreDir, getDataStoreFile } from "../../content/paths.js";
|
|
10
10
|
import { globalContentLayer } from "../../content/instance.js";
|
|
11
11
|
import { createContentTypesGenerator } from "../../content/index.js";
|
|
12
12
|
import { MutableDataStore } from "../../content/mutable-data-store.js";
|
|
@@ -59,11 +59,21 @@ async function clearContentLayerCache({
|
|
|
59
59
|
fs = fsMod,
|
|
60
60
|
isDev
|
|
61
61
|
}) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
if (settings.config.experimental.collectionStorage === "chunked") {
|
|
63
|
+
const dataStore = getDataStoreDir(settings, isDev);
|
|
64
|
+
if (fs.existsSync(dataStore)) {
|
|
65
|
+
logger.debug("content", "clearing data store");
|
|
66
|
+
await fs.promises.rm(dataStore, { force: true, recursive: true });
|
|
67
|
+
await fs.promises.mkdir(dataStore, { recursive: true });
|
|
68
|
+
logger.warn("content", "data store cleared (force)");
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
const dataStore = getDataStoreFile(settings, isDev);
|
|
72
|
+
if (fs.existsSync(dataStore)) {
|
|
73
|
+
logger.debug("content", "clearing data store");
|
|
74
|
+
await fs.promises.rm(dataStore, { force: true });
|
|
75
|
+
logger.warn("content", "data store cleared (force)");
|
|
76
|
+
}
|
|
67
77
|
}
|
|
68
78
|
}
|
|
69
79
|
async function syncInternal({
|
|
@@ -88,8 +98,13 @@ async function syncInternal({
|
|
|
88
98
|
settings.timer.start("Sync content layer");
|
|
89
99
|
let store;
|
|
90
100
|
try {
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
if (settings.config.experimental.collectionStorage === "chunked") {
|
|
102
|
+
const dataStoreDir = getDataStoreDir(settings, isDev);
|
|
103
|
+
store = await MutableDataStore.fromDir(dataStoreDir);
|
|
104
|
+
} else {
|
|
105
|
+
const dataStoreFile = getDataStoreFile(settings, isDev);
|
|
106
|
+
store = await MutableDataStore.fromFile(dataStoreFile);
|
|
107
|
+
}
|
|
93
108
|
} catch (err) {
|
|
94
109
|
logger.error("content", err.message);
|
|
95
110
|
}
|
|
@@ -14,6 +14,7 @@ import { validateSetAdapter } from "../core/dev/adapter-validation.js";
|
|
|
14
14
|
import { getRouteGenerator } from "../core/routing/generator.js";
|
|
15
15
|
import { getClientOutputDirectory } from "../prerender/utils.js";
|
|
16
16
|
import { validateSupportedFeatures } from "./features-validation.js";
|
|
17
|
+
import { loadLoggerDestination } from "../core/logger/load.js";
|
|
17
18
|
async function withTakingALongTimeMsg({
|
|
18
19
|
name,
|
|
19
20
|
hookName,
|
|
@@ -139,6 +140,7 @@ async function runHookConfigSetup({
|
|
|
139
140
|
let astroJSXRenderer = null;
|
|
140
141
|
for (let i = 0; i < updatedConfig.integrations.length; i++) {
|
|
141
142
|
const integration = updatedConfig.integrations[i];
|
|
143
|
+
let isLoggerUpdated = false;
|
|
142
144
|
const { integrationLogger } = await runHookInternal({
|
|
143
145
|
integration,
|
|
144
146
|
hookName: "astro:config:setup",
|
|
@@ -169,6 +171,9 @@ async function runHookConfigSetup({
|
|
|
169
171
|
updatedSettings.scripts.push({ stage, content });
|
|
170
172
|
},
|
|
171
173
|
updateConfig: (newConfig) => {
|
|
174
|
+
if (newConfig.logger?.entrypoint) {
|
|
175
|
+
isLoggerUpdated = true;
|
|
176
|
+
}
|
|
172
177
|
updatedConfig = mergeConfig(updatedConfig, newConfig);
|
|
173
178
|
return { ...updatedConfig };
|
|
174
179
|
},
|
|
@@ -254,6 +259,9 @@ async function runHookConfigSetup({
|
|
|
254
259
|
}
|
|
255
260
|
try {
|
|
256
261
|
updatedConfig = await validateConfigRefined(updatedConfig);
|
|
262
|
+
if (isLoggerUpdated) {
|
|
263
|
+
logger.setDestination(await loadLoggerDestination(updatedConfig.logger));
|
|
264
|
+
}
|
|
257
265
|
} catch (error) {
|
|
258
266
|
integrationLogger.error("An error occurred while updating the config");
|
|
259
267
|
throw error;
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getStyleResources,
|
|
15
15
|
shouldTrackCspHashes
|
|
16
16
|
} from "../core/csp/common.js";
|
|
17
|
+
import { partitionByKind } from "../core/csp/runtime.js";
|
|
17
18
|
import { createKey, encodeKey, getEnvironmentKey, hasEnvironmentKey } from "../core/encryption.js";
|
|
18
19
|
import { MIDDLEWARE_MODULE_ID } from "../core/middleware/vite-plugin.js";
|
|
19
20
|
import { SERVER_ISLAND_MANIFEST } from "../core/server-islands/vite-plugin-server-islands.js";
|
|
@@ -131,15 +132,29 @@ async function createSerializedManifest(settings, encodedKey) {
|
|
|
131
132
|
};
|
|
132
133
|
}
|
|
133
134
|
if (shouldTrackCspHashes(settings.config.security.csp)) {
|
|
135
|
+
const cspConfig = settings.config.security.csp;
|
|
136
|
+
const scriptDirective = {
|
|
137
|
+
resources: getScriptResources(cspConfig),
|
|
138
|
+
hashes: getScriptHashes(cspConfig),
|
|
139
|
+
strictDynamic: getStrictDynamic(cspConfig)
|
|
140
|
+
};
|
|
141
|
+
const styleDirective = {
|
|
142
|
+
resources: getStyleResources(cspConfig),
|
|
143
|
+
hashes: getStyleHashes(cspConfig)
|
|
144
|
+
};
|
|
145
|
+
const scriptDefault = partitionByKind(scriptDirective).default;
|
|
146
|
+
const styleDefault = partitionByKind(styleDirective).default;
|
|
134
147
|
csp = {
|
|
135
148
|
cspDestination: settings.adapter?.adapterFeatures?.staticHeaders ? "adapter" : void 0,
|
|
136
|
-
|
|
137
|
-
scriptResources: getScriptResources(settings.config.security.csp),
|
|
138
|
-
styleHashes: getStyleHashes(settings.config.security.csp),
|
|
139
|
-
styleResources: getStyleResources(settings.config.security.csp),
|
|
140
|
-
algorithm: getAlgorithm(settings.config.security.csp),
|
|
149
|
+
algorithm: getAlgorithm(cspConfig),
|
|
141
150
|
directives: getDirectives(settings),
|
|
142
|
-
|
|
151
|
+
scriptHashes: scriptDefault.hashes,
|
|
152
|
+
scriptResources: scriptDefault.resources,
|
|
153
|
+
isStrictDynamic: scriptDirective.strictDynamic,
|
|
154
|
+
styleHashes: styleDefault.hashes,
|
|
155
|
+
styleResources: styleDefault.resources,
|
|
156
|
+
scriptDirective,
|
|
157
|
+
styleDirective
|
|
143
158
|
};
|
|
144
159
|
}
|
|
145
160
|
let loggerConfig = void 0;
|
|
@@ -7,6 +7,8 @@ export declare const escapeHTML: (str: string) => string;
|
|
|
7
7
|
* @see https://mathiasbynens.be/notes/etago
|
|
8
8
|
*/
|
|
9
9
|
export declare function stringifyForScript(value: any): string;
|
|
10
|
+
/** Escapes CSS text so it can be embedded inside a `<style>` tag. */
|
|
11
|
+
export declare function escapeStyleText(value: string): string;
|
|
10
12
|
export declare class HTMLBytes extends Uint8Array {
|
|
11
13
|
}
|
|
12
14
|
declare const htmlStringSymbol: unique symbol;
|
|
@@ -4,6 +4,9 @@ const escapeHTML = escape;
|
|
|
4
4
|
function stringifyForScript(value) {
|
|
5
5
|
return JSON.stringify(value)?.replace(/</g, "\\u003c");
|
|
6
6
|
}
|
|
7
|
+
function escapeStyleText(value) {
|
|
8
|
+
return value.replaceAll("<", "\\3C ");
|
|
9
|
+
}
|
|
7
10
|
class HTMLBytes extends Uint8Array {
|
|
8
11
|
}
|
|
9
12
|
Object.defineProperty(HTMLBytes.prototype, Symbol.toStringTag, {
|
|
@@ -77,6 +80,7 @@ export {
|
|
|
77
80
|
HTMLBytes,
|
|
78
81
|
HTMLString,
|
|
79
82
|
escapeHTML,
|
|
83
|
+
escapeStyleText,
|
|
80
84
|
isHTMLBytes,
|
|
81
85
|
isHTMLString,
|
|
82
86
|
markHTMLString,
|
|
@@ -1,34 +1,95 @@
|
|
|
1
|
+
import { partitionByKind } from "../../../core/csp/runtime.js";
|
|
1
2
|
function renderCspContent(result) {
|
|
3
|
+
const { scriptDirective, styleDirective, directives } = result;
|
|
4
|
+
const script = partitionByKind(scriptDirective);
|
|
5
|
+
const style = partitionByKind(styleDirective);
|
|
2
6
|
const finalScriptHashes = /* @__PURE__ */ new Set();
|
|
3
|
-
const
|
|
4
|
-
for (const scriptHash of result.scriptHashes) {
|
|
7
|
+
for (const scriptHash of script.default.hashes) {
|
|
5
8
|
finalScriptHashes.add(`'${scriptHash}'`);
|
|
6
9
|
}
|
|
7
|
-
for (const
|
|
10
|
+
for (const scriptHash of result._metadata.extraScriptHashes) {
|
|
11
|
+
finalScriptHashes.add(`'${scriptHash}'`);
|
|
12
|
+
}
|
|
13
|
+
const finalStyleHashes = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const styleHash of style.default.hashes) {
|
|
8
15
|
finalStyleHashes.add(`'${styleHash}'`);
|
|
9
16
|
}
|
|
10
17
|
for (const styleHash of result._metadata.extraStyleHashes) {
|
|
11
18
|
finalStyleHashes.add(`'${styleHash}'`);
|
|
12
19
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
let directives;
|
|
17
|
-
if (result.directives.length > 0) {
|
|
18
|
-
directives = result.directives.join(";") + ";";
|
|
20
|
+
let directivesContent;
|
|
21
|
+
if (directives.length > 0) {
|
|
22
|
+
directivesContent = directives.join(";") + ";";
|
|
19
23
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
const scriptResources = script.default.resources.length > 0 ? script.default.resources.join(" ") : "'self'";
|
|
25
|
+
const styleResources = style.default.resources.length > 0 ? style.default.resources.join(" ") : "'self'";
|
|
26
|
+
const scriptElementDefaultResource = script.default.resources.length > 0 ? "" : "'self'";
|
|
27
|
+
const styleElementDefaultResource = style.default.resources.length > 0 ? "" : "'self'";
|
|
28
|
+
const scriptElemActive = isEnabled(script.element);
|
|
29
|
+
const styleElemActive = isEnabled(style.element);
|
|
30
|
+
const strictDynamicSuffix = scriptDirective.strictDynamic ? ` 'strict-dynamic'` : "";
|
|
31
|
+
const scriptBaselineTokens = [
|
|
32
|
+
...scriptElemActive ? [] : [...finalScriptHashes],
|
|
33
|
+
...scriptDirective.strictDynamic ? [`'strict-dynamic'`] : []
|
|
34
|
+
];
|
|
35
|
+
const scriptSrc = `script-src ${scriptResources} ${scriptBaselineTokens.join(" ")};`;
|
|
36
|
+
const styleSrc = `style-src ${styleResources} ${(styleElemActive ? [] : [...finalStyleHashes]).join(" ")};`;
|
|
37
|
+
const scriptSrcElem = scriptElemActive ? renderSpecificDirective(
|
|
38
|
+
"script-src-elem",
|
|
39
|
+
script.element.resources,
|
|
40
|
+
scriptElementDefaultResource,
|
|
41
|
+
finalScriptHashes,
|
|
42
|
+
script.element.hashes,
|
|
43
|
+
strictDynamicSuffix
|
|
44
|
+
) : void 0;
|
|
45
|
+
const scriptSrcAttr = isEnabled(script.attribute) ? renderSpecificDirective(
|
|
46
|
+
"script-src-attr",
|
|
47
|
+
script.attribute.resources,
|
|
48
|
+
"'none'",
|
|
49
|
+
void 0,
|
|
50
|
+
script.attribute.hashes
|
|
51
|
+
) : void 0;
|
|
52
|
+
const styleSrcElem = styleElemActive ? renderSpecificDirective(
|
|
53
|
+
"style-src-elem",
|
|
54
|
+
style.element.resources,
|
|
55
|
+
styleElementDefaultResource,
|
|
56
|
+
finalStyleHashes,
|
|
57
|
+
style.element.hashes
|
|
58
|
+
) : void 0;
|
|
59
|
+
const styleSrcAttr = isEnabled(style.attribute) ? renderSpecificDirective(
|
|
60
|
+
"style-src-attr",
|
|
61
|
+
style.attribute.resources,
|
|
62
|
+
"'none'",
|
|
63
|
+
void 0,
|
|
64
|
+
style.attribute.hashes
|
|
65
|
+
) : void 0;
|
|
66
|
+
return [
|
|
67
|
+
directivesContent,
|
|
68
|
+
scriptSrc,
|
|
69
|
+
scriptSrcElem,
|
|
70
|
+
scriptSrcAttr,
|
|
71
|
+
styleSrc,
|
|
72
|
+
styleSrcElem,
|
|
73
|
+
styleSrcAttr
|
|
74
|
+
].filter(Boolean).join(" ");
|
|
75
|
+
}
|
|
76
|
+
function isEnabled(sources) {
|
|
77
|
+
return sources.resources.length > 0 || sources.hashes.length > 0;
|
|
78
|
+
}
|
|
79
|
+
function renderSpecificDirective(name, resources, defaultResource, sharedHashes, ownHashes, suffix = "") {
|
|
80
|
+
const hashes = new Set(sharedHashes);
|
|
81
|
+
for (const hash of ownHashes) {
|
|
82
|
+
hashes.add(`'${hash}'`);
|
|
23
83
|
}
|
|
24
|
-
let
|
|
25
|
-
if (
|
|
26
|
-
|
|
84
|
+
let finalResources;
|
|
85
|
+
if (resources.length > 0) {
|
|
86
|
+
finalResources = resources.map((r) => `${r}`).join(" ");
|
|
87
|
+
} else if (defaultResource === "'none'" && hashes.size > 0) {
|
|
88
|
+
finalResources = "";
|
|
89
|
+
} else {
|
|
90
|
+
finalResources = defaultResource;
|
|
27
91
|
}
|
|
28
|
-
|
|
29
|
-
const scriptSrc = `script-src ${scriptResources} ${Array.from(finalScriptHashes).join(" ")}${strictDynamic};`;
|
|
30
|
-
const styleSrc = `style-src ${styleResources} ${Array.from(finalStyleHashes).join(" ")};`;
|
|
31
|
-
return [directives, scriptSrc, styleSrc].filter(Boolean).join(" ");
|
|
92
|
+
return `${name} ${[finalResources, ...hashes].filter(Boolean).join(" ")}${suffix};`;
|
|
32
93
|
}
|
|
33
94
|
export {
|
|
34
95
|
renderCspContent
|
|
@@ -5,6 +5,7 @@ import { createThinHead } from "./astro/head-and-content.js";
|
|
|
5
5
|
import { createRenderInstruction } from "./instruction.js";
|
|
6
6
|
import { SERVER_ISLAND_START } from "./server-islands-shared.js";
|
|
7
7
|
import { renderSlotToString } from "./slot.js";
|
|
8
|
+
import { toAttributeString } from "./util.js";
|
|
8
9
|
const internalProps = /* @__PURE__ */ new Set([
|
|
9
10
|
"server:component-path",
|
|
10
11
|
"server:component-export",
|
|
@@ -148,16 +149,17 @@ class ServerIslandComponent {
|
|
|
148
149
|
serverIslandUrl += "?" + potentialSearchParams.toString();
|
|
149
150
|
this.result._metadata.extraHead.push(
|
|
150
151
|
markHTMLString(
|
|
151
|
-
`<link rel="preload" as="fetch" href="${serverIslandUrl}" crossorigin="anonymous">`
|
|
152
|
+
`<link rel="preload" as="fetch" href="${toAttributeString(serverIslandUrl)}" crossorigin="anonymous">`
|
|
152
153
|
)
|
|
153
154
|
);
|
|
154
155
|
}
|
|
155
156
|
const adapterHeaders = this.result.internalFetchHeaders || {};
|
|
156
157
|
const headersJson = stringifyForScript(adapterHeaders);
|
|
158
|
+
const serverIslandUrlJson = stringifyForScript(serverIslandUrl);
|
|
157
159
|
const method = useGETRequest ? (
|
|
158
160
|
// GET request
|
|
159
161
|
`const headers = new Headers(${headersJson});
|
|
160
|
-
let response = await fetch(
|
|
162
|
+
let response = await fetch(${serverIslandUrlJson}, { headers });`
|
|
161
163
|
) : (
|
|
162
164
|
// POST request
|
|
163
165
|
`let data = {
|
|
@@ -166,13 +168,13 @@ let response = await fetch('${serverIslandUrl}', { headers });`
|
|
|
166
168
|
encryptedSlots: ${stringifyForScript(slotsEncrypted)},
|
|
167
169
|
};
|
|
168
170
|
const headers = new Headers({ 'Content-Type': 'application/json', ...${headersJson} });
|
|
169
|
-
let response = await fetch(
|
|
171
|
+
let response = await fetch(${serverIslandUrlJson}, {
|
|
170
172
|
method: 'POST',
|
|
171
173
|
body: JSON.stringify(data),
|
|
172
174
|
headers,
|
|
173
175
|
});`
|
|
174
176
|
);
|
|
175
|
-
this.islandContent = `${method}replaceServerIsland(
|
|
177
|
+
this.islandContent = `${method}replaceServerIsland(${stringifyForScript(hostId)}, response);`;
|
|
176
178
|
return this.islandContent;
|
|
177
179
|
}
|
|
178
180
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import cssesc from "../../transitions/cssesc.js";
|
|
2
2
|
import { fade, slide } from "../../transitions/index.js";
|
|
3
|
-
import { markHTMLString } from "./escape.js";
|
|
3
|
+
import { escapeStyleText, markHTMLString } from "./escape.js";
|
|
4
4
|
const transitionNameMap = /* @__PURE__ */ new WeakMap();
|
|
5
5
|
function incrementTransitionNumber(result) {
|
|
6
6
|
let num = 1;
|
|
@@ -56,7 +56,7 @@ function renderTransition(result, hash, animationName, transitionName) {
|
|
|
56
56
|
sheet.addAnimationRaw("new", "animation: none; mix-blend-mode: normal;");
|
|
57
57
|
sheet.addModern("group", "animation: none");
|
|
58
58
|
}
|
|
59
|
-
const css = sheet.toString();
|
|
59
|
+
const css = escapeStyleText(sheet.toString());
|
|
60
60
|
result._metadata.extraHead.push(markHTMLString(`<style>${css}</style>`));
|
|
61
61
|
return scope;
|
|
62
62
|
}
|
package/dist/types/astro.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Server } from 'node:http';
|
|
2
|
+
import type { CspHash } from '../core/csp/config.js';
|
|
2
3
|
import type { AstroTimer } from '../core/config/timer.js';
|
|
3
4
|
import type { TSConfig } from '../core/config/tsconfig.js';
|
|
4
5
|
import type { AstroLogger, AstroLoggerLevel } from '../core/logger/core.js';
|
|
@@ -18,7 +19,6 @@ export type SerializedRouteData = Omit<RouteData, 'generate' | 'pattern' | 'redi
|
|
|
18
19
|
trailingSlash: AstroConfig['trailingSlash'];
|
|
19
20
|
};
|
|
20
21
|
};
|
|
21
|
-
type CspObject = Required<Exclude<AstroConfig['security']['csp'], boolean>>;
|
|
22
22
|
export interface AstroSettings {
|
|
23
23
|
config: AstroConfig;
|
|
24
24
|
adapter: AstroAdapter | undefined;
|
|
@@ -64,7 +64,7 @@ export interface AstroSettings {
|
|
|
64
64
|
buildOutput: undefined | 'static' | 'server';
|
|
65
65
|
injectedCsp: {
|
|
66
66
|
fontResources: Set<string>;
|
|
67
|
-
styleHashes:
|
|
67
|
+
styleHashes: CspHash[];
|
|
68
68
|
};
|
|
69
69
|
logLevel: AstroLoggerLevel;
|
|
70
70
|
fontsHttpServer: Server | null;
|
|
@@ -89,4 +89,3 @@ export interface ImportedDevStyle {
|
|
|
89
89
|
url: string;
|
|
90
90
|
content: string;
|
|
91
91
|
}
|
|
92
|
-
export {};
|