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

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 (69) hide show
  1. package/CHANGELOG.md +345 -0
  2. package/README.md +1069 -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 -36
  12. package/dist/config.js +47 -1
  13. package/dist/graphql-codegen.d.ts +117 -0
  14. package/dist/graphql-codegen.js +705 -0
  15. package/dist/http.js +1 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.js +2 -1
  18. package/dist/next/attrs.d.ts +51 -12
  19. package/dist/next/attrs.js +74 -20
  20. package/dist/next/client.d.ts +112 -38
  21. package/dist/next/client.js +131 -83
  22. package/dist/next/entry-fields.d.ts +162 -0
  23. package/dist/next/entry-fields.js +2 -0
  24. package/dist/next/errors.d.ts +136 -0
  25. package/dist/next/errors.js +214 -0
  26. package/dist/next/field-names.d.ts +37 -0
  27. package/dist/next/field-names.js +145 -0
  28. package/dist/next/graphql/build.d.ts +27 -0
  29. package/dist/next/graphql/build.js +98 -0
  30. package/dist/next/graphql/documents.d.ts +67 -0
  31. package/dist/next/graphql/documents.js +35 -0
  32. package/dist/next/graphql/edit-mode.d.ts +16 -0
  33. package/dist/next/graphql/edit-mode.js +93 -0
  34. package/dist/next/graphql/filter-values.d.ts +34 -0
  35. package/dist/next/graphql/filter-values.js +96 -0
  36. package/dist/next/graphql/introspection.d.ts +89 -0
  37. package/dist/next/graphql/introspection.js +102 -0
  38. package/dist/next/graphql/plan.d.ts +115 -0
  39. package/dist/next/graphql/plan.js +531 -0
  40. package/dist/next/graphql/request.d.ts +228 -0
  41. package/dist/next/graphql/request.js +283 -0
  42. package/dist/next/graphql/rest.d.ts +66 -0
  43. package/dist/next/graphql/rest.js +502 -0
  44. package/dist/next/graphql/selection.d.ts +55 -0
  45. package/dist/next/graphql/selection.js +212 -0
  46. package/dist/next/graphql/sha256.d.ts +13 -0
  47. package/dist/next/graphql/sha256.js +86 -0
  48. package/dist/next/graphql/summary.d.ts +83 -0
  49. package/dist/next/graphql/summary.js +151 -0
  50. package/dist/next/graphql/tree-layout.d.ts +36 -0
  51. package/dist/next/graphql/tree-layout.js +20 -0
  52. package/dist/next/graphql/tree.d.ts +171 -0
  53. package/dist/next/graphql/tree.js +249 -0
  54. package/dist/next/graphql/typed.d.ts +261 -0
  55. package/dist/next/graphql/typed.js +146 -0
  56. package/dist/next/index.d.ts +28 -5
  57. package/dist/next/index.js +25 -1
  58. package/dist/next/inflate.d.ts +25 -7
  59. package/dist/next/inflate.js +46 -32
  60. package/dist/next/key-family.d.ts +34 -0
  61. package/dist/next/key-family.js +74 -0
  62. package/dist/next/select-types.d.ts +44 -8
  63. package/dist/next/system-keys.d.ts +27 -0
  64. package/dist/next/system-keys.js +42 -0
  65. package/dist/nextjs/index.d.ts +174 -12
  66. package/dist/nextjs/index.js +270 -23
  67. package/dist/nextjs/overlay.d.ts +5 -0
  68. package/dist/nextjs/overlay.js +35 -0
  69. package/package.json +31 -13
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_API_VERSION = exports.CAPA_ENV = exports.EDIT_CACHE_CONTROL = exports.DRAFT_COOKIE = exports.EDIT_HEADER = exports.EDIT_PARAM = exports.LAYOUT_PAGE = void 0;
3
+ exports.DEFAULT_API_VERSION = exports.CAPA_ENV_ALIASES = exports.CAPA_ENV = exports.EDIT_CACHE_CONTROL = exports.DRAFT_COOKIE = exports.EDIT_HEADER = exports.EDIT_PARAM = exports.LAYOUT_PAGE = exports.gql = exports.MEDIA_TAG = exports.GRAPHQL_TAG = void 0;
4
4
  exports.withCache = withCache;
5
+ exports.modelTag = modelTag;
5
6
  exports.tagsFor = tagsFor;
6
7
  exports.revalidateFromWebhook = revalidateFromWebhook;
7
8
  exports.draftClient = draftClient;
@@ -12,12 +13,17 @@ exports.editMode = editMode;
12
13
  exports.resolveEditRequest = resolveEditRequest;
13
14
  exports.getPublishedClient = getPublishedClient;
14
15
  exports.getCapaClient = getCapaClient;
16
+ exports.graphql = graphql;
15
17
  exports.safeSitePath = safeSitePath;
16
18
  exports.createPreviewRoute = createPreviewRoute;
17
19
  exports.exitPreviewRoute = exitPreviewRoute;
18
20
  exports.capaMiddleware = capaMiddleware;
19
21
  const next_1 = require("../next");
22
+ Object.defineProperty(exports, "gql", { enumerable: true, get: function () { return next_1.gql; } });
20
23
  Object.defineProperty(exports, "LAYOUT_PAGE", { enumerable: true, get: function () { return next_1.LAYOUT_PAGE; } });
24
+ const client_1 = require("../next/client");
25
+ const sha256_1 = require("../next/graphql/sha256");
26
+ const key_family_1 = require("../next/key-family");
21
27
  /** Add Next.js fetch-cache options without importing `next/*`. */
22
28
  function withCache(fetchImpl, options) {
23
29
  return (async (input, init = {}) => {
@@ -31,6 +37,39 @@ function withCache(fetchImpl, options) {
31
37
  return fetchImpl(input, { ...init, next });
32
38
  });
33
39
  }
40
+ /**
41
+ * The Next.js cache tag for every read of a model, by its namespace. A GraphQL
42
+ * query names its models by namespace (`articles`), not by id, so this is the
43
+ * tag a GraphQL read is cached under, and `revalidateFromWebhook` revalidates
44
+ * it when an entry of that model changes.
45
+ */
46
+ function modelTag(namespace) {
47
+ return `capa:model:${namespace}`;
48
+ }
49
+ /**
50
+ * The tag `graphql()` keeps a read under when it gives a `revalidate` and no
51
+ * `tags`, and that `revalidateFromWebhook` revalidates on every content
52
+ * change: such a page is never stale after a publish, at the price of
53
+ * refreshing on any publish. Name the models it reads with
54
+ * `tagsFor({ namespace })` to refresh it only when one of those changes. A
55
+ * read with neither `tags` nor `revalidate` is not kept at all.
56
+ */
57
+ exports.GRAPHQL_TAG = "capa:graphql";
58
+ /**
59
+ * The tag `tagsFor({ namespace })` adds beside its model tags, and that
60
+ * `revalidateFromWebhook` revalidates on a media event: a GraphQL read shows
61
+ * a file's URL and alt text whichever models it names, so editing a file in
62
+ * the media library refreshes it.
63
+ */
64
+ exports.MEDIA_TAG = "capa:media";
65
+ /**
66
+ * Next.js cache tags for a read. `model`, `entry`, `key` and `tenant` are the
67
+ * API's surrogate keys (`m:`, `e:`, `k:`, `t:`), by id; `namespace` is one
68
+ * `capa:model:<namespace>` tag per model a GraphQL query reads, which
69
+ * `capa-codegen --graphql` lists as `<Name>Models`, and `MEDIA_TAG`, since
70
+ * the query may show a file from the media library. Each pairs with
71
+ * `revalidateFromWebhook`.
72
+ */
34
73
  function tagsFor(input) {
35
74
  const tags = [];
36
75
  if (input.model)
@@ -41,9 +80,26 @@ function tagsFor(input) {
41
80
  tags.push(`k:${input.key}`);
42
81
  if (input.tenant)
43
82
  tags.push(`t:${input.tenant}`);
83
+ const namespaces = typeof input.namespace === "string" ? [input.namespace] : (input.namespace ?? []);
84
+ for (const namespace of namespaces)
85
+ if (namespace)
86
+ tags.push(modelTag(namespace));
87
+ if (namespaces.some(Boolean))
88
+ tags.push(exports.MEDIA_TAG);
44
89
  return tags;
45
90
  }
46
- /** Revalidate the concrete entry and model identities carried by a webhook. */
91
+ /**
92
+ * Revalidate every tag a webhook's entry and model can be cached under: the
93
+ * entry (`e:`), the model by id (`m:`) and by namespace (`capa:model:`, the
94
+ * tag a GraphQL read uses). The namespace is `data.modelNamespace` on
95
+ * `instance.published`, `instance.unpublished` and `model.published`, and
96
+ * `data.namespace` on the other events (docs/WEBHOOKS.md). Any of those, and
97
+ * a `media.` event, also revalidates `GRAPHQL_TAG`, the tag of a `graphql()`
98
+ * read that named no tags. A `media.` event (a file's alt text, name or
99
+ * visibility changed, or the file went) revalidates the file's own key
100
+ * (`f:<fileId>`, as REST's `Surrogate-Key` names it) and `MEDIA_TAG`, which
101
+ * every read tagged by namespace carries, since it may show that file.
102
+ */
47
103
  async function revalidateFromWebhook(input) {
48
104
  const data = input.payload?.data ?? input.payload;
49
105
  const tags = [];
@@ -52,14 +108,34 @@ async function revalidateFromWebhook(input) {
52
108
  tags.push(`e:${entryId}`);
53
109
  if (typeof data?.modelId === "string" && data.modelId)
54
110
  tags.push(`m:${data.modelId}`);
111
+ const namespace = data?.modelNamespace ?? data?.namespace;
112
+ if (typeof namespace === "string" && namespace)
113
+ tags.push(modelTag(namespace));
114
+ const media = typeof input.payload?.type === "string" && input.payload.type.startsWith("media.");
115
+ if (media) {
116
+ if (typeof data?.fileId === "string" && data.fileId)
117
+ tags.push(`f:${data.fileId}`);
118
+ tags.push(exports.MEDIA_TAG);
119
+ }
120
+ if (tags.length > 0 || media)
121
+ tags.push(exports.GRAPHQL_TAG);
55
122
  const unique = [...new Set(tags)];
56
123
  for (const tag of unique)
57
124
  await input.revalidateTag(tag);
58
125
  return unique;
59
126
  }
60
- /** Select a client using server-only draft state supplied by the caller. */
127
+ /**
128
+ * Select a client using server-only draft state supplied by the caller. Pass
129
+ * codegen's `CapaQuery` to type the builder, as with `createClient`:
130
+ * `draftClient<CapaQuery>({ ... })`. `production` may hold the legacy key a
131
+ * site already has; `draft` needs a `cap_` key, and a legacy one throws a
132
+ * `TypeError` when draft mode selects it.
133
+ */
61
134
  async function draftClient(input) {
62
- return (0, next_1.createClient)((await input.isDraft()) ? input.draft : input.production);
135
+ if (!(await input.isDraft()))
136
+ return (0, next_1.createClient)(input.production);
137
+ (0, key_family_1.requireDraftKey)(input.draft?.apiKey, "the draft config");
138
+ return (0, next_1.createClient)(input.draft);
63
139
  }
64
140
  // ------------------------------------------------------------------ pages ---
65
141
  /**
@@ -233,7 +309,11 @@ exports.EDIT_CACHE_CONTROL = "private, no-store";
233
309
  */
234
310
  async function editMode(input) {
235
311
  const [draft, headers] = await Promise.all([input.draftMode(), input.headers()]);
236
- return draft.isEnabled === true || headers.get(exports.EDIT_HEADER) === "1";
312
+ return draft.isEnabled === true || verifiedEdit(headers);
313
+ }
314
+ /** Whether `resolveEditRequest` verified a `capa-edit` token for this request. */
315
+ function verifiedEdit(headers) {
316
+ return headers.get(exports.EDIT_HEADER) === "1";
237
317
  }
238
318
  /**
239
319
  * The middleware half of edit mode.
@@ -271,51 +351,218 @@ async function resolveEditRequest(request, client) {
271
351
  return { edit, verified, headers, cacheControl: edit ? exports.EDIT_CACHE_CONTROL : null };
272
352
  }
273
353
  // ------------------------------------------------- five-minute integration ---
274
- /** Where the env-driven helpers read their settings (M6). */
354
+ /**
355
+ * Where the env-driven helpers read their settings (M6): the one pair of
356
+ * names the whole product uses (the MCP server, `capa-codegen`, `capa
357
+ * persist`, every curl example in the API docs).
358
+ */
275
359
  exports.CAPA_ENV = {
276
360
  baseUrl: "CAPA_API_URL",
277
361
  apiKey: "CAPA_KEY",
278
362
  draftKey: "CAPA_DRAFT_KEY",
279
363
  version: "CAPA_API_VERSION",
280
364
  };
365
+ /** Older names that still work, read only when the name above is unset. */
366
+ exports.CAPA_ENV_ALIASES = {
367
+ CAPA_API_URL: "CAPA_BASE_URL",
368
+ CAPA_KEY: "CAPA_API_KEY",
369
+ };
281
370
  exports.DEFAULT_API_VERSION = "2026-10-01";
282
- function readEnv(name) {
371
+ function readOne(name) {
283
372
  const env = globalThis.process?.env;
284
373
  const value = env?.[name];
285
374
  return value === undefined || value === "" ? undefined : value;
286
375
  }
376
+ function readEnv(name) {
377
+ const alias = exports.CAPA_ENV_ALIASES[name];
378
+ return readOne(name) ?? (alias ? readOne(alias) : undefined);
379
+ }
287
380
  function requireEnv(name) {
288
381
  const value = readEnv(name);
289
- if (!value)
290
- throw new Error(`@capacms/sdk/nextjs: ${name} is not set.`);
291
- return value;
382
+ if (value)
383
+ return value;
384
+ const alias = exports.CAPA_ENV_ALIASES[name];
385
+ throw new Error(`@capacms/sdk/nextjs: ${name} is not set${alias ? ` (${alias} also works)` : ""}.`);
292
386
  }
293
- /** The published-key client from env: what verifying a token needs. */
387
+ /**
388
+ * A client config from env, `key` naming the env var for the key. A setting
389
+ * `config` gives is used as given and its env var is never read, so a full
390
+ * `config` needs no env at all.
391
+ */
392
+ function envConfig(config, key) {
393
+ return {
394
+ ...config,
395
+ baseUrl: config?.baseUrl ?? requireEnv(exports.CAPA_ENV.baseUrl),
396
+ apiKey: config?.apiKey ?? requireEnv(key),
397
+ version: config?.version ?? readEnv(exports.CAPA_ENV.version) ?? exports.DEFAULT_API_VERSION,
398
+ };
399
+ }
400
+ /** The published or the draft config from env; the draft key must be a `cap_` key. */
401
+ function modeConfig(config, draft) {
402
+ if (!draft)
403
+ return envConfig(config, exports.CAPA_ENV.apiKey);
404
+ const settings = envConfig(config, exports.CAPA_ENV.draftKey);
405
+ (0, key_family_1.requireDraftKey)(settings.apiKey, config?.apiKey !== undefined ? "config.apiKey" : exports.CAPA_ENV.draftKey);
406
+ return settings;
407
+ }
408
+ /**
409
+ * The published-key client from env: what verifying a token needs. `overrides`
410
+ * win over env, and pass codegen's `CapaQuery` to type the builder:
411
+ * `getPublishedClient<CapaQuery>()`.
412
+ */
294
413
  function getPublishedClient(overrides = {}) {
295
- return (0, next_1.createClient)({
296
- baseUrl: requireEnv(exports.CAPA_ENV.baseUrl),
297
- apiKey: requireEnv(exports.CAPA_ENV.apiKey),
298
- version: readEnv(exports.CAPA_ENV.version) ?? exports.DEFAULT_API_VERSION,
299
- ...overrides,
300
- });
414
+ return (0, next_1.createClient)(envConfig(overrides, exports.CAPA_ENV.apiKey));
301
415
  }
302
416
  /**
303
417
  * The client for this request, from env: the draft key under draft mode,
304
418
  * otherwise the published key, and `editMode` worked out for you.
305
419
  *
306
420
  * import { draftMode, headers } from "next/headers";
307
- * const capa = await getCapaClient({ draftMode, headers });
421
+ * const capa = await getCapaClient<CapaQuery>({ draftMode, headers });
422
+ * const { data } = await capa.graphql.query(selection, { tags: tagsFor({ namespace: "articles" }) });
423
+ *
424
+ * `CapaQuery` (from `capa-codegen --graphql`) types the builder, as with
425
+ * `createClient`; leave it out for an untyped client. `config` wins over env.
426
+ *
427
+ * Its REST reads are sent as `createClient` sends them, which Next does not
428
+ * keep. Its GraphQL reads, a document or the builder, are sent the same way
429
+ * unless a call gives `tags` or `revalidate`, so a publish shows up on both
430
+ * alike. Given either, a read is kept in Next's data cache exactly as
431
+ * `graphql()` keeps it: a published read with no errors, never a draft or an
432
+ * edit-mode page.
308
433
  */
309
434
  async function getCapaClient(input) {
310
435
  const draft = (await input.draftMode()).isEnabled === true;
311
436
  const edit = await editMode({ draftMode: input.draftMode, headers: input.headers });
312
- return (0, next_1.createClient)({
313
- baseUrl: requireEnv(exports.CAPA_ENV.baseUrl),
314
- apiKey: requireEnv(draft ? exports.CAPA_ENV.draftKey : exports.CAPA_ENV.apiKey),
315
- version: readEnv(exports.CAPA_ENV.version) ?? exports.DEFAULT_API_VERSION,
437
+ const settings = modeConfig(input.config, draft);
438
+ const client = (0, next_1.createClient)({ editMode: edit, ...settings });
439
+ const read = { draft, edit, settings, given: input.unstable_cache };
440
+ const graphql = (0, client_1.graphqlClient)((document, variables, options = {}) => {
441
+ const { tags, revalidate, ...call } = options;
442
+ return readInNext(read, document, variables, call, { tags, revalidate });
443
+ });
444
+ return { ...client, graphql };
445
+ }
446
+ let loadedUnstableCache;
447
+ /**
448
+ * Next's own `unstable_cache`, loaded on first use, or null where `next/cache`
449
+ * cannot be loaded (Next not installed). The package never imports `next/*`
450
+ * at the top, so a Node script can import this file without Next.
451
+ */
452
+ function nextUnstableCache() {
453
+ if (loadedUnstableCache === undefined) {
454
+ try {
455
+ loadedUnstableCache = require("next/cache").unstable_cache ?? null;
456
+ }
457
+ catch {
458
+ loadedUnstableCache = null;
459
+ }
460
+ }
461
+ return loadedUnstableCache;
462
+ }
463
+ /** Thrown inside `unstable_cache` so that a result with errors is never stored; caught at once. */
464
+ class NotCached extends Error {
465
+ result;
466
+ constructor(result) {
467
+ super(`@capacms/sdk/nextjs: this GraphQL read answered with errors, so it was not cached: ${result.errors[0]?.message ?? "unknown error"}`);
468
+ this.result = result;
469
+ this.name = "NotCached";
470
+ }
471
+ }
472
+ /**
473
+ * What a cached read is keyed by: everything that changes its answer. The key
474
+ * is hashed, so the API key never reaches the cache's index.
475
+ */
476
+ async function cacheKeyOf(settings, document, variables, call) {
477
+ return [
478
+ "@capacms/sdk/nextjs graphql",
479
+ settings.baseUrl,
480
+ await (0, sha256_1.sha256Hex)(settings.apiKey),
481
+ settings.version ?? "",
482
+ String(settings.contract ?? ""),
483
+ settings.schemaChecksum ?? "",
484
+ document,
485
+ JSON.stringify(variables ?? null),
486
+ call.operationName ?? "",
487
+ call.persisted ? "persisted" : "",
488
+ call.method ?? "",
489
+ ];
490
+ }
491
+ async function graphql(document, variables, options = {}) {
492
+ const { tags, revalidate, draft: draftOption, draftMode, headers, config, unstable_cache: given, ...call } = options;
493
+ const draft = draftOption ?? (draftMode ? (await draftMode()).isEnabled === true : false);
494
+ const edit = draft || (headers ? verifiedEdit(await headers()) : false);
495
+ const read = { draft, edit, settings: modeConfig(config, draft), given };
496
+ return readInNext(read, document, variables, call, { tags, revalidate });
497
+ }
498
+ /**
499
+ * Whether a read said anything about Next's cache: tags to keep it under, or a
500
+ * `revalidate`. One that said neither is sent as the REST reads are, so a
501
+ * publish shows up on both the same way.
502
+ */
503
+ function namesCaching({ tags, revalidate }) {
504
+ return (tags !== undefined && tags.length > 0) || revalidate !== undefined;
505
+ }
506
+ /**
507
+ * One GraphQL read in Next, for `graphql()` and `getCapaClient`.
508
+ *
509
+ * With no `tags` and no `revalidate` it is sent exactly as `entries.list` and
510
+ * `entries.get` send theirs: a plain fetch with no `cache` and no `next`, which
511
+ * Next does not keep, so the page shows a publish on its next render whether
512
+ * it reads by REST or by GraphQL. Given `tags` or a `revalidate`, Next's data
513
+ * cache holds a published read with no errors, and the fetch under it is never
514
+ * cached. A draft, an edit-mode read (its entries carry marks a cached copy
515
+ * would lose) and `revalidate: 0` are not cached.
516
+ */
517
+ async function readInNext(read, document, variables, call, options) {
518
+ const { draft, edit, settings, given } = read;
519
+ if (!draft && !edit && !namesCaching(options)) {
520
+ return (0, next_1.createClient)({ editMode: false, ...settings }).graphql(document, variables, call);
521
+ }
522
+ // `tags: []` names no tag, so the read is kept under `GRAPHQL_TAG` like one
523
+ // with no tags: kept under none, nothing could ever revalidate it.
524
+ const tags = options.tags && options.tags.length > 0 ? options.tags : undefined;
525
+ const revalidate = options.revalidate;
526
+ const baseFetch = settings.fetch ?? globalThis.fetch;
527
+ const cache = given ?? nextUnstableCache();
528
+ if (cache && !draft) {
529
+ if (edit || revalidate === 0) {
530
+ return (0, next_1.createClient)({ editMode: edit, ...settings, fetch: withCache(baseFetch, { revalidate: 0 }) }).graphql(document, variables, call);
531
+ }
532
+ const client = (0, next_1.createClient)({ editMode: false, ...settings, fetch: baseFetch });
533
+ let sent = false;
534
+ const send = async () => {
535
+ sent = true;
536
+ const result = await client.graphql(document, variables, call);
537
+ if (result.errors.length > 0)
538
+ throw new NotCached(result);
539
+ return result;
540
+ };
541
+ const keyParts = await cacheKeyOf(settings, document, variables, call);
542
+ const cacheOptions = { tags: tags ?? [exports.GRAPHQL_TAG] };
543
+ if (revalidate !== undefined)
544
+ cacheOptions.revalidate = revalidate;
545
+ try {
546
+ return await cache(send, keyParts, cacheOptions)();
547
+ }
548
+ catch (error) {
549
+ if (error instanceof NotCached)
550
+ return error.result;
551
+ // Next's own `unstable_cache` throws before it reads anything outside a
552
+ // Next request (a script, a test, a build step with no cache): the read
553
+ // is then sent as below. One passed in throws as it did.
554
+ if (given || sent)
555
+ throw error;
556
+ }
557
+ }
558
+ const client = (0, next_1.createClient)({
316
559
  editMode: edit,
317
- ...input.config,
560
+ ...settings,
561
+ fetch: draft
562
+ ? withCache(baseFetch, { revalidate: 0 })
563
+ : withCache(baseFetch, { tags: tags ?? [exports.GRAPHQL_TAG], revalidate }),
318
564
  });
565
+ return client.graphql(document, variables, call);
319
566
  }
320
567
  /** Only a path on this site: never `//elsewhere.example` or a full URL. */
321
568
  function safeSitePath(value) {
@@ -0,0 +1,5 @@
1
+ export interface CapaOverlayProps {
2
+ /** The Capa admin origins allowed to drive the overlay. */
3
+ adminOrigins: string[];
4
+ }
5
+ export declare function CapaOverlay({ adminOrigins }: CapaOverlayProps): null;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ "use client";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.CapaOverlay = CapaOverlay;
5
+ /**
6
+ * `<CapaOverlay />`: Capa's live preview overlay as one Next.js client
7
+ * component (M6).
8
+ *
9
+ * // app/layout.tsx (a server component)
10
+ * import { CapaOverlay } from "@capacms/sdk/nextjs/overlay";
11
+ * {edit ? <CapaOverlay adminOrigins={["https://app.capacms.com"]} /> : null}
12
+ *
13
+ * Render it only in edit mode (`editMode()` from `@capacms/sdk/nextjs`), so a
14
+ * visitor never downloads it. Inside the Capa editor it outlines the focused
15
+ * field, reports clicks back, and on a save re-renders the page with
16
+ * `router.refresh()`, keeping the scroll position. Outside a Capa frame it does
17
+ * nothing at all.
18
+ *
19
+ * Its own entry point, apart from `@capacms/sdk/nextjs`, because it imports
20
+ * `react` and `next/navigation` and is a client module: the server helpers must
21
+ * stay free of both.
22
+ */
23
+ const react_1 = require("react");
24
+ const navigation_1 = require("next/navigation");
25
+ const overlay_1 = require("../overlay");
26
+ function CapaOverlay({ adminOrigins }) {
27
+ const router = (0, navigation_1.useRouter)();
28
+ // A string, so a new array with the same origins does not restart it.
29
+ const origins = adminOrigins.join(",");
30
+ (0, react_1.useEffect)(() => (0, overlay_1.startOverlay)({
31
+ adminOrigins: origins.split(",").filter(Boolean),
32
+ onRefresh: () => router.refresh(),
33
+ }), [origins, router]);
34
+ return null;
35
+ }
package/package.json CHANGED
@@ -1,16 +1,9 @@
1
1
  {
2
2
  "name": "@capacms/sdk",
3
- "version": "1.0.0-next.4",
3
+ "version": "1.0.0-next.7",
4
+ "description": "The TypeScript SDK for Capa's content API: REST and GraphQL reads typed from your models, Next.js caching and live preview, and codegen.",
4
5
  "license": "UNLICENSED",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/ZVN-DEV/capa-cms-future.git",
8
- "directory": "packages/sdk"
9
- },
10
- "homepage": "https://github.com/ZVN-DEV/capa-cms-future/tree/main/packages/sdk#readme",
11
- "bugs": {
12
- "url": "https://github.com/ZVN-DEV/capa-cms-future/issues"
13
- },
6
+ "homepage": "https://docs.capacms.com/api",
14
7
  "engines": {
15
8
  "node": ">=18"
16
9
  },
@@ -18,7 +11,8 @@
18
11
  "files": [
19
12
  "dist",
20
13
  "bin",
21
- "README.md"
14
+ "README.md",
15
+ "CHANGELOG.md"
22
16
  ],
23
17
  "publishConfig": {
24
18
  "access": "public",
@@ -43,7 +37,11 @@
43
37
  "types": "./dist/overlay/index.d.ts",
44
38
  "default": "./dist/overlay/index.js"
45
39
  },
46
- "./package.json": "./package.json"
40
+ "./package.json": "./package.json",
41
+ "./nextjs/overlay": {
42
+ "types": "./dist/nextjs/overlay.d.ts",
43
+ "default": "./dist/nextjs/overlay.js"
44
+ }
47
45
  },
48
46
  "typesVersions": {
49
47
  "*": {
@@ -56,21 +54,41 @@
56
54
  "overlay": [
57
55
  "dist/overlay/index.d.ts"
58
56
  ],
57
+ "nextjs/overlay": [
58
+ "dist/nextjs/overlay.d.ts"
59
+ ],
59
60
  "*": [
60
61
  "dist/index.d.ts"
61
62
  ]
62
63
  }
63
64
  },
64
65
  "bin": {
66
+ "capa": "bin/capa.js",
65
67
  "capa-codegen": "bin/capa-codegen.js"
66
68
  },
69
+ "peerDependencies": {
70
+ "graphql": "^16.9.0",
71
+ "react": ">=18"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "graphql": {
75
+ "optional": true
76
+ },
77
+ "react": {
78
+ "optional": true
79
+ }
80
+ },
67
81
  "devDependencies": {
82
+ "graphql": "^16.9.0",
68
83
  "typescript": "^5.5.0",
84
+ "@types/react": "19.0.1",
85
+ "next": "15.5.9",
86
+ "react": "19.2.8",
69
87
  "@capa/shared": "0.1.0"
70
88
  },
71
89
  "scripts": {
72
90
  "build": "tsc -p tsconfig.json",
73
91
  "typecheck": "tsc -p tsconfig.json --noEmit",
74
- "test": "tsc -p tsconfig.json && node --test test/codegen.test.js test/client.test.js test/next-client.test.js test/nextjs.test.js test/webhooks.test.js test/attrs.test.js test/overlay.test.js test/inflate.test.js && tsc -p test/types/tsconfig.consumer.json --noEmit"
92
+ "test": "tsc -p tsconfig.json && node --test test/comments.test.js test/codegen.test.js test/client.test.js test/next-client.test.js test/nextjs.test.js test/webhooks.test.js test/attrs.test.js test/overlay.test.js test/inflate.test.js test/field-names.test.js test/next-graphql.test.js test/graphql-codegen.test.js test/graphql-contract.test.js test/readme-snippets.test.js test/nextjs-app.test.js test/package-json.test.js test/changelog.test.js test/published-docs.test.js test/builder-messages.test.js && tsc -p test/types/tsconfig.consumer.json --noEmit && tsc -p test/types/tsconfig.nonstrict.json --noEmit"
75
93
  }
76
94
  }