@ai-react-markdown/core 1.4.2 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { JSX, ComponentType, PropsWithChildren, CSSProperties, FC } from 'react';
3
3
  import { Element, ElementContent } from 'hast';
4
+ import { defaultSchema } from 'rehype-sanitize';
4
5
 
5
6
  /**
6
7
  * Public types for the local Markdown wrapper. Ported 1:1 from react-markdown
@@ -17,6 +18,24 @@ interface ExtraProps {
17
18
  type Components = {
18
19
  [Key in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[Key] & ExtraProps> | keyof JSX.IntrinsicElements;
19
20
  };
21
+ /** Transform every URL on every element attribute. Return null/empty to strip. */
22
+ type UrlTransform = (url: string, key: string, node: Readonly<Element>) => string | null | undefined;
23
+
24
+ /**
25
+ * Default URL transform — same allowlist as react-markdown / GitHub.
26
+ *
27
+ * @module components/markdown/urlTransform
28
+ */
29
+
30
+ /**
31
+ * Make a URL safe.
32
+ *
33
+ * Allows `http`, `https`, `irc`, `ircs`, `mailto`, and `xmpp` protocols, plus
34
+ * URLs relative to the current protocol (e.g. `/foo`). Other protocols are
35
+ * stripped to the empty string. Mirrors GitHub's behaviour and matches
36
+ * `micromark-util-sanitize-uri` minus the URL-encoding pass.
37
+ */
38
+ declare const defaultUrlTransform: UrlTransform;
20
39
 
21
40
  /**
22
41
  * Core type definitions, enums, and default configuration for ai-react-markdown.
@@ -722,6 +741,85 @@ interface AIMarkdownMetadataProviderProps<TMetadata extends AIMarkdownMetadata =
722
741
  */
723
742
  type AIMDContentPreprocessor = (content: string) => string;
724
743
 
744
+ /**
745
+ * Builds a `rehype-sanitize` schema by handing the caller a deep clone of the
746
+ * library default ({@link sanitizeSchema}) to mutate or replace.
747
+ *
748
+ * The mutate-and-return pattern matches the ergonomics of Next.js's
749
+ * `webpack(config)` and Express middleware: a callback receives a draft,
750
+ * either modifies it in place (returning nothing) or returns a fresh object
751
+ * to replace it. The library guarantees the draft is a deep clone, so direct
752
+ * mutation never leaks into the shared default singleton.
753
+ *
754
+ * @module components/extendSanitizeSchema
755
+ */
756
+
757
+ /**
758
+ * The full `rehype-sanitize` schema type. Re-exported as the canonical
759
+ * library-internal alias so other modules don't each redeclare
760
+ * `typeof defaultSchema`.
761
+ *
762
+ * The shape is owned by `rehype-sanitize`; consumers should treat it as
763
+ * tracking that upstream type — it may evolve across rehype-sanitize major
764
+ * versions.
765
+ */
766
+ type SanitizeSchema = typeof defaultSchema;
767
+ /**
768
+ * Build a sanitize schema by mutating (or replacing) a deep clone of the
769
+ * library default.
770
+ *
771
+ * Designed to be called ONCE at module scope so the returned object has a
772
+ * stable identity across renders — passing it directly into
773
+ * `<AIMarkdown sanitizeSchema={…}>` keeps the block-memo cache warm.
774
+ *
775
+ * @example Append a custom URL protocol via mutation:
776
+ * ```ts
777
+ * const SCHEMA = extendSanitizeSchema((s) => {
778
+ * s.protocols!.href!.push('myapp');
779
+ * s.protocols!.src!.push('myapp');
780
+ * });
781
+ *
782
+ * function App() {
783
+ * return <AIMarkdown content={…} sanitizeSchema={SCHEMA} />;
784
+ * }
785
+ * ```
786
+ *
787
+ * @example Return-style replacement for wider edits:
788
+ * ```ts
789
+ * const SCHEMA = extendSanitizeSchema((s) => ({
790
+ * ...s,
791
+ * tagNames: [...(s.tagNames ?? []), 'my-widget'],
792
+ * }));
793
+ * ```
794
+ *
795
+ * @remarks Allowing a protocol on `protocols.href` lets the URL through the
796
+ * SECOND sanitize gate. The FIRST gate (`urlTransform`) must permit the
797
+ * same protocol independently — see the `urlTransform` prop on
798
+ * `<AIMarkdown>` and the exported {@link defaultUrlTransform} for
799
+ * composition. Keep the two protocol lists in sync.
800
+ *
801
+ * ### Footguns
802
+ *
803
+ * - **Reassigning the local parameter** (`(s) => { s = { …new schema… }; }`)
804
+ * does NOT replace the draft — JS only rebinds the local variable. Either
805
+ * mutate the original draft or `return` the new object explicitly.
806
+ * - **Returning `null`** is treated the same as returning nothing (the
807
+ * modified draft is used). The TypeScript signature does not permit
808
+ * `null`, but JS callers or `as`-casted code paths could silently hit
809
+ * this. Prefer `return` with no value, or an explicit `return draft;`.
810
+ * - **Throwing inside the modifier** propagates to the call site
811
+ * uncaught — there is no try/catch. Callers usually invoke this once at
812
+ * module load, where a thrown error surfaces as a startup-time crash and
813
+ * is the correct failure mode.
814
+ *
815
+ * @param modifier - Receives a deep clone of the library default. Mutate it
816
+ * freely; either return the (possibly different) result, or return
817
+ * nothing to use the mutated draft. Returning `undefined` (and, by
818
+ * convention, `null`) is treated the same as a mutate-only call.
819
+ * @returns A new `Schema` object — never the library default singleton.
820
+ */
821
+ declare function extendSanitizeSchema(modifier: (draft: SanitizeSchema) => SanitizeSchema | void): SanitizeSchema;
822
+
725
823
  /**
726
824
  * Hook for referential stability of deep-equal values.
727
825
  *
@@ -755,6 +853,23 @@ type AIMDContentPreprocessor = (content: string) => string;
755
853
  */
756
854
  declare function useStableValue<T>(value: T): T;
757
855
 
856
+ /**
857
+ * Builds the `rehype-sanitize` schema used by {@link MarkdownContent}.
858
+ *
859
+ * Extracted into its own module so the merge logic can be unit-tested in
860
+ * isolation without pulling in React or the full markdown pipeline.
861
+ *
862
+ * @module components/sanitizeSchema
863
+ */
864
+
865
+ type Schema = typeof defaultSchema;
866
+ /**
867
+ * The full sanitize schema used by the markdown renderer: extends
868
+ * `defaultSchema` to allow `<mark>`, the KaTeX math class names, and the
869
+ * three custom hast tags emitted by cross-chunk coordination handlers.
870
+ */
871
+ declare const sanitizeSchema: Schema;
872
+
758
873
  /**
759
874
  * Cross-chunk shared state. Holds per-chunk contributions (refs, defs,
760
875
  * linkDefs) keyed by Symbol identity allocated via useId reactId, with
@@ -957,12 +1072,96 @@ interface AIMarkdownProps<TConfig extends AIMarkdownRenderConfig = AIMarkdownRen
957
1072
  * characters like `:`, `/`, or spaces.
958
1073
  */
959
1074
  documentId?: string;
1075
+ /**
1076
+ * Override the function that decides which URL protocols are allowed
1077
+ * through the FIRST sanitization gate. The default allowlist mirrors
1078
+ * `react-markdown` / GitHub: `http`, `https`, `irc`, `ircs`, `mailto`,
1079
+ * `xmpp`. Anything else is rewritten to `''`.
1080
+ *
1081
+ * **Recommended pattern**: compose with the exported
1082
+ * {@link defaultUrlTransform} so the built-in XSS protections survive,
1083
+ * and define the result at module scope so its identity is stable across
1084
+ * renders:
1085
+ *
1086
+ * ```ts
1087
+ * import AIMarkdown, { defaultUrlTransform } from '@ai-react-markdown/core';
1088
+ *
1089
+ * const ALLOWED = /^(myapp|tel):/i;
1090
+ * const URL_TRANSFORM = (url, key, node) =>
1091
+ * ALLOWED.test(url) ? url : defaultUrlTransform(url, key, node);
1092
+ *
1093
+ * function App() {
1094
+ * return <AIMarkdown urlTransform={URL_TRANSFORM} ... />;
1095
+ * }
1096
+ * ```
1097
+ *
1098
+ * **Regex-escaping**: scheme names per RFC 3986 may contain `+`, `-`, and
1099
+ * `.` (e.g. `web+app`, `coap+tcp`). All three are regex metacharacters,
1100
+ * so write `/^web\+app:/i` rather than `/^web+app:/i`. The latter would
1101
+ * match URLs starting with `we`, `wee`, `weee`, … and silently broaden
1102
+ * the allowlist.
1103
+ *
1104
+ * **Reference stability matters.** The block-memo cache treats this prop
1105
+ * as a dependency. Defining the function inline (`urlTransform={(url) =>
1106
+ * …}`) creates a new closure on every parent render, discards the cache
1107
+ * for the entire markdown document on each render, and effectively
1108
+ * disables Phase 5 memoization. In development the library will
1109
+ * `console.warn` if it detects this pattern.
1110
+ *
1111
+ * Allowing a protocol here is necessary but **not sufficient** to render
1112
+ * a link — the second gate (`rehype-sanitize`) also enforces its own
1113
+ * protocol allowlist. See the {@link sanitizeSchema} prop and the
1114
+ * matching {@link extendSanitizeSchema} helper for the second gate.
1115
+ *
1116
+ * **API stability**: the `UrlTransform` type tracks the upstream
1117
+ * `react-markdown` shape and may change with its major versions.
1118
+ */
1119
+ urlTransform?: UrlTransform | null;
1120
+ /**
1121
+ * Override the `rehype-sanitize` schema applied to the rendered output.
1122
+ * The library default ({@link sanitizeSchema}) extends `rehype-sanitize`'s
1123
+ * own `defaultSchema` with the `<mark>` tag, KaTeX class names, and the
1124
+ * cross-chunk coordination tags (`cross-chunk-link`, `cross-chunk-image`,
1125
+ * `footnote-sup`).
1126
+ *
1127
+ * **Recommended pattern**: build the schema with {@link extendSanitizeSchema}
1128
+ * (mutate-and-return form) so those library additions stay intact, and
1129
+ * define the result at module scope:
1130
+ *
1131
+ * ```ts
1132
+ * import AIMarkdown, { extendSanitizeSchema } from '@ai-react-markdown/core';
1133
+ *
1134
+ * const SCHEMA = extendSanitizeSchema((s) => {
1135
+ * s.protocols.href.push('myapp');
1136
+ * s.protocols.src.push('myapp');
1137
+ * });
1138
+ *
1139
+ * function App() {
1140
+ * return <AIMarkdown sanitizeSchema={SCHEMA} ... />;
1141
+ * }
1142
+ * ```
1143
+ *
1144
+ * **Footgun**: hand-rolling a schema (e.g. spreading from
1145
+ * `rehype-sanitize`'s `defaultSchema` directly) silently drops the
1146
+ * cross-chunk tag allowlist — coordinated multi-chunk rendering will then
1147
+ * lose its placeholders. Prefer the helper unless you have a specific
1148
+ * reason to opt out.
1149
+ *
1150
+ * **Reference stability matters.** Inline `<AIMarkdown sanitizeSchema={{
1151
+ * ...sanitizeSchema, protocols: {...} }}>` is mitigated by an internal
1152
+ * `useStableValue` deep-equal pass, but the safer pattern is still
1153
+ * module-scope. Development builds will `console.warn` on identity flips.
1154
+ *
1155
+ * **API stability**: the `SanitizeSchema` type tracks the upstream
1156
+ * `rehype-sanitize` shape and may change with its major versions.
1157
+ */
1158
+ sanitizeSchema?: SanitizeSchema;
960
1159
  }
961
1160
  /**
962
1161
  * Root component that preprocesses markdown content and renders it through
963
1162
  * a configurable remark/rehype pipeline wrapped in typography and style layers.
964
1163
  */
965
- declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, documentId, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
1164
+ declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, documentId, urlTransform, sanitizeSchema, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
966
1165
  declare const _default: typeof AIMarkdownComponent;
967
1166
 
968
- export { type AIMDContentPreprocessor, type AIMarkdownColorScheme, type AIMarkdownCustomComponents, AIMarkdownDocuments, type AIMarkdownDocumentsProps, type AIMarkdownExtraStylesComponent, type AIMarkdownExtraStylesProps, type AIMarkdownMetadata, type AIMarkdownProps, type AIMarkdownRenderConfig, AIMarkdownRenderDisplayOptimizeAbility, AIMarkdownRenderExtraSyntax, type AIMarkdownRenderState, type AIMarkdownTypographyComponent, type AIMarkdownTypographyProps, type AIMarkdownVariant, type ChunkData, type FootnoteDef, type LinkDef, type PartialDeep, type RefKind, type RefRecord, type Registry, _default as default, defaultAIMarkdownRenderConfig, useAIMarkdownMetadata, useAIMarkdownRenderState, useDocumentRegistry, useStableValue };
1167
+ export { type AIMDContentPreprocessor, type AIMarkdownColorScheme, type AIMarkdownCustomComponents, AIMarkdownDocuments, type AIMarkdownDocumentsProps, type AIMarkdownExtraStylesComponent, type AIMarkdownExtraStylesProps, type AIMarkdownMetadata, type AIMarkdownProps, type AIMarkdownRenderConfig, AIMarkdownRenderDisplayOptimizeAbility, AIMarkdownRenderExtraSyntax, type AIMarkdownRenderState, type AIMarkdownTypographyComponent, type AIMarkdownTypographyProps, type AIMarkdownVariant, type ChunkData, type FootnoteDef, type LinkDef, type PartialDeep, type RefKind, type RefRecord, type Registry, type SanitizeSchema, type UrlTransform, _default as default, defaultAIMarkdownRenderConfig, defaultUrlTransform, extendSanitizeSchema, sanitizeSchema, useAIMarkdownMetadata, useAIMarkdownRenderState, useDocumentRegistry, useStableValue };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { JSX, ComponentType, PropsWithChildren, CSSProperties, FC } from 'react';
3
3
  import { Element, ElementContent } from 'hast';
4
+ import { defaultSchema } from 'rehype-sanitize';
4
5
 
5
6
  /**
6
7
  * Public types for the local Markdown wrapper. Ported 1:1 from react-markdown
@@ -17,6 +18,24 @@ interface ExtraProps {
17
18
  type Components = {
18
19
  [Key in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[Key] & ExtraProps> | keyof JSX.IntrinsicElements;
19
20
  };
21
+ /** Transform every URL on every element attribute. Return null/empty to strip. */
22
+ type UrlTransform = (url: string, key: string, node: Readonly<Element>) => string | null | undefined;
23
+
24
+ /**
25
+ * Default URL transform — same allowlist as react-markdown / GitHub.
26
+ *
27
+ * @module components/markdown/urlTransform
28
+ */
29
+
30
+ /**
31
+ * Make a URL safe.
32
+ *
33
+ * Allows `http`, `https`, `irc`, `ircs`, `mailto`, and `xmpp` protocols, plus
34
+ * URLs relative to the current protocol (e.g. `/foo`). Other protocols are
35
+ * stripped to the empty string. Mirrors GitHub's behaviour and matches
36
+ * `micromark-util-sanitize-uri` minus the URL-encoding pass.
37
+ */
38
+ declare const defaultUrlTransform: UrlTransform;
20
39
 
21
40
  /**
22
41
  * Core type definitions, enums, and default configuration for ai-react-markdown.
@@ -722,6 +741,85 @@ interface AIMarkdownMetadataProviderProps<TMetadata extends AIMarkdownMetadata =
722
741
  */
723
742
  type AIMDContentPreprocessor = (content: string) => string;
724
743
 
744
+ /**
745
+ * Builds a `rehype-sanitize` schema by handing the caller a deep clone of the
746
+ * library default ({@link sanitizeSchema}) to mutate or replace.
747
+ *
748
+ * The mutate-and-return pattern matches the ergonomics of Next.js's
749
+ * `webpack(config)` and Express middleware: a callback receives a draft,
750
+ * either modifies it in place (returning nothing) or returns a fresh object
751
+ * to replace it. The library guarantees the draft is a deep clone, so direct
752
+ * mutation never leaks into the shared default singleton.
753
+ *
754
+ * @module components/extendSanitizeSchema
755
+ */
756
+
757
+ /**
758
+ * The full `rehype-sanitize` schema type. Re-exported as the canonical
759
+ * library-internal alias so other modules don't each redeclare
760
+ * `typeof defaultSchema`.
761
+ *
762
+ * The shape is owned by `rehype-sanitize`; consumers should treat it as
763
+ * tracking that upstream type — it may evolve across rehype-sanitize major
764
+ * versions.
765
+ */
766
+ type SanitizeSchema = typeof defaultSchema;
767
+ /**
768
+ * Build a sanitize schema by mutating (or replacing) a deep clone of the
769
+ * library default.
770
+ *
771
+ * Designed to be called ONCE at module scope so the returned object has a
772
+ * stable identity across renders — passing it directly into
773
+ * `<AIMarkdown sanitizeSchema={…}>` keeps the block-memo cache warm.
774
+ *
775
+ * @example Append a custom URL protocol via mutation:
776
+ * ```ts
777
+ * const SCHEMA = extendSanitizeSchema((s) => {
778
+ * s.protocols!.href!.push('myapp');
779
+ * s.protocols!.src!.push('myapp');
780
+ * });
781
+ *
782
+ * function App() {
783
+ * return <AIMarkdown content={…} sanitizeSchema={SCHEMA} />;
784
+ * }
785
+ * ```
786
+ *
787
+ * @example Return-style replacement for wider edits:
788
+ * ```ts
789
+ * const SCHEMA = extendSanitizeSchema((s) => ({
790
+ * ...s,
791
+ * tagNames: [...(s.tagNames ?? []), 'my-widget'],
792
+ * }));
793
+ * ```
794
+ *
795
+ * @remarks Allowing a protocol on `protocols.href` lets the URL through the
796
+ * SECOND sanitize gate. The FIRST gate (`urlTransform`) must permit the
797
+ * same protocol independently — see the `urlTransform` prop on
798
+ * `<AIMarkdown>` and the exported {@link defaultUrlTransform} for
799
+ * composition. Keep the two protocol lists in sync.
800
+ *
801
+ * ### Footguns
802
+ *
803
+ * - **Reassigning the local parameter** (`(s) => { s = { …new schema… }; }`)
804
+ * does NOT replace the draft — JS only rebinds the local variable. Either
805
+ * mutate the original draft or `return` the new object explicitly.
806
+ * - **Returning `null`** is treated the same as returning nothing (the
807
+ * modified draft is used). The TypeScript signature does not permit
808
+ * `null`, but JS callers or `as`-casted code paths could silently hit
809
+ * this. Prefer `return` with no value, or an explicit `return draft;`.
810
+ * - **Throwing inside the modifier** propagates to the call site
811
+ * uncaught — there is no try/catch. Callers usually invoke this once at
812
+ * module load, where a thrown error surfaces as a startup-time crash and
813
+ * is the correct failure mode.
814
+ *
815
+ * @param modifier - Receives a deep clone of the library default. Mutate it
816
+ * freely; either return the (possibly different) result, or return
817
+ * nothing to use the mutated draft. Returning `undefined` (and, by
818
+ * convention, `null`) is treated the same as a mutate-only call.
819
+ * @returns A new `Schema` object — never the library default singleton.
820
+ */
821
+ declare function extendSanitizeSchema(modifier: (draft: SanitizeSchema) => SanitizeSchema | void): SanitizeSchema;
822
+
725
823
  /**
726
824
  * Hook for referential stability of deep-equal values.
727
825
  *
@@ -755,6 +853,23 @@ type AIMDContentPreprocessor = (content: string) => string;
755
853
  */
756
854
  declare function useStableValue<T>(value: T): T;
757
855
 
856
+ /**
857
+ * Builds the `rehype-sanitize` schema used by {@link MarkdownContent}.
858
+ *
859
+ * Extracted into its own module so the merge logic can be unit-tested in
860
+ * isolation without pulling in React or the full markdown pipeline.
861
+ *
862
+ * @module components/sanitizeSchema
863
+ */
864
+
865
+ type Schema = typeof defaultSchema;
866
+ /**
867
+ * The full sanitize schema used by the markdown renderer: extends
868
+ * `defaultSchema` to allow `<mark>`, the KaTeX math class names, and the
869
+ * three custom hast tags emitted by cross-chunk coordination handlers.
870
+ */
871
+ declare const sanitizeSchema: Schema;
872
+
758
873
  /**
759
874
  * Cross-chunk shared state. Holds per-chunk contributions (refs, defs,
760
875
  * linkDefs) keyed by Symbol identity allocated via useId reactId, with
@@ -957,12 +1072,96 @@ interface AIMarkdownProps<TConfig extends AIMarkdownRenderConfig = AIMarkdownRen
957
1072
  * characters like `:`, `/`, or spaces.
958
1073
  */
959
1074
  documentId?: string;
1075
+ /**
1076
+ * Override the function that decides which URL protocols are allowed
1077
+ * through the FIRST sanitization gate. The default allowlist mirrors
1078
+ * `react-markdown` / GitHub: `http`, `https`, `irc`, `ircs`, `mailto`,
1079
+ * `xmpp`. Anything else is rewritten to `''`.
1080
+ *
1081
+ * **Recommended pattern**: compose with the exported
1082
+ * {@link defaultUrlTransform} so the built-in XSS protections survive,
1083
+ * and define the result at module scope so its identity is stable across
1084
+ * renders:
1085
+ *
1086
+ * ```ts
1087
+ * import AIMarkdown, { defaultUrlTransform } from '@ai-react-markdown/core';
1088
+ *
1089
+ * const ALLOWED = /^(myapp|tel):/i;
1090
+ * const URL_TRANSFORM = (url, key, node) =>
1091
+ * ALLOWED.test(url) ? url : defaultUrlTransform(url, key, node);
1092
+ *
1093
+ * function App() {
1094
+ * return <AIMarkdown urlTransform={URL_TRANSFORM} ... />;
1095
+ * }
1096
+ * ```
1097
+ *
1098
+ * **Regex-escaping**: scheme names per RFC 3986 may contain `+`, `-`, and
1099
+ * `.` (e.g. `web+app`, `coap+tcp`). All three are regex metacharacters,
1100
+ * so write `/^web\+app:/i` rather than `/^web+app:/i`. The latter would
1101
+ * match URLs starting with `we`, `wee`, `weee`, … and silently broaden
1102
+ * the allowlist.
1103
+ *
1104
+ * **Reference stability matters.** The block-memo cache treats this prop
1105
+ * as a dependency. Defining the function inline (`urlTransform={(url) =>
1106
+ * …}`) creates a new closure on every parent render, discards the cache
1107
+ * for the entire markdown document on each render, and effectively
1108
+ * disables Phase 5 memoization. In development the library will
1109
+ * `console.warn` if it detects this pattern.
1110
+ *
1111
+ * Allowing a protocol here is necessary but **not sufficient** to render
1112
+ * a link — the second gate (`rehype-sanitize`) also enforces its own
1113
+ * protocol allowlist. See the {@link sanitizeSchema} prop and the
1114
+ * matching {@link extendSanitizeSchema} helper for the second gate.
1115
+ *
1116
+ * **API stability**: the `UrlTransform` type tracks the upstream
1117
+ * `react-markdown` shape and may change with its major versions.
1118
+ */
1119
+ urlTransform?: UrlTransform | null;
1120
+ /**
1121
+ * Override the `rehype-sanitize` schema applied to the rendered output.
1122
+ * The library default ({@link sanitizeSchema}) extends `rehype-sanitize`'s
1123
+ * own `defaultSchema` with the `<mark>` tag, KaTeX class names, and the
1124
+ * cross-chunk coordination tags (`cross-chunk-link`, `cross-chunk-image`,
1125
+ * `footnote-sup`).
1126
+ *
1127
+ * **Recommended pattern**: build the schema with {@link extendSanitizeSchema}
1128
+ * (mutate-and-return form) so those library additions stay intact, and
1129
+ * define the result at module scope:
1130
+ *
1131
+ * ```ts
1132
+ * import AIMarkdown, { extendSanitizeSchema } from '@ai-react-markdown/core';
1133
+ *
1134
+ * const SCHEMA = extendSanitizeSchema((s) => {
1135
+ * s.protocols.href.push('myapp');
1136
+ * s.protocols.src.push('myapp');
1137
+ * });
1138
+ *
1139
+ * function App() {
1140
+ * return <AIMarkdown sanitizeSchema={SCHEMA} ... />;
1141
+ * }
1142
+ * ```
1143
+ *
1144
+ * **Footgun**: hand-rolling a schema (e.g. spreading from
1145
+ * `rehype-sanitize`'s `defaultSchema` directly) silently drops the
1146
+ * cross-chunk tag allowlist — coordinated multi-chunk rendering will then
1147
+ * lose its placeholders. Prefer the helper unless you have a specific
1148
+ * reason to opt out.
1149
+ *
1150
+ * **Reference stability matters.** Inline `<AIMarkdown sanitizeSchema={{
1151
+ * ...sanitizeSchema, protocols: {...} }}>` is mitigated by an internal
1152
+ * `useStableValue` deep-equal pass, but the safer pattern is still
1153
+ * module-scope. Development builds will `console.warn` on identity flips.
1154
+ *
1155
+ * **API stability**: the `SanitizeSchema` type tracks the upstream
1156
+ * `rehype-sanitize` shape and may change with its major versions.
1157
+ */
1158
+ sanitizeSchema?: SanitizeSchema;
960
1159
  }
961
1160
  /**
962
1161
  * Root component that preprocesses markdown content and renders it through
963
1162
  * a configurable remark/rehype pipeline wrapped in typography and style layers.
964
1163
  */
965
- declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, documentId, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
1164
+ declare const AIMarkdownComponent: <TConfig extends AIMarkdownRenderConfig = AIMarkdownRenderConfig, TRenderData extends AIMarkdownMetadata = AIMarkdownMetadata>({ streaming, content, fontSize, contentPreprocessors, customComponents, defaultConfig, config, metadata, Typography, ExtraStyles, variant, colorScheme, documentId, urlTransform, sanitizeSchema, }: AIMarkdownProps<TConfig, TRenderData>) => react_jsx_runtime.JSX.Element;
966
1165
  declare const _default: typeof AIMarkdownComponent;
967
1166
 
968
- export { type AIMDContentPreprocessor, type AIMarkdownColorScheme, type AIMarkdownCustomComponents, AIMarkdownDocuments, type AIMarkdownDocumentsProps, type AIMarkdownExtraStylesComponent, type AIMarkdownExtraStylesProps, type AIMarkdownMetadata, type AIMarkdownProps, type AIMarkdownRenderConfig, AIMarkdownRenderDisplayOptimizeAbility, AIMarkdownRenderExtraSyntax, type AIMarkdownRenderState, type AIMarkdownTypographyComponent, type AIMarkdownTypographyProps, type AIMarkdownVariant, type ChunkData, type FootnoteDef, type LinkDef, type PartialDeep, type RefKind, type RefRecord, type Registry, _default as default, defaultAIMarkdownRenderConfig, useAIMarkdownMetadata, useAIMarkdownRenderState, useDocumentRegistry, useStableValue };
1167
+ export { type AIMDContentPreprocessor, type AIMarkdownColorScheme, type AIMarkdownCustomComponents, AIMarkdownDocuments, type AIMarkdownDocumentsProps, type AIMarkdownExtraStylesComponent, type AIMarkdownExtraStylesProps, type AIMarkdownMetadata, type AIMarkdownProps, type AIMarkdownRenderConfig, AIMarkdownRenderDisplayOptimizeAbility, AIMarkdownRenderExtraSyntax, type AIMarkdownRenderState, type AIMarkdownTypographyComponent, type AIMarkdownTypographyProps, type AIMarkdownVariant, type ChunkData, type FootnoteDef, type LinkDef, type PartialDeep, type RefKind, type RefRecord, type Registry, type SanitizeSchema, type UrlTransform, _default as default, defaultAIMarkdownRenderConfig, defaultUrlTransform, extendSanitizeSchema, sanitizeSchema, useAIMarkdownMetadata, useAIMarkdownRenderState, useDocumentRegistry, useStableValue };