@asteroidcms/core-utils 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,70 @@
1
+ Asteroid CMS Core Utils — Proprietary License
2
+ Copyright (c) 2026 Asteroid. All rights reserved.
3
+
4
+ This software and its associated source code, documentation, type
5
+ definitions, build artifacts, and any accompanying files (collectively,
6
+ the "Software") are the exclusive intellectual property of Asteroid
7
+ ("Licensor"). The Software is licensed, not sold.
8
+
9
+ 1. Grant of License
10
+ Subject to the terms below, Licensor grants you ("Licensee") a
11
+ personal, non-exclusive, non-transferable, non-sublicensable,
12
+ revocable, royalty-free license to install and use the Software,
13
+ solely as a dependency of an application, to integrate with Asteroid
14
+ CMS services. This grant covers use of the Software in its original,
15
+ unmodified form, including normal package-manager installation,
16
+ bundling, and execution of the Software as part of the Licensee's
17
+ application.
18
+
19
+ 2. Restrictions
20
+ Licensee shall NOT, in whole or in part, directly or indirectly:
21
+ (a) copy, reproduce, fork, mirror, or republish the Software or any
22
+ portion of its source code, except as strictly required for the
23
+ internal operation of Licensee's own application;
24
+ (b) modify, adapt, translate, refactor, port, or create derivative
25
+ works of the Software;
26
+ (c) redistribute, sublicense, sell, rent, lease, lend, host, or
27
+ otherwise make the Software (modified or unmodified) available
28
+ to any third party as a standalone library, package, service,
29
+ or component;
30
+ (d) remove, alter, or obscure any copyright, trademark, attribution,
31
+ or proprietary notice contained in the Software;
32
+ (e) reverse-engineer, decompile, or disassemble the Software, except
33
+ to the extent such activity is expressly permitted by applicable
34
+ law notwithstanding this restriction;
35
+ (f) use the Software to build, train, or evaluate a product that
36
+ competes with Asteroid CMS or this package.
37
+
38
+ 3. Ownership
39
+ All right, title, and interest in and to the Software, including all
40
+ intellectual property rights therein, are and shall remain the sole
41
+ and exclusive property of Asteroid. No rights are granted to
42
+ Licensee other than as expressly set forth in this License. All
43
+ rights not expressly granted are reserved by Asteroid.
44
+
45
+ 4. Termination
46
+ This License is effective until terminated. It will terminate
47
+ automatically, without notice, if Licensee breaches any term of this
48
+ License. Upon termination, Licensee must cease all use of the
49
+ Software and destroy all copies in Licensee's possession or control.
50
+
51
+ 5. No Warranty
52
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
53
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
54
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
55
+ NON-INFRINGEMENT.
56
+
57
+ 6. Limitation of Liability
58
+ IN NO EVENT SHALL ASTEROID OR ITS CONTRIBUTORS BE LIABLE FOR ANY
59
+ CLAIM, DAMAGES, OR OTHER LIABILITY — WHETHER IN AN ACTION OF
60
+ CONTRACT, TORT, OR OTHERWISE — ARISING FROM, OUT OF, OR IN
61
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
62
+ SOFTWARE.
63
+
64
+ 7. Governing Law
65
+ This License shall be governed by and construed in accordance with
66
+ the laws of the jurisdiction in which Asteroid is established,
67
+ without regard to its conflict-of-laws principles.
68
+
69
+ For licensing inquiries, partnership, or to request rights beyond the
70
+ scope of this License, contact: legal@theasteroid.tech
package/README.md ADDED
@@ -0,0 +1,327 @@
1
+ <div align="center">
2
+ <h1>
3
+ <div style="display: inline-flex; align-items: center; gap: 4px;">
4
+ <img src="https://cms.theasteroid.tech/logo/logo_gradient.svg" alt="@asteroidcms" height="25px" />
5
+ <span>/core-utils</span>
6
+ </div>
7
+ </h1>
8
+ <p>Seamless integration utilities for <a href="https://cms.theasteroid.tech">Asteroid CMS</a> — a single React provider, Apollo client, content hooks, media helpers, and a rich-text renderer.</p>
9
+ </div>
10
+
11
+ - **Provider-driven** — configure `cmsUrl`, `apiKey`, and Apollo behavior in one place
12
+ - **API-key auth only** — sends `x-api-key` on every request, nothing else
13
+ - **Typed hooks** — `useCmsContent` / `useCmsMutate` build GraphQL on the fly from a declarative selection
14
+ - **Tree-shakeable** — ESM + CJS + types, `@apollo/client`/`react` as peer deps
15
+
16
+ ---
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install @asteroidcms/core-utils @apollo/client graphql react react-dom
22
+ ```
23
+
24
+ ```bash
25
+ npm install @apollo/client-integration-nextjs # for nextjs (optional)
26
+ ```
27
+
28
+ `@apollo/client`, `graphql`, `react`, and `react-dom` are peer dependencies.
29
+
30
+ ---
31
+
32
+ ## Quick start
33
+
34
+ Wrap your app once:
35
+
36
+ ```tsx
37
+ import { AsteroidCMSProvider } from "@asteroidcms/core-utils";
38
+
39
+ export function Root() {
40
+ return (
41
+ <AsteroidCMSProvider
42
+ cmsUrl="https://cms-api.example.com"
43
+ apiKey={import.meta.env.VITE_CMS_API_KEY}
44
+ >
45
+ <App />
46
+ </AsteroidCMSProvider>
47
+ );
48
+ }
49
+ ```
50
+
51
+ Then use the hooks anywhere:
52
+
53
+ ```tsx
54
+ import { useCmsContent, useCmsImage } from "@asteroidcms/core-utils";
55
+
56
+ function NewsList() {
57
+ const cmsImage = useCmsImage();
58
+ const { data, loading } = useCmsContent<Article[]>({
59
+ schema_slug: "news",
60
+ limit: 10,
61
+ status: "PUBLISHED",
62
+ select: ["title", "slug", "publish_date", "cover_image"],
63
+ });
64
+
65
+ if (loading) return <p>Loading…</p>;
66
+ return (
67
+ <ul>
68
+ {data?.map((a) => (
69
+ <li key={a.slug}>
70
+ <img src={cmsImage(a.cover_image)} alt="" />
71
+ <a href={`/news/${a.slug}`}>{a.title}</a>
72
+ </li>
73
+ ))}
74
+ </ul>
75
+ );
76
+ }
77
+ ```
78
+
79
+ ---
80
+
81
+ ## `<AsteroidCMSProvider>`
82
+
83
+ | Prop | Type | Required | Default | Description |
84
+ | --------------- | ------------------------------ | -------- | ------------------ | ------------------------------------------------------------ |
85
+ | `cmsUrl` | `string` | ✓ | — | Base URL of the Asteroid CMS API. |
86
+ | `apiKey` | `string` | ✓ | — | Sent on every request as the `x-api-key` header. |
87
+ | `graphqlPath` | `string` | | `/graphql` | Path appended to `cmsUrl` for the GraphQL endpoint. |
88
+ | `mediaPath` | `string` | | `/media/canonical` | Path used by `cmsImage` / `useCmsImage`. |
89
+ | `headers` | `Record<string, string>` | | `{}` | Extra headers merged onto every GraphQL request. |
90
+ | `onError` | `(error: unknown) => void` | | — | Called for each GraphQL / network / protocol error. |
91
+ | `cacheConfig` | `InMemoryCacheConfig` | | — | Forwarded to `new InMemoryCache(...)` — e.g. `typePolicies`. |
92
+ | `apolloOptions` | `Partial<ApolloClientOptions>` | | — | Escape hatch — overrides any field on the Apollo client. |
93
+ | `client` | `ApolloClient` | | — | Bring your own pre-built client; skips the internal factory. |
94
+
95
+ Example with everything wired:
96
+
97
+ ```tsx
98
+ <AsteroidCMSProvider
99
+ cmsUrl="https://cms-api.example.com"
100
+ apiKey={process.env.NEXT_PUBLIC_CMS_API_KEY!}
101
+ headers={{ "x-tenant": "acme" }}
102
+ onError={(err) => toast.error(String((err as Error).message ?? err))}
103
+ cacheConfig={{
104
+ typePolicies: {
105
+ Query: {
106
+ fields: { contentEntries: { keyArgs: ["schema_slug", "filter"] } },
107
+ },
108
+ },
109
+ }}
110
+ >
111
+ <App />
112
+ </AsteroidCMSProvider>
113
+ ```
114
+
115
+ ---
116
+
117
+ ## `useCmsContent`
118
+
119
+ React hook for **querying** content. Builds a GraphQL document from a declarative selection.
120
+
121
+ ```ts
122
+ const { data, loading, error, refetch } = useCmsContent<T>({
123
+ schema_slug, // required
124
+ entrySlug, // when set → single entry, otherwise list
125
+ select, // fields / nested references
126
+ fullData, // include raw `data` object
127
+ limit,
128
+ offset, // list pagination
129
+ status, // "DRAFT" | "PUBLISHED" | "ARCHIVED"
130
+ filter, // { category: "politics", region: "bagmati" }
131
+ search, // [{ field: "title", value: "gagan", mode: "i" }]
132
+ });
133
+ ```
134
+
135
+ ### Single entry
136
+
137
+ ```tsx
138
+ const { data: article } = useCmsContent({
139
+ schema_slug: "news",
140
+ entrySlug: "police-launch-probe",
141
+ fullData: true,
142
+ });
143
+ ```
144
+
145
+ ### Paginated + filtered list
146
+
147
+ ```tsx
148
+ const { data: politics } = useCmsContent({
149
+ schema_slug: "news",
150
+ limit: 20,
151
+ offset: 0,
152
+ status: "PUBLISHED",
153
+ filter: { category: "politics" },
154
+ select: ["title", "slug", "publish_date"],
155
+ });
156
+ ```
157
+
158
+ ### Regex search
159
+
160
+ ```tsx
161
+ const { data: matches } = useCmsContent({
162
+ schema_slug: "news",
163
+ search: [{ field: "title", value: "gagan", mode: "i" }],
164
+ select: ["title", "slug"],
165
+ });
166
+ ```
167
+
168
+ ### Deeply nested references with aliasing
169
+
170
+ ```tsx
171
+ const { data: topStories } = useCmsContent({
172
+ schema_slug: "top_stories",
173
+ select: [
174
+ { field: "slug", as: "id" },
175
+ "order",
176
+ {
177
+ field: "news",
178
+ single: true,
179
+ as: "featuredNews",
180
+ select: [
181
+ "title",
182
+ "publish_date",
183
+ {
184
+ field: "category",
185
+ single: true,
186
+ select: [{ field: "title", as: "categoryName" }],
187
+ },
188
+ { field: "author", single: true, select: ["name", "avatar"] },
189
+ ],
190
+ },
191
+ ],
192
+ });
193
+ // → topStories[0].featuredNews.categoryName
194
+ ```
195
+
196
+ ---
197
+
198
+ ## `useCmsMutate`
199
+
200
+ `create` / `update` / `delete` against a schema, with the same selection syntax.
201
+
202
+ ### Create
203
+
204
+ ```tsx
205
+ const { mutate: subscribe } = useCmsMutate({
206
+ schema_slug: "news_letter_response",
207
+ mutationType: "create",
208
+ });
209
+
210
+ subscribe({
211
+ variables: { data: { email: "a@b.com", name: "Abhishek" } },
212
+ });
213
+ ```
214
+
215
+ ### Update
216
+
217
+ ```tsx
218
+ const { mutate: updateArticle } = useCmsMutate({
219
+ schema_slug: "news",
220
+ mutationType: "update",
221
+ entryId: "abc123",
222
+ select: ["title", "slug"],
223
+ });
224
+
225
+ updateArticle({ variables: { data: { title: "New title" } } });
226
+ ```
227
+
228
+ ### Delete
229
+
230
+ ```tsx
231
+ const { mutate: removeComment } = useCmsMutate({
232
+ schema_slug: "comment",
233
+ mutationType: "delete",
234
+ entryId: "xyz789",
235
+ });
236
+
237
+ removeComment();
238
+ ```
239
+
240
+ ---
241
+
242
+ ## `cmsImage` / `useCmsImage`
243
+
244
+ Build a canonical media URL for an asset id.
245
+
246
+ ```tsx
247
+ // Inside React — preferred
248
+ const cmsImage = useCmsImage();
249
+ <img src={cmsImage(article.cover_image)} alt="" />;
250
+
251
+ // Outside React (loaders, scripts, SSR)
252
+ import { cmsImage } from "@asteroidcms/core-utils";
253
+ cmsImage(id, { cmsUrl: "https://cms-api.example.com" });
254
+ ```
255
+
256
+ ---
257
+
258
+ ## `<RichTextContent>`
259
+
260
+ Render Asteroid CMS rich-text JSON/HTML with syntax-highlighted code blocks (via `highlight.js`).
261
+
262
+ ```tsx
263
+ import { RichTextContent } from "@asteroidcms/core-utils";
264
+
265
+ <RichTextContent
266
+ content={article.body}
267
+ classMap={{ p: "my-2 leading-relaxed", h2: "text-2xl font-bold" }}
268
+ />;
269
+ ```
270
+
271
+ Or use the parser directly:
272
+
273
+ ```ts
274
+ import { parseRichText } from "@asteroidcms/core-utils";
275
+
276
+ const html = parseRichText(article.body, {
277
+ classMap: {
278
+ /* ... */
279
+ },
280
+ });
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Advanced — bring your own Apollo client
286
+
287
+ ```tsx
288
+ import { ApolloClient, InMemoryCache } from "@apollo/client";
289
+ import { AsteroidCMSProvider } from "@asteroidcms/core-utils";
290
+
291
+ const client = new ApolloClient({ uri: "...", cache: new InMemoryCache() });
292
+
293
+ <AsteroidCMSProvider cmsUrl="..." apiKey="..." client={client}>
294
+ <App />
295
+ </AsteroidCMSProvider>;
296
+ ```
297
+
298
+ Or build the client yourself with the same factory used internally:
299
+
300
+ ```ts
301
+ import { createApolloClient } from "@asteroidcms/core-utils";
302
+
303
+ const client = createApolloClient({
304
+ cmsUrl: "https://cms-api.example.com",
305
+ apiKey: "...",
306
+ });
307
+ ```
308
+
309
+ ---
310
+
311
+ ## Development
312
+
313
+ ```bash
314
+ npm install
315
+ npm run typecheck
316
+ npm run build # writes dist/index.js, dist/index.cjs, dist/index.d.ts
317
+ ```
318
+
319
+ ---
320
+
321
+ ## License
322
+
323
+ Proprietary — Copyright © Asteroid. All rights reserved.
324
+
325
+ This package is licensed for use only; copying, modifying, or
326
+ redistributing the source — in whole or in part — is not permitted.
327
+ See [LICENSE](./LICENSE) for the full terms.