canopycms-next 0.0.62 → 0.0.64

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/dist/adapter.d.ts CHANGED
@@ -23,7 +23,7 @@ export declare function wrapNextRequest(req: Request): CanopyRequest;
23
23
  * // app/api/canopycms/[...canopycms]/route.ts
24
24
  * import { createCanopyCatchAllHandler } from 'canopycms-next'
25
25
  * import { createClerkAuthPlugin } from 'canopycms-auth-clerk'
26
- * import config from '../../../../canopycms.config'
26
+ * import config from '../../../../canopycms.config.js'
27
27
  *
28
28
  * const handler = createCanopyCatchAllHandler({
29
29
  * config: config.server,
@@ -46,4 +46,3 @@ export declare const createCanopyCatchAllHandler: (options: CanopyNextOptions) =
46
46
  [key: string]: unknown;
47
47
  };
48
48
  }) => Promise<Response>;
49
- //# sourceMappingURL=adapter.d.ts.map
package/dist/adapter.js CHANGED
@@ -92,7 +92,7 @@ async function extractPathSegments(ctx) {
92
92
  * // app/api/canopycms/[...canopycms]/route.ts
93
93
  * import { createCanopyCatchAllHandler } from 'canopycms-next'
94
94
  * import { createClerkAuthPlugin } from 'canopycms-auth-clerk'
95
- * import config from '../../../../canopycms.config'
95
+ * import config from '../../../../canopycms.config.js'
96
96
  *
97
97
  * const handler = createCanopyCatchAllHandler({
98
98
  * config: config.server,
@@ -130,4 +130,3 @@ export const createCanopyCatchAllHandler = (options) => {
130
130
  }
131
131
  };
132
132
  };
133
- //# sourceMappingURL=adapter.js.map
package/dist/client.d.ts CHANGED
@@ -16,7 +16,7 @@ import type { CanopyClientConfig, CustomFieldRenderers } from 'canopycms/client'
16
16
  * // app/edit/page.tsx
17
17
  * 'use client'
18
18
  * import { NextCanopyEditorPage } from 'canopycms-next/client'
19
- * import config from '../../canopycms.config'
19
+ * import config from '../../canopycms.config.js'
20
20
  *
21
21
  * export default function EditPage() {
22
22
  * const clientConfig = config.client()
@@ -26,4 +26,3 @@ import type { CanopyClientConfig, CustomFieldRenderers } from 'canopycms/client'
26
26
  * ```
27
27
  */
28
28
  export declare const NextCanopyEditorPage: (config: CanopyClientConfig, customRenderers?: CustomFieldRenderers) => () => import("react/jsx-runtime").JSX.Element;
29
- //# sourceMappingURL=client.d.ts.map
package/dist/client.js CHANGED
@@ -20,7 +20,7 @@ import { CanopyEditorPage } from 'canopycms/client';
20
20
  * // app/edit/page.tsx
21
21
  * 'use client'
22
22
  * import { NextCanopyEditorPage } from 'canopycms-next/client'
23
- * import config from '../../canopycms.config'
23
+ * import config from '../../canopycms.config.js'
24
24
  *
25
25
  * export default function EditPage() {
26
26
  * const clientConfig = config.client()
@@ -47,4 +47,3 @@ export const NextCanopyEditorPage = (config, customRenderers) => {
47
47
  return (_jsx(Suspense, { fallback: null, children: _jsx(EditorWithSearchParams, {}) }));
48
48
  };
49
49
  };
50
- //# sourceMappingURL=client.js.map
package/dist/config.d.ts CHANGED
@@ -1,2 +1 @@
1
- export { withCanopy, type WithCanopyOptions } from './with-canopy';
2
- //# sourceMappingURL=config.d.ts.map
1
+ export { withCanopy, type WithCanopyOptions } from './with-canopy.js';
package/dist/config.js CHANGED
@@ -1,2 +1 @@
1
- export { withCanopy } from './with-canopy';
2
- //# sourceMappingURL=config.js.map
1
+ export { withCanopy } from './with-canopy.js';
@@ -1,12 +1,43 @@
1
- import { type CanopyContext, type CanopyBuildContext, type CanopyServices } from 'canopycms/server';
1
+ import { type CanopyContext, type CanopyBuildContext, type CanopyServices, type SeoFieldLocation } from 'canopycms/server';
2
2
  import type { CanopyConfig, AuthPlugin, FieldConfig } from 'canopycms';
3
- import { createCanopyCatchAllHandler } from './adapter';
4
- import { type GenerateContentStaticParamsOptions } from './static';
3
+ import type { Metadata, MetadataRoute } from 'next';
4
+ import { createCanopyCatchAllHandler } from './adapter.js';
5
+ import { type EntryToMetadataOptions, type GenerateContentSitemapOptions, type GenerateContentStaticParamsOptions } from './static.js';
6
+ /**
7
+ * Merge a context-wide default SEO field location (`createNextCanopyContext({ seo })`) with a
8
+ * per-call override.
9
+ *
10
+ * `generateContentSitemap`'s `noindex` exclusion and `entryToMetadata`'s field extraction/`noindex`
11
+ * read must agree on where the SEO fields live, or the two surfaces drift: an entry can end up
12
+ * `noindex` on the page while the sitemap still advertises its URL (or the reverse), because each
13
+ * call site independently forgot (or mis-set) `group`/`fields`. Both bound helpers below call this
14
+ * SAME function with the SAME context-wide default, which is what makes agreement structural
15
+ * rather than a matter of adopter discipline.
16
+ *
17
+ * Only keys the caller actually supplies override the default — an omitted (or explicitly
18
+ * `undefined`) `fields`/`group` falls through to the context-wide value, so setting a default once
19
+ * and never repeating it per call is the common case. A plain `{ ...contextDefault, ...callOverride
20
+ * }` would NOT have this property: `entryToMetadata`'s options type always has `fields`/`group`
21
+ * keys, so a caller who omits them still produces an object whose `fields`/`group` are present but
22
+ * `undefined`, and spreading that would silently clobber the default. Checking each key explicitly
23
+ * avoids that trap. Exported for direct unit testing.
24
+ */
25
+ export declare function mergeSeoFieldLocation(contextDefault: SeoFieldLocation | undefined, callOverride: SeoFieldLocation | undefined): SeoFieldLocation;
5
26
  export interface NextCanopyOptions {
6
27
  config: CanopyConfig;
7
28
  /** Auth plugin for user authentication. Optional for static deployments (deployedAs: 'static'). */
8
29
  authPlugin?: AuthPlugin;
9
30
  entrySchemaRegistry: Record<string, readonly FieldConfig[]>;
31
+ /**
32
+ * Where the SEO fields live (`fields` name overrides, and/or a nested `group`). Set once here
33
+ * and it is shared by BOTH the returned `generateContentSitemap`'s `noindex` exclusion and the
34
+ * returned `entryToMetadata`'s field extraction, so the two surfaces cannot independently forget
35
+ * or mis-set it and drift apart — see `mergeSeoFieldLocation`. Omit for the flat (inline-group)
36
+ * convention, which is the default on both the schema side (`defineSeoFieldGroup()`) and here.
37
+ * A per-call `seo`/`fields`/`group` option on either helper still overrides this default for
38
+ * that one call.
39
+ */
40
+ seo?: SeoFieldLocation;
10
41
  }
11
42
  /**
12
43
  * Wrap a build context so its operations throw if misused at request time on a production server.
@@ -55,6 +86,25 @@ export interface NextCanopyContextResult {
55
86
  readByUrlPath: CanopyContext['readByUrlPath'];
56
87
  /** Phase-selecting `read` (build context at build, runtime context at request) — counterpart to readByUrlPath. */
57
88
  read: CanopyContext['read'];
89
+ /**
90
+ * Phase-selecting `listEntries` — every entry under `rootPath` in a single filesystem
91
+ * pass, each with its `urlPath`, `slug`, `entryType`, `data` and `schema`. Use it instead
92
+ * of "enumerate paths, then read each one", which is an N+1 over the content tree and
93
+ * invites URL-reconstruction bugs; the returned `urlPath` round-trips through
94
+ * `readByUrlPath` by construction, including for multi-segment paths.
95
+ *
96
+ * ```ts
97
+ * const articles = await listEntries<ArticleContent>({
98
+ * filter: (e) => e.entryType === 'article',
99
+ * })
100
+ * ```
101
+ *
102
+ * At build time this reads filesystem-direct; at request time it uses the branch-aware,
103
+ * **ACL-enforced** runtime context, so entries the current user cannot read are omitted.
104
+ * Note it takes no `branch` option — see `CanopyContext['listEntries']` for the
105
+ * base-branch pinning caveat in prod.
106
+ */
107
+ listEntries: CanopyContext['listEntries'];
58
108
  /**
59
109
  * Build the array Next's `generateStaticParams` expects from CanopyCMS content. Enumeration-only
60
110
  * (reads the set of routable paths, never entry content) and closes over the build context, so your
@@ -62,6 +112,28 @@ export interface NextCanopyContextResult {
62
112
  * `[slug]` route, or the default catch-all for `[...slug]`.
63
113
  */
64
114
  generateContentStaticParams: (options?: GenerateContentStaticParamsOptions) => Promise<Array<Record<string, string | string[]>>>;
115
+ /**
116
+ * Build `app/sitemap.ts`'s `MetadataRoute.Sitemap` from CanopyCMS content. Includes EVERY
117
+ * routable entry type by default and excludes `noindex` entries through the same predicate
118
+ * `entryToMetadata` uses for `robots`. Bound to the build context, so your `sitemap.ts` never
119
+ * imports the admin `getCanopyForBuild`.
120
+ *
121
+ * Reads the SEO field location from `createNextCanopyContext({ seo })` by default (shared with
122
+ * `entryToMetadata` below via `mergeSeoFieldLocation` — see that function's doc). Pass `seo` on a
123
+ * given call to override just that call.
124
+ */
125
+ generateContentSitemap: (options: GenerateContentSitemapOptions) => Promise<MetadataRoute.Sitemap>;
126
+ /**
127
+ * Map an entry's SEO fields onto a Next `Metadata` for `generateMetadata`. Re-exposed here so a
128
+ * page module has one CanopyCMS import (your `lib/canopy.ts`) rather than two.
129
+ *
130
+ * Reads the SAME SEO field location as `generateContentSitemap` above by default (the
131
+ * `createNextCanopyContext({ seo })` value); the two are bound from one context-wide default so
132
+ * they cannot silently disagree about where `noindex` (or a renamed field) lives. Pass
133
+ * `fields`/`group` on a given call to override just that call. This is a pure
134
+ * mapping and touches no context.
135
+ */
136
+ entryToMetadata: (entryData: unknown, options?: EntryToMetadataOptions) => Metadata;
65
137
  /** API catch-all route handler */
66
138
  handler: ReturnType<typeof createCanopyCatchAllHandler>;
67
139
  /** Underlying services (rarely needed directly) */
@@ -77,4 +149,3 @@ export interface NextCanopyContextResult {
77
149
  * auth works without network access (Lambda in prod, local in dev). The cache is populated by the worker daemon.
78
150
  */
79
151
  export declare function createNextCanopyContext(options: NextCanopyOptions): Promise<NextCanopyContextResult>;
80
- //# sourceMappingURL=context-wrapper.d.ts.map
@@ -4,8 +4,35 @@ import { headers } from 'next/headers';
4
4
  import { createCanopyContext, isBuildMode, startDevContentWatcher, createCanopyServices, createAssetStore, operatingStrategy, resolveCanopyUser, STATIC_DEPLOY_USER, } from 'canopycms/server';
5
5
  import { assertAuthPluginAllowedForMode } from 'canopycms/auth';
6
6
  import { CachingAuthPlugin, FileBasedAuthCache } from 'canopycms/auth/cache';
7
- import { createCanopyCatchAllHandler } from './adapter';
8
- import { collectStaticParams } from './static';
7
+ import { createCanopyCatchAllHandler } from './adapter.js';
8
+ import { collectStaticParams, entryToMetadata as entryToMetadataCore, generateContentSitemap, } from './static.js';
9
+ /**
10
+ * Merge a context-wide default SEO field location (`createNextCanopyContext({ seo })`) with a
11
+ * per-call override.
12
+ *
13
+ * `generateContentSitemap`'s `noindex` exclusion and `entryToMetadata`'s field extraction/`noindex`
14
+ * read must agree on where the SEO fields live, or the two surfaces drift: an entry can end up
15
+ * `noindex` on the page while the sitemap still advertises its URL (or the reverse), because each
16
+ * call site independently forgot (or mis-set) `group`/`fields`. Both bound helpers below call this
17
+ * SAME function with the SAME context-wide default, which is what makes agreement structural
18
+ * rather than a matter of adopter discipline.
19
+ *
20
+ * Only keys the caller actually supplies override the default — an omitted (or explicitly
21
+ * `undefined`) `fields`/`group` falls through to the context-wide value, so setting a default once
22
+ * and never repeating it per call is the common case. A plain `{ ...contextDefault, ...callOverride
23
+ * }` would NOT have this property: `entryToMetadata`'s options type always has `fields`/`group`
24
+ * keys, so a caller who omits them still produces an object whose `fields`/`group` are present but
25
+ * `undefined`, and spreading that would silently clobber the default. Checking each key explicitly
26
+ * avoids that trap. Exported for direct unit testing.
27
+ */
28
+ export function mergeSeoFieldLocation(contextDefault, callOverride) {
29
+ const merged = { ...contextDefault };
30
+ if (callOverride?.fields !== undefined)
31
+ merged.fields = callOverride.fields;
32
+ if (callOverride?.group !== undefined)
33
+ merged.group = callOverride.group;
34
+ return merged;
35
+ }
9
36
  let warnedStaticMode = false;
10
37
  /**
11
38
  * Stub auth plugin for static deployments where no real auth is needed.
@@ -209,11 +236,36 @@ export async function createNextCanopyContext(options) {
209
236
  const ctx = isBuildMode() ? await getCanopyForBuild() : await getCanopy();
210
237
  return ctx.read(input);
211
238
  };
239
+ const listEntries = async (opts) => {
240
+ const ctx = isBuildMode() ? await getCanopyForBuild() : await getCanopy();
241
+ return ctx.listEntries(opts);
242
+ };
212
243
  // Enumeration-only static-params helper, bound to the (guarded) build context so page modules don't
213
244
  // import the admin context just to list paths. generateStaticParams is build-only, so this is safe.
214
245
  const generateContentStaticParams = async (options = {}) => {
215
246
  return collectStaticParams(await getCanopyForBuild(), options);
216
247
  };
248
+ // Sitemap generation reads entry CONTENT (to apply the noindex predicate), so unlike
249
+ // generateContentStaticParams it is not enumeration-only — but sitemap.ts is build-only in a
250
+ // static export, and on a prod `server` deployment guardBuildContext throws if it is reached at
251
+ // request time. Binding it here keeps the admin context out of the route module either way.
252
+ //
253
+ // Both this and boundEntryToMetadata below feed options.seo (the context-wide default set on
254
+ // createNextCanopyContext) through the SAME mergeSeoFieldLocation call, so the noindex exclusion
255
+ // here and entryToMetadata's noindex/field read cannot independently drift out of sync.
256
+ const boundGenerateContentSitemap = async (callOptions) => {
257
+ return generateContentSitemap(await getCanopyForBuild(), {
258
+ ...callOptions,
259
+ seo: mergeSeoFieldLocation(options.seo, callOptions.seo),
260
+ });
261
+ };
262
+ const boundEntryToMetadata = (entryData, callOptions = {}) => {
263
+ const seoLocation = mergeSeoFieldLocation(options.seo, {
264
+ fields: callOptions.fields,
265
+ group: callOptions.group,
266
+ });
267
+ return entryToMetadataCore(entryData, { ...callOptions, ...seoLocation });
268
+ };
217
269
  // Create API handler using same services
218
270
  const handler = createCanopyCatchAllHandler({
219
271
  ...options,
@@ -226,9 +278,11 @@ export async function createNextCanopyContext(options) {
226
278
  getCanopyForBuild,
227
279
  readByUrlPath,
228
280
  read,
281
+ listEntries,
229
282
  generateContentStaticParams,
283
+ generateContentSitemap: boundGenerateContentSitemap,
284
+ entryToMetadata: boundEntryToMetadata,
230
285
  handler,
231
286
  services,
232
287
  };
233
288
  }
234
- //# sourceMappingURL=context-wrapper.js.map
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- export { createCanopyCatchAllHandler, wrapNextRequest, type CanopyNextOptions } from './adapter';
2
- export { createNextCanopyContext, type NextCanopyOptions, type NextCanopyContextResult, } from './context-wrapper';
3
- export { collectStaticParams, type GenerateContentStaticParamsOptions } from './static';
4
- export { createMockAuthPlugin, createRejectingAuthPlugin } from './test-utils';
5
- //# sourceMappingURL=index.d.ts.map
1
+ export { createCanopyCatchAllHandler, wrapNextRequest, type CanopyNextOptions } from './adapter.js';
2
+ export { createNextCanopyContext, type NextCanopyOptions, type NextCanopyContextResult, } from './context-wrapper.js';
3
+ export { collectStaticParams, generateContentSitemap, entryToMetadata, type GenerateContentStaticParamsOptions, type GenerateContentSitemapOptions, type EntryToMetadataOptions, type SitemapExtraUrl, } from './static.js';
4
+ export { createMockAuthPlugin, createRejectingAuthPlugin } from './test-utils.js';
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
- export { createCanopyCatchAllHandler, wrapNextRequest } from './adapter';
2
- export { createNextCanopyContext, } from './context-wrapper';
3
- export { collectStaticParams } from './static';
4
- export { createMockAuthPlugin, createRejectingAuthPlugin } from './test-utils';
5
- //# sourceMappingURL=index.js.map
1
+ export { createCanopyCatchAllHandler, wrapNextRequest } from './adapter.js';
2
+ export { createNextCanopyContext, } from './context-wrapper.js';
3
+ export { collectStaticParams, generateContentSitemap, entryToMetadata, } from './static.js';
4
+ export { createMockAuthPlugin, createRejectingAuthPlugin } from './test-utils.js';
package/dist/static.d.ts CHANGED
@@ -1,10 +1,17 @@
1
- import { type CanopyBuildContext, type CollectStaticPathsOptions } from 'canopycms/server';
1
+ import type { Metadata, MetadataRoute } from 'next';
2
+ import { type CanopyBuildContext, type CollectStaticPathsOptions, type ExtractSeoFieldsOptions, type RoutableEntry, type SeoFieldLocation, type SeoOgType } from 'canopycms/server';
2
3
  /**
3
- * Next.js static-export helpers built on the framework-agnostic core in `canopycms/server`.
4
+ * Next.js static-export helpers built on the framework-agnostic core in `canopycms/server`:
5
+ * static params (`collectStaticParams`), the content sitemap (`generateContentSitemap`), and
6
+ * per-entry metadata (`entryToMetadata`).
4
7
  *
5
- * Today this ships static-params generation. Sitemap and SEO-metadata helpers are tracked as
6
- * follow-up work (see .claude/future-tasks/static-export-sitemap.md and
7
- * .claude/future-tasks/static-export-seo-metadata.md).
8
+ * The last two ship together on purpose. `noindex` has to suppress a page in BOTH surfaces —
9
+ * `robots: { index: false }` on the page and absence from the sitemap — and both read it through
10
+ * the same core `isNoindexEntry` predicate, so they cannot disagree about which pages are
11
+ * advertised.
12
+ *
13
+ * `robots.txt` is out of scope: it is a handful of static lines with no CanopyCMS content behind
14
+ * it. Write `app/robots.ts` yourself, and point its `sitemap` at the route below.
8
15
  */
9
16
  export interface GenerateContentStaticParamsOptions extends CollectStaticPathsOptions {
10
17
  /** Route param name. Default 'slug'. */
@@ -20,6 +27,10 @@ export interface GenerateContentStaticParamsOptions extends CollectStaticPathsOp
20
27
  * route's base (e.g. `'/docs'`). Entries are scoped to that prefix and `segments` are made relative
21
28
  * to it, so the params match the route. Without it, segments are the full URL path. Applies to
22
29
  * catch-all shapes (it rewrites `segments`); it has no effect with `shape: 'single'`.
30
+ *
31
+ * Note that `shape: 'single'` also SKIPS collection index entries, whose URL is the
32
+ * collection's own path rather than a single slug segment. Render those from the collection's
33
+ * route (e.g. `app/posts/page.tsx`), not from `[slug]`.
23
34
  */
24
35
  basePath?: string;
25
36
  }
@@ -36,4 +47,197 @@ export interface GenerateContentStaticParamsOptions extends CollectStaticPathsOp
36
47
  * `[[...slug]]`, otherwise exclude it via `options.filter` (e.g. `(e) => e.segments.length > 0`).
37
48
  */
38
49
  export declare function collectStaticParams(buildCtx: Pick<CanopyBuildContext, 'listEntries'>, options?: GenerateContentStaticParamsOptions): Promise<Array<Record<string, string | string[]>>>;
39
- //# sourceMappingURL=static.d.ts.map
50
+ type SitemapItem = MetadataRoute.Sitemap[number];
51
+ /**
52
+ * A URL with no CanopyCMS entry behind it (a hand-written app route, a feed).
53
+ *
54
+ * **An extra URL is entirely hand-managed — it inherits nothing from the entry pipeline.**
55
+ * `generateContentSitemap` applies `isNoindexEntry` and a default `lastModified` only while
56
+ * walking real entries; this list is appended afterwards and passes through both:
57
+ *
58
+ * - **No `noindex` gate.** Nothing checks the SEO flag for an extra URL, because there is no
59
+ * entry data to check. If you are using `extraUrls` to re-advertise a real entry under a
60
+ * different path (the usual reason — an entry whose structural `urlPath` no route serves),
61
+ * you are responsible for not listing it when that entry is marked `noindex`. Marking the
62
+ * entry does not remove the extra URL.
63
+ * - **No `lastModified` fallback.** The per-entry branch defaults to `entry.updatedAt`; here,
64
+ * omitting `lastModified` means the URL simply ships without a date.
65
+ *
66
+ * Both are easy to get wrong in exactly the same direction, and both used to be unavoidable:
67
+ * `extraUrls` was once the only way to advertise a real entry at a different URL, so every such
68
+ * use re-derived `noindex` and `lastModified` by hand — this repo's own reference app among them,
69
+ * and it got `lastModified` wrong.
70
+ *
71
+ * It is no longer the only way, so it should no longer be that way:
72
+ *
73
+ * 1. **Model the entry so its natural `urlPath` IS the URL you serve.** An `index` entry collapses
74
+ * onto its collection's path, and a root `index` entry onto `/`. Nothing to keep in sync. This
75
+ * is what the reference app does now, which is why its sitemap passes neither option.
76
+ * 2. **Failing that, use `pathFor`** (see `GenerateContentSitemapOptions`), which overrides the
77
+ * URL while keeping the entry inside the walk — so the `noindex` gate, the `lastModified`
78
+ * default and `priority` all still apply.
79
+ * 3. **Keep `extraUrls` for what its name says:** URLs with no entry behind them at all.
80
+ */
81
+ export interface SitemapExtraUrl {
82
+ /** Site-relative path ('/blog') or an absolute URL. Trailing-slash rules apply to the former. */
83
+ path: string;
84
+ /** No fallback: omit this and the URL ships with no date at all (entries default to `updatedAt`). */
85
+ lastModified?: Date | string;
86
+ changeFrequency?: SitemapItem['changeFrequency'];
87
+ priority?: number;
88
+ }
89
+ export interface GenerateContentSitemapOptions {
90
+ /**
91
+ * Site origin, e.g. `https://example.com`. Required: a sitemap must carry absolute URLs, or
92
+ * search engines reject the whole file. Enforced — `generateContentSitemap` throws if this
93
+ * isn't an absolute URL (no scheme, or empty).
94
+ */
95
+ siteUrl: string;
96
+ /**
97
+ * Emit site-relative URLs with a trailing slash (`/contact/` rather than `/contact`).
98
+ *
99
+ * **Explicit on purpose.** CanopyCMS cannot see your `next.config.ts`, so it cannot know
100
+ * whether your site serves `trailingSlash: true`. Set this to match, or the sitemap advertises
101
+ * URLs that redirect — a real adopter shipped exactly that mismatch.
102
+ */
103
+ trailingSlash?: boolean;
104
+ /** Scope to a collection logical path (e.g. 'content/posts'). Defaults to the whole content root. */
105
+ rootPath?: string;
106
+ /** Where the SEO fields live, when they aren't the flat defaults. Drives the `noindex` read. */
107
+ seo?: SeoFieldLocation;
108
+ /**
109
+ * Drop entries from the sitemap. Applied ON TOP of the `noindex` exclusion, which is not
110
+ * optional. Use for structural URLs that shouldn't be advertised (e.g. a bare collection index).
111
+ */
112
+ exclude?: (entry: RoutableEntry) => boolean;
113
+ /**
114
+ * `<lastmod>` per entry. Defaults to the entry's `updatedAt`.
115
+ *
116
+ * **`updatedAt` is filesystem mtime, not an editorial timestamp.** A fresh CI clone resets every
117
+ * file's mtime to checkout time, so on a clean build agent the default dates every URL to the
118
+ * moment the tree was cloned. Treat the default as "changed since the last build" at best; if
119
+ * you need a truthful `<lastmod>`, supply one here (a content date field, or git commit time)
120
+ * or return `undefined` to omit the element entirely, which is better than asserting a date you
121
+ * cannot stand behind.
122
+ */
123
+ lastModified?: (entry: RoutableEntry) => Date | string | undefined;
124
+ /** `<priority>` per entry. Omitted from the URL when this returns undefined (the default). */
125
+ priority?: (entry: RoutableEntry) => number | undefined;
126
+ /**
127
+ * Override the URL an entry is advertised at, keeping it INSIDE the entry walk.
128
+ *
129
+ * Return a path to advertise the entry at instead of its structural `urlPath`; return `null`
130
+ * (or `undefined`) to leave it alone.
131
+ *
132
+ * **`null` means "no opinion", not "drop it".** So the obvious shape — handle the one entry
133
+ * type you care about, return `null` for the rest — advertises every other entry at its own
134
+ * path rather than silently emitting a one-URL sitemap. Dropping an entry stays `exclude`'s
135
+ * job, and keeping the two separate is the whole reason for this choice: a `pathFor` whose
136
+ * natural-looking callback silently omitted everything else would re-introduce exactly the
137
+ * quietly-short sitemap this module exists to prevent.
138
+ *
139
+ * This is the principled alternative to reaching for `extraUrls` to reroute a REAL entry.
140
+ * Because the entry stays in the walk, it keeps the `isNoindexEntry` gate, the `updatedAt`
141
+ * `lastModified` default and its `priority` automatically — all three of which an `extraUrls`
142
+ * entry makes you re-derive by hand, and get wrong independently (see {@link SitemapExtraUrl}).
143
+ *
144
+ * **It changes what is ADVERTISED, not what is BUILT.** `generateContentStaticParams` still
145
+ * enumerates the entry at its structural path, so the URL returned here must be one your app
146
+ * actually routes — otherwise you have advertised a 404, the same hazard `extraUrls` carries.
147
+ *
148
+ * Return a **site-relative path**. The value is trimmed, and an empty or off-site return (an
149
+ * absolute `https://…` or a protocol-relative `//host/…`) throws rather than being emitted:
150
+ * the first silently claims `/`, and the second lands in the sitemap as a non-absolute `<loc>`,
151
+ * which invalidates the whole file. `extraUrls` is where an off-origin URL is legitimate.
152
+ *
153
+ * Reach for it only when the URL is fixed by something OUTSIDE the content tree: published URLs
154
+ * you cannot change, or a route prefix that deliberately differs from the content layout. When
155
+ * you control the modelling, model the entry so its natural `urlPath` is already the URL you
156
+ * serve — an `index` entry collapses onto its collection's path, and a root `index` entry onto
157
+ * `/` — which needs no option at all. That ordering is not advice we only give: it is what the
158
+ * reference app in this repo does, which is why its sitemap passes neither this nor `extraUrls`.
159
+ *
160
+ * Runs AFTER the `noindex` and `exclude` gates (a dropped entry needs no URL). `exclude`,
161
+ * `lastModified` and `priority` receive the entry as ENUMERATED, so `entry.urlPath` there is
162
+ * always the structural path and never your override — branch on the entry, not on the URL.
163
+ *
164
+ * Two entries rewritten onto one URL are NOT caught by the build's `assertNoDuplicateUrlPaths`
165
+ * guard, which runs on the raw listing and never sees these rewrites; `dedupeSitemapItems`
166
+ * warns and keeps the first.
167
+ *
168
+ * @example
169
+ * // Content lives under content/articles/*, but this site has always published /blog/*.
170
+ * pathFor: (entry) =>
171
+ * entry.entryType === 'article' ? entry.urlPath.replace(/^\/articles\//, '/blog/') : null,
172
+ */
173
+ pathFor?: (entry: RoutableEntry) => string | null | undefined;
174
+ /**
175
+ * URLs with NO entry behind them — hand-written app routes, feeds — appended to the result.
176
+ *
177
+ * Appended after the entry walk, so these bypass BOTH the `isNoindexEntry` gate and the
178
+ * `lastModified` default. To point a REAL entry at a different URL, use `pathFor` above, which
179
+ * keeps both. See {@link SitemapExtraUrl}.
180
+ */
181
+ extraUrls?: SitemapExtraUrl[];
182
+ }
183
+ /**
184
+ * Build Next's `MetadataRoute.Sitemap` from CanopyCMS content.
185
+ *
186
+ * **Every routable entry type is included by default.** There is no list of "sitemap-able" entry
187
+ * types to keep in sync, and that is the whole design: a hand-rolled sitemap that enumerates a
188
+ * remembered list of entry types omits whichever type nobody added, ships green, and takes the
189
+ * missing pages out of search results silently. Omitting a URL here requires an explicit
190
+ * `exclude` predicate or a `noindex` flag on the entry.
191
+ *
192
+ * `noindex` entries are excluded, via the same core `isNoindexEntry` predicate that drives
193
+ * `robots: { index: false }` in `entryToMetadata`. They are still BUILT — enumeration
194
+ * (`generateContentStaticParams`) does not filter them, so the page exists for anyone holding
195
+ * the link; it just isn't advertised.
196
+ *
197
+ * `changeFrequency` is not emitted for entries: a blanket value asserted for every URL carries no
198
+ * information, and search engines say they ignore it. Set it per-URL via `extraUrls` if you want it.
199
+ *
200
+ * Output is stably ordered ('/' first, then alphabetical) so a rebuild doesn't reshuffle the file.
201
+ *
202
+ * @example
203
+ * // app/sitemap.ts
204
+ * export const dynamic = 'force-static' // required for output: 'export'
205
+ * export default () => contentSitemap({ siteUrl: 'https://example.com', trailingSlash: true })
206
+ */
207
+ export declare function generateContentSitemap(buildCtx: Pick<CanopyBuildContext, 'listEntries'>, options: GenerateContentSitemapOptions): Promise<MetadataRoute.Sitemap>;
208
+ export interface EntryToMetadataOptions extends ExtractSeoFieldsOptions {
209
+ /** Route path for this entry — the canonical URL when the entry sets none. */
210
+ path?: string;
211
+ /**
212
+ * Site origin for resolving relative canonical / OG image URLs. Omit to leave them relative and
213
+ * let Next's `metadataBase` resolve them.
214
+ */
215
+ siteUrl?: string;
216
+ /** Emit site-relative URLs with a trailing slash. Match your sitemap and your routing config. */
217
+ trailingSlash?: boolean;
218
+ /** Site name (openGraph.siteName, and the social-card title fallback). */
219
+ siteName?: string;
220
+ /** Root-layout mode: emit `title: { template, default }` (e.g. `'%s | Example'`). */
221
+ titleTemplate?: string;
222
+ /** og:type when the entry sets none. Defaults to 'website'. */
223
+ defaultOgType?: SeoOgType;
224
+ }
225
+ /**
226
+ * Map an entry's SEO fields onto a Next `Metadata` object.
227
+ *
228
+ * Title and description follow ONE convention everywhere: the entry's meta field, else the
229
+ * fallback you pass, else unset (so the root layout's default applies). An empty CMS field counts
230
+ * as unset — see `extractSeoFields`.
231
+ *
232
+ * `noindex` goes through the same core predicate the sitemap uses, so a page marked noindex is
233
+ * suppressed in both surfaces or neither.
234
+ *
235
+ * @example
236
+ * export async function generateMetadata({ params }): Promise<Metadata> {
237
+ * const { slug } = await params
238
+ * const result = await readByUrlPath(`/posts/${slug}`)
239
+ * return entryToMetadata(result?.data, { path: `/posts/${slug}`, fallbackTitle: result?.data.title })
240
+ * }
241
+ */
242
+ export declare function entryToMetadata(entryData: unknown, opts?: EntryToMetadataOptions): Metadata;
243
+ export {};
package/dist/static.js CHANGED
@@ -1,4 +1,4 @@
1
- import { collectStaticPaths, } from 'canopycms/server';
1
+ import { collectRoutableEntries, collectStaticPaths, extractSeoFields, isAbsoluteUrl, isIndexSlug, isNoindexEntry, resolveSeoUrl, } from 'canopycms/server';
2
2
  /**
3
3
  * Shape CanopyCMS content paths into the array Next's `generateStaticParams` expects.
4
4
  *
@@ -14,7 +14,13 @@ import { collectStaticPaths, } from 'canopycms/server';
14
14
  export async function collectStaticParams(buildCtx, options = {}) {
15
15
  const { paramName = 'slug', shape = 'catch-all', basePath, ...collectOptions } = options;
16
16
  let entries = await collectStaticPaths(buildCtx, collectOptions);
17
- if (basePath) {
17
+ // basePath only means something for the catch-all shape: it rewrites `segments` relative to the
18
+ // route's URL prefix, and 'single' shape never reads `segments` (it emits `entry.slug`). Applying
19
+ // the prefix FILTER unconditionally — as this once did — silently dropped every entry outside the
20
+ // prefix for 'single' too, contradicting this option's own doc above ("no effect with shape:
21
+ // 'single'") and meaning unbuilt pages for anyone who set basePath alongside 'single'. Scope with
22
+ // `rootPath` for 'single' instead.
23
+ if (basePath && shape !== 'single') {
18
24
  // Make segments relative to a nested route's base prefix (e.g. '/docs' for app/docs/[[...slug]]).
19
25
  // urlPath is always lowercased (see content-listing), so lowercase the prefix to match.
20
26
  const prefix = (basePath.endsWith('/') ? basePath.slice(0, -1) : basePath).toLowerCase();
@@ -25,6 +31,227 @@ export async function collectStaticParams(buildCtx, options = {}) {
25
31
  return { ...entry, segments: rel ? rel.split('/') : [] };
26
32
  });
27
33
  }
28
- return entries.map((entry) => shape === 'single' ? { [paramName]: entry.slug } : { [paramName]: entry.segments });
34
+ if (shape === 'single') {
35
+ // Drop index entries. Their URL is the COLLECTION's path (`/posts`, not `/posts/index`), a
36
+ // shape a single-segment `[slug]` route cannot represent — that page belongs to the
37
+ // collection's own route. Emitting one produced a param whose URL `readByUrlPath` refuses to
38
+ // resolve, so Next prerendered a guaranteed notFound (and an `output: export` build can fail
39
+ // outright). Catch-all is unaffected: it reads the already-collapsed `segments`, which for an
40
+ // index entry are the collection's.
41
+ return entries
42
+ .filter((entry) => !isIndexSlug(entry.slug))
43
+ .map((entry) => ({ [paramName]: entry.slug }));
44
+ }
45
+ return entries.map((entry) => ({ [paramName]: entry.segments }));
46
+ }
47
+ /**
48
+ * Build Next's `MetadataRoute.Sitemap` from CanopyCMS content.
49
+ *
50
+ * **Every routable entry type is included by default.** There is no list of "sitemap-able" entry
51
+ * types to keep in sync, and that is the whole design: a hand-rolled sitemap that enumerates a
52
+ * remembered list of entry types omits whichever type nobody added, ships green, and takes the
53
+ * missing pages out of search results silently. Omitting a URL here requires an explicit
54
+ * `exclude` predicate or a `noindex` flag on the entry.
55
+ *
56
+ * `noindex` entries are excluded, via the same core `isNoindexEntry` predicate that drives
57
+ * `robots: { index: false }` in `entryToMetadata`. They are still BUILT — enumeration
58
+ * (`generateContentStaticParams`) does not filter them, so the page exists for anyone holding
59
+ * the link; it just isn't advertised.
60
+ *
61
+ * `changeFrequency` is not emitted for entries: a blanket value asserted for every URL carries no
62
+ * information, and search engines say they ignore it. Set it per-URL via `extraUrls` if you want it.
63
+ *
64
+ * Output is stably ordered ('/' first, then alphabetical) so a rebuild doesn't reshuffle the file.
65
+ *
66
+ * @example
67
+ * // app/sitemap.ts
68
+ * export const dynamic = 'force-static' // required for output: 'export'
69
+ * export default () => contentSitemap({ siteUrl: 'https://example.com', trailingSlash: true })
70
+ */
71
+ export async function generateContentSitemap(buildCtx, options) {
72
+ const { siteUrl, trailingSlash, rootPath, seo, exclude, lastModified, priority, pathFor } = options;
73
+ // A sitemap with a non-absolute <loc> is invalid — search engines reject the WHOLE file, not
74
+ // just the bad entry, and the build stays green because nothing here threw. 'example.com' (no
75
+ // scheme) and '' both pass through resolveSeoUrl unmodified today; catch them here instead.
76
+ if (!isAbsoluteUrl(siteUrl)) {
77
+ throw new Error(`CanopyCMS: generateContentSitemap requires an absolute siteUrl (e.g. "https://example.com"), ` +
78
+ `got ${JSON.stringify(siteUrl)}. A sitemap whose <loc> values aren't absolute URLs is invalid ` +
79
+ 'and search engines silently reject the entire file.');
80
+ }
81
+ const urlOpts = { siteUrl, trailingSlash };
82
+ const entries = await collectRoutableEntries(buildCtx, { rootPath });
83
+ const items = [];
84
+ for (const entry of entries) {
85
+ if (isNoindexEntry(entry.data, seo))
86
+ continue;
87
+ if (exclude?.(entry))
88
+ continue;
89
+ const modified = lastModified ? lastModified(entry) : entry.updatedAt;
90
+ const entryPriority = priority?.(entry);
91
+ items.push({
92
+ url: resolveSeoUrl(resolveEntrySitemapPath(entry, pathFor), urlOpts),
93
+ ...(modified ? { lastModified: modified } : {}),
94
+ ...(entryPriority === undefined ? {} : { priority: entryPriority }),
95
+ });
96
+ }
97
+ // Deliberately outside the entry loop above, and therefore outside its `isNoindexEntry` gate
98
+ // and its `entry.updatedAt` fallback — an extra URL has no entry to derive either from. This is
99
+ // the behaviour documented on SitemapExtraUrl; if that ever changes, change the doc with it.
100
+ for (const extra of options.extraUrls ?? []) {
101
+ items.push({
102
+ url: resolveSeoUrl(extra.path, urlOpts),
103
+ ...(extra.lastModified ? { lastModified: extra.lastModified } : {}),
104
+ ...(extra.changeFrequency ? { changeFrequency: extra.changeFrequency } : {}),
105
+ ...(extra.priority === undefined ? {} : { priority: extra.priority }),
106
+ });
107
+ }
108
+ const root = resolveSeoUrl('/', urlOpts);
109
+ return dedupeSitemapItems(items).sort((a, b) => a.url === root ? -1 : b.url === root ? 1 : a.url.localeCompare(b.url));
110
+ }
111
+ /**
112
+ * Resolve the path an entry is advertised at, applying the caller's `pathFor` override.
113
+ *
114
+ * `null`/`undefined` mean "no opinion" and keep the entry's own `urlPath` — see the `pathFor`
115
+ * doc for why that is not "drop it".
116
+ *
117
+ * Two shapes are rejected rather than passed through, because each produces a WRONG SITEMAP THAT
118
+ * SHIPS GREEN — the failure mode every other guard in this file exists to convert into a red
119
+ * build. (A third, the WHATWG-backslash spellings like `/\evil.com/x`, is not thrown but
120
+ * neutralized downstream by `resolveSeoUrl`'s `neutralizeImplicitOffOrigin`.)
121
+ *
122
+ * - **Empty or whitespace-only.** `resolveSeoUrl('')` resolves to the site root, so an empty
123
+ * return would quietly advertise this entry at `/` and collide with whatever really lives there.
124
+ * - **Absolute (scheme-qualified or protocol-relative).** `resolveSeoUrl` passes both through
125
+ * verbatim, by design — for `extraUrls`, where an absolute URL is explicitly allowed. Here it is
126
+ * not: a protocol-relative `//host/x` lands in the sitemap as a NON-absolute `<loc>`, the exact
127
+ * invalidity `generateContentSitemap`'s `siteUrl` guard exists to prevent, and search engines
128
+ * reject the whole file for it. Refused whatever the origin — including one matching `siteUrl` —
129
+ * so the rule is "return a site-relative path" with no host comparison to get subtly wrong.
130
+ *
131
+ * The returned path is TRIMMED. Surrounding whitespace is never intended and survives into the
132
+ * URL otherwise (`' /blog'` → `<siteUrl>/ /blog`) — which this function would previously detect
133
+ * while checking for emptiness and then hand back untrimmed anyway.
134
+ */
135
+ function resolveEntrySitemapPath(entry, pathFor) {
136
+ if (!pathFor)
137
+ return entry.urlPath;
138
+ const override = pathFor(entry);
139
+ if (override === null || override === undefined)
140
+ return entry.urlPath;
141
+ const trimmed = override.trim();
142
+ const where = `the ${JSON.stringify(entry.entryType)} entry at ${JSON.stringify(entry.urlPath)}`;
143
+ if (trimmed === '') {
144
+ throw new Error(`CanopyCMS: generateContentSitemap's pathFor returned an empty path for ${where}. An empty ` +
145
+ 'path resolves to the site root, so honouring it would advertise this entry at "/" and ' +
146
+ "collide with whatever really lives there. Return null to keep the entry's own urlPath.");
147
+ }
148
+ if (isAbsoluteUrl(trimmed)) {
149
+ throw new Error(`CanopyCMS: generateContentSitemap's pathFor returned an absolute URL ` +
150
+ `(${JSON.stringify(trimmed)}) for ${where}. Return a site-relative path instead — the ` +
151
+ 'site origin is applied for you. Absolute returns are refused whatever the origin, rather ' +
152
+ 'than only off-origin ones, so there is one rule to remember and no host comparison to get ' +
153
+ 'subtly wrong; a protocol-relative value (`//host/x`) is the reason it matters, since that ' +
154
+ 'ships as a non-absolute <loc> and invalidates the entire sitemap. Use extraUrls to list a ' +
155
+ 'URL on another origin.');
156
+ }
157
+ return trimmed;
158
+ }
159
+ /**
160
+ * Drop duplicate `<loc>` entries, keeping the first occurrence and warning about each collision.
161
+ *
162
+ * A duplicate URL isn't fatal to a crawler, but it usually means two URLs are unintentionally
163
+ * sharing one `<loc>` — an index entry collapsing onto a sibling's path, two `urlPath`s that only
164
+ * differ by case (`urlPath` is lowercased — see `content-listing.ts`), an `extraUrls` path
165
+ * repeating an entry's URL, or a `pathFor` that rewrote two entries onto the same path. Warning
166
+ * rather than silently deduping turns that into a build-time signal an adopter can act on instead
167
+ * of a sitemap that just quietly has fewer URLs than expected.
168
+ *
169
+ * Still needed alongside `assertNoDuplicateUrlPaths` (canopycms `static/index.ts`), which fails a
170
+ * production build on the entry-vs-entry case before generation ever reaches here. This covers the
171
+ * three cases that guard cannot: a collision involving an `extraUrls` path, which is supplied here
172
+ * and never appears in the content enumeration; a collision created by `pathFor`, which rewrites
173
+ * URLs after that guard has already run on the raw listing; and any call outside build mode, where
174
+ * that guard is deliberately silent.
175
+ */
176
+ function dedupeSitemapItems(items) {
177
+ const seen = new Set();
178
+ const result = [];
179
+ for (const item of items) {
180
+ if (seen.has(item.url)) {
181
+ console.warn(`CanopyCMS: generateContentSitemap found more than one entry resolving to the same sitemap ` +
182
+ `URL ${JSON.stringify(item.url)}. Keeping the first and dropping the rest — this usually means ` +
183
+ 'two entries, an entry and an extraUrls path, or a pathFor override are unintentionally ' +
184
+ 'sharing one URL.');
185
+ continue;
186
+ }
187
+ seen.add(item.url);
188
+ result.push(item);
189
+ }
190
+ return result;
191
+ }
192
+ /**
193
+ * Map an entry's SEO fields onto a Next `Metadata` object.
194
+ *
195
+ * Title and description follow ONE convention everywhere: the entry's meta field, else the
196
+ * fallback you pass, else unset (so the root layout's default applies). An empty CMS field counts
197
+ * as unset — see `extractSeoFields`.
198
+ *
199
+ * `noindex` goes through the same core predicate the sitemap uses, so a page marked noindex is
200
+ * suppressed in both surfaces or neither.
201
+ *
202
+ * @example
203
+ * export async function generateMetadata({ params }): Promise<Metadata> {
204
+ * const { slug } = await params
205
+ * const result = await readByUrlPath(`/posts/${slug}`)
206
+ * return entryToMetadata(result?.data, { path: `/posts/${slug}`, fallbackTitle: result?.data.title })
207
+ * }
208
+ */
209
+ export function entryToMetadata(entryData, opts = {}) {
210
+ const seo = extractSeoFields(entryData, opts);
211
+ const urlOpts = { siteUrl: opts.siteUrl, trailingSlash: opts.trailingSlash };
212
+ // The entry's own canonical wins over the route path; an absolute one passes through verbatim
213
+ // (see resolveSeoUrl — the absolute check runs before any normalization, which is a fixed bug).
214
+ const canonicalSource = seo.canonical ?? opts.path;
215
+ const canonical = canonicalSource ? resolveSeoUrl(canonicalSource, urlOpts) : undefined;
216
+ // Images are files, never directory-style URLs: never append a trailing slash to one.
217
+ const ogImage = seo.ogImage ? resolveSeoUrl(seo.ogImage, { siteUrl: opts.siteUrl }) : undefined;
218
+ // og:title falls back to the site name so a page that inherits the layout title still emits a
219
+ // usable card — Next REPLACES `openGraph` wholesale rather than deep-merging it.
220
+ const socialTitle = seo.title ?? opts.siteName;
221
+ const ogType = seo.ogType ?? opts.defaultOgType ?? 'website';
222
+ const openGraphBase = {
223
+ ...(socialTitle ? { title: socialTitle } : {}),
224
+ ...(seo.description ? { description: seo.description } : {}),
225
+ ...(canonical ? { url: canonical } : {}),
226
+ ...(opts.siteName ? { siteName: opts.siteName } : {}),
227
+ ...(ogImage ? { images: [{ url: ogImage }] } : {}),
228
+ };
229
+ const metadata = {
230
+ ...(canonical ? { alternates: { canonical } } : {}),
231
+ // Spelled out per branch rather than cast: Next's OpenGraph metadata is a discriminated
232
+ // union on `type`.
233
+ openGraph: ogType === 'article'
234
+ ? { ...openGraphBase, type: 'article' }
235
+ : ogType === 'profile'
236
+ ? { ...openGraphBase, type: 'profile' }
237
+ : { ...openGraphBase, type: 'website' },
238
+ twitter: {
239
+ card: seo.twitterCard ?? 'summary_large_image',
240
+ ...(socialTitle ? { title: socialTitle } : {}),
241
+ ...(seo.description ? { description: seo.description } : {}),
242
+ ...(ogImage ? { images: [ogImage] } : {}),
243
+ },
244
+ };
245
+ if (opts.titleTemplate) {
246
+ metadata.title = { template: opts.titleTemplate, default: seo.title ?? opts.siteName ?? '' };
247
+ }
248
+ else if (seo.title) {
249
+ metadata.title = seo.title;
250
+ }
251
+ if (seo.description)
252
+ metadata.description = seo.description;
253
+ // Same predicate as the sitemap's exclusion — these two must never be derived separately.
254
+ if (isNoindexEntry(entryData, opts))
255
+ metadata.robots = { index: false, follow: false };
256
+ return metadata;
29
257
  }
30
- //# sourceMappingURL=static.js.map
@@ -9,4 +9,3 @@ export declare const createMockAuthPlugin: (user?: AuthenticatedUser) => AuthPlu
9
9
  * Create a mock AuthPlugin that rejects all authentication.
10
10
  */
11
11
  export declare const createRejectingAuthPlugin: (error?: string) => AuthPlugin;
12
- //# sourceMappingURL=test-utils.d.ts.map
@@ -33,4 +33,3 @@ export const createRejectingAuthPlugin = (error = 'Unauthorized') => ({
33
33
  getGroupMetadata: async () => null,
34
34
  listGroups: async () => [],
35
35
  });
36
- //# sourceMappingURL=test-utils.js.map
@@ -60,4 +60,3 @@ export interface WithCanopyOptions {
60
60
  * ```
61
61
  */
62
62
  export declare function withCanopy(nextConfig?: NextConfig, options?: WithCanopyOptions): NextConfig;
63
- //# sourceMappingURL=with-canopy.d.ts.map
@@ -217,4 +217,3 @@ export function withCanopy(nextConfig = {}, options = {}) {
217
217
  rewrites: withAssetsRewrite(nextConfig.rewrites),
218
218
  };
219
219
  }
220
- //# sourceMappingURL=with-canopy.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canopycms-next",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
4
4
  "description": "Next.js adapter for CanopyCMS",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "peerDependencies": {
37
37
  "next": "^13.5.7 || ^14.2.25 || ^15.5.21 || >=16.0.0 <16.2.0 || >=16.3.0 <17.0.0",
38
38
  "react": "^18.0.0 || ^19.0.0",
39
- "canopycms": "0.0.62"
39
+ "canopycms": "0.0.64"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^22.9.0",
@@ -46,10 +46,10 @@
46
46
  "react": "^18.3.1",
47
47
  "typescript": "^5.6.3",
48
48
  "vitest": "^4.1.0",
49
- "canopycms": "0.0.62"
49
+ "canopycms": "0.0.64"
50
50
  },
51
51
  "scripts": {
52
- "build": "tsc -p tsconfig.build.json && esbuild src/config.ts --bundle --platform=node --packages=external --external:next --format=cjs --outfile=dist/config.cjs && esbuild src/config.ts --bundle --platform=node --packages=external --external:next --format=esm --outfile=dist/config.mjs",
52
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/add-js-extensions.mjs dist && esbuild src/config.ts --bundle --platform=node --packages=external --external:next --format=cjs --outfile=dist/config.cjs && esbuild src/config.ts --bundle --platform=node --packages=external --external:next --format=esm --outfile=dist/config.mjs",
53
53
  "lint": "eslint src/",
54
54
  "test": "vitest run --reporter=dot",
55
55
  "typecheck": "tsc --noEmit"
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAEnB,MAAM,gBAAgB,CAAA;AAGvB;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CA0B3D;AAkDD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,iBAAiB,MAIlE,KAAK,OAAO,EACZ,MAAM;IACJ,MAAM,CAAC,EACH,OAAO,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,GACzD;QAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;CACrD,KACA,OAAO,CAAC,QAAQ,CAoBpB,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,kBAAkB,EAC1B,kBAAkB,oBAAoB,kDAwBvC,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAGnD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAA0B,EAC1B,eAAsC,EACtC,EAAE;IACF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAE1D,8EAA8E;IAC9E,0EAA0E;IAC1E,+EAA+E;IAC/E,4EAA4E;IAC5E,SAAS,sBAAsB;QAC7B,MAAM,eAAe,GAAG,eAAe,EAAE,CAAA;QACzC,MAAM,YAAY,GAAG;YACnB,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS;YAClD,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS;SACjD,CAAA;QACD,OAAO,KAAC,QAAQ,IAAC,YAAY,EAAE,YAAY,GAAI,CAAA;IACjD,CAAC;IAED,OAAO,SAAS,cAAc;QAC5B,OAAO,CACL,KAAC,QAAQ,IAAC,QAAQ,EAAE,IAAI,YACtB,KAAC,sBAAsB,KAAG,GACjB,CACZ,CAAA;IACH,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=client.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../src/client.test.tsx"],"names":[],"mappings":""}
@@ -1,29 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- // Mocked so this stays a node-environment test: the factory under test calls
3
- // CanopyEditorPage at CALL time (not render time), so asserting what it was
4
- // handed needs no DOM, no jsdom and no testing-library.
5
- const canopyEditorPage = vi.fn(() => () => null);
6
- vi.mock('canopycms/client', () => ({
7
- CanopyEditorPage: (...args) => canopyEditorPage(...args),
8
- }));
9
- vi.mock('next/navigation', () => ({ useSearchParams: () => new URLSearchParams() }));
10
- const { NextCanopyEditorPage } = await import('./client');
11
- const config = { apiBase: '/api/canopycms' };
12
- describe('NextCanopyEditorPage', () => {
13
- it('forwards customRenderers to CanopyEditorPage', () => {
14
- // Next is the primary target, so this wrapper is the entrypoint adopters
15
- // import. Accepting `customRenderers` only on the core CanopyEditorPage
16
- // left the extension point unreachable from the path every adopter uses
17
- // - and silently, since an ignored extra argument is not a type error at
18
- // the call site.
19
- const customRenderers = { number: () => null };
20
- NextCanopyEditorPage(config, customRenderers);
21
- expect(canopyEditorPage).toHaveBeenCalledWith(config, customRenderers);
22
- });
23
- it('still works with no customRenderers (the scaffolded call shape)', () => {
24
- canopyEditorPage.mockClear();
25
- NextCanopyEditorPage(config);
26
- expect(canopyEditorPage).toHaveBeenCalledWith(config, undefined);
27
- });
28
- });
29
- //# sourceMappingURL=client.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.test.js","sourceRoot":"","sources":["../src/client.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAEjD,6EAA6E;AAC7E,4EAA4E;AAC5E,wDAAwD;AACxD,MAAM,gBAAgB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;AAChD,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,gBAAgB,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAI,IAAW,CAAC;CAC5E,CAAC,CAAC,CAAA;AACH,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,EAAE,EAAE,CAAC,CAAC,CAAA;AAEpF,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAA;AAEzD,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,gBAAgB,EAAW,CAAA;AAErD,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,yEAAyE;QACzE,iBAAiB;QACjB,MAAM,eAAe,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAA;QAE9C,oBAAoB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;QAE7C,MAAM,CAAC,gBAAgB,CAAC,CAAC,oBAAoB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IACxE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,gBAAgB,CAAC,SAAS,EAAE,CAAA;QAE5B,oBAAoB,CAAC,MAAM,CAAC,CAAA;QAE5B,MAAM,CAAC,gBAAgB,CAAC,CAAC,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAClE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA0B,MAAM,eAAe,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-wrapper.d.ts","sourceRoot":"","sources":["../src/context-wrapper.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EAWpB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAc,WAAW,EAAE,MAAM,WAAW,CAAA;AAGlF,OAAO,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAA;AACvD,OAAO,EAAuB,KAAK,kCAAkC,EAAE,MAAM,UAAU,CAAA;AAiCvF,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,CAAA;IACpB,mGAAmG;IACnG,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC,CAAA;CAC5D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,EAAE,YAAY,GACnB,kBAAkB,CA4CpB;AAED,MAAM,WAAW,uBAAuB;IACtC,8GAA8G;IAC9G,SAAS,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAA;IACvC;;;;;;;;;;;;;OAaG;IACH,iBAAiB,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACpD;;;;;;;OAOG;IACH,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC,CAAA;IAC7C,kHAAkH;IAClH,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC3B;;;;;OAKG;IACH,2BAA2B,EAAE,CAC3B,OAAO,CAAC,EAAE,kCAAkC,KACzC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;IACtD,kCAAkC;IAClC,OAAO,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAA;IACvD,mDAAmD;IACnD,QAAQ,EAAE,cAAc,CAAA;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,uBAAuB,CAAC,CAkLlC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-wrapper.js","sourceRoot":"","sources":["../src/context-wrapper.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAEhG,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,kCAAkC,EAAE,MAAM,UAAU,CAAA;AAEvF,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,eAAe,EAA0B,MAAM,WAAW,CAAA;AAEhG,OAAO,EACL,uBAAuB,GAGxB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,mBAAmB,EAA2C,MAAM,UAAU,CAAA;AAEvF,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC/B,MAAM,kBAAkB,CAAA;AAEzB;;;;;;GAMG;AAEH,MAAM,WAAW,kCAAmC,SAAQ,yBAAyB;IACnF,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAA;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,EACjD,OAAO,GAAE,kCAAuC,GAC/C,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAmBnD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"static.js","sourceRoot":"","sources":["../src/static.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAGnB,MAAM,kBAAkB,CAAA;AA4BzB;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,QAAiD,EACjD,UAA8C,EAAE;IAEhD,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,KAAK,GAAG,WAAW,EAAE,QAAQ,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,CAAA;IACxF,IAAI,OAAO,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAA;IAEhE,IAAI,QAAQ,EAAE,CAAC;QACb,kGAAkG;QAClG,wFAAwF;QACxF,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAA;QACxF,OAAO,GAAG,OAAO;aACd,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;aACrF,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACb,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YAClF,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;QAC1D,CAAC,CAAC,CAAA;IACN,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3B,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,CACnF,CAAA;AACH,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAIlD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAM,iBAIL,KACA,UAeD,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAsB,KAAG,UAMjE,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,GAAG,QAAQ,CAAA;AAEvB;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,OAA0B;IACxB,IAAI,EAAE,eAAe;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,CAAC,MAAM,CAAC;CACjB,EACW,EAAE,CAAC,CAAC;IAChB,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QACzB,OAAO,EAAE,IAAI;QACb,IAAI,EAAE;YACJ,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,cAAc,EAAE,IAAI,CAAC,MAAM;SAC5B;KACF,CAAC;IACF,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;IAC3B,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;IACjC,gBAAgB,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;IAClC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;CAC3B,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,KAAK,GAAG,cAAc,EAAc,EAAE,CAAC,CAAC;IAChF,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACrD,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;IAC3B,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;IACjC,gBAAgB,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;IAClC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;CAC3B,CAAC,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"with-canopy.d.ts","sourceRoot":"","sources":["../src/with-canopy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAoGtC,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAyBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,UAAU,CACxB,UAAU,GAAE,UAAe,EAC3B,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CA2EZ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"with-canopy.js","sourceRoot":"","sources":["../src/with-canopy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAA;AAG5B,gEAAgE;AAChE,MAAM,iBAAiB,GAAG,CAAC,WAAW,CAAC,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG;IACxB,gBAAgB;IAChB,sBAAsB;IACtB,oBAAoB;IACpB,eAAe;CAChB,CAAA;AAED;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAA;AAEvD;;;;;;;;;;;GAWG;AACH,MAAM,sBAAsB,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAA;AAE1D;;;;;GAKG;AACH,MAAM,8BAA8B,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;AAEjE;;;;;;;;;;GAUG;AACH,MAAM,cAAc,GAAG;IACrB,MAAM,EAAE,gBAAgB;IACxB,WAAW,EAAE,yCAAyC;CACvD,CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,iBAAiB,CACxB,gBAAwC;IAExC,OAAO,KAAK,IAAI,EAAE;QAChB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO,CAAC,cAAc,CAAC,CAAA;QACzB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAA;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAA;QACpC,CAAC;QAED,OAAO;YACL,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;YACrC,UAAU,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,cAAc,CAAC;YAC1D,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,EAAE;SAChC,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AAyBD;;;;;GAKG;AACH,SAAS,mBAAmB,CAAC,OAA+B;IAC1D,IAAI,CAAC;QACH,oEAAoE;QACpE,mEAAmE;QACnE,kEAAkE;QAClE,oEAAoE;QACpE,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACrC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;SAChD,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;QAC3D,uCAAuC;QACvC,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,UAAU,UAAU,CACxB,aAAyB,EAAE,EAC3B,UAA6B,EAAE;IAE/B,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAA;IAC1E,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QACzD,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,CAAA;YACZ,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,oCAAoC;IACpC,MAAM,gBAAgB,GAAG,UAAU,CAAC,iBAAiB,IAAI,EAAE,CAAA;IAC3D,MAAM,WAAW,GAAG;QAClB,GAAG,IAAI,GAAG,CAAC;YACT,GAAG,gBAAgB;YACnB,GAAG,iBAAiB;YACpB,GAAG,iBAAiB;YACpB,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;SAC5B,CAAC;KACH,CAAA;IAED,MAAM,UAAU,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAA;IAE/C,4EAA4E;IAC5E,0EAA0E;IAC1E,+EAA+E;IAC/E,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,CAAA;IAC1C,MAAM,OAAO,GAA0B,UAAU;QAC/C,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YACd,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;YAC9C,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAA;YAE/C,4EAA4E;YAC5E,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBACvB,IAAI,EAAE,0BAA0B;gBAChC,OAAO,EAAE,gBAAgB;gBACzB,OAAO,EAAE;oBACP,KAAK,EAAE,UAAU;iBAClB;aACF,CAAC,CAAA;YAEF,2CAA2C;YAC3C,IAAI,OAAO,eAAe,KAAK,UAAU,EAAE,CAAC;gBAC1C,OAAO,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YACrC,CAAC;YACD,OAAO,MAAM,CAAA;QACf,CAAC;QACH,CAAC,CAAC,eAAe,CAAA;IAEnB,wEAAwE;IACxE,sEAAsE;IACtE,kEAAkE;IAClE,sEAAsE;IACtE,2EAA2E;IAE3E,0FAA0F;IAC1F,2FAA2F;IAC3F,oFAAoF;IACpF,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,cAAc,GAAG;QACrB,GAAG,IAAI,GAAG,CAAC;YACT,GAAG,CAAC,UAAU,CAAC,cAAc,IAAI,8BAA8B,CAAC;YAChE,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,mBAAmB,CAAC;SACxE,CAAC;KACH,CAAA;IAED,OAAO;QACL,GAAG,UAAU;QACb,iBAAiB,EAAE,WAAW;QAC9B,cAAc;QACd,OAAO;QACP,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,QAAQ,CAAC;KACjD,CAAA;AACH,CAAC"}