astro 5.9.3 → 5.10.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.
Files changed (50) hide show
  1. package/client.d.ts +1 -3
  2. package/components/Image.astro +5 -6
  3. package/components/Picture.astro +5 -5
  4. package/components/ResponsivePicture.astro +1 -0
  5. package/dist/actions/integration.d.ts +2 -1
  6. package/dist/actions/integration.js +3 -2
  7. package/dist/actions/utils.d.ts +1 -1
  8. package/dist/actions/utils.js +9 -8
  9. package/dist/assets/internal.d.ts +1 -5
  10. package/dist/assets/internal.js +21 -23
  11. package/dist/assets/types.d.ts +4 -4
  12. package/dist/assets/vite-plugin-assets.js +2 -2
  13. package/dist/content/config.d.ts +74 -0
  14. package/dist/content/config.js +78 -0
  15. package/dist/content/consts.d.ts +1 -0
  16. package/dist/content/consts.js +2 -0
  17. package/dist/content/content-layer.js +3 -3
  18. package/dist/content/loaders/errors.d.ts +20 -0
  19. package/dist/content/loaders/errors.js +64 -0
  20. package/dist/content/loaders/types.d.ts +21 -0
  21. package/dist/content/runtime.d.ts +23 -7
  22. package/dist/content/runtime.js +218 -28
  23. package/dist/content/types-generator.js +11 -4
  24. package/dist/content/utils.d.ts +37 -1
  25. package/dist/content/utils.js +29 -8
  26. package/dist/content/vite-plugin-content-virtual-mod.d.ts +1 -1
  27. package/dist/content/vite-plugin-content-virtual-mod.js +20 -6
  28. package/dist/core/config/schemas/base.d.ts +44 -44
  29. package/dist/core/config/schemas/base.js +9 -9
  30. package/dist/core/config/schemas/refined.js +0 -7
  31. package/dist/core/config/schemas/relative.d.ts +58 -58
  32. package/dist/core/constants.js +1 -1
  33. package/dist/core/csp/config.d.ts +3 -3
  34. package/dist/core/csp/config.js +1 -0
  35. package/dist/core/dev/dev.js +1 -1
  36. package/dist/core/errors/errors-data.d.ts +16 -0
  37. package/dist/core/errors/errors-data.js +15 -4
  38. package/dist/core/errors/errors.js +1 -1
  39. package/dist/core/messages.js +2 -2
  40. package/dist/core/session.d.ts +1 -1
  41. package/dist/core/session.js +10 -9
  42. package/dist/integrations/hooks.js +5 -2
  43. package/dist/runtime/client/dev-toolbar/apps/astro.js +4 -6
  44. package/dist/runtime/server/render/csp.js +1 -1
  45. package/dist/types/public/config.d.ts +40 -131
  46. package/dist/types/public/content.d.ts +30 -0
  47. package/package.json +4 -3
  48. package/templates/content/module.mjs +14 -0
  49. package/templates/content/types.d.ts +43 -0
  50. package/types/content.d.ts +23 -80
package/client.d.ts CHANGED
@@ -48,9 +48,7 @@ declare module 'astro:assets' {
48
48
  getImage: (
49
49
  options: import('./dist/assets/types.js').UnresolvedImageTransform,
50
50
  ) => Promise<import('./dist/assets/types.js').GetImageResult>;
51
- imageConfig: import('./dist/types/public/config.js').AstroConfig['image'] & {
52
- experimentalResponsiveImages: boolean;
53
- };
51
+ imageConfig: import('./dist/types/public/config.js').AstroConfig['image'];
54
52
  getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
55
53
  inferRemoteSize: typeof import('./dist/assets/utils/index.js').inferRemoteSize;
56
54
  Image: typeof import('./components/Image.astro').default;
@@ -24,14 +24,13 @@ if (typeof props.height === 'string') {
24
24
  props.height = parseInt(props.height);
25
25
  }
26
26
 
27
- const layout = props.layout ?? imageConfig.experimentalLayout ?? 'none';
28
- const useResponsive = imageConfig.experimentalResponsiveImages && layout !== 'none';
27
+ const layout = props.layout ?? imageConfig.layout ?? 'none';
29
28
 
30
- if (useResponsive) {
29
+ if (layout !== 'none') {
31
30
  // Apply defaults from imageConfig if not provided
32
- props.layout ??= imageConfig.experimentalLayout;
33
- props.fit ??= imageConfig.experimentalObjectFit ?? 'cover';
34
- props.position ??= imageConfig.experimentalObjectPosition ?? 'center';
31
+ props.layout ??= imageConfig.layout;
32
+ props.fit ??= imageConfig.objectFit ?? 'cover';
33
+ props.position ??= imageConfig.objectPosition ?? 'center';
35
34
  }
36
35
 
37
36
  const image = await getImage(props as UnresolvedImageTransform);
@@ -42,14 +42,14 @@ if (scopedStyleClass) {
42
42
  }
43
43
  }
44
44
 
45
- const layout = props.layout ?? imageConfig.experimentalLayout ?? 'none';
46
- const useResponsive = imageConfig.experimentalResponsiveImages && layout !== 'none';
45
+ const layout = props.layout ?? imageConfig.layout ?? 'none';
46
+ const useResponsive = layout !== 'none';
47
47
 
48
48
  if (useResponsive) {
49
49
  // Apply defaults from imageConfig if not provided
50
- props.layout ??= imageConfig.experimentalLayout;
51
- props.fit ??= imageConfig.experimentalObjectFit ?? 'cover';
52
- props.position ??= imageConfig.experimentalObjectPosition ?? 'center';
50
+ props.layout ??= imageConfig.layout;
51
+ props.fit ??= imageConfig.objectFit ?? 'cover';
52
+ props.position ??= imageConfig.objectPosition ?? 'center';
53
53
  }
54
54
 
55
55
  for (const key in props) {
@@ -4,6 +4,7 @@ import { default as Picture, type Props as PictureProps } from './Picture.astro'
4
4
  type Props = PictureProps;
5
5
 
6
6
  const { class: className, ...props } = Astro.props;
7
+ import './image.css';
7
8
  ---
8
9
 
9
10
  {/* Applying class outside of the spread prevents it from applying unnecessary astro-* classes */}
@@ -4,6 +4,7 @@ import type { AstroIntegration } from '../types/public/integrations.js';
4
4
  * This integration is applied when the user is using Actions in their project.
5
5
  * It will inject the necessary routes and middlewares to handle actions.
6
6
  */
7
- export default function astroIntegrationActionsRouteHandler({ settings, }: {
7
+ export default function astroIntegrationActionsRouteHandler({ settings, filename, }: {
8
8
  settings: AstroSettings;
9
+ filename: string;
9
10
  }): AstroIntegration;
@@ -3,7 +3,8 @@ import { AstroError } from "../core/errors/errors.js";
3
3
  import { viteID } from "../core/util.js";
4
4
  import { ACTIONS_TYPES_FILE, ACTION_RPC_ROUTE_PATTERN, VIRTUAL_MODULE_ID } from "./consts.js";
5
5
  function astroIntegrationActionsRouteHandler({
6
- settings
6
+ settings,
7
+ filename
7
8
  }) {
8
9
  return {
9
10
  name: VIRTUAL_MODULE_ID,
@@ -23,7 +24,7 @@ function astroIntegrationActionsRouteHandler({
23
24
  throw error;
24
25
  }
25
26
  const stringifiedActionsImport = JSON.stringify(
26
- viteID(new URL("./actions", params.config.srcDir))
27
+ viteID(new URL(`./${filename}`, params.config.srcDir))
27
28
  );
28
29
  settings.injectedTypes.push({
29
30
  filename: ACTIONS_TYPES_FILE,
@@ -7,4 +7,4 @@ export declare function createCallAction(context: ActionAPIContext): APIContext[
7
7
  /**
8
8
  * Check whether the Actions config file is present.
9
9
  */
10
- export declare function isActionsFilePresent(fs: typeof fsMod, srcDir: URL): Promise<boolean>;
10
+ export declare function isActionsFilePresent(fs: typeof fsMod, srcDir: URL): Promise<string | false>;
@@ -26,20 +26,20 @@ async function isActionsFilePresent(fs, srcDir) {
26
26
  if (!actionsFile) return false;
27
27
  let contents;
28
28
  try {
29
- contents = fs.readFileSync(actionsFile, "utf-8");
29
+ contents = fs.readFileSync(actionsFile.url, "utf-8");
30
30
  } catch {
31
31
  return false;
32
32
  }
33
- const [, exports] = eslexer.parse(contents, actionsFile.pathname);
33
+ const [, exports] = eslexer.parse(contents, actionsFile.url.pathname);
34
34
  for (const exp of exports) {
35
35
  if (exp.n === "server") {
36
- return true;
36
+ return actionsFile.filename;
37
37
  }
38
38
  }
39
39
  return false;
40
40
  }
41
41
  function search(fs, srcDir) {
42
- const paths = [
42
+ const filenames = [
43
43
  "actions.mjs",
44
44
  "actions.js",
45
45
  "actions.mts",
@@ -48,10 +48,11 @@ function search(fs, srcDir) {
48
48
  "actions/index.js",
49
49
  "actions/index.mts",
50
50
  "actions/index.ts"
51
- ].map((p) => new URL(p, srcDir));
52
- for (const file of paths) {
53
- if (fs.existsSync(file)) {
54
- return file;
51
+ ];
52
+ for (const filename of filenames) {
53
+ const url = new URL(filename, srcDir);
54
+ if (fs.existsSync(url)) {
55
+ return { filename, url };
55
56
  }
56
57
  }
57
58
  return void 0;
@@ -2,8 +2,4 @@ import type { AstroConfig } from '../types/public/config.js';
2
2
  import { type ImageService } from './services/service.js';
3
3
  import { type GetImageResult, type UnresolvedImageTransform } from './types.js';
4
4
  export declare function getConfiguredImageService(): Promise<ImageService>;
5
- type ImageConfig = AstroConfig['image'] & {
6
- experimentalResponsiveImages: boolean;
7
- };
8
- export declare function getImage(options: UnresolvedImageTransform, imageConfig: ImageConfig): Promise<GetImageResult>;
9
- export {};
5
+ export declare function getImage(options: UnresolvedImageTransform, imageConfig: AstroConfig['image']): Promise<GetImageResult>;
@@ -89,36 +89,34 @@ async function getImage(options, imageConfig) {
89
89
  }
90
90
  }
91
91
  resolvedOptions.src = clonedSrc;
92
- const layout = options.layout ?? imageConfig.experimentalLayout;
93
- if (imageConfig.experimentalResponsiveImages && layout) {
92
+ const layout = options.layout ?? imageConfig.layout ?? "none";
93
+ if (resolvedOptions.priority) {
94
+ resolvedOptions.loading ??= "eager";
95
+ resolvedOptions.decoding ??= "sync";
96
+ resolvedOptions.fetchpriority ??= "high";
97
+ delete resolvedOptions.priority;
98
+ } else {
99
+ resolvedOptions.loading ??= "lazy";
100
+ resolvedOptions.decoding ??= "async";
101
+ resolvedOptions.fetchpriority ??= "auto";
102
+ }
103
+ if (layout !== "none") {
94
104
  resolvedOptions.widths ||= getWidths({
95
105
  width: resolvedOptions.width,
96
106
  layout,
97
107
  originalWidth,
98
- breakpoints: imageConfig.experimentalBreakpoints?.length ? imageConfig.experimentalBreakpoints : isLocalService(service) ? LIMITED_RESOLUTIONS : DEFAULT_RESOLUTIONS
108
+ breakpoints: imageConfig.breakpoints?.length ? imageConfig.breakpoints : isLocalService(service) ? LIMITED_RESOLUTIONS : DEFAULT_RESOLUTIONS
99
109
  });
100
110
  resolvedOptions.sizes ||= getSizesAttribute({ width: resolvedOptions.width, layout });
101
- if (resolvedOptions.priority) {
102
- resolvedOptions.loading ??= "eager";
103
- resolvedOptions.decoding ??= "sync";
104
- resolvedOptions.fetchpriority ??= "high";
105
- } else {
106
- resolvedOptions.loading ??= "lazy";
107
- resolvedOptions.decoding ??= "async";
108
- resolvedOptions.fetchpriority ??= "auto";
109
- }
110
- delete resolvedOptions.priority;
111
111
  delete resolvedOptions.densities;
112
- if (layout !== "none") {
113
- resolvedOptions.style = addCSSVarsToStyle(
114
- {
115
- fit: cssFitValues.includes(resolvedOptions.fit ?? "") && resolvedOptions.fit,
116
- pos: resolvedOptions.position
117
- },
118
- resolvedOptions.style
119
- );
120
- resolvedOptions["data-astro-image"] = layout;
121
- }
112
+ resolvedOptions.style = addCSSVarsToStyle(
113
+ {
114
+ fit: cssFitValues.includes(resolvedOptions.fit ?? "") && resolvedOptions.fit,
115
+ pos: resolvedOptions.position
116
+ },
117
+ resolvedOptions.style
118
+ );
119
+ resolvedOptions["data-astro-image"] = layout;
122
120
  }
123
121
  const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
124
122
  const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : [];
@@ -138,9 +138,9 @@ type ImageSharedProps<T> = T & {
138
138
  quality?: ImageQuality;
139
139
  } & ({
140
140
  /**
141
- * The layout type for responsive images. Requires the `experimental.responsiveImages` flag to be enabled in the Astro config.
141
+ * The layout type for responsive images.
142
142
  *
143
- * Allowed values are `constrained`, `fixed`, `full-width` or `none`. Defaults to value of `image.experimentalLayout`.
143
+ * Allowed values are `constrained`, `fixed`, `full-width` or `none`. Defaults to value of `image.layout`.
144
144
  *
145
145
  * - `constrained` - The image will scale to fit the container, maintaining its aspect ratio, but will not exceed the specified dimensions.
146
146
  * - `fixed` - The image will maintain its original dimensions.
@@ -153,7 +153,7 @@ type ImageSharedProps<T> = T & {
153
153
  */
154
154
  layout?: ImageLayout;
155
155
  /**
156
- * Defines how the image should be cropped if the aspect ratio is changed. Requires the `experimental.responsiveImages` flag to be enabled in the Astro config.
156
+ * Defines how the image should be cropped if the aspect ratio is changed. Requires `layout` to be set.
157
157
  *
158
158
  * Default is `cover`. Allowed values are `fill`, `contain`, `cover`, `none` or `scale-down`. These behave like the equivalent CSS `object-fit` values. Other values may be passed if supported by the image service.
159
159
  *
@@ -164,7 +164,7 @@ type ImageSharedProps<T> = T & {
164
164
  */
165
165
  fit?: ImageFit;
166
166
  /**
167
- * Defines the position of the image when cropping. Requires the `experimental.responsiveImages` flag to be enabled in the Astro config.
167
+ * Defines the position of the image when cropping. Requires `layout` to be set.
168
168
  *
169
169
  * The value is a string that specifies the position of the image, which matches the CSS `object-position` property. Other values may be passed if supported by the image service.
170
170
  *
@@ -71,7 +71,7 @@ function assets({ fs, settings, sync, logger }) {
71
71
  globalThis.astroAsset = {
72
72
  referencedImages: /* @__PURE__ */ new Set()
73
73
  };
74
- const imageComponentPrefix = settings.config.experimental.responsiveImages && settings.config.image.experimentalDefaultStyles ? "Responsive" : "";
74
+ const imageComponentPrefix = settings.config.image.responsiveStyles ? "Responsive" : "";
75
75
  return [
76
76
  // Expose the components and different utilities from `astro:assets`
77
77
  {
@@ -98,7 +98,7 @@ function assets({ fs, settings, sync, logger }) {
98
98
  export { default as Font } from "astro/components/Font.astro";
99
99
  export { inferRemoteSize } from "astro/assets/utils/inferRemoteSize.js";
100
100
 
101
- export const imageConfig = ${JSON.stringify({ ...settings.config.image, experimentalResponsiveImages: settings.config.experimental.responsiveImages })};
101
+ export const imageConfig = ${JSON.stringify(settings.config.image)};
102
102
  // This is used by the @astrojs/node integration to locate images.
103
103
  // It's unused on other platforms, but on some platforms like Netlify (and presumably also Vercel)
104
104
  // new URL("dist/...") is interpreted by the bundler as a signal to include that directory
@@ -0,0 +1,74 @@
1
+ import type { ZodLiteral, ZodNumber, ZodObject, ZodString, ZodType, ZodUnion } from 'zod';
2
+ import type { LiveLoader, Loader } from './loaders/types.js';
3
+ export type ImageFunction = () => ZodObject<{
4
+ src: ZodString;
5
+ width: ZodNumber;
6
+ height: ZodNumber;
7
+ format: ZodUnion<[
8
+ ZodLiteral<'png'>,
9
+ ZodLiteral<'jpg'>,
10
+ ZodLiteral<'jpeg'>,
11
+ ZodLiteral<'tiff'>,
12
+ ZodLiteral<'webp'>,
13
+ ZodLiteral<'gif'>,
14
+ ZodLiteral<'svg'>,
15
+ ZodLiteral<'avif'>
16
+ ]>;
17
+ }>;
18
+ export interface DataEntry {
19
+ id: string;
20
+ data: Record<string, unknown>;
21
+ filePath?: string;
22
+ body?: string;
23
+ }
24
+ export interface DataStore {
25
+ get: (key: string) => DataEntry;
26
+ entries: () => Array<[id: string, DataEntry]>;
27
+ set: (key: string, data: Record<string, unknown>, body?: string, filePath?: string) => void;
28
+ values: () => Array<DataEntry>;
29
+ keys: () => Array<string>;
30
+ delete: (key: string) => void;
31
+ clear: () => void;
32
+ has: (key: string) => boolean;
33
+ }
34
+ export interface MetaStore {
35
+ get: (key: string) => string | undefined;
36
+ set: (key: string, value: string) => void;
37
+ delete: (key: string) => void;
38
+ has: (key: string) => boolean;
39
+ }
40
+ export type BaseSchema = ZodType;
41
+ export type SchemaContext = {
42
+ image: ImageFunction;
43
+ };
44
+ type ContentLayerConfig<S extends BaseSchema, TData extends {
45
+ id: string;
46
+ } = {
47
+ id: string;
48
+ }> = {
49
+ type?: 'content_layer';
50
+ schema?: S | ((context: SchemaContext) => S);
51
+ loader: Loader | (() => Array<TData> | Promise<Array<TData>> | Record<string, Omit<TData, 'id'> & {
52
+ id?: string;
53
+ }> | Promise<Record<string, Omit<TData, 'id'> & {
54
+ id?: string;
55
+ }>>);
56
+ };
57
+ type DataCollectionConfig<S extends BaseSchema> = {
58
+ type: 'data';
59
+ schema?: S | ((context: SchemaContext) => S);
60
+ };
61
+ type ContentCollectionConfig<S extends BaseSchema> = {
62
+ type?: 'content';
63
+ schema?: S | ((context: SchemaContext) => S);
64
+ loader?: never;
65
+ };
66
+ export type LiveCollectionConfig<L extends LiveLoader, S extends BaseSchema | undefined = undefined> = {
67
+ type: 'live';
68
+ schema?: S;
69
+ loader: L;
70
+ };
71
+ export type CollectionConfig<S extends BaseSchema> = ContentCollectionConfig<S> | DataCollectionConfig<S> | ContentLayerConfig<S>;
72
+ export declare function defineLiveCollection<L extends LiveLoader, S extends BaseSchema | undefined = undefined>(config: LiveCollectionConfig<L, S>): LiveCollectionConfig<L, S>;
73
+ export declare function defineCollection<S extends BaseSchema>(config: CollectionConfig<S>): CollectionConfig<S>;
74
+ export {};
@@ -0,0 +1,78 @@
1
+ import { AstroError, AstroErrorData, AstroUserError } from "../core/errors/index.js";
2
+ import { CONTENT_LAYER_TYPE, LIVE_CONTENT_TYPE } from "./consts.js";
3
+ function getImporterFilename() {
4
+ const stackLine = new Error().stack?.split("\n").find(
5
+ (line) => !line.includes("defineCollection") && !line.includes("defineLiveCollection") && !line.includes("getImporterFilename") && line !== "Error"
6
+ );
7
+ if (!stackLine) {
8
+ return void 0;
9
+ }
10
+ const match = /\/((?:src|chunks)\/.*?):\d+:\d+/.exec(stackLine);
11
+ return match?.[1] ?? void 0;
12
+ }
13
+ function defineLiveCollection(config) {
14
+ const importerFilename = getImporterFilename();
15
+ if (!importerFilename?.includes("live.config")) {
16
+ throw new AstroError({
17
+ ...AstroErrorData.LiveContentConfigError,
18
+ message: AstroErrorData.LiveContentConfigError.message(
19
+ "Live collections must be defined in a `src/live.config.ts` file.",
20
+ importerFilename ?? "your content config file"
21
+ )
22
+ });
23
+ }
24
+ if (config.type !== LIVE_CONTENT_TYPE) {
25
+ throw new AstroError({
26
+ ...AstroErrorData.LiveContentConfigError,
27
+ message: AstroErrorData.LiveContentConfigError.message(
28
+ "Collections in a live config file must have a type of `live`.",
29
+ importerFilename
30
+ )
31
+ });
32
+ }
33
+ if (!config.loader) {
34
+ throw new AstroError({
35
+ ...AstroErrorData.LiveContentConfigError,
36
+ message: AstroErrorData.LiveContentConfigError.message(
37
+ "Live collections must have a `loader` defined.",
38
+ importerFilename
39
+ )
40
+ });
41
+ }
42
+ if (typeof config.schema === "function") {
43
+ throw new AstroError({
44
+ ...AstroErrorData.LiveContentConfigError,
45
+ message: AstroErrorData.LiveContentConfigError.message(
46
+ "The schema cannot be a function for live collections. Please use a schema object instead.",
47
+ importerFilename
48
+ )
49
+ });
50
+ }
51
+ return config;
52
+ }
53
+ function defineCollection(config) {
54
+ const importerFilename = getImporterFilename();
55
+ if (importerFilename?.includes("live.config")) {
56
+ throw new AstroError({
57
+ ...AstroErrorData.LiveContentConfigError,
58
+ message: AstroErrorData.LiveContentConfigError.message(
59
+ "Collections in a live config file must use `defineLiveCollection`.",
60
+ importerFilename
61
+ )
62
+ });
63
+ }
64
+ if ("loader" in config) {
65
+ if (config.type && config.type !== CONTENT_LAYER_TYPE) {
66
+ throw new AstroUserError(
67
+ `Collections that use the Content Layer API must have a \`loader\` defined and no \`type\` set. Check your collection definitions in ${importerFilename ?? "your content config file"}.`
68
+ );
69
+ }
70
+ config.type = CONTENT_LAYER_TYPE;
71
+ }
72
+ if (!config.type) config.type = "content";
73
+ return config;
74
+ }
75
+ export {
76
+ defineCollection,
77
+ defineLiveCollection
78
+ };
@@ -24,3 +24,4 @@ export declare const MODULES_IMPORTS_FILE = "content-modules.mjs";
24
24
  export declare const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
25
25
  export declare const COLLECTIONS_DIR = "collections/";
26
26
  export declare const CONTENT_LAYER_TYPE = "content_layer";
27
+ export declare const LIVE_CONTENT_TYPE = "live";
@@ -31,6 +31,7 @@ const MODULES_IMPORTS_FILE = "content-modules.mjs";
31
31
  const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
32
32
  const COLLECTIONS_DIR = "collections/";
33
33
  const CONTENT_LAYER_TYPE = "content_layer";
34
+ const LIVE_CONTENT_TYPE = "live";
34
35
  export {
35
36
  ASSET_IMPORTS_FILE,
36
37
  ASSET_IMPORTS_RESOLVED_STUB_ID,
@@ -50,6 +51,7 @@ export {
50
51
  DEFERRED_MODULE,
51
52
  IMAGE_IMPORT_PREFIX,
52
53
  LINKS_PLACEHOLDER,
54
+ LIVE_CONTENT_TYPE,
53
55
  MODULES_IMPORTS_FILE,
54
56
  MODULES_MJS_ID,
55
57
  MODULES_MJS_VIRTUAL_ID,
@@ -164,7 +164,7 @@ ${contentConfig.error.message}`);
164
164
  logger.info("Content config changed");
165
165
  shouldClear = true;
166
166
  }
167
- if (previousAstroVersion && previousAstroVersion !== "5.9.3") {
167
+ if (previousAstroVersion && previousAstroVersion !== "5.10.0") {
168
168
  logger.info("Astro version changed");
169
169
  shouldClear = true;
170
170
  }
@@ -172,8 +172,8 @@ ${contentConfig.error.message}`);
172
172
  logger.info("Clearing content store");
173
173
  this.#store.clearAll();
174
174
  }
175
- if ("5.9.3") {
176
- await this.#store.metaStore().set("astro-version", "5.9.3");
175
+ if ("5.10.0") {
176
+ await this.#store.metaStore().set("astro-version", "5.10.0");
177
177
  }
178
178
  if (currentConfigDigest) {
179
179
  await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -0,0 +1,20 @@
1
+ import type { ZodError } from 'zod';
2
+ export declare class LiveCollectionError extends Error {
3
+ readonly collection: string;
4
+ readonly message: string;
5
+ readonly cause?: Error | undefined;
6
+ constructor(collection: string, message: string, cause?: Error | undefined);
7
+ static is(error: unknown): error is LiveCollectionError;
8
+ }
9
+ export declare class LiveEntryNotFoundError extends LiveCollectionError {
10
+ constructor(collection: string, entryFilter: string | Record<string, unknown>);
11
+ static is(error: unknown): error is LiveEntryNotFoundError;
12
+ }
13
+ export declare class LiveCollectionValidationError extends LiveCollectionError {
14
+ constructor(collection: string, entryId: string, error: ZodError);
15
+ static is(error: unknown): error is LiveCollectionValidationError;
16
+ }
17
+ export declare class LiveCollectionCacheHintError extends LiveCollectionError {
18
+ constructor(collection: string, entryId: string | undefined, error: ZodError);
19
+ static is(error: unknown): error is LiveCollectionCacheHintError;
20
+ }
@@ -0,0 +1,64 @@
1
+ class LiveCollectionError extends Error {
2
+ constructor(collection, message, cause) {
3
+ super(message);
4
+ this.collection = collection;
5
+ this.message = message;
6
+ this.cause = cause;
7
+ this.name = "LiveCollectionError";
8
+ }
9
+ static is(error) {
10
+ return error instanceof LiveCollectionError;
11
+ }
12
+ }
13
+ class LiveEntryNotFoundError extends LiveCollectionError {
14
+ constructor(collection, entryFilter) {
15
+ super(
16
+ collection,
17
+ `Entry ${collection} \u2192 ${typeof entryFilter === "string" ? entryFilter : JSON.stringify(entryFilter)} was not found.`
18
+ );
19
+ this.name = "LiveEntryNotFoundError";
20
+ }
21
+ static is(error) {
22
+ return error?.name === "LiveEntryNotFoundError";
23
+ }
24
+ }
25
+ class LiveCollectionValidationError extends LiveCollectionError {
26
+ constructor(collection, entryId, error) {
27
+ super(
28
+ collection,
29
+ [
30
+ `**${collection} \u2192 ${entryId}** data does not match the collection schema.
31
+ `,
32
+ ...error.errors.map((zodError) => ` **${zodError.path.join(".")}**: ${zodError.message}`),
33
+ ""
34
+ ].join("\n")
35
+ );
36
+ this.name = "LiveCollectionValidationError";
37
+ }
38
+ static is(error) {
39
+ return error?.name === "LiveCollectionValidationError";
40
+ }
41
+ }
42
+ class LiveCollectionCacheHintError extends LiveCollectionError {
43
+ constructor(collection, entryId, error) {
44
+ super(
45
+ collection,
46
+ [
47
+ `**${String(collection)}${entryId ? ` \u2192 ${String(entryId)}` : ""}** returned an invalid cache hint.
48
+ `,
49
+ ...error.errors.map((zodError) => ` **${zodError.path.join(".")}**: ${zodError.message}`),
50
+ ""
51
+ ].join("\n")
52
+ );
53
+ this.name = "LiveCollectionCacheHintError";
54
+ }
55
+ static is(error) {
56
+ return error?.name === "LiveCollectionCacheHintError";
57
+ }
58
+ }
59
+ export {
60
+ LiveCollectionCacheHintError,
61
+ LiveCollectionError,
62
+ LiveCollectionValidationError,
63
+ LiveEntryNotFoundError
64
+ };
@@ -2,6 +2,7 @@ import type { FSWatcher } from 'vite';
2
2
  import type { ZodSchema } from 'zod';
3
3
  import type { AstroIntegrationLogger } from '../../core/logger/core.js';
4
4
  import type { AstroConfig } from '../../types/public/config.js';
5
+ import type { LiveDataCollection, LiveDataEntry } from '../../types/public/content.js';
5
6
  import type { RenderedContent } from '../data-store.js';
6
7
  import type { DataStore, MetaStore } from '../mutable-data-store.js';
7
8
  export type { DataStore, MetaStore };
@@ -42,3 +43,23 @@ export interface Loader {
42
43
  /** Optionally, define the schema of the data. Will be overridden by user-defined schema */
43
44
  schema?: ZodSchema | Promise<ZodSchema> | (() => ZodSchema | Promise<ZodSchema>);
44
45
  }
46
+ export interface LoadEntryContext<TEntryFilter = never> {
47
+ filter: TEntryFilter extends never ? {
48
+ id: string;
49
+ } : TEntryFilter;
50
+ }
51
+ export interface LoadCollectionContext<TCollectionFilter = unknown> {
52
+ filter?: TCollectionFilter;
53
+ }
54
+ export interface LiveLoader<TData extends Record<string, any> = Record<string, unknown>, TEntryFilter extends Record<string, any> | never = never, TCollectionFilter extends Record<string, any> | never = never, TError extends Error = Error> {
55
+ /** Unique name of the loader, e.g. the npm package name */
56
+ name: string;
57
+ /** Load a single entry */
58
+ loadEntry: (context: LoadEntryContext<TEntryFilter>) => Promise<LiveDataEntry<TData> | undefined | {
59
+ error: TError;
60
+ }>;
61
+ /** Load a collection of entries */
62
+ loadCollection: (context: LoadCollectionContext<TCollectionFilter>) => Promise<LiveDataCollection<TData> | {
63
+ error: TError;
64
+ }>;
65
+ }
@@ -1,24 +1,33 @@
1
1
  import type { MarkdownHeading } from '@astrojs/markdown-remark';
2
2
  import { z } from 'zod';
3
3
  import { type AstroComponentFactory } from '../runtime/server/index.js';
4
+ import type { LiveDataCollectionResult, LiveDataEntryResult } from '../types/public/content.js';
5
+ import { type LIVE_CONTENT_TYPE } from './consts.js';
4
6
  import { type DataEntry } from './data-store.js';
7
+ import { LiveCollectionCacheHintError, LiveCollectionError, LiveCollectionValidationError, LiveEntryNotFoundError } from './loaders/errors.js';
8
+ import type { LiveLoader } from './loaders/types.js';
5
9
  import type { ContentLookupMap } from './utils.js';
10
+ export { LiveCollectionError, LiveCollectionCacheHintError, LiveEntryNotFoundError, LiveCollectionValidationError, };
6
11
  type LazyImport = () => Promise<any>;
7
12
  type GlobResult = Record<string, LazyImport>;
8
13
  type CollectionToEntryMap = Record<string, GlobResult>;
9
14
  type GetEntryImport = (collection: string, lookupId: string) => Promise<LazyImport>;
10
- export declare function getImporterFilename(): string | null;
11
- export declare function defineCollection(config: any): any;
15
+ type LiveCollectionConfigMap = Record<string, {
16
+ loader: LiveLoader;
17
+ type: typeof LIVE_CONTENT_TYPE;
18
+ schema?: z.ZodType;
19
+ }>;
12
20
  export declare function createCollectionToGlobResultMap({ globResult, contentDir, }: {
13
21
  globResult: GlobResult;
14
22
  contentDir: string;
15
23
  }): CollectionToEntryMap;
16
- export declare function createGetCollection({ contentCollectionToEntryMap, dataCollectionToEntryMap, getRenderEntryImport, cacheEntriesByCollection, }: {
24
+ export declare function createGetCollection({ contentCollectionToEntryMap, dataCollectionToEntryMap, getRenderEntryImport, cacheEntriesByCollection, liveCollections, }: {
17
25
  contentCollectionToEntryMap: CollectionToEntryMap;
18
26
  dataCollectionToEntryMap: CollectionToEntryMap;
19
27
  getRenderEntryImport: GetEntryImport;
20
28
  cacheEntriesByCollection: Map<string, any[]>;
21
- }): (collection: string, filter?: (entry: any) => unknown) => Promise<any[]>;
29
+ liveCollections: LiveCollectionConfigMap;
30
+ }): (collection: string, filter?: ((entry: any) => unknown) | Record<string, unknown>) => Promise<any[]>;
22
31
  export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImport, collectionNames, getEntry, }: {
23
32
  getEntryImport: GetEntryImport;
24
33
  getRenderEntryImport: GetEntryImport;
@@ -61,11 +70,12 @@ type EntryLookupObject = {
61
70
  collection: string;
62
71
  slug: string;
63
72
  };
64
- export declare function createGetEntry({ getEntryImport, getRenderEntryImport, collectionNames, }: {
73
+ export declare function createGetEntry({ getEntryImport, getRenderEntryImport, collectionNames, liveCollections, }: {
65
74
  getEntryImport: GetEntryImport;
66
75
  getRenderEntryImport: GetEntryImport;
67
76
  collectionNames: Set<string>;
68
- }): (collectionOrLookupObject: string | EntryLookupObject, _lookupId?: string) => Promise<ContentEntryResult | DataEntryResult | undefined>;
77
+ liveCollections: LiveCollectionConfigMap;
78
+ }): (collectionOrLookupObject: string | EntryLookupObject, lookup?: string | Record<string, unknown>) => Promise<ContentEntryResult | DataEntryResult | undefined>;
69
79
  export declare function createGetEntries(getEntry: ReturnType<typeof createGetEntry>): (entries: {
70
80
  collection: string;
71
81
  id: string;
@@ -73,6 +83,12 @@ export declare function createGetEntries(getEntry: ReturnType<typeof createGetEn
73
83
  collection: string;
74
84
  slug: string;
75
85
  }[]) => Promise<(ContentEntryResult | DataEntryResult | undefined)[]>;
86
+ export declare function createGetLiveCollection({ liveCollections, }: {
87
+ liveCollections: LiveCollectionConfigMap;
88
+ }): (collection: string, filter?: Record<string, unknown>) => Promise<LiveDataCollectionResult>;
89
+ export declare function createGetLiveEntry({ liveCollections, }: {
90
+ liveCollections: LiveCollectionConfigMap;
91
+ }): (collection: string, lookup: string | Record<string, unknown>) => Promise<LiveDataEntryResult>;
76
92
  type RenderResult = {
77
93
  Content: AstroComponentFactory;
78
94
  headings: MarkdownHeading[];
@@ -122,4 +138,4 @@ export declare function createReference({ lookupMap }: {
122
138
  slug: string;
123
139
  collection: string;
124
140
  }>;
125
- export {};
141
+ export declare function defineCollection(config: any): import("./config.js").CollectionConfig<import("./config.js").BaseSchema>;