@asteroidcms/core-utils 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -4
- package/dist/client.cjs +1684 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +316 -0
- package/dist/client.d.ts +316 -0
- package/dist/client.js +1669 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +99 -740
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -180
- package/dist/index.d.ts +42 -180
- package/dist/index.js +97 -731
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { PropsWithChildren, RefObject } from 'react';
|
|
4
|
+
import * as _apollo_client from '@apollo/client';
|
|
5
|
+
import { InMemoryCacheConfig, ApolloClientOptions, ApolloClient } from '@apollo/client';
|
|
6
|
+
import { useMutation } from '@apollo/client/react';
|
|
7
|
+
|
|
8
|
+
type AsteroidCMSConfig = {
|
|
9
|
+
/** Base URL of the Asteroid CMS API (e.g. https://cms-api.example.com). */
|
|
10
|
+
cmsUrl: string;
|
|
11
|
+
/** API key sent as the `x-api-key` header on every request. */
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/** Optional path appended to `cmsUrl` for the GraphQL endpoint. Defaults to "/graphql". */
|
|
14
|
+
graphqlPath?: string;
|
|
15
|
+
/** Optional path appended to `cmsUrl` for canonical media. Defaults to "/media/canonical". */
|
|
16
|
+
mediaPath?: string;
|
|
17
|
+
/** Extra headers attached to every GraphQL request. */
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
/** Called for every GraphQL/network error. Use this to wire your own toast/logger. */
|
|
20
|
+
onError?: (error: unknown) => void;
|
|
21
|
+
/** Optional cache options forwarded to the InMemoryCache constructor. */
|
|
22
|
+
cacheConfig?: InMemoryCacheConfig;
|
|
23
|
+
/** Escape hatch — override any field on the underlying ApolloClient. */
|
|
24
|
+
apolloOptions?: Partial<ApolloClientOptions>;
|
|
25
|
+
/** Provide a pre-built ApolloClient and skip the internal factory entirely. */
|
|
26
|
+
client?: ApolloClient;
|
|
27
|
+
};
|
|
28
|
+
type ResolvedAsteroidCMSConfig = Required<Pick<AsteroidCMSConfig, "cmsUrl" | "apiKey" | "graphqlPath" | "mediaPath">> & {
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
onError?: AsteroidCMSConfig["onError"];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type AsteroidCMSProviderProps = PropsWithChildren<AsteroidCMSConfig>;
|
|
34
|
+
/**
|
|
35
|
+
* Root provider for `@asteroidcms/core-utils`.
|
|
36
|
+
*
|
|
37
|
+
* Wrap your app once at the top level:
|
|
38
|
+
*
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <AsteroidCMSProvider cmsUrl="https://cms.example.com" apiKey="...">
|
|
41
|
+
* <App />
|
|
42
|
+
* </AsteroidCMSProvider>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Everything below — `useCmsContent`, `useCmsMutate`, `useCmsImage`,
|
|
46
|
+
* `<RichTextContent>` — picks up `cmsUrl` and `apiKey` from this provider.
|
|
47
|
+
*/
|
|
48
|
+
declare function AsteroidCMSProvider({ children, ...config }: AsteroidCMSProviderProps): react_jsx_runtime.JSX.Element;
|
|
49
|
+
|
|
50
|
+
declare function useAsteroidCMSConfig(): ResolvedAsteroidCMSConfig;
|
|
51
|
+
|
|
52
|
+
type FieldSelector$1 = string | {
|
|
53
|
+
field: string;
|
|
54
|
+
as?: string;
|
|
55
|
+
};
|
|
56
|
+
type ReferenceExpansion$1 = {
|
|
57
|
+
field: string;
|
|
58
|
+
as?: string;
|
|
59
|
+
single?: boolean;
|
|
60
|
+
select?: readonly (FieldSelector$1 | ReferenceExpansion$1)[];
|
|
61
|
+
};
|
|
62
|
+
type ContentStatus = "DRAFT" | "PUBLISHED" | "ARCHIVED";
|
|
63
|
+
type CmsSearchCondition = {
|
|
64
|
+
field: string;
|
|
65
|
+
value: string;
|
|
66
|
+
mode?: string;
|
|
67
|
+
};
|
|
68
|
+
type UseCmsContentOptions = {
|
|
69
|
+
schema_slug: string;
|
|
70
|
+
entrySlug?: string;
|
|
71
|
+
select?: readonly (FieldSelector$1 | ReferenceExpansion$1)[];
|
|
72
|
+
fullData?: boolean;
|
|
73
|
+
limit?: number;
|
|
74
|
+
offset?: number;
|
|
75
|
+
status?: ContentStatus;
|
|
76
|
+
filter?: Record<string, string | number | boolean | null>;
|
|
77
|
+
search?: CmsSearchCondition[];
|
|
78
|
+
variables?: Record<string, any>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* React hook for querying content from Asteroid CMS via a flexible GraphQL API.
|
|
82
|
+
*
|
|
83
|
+
* Supports single-entry fetches (`entrySlug`) and paginated/filtered lists,
|
|
84
|
+
* with arbitrarily nested reference expansion and field aliasing.
|
|
85
|
+
*/
|
|
86
|
+
declare function useCmsContent<T = unknown>({ schema_slug, entrySlug, select, fullData, limit, offset, status, filter, search, variables, }: UseCmsContentOptions): {
|
|
87
|
+
client: _apollo_client.ApolloClient;
|
|
88
|
+
observable: _apollo_client.ObservableQuery<unknown, _apollo_client.OperationVariables>;
|
|
89
|
+
previousData?: unknown;
|
|
90
|
+
networkStatus: _apollo_client.NetworkStatus;
|
|
91
|
+
startPolling: (pollInterval: number) => void;
|
|
92
|
+
stopPolling: () => void;
|
|
93
|
+
subscribeToMore: _apollo_client.SubscribeToMoreFunction<unknown, _apollo_client.OperationVariables>;
|
|
94
|
+
updateQuery: (mapFn: _apollo_client.UpdateQueryMapFn<unknown, _apollo_client.OperationVariables>) => void;
|
|
95
|
+
refetch: (variables?: Partial<_apollo_client.OperationVariables> | undefined) => Promise<{
|
|
96
|
+
data: unknown;
|
|
97
|
+
error?: _apollo_client.ErrorLike;
|
|
98
|
+
}>;
|
|
99
|
+
variables: _apollo_client.OperationVariables;
|
|
100
|
+
fetchMore: <TFetchData = unknown, TFetchVars extends _apollo_client.OperationVariables = _apollo_client.OperationVariables>(fetchMoreOptions: _apollo_client.ObservableQuery.FetchMoreOptions<unknown, _apollo_client.OperationVariables, TFetchData, TFetchVars>) => Promise<{
|
|
101
|
+
data: TFetchData | undefined;
|
|
102
|
+
error?: _apollo_client.ErrorLike;
|
|
103
|
+
}>;
|
|
104
|
+
dataState: "empty";
|
|
105
|
+
loading: boolean;
|
|
106
|
+
error: _apollo_client.ErrorLike | undefined;
|
|
107
|
+
data: T | undefined;
|
|
108
|
+
} | {
|
|
109
|
+
client: _apollo_client.ApolloClient;
|
|
110
|
+
observable: _apollo_client.ObservableQuery<unknown, _apollo_client.OperationVariables>;
|
|
111
|
+
previousData?: unknown;
|
|
112
|
+
networkStatus: _apollo_client.NetworkStatus;
|
|
113
|
+
startPolling: (pollInterval: number) => void;
|
|
114
|
+
stopPolling: () => void;
|
|
115
|
+
subscribeToMore: _apollo_client.SubscribeToMoreFunction<unknown, _apollo_client.OperationVariables>;
|
|
116
|
+
updateQuery: (mapFn: _apollo_client.UpdateQueryMapFn<unknown, _apollo_client.OperationVariables>) => void;
|
|
117
|
+
refetch: (variables?: Partial<_apollo_client.OperationVariables> | undefined) => Promise<{
|
|
118
|
+
data: unknown;
|
|
119
|
+
error?: _apollo_client.ErrorLike;
|
|
120
|
+
}>;
|
|
121
|
+
variables: _apollo_client.OperationVariables;
|
|
122
|
+
fetchMore: <TFetchData = unknown, TFetchVars extends _apollo_client.OperationVariables = _apollo_client.OperationVariables>(fetchMoreOptions: _apollo_client.ObservableQuery.FetchMoreOptions<unknown, _apollo_client.OperationVariables, TFetchData, TFetchVars>) => Promise<{
|
|
123
|
+
data: TFetchData | undefined;
|
|
124
|
+
error?: _apollo_client.ErrorLike;
|
|
125
|
+
}>;
|
|
126
|
+
dataState: "complete";
|
|
127
|
+
loading: boolean;
|
|
128
|
+
error: _apollo_client.ErrorLike | undefined;
|
|
129
|
+
data: T | undefined;
|
|
130
|
+
} | {
|
|
131
|
+
client: _apollo_client.ApolloClient;
|
|
132
|
+
observable: _apollo_client.ObservableQuery<unknown, _apollo_client.OperationVariables>;
|
|
133
|
+
previousData?: unknown;
|
|
134
|
+
networkStatus: _apollo_client.NetworkStatus;
|
|
135
|
+
startPolling: (pollInterval: number) => void;
|
|
136
|
+
stopPolling: () => void;
|
|
137
|
+
subscribeToMore: _apollo_client.SubscribeToMoreFunction<unknown, _apollo_client.OperationVariables>;
|
|
138
|
+
updateQuery: (mapFn: _apollo_client.UpdateQueryMapFn<unknown, _apollo_client.OperationVariables>) => void;
|
|
139
|
+
refetch: (variables?: Partial<_apollo_client.OperationVariables> | undefined) => Promise<{
|
|
140
|
+
data: unknown;
|
|
141
|
+
error?: _apollo_client.ErrorLike;
|
|
142
|
+
}>;
|
|
143
|
+
variables: _apollo_client.OperationVariables;
|
|
144
|
+
fetchMore: <TFetchData = unknown, TFetchVars extends _apollo_client.OperationVariables = _apollo_client.OperationVariables>(fetchMoreOptions: _apollo_client.ObservableQuery.FetchMoreOptions<unknown, _apollo_client.OperationVariables, TFetchData, TFetchVars>) => Promise<{
|
|
145
|
+
data: TFetchData | undefined;
|
|
146
|
+
error?: _apollo_client.ErrorLike;
|
|
147
|
+
}>;
|
|
148
|
+
dataState: "streaming";
|
|
149
|
+
loading: boolean;
|
|
150
|
+
error: _apollo_client.ErrorLike | undefined;
|
|
151
|
+
data: T | undefined;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
type FieldSelector = string | {
|
|
155
|
+
field: string;
|
|
156
|
+
as?: string;
|
|
157
|
+
single?: boolean;
|
|
158
|
+
select?: readonly (FieldSelector | ReferenceExpansion)[];
|
|
159
|
+
};
|
|
160
|
+
type ReferenceExpansion = {
|
|
161
|
+
field: string;
|
|
162
|
+
as?: string;
|
|
163
|
+
single?: boolean;
|
|
164
|
+
select?: readonly (FieldSelector | ReferenceExpansion)[];
|
|
165
|
+
};
|
|
166
|
+
type MutationType = "create" | "update" | "delete";
|
|
167
|
+
type UseCmsMutateOptions = {
|
|
168
|
+
schema_slug: string;
|
|
169
|
+
entrySlug?: string;
|
|
170
|
+
select?: readonly (FieldSelector | ReferenceExpansion)[];
|
|
171
|
+
fullData?: boolean;
|
|
172
|
+
mutationType?: MutationType;
|
|
173
|
+
entryId?: string;
|
|
174
|
+
variables?: Record<string, any>;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* React hook for mutating content in Asteroid CMS (`create` / `update` / `delete`).
|
|
178
|
+
* Uses the same selection syntax as `useCmsContent`.
|
|
179
|
+
*/
|
|
180
|
+
declare function useCmsMutate<TData = unknown>({ schema_slug, select, fullData, mutationType, entryId, variables: inputVariables, }: UseCmsMutateOptions): {
|
|
181
|
+
readonly data: TData | undefined;
|
|
182
|
+
readonly loading: boolean;
|
|
183
|
+
readonly called: boolean;
|
|
184
|
+
readonly client: _apollo_client.ApolloClient;
|
|
185
|
+
readonly reset: () => void;
|
|
186
|
+
readonly error: _apollo_client.ErrorLike | undefined;
|
|
187
|
+
readonly mutate: useMutation.MutationFunction<unknown, {
|
|
188
|
+
[x: string]: any;
|
|
189
|
+
}, _apollo_client.ApolloCache, "none">;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/** Hook variant that pulls `cmsUrl`/`mediaPath` from the provider. */
|
|
193
|
+
declare function useCmsImage(): (id?: string) => string;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Portable rich-text parser for CMS content.
|
|
197
|
+
*
|
|
198
|
+
* Zero runtime dependencies. Safe to copy into any consuming frontend.
|
|
199
|
+
*
|
|
200
|
+
* Pipeline:
|
|
201
|
+
* 1. Detect & migrate legacy "markdown-in-divs" content to clean semantic HTML.
|
|
202
|
+
* 2. Walk the HTML through a tag/attribute allowlist (strips <script>, event
|
|
203
|
+
* handlers, javascript: URLs, unknown tags).
|
|
204
|
+
* 3. Merge per-tag and per-variant classes from `options.classMap`.
|
|
205
|
+
*
|
|
206
|
+
* Idempotent: parseRichText(parseRichText(x, opts), opts) === parseRichText(x, opts).
|
|
207
|
+
*/
|
|
208
|
+
type RichTextClassKey = "p" | "br" | "hr" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "ul" | "ol" | "li" | "blockquote" | "pre" | "code" | "inlineCode" | "a" | "strong" | "em" | "u" | "s" | "kbd" | "table" | "tableWrapper" | "thead" | "tbody" | "tr" | "th" | "td" | "figure" | "figcaption" | "img" | "span" | "callout" | "calloutTitle";
|
|
209
|
+
type RichTextClassMap = Partial<Record<RichTextClassKey, string>> & {
|
|
210
|
+
/** Variant overrides keyed as `${matchKey}:${variant}`, e.g. "callout:warning". */
|
|
211
|
+
variants?: Record<string, string>;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
interface RichTextContentProps {
|
|
215
|
+
html: string;
|
|
216
|
+
classMap?: RichTextClassMap;
|
|
217
|
+
/** Wrapper element. Defaults to `div`. Use `article` for blog content. */
|
|
218
|
+
as?: keyof React.JSX.IntrinsicElements;
|
|
219
|
+
className?: string;
|
|
220
|
+
/**
|
|
221
|
+
* Fires after the parsed HTML is in the DOM and post-render enhancements
|
|
222
|
+
* (syntax highlighting, copy buttons, blockquote decorations) have run.
|
|
223
|
+
* The wrapper element is passed back so callers can read headings, attach
|
|
224
|
+
* a ToC observer, or do other DOM work without re-querying.
|
|
225
|
+
*/
|
|
226
|
+
onReady?: (root: HTMLElement) => void;
|
|
227
|
+
/**
|
|
228
|
+
* Optional ref to the wrapper element. Useful for hooks like
|
|
229
|
+
* `useTableOfContents` that need a stable reference to the rendered tree.
|
|
230
|
+
*/
|
|
231
|
+
contentRef?: React.MutableRefObject<HTMLElement | null>;
|
|
232
|
+
}
|
|
233
|
+
declare function RichTextContent({ html, classMap, as, className, onReady, contentRef, }: RichTextContentProps): react.DOMElement<{
|
|
234
|
+
ref: react.MutableRefObject<HTMLElement | null>;
|
|
235
|
+
className: string | undefined;
|
|
236
|
+
dangerouslySetInnerHTML: {
|
|
237
|
+
__html: string;
|
|
238
|
+
};
|
|
239
|
+
}, HTMLElement>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Heading extraction helpers used to build tables of contents (ToC) from
|
|
243
|
+
* rich-text HTML. Server-safe — no React, no DOM dependency in the HTML
|
|
244
|
+
* variant. The DOM variant assigns missing `id`s in-place so anchor links
|
|
245
|
+
* resolve immediately.
|
|
246
|
+
*/
|
|
247
|
+
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
248
|
+
interface ExtractedHeading {
|
|
249
|
+
id: string;
|
|
250
|
+
text: string;
|
|
251
|
+
level: HeadingLevel;
|
|
252
|
+
}
|
|
253
|
+
interface ExtractHeadingsOptions {
|
|
254
|
+
/** Levels to include. Defaults to `[2, 3]` — typical doc page outline. */
|
|
255
|
+
levels?: ReadonlyArray<HeadingLevel>;
|
|
256
|
+
/** Custom slug function. Defaults to a lowercase/kebab/diacritic-safe slug. */
|
|
257
|
+
slugify?: (text: string, index: number) => string;
|
|
258
|
+
}
|
|
259
|
+
declare function slugify(text: string): string;
|
|
260
|
+
/**
|
|
261
|
+
* Parse headings out of a raw HTML string. Returns headings in document
|
|
262
|
+
* order with stable, de-duplicated IDs.
|
|
263
|
+
*
|
|
264
|
+
* If a heading already has an `id` attribute, it's preserved verbatim
|
|
265
|
+
* (and reserved so later slugs don't collide with it).
|
|
266
|
+
*/
|
|
267
|
+
declare function extractHeadingsFromHtml(html: string, options?: ExtractHeadingsOptions): ExtractedHeading[];
|
|
268
|
+
/**
|
|
269
|
+
* Walk a rendered DOM subtree, collect headings, and assign missing `id`s
|
|
270
|
+
* in-place so anchor links resolve immediately. Also sets `scrollMarginTop`
|
|
271
|
+
* on each heading when `scrollMarginTop` is provided so navigation lands
|
|
272
|
+
* cleanly below a sticky header.
|
|
273
|
+
*/
|
|
274
|
+
declare function extractHeadingsFromElement(root: HTMLElement, options?: ExtractHeadingsOptions & {
|
|
275
|
+
scrollMarginTop?: number;
|
|
276
|
+
}): ExtractedHeading[];
|
|
277
|
+
|
|
278
|
+
interface UseTableOfContentsOptions {
|
|
279
|
+
/** Heading levels to include. Defaults to `[2, 3]`. */
|
|
280
|
+
levels?: ReadonlyArray<HeadingLevel>;
|
|
281
|
+
/**
|
|
282
|
+
* Re-collect headings whenever this value changes. Pass the article slug
|
|
283
|
+
* (or any stable identifier) so swapping content rebuilds the ToC.
|
|
284
|
+
*/
|
|
285
|
+
contentKey?: string | number | null;
|
|
286
|
+
/** Pixels to subtract from heading scroll-into-view target. Default 24. */
|
|
287
|
+
scrollMarginTop?: number;
|
|
288
|
+
/**
|
|
289
|
+
* Distance from the top of the viewport (in px) at which a heading becomes
|
|
290
|
+
* "active". A heading is considered active once its top edge has scrolled
|
|
291
|
+
* past this line. Default `96` — works well with a sticky header that
|
|
292
|
+
* stands ~60–80px tall.
|
|
293
|
+
*/
|
|
294
|
+
activationOffset?: number;
|
|
295
|
+
}
|
|
296
|
+
interface UseTableOfContentsResult {
|
|
297
|
+
items: ExtractedHeading[];
|
|
298
|
+
activeId: string;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Build a table of contents from a rendered element and track which
|
|
302
|
+
* heading is currently in view via IntersectionObserver. Resilient to
|
|
303
|
+
* content swaps when `contentKey` is provided.
|
|
304
|
+
*
|
|
305
|
+
* Usage:
|
|
306
|
+
* ```tsx
|
|
307
|
+
* const ref = useRef<HTMLDivElement>(null);
|
|
308
|
+
* const { items, activeId } = useTableOfContents(ref, {
|
|
309
|
+
* contentKey: article.slug,
|
|
310
|
+
* levels: [2, 3],
|
|
311
|
+
* });
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
declare function useTableOfContents(ref: RefObject<HTMLElement | null>, options?: UseTableOfContentsOptions): UseTableOfContentsResult;
|
|
315
|
+
|
|
316
|
+
export { AsteroidCMSProvider, type AsteroidCMSProviderProps, type ExtractHeadingsOptions, type ExtractedHeading, type HeadingLevel, RichTextContent, type UseCmsContentOptions, type UseCmsMutateOptions, type UseTableOfContentsOptions, type UseTableOfContentsResult, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate, useTableOfContents };
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { PropsWithChildren, RefObject } from 'react';
|
|
4
|
+
import * as _apollo_client from '@apollo/client';
|
|
5
|
+
import { InMemoryCacheConfig, ApolloClientOptions, ApolloClient } from '@apollo/client';
|
|
6
|
+
import { useMutation } from '@apollo/client/react';
|
|
7
|
+
|
|
8
|
+
type AsteroidCMSConfig = {
|
|
9
|
+
/** Base URL of the Asteroid CMS API (e.g. https://cms-api.example.com). */
|
|
10
|
+
cmsUrl: string;
|
|
11
|
+
/** API key sent as the `x-api-key` header on every request. */
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/** Optional path appended to `cmsUrl` for the GraphQL endpoint. Defaults to "/graphql". */
|
|
14
|
+
graphqlPath?: string;
|
|
15
|
+
/** Optional path appended to `cmsUrl` for canonical media. Defaults to "/media/canonical". */
|
|
16
|
+
mediaPath?: string;
|
|
17
|
+
/** Extra headers attached to every GraphQL request. */
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
/** Called for every GraphQL/network error. Use this to wire your own toast/logger. */
|
|
20
|
+
onError?: (error: unknown) => void;
|
|
21
|
+
/** Optional cache options forwarded to the InMemoryCache constructor. */
|
|
22
|
+
cacheConfig?: InMemoryCacheConfig;
|
|
23
|
+
/** Escape hatch — override any field on the underlying ApolloClient. */
|
|
24
|
+
apolloOptions?: Partial<ApolloClientOptions>;
|
|
25
|
+
/** Provide a pre-built ApolloClient and skip the internal factory entirely. */
|
|
26
|
+
client?: ApolloClient;
|
|
27
|
+
};
|
|
28
|
+
type ResolvedAsteroidCMSConfig = Required<Pick<AsteroidCMSConfig, "cmsUrl" | "apiKey" | "graphqlPath" | "mediaPath">> & {
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
onError?: AsteroidCMSConfig["onError"];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type AsteroidCMSProviderProps = PropsWithChildren<AsteroidCMSConfig>;
|
|
34
|
+
/**
|
|
35
|
+
* Root provider for `@asteroidcms/core-utils`.
|
|
36
|
+
*
|
|
37
|
+
* Wrap your app once at the top level:
|
|
38
|
+
*
|
|
39
|
+
* ```tsx
|
|
40
|
+
* <AsteroidCMSProvider cmsUrl="https://cms.example.com" apiKey="...">
|
|
41
|
+
* <App />
|
|
42
|
+
* </AsteroidCMSProvider>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Everything below — `useCmsContent`, `useCmsMutate`, `useCmsImage`,
|
|
46
|
+
* `<RichTextContent>` — picks up `cmsUrl` and `apiKey` from this provider.
|
|
47
|
+
*/
|
|
48
|
+
declare function AsteroidCMSProvider({ children, ...config }: AsteroidCMSProviderProps): react_jsx_runtime.JSX.Element;
|
|
49
|
+
|
|
50
|
+
declare function useAsteroidCMSConfig(): ResolvedAsteroidCMSConfig;
|
|
51
|
+
|
|
52
|
+
type FieldSelector$1 = string | {
|
|
53
|
+
field: string;
|
|
54
|
+
as?: string;
|
|
55
|
+
};
|
|
56
|
+
type ReferenceExpansion$1 = {
|
|
57
|
+
field: string;
|
|
58
|
+
as?: string;
|
|
59
|
+
single?: boolean;
|
|
60
|
+
select?: readonly (FieldSelector$1 | ReferenceExpansion$1)[];
|
|
61
|
+
};
|
|
62
|
+
type ContentStatus = "DRAFT" | "PUBLISHED" | "ARCHIVED";
|
|
63
|
+
type CmsSearchCondition = {
|
|
64
|
+
field: string;
|
|
65
|
+
value: string;
|
|
66
|
+
mode?: string;
|
|
67
|
+
};
|
|
68
|
+
type UseCmsContentOptions = {
|
|
69
|
+
schema_slug: string;
|
|
70
|
+
entrySlug?: string;
|
|
71
|
+
select?: readonly (FieldSelector$1 | ReferenceExpansion$1)[];
|
|
72
|
+
fullData?: boolean;
|
|
73
|
+
limit?: number;
|
|
74
|
+
offset?: number;
|
|
75
|
+
status?: ContentStatus;
|
|
76
|
+
filter?: Record<string, string | number | boolean | null>;
|
|
77
|
+
search?: CmsSearchCondition[];
|
|
78
|
+
variables?: Record<string, any>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* React hook for querying content from Asteroid CMS via a flexible GraphQL API.
|
|
82
|
+
*
|
|
83
|
+
* Supports single-entry fetches (`entrySlug`) and paginated/filtered lists,
|
|
84
|
+
* with arbitrarily nested reference expansion and field aliasing.
|
|
85
|
+
*/
|
|
86
|
+
declare function useCmsContent<T = unknown>({ schema_slug, entrySlug, select, fullData, limit, offset, status, filter, search, variables, }: UseCmsContentOptions): {
|
|
87
|
+
client: _apollo_client.ApolloClient;
|
|
88
|
+
observable: _apollo_client.ObservableQuery<unknown, _apollo_client.OperationVariables>;
|
|
89
|
+
previousData?: unknown;
|
|
90
|
+
networkStatus: _apollo_client.NetworkStatus;
|
|
91
|
+
startPolling: (pollInterval: number) => void;
|
|
92
|
+
stopPolling: () => void;
|
|
93
|
+
subscribeToMore: _apollo_client.SubscribeToMoreFunction<unknown, _apollo_client.OperationVariables>;
|
|
94
|
+
updateQuery: (mapFn: _apollo_client.UpdateQueryMapFn<unknown, _apollo_client.OperationVariables>) => void;
|
|
95
|
+
refetch: (variables?: Partial<_apollo_client.OperationVariables> | undefined) => Promise<{
|
|
96
|
+
data: unknown;
|
|
97
|
+
error?: _apollo_client.ErrorLike;
|
|
98
|
+
}>;
|
|
99
|
+
variables: _apollo_client.OperationVariables;
|
|
100
|
+
fetchMore: <TFetchData = unknown, TFetchVars extends _apollo_client.OperationVariables = _apollo_client.OperationVariables>(fetchMoreOptions: _apollo_client.ObservableQuery.FetchMoreOptions<unknown, _apollo_client.OperationVariables, TFetchData, TFetchVars>) => Promise<{
|
|
101
|
+
data: TFetchData | undefined;
|
|
102
|
+
error?: _apollo_client.ErrorLike;
|
|
103
|
+
}>;
|
|
104
|
+
dataState: "empty";
|
|
105
|
+
loading: boolean;
|
|
106
|
+
error: _apollo_client.ErrorLike | undefined;
|
|
107
|
+
data: T | undefined;
|
|
108
|
+
} | {
|
|
109
|
+
client: _apollo_client.ApolloClient;
|
|
110
|
+
observable: _apollo_client.ObservableQuery<unknown, _apollo_client.OperationVariables>;
|
|
111
|
+
previousData?: unknown;
|
|
112
|
+
networkStatus: _apollo_client.NetworkStatus;
|
|
113
|
+
startPolling: (pollInterval: number) => void;
|
|
114
|
+
stopPolling: () => void;
|
|
115
|
+
subscribeToMore: _apollo_client.SubscribeToMoreFunction<unknown, _apollo_client.OperationVariables>;
|
|
116
|
+
updateQuery: (mapFn: _apollo_client.UpdateQueryMapFn<unknown, _apollo_client.OperationVariables>) => void;
|
|
117
|
+
refetch: (variables?: Partial<_apollo_client.OperationVariables> | undefined) => Promise<{
|
|
118
|
+
data: unknown;
|
|
119
|
+
error?: _apollo_client.ErrorLike;
|
|
120
|
+
}>;
|
|
121
|
+
variables: _apollo_client.OperationVariables;
|
|
122
|
+
fetchMore: <TFetchData = unknown, TFetchVars extends _apollo_client.OperationVariables = _apollo_client.OperationVariables>(fetchMoreOptions: _apollo_client.ObservableQuery.FetchMoreOptions<unknown, _apollo_client.OperationVariables, TFetchData, TFetchVars>) => Promise<{
|
|
123
|
+
data: TFetchData | undefined;
|
|
124
|
+
error?: _apollo_client.ErrorLike;
|
|
125
|
+
}>;
|
|
126
|
+
dataState: "complete";
|
|
127
|
+
loading: boolean;
|
|
128
|
+
error: _apollo_client.ErrorLike | undefined;
|
|
129
|
+
data: T | undefined;
|
|
130
|
+
} | {
|
|
131
|
+
client: _apollo_client.ApolloClient;
|
|
132
|
+
observable: _apollo_client.ObservableQuery<unknown, _apollo_client.OperationVariables>;
|
|
133
|
+
previousData?: unknown;
|
|
134
|
+
networkStatus: _apollo_client.NetworkStatus;
|
|
135
|
+
startPolling: (pollInterval: number) => void;
|
|
136
|
+
stopPolling: () => void;
|
|
137
|
+
subscribeToMore: _apollo_client.SubscribeToMoreFunction<unknown, _apollo_client.OperationVariables>;
|
|
138
|
+
updateQuery: (mapFn: _apollo_client.UpdateQueryMapFn<unknown, _apollo_client.OperationVariables>) => void;
|
|
139
|
+
refetch: (variables?: Partial<_apollo_client.OperationVariables> | undefined) => Promise<{
|
|
140
|
+
data: unknown;
|
|
141
|
+
error?: _apollo_client.ErrorLike;
|
|
142
|
+
}>;
|
|
143
|
+
variables: _apollo_client.OperationVariables;
|
|
144
|
+
fetchMore: <TFetchData = unknown, TFetchVars extends _apollo_client.OperationVariables = _apollo_client.OperationVariables>(fetchMoreOptions: _apollo_client.ObservableQuery.FetchMoreOptions<unknown, _apollo_client.OperationVariables, TFetchData, TFetchVars>) => Promise<{
|
|
145
|
+
data: TFetchData | undefined;
|
|
146
|
+
error?: _apollo_client.ErrorLike;
|
|
147
|
+
}>;
|
|
148
|
+
dataState: "streaming";
|
|
149
|
+
loading: boolean;
|
|
150
|
+
error: _apollo_client.ErrorLike | undefined;
|
|
151
|
+
data: T | undefined;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
type FieldSelector = string | {
|
|
155
|
+
field: string;
|
|
156
|
+
as?: string;
|
|
157
|
+
single?: boolean;
|
|
158
|
+
select?: readonly (FieldSelector | ReferenceExpansion)[];
|
|
159
|
+
};
|
|
160
|
+
type ReferenceExpansion = {
|
|
161
|
+
field: string;
|
|
162
|
+
as?: string;
|
|
163
|
+
single?: boolean;
|
|
164
|
+
select?: readonly (FieldSelector | ReferenceExpansion)[];
|
|
165
|
+
};
|
|
166
|
+
type MutationType = "create" | "update" | "delete";
|
|
167
|
+
type UseCmsMutateOptions = {
|
|
168
|
+
schema_slug: string;
|
|
169
|
+
entrySlug?: string;
|
|
170
|
+
select?: readonly (FieldSelector | ReferenceExpansion)[];
|
|
171
|
+
fullData?: boolean;
|
|
172
|
+
mutationType?: MutationType;
|
|
173
|
+
entryId?: string;
|
|
174
|
+
variables?: Record<string, any>;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* React hook for mutating content in Asteroid CMS (`create` / `update` / `delete`).
|
|
178
|
+
* Uses the same selection syntax as `useCmsContent`.
|
|
179
|
+
*/
|
|
180
|
+
declare function useCmsMutate<TData = unknown>({ schema_slug, select, fullData, mutationType, entryId, variables: inputVariables, }: UseCmsMutateOptions): {
|
|
181
|
+
readonly data: TData | undefined;
|
|
182
|
+
readonly loading: boolean;
|
|
183
|
+
readonly called: boolean;
|
|
184
|
+
readonly client: _apollo_client.ApolloClient;
|
|
185
|
+
readonly reset: () => void;
|
|
186
|
+
readonly error: _apollo_client.ErrorLike | undefined;
|
|
187
|
+
readonly mutate: useMutation.MutationFunction<unknown, {
|
|
188
|
+
[x: string]: any;
|
|
189
|
+
}, _apollo_client.ApolloCache, "none">;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/** Hook variant that pulls `cmsUrl`/`mediaPath` from the provider. */
|
|
193
|
+
declare function useCmsImage(): (id?: string) => string;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Portable rich-text parser for CMS content.
|
|
197
|
+
*
|
|
198
|
+
* Zero runtime dependencies. Safe to copy into any consuming frontend.
|
|
199
|
+
*
|
|
200
|
+
* Pipeline:
|
|
201
|
+
* 1. Detect & migrate legacy "markdown-in-divs" content to clean semantic HTML.
|
|
202
|
+
* 2. Walk the HTML through a tag/attribute allowlist (strips <script>, event
|
|
203
|
+
* handlers, javascript: URLs, unknown tags).
|
|
204
|
+
* 3. Merge per-tag and per-variant classes from `options.classMap`.
|
|
205
|
+
*
|
|
206
|
+
* Idempotent: parseRichText(parseRichText(x, opts), opts) === parseRichText(x, opts).
|
|
207
|
+
*/
|
|
208
|
+
type RichTextClassKey = "p" | "br" | "hr" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "ul" | "ol" | "li" | "blockquote" | "pre" | "code" | "inlineCode" | "a" | "strong" | "em" | "u" | "s" | "kbd" | "table" | "tableWrapper" | "thead" | "tbody" | "tr" | "th" | "td" | "figure" | "figcaption" | "img" | "span" | "callout" | "calloutTitle";
|
|
209
|
+
type RichTextClassMap = Partial<Record<RichTextClassKey, string>> & {
|
|
210
|
+
/** Variant overrides keyed as `${matchKey}:${variant}`, e.g. "callout:warning". */
|
|
211
|
+
variants?: Record<string, string>;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
interface RichTextContentProps {
|
|
215
|
+
html: string;
|
|
216
|
+
classMap?: RichTextClassMap;
|
|
217
|
+
/** Wrapper element. Defaults to `div`. Use `article` for blog content. */
|
|
218
|
+
as?: keyof React.JSX.IntrinsicElements;
|
|
219
|
+
className?: string;
|
|
220
|
+
/**
|
|
221
|
+
* Fires after the parsed HTML is in the DOM and post-render enhancements
|
|
222
|
+
* (syntax highlighting, copy buttons, blockquote decorations) have run.
|
|
223
|
+
* The wrapper element is passed back so callers can read headings, attach
|
|
224
|
+
* a ToC observer, or do other DOM work without re-querying.
|
|
225
|
+
*/
|
|
226
|
+
onReady?: (root: HTMLElement) => void;
|
|
227
|
+
/**
|
|
228
|
+
* Optional ref to the wrapper element. Useful for hooks like
|
|
229
|
+
* `useTableOfContents` that need a stable reference to the rendered tree.
|
|
230
|
+
*/
|
|
231
|
+
contentRef?: React.MutableRefObject<HTMLElement | null>;
|
|
232
|
+
}
|
|
233
|
+
declare function RichTextContent({ html, classMap, as, className, onReady, contentRef, }: RichTextContentProps): react.DOMElement<{
|
|
234
|
+
ref: react.MutableRefObject<HTMLElement | null>;
|
|
235
|
+
className: string | undefined;
|
|
236
|
+
dangerouslySetInnerHTML: {
|
|
237
|
+
__html: string;
|
|
238
|
+
};
|
|
239
|
+
}, HTMLElement>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Heading extraction helpers used to build tables of contents (ToC) from
|
|
243
|
+
* rich-text HTML. Server-safe — no React, no DOM dependency in the HTML
|
|
244
|
+
* variant. The DOM variant assigns missing `id`s in-place so anchor links
|
|
245
|
+
* resolve immediately.
|
|
246
|
+
*/
|
|
247
|
+
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
248
|
+
interface ExtractedHeading {
|
|
249
|
+
id: string;
|
|
250
|
+
text: string;
|
|
251
|
+
level: HeadingLevel;
|
|
252
|
+
}
|
|
253
|
+
interface ExtractHeadingsOptions {
|
|
254
|
+
/** Levels to include. Defaults to `[2, 3]` — typical doc page outline. */
|
|
255
|
+
levels?: ReadonlyArray<HeadingLevel>;
|
|
256
|
+
/** Custom slug function. Defaults to a lowercase/kebab/diacritic-safe slug. */
|
|
257
|
+
slugify?: (text: string, index: number) => string;
|
|
258
|
+
}
|
|
259
|
+
declare function slugify(text: string): string;
|
|
260
|
+
/**
|
|
261
|
+
* Parse headings out of a raw HTML string. Returns headings in document
|
|
262
|
+
* order with stable, de-duplicated IDs.
|
|
263
|
+
*
|
|
264
|
+
* If a heading already has an `id` attribute, it's preserved verbatim
|
|
265
|
+
* (and reserved so later slugs don't collide with it).
|
|
266
|
+
*/
|
|
267
|
+
declare function extractHeadingsFromHtml(html: string, options?: ExtractHeadingsOptions): ExtractedHeading[];
|
|
268
|
+
/**
|
|
269
|
+
* Walk a rendered DOM subtree, collect headings, and assign missing `id`s
|
|
270
|
+
* in-place so anchor links resolve immediately. Also sets `scrollMarginTop`
|
|
271
|
+
* on each heading when `scrollMarginTop` is provided so navigation lands
|
|
272
|
+
* cleanly below a sticky header.
|
|
273
|
+
*/
|
|
274
|
+
declare function extractHeadingsFromElement(root: HTMLElement, options?: ExtractHeadingsOptions & {
|
|
275
|
+
scrollMarginTop?: number;
|
|
276
|
+
}): ExtractedHeading[];
|
|
277
|
+
|
|
278
|
+
interface UseTableOfContentsOptions {
|
|
279
|
+
/** Heading levels to include. Defaults to `[2, 3]`. */
|
|
280
|
+
levels?: ReadonlyArray<HeadingLevel>;
|
|
281
|
+
/**
|
|
282
|
+
* Re-collect headings whenever this value changes. Pass the article slug
|
|
283
|
+
* (or any stable identifier) so swapping content rebuilds the ToC.
|
|
284
|
+
*/
|
|
285
|
+
contentKey?: string | number | null;
|
|
286
|
+
/** Pixels to subtract from heading scroll-into-view target. Default 24. */
|
|
287
|
+
scrollMarginTop?: number;
|
|
288
|
+
/**
|
|
289
|
+
* Distance from the top of the viewport (in px) at which a heading becomes
|
|
290
|
+
* "active". A heading is considered active once its top edge has scrolled
|
|
291
|
+
* past this line. Default `96` — works well with a sticky header that
|
|
292
|
+
* stands ~60–80px tall.
|
|
293
|
+
*/
|
|
294
|
+
activationOffset?: number;
|
|
295
|
+
}
|
|
296
|
+
interface UseTableOfContentsResult {
|
|
297
|
+
items: ExtractedHeading[];
|
|
298
|
+
activeId: string;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Build a table of contents from a rendered element and track which
|
|
302
|
+
* heading is currently in view via IntersectionObserver. Resilient to
|
|
303
|
+
* content swaps when `contentKey` is provided.
|
|
304
|
+
*
|
|
305
|
+
* Usage:
|
|
306
|
+
* ```tsx
|
|
307
|
+
* const ref = useRef<HTMLDivElement>(null);
|
|
308
|
+
* const { items, activeId } = useTableOfContents(ref, {
|
|
309
|
+
* contentKey: article.slug,
|
|
310
|
+
* levels: [2, 3],
|
|
311
|
+
* });
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
declare function useTableOfContents(ref: RefObject<HTMLElement | null>, options?: UseTableOfContentsOptions): UseTableOfContentsResult;
|
|
315
|
+
|
|
316
|
+
export { AsteroidCMSProvider, type AsteroidCMSProviderProps, type ExtractHeadingsOptions, type ExtractedHeading, type HeadingLevel, RichTextContent, type UseCmsContentOptions, type UseCmsMutateOptions, type UseTableOfContentsOptions, type UseTableOfContentsResult, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate, useTableOfContents };
|