@craftware/crafty-sdk 0.6.0 → 0.8.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/README.md +47 -3
- package/dist/client.d.ts +5 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -0
- package/dist/client.js.map +1 -1
- package/dist/crafty-visual-editing.global.js +1 -1
- package/dist/crafty-visual-editing.global.js.map +1 -1
- package/dist/http.d.ts +8 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +50 -11
- package/dist/http.js.map +1 -1
- package/dist/image.d.ts +27 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +110 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/assistant.d.ts +30 -0
- package/dist/resources/assistant.d.ts.map +1 -0
- package/dist/resources/assistant.js +51 -0
- package/dist/resources/assistant.js.map +1 -0
- package/dist/resources/entries.js +2 -2
- package/dist/resources/entries.js.map +1 -1
- package/dist/resources/forms.d.ts.map +1 -1
- package/dist/resources/forms.js +4 -4
- package/dist/resources/forms.js.map +1 -1
- package/dist/resources/pages.js +2 -2
- package/dist/resources/pages.js.map +1 -1
- package/dist/types/public.d.ts +101 -1
- package/dist/types/public.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/crafty-cms/SKILL.md +40 -7
- package/skills/crafty-cms/references/mcp-configuration.md +23 -7
- package/skills/crafty-cms/references/migrate-to-crafty.md +85 -0
- package/skills/crafty-cms/references/sdk-development.md +59 -7
|
@@ -23,7 +23,7 @@ const crafty = createCraftyClient({
|
|
|
23
23
|
});
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
`createCraftyClient(config)` returns a plain object with six resources (`collections`, `entries`, `pages`, `forms`, `mediaCollections`, `mediaFiles`) and the visual editor helpers `attrs`, `attrsHtml` and `previewFromRequest`.
|
|
26
|
+
`createCraftyClient(config)` returns a plain object with six resources (`collections`, `entries`, `pages`, `forms`, `mediaCollections`, `mediaFiles`), the image helpers `image` and `srcset`, and the visual editor helpers `attrs`, `attrsHtml` and `previewFromRequest`.
|
|
27
27
|
|
|
28
28
|
### Config options
|
|
29
29
|
|
|
@@ -70,7 +70,7 @@ crafty.entries.list(collectionSlug, query?, options?) // => { data: CraftyEntry
|
|
|
70
70
|
crafty.entries.get(collectionSlug, id, options?) // => { data: CraftyEntry }
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
`query`: `sort_by`, `sort_order` (`asc`/`desc`), `per_page`, `page`, plus any extra key/value filters passed through to the API.
|
|
73
|
+
`query`: `q` (text search over the collection's text fields), `sort_by`, `sort_order` (`asc`/`desc`), `per_page`, `page`, plus any extra key/value filters passed through to the API.
|
|
74
74
|
|
|
75
75
|
### `pages`
|
|
76
76
|
|
|
@@ -113,7 +113,7 @@ const cta = landing.requireBlock("cta"); // throws plain Error if abs
|
|
|
113
113
|
| `boolean` | boolean |
|
|
114
114
|
| `stringList` | `string[]` (e.g. `["AI", "Cloud"]`) |
|
|
115
115
|
| `repeater` | array of objects; every declared subfield is present, `null` when empty (e.g. `[{ icon, title, text, tags: string[] }]`) |
|
|
116
|
-
| `reference` | collection reference: entry summary `{ id, slug, label }` or `null`; media reference (`model: "Media"`): the media id, plus `<key>_media` with `{ id, url, file_name, mime_type, size, is_public }` or `null` |
|
|
116
|
+
| `reference` | collection reference: entry summary `{ id, slug, label }` or `null`; media reference (`model: "Media"`): the media id, plus `<key>_media` with `{ id, url, file_name, mime_type, size, is_public, image_transform_base }` or `null` |
|
|
117
117
|
| `multiReference` | collection: array of entries; media (`model: "Media"`): array of media ids, plus `<key>_media` with the files in the same order (missing files are skipped) |
|
|
118
118
|
|
|
119
119
|
Entries expose media references the same way (`entry.image` is the id, `entry.image_media.url` the file).
|
|
@@ -124,21 +124,72 @@ const items = solutions?.data.items ?? [];
|
|
|
124
124
|
items.map((item) => `${item.title}: ${item.tags.join(", ")}`);
|
|
125
125
|
|
|
126
126
|
const hero = home.block("hero");
|
|
127
|
-
const heroUrl = hero?.data.image_media
|
|
127
|
+
const heroUrl = crafty.image(hero?.data.image_media, { width: 1600, format: "auto" }); // reference, model "Media"
|
|
128
128
|
const slides = hero?.data.gallery_media ?? []; // multiReference, model "Media"
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
+
### Images: always go through `crafty.image()`
|
|
132
|
+
|
|
133
|
+
Render every Crafty image with `crafty.image(media, { width, format: "auto" })` (and `crafty.srcset(media, widths, { format: "auto" })` for responsive images) instead of the raw `url`. Sites that ship the original upload fail Core Web Vitals (LCP).
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
const photo = block.data.image_media;
|
|
137
|
+
|
|
138
|
+
<img
|
|
139
|
+
src={crafty.image(photo, { width: 800, format: "auto" })}
|
|
140
|
+
srcSet={crafty.srcset(photo, [400, 800, 1200, 1600], { format: "auto" })}
|
|
141
|
+
sizes="(min-width: 768px) 50vw, 100vw"
|
|
142
|
+
width={800}
|
|
143
|
+
height={450}
|
|
144
|
+
loading="lazy"
|
|
145
|
+
alt={block.data.title ?? ""}
|
|
146
|
+
/>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- `crafty.image(media, { width?, height?, fit?, quality?, format? })` → string. `media` is a `<key>_media` object, a `CraftyMediaFile`, or a URL string. `width`/`height` 1–4096, `quality` 1–100, `fit`: `scale-down` | `contain` | `cover` | `crop` | `pad`, `format`: `auto` | `webp` | `avif` | `jpeg` | `png`. Invalid options are dropped.
|
|
150
|
+
- `crafty.srcset(media, widths, options?)` → `"<url> 400w, <url> 800w"`, or `""` when the image cannot be transformed.
|
|
151
|
+
- Pick `width` from the rendered size (about 2x for retina on small images); use `fit: "cover"` with both `width` and `height` for fixed-ratio cards and thumbnails.
|
|
152
|
+
- Transforms only apply to **public** raster images when the Crafty server has Cloudflare Image Transformations enabled (the media then carries `image_transform_base`). Otherwise `image()` returns the original `url` and `srcset()` returns `""`, so the same code always works. Upload images to a public repository (`manage_media_collection` with `is_public: true`) when the site needs permanent, resizable URLs.
|
|
153
|
+
- Use `og:image` with `crafty.image(media, { width: 1200, height: 630, fit: "cover", format: "jpeg" })`: social scrapers do not support AVIF/WebP negotiation.
|
|
154
|
+
- Do not hand-build `/cdn-cgi/image/...` URLs or append `?w=` query parameters.
|
|
155
|
+
|
|
131
156
|
Prefer modelling lists as `repeater` / `stringList` fields on a block type over collections or `json` fields; see the `crafty-cms` skill.
|
|
132
157
|
|
|
158
|
+
### Generated types (`crafty types`)
|
|
159
|
+
|
|
160
|
+
Do not guess field keys or shapes: generate them from the site's real content models with the CLI.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npx @craftware/crafty-cli types # CRAFTY_API_URL + CRAFTY_API_KEY from .env
|
|
164
|
+
npx @craftware/crafty-cli types --api-key=$KEY --url=https://<tenant>.crafty.do --out=src/crafty.types.ts
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
It reads `GET /api/v1/schema` (JSON Schema of every collection and block type of the API key's environment, versioned by `format_version`) and writes `crafty.types.ts`:
|
|
168
|
+
|
|
169
|
+
- `<Slug>Entry` per collection (entry metadata + fields), `<Slug>BlockData` and `<Slug>Block` per block type, the `CraftyPageBlock` union (discriminated by `type`), the `CraftyCollectionTypes` / `CraftyBlockDataTypes` slug maps, and `CraftyEntryReference` / `CraftyMediaSummary`.
|
|
170
|
+
- `select` → union of option values, `stringList` → `string[]`, `repeater` → array of item objects, `json` → `unknown`; optional fields are `| null`.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import type { CarsEntry, CraftyPageBlock } from "./crafty.types";
|
|
174
|
+
|
|
175
|
+
const cars = (await crafty.entries.list("cars")).data as unknown as CarsEntry[];
|
|
176
|
+
const page = await crafty.pages.get("home");
|
|
177
|
+
for (const block of page.blocks as unknown as CraftyPageBlock[]) {
|
|
178
|
+
if (block.type === "hero") block.data.heading; // HeroBlockData
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Types depend on the environment (generate with the key of the environment the code targets) and go stale when a collection or block type changes — re-run the command after `create_block_type`, `update_block_type`, `create_collection` or `update_collection`.
|
|
183
|
+
|
|
133
184
|
### `forms`
|
|
134
185
|
|
|
135
186
|
```ts
|
|
136
187
|
crafty.forms.list(options?) // => { data: CraftyForm[] } (requires apiKey)
|
|
137
188
|
crafty.forms.get(slug, options?) // => { data: CraftyForm } (requires apiKey)
|
|
138
|
-
crafty.forms.submit(formSlug, data, options?) // => { id, form_id, submitted_at } (public,
|
|
189
|
+
crafty.forms.submit(formSlug, data, options?) // => { id, form_id, submitted_at } (public; apiKey optional, picks the environment)
|
|
139
190
|
```
|
|
140
191
|
|
|
141
|
-
`forms.submit()` uses a **public** endpoint:
|
|
192
|
+
`forms.submit()` uses a **public** endpoint: the `X-API-Key`, when configured, only selects the environment (a staging key submits to staging, no key submits to production), but it **requires `siteSlug` in the client config** — calling it without one throws a synchronous `CraftyError` with code `BAD_REQUEST`.
|
|
142
193
|
|
|
143
194
|
```ts
|
|
144
195
|
const crafty = createCraftyClient({
|
|
@@ -170,7 +221,7 @@ crafty.mediaCollections.list(query?, options?) // => { data: CraftyMediaCollect
|
|
|
170
221
|
crafty.mediaFiles.list(query?, options?) // => { data: CraftyMediaFile[], links, meta: { ...pagination, ttl_minutes } }
|
|
171
222
|
```
|
|
172
223
|
|
|
173
|
-
`query`: `collection`, `directory_id`, `ttl_minutes` (signed-URL lifetime), `per_page`, `page`.
|
|
224
|
+
`query`: `collection`, `directory_id`, `ttl_minutes` (signed-URL lifetime), `per_page`, `page`. Each file has `url`, `is_public`, `expires_at` and `image_transform_base`; pass the file to `crafty.image()` to render it.
|
|
174
225
|
|
|
175
226
|
## `llms.txt`
|
|
176
227
|
|
|
@@ -284,6 +335,7 @@ try {
|
|
|
284
335
|
|
|
285
336
|
- `attrs(target, field, { label? })` / `crafty.attrs(...)` → `{ "data-crafty-block" | "data-crafty-entry", "data-crafty-field", "data-crafty-label"? }` to spread on the element that renders the value; `{}` when the target has no valid id. `field` is a field key or a path into a list or JSON value (`sections[3].title`, `items[2].title` for a block-type `repeater`, `tags[1]` for a `stringList`, `items[0].tags[1]` for a stringList inside a repeater item). The index is the item's position in the stored data.
|
|
286
337
|
- `attrsHtml(target, field, { label? })` → the same attributes as an escaped HTML string.
|
|
338
|
+
- `image(media, options?)` / `crafty.image(...)` and `srcset(media, widths, options?)` / `crafty.srcset(...)` → resized image URLs; see "Images: always go through `crafty.image()`".
|
|
287
339
|
- `previewFromRequest(req)` → preview token of an incoming request (URL string, `URL`, Web `Request`, Node `req.url`, Express `req.query`) or `null`.
|
|
288
340
|
- `readPreviewToken()` → token in the browser, or `null`.
|
|
289
341
|
- `enableVisualEditing(options)` from `@craftware/crafty-sdk/visual-editing` → connects the page to the editor; no-op outside the admin iframe. Options: `onRefresh`, `adminOrigins`, `page`, `entries`, `touchHighlightMs`.
|