@avocadostudio-ai/shared 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-responses.d.ts +5 -0
- package/dist/api-responses.js +12 -0
- package/dist/block-manifest.d.ts +18 -10
- package/dist/block-manifest.js +114 -1
- package/dist/blocks/_helpers.d.ts +21 -0
- package/dist/blocks/_helpers.js +21 -0
- package/dist/blocks/_registry.d.ts +61 -2
- package/dist/blocks/_registry.js +88 -1
- package/dist/blocks/banner.js +3 -1
- package/dist/blocks/card-grid.js +3 -1
- package/dist/blocks/card.js +3 -1
- package/dist/blocks/carousel.js +3 -1
- package/dist/blocks/cta.js +7 -3
- package/dist/blocks/feature-grid.js +1 -1
- package/dist/blocks/hero.js +7 -3
- package/dist/blocks/site-header.js +3 -1
- package/dist/blocks/stats.js +1 -1
- package/dist/blocks/table.js +5 -2
- package/dist/blocks/testimonials.js +1 -1
- package/dist/blocks/two-column.js +49 -3
- package/dist/editable-coverage.d.ts +85 -0
- package/dist/editable-coverage.js +342 -0
- package/dist/editable-path.d.ts +11 -0
- package/dist/editable-path.js +17 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/links.d.ts +243 -0
- package/dist/links.js +509 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
export { EDITOR_PROTOCOL_VERSION } from "./protocol.ts";
|
|
2
2
|
export { getConfiguredDraftSecret, getSafeInternalRedirectPath, validateDraftSecret, type DraftSecretValidationResult } from "./draft-mode.ts";
|
|
3
3
|
export type { FieldDiffKind, FieldDiff, BlockDiffStatus, BlockDiff, PageDiffStatus, PageDiff, PublishDiff, SiteConfigFieldDiff, SiteConfigDiff, } from "./publish-diff.ts";
|
|
4
|
-
export { isImagePath, toAltPath, setPropAtPath } from "./editable-path.ts";
|
|
4
|
+
export { isImagePath, toAltPath, isAltPath, toImagePath, setPropAtPath } from "./editable-path.ts";
|
|
5
|
+
export { extractMarkedBlocks, editableCoverage, formatEditableCoverage } from "./editable-coverage.ts";
|
|
6
|
+
export type { MarkedBlock, BlockCoverageGap, EditableCoverage } from "./editable-coverage.ts";
|
|
7
|
+
export { parseLink, resolveLink, normalizeLinkPath, isKnownRoute, internalPathForUrl, rankLinkTargets, rankFileTargets, suggestLinkTargets, scoreLinkCandidate, suggestLinkTarget, newTabKeyFor, linkAttrs, isFilePath, knownFileExtensions, linksInRichText, type LinkKind, type ParsedLink, type ResolvedLink, type LinkPageOption, type LinkFileOption, type LinkSuggestions, } from "./links.ts";
|
|
5
8
|
export { parseInline, parseRichText, parseRichTextBlocks, normalizeRichTextBody, resolveRichTextHeadingLevel, clampMarkdownHeadings, unescapeMarkdownText, isRichTextDoc, fromMarkdown, toMarkdown, mergeRichTextDoc, NODE, MARK, type InlineToken, type RichTextBlock, type RichTextList, type RichTextListItem, type RichTextDoc, type RichTextNode, type RichTextMark } from "@avocadostudio-ai/richtext";
|
|
6
9
|
export { blockDefinitionSchema, blockManifestSchema, buildBlockManifest, jsonSchemaLikeSchema, validateByJsonSchemaLike, findManifestSchemaIssue, type ManifestSchemaIssue, validateManifestDefaultProps, deriveFieldMetaFromSchema, resolveManifestFieldMeta, isProseMirrorDocSchema, type BlockDefinition, type BlockManifest } from "./block-manifest.ts";
|
|
7
10
|
export { z } from "zod";
|
|
8
|
-
export { type FieldKind, type ImageSpec, type FieldMeta, type ListFieldMeta, type BlockMeta, type BlockType, type BlockInstance, type BlockRegistration, IMAGE_PLACEHOLDER, isImagePlaceholder, registerBlock, resetListFieldWarnings, getBlockMeta, getAllBlockMeta, getImageFields, getListImageFields, isFieldInlineEditable, getImageSpec, isChrome, getChromeTypes, blockSchemas, allowedBlockTypes, declareBlockCatalogue, getBlockCatalogue, isInBlockCatalogue, catalogueBlockTypes, undeclaredBlockTypes, getPropDisplayName, defaultListItemForBlock, blockInstanceSchema, blockInstanceSchemaLenient, validateBlockProps, getBlockJsonSchema, } from "./blocks/_registry.ts";
|
|
11
|
+
export { type FieldKind, type ImageSpec, type FieldMeta, type ListFieldMeta, type BlockMeta, type BlockType, type BlockInstance, type BlockRegistration, IMAGE_PLACEHOLDER, isImagePlaceholder, registerBlock, resetListFieldWarnings, getBlockMeta, getAllBlockMeta, blockAcceptsProp, blockListItemAcceptsKey, getImageFields, getListImageFields, getMediaFields, isFieldInlineEditable, getImageSpec, isChrome, getChromeTypes, blockSchemas, allowedBlockTypes, declareBlockCatalogue, getBlockCatalogue, isInBlockCatalogue, catalogueBlockTypes, undeclaredBlockTypes, getPropDisplayName, defaultListItemForBlock, blockInstanceSchema, blockInstanceSchemaLenient, validateBlockProps, getBlockJsonSchema, } from "./blocks/_registry.ts";
|
|
9
12
|
export { defaultPropsForType, declaredDefaultPropsForType, resolveHeadingTag, resolveItemHeadingTag, DEFAULT_HEADING_LEVELS, } from "./blocks/index.ts";
|
|
10
13
|
export { blockTypeToCamel, camelToBlockType, blockTypeToLower, lowerToBlockType, } from "./block-names.ts";
|
|
11
14
|
export { makeAddBlock, generateBlockId, makeAddItem, generateItemId, ensureItemIds, type AddBlockOp, type MakeAddBlockOptions, type AddItemOp, type MakeAddItemOptions, } from "./ops/builders.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { EDITOR_PROTOCOL_VERSION } from "./protocol.js";
|
|
2
2
|
export { getConfiguredDraftSecret, getSafeInternalRedirectPath, validateDraftSecret } from "./draft-mode.js";
|
|
3
|
-
export { isImagePath, toAltPath, setPropAtPath } from "./editable-path.js";
|
|
3
|
+
export { isImagePath, toAltPath, isAltPath, toImagePath, setPropAtPath } from "./editable-path.js";
|
|
4
|
+
export { extractMarkedBlocks, editableCoverage, formatEditableCoverage } from "./editable-coverage.js";
|
|
5
|
+
export { parseLink, resolveLink, normalizeLinkPath, isKnownRoute, internalPathForUrl, rankLinkTargets, rankFileTargets, suggestLinkTargets, scoreLinkCandidate, suggestLinkTarget, newTabKeyFor, linkAttrs, isFilePath, knownFileExtensions, linksInRichText, } from "./links.js";
|
|
4
6
|
/*
|
|
5
7
|
* The rich-text grammar lives in `@avocadostudio-ai/richtext`, which owns the
|
|
6
8
|
* parser and every CMS converter and has no dependencies of its own. It is
|
|
@@ -33,7 +35,7 @@ export {
|
|
|
33
35
|
// Constants & helpers
|
|
34
36
|
IMAGE_PLACEHOLDER, isImagePlaceholder,
|
|
35
37
|
// Registry functions
|
|
36
|
-
registerBlock, resetListFieldWarnings, getBlockMeta, getAllBlockMeta, getImageFields, getListImageFields, isFieldInlineEditable, getImageSpec, isChrome, getChromeTypes,
|
|
38
|
+
registerBlock, resetListFieldWarnings, getBlockMeta, getAllBlockMeta, blockAcceptsProp, blockListItemAcceptsKey, getImageFields, getListImageFields, getMediaFields, isFieldInlineEditable, getImageSpec, isChrome, getChromeTypes,
|
|
37
39
|
// Backwards-compatible exports
|
|
38
40
|
blockSchemas, allowedBlockTypes,
|
|
39
41
|
// The catalogue a site actually renders — see `declareBlockCatalogue`
|
package/dist/links.d.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a link field's string actually means.
|
|
3
|
+
*
|
|
4
|
+
* A link is stored as a plain string — `/pricing`, `https://example.com`,
|
|
5
|
+
* `mailto:hi@example.com` — and three places need to agree on how to read it:
|
|
6
|
+
* the editor's link control (which icon, which label, which warning), the
|
|
7
|
+
* `seo.internal-link-dead` check (is this route real?), and the rename rewriter
|
|
8
|
+
* (is this string a route at all?). Before this module each answered the
|
|
9
|
+
* question with its own inline `startsWith("/")`, and they did not agree: the
|
|
10
|
+
* rename rewriter matched on prop *names* containing "href", so a link stored
|
|
11
|
+
* under `url` or inside Footer's `label|url` text went stale on every rename.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here validates in the blocking sense. A path this module cannot
|
|
14
|
+
* resolve is still a legitimate link — on a CMS-backed site Avocado sees only
|
|
15
|
+
* the pages it was handed, and a route can exist in Next, in the CMS, or behind
|
|
16
|
+
* a rewrite and be invisible from here. `resolveLink` reports what it knows; it
|
|
17
|
+
* never decides what the editor may type.
|
|
18
|
+
*/
|
|
19
|
+
/** How a link string addresses its target. */
|
|
20
|
+
export type LinkKind = "empty" | "page" | "file" | "external" | "email" | "phone" | "anchor";
|
|
21
|
+
/** Does this route-shaped path name a document rather than a page? */
|
|
22
|
+
export declare function isFilePath(path: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The file extensions this module recognises, for a caller that has to describe
|
|
25
|
+
* them — an upload control's `accept`, a picker's filter, a docs page.
|
|
26
|
+
*/
|
|
27
|
+
export declare function knownFileExtensions(): string[];
|
|
28
|
+
/** A link string, classified. */
|
|
29
|
+
export type ParsedLink = {
|
|
30
|
+
kind: LinkKind;
|
|
31
|
+
/** The original string, untouched. */
|
|
32
|
+
raw: string;
|
|
33
|
+
/**
|
|
34
|
+
* The route, for `kind: "page"` — path only, with `?query` and `#fragment`
|
|
35
|
+
* stripped. This is what `resolveLink` matches against the site's slugs.
|
|
36
|
+
*/
|
|
37
|
+
path?: string;
|
|
38
|
+
/** The `?a=b#c` tail of a page link, preserved so a rewrite can restore it. */
|
|
39
|
+
suffix?: string;
|
|
40
|
+
/** Host for `external`, address for `email`, number for `phone`, id for `anchor`. */
|
|
41
|
+
target?: string;
|
|
42
|
+
};
|
|
43
|
+
/** A page the editor can offer as a link target. */
|
|
44
|
+
export type LinkPageOption = {
|
|
45
|
+
slug: string;
|
|
46
|
+
/** The URL, when the site says it differs from the slug (locale prefixes). */
|
|
47
|
+
path?: string;
|
|
48
|
+
title?: string;
|
|
49
|
+
};
|
|
50
|
+
/** Classify a link string. Never throws; anything unrecognised parses as `page`-ish text. */
|
|
51
|
+
export declare function parseLink(value: unknown): ParsedLink;
|
|
52
|
+
/**
|
|
53
|
+
* Give a route its leading slash and drop a trailing one.
|
|
54
|
+
*
|
|
55
|
+
* `pricing`, `/pricing`, `/pricing/` are the same page. The editor accepts all
|
|
56
|
+
* three and stores the middle one. The homepage keeps its lone slash.
|
|
57
|
+
*/
|
|
58
|
+
export declare function normalizeLinkPath(path: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Does this route exist among the site's known pages?
|
|
61
|
+
*
|
|
62
|
+
* Compares on the path alone — a link may legitimately carry a query or a
|
|
63
|
+
* fragment, and neither changes which page it points at — and accepts a
|
|
64
|
+
* trailing slash on either side. This is the matcher `seo.internal-link-dead`
|
|
65
|
+
* uses, extracted so the inline field warning and the checks panel cannot
|
|
66
|
+
* disagree about what "dead" means.
|
|
67
|
+
*/
|
|
68
|
+
export declare function isKnownRoute(path: string, knownSlugs: Iterable<string>): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* A document the site can link to, as the editor needs to know it.
|
|
71
|
+
*
|
|
72
|
+
* `path` is what goes in the link. Everything else is for showing it to a
|
|
73
|
+
* person, and every field but `path` is optional because a site that answers
|
|
74
|
+
* this question from a directory listing has only the path.
|
|
75
|
+
*/
|
|
76
|
+
export type LinkFileOption = {
|
|
77
|
+
/** The URL to link to — `/downloads/menu-de.pdf`. */
|
|
78
|
+
path: string;
|
|
79
|
+
/** What to call it in a picker. Defaults to the filename. */
|
|
80
|
+
name?: string;
|
|
81
|
+
/** MIME type, when the store knows it. */
|
|
82
|
+
contentType?: string;
|
|
83
|
+
/** Bytes, when the store knows it — a picker shows it, nothing decides on it. */
|
|
84
|
+
size?: number;
|
|
85
|
+
};
|
|
86
|
+
/** A parsed link plus what the site knows about its target. */
|
|
87
|
+
export type ResolvedLink = ParsedLink & {
|
|
88
|
+
/** The matching page, when `kind` is `page` and the route is known. */
|
|
89
|
+
page?: LinkPageOption;
|
|
90
|
+
/** The matching document, when `kind` is `file` and the asset list has it. */
|
|
91
|
+
file?: LinkFileOption;
|
|
92
|
+
/**
|
|
93
|
+
* True for a link whose target matches nothing the site knows about.
|
|
94
|
+
*
|
|
95
|
+
* For a `page` this is decided from the slug list, which is always present.
|
|
96
|
+
* For a `file` it is decided from the asset list, which is *not* — a site
|
|
97
|
+
* that cannot enumerate its documents passes none, and then `missing` stays
|
|
98
|
+
* undefined rather than becoming `true`. "We did not check" and "it is not
|
|
99
|
+
* there" have to stay distinguishable, or every site without an asset store
|
|
100
|
+
* would report all of its own documents as broken.
|
|
101
|
+
*/
|
|
102
|
+
missing?: boolean;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Classify a link *and* look up its target. Pages may be keyed by `slug` or by
|
|
106
|
+
* `path` (they differ on locale-prefixed sites — see
|
|
107
|
+
* `docs/ideas/page-identity-punch-list.md`), so both are matched.
|
|
108
|
+
*
|
|
109
|
+
* `files` is optional and its absence is meaningful — see `missing` above.
|
|
110
|
+
*/
|
|
111
|
+
export declare function resolveLink(value: unknown, pages?: readonly LinkPageOption[], files?: readonly LinkFileOption[]): ResolvedLink;
|
|
112
|
+
/**
|
|
113
|
+
* How well a page answers what was typed, from 0 (nothing in common) to 1.
|
|
114
|
+
*
|
|
115
|
+
* The balanced mean of precision and recall over shared words. Balanced because
|
|
116
|
+
* both halves are wrong alone: precision by itself ranks a sprawling page that
|
|
117
|
+
* happens to contain the word above the page actually named for it, and recall
|
|
118
|
+
* by itself ranks a one-word slug above every longer match.
|
|
119
|
+
*
|
|
120
|
+
* Pass the site's pages as `corpus` to weight words by how distinctive they are
|
|
121
|
+
* (see `tokenWeights`); without it every word counts the same.
|
|
122
|
+
*/
|
|
123
|
+
export declare function scoreLinkCandidate(query: string, page: LinkPageOption, corpus?: readonly LinkPageOption[]): number;
|
|
124
|
+
/**
|
|
125
|
+
* Known pages ranked by how well they answer `query`, best first.
|
|
126
|
+
*
|
|
127
|
+
* Held to the same floor as `suggestLinkTarget`, which it did not used to be:
|
|
128
|
+
* anything sharing a single token came back, so on a tri-lingual site every
|
|
129
|
+
* `/fr/*` page answered every French-flavoured query. Typing a document path
|
|
130
|
+
* into the link picker listed `/fr/`, `/fr/faq/` and `/fr/evenements/` — six
|
|
131
|
+
* rows of pages, each one click away from replacing a working PDF link with a
|
|
132
|
+
* link to the FAQ. A near-miss still ranks well above the floor; what the floor
|
|
133
|
+
* removes is the coincidence.
|
|
134
|
+
*/
|
|
135
|
+
export declare function rankLinkTargets(query: string, pages: readonly LinkPageOption[]): LinkPageOption[];
|
|
136
|
+
/**
|
|
137
|
+
* Known documents ranked by how well they answer `query`, best first.
|
|
138
|
+
*
|
|
139
|
+
* The same scorer as pages, and the corpus weighting is what makes it work
|
|
140
|
+
* here: every document on a site shares its directory and its extension, so
|
|
141
|
+
* `downloads` and `pdf` identify nothing and are weighted to nearly nothing,
|
|
142
|
+
* while the part of the filename someone got wrong is what decides the order.
|
|
143
|
+
*
|
|
144
|
+
* This is the half a substring filter cannot do. `AadventureArenaBerm` is a
|
|
145
|
+
* real filename on a real site, typo included; a person typing it from memory
|
|
146
|
+
* gets one character wrong and a substring match returns nothing at all —
|
|
147
|
+
* which reads exactly like "this site has no such document".
|
|
148
|
+
*/
|
|
149
|
+
export declare function rankFileTargets(query: string, files: readonly LinkFileOption[]): LinkFileOption[];
|
|
150
|
+
/** What a link picker should offer for what has been typed so far. */
|
|
151
|
+
export type LinkSuggestions = {
|
|
152
|
+
pages: LinkPageOption[];
|
|
153
|
+
files: LinkFileOption[];
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* The pages and documents to offer for a partly-typed link.
|
|
157
|
+
*
|
|
158
|
+
* Two surfaces ask this question — the property panel's link field and the
|
|
159
|
+
* prose editor's link popover — and they answered it with their own inline
|
|
160
|
+
* copies of "substring, else rank". The copies disagreed, and both made the
|
|
161
|
+
* same mistake: they ranked *pages* for a query the parser had already
|
|
162
|
+
* classified as a document. A screenshot of the result is why this function
|
|
163
|
+
* exists — `/downloads/…-Gruppen-FR.pdf` typed in, six pages offered, not one
|
|
164
|
+
* of the site's fifteen PDFs among them.
|
|
165
|
+
*
|
|
166
|
+
* So the kind decides which list is offered at all:
|
|
167
|
+
*
|
|
168
|
+
* - a document path offers documents, never pages;
|
|
169
|
+
* - a route offers pages, and any document whose path literally contains what
|
|
170
|
+
* was typed (`menu` should still find the menu PDFs);
|
|
171
|
+
* - `mailto:`, `tel:`, `#anchor` and `https://` offer neither — there is
|
|
172
|
+
* nothing on this site they could mean, and the caller's empty state can say
|
|
173
|
+
* so instead.
|
|
174
|
+
*
|
|
175
|
+
* Within a kind, a literal substring is what a person typing into a box
|
|
176
|
+
* expects, and ranking is the fallback for when nothing matches literally.
|
|
177
|
+
*/
|
|
178
|
+
export declare function suggestLinkTargets(query: string, options?: {
|
|
179
|
+
pages?: readonly LinkPageOption[];
|
|
180
|
+
files?: readonly LinkFileOption[];
|
|
181
|
+
limit?: number;
|
|
182
|
+
/**
|
|
183
|
+
* With an empty box, list the site's first documents alongside its pages.
|
|
184
|
+
* The prose picker wants that — it is how an editor discovers the site has
|
|
185
|
+
* documents at all. The link *field* does not: a document shelf under every
|
|
186
|
+
* link field would bury the pages, which is what a link usually wants.
|
|
187
|
+
*/
|
|
188
|
+
browseFiles?: boolean;
|
|
189
|
+
}): LinkSuggestions;
|
|
190
|
+
/**
|
|
191
|
+
* The page a dead internal link probably meant.
|
|
192
|
+
*
|
|
193
|
+
* `/avocado-sustainability` on a site that has `/sustainability` is a typo, a
|
|
194
|
+
* stale link, or a slug someone renamed by hand — and the site already holds
|
|
195
|
+
* everything needed to say so. Returns undefined when nothing scores well
|
|
196
|
+
* enough, or when the link already resolves; the caller offers what comes back,
|
|
197
|
+
* and never applies it on the editor's behalf.
|
|
198
|
+
*/
|
|
199
|
+
export declare function suggestLinkTarget(value: unknown, pages: readonly LinkPageOption[]): LinkPageOption | undefined;
|
|
200
|
+
/**
|
|
201
|
+
* The absolute URL a viewer would copy out of the address bar, turned back into
|
|
202
|
+
* an internal route — when it belongs to this site.
|
|
203
|
+
*
|
|
204
|
+
* Pasting the address bar is how people actually get a URL, and an absolute one
|
|
205
|
+
* survives right up until the site moves domain. Returns undefined when the
|
|
206
|
+
* origin is someone else's, so the caller leaves the link alone.
|
|
207
|
+
*/
|
|
208
|
+
export declare function internalPathForUrl(value: string, siteOrigin: string | undefined): string | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* The prop holding the "open in new tab" flag for a link prop, by convention.
|
|
211
|
+
*
|
|
212
|
+
* `ctaHref` → `ctaNewTab`, `secondaryCtaHref` → `secondaryCtaNewTab`,
|
|
213
|
+
* `href` → `newTab`. The same shape as the `image`/`imageAlt` pairing the
|
|
214
|
+
* property panel already resolves by name, so a block declares two props and
|
|
215
|
+
* the editor renders one control.
|
|
216
|
+
*/
|
|
217
|
+
export declare function newTabKeyFor(linkKey: string): string;
|
|
218
|
+
/** Anchor attributes for a link, given its stored value and new-tab flag. */
|
|
219
|
+
export declare function linkAttrs(href: unknown, newTab?: unknown): {
|
|
220
|
+
href: string;
|
|
221
|
+
target?: string;
|
|
222
|
+
rel?: string;
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Every href inside a richtext value.
|
|
226
|
+
*
|
|
227
|
+
* A link is not only a `link`-kind prop. Most of the links on a real page are
|
|
228
|
+
* written *into* prose — `[Menükarte](/downloads/menu-de.pdf)` — and every
|
|
229
|
+
* link-aware surface we have was walking declared fields only. So the four
|
|
230
|
+
* menu-PDF links on a live site's Bistro section were not checked, not
|
|
231
|
+
* rewritten on rename, and not reported; the one linking to a filename with a
|
|
232
|
+
* typo in it had been wrong since August with nothing able to notice.
|
|
233
|
+
*
|
|
234
|
+
* Three shapes, because a richtext value is three things depending on where it
|
|
235
|
+
* came from: markdown (a site that projects its CMS prose to markdown), a
|
|
236
|
+
* ProseMirror document (the editor's own format), and raw HTML (a block with a
|
|
237
|
+
* loose schema). Walking all three costs one function and means a caller never
|
|
238
|
+
* has to know which it was handed.
|
|
239
|
+
*
|
|
240
|
+
* Returns hrefs in document order, duplicates included — a caller that reports
|
|
241
|
+
* findings wants one per occurrence, and a caller that wants a set can make one.
|
|
242
|
+
*/
|
|
243
|
+
export declare function linksInRichText(value: unknown): string[];
|