@capacms/sdk 1.0.0-next.4 → 1.0.0-next.6

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 (68) hide show
  1. package/CHANGELOG.md +323 -0
  2. package/README.md +1043 -186
  3. package/bin/capa-codegen.js +192 -5
  4. package/bin/capa.js +208 -0
  5. package/bin/graphql-project.js +142 -0
  6. package/bin/project-env.js +58 -0
  7. package/dist/client.d.ts +5 -0
  8. package/dist/client.js +17 -0
  9. package/dist/codegen.d.ts +55 -0
  10. package/dist/codegen.js +320 -39
  11. package/dist/config.d.ts +5 -1
  12. package/dist/graphql-codegen.d.ts +117 -0
  13. package/dist/graphql-codegen.js +705 -0
  14. package/dist/http.js +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +2 -1
  17. package/dist/next/attrs.d.ts +51 -12
  18. package/dist/next/attrs.js +74 -20
  19. package/dist/next/client.d.ts +111 -38
  20. package/dist/next/client.js +116 -82
  21. package/dist/next/entry-fields.d.ts +162 -0
  22. package/dist/next/entry-fields.js +2 -0
  23. package/dist/next/errors.d.ts +136 -0
  24. package/dist/next/errors.js +214 -0
  25. package/dist/next/field-names.d.ts +37 -0
  26. package/dist/next/field-names.js +145 -0
  27. package/dist/next/graphql/build.d.ts +27 -0
  28. package/dist/next/graphql/build.js +98 -0
  29. package/dist/next/graphql/documents.d.ts +67 -0
  30. package/dist/next/graphql/documents.js +35 -0
  31. package/dist/next/graphql/edit-mode.d.ts +16 -0
  32. package/dist/next/graphql/edit-mode.js +93 -0
  33. package/dist/next/graphql/filter-values.d.ts +34 -0
  34. package/dist/next/graphql/filter-values.js +96 -0
  35. package/dist/next/graphql/introspection.d.ts +89 -0
  36. package/dist/next/graphql/introspection.js +102 -0
  37. package/dist/next/graphql/plan.d.ts +115 -0
  38. package/dist/next/graphql/plan.js +531 -0
  39. package/dist/next/graphql/request.d.ts +228 -0
  40. package/dist/next/graphql/request.js +283 -0
  41. package/dist/next/graphql/rest.d.ts +66 -0
  42. package/dist/next/graphql/rest.js +502 -0
  43. package/dist/next/graphql/selection.d.ts +55 -0
  44. package/dist/next/graphql/selection.js +212 -0
  45. package/dist/next/graphql/sha256.d.ts +13 -0
  46. package/dist/next/graphql/sha256.js +86 -0
  47. package/dist/next/graphql/summary.d.ts +83 -0
  48. package/dist/next/graphql/summary.js +151 -0
  49. package/dist/next/graphql/tree-layout.d.ts +36 -0
  50. package/dist/next/graphql/tree-layout.js +20 -0
  51. package/dist/next/graphql/tree.d.ts +171 -0
  52. package/dist/next/graphql/tree.js +249 -0
  53. package/dist/next/graphql/typed.d.ts +261 -0
  54. package/dist/next/graphql/typed.js +146 -0
  55. package/dist/next/index.d.ts +28 -5
  56. package/dist/next/index.js +25 -1
  57. package/dist/next/inflate.d.ts +25 -7
  58. package/dist/next/inflate.js +46 -32
  59. package/dist/next/key-family.d.ts +34 -0
  60. package/dist/next/key-family.js +74 -0
  61. package/dist/next/select-types.d.ts +44 -8
  62. package/dist/next/system-keys.d.ts +27 -0
  63. package/dist/next/system-keys.js +42 -0
  64. package/dist/nextjs/index.d.ts +165 -12
  65. package/dist/nextjs/index.js +247 -23
  66. package/dist/nextjs/overlay.d.ts +5 -0
  67. package/dist/nextjs/overlay.js +35 -0
  68. package/package.json +35 -13
package/CHANGELOG.md ADDED
@@ -0,0 +1,323 @@
1
+ # @capacms/sdk changelog
2
+
3
+ ## Unreleased
4
+
5
+ - 1.0.0-next.6. GraphQL, on `@capacms/sdk/next` and `@capacms/sdk/nextjs`,
6
+ and two commands, `capa-codegen --graphql` and `capa persist`. Additive:
7
+ every call that existed behaves as before. `graphql` is an optional peer
8
+ dependency that only the two commands load. The package's types now need
9
+ TypeScript 5.0 or later, with `strict` on or off.
10
+
11
+ Calls and errors. `client.graphql(document, variables?, options?)` runs a
12
+ query against `/api/graphql` and resolves with
13
+ `{ data, errors, extensions, cacheTags }` once the API has run it, even
14
+ when `errors` is not empty: a root field that failed is `null` and the
15
+ others keep their data. A request the API refuses as a whole, `errors` and
16
+ no `data`, throws `CapaError` whatever its status: a 4xx, or the 200 that
17
+ GraphQL over HTTP sends on `application/json`, thrown with `status` 400.
18
+ 401, 402, 403, 404, 405 and 429 keep their own. `CapaError` carries every
19
+ error of the refusal in `graphqlErrors`. Each error is a
20
+ `CapaGraphQLError`, a plain object: `message`, `locations`, `path` and
21
+ `extensions` as the spec writes them, with `code`, `hint`, `docs`, `param`
22
+ and `type` lifted out, so `Response.json(result)` and a client component's
23
+ props keep every field. `isCapaGraphQLError` checks one by its shape.
24
+ Where GraphQL is switched off (`CAPA_API_GRAPHQL=off`) the call throws
25
+ "This Capa deployment does not serve GraphQL." with a hint to read over
26
+ REST meanwhile. `extensions.cost` (`requestedQueryCost`,
27
+ `actualQueryCost`, and `budget`, whose `counted` is what the 5,000-entry
28
+ limit checks) and `extensions.deprecations` (`{ coordinate, reason }` for
29
+ each deprecated member a document used) are typed.
30
+
31
+ Sending. A read is a GET whenever its URL fits the API's 8,192-byte limit,
32
+ so a production key's read is cached by the CDN and the API with no option
33
+ set, and a POST when it does not; no document is refused for its length.
34
+ `method: "POST"` always sends a POST. A host that serves GraphQL by POST
35
+ only (the admin host) answers a GET as a path it does not serve; the read
36
+ is repeated as a POST, and that host is read by POST for five minutes. A
37
+ 429 (over the API's read limits: 4 running per project, 3 of them per
38
+ client, and 64 waiting per client or 256 per key, with `/api/entries`
39
+ reads counted in the same limits) is sent again after its `Retry-After`
40
+ plus up to 250 ms, up to 3 times; `retries: 0` throws it, and a thrown 429
41
+ carries `retryAfter` in seconds, on REST calls too. `createClient` takes the legacy key a site holds for
42
+ every read, `pk_`, `sk_` or unprefixed as older tenants were minted, and
43
+ warns once per process without printing any of it; `preview()` and draft
44
+ reads (`draftClient`, `CAPA_DRAFT_KEY`) take a `cap_` key only.
45
+
46
+ Persisted queries. `persisted: true` sends the document's sha256 as a
47
+ cacheable GET (a POST when the variables are too long for a URL), and on
48
+ `PersistedQueryNotFound` one POST with the document. The API stores a
49
+ document only for a development key; a hash a host declined is
50
+ remembered for five minutes and sent by POST meanwhile. `capa persist`
51
+ registers a project's documents at build time, with the development key
52
+ in `CAPA_DRAFT_KEY`, on `CAPA_API_URL` (`CAPA_ADMIN_URL` for a self-hosted
53
+ stack whose read host stores nothing), and refuses a production key
54
+ before it sends anything. Every document is pinned (`Capa-Persist: pin`),
55
+ so documents registered in the Explorer or a preview never evict it, and
56
+ one stored without its pin fails the run. A refusal is reported with its
57
+ code. `--release` and `--env` name the build its pins belong to
58
+ (`Capa-Persist: pin; release=<id>; env=<env>`), read from Vercel's and
59
+ Netlify's build variables when not given, so the API keeps the latest
60
+ production releases' documents before a preview's.
61
+
62
+ Typed documents. `capa-codegen --graphql` checks the project's `.graphql`
63
+ files and its `#graphql`, `/* capa */` and gql`` literals against the
64
+ key's schema, and writes one module: the schema's types, `CapaQuery`, a
65
+ `TypedDocument` per operation with `<Name>Models`, the models it reads,
66
+ beside it, a `<Name>Fragment` per fragment and `capaTreeLayout`.
67
+ `tagsFor({ namespace: <Name>Models })` tags a Next.js read with every model
68
+ its query reads, relations and filters through them included, so no model
69
+ is left out by hand, and with `capa:media`, which `revalidateFromWebhook`
70
+ revalidates when a file in the media library is edited. `client.graphql(doc, vars)` then infers data and
71
+ variables with no cast, a literal is typed by its own text, and a
72
+ document's required variables are required in the call. A literal codegen
73
+ has not read yet does not compile (`RunCapaCodegen`). A fragment in a
74
+ literal of its own, spread with `${FRAGMENT}` into a query kept
75
+ `as const`, works as in Hydrogen. A field, argument or value a later
76
+ `Capa-Version` phases out is `@deprecated`, and each use is printed with
77
+ its file, line and reason. `--watch`, `--check`, `--save-schema` and
78
+ `--schema` are supported. Both commands read `CAPA_API_URL` and `CAPA_KEY`
79
+ (`CAPA_BASE_URL` and `CAPA_API_KEY` still work) from the shell or the
80
+ project's `.env` files, as `next dev` does.
81
+
82
+ REST types. `capa-codegen` without `--graphql` takes a `cap_` key: it
83
+ writes the model interfaces the `/v2` schema gives a legacy key from the
84
+ key's GraphQL schema, keyed by namespace, with no tenant id, and from a
85
+ saved schema with `--schema`. Every field is optional there, an enum is a
86
+ `string` and a field GraphQL leaves out is `unknown`, since GraphQL does
87
+ not say more; no `CAPA_SCHEMA_CHECKSUM` is written. Whichever key reads
88
+ it, the module now parses for any namespace: a field that is not an
89
+ identifier is quoted (`"am/pm_indicator"?: string;`), a model whose
90
+ PascalCase name is not one is named as GraphQL names it (`2024_events` is
91
+ `_2024Events`, its references and `Select` and `Attrs` aliases too), and an
92
+ enum's values are escaped. A read's `fields` is typed as the API returns
93
+ it: `entries.list<Articles, typeof select>` types exactly the fields the
94
+ select names, each always there and `null` when it was never filled, an
95
+ expanded relation as the entry (with `fields`) or
96
+ `{ id, model, missing: true }`, one it does not expand as `{ id, model }`,
97
+ a relation list as `{ items, pageInfo }` and media as
98
+ `{ id, url, alt, type, width, height }` (`EntryFields`). A select the
99
+ compiler cannot read, a string or one typed `Select<T>`, types every field
100
+ and each relation as any of the three. A flat read's relations are
101
+ references, `included` holds partial entries, and `inflate` returns the
102
+ tree read's type. BREAKING for code that compiled against the stored
103
+ shape (`fields.author.name`, `fields.coauthors[0]`), which read
104
+ `undefined` or threw at run time. A relation may be named alone in a
105
+ typed select, as a reference.
106
+
107
+ The typed builder. `client.graphql.query(selection)` builds the document
108
+ from an object and, with `createClient<CapaQuery>()`, types the result from
109
+ exactly what was selected. A misspelled field, argument, filter field or
110
+ operator, an argument of the wrong type, a sort value outside the enum
111
+ and a missing required argument (`article` without `args: { id }`) do not
112
+ compile, and the compiler's error names the key and where it was written
113
+ (`SelectionError<"titel is not a field of articles.nodes">`).
114
+ `NodeOf<CapaQuery, typeof selection, "articles">` is one entry of a read,
115
+ for a component's props, and `QueryResult` the whole of its `data`. An alias reads a field again in the same request:
116
+ `{ latest: { __aliasFor: "articles", args, nodes } }` is sent as
117
+ `latest: articles(...)`, and typed and checked as `articles`. A `Date` in
118
+ `args` is sent as its ISO text. `selectionToDocument` returns the text a
119
+ selection sends. It takes no `persisted`, and throws a `TypeError` for it
120
+ before any request: its document is printed when it runs, so `capa persist`
121
+ never stored it, and it is already a cacheable GET.
122
+
123
+ Paging, tags and descriptions. The builders page back as the API does:
124
+ REST's `before` with a `limit` is sent as `last` with `before`, and
125
+ `before=end` as `last` alone, the end of the list. A model's filter takes
126
+ `_tags`, as REST's `where` takes `$tags`, with the operators the key's
127
+ schema declares for it. `graphqlSchema()`, the builders and codegen read a
128
+ field's `Capa field ...` tag from the last line of its description, after
129
+ the field's label, which is where the API writes it.
130
+
131
+ REST's shape. `toTree(data, selection, layout)` turns a builder result into
132
+ REST's `shape=tree` data for the same read, typed from the selection:
133
+ system keys beside `fields`, each field under its namespace, media values
134
+ in REST's public shape and order (`id`, `url`, `alt`, `type`, `width`,
135
+ `height`), and each entry's `model` from the layout. A relation list
136
+ without `pageInfo` selected is `{ items }`, and a missing item of one is
137
+ left out, where REST keeps a `{ id, model, missing: true }` slot. The layout
138
+ is the `capaTreeLayout` constant codegen writes, so a server reads no schema
139
+ for it, or a schema read with `client.graphqlSchema()`. A root alias
140
+ converts as the field it names; an alias below a root is refused, since
141
+ REST reads each field once. `selectToSelection` writes a REST read as a
142
+ builder selection, and `graphqlToSelect` takes a selection back to REST
143
+ as it takes a tool spec.
144
+
145
+ The tool spec. `buildGraphQLQuery`, `graphqlToSelect` and `selectToGraphQL`
146
+ move between the spec the Explorer and `@capa/mcp` share, GraphQL text and
147
+ the equal REST request, written exactly as the API writes it in
148
+ `extensions.capa.rest`: the filter as `where` JSON, and a system key a
149
+ field shadows as `$tags`, `$createdAt` or `author.$id`. They nest at most 4
150
+ relations below the root entry, which is the API's 5 levels of entries,
151
+ and send each filter value as the type its filter input declares, so
152
+ `has: "true"` on a list of true/false values goes as `true`. They check a
153
+ filter's names through `and`, `or`, `not` and one hop, refuse a system
154
+ field GraphQL does not filter (`_version`) and a hop through a relation the
155
+ key cannot read without naming the hidden model, and refuse a key a spec
156
+ does not take (`frist`) with the keys it does. `selectToGraphQL` selects
157
+ what REST returns: all six media fields, and for `*` or no select the
158
+ system keys, every field and each relation list as a connection of ids.
159
+ `inflate` and `Select<T>` read `$tags`, `$createdAt` and the other `$`
160
+ names as system keys (`SystemKey`). The schema summary lists the models
161
+ GraphQL leaves out in `restOnly`, and the builder refuses one by naming
162
+ its REST read, never as an unknown model with another model suggested.
163
+ A read of one entry pages a relation list from a cursor: `after` on the
164
+ relation's spec, `after:` in a select, `args.after` in a selection. A list
165
+ read refuses it, as the API does. `sort` takes one value or a list, at the
166
+ root too. A spec written as REST writes a read (`author.name`,
167
+ `author(name)`, `*`, a sort of `-publishedAt`) throws with what to write
168
+ instead.
169
+
170
+ Next.js. `graphql()` on `@capacms/sdk/nextjs` reads `CAPA_API_URL`,
171
+ `CAPA_KEY` and `CAPA_API_VERSION` (a setting in `config` wins), works out
172
+ draft and edit mode from `{ draftMode, headers }` as `getCapaClient` does,
173
+ and keeps a published read that answered with no `errors` in Next's data
174
+ cache, through `unstable_cache`, under its `tags`: until one of them is
175
+ revalidated, or for `revalidate` seconds. A read with no `tags` is tagged
176
+ `capa:graphql` (`GRAPHQL_TAG`), which `revalidateFromWebhook` revalidates
177
+ on every content and media event; `tagsFor({ namespace })` tags a read by
178
+ the models it reads. Drafts are read uncached, by GET. It is not
179
+ persisted by default. `draftClient`, `getCapaClient` and
180
+ `getPublishedClient` take `CapaQuery` like `createClient`, and
181
+ `getCapaClient` keeps its GraphQL reads, a document or the typed builder,
182
+ in Next's data cache the same way, under the `tags` and `revalidate` each
183
+ call gives.
184
+
185
+ The package root. `createClient` from `@capacms/sdk`, the legacy `/v2/api`
186
+ client, refuses a `cap_` key with a `TypeError` that names
187
+ `@capacms/sdk/next`, before it asks for a tenant id or sends anything,
188
+ where every read failed as a 401 "Invalid API key". Its `CapaError`
189
+ message joins the body with a colon. The README opens with which import is
190
+ which and links the API reference, and `homepage` is
191
+ https://docs.capacms.com/api.
192
+
193
+ Edit mode. A GraphQL read in edit mode is marked like a REST read: each
194
+ object that selected `id` and `model` is an entry, and
195
+ `capaAttrs(node, field)` and `fieldAttrs(node)` tag a field GraphQL renamed
196
+ by its namespace. To know which fields those are, the client reads the
197
+ key's type and field names once a minute, beside the page's read rather
198
+ than after it; a document that never says `model` reads none. `toTree`
199
+ keeps the mark, and `markGraphQLEntries` is exported. With next.5's
200
+ `FieldAttrs<T>`: `fieldAttrs` also takes a GraphQL node and, for a REST
201
+ entry typed with `Model`, still returns exactly `FieldAttrs<Model>`, so the
202
+ `<Model>Attrs` aliases codegen writes keep working. `TaggableField<E>` is
203
+ exported beside it.
204
+
205
+ - 1.0.0-next.5. `<CapaOverlay adminOrigins={[...]} />` from the new entry
206
+ point `@capacms/sdk/nextjs/overlay`: the live preview overlay as one Next.js
207
+ client component, refreshing with `router.refresh()` on save (M6). `react`
208
+ and `next` are optional peer dependencies, needed only for that entry. The
209
+ `FieldAttrs<T>` type is exported from `@capacms/sdk/next`, and `capa-codegen`
210
+ writes a `<Model>Attrs` alias beside each `<Model>Select`, so
211
+ `const a: ArticleAttrs = fieldAttrs(entry)` catches a wrong field name at
212
+ compile time (M5).
213
+ - 1.0.0-next.4. Edit mode. BREAKING for sites that call `capaAttrs(entry,
214
+ field)` with no third argument: it now tags only entries read in edit mode, so
215
+ a published page ships no `data-capa-` attributes. Create the client with
216
+ `editMode: true` (work it out with `editMode({ draftMode, headers })` from
217
+ `@capacms/sdk/nextjs`) and every entry it reads, related entries included, is
218
+ marked. Sites that pass a flag keep working unchanged. New in
219
+ `@capacms/sdk/nextjs`: `editMode`, `resolveEditRequest` for middleware (checks
220
+ a `capa-edit` token with Capa, strips a forged `x-capa-edit`, returns the
221
+ `private, no-store` Cache-Control to set), and the constants `EDIT_PARAM`,
222
+ `EDIT_HEADER`, `DRAFT_COOKIE`, `EDIT_CACHE_CONTROL`. New on the client: a
223
+ `path` option (config and per call) sent as `Capa-Path` beside `Capa-Page`, so
224
+ Capa can list the concrete URLs an entry appears on. `markEditEntries`,
225
+ `isEditEntry` and `CAPA_EDIT` are exported from `@capacms/sdk/next`.
226
+ Five-minute integration (M6) in `@capacms/sdk/nextjs`: `getCapaClient`
227
+ (keys and edit mode from env), `getPublishedClient`, `createPreviewRoute`,
228
+ `exitPreviewRoute` and `capaMiddleware` (preview links, the Published view,
229
+ edit mode and `no-store` in one line). Typed `fieldAttrs(entry).title` on
230
+ `@capacms/sdk/next` (M5): a wrong field name is a compile error.
231
+ Flat responses: `entries.list` and `entries.get` take `shape: "flat"`, which
232
+ sends `?shape=flat` and returns every relation as a `{ id, model }`
233
+ reference with each expanded entry once in `included`, typed by the select.
234
+ The result carries the select it sent. `inflate(result)` turns it back into
235
+ the tree result, deep-equal to a `shape=tree` read of the same request; it
236
+ returns copies and stops where the select stops, so cycles end. New types:
237
+ `FlatPage`, `FlatSingle`, `FlatListOptions`, `FlatGetOptions`, `Included`,
238
+ `ExpandedTargets`, `ResponseShape`, `FlatResponse`. Additive: a read without
239
+ `shape` sends the same URL and returns the same result as before.
240
+ - 1.0.0-next.3. Reads made from a layout are no longer charged to `/`.
241
+ `routeOf` returned the route a file sits at, so `app/layout.tsx` came out as
242
+ `/` and every Site singleton or nav read in a root layout was recorded as a
243
+ read of the home page, on every page of the site. `routeOf` now returns
244
+ `"(layout)"` (exported as `LAYOUT_PAGE` from `@capacms/sdk/next` and
245
+ `@capacms/sdk/nextjs`) for an app-router `layout.*` or `template.*`, and a
246
+ read whose page is `"(layout)"` sends no `Capa-Page`, even when the client was
247
+ created with a `page`. The return type is still `string`, so layout code that
248
+ already passes `routeOf(import.meta.url)` needs no change: upgrading fixes it.
249
+ No API change: the API already records nothing for a read without the
250
+ header. In the pages router, `pages/layout.tsx` is now the page `/layout`
251
+ rather than `/`.
252
+ - 1.0.0-next.2. The overlay reports `visible { entryId, field }`, the tagged
253
+ element at the centre of the viewport, while the page scrolls: at most every
254
+ 150ms and only when it changes (the topmost visible element at the very top
255
+ of a page, the bottommost at the very bottom). The Capa editor's "Follow the
256
+ page" scrolls the form to match. Additive and still protocol `v: 1`, so an older admin
257
+ ignores it and an older overlay simply never sends it. `pickCentred` and
258
+ `scrollEdge`, the pure choice behind it, and `visibleMessage` are exported
259
+ for tests.
260
+ - 1.0.0-next.1. Live preview. `capaAttrs(entry, field, enabled)` on
261
+ `@capacms/sdk/next` tags an element with the entry and field it renders,
262
+ typed so `field` is one of the entry's data keys. The new entry point
263
+ `@capacms/sdk/overlay` exports `startOverlay({ adminOrigins, onRefresh })`,
264
+ which, inside the Capa editor's preview frame, outlines the field being
265
+ edited, reports clicks on tagged elements back to the editor and re-renders
266
+ the draft after a save. It does nothing outside a frame and has no
267
+ dependencies. `acceptMessage` is exported for tests. See "Live preview" in
268
+ the README.
269
+ - `routeOf(import.meta.url)` now decodes the file URL. A real `import.meta.url`
270
+ percent-encodes brackets, so a dynamic route such as `app/blog/[slug]/page.tsx`
271
+ came out as `/blog/%5Bslug%5D` and every read with that `page` threw a
272
+ `TypeError`.
273
+ - The package is published as `@capacms/sdk`. The `capa` scope on npm was
274
+ already taken, so the org is `capacms`; entry points are `@capacms/sdk`
275
+ (legacy `/v2` client), `@capacms/sdk/next` and `@capacms/sdk/nextjs`.
276
+ Nothing else about the package changed. Earlier notes below that named
277
+ `@capa/sdk` were written before the first publish and mean this package.
278
+ - Added `page` to `CapaNextConfig` and to every call's options, sending the
279
+ `Capa-Page` header so Capa can report which of a site's pages read which
280
+ entries. Telemetry only: it does not change a response, a cache key or an
281
+ `ETag`. A malformed value throws a `TypeError`, because the API ignores a
282
+ header it cannot store and a typo should surface where it is written.
283
+ - Added `client.pages.list()` and `client.pages.get(page)` for the page list and
284
+ one page's detail. `list({ entry })` narrows to the pages that read one entry
285
+ and adds `entryReads` to each row; `get` returns `null` for an unknown page.
286
+ - Added `client.preview(token)`, which verifies a preview token minted by the
287
+ Capa admin and returns the claim, or `null` when the token is invalid or
288
+ expired. Every other failure throws.
289
+ - Added `schemaChecksum` to `CapaNextConfig`, sending the `Capa-Schema` header on
290
+ every call so Capa can tell a site built against the current models from one
291
+ built against an older set. Telemetry only, like `page`: it changes no
292
+ response, cache key or `ETag`. A malformed value throws a `TypeError`, because
293
+ a stamp the API drops in silence looks exactly like a site that is up to date.
294
+ - `capa-codegen` now writes `export const CAPA_SCHEMA_CHECKSUM` beside the
295
+ checksum comment it already wrote, so the value can be imported and handed to
296
+ `createClient`. That constant is the only new byte in the generated file.
297
+ - `client.pages.get(page)` now carries `insights`, the suggestions Capa draws
298
+ from that page's own reads (`overfetch`, `fanout`, `cache`, `drift`), each
299
+ with the numbers behind it and a copyable rewrite where there is one, and
300
+ every `queries[]` row carries `selection`, the parsed Selection IR of its
301
+ `select`, or `selectionError` when it no longer parses.
302
+ - Every `queries[]` row on `client.pages.get(page)` also carries `url`, the
303
+ request that group most often made, so a caller can print the real call with
304
+ its filters, sort and limit instead of reconstructing one from `select`. It is
305
+ `null` once the group has been folded into the daily rollup, which keeps
306
+ counts rather than requests. The detail itself gains `lastReadAt`.
307
+ - `client.pages.list()` now carries `meta.insights`, the tenant-wide `unused`
308
+ rows: entries no page has read in 30 days and nobody has edited in 90.
309
+ - Added `routeOf(file)`, `preview(token, client)` and `pagesFor(client)` to
310
+ `@capacms/sdk/nextjs`. `routeOf` turns a Next route file into a page string,
311
+ dropping route groups, parallel slots, leaf file names and extensions, and
312
+ throws rather than guessing for files Next does not route.
313
+
314
+ ## 1.0.0-next.0
315
+
316
+ - Added `@capacms/sdk/next`, the dependency-free `/api/` read client for `cap_`
317
+ keys, typed selects, cursor iteration, structured `/api/` errors, and
318
+ surrogate cache tags.
319
+ - Added `@capacms/sdk/nextjs` helpers for Next fetch caching, surrogate tag
320
+ construction, webhook revalidation, and server-only draft client selection.
321
+ - Kept the legacy `/v2/api` client as the root export.
322
+ - Added relation-aware `capa-codegen` output while preserving byte-identical
323
+ output for schemas without relations.