@murumets-ee/content 0.0.0-preview-20261014110621
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/LICENSE +94 -0
- package/README.md +15 -0
- package/dist/admin.d.mts +56 -0
- package/dist/admin.d.mts.map +1 -0
- package/dist/admin.mjs +1 -0
- package/dist/block-resolver.d.mts +320 -0
- package/dist/block-resolver.d.mts.map +1 -0
- package/dist/block-resolver.mjs +2 -0
- package/dist/block-resolver.mjs.map +1 -0
- package/dist/breadcrumbs-BqSuMNac.mjs +2 -0
- package/dist/breadcrumbs-BqSuMNac.mjs.map +1 -0
- package/dist/chrome-entity-names-zYJzP-_U.mjs +2 -0
- package/dist/chrome-entity-names-zYJzP-_U.mjs.map +1 -0
- package/dist/chrome-provider.d.mts +143 -0
- package/dist/chrome-provider.d.mts.map +1 -0
- package/dist/chrome-provider.mjs +2 -0
- package/dist/chrome-provider.mjs.map +1 -0
- package/dist/client.d.mts +89 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +2 -0
- package/dist/client.mjs.map +1 -0
- package/dist/content-config-store-8omQF5p5.mjs +2 -0
- package/dist/content-config-store-8omQF5p5.mjs.map +1 -0
- package/dist/context-D7eEItCY.mjs +2 -0
- package/dist/context-D7eEItCY.mjs.map +1 -0
- package/dist/detail-template-names-CD1Y9n1U.mjs +2 -0
- package/dist/detail-template-names-CD1Y9n1U.mjs.map +1 -0
- package/dist/detail-template-provider.d.mts +28 -0
- package/dist/detail-template-provider.d.mts.map +1 -0
- package/dist/detail-template-provider.mjs +2 -0
- package/dist/detail-template-provider.mjs.map +1 -0
- package/dist/document-site-B8tmEqLw.mjs +2 -0
- package/dist/document-site-B8tmEqLw.mjs.map +1 -0
- package/dist/entities/index.d.mts +64 -0
- package/dist/entities/index.d.mts.map +1 -0
- package/dist/entities/index.mjs +2 -0
- package/dist/entities/index.mjs.map +1 -0
- package/dist/enumerate-CYHJKQz6.mjs +2 -0
- package/dist/enumerate-CYHJKQz6.mjs.map +1 -0
- package/dist/errors-NRNILhhm.mjs +2 -0
- package/dist/errors-NRNILhhm.mjs.map +1 -0
- package/dist/index-cT8Qk4gu.d.mts +275 -0
- package/dist/index-cT8Qk4gu.d.mts.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/locale-entity-CoSq3C77.mjs +2 -0
- package/dist/locale-entity-CoSq3C77.mjs.map +1 -0
- package/dist/locale-registry-COsJrVZl.mjs +2 -0
- package/dist/locale-registry-COsJrVZl.mjs.map +1 -0
- package/dist/locale-utils-B0mFoV47.mjs +2 -0
- package/dist/locale-utils-B0mFoV47.mjs.map +1 -0
- package/dist/lock-service.d.mts +58 -0
- package/dist/lock-service.d.mts.map +1 -0
- package/dist/lock-service.mjs +2 -0
- package/dist/lock-service.mjs.map +1 -0
- package/dist/locks-5779ugSW.mjs +2 -0
- package/dist/locks-5779ugSW.mjs.map +1 -0
- package/dist/plugin-CjgqBSFy.mjs +2 -0
- package/dist/plugin-CjgqBSFy.mjs.map +1 -0
- package/dist/plugin.d.mts +41 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +1 -0
- package/dist/query-client.d.mts +131 -0
- package/dist/query-client.d.mts.map +1 -0
- package/dist/query-client.mjs +2 -0
- package/dist/query-client.mjs.map +1 -0
- package/dist/registry-CaPbY5lm.mjs +2 -0
- package/dist/registry-CaPbY5lm.mjs.map +1 -0
- package/dist/resolve-context-DklN53BH.mjs +2 -0
- package/dist/resolve-context-DklN53BH.mjs.map +1 -0
- package/dist/resolve.d.mts +651 -0
- package/dist/resolve.d.mts.map +1 -0
- package/dist/resolve.mjs +9 -0
- package/dist/resolve.mjs.map +1 -0
- package/dist/rolldown-runtime-DK3Fl9T5.mjs +1 -0
- package/dist/routes-siXmjKDA.mjs +2 -0
- package/dist/routes-siXmjKDA.mjs.map +1 -0
- package/dist/routing-strategy-CAG3tZP0.d.mts +155 -0
- package/dist/routing-strategy-CAG3tZP0.d.mts.map +1 -0
- package/dist/routing-strategy-D6B4ngT9.mjs +2 -0
- package/dist/routing-strategy-D6B4ngT9.mjs.map +1 -0
- package/dist/schema-BYrYzsmO.mjs +2 -0
- package/dist/schema-BYrYzsmO.mjs.map +1 -0
- package/dist/schema-LlK6EkOJ.mjs +2 -0
- package/dist/schema-LlK6EkOJ.mjs.map +1 -0
- package/dist/seed.d.mts +177 -0
- package/dist/seed.d.mts.map +1 -0
- package/dist/seed.mjs +2 -0
- package/dist/seed.mjs.map +1 -0
- package/dist/site-chrome-BAtQaAqC.mjs +2 -0
- package/dist/site-chrome-BAtQaAqC.mjs.map +1 -0
- package/dist/site-chrome-DNCcePSR.d.mts +98 -0
- package/dist/site-chrome-DNCcePSR.d.mts.map +1 -0
- package/dist/translation-provenance-B41lEDM-.mjs +2 -0
- package/dist/translation-provenance-B41lEDM-.mjs.map +1 -0
- package/dist/translation-provenance-BZ0MlbFR.d.mts +253 -0
- package/dist/translation-provenance-BZ0MlbFR.d.mts.map +1 -0
- package/dist/translation.d.mts +144 -0
- package/dist/translation.d.mts.map +1 -0
- package/dist/translation.mjs +2 -0
- package/dist/translation.mjs.map +1 -0
- package/dist/types-BOk9tg_9.d.mts +150 -0
- package/dist/types-BOk9tg_9.d.mts.map +1 -0
- package/dist/versionable-Br_G5VGR.mjs +2 -0
- package/dist/versionable-Br_G5VGR.mjs.map +1 -0
- package/dist/versionable-CRvV87nZ.d.mts +18 -0
- package/dist/versionable-CRvV87nZ.d.mts.map +1 -0
- package/dist/versions-w2ui-Vzb.mjs +2 -0
- package/dist/versions-w2ui-Vzb.mjs.map +1 -0
- package/package.json +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Elastic License 2.0 (ELv2)
|
|
2
|
+
|
|
3
|
+
URL: https://www.elastic.co/licensing/elastic-license
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
12
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
13
|
+
available, and prepare derivative works of the software, in each case subject
|
|
14
|
+
to the limitations and conditions below.
|
|
15
|
+
|
|
16
|
+
## Limitations
|
|
17
|
+
|
|
18
|
+
You may not provide the software to third parties as a hosted or managed
|
|
19
|
+
service, where the service provides users with access to any substantial set
|
|
20
|
+
of the features or functionality of the software.
|
|
21
|
+
|
|
22
|
+
You may not move, change, disable, or circumvent the license key functionality
|
|
23
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
24
|
+
software that is protected by the license key.
|
|
25
|
+
|
|
26
|
+
You may not alter, remove, or obscure any licensing, copyright, or other
|
|
27
|
+
notices of the licensor in the software. Any use of the licensor's trademarks
|
|
28
|
+
is subject to applicable law.
|
|
29
|
+
|
|
30
|
+
## Patents
|
|
31
|
+
|
|
32
|
+
The licensor grants you a license, under any patent claims the licensor can
|
|
33
|
+
license, or becomes able to license, to make, have made, use, sell, offer for
|
|
34
|
+
sale, import and have imported the software, in each case subject to the
|
|
35
|
+
limitations and conditions in this license. This license does not cover any
|
|
36
|
+
patent claims that you cause to be infringed by modifications or additions to
|
|
37
|
+
the software. If you or your company make any written claim that the software
|
|
38
|
+
infringes or contributes to infringement of any patent, your patent license
|
|
39
|
+
for the software granted under these terms ends immediately. If your company
|
|
40
|
+
makes such a claim, your patent license ends immediately for work on behalf
|
|
41
|
+
of your company.
|
|
42
|
+
|
|
43
|
+
## Notices
|
|
44
|
+
|
|
45
|
+
You must ensure that anyone who gets a copy of any part of the software from
|
|
46
|
+
you also gets a copy of these terms.
|
|
47
|
+
|
|
48
|
+
If you modify the software, you must include in any modified copies of the
|
|
49
|
+
software prominent notices stating that you have modified the software.
|
|
50
|
+
|
|
51
|
+
## No Other Rights
|
|
52
|
+
|
|
53
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
54
|
+
these terms.
|
|
55
|
+
|
|
56
|
+
## Termination
|
|
57
|
+
|
|
58
|
+
If you use the software in violation of these terms, such use is not licensed,
|
|
59
|
+
and your licenses will automatically terminate. If the licensor provides you
|
|
60
|
+
with a notice of your violation, and you cease all violation of this license
|
|
61
|
+
no later than 30 days after you receive that notice, your licenses will be
|
|
62
|
+
reinstated retroactively. However, if you violate these terms after such
|
|
63
|
+
reinstatement, any additional violation of these terms will cause your
|
|
64
|
+
licenses to terminate automatically and permanently.
|
|
65
|
+
|
|
66
|
+
## No Liability
|
|
67
|
+
|
|
68
|
+
As far as the law allows, the software comes as is, without any warranty or
|
|
69
|
+
condition, and the licensor will not be liable to you for any damages arising
|
|
70
|
+
out of these terms or the use or nature of the software, under any kind of
|
|
71
|
+
legal claim.
|
|
72
|
+
|
|
73
|
+
## Definitions
|
|
74
|
+
|
|
75
|
+
The **licensor** is the entity offering these terms, and the **software** is
|
|
76
|
+
the software the licensor makes available under these terms, including any
|
|
77
|
+
portion of it.
|
|
78
|
+
|
|
79
|
+
**you** refers to the individual or entity agreeing to these terms.
|
|
80
|
+
|
|
81
|
+
**your company** is any legal entity, sole proprietorship, or other kind of
|
|
82
|
+
organization that you work for, plus all organizations that have control over,
|
|
83
|
+
are under the control of, or are under common control with that organization.
|
|
84
|
+
**control** means ownership of substantially all the assets of an entity, or
|
|
85
|
+
the power to direct the management and policies of an entity (for example, by
|
|
86
|
+
voting right, contract, or otherwise). Control can be direct or indirect.
|
|
87
|
+
|
|
88
|
+
**your licenses** are all the licenses granted to you for the software under
|
|
89
|
+
these terms.
|
|
90
|
+
|
|
91
|
+
**use** means anything you do with the software requiring one of your
|
|
92
|
+
licenses.
|
|
93
|
+
|
|
94
|
+
**trademark** means trademarks, service marks, and similar rights.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @murumets-ee/content
|
|
2
|
+
|
|
3
|
+
Content management — localization, publishing workflows, versioning, drafts, and translation status.
|
|
4
|
+
|
|
5
|
+
Part of [Lumi CMS](https://github.com/murumets-ee/lumi-cms) — a modular, type-safe CMS toolkit for Node.js.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @murumets-ee/content
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
|
|
15
|
+
[Elastic License 2.0 (ELv2)](../../LICENSE)
|
package/dist/admin.d.mts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AdminRoute } from "@murumets-ee/core";
|
|
2
|
+
|
|
3
|
+
//#region src/paths/routes.d.ts
|
|
4
|
+
/** Route permission + the roles that hold it by default. */
|
|
5
|
+
declare const PATH_RESOLVE_PERMISSION = "path:resolve";
|
|
6
|
+
/** The answer shape the caller (admin-ui's editor canvas) consumes. */
|
|
7
|
+
type ResolvePathResponse =
|
|
8
|
+
/**
|
|
9
|
+
* The path is served by this document.
|
|
10
|
+
*
|
|
11
|
+
* `form` is present exactly when the document's TYPE has no canvas (no
|
|
12
|
+
* `field.blockTree()` — an article with a rich-text body): the editor has
|
|
13
|
+
* nothing to draw it with, so preview shows the site's own page instead and
|
|
14
|
+
* the bar needs a name for it and a way to its form (editor-refurb D017).
|
|
15
|
+
* Absent for a type with a canvas, whose answer is unchanged.
|
|
16
|
+
*/
|
|
17
|
+
{
|
|
18
|
+
readonly kind: 'document';
|
|
19
|
+
readonly entityType: string;
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly form?: ResolvedFormDocument;
|
|
22
|
+
} /** The path redirects OFF-SITE (a `url`-kind manual redirect). */ | {
|
|
23
|
+
readonly kind: 'redirect';
|
|
24
|
+
readonly to: string;
|
|
25
|
+
} /** Nothing the caller may see owns this path. */ | {
|
|
26
|
+
readonly kind: 'none';
|
|
27
|
+
};
|
|
28
|
+
/** What the editor may say about a document it cannot draw. */
|
|
29
|
+
interface ResolvedFormDocument {
|
|
30
|
+
/** The row's title in the asked locale; `null` when it has none or the read failed. */
|
|
31
|
+
readonly title: string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Whether the SITE shows it: `false` for a draft of a publishable type (the
|
|
34
|
+
* front serves published content only, so there is nothing to frame), `true`
|
|
35
|
+
* otherwise. `null` when the row could not be read.
|
|
36
|
+
*/
|
|
37
|
+
readonly published: boolean | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build the path-resolution admin routes. One entry: `GET /paths/resolve`.
|
|
41
|
+
*/
|
|
42
|
+
declare function pathRoutes(): AdminRoute[];
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/redirects/routes.d.ts
|
|
45
|
+
/**
|
|
46
|
+
* Build the manual-redirect admin API routes.
|
|
47
|
+
*
|
|
48
|
+
* Returns `AdminRoute[]` from `combineAdminRoutes` — one entry for prefix
|
|
49
|
+
* `'redirects'` with GET/POST/PATCH/DELETE handlers. Every entry gates on
|
|
50
|
+
* `redirect:manage` (defaultRoles `['admin']`); the permission is
|
|
51
|
+
* auto-registered into the catalog by `defineAdminRoute`.
|
|
52
|
+
*/
|
|
53
|
+
declare function redirectRoutes(): AdminRoute[];
|
|
54
|
+
//#endregion
|
|
55
|
+
export { PATH_RESOLVE_PERMISSION, type ResolvePathResponse, pathRoutes, redirectRoutes };
|
|
56
|
+
//# sourceMappingURL=admin.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.d.mts","names":[],"sources":["../src/paths/routes.ts","../src/redirects/routes.ts"],"mappings":";;;;cA+Da,uBAAA;;KAWD,mBAAA;;;;;;;;;;;WAWG,IAAA;EAAA,SACA,UAAA;EAAA,SACA,EAAA;EAAA,SACA,IAAA,GAAO,oBAAoB;AAAA;WAG3B,IAAA;EAAA,SAA2B,EAAA;AAAA;WAE3B,IAAA;AAAA;;UAGE,oBAAA;;WAEN,KAAA;;;;;;WAMA,SAAS;AAAA;;;;iBAwKJ,UAAA,IAAc,UAAU;;;AC6RxC;;;;AAA4C;;;;AAA5C,iBAAgB,cAAA,IAAkB,UAAU"}
|
package/dist/admin.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t,t as n}from"./routes-siXmjKDA.mjs";export{e as PATH_RESOLVE_PERMISSION,t as pathRoutes,n as redirectRoutes};
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { Entity } from "@murumets-ee/entity";
|
|
2
|
+
import { ToolkitApp } from "@murumets-ee/core";
|
|
3
|
+
import { CollectionFieldLookup, CollectionResult, CollectionSourceSpec, DocumentBreadcrumb, DocumentDetail, DocumentDetailPart, EmbedLookup, EmbedReference, FindManyClientLike, LiveValues, TemplateNamespace } from "@murumets-ee/blocks/server";
|
|
4
|
+
|
|
5
|
+
//#region src/embedded-documents.d.ts
|
|
6
|
+
/** What the resolver needs to expand embeds; omitted → embeds show nothing. */
|
|
7
|
+
interface EmbeddedDocumentDeps {
|
|
8
|
+
readonly lookup: EmbedLookup;
|
|
9
|
+
/**
|
|
10
|
+
* The PUBLISHED top-level blocks of each referenced document, keyed by
|
|
11
|
+
* `embedReferenceKey`. A document that is unpublished, trashed, of another
|
|
12
|
+
* site or absent has NO entry. Must not throw for one bad id.
|
|
13
|
+
*/
|
|
14
|
+
readonly load: (refs: readonly EmbedReference[], locale: string) => Promise<ReadonlyMap<string, readonly unknown[]>>;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/block-resolver.d.ts
|
|
18
|
+
/** Structural mirror of `content-api`'s `MediaProjection`. */
|
|
19
|
+
interface BlockMediaProjection {
|
|
20
|
+
id: string;
|
|
21
|
+
url: string;
|
|
22
|
+
mimeType: string | null;
|
|
23
|
+
width: number | null;
|
|
24
|
+
height: number | null;
|
|
25
|
+
alt: string | null;
|
|
26
|
+
title: string | null;
|
|
27
|
+
variants: Record<string, string>;
|
|
28
|
+
sources: Record<string, Array<{
|
|
29
|
+
url: string;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
}>>;
|
|
33
|
+
avifSources?: Record<string, Array<{
|
|
34
|
+
url: string;
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
}>>;
|
|
38
|
+
focalPoint: {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
};
|
|
42
|
+
blurDataUrl: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* The files cut for each spot crop the photograph has files for (images
|
|
45
|
+
* PR16) — present only when the resolver was ASKED (`{ spots: true }`) and
|
|
46
|
+
* the photograph records some. Never embedded in a tree: {@link writeSlot}
|
|
47
|
+
* picks the one list a placement's own `crop` names and drops the rest.
|
|
48
|
+
*/
|
|
49
|
+
spotSources?: BlockSpotSources[];
|
|
50
|
+
}
|
|
51
|
+
/** One spot crop's files, as the media resolver publishes them. */
|
|
52
|
+
interface BlockSpotSources {
|
|
53
|
+
crop: {
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
w: number;
|
|
57
|
+
h: number;
|
|
58
|
+
};
|
|
59
|
+
sources: Array<{
|
|
60
|
+
url: string;
|
|
61
|
+
width: number;
|
|
62
|
+
height: number;
|
|
63
|
+
}>;
|
|
64
|
+
avifSources?: Array<{
|
|
65
|
+
url: string;
|
|
66
|
+
width: number;
|
|
67
|
+
height: number;
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Structural mirror of `content-api`'s `ResolveMedia`.
|
|
72
|
+
*
|
|
73
|
+
* The SAME function an app passes as `ContentApiConfig.resolveMedia` — pass one
|
|
74
|
+
* value to both, so a media ref inside a block cannot resolve through a
|
|
75
|
+
* different path (or a different publish gate) than a top-level media field.
|
|
76
|
+
*/
|
|
77
|
+
type BlockMediaResolver = (ids: readonly string[], opts?: {
|
|
78
|
+
locale?: string | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Also publish the files cut for spot crops (images PR16). Asked by the
|
|
81
|
+
* BLOCK walk only — a top-level media field has no placement crop, so its
|
|
82
|
+
* projection stays exactly what it was.
|
|
83
|
+
*/
|
|
84
|
+
spots?: boolean | undefined;
|
|
85
|
+
}) => Promise<Map<string, BlockMediaProjection>>;
|
|
86
|
+
/** Structural mirror of `content-api`'s `UnresolvedBlockRef`. */
|
|
87
|
+
interface UnresolvedBlockRef {
|
|
88
|
+
readonly kind: 'media' | 'link';
|
|
89
|
+
readonly ref: string;
|
|
90
|
+
readonly reason: 'unresolvable' | 'budget';
|
|
91
|
+
}
|
|
92
|
+
/** Structural mirror of `content-api`'s `BlockTreeRef`. */
|
|
93
|
+
interface BlockTreeRef {
|
|
94
|
+
readonly entity: string;
|
|
95
|
+
readonly id: string;
|
|
96
|
+
}
|
|
97
|
+
/** Structural mirror of `content-api`'s `ResolvedBlockTree`. */
|
|
98
|
+
interface ResolvedBlockTree {
|
|
99
|
+
readonly resolved: 1;
|
|
100
|
+
readonly blocks: readonly unknown[];
|
|
101
|
+
readonly links: Readonly<Record<string, string | null>>;
|
|
102
|
+
readonly unresolved?: readonly UnresolvedBlockRef[];
|
|
103
|
+
readonly refs?: readonly BlockTreeRef[];
|
|
104
|
+
readonly collections?: Readonly<Record<string, CollectionResult>>;
|
|
105
|
+
/** What this tree's live-bound Price fields resolved to (block-library F012). */
|
|
106
|
+
readonly live?: LiveValues;
|
|
107
|
+
/** The site's currency (block-library F012) — present whenever the site configures one. */
|
|
108
|
+
readonly currency?: string;
|
|
109
|
+
/**
|
|
110
|
+
* The row this tree is drawn for, projected through its entity's detail
|
|
111
|
+
* declaration (block-library PR16) — price, facts, a sanitised HTML body, a
|
|
112
|
+
* viewer. Absent unless the app declares a detail for the entity and the row
|
|
113
|
+
* has a value for at least one declared part.
|
|
114
|
+
*/
|
|
115
|
+
readonly detail?: DocumentDetail;
|
|
116
|
+
/**
|
|
117
|
+
* Block type → the data a plugin's provider resolved for it
|
|
118
|
+
* (`blocks.dataProviders`, davatoidud-toolkit PR04) — only for the types this
|
|
119
|
+
* tree draws, and absent when there is none. Each value is validated by the
|
|
120
|
+
* block that draws it.
|
|
121
|
+
*/
|
|
122
|
+
readonly blockData?: Readonly<Record<string, unknown>>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* What resolving COLLECTION blocks needs from the app — built once per app by
|
|
126
|
+
* {@link createBlockResolver}, absent when the app declares no sources.
|
|
127
|
+
*/
|
|
128
|
+
interface BlockCollectionDeps {
|
|
129
|
+
readonly sources: readonly CollectionSourceSpec[];
|
|
130
|
+
/** Name AND field restriction (kulli-editor F019) — `serverCollectionFieldLookup()`. */
|
|
131
|
+
readonly sourceFieldOf: CollectionFieldLookup;
|
|
132
|
+
readonly getEntity: (name: string) => Entity | undefined;
|
|
133
|
+
/** A PUBLISHED-only reader bound to the ambient request context — the caller. */
|
|
134
|
+
readonly clientFor: (entity: Entity) => FindManyClientLike;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The slice of `content-api`'s `ResolutionBudget` this provider uses —
|
|
138
|
+
* narrowed to `claim`, for the reason `ChromeResolutionBudget` states: a
|
|
139
|
+
* provider asks for work and is told how much it may do; it has no business
|
|
140
|
+
* reading the remaining count or the drop summary.
|
|
141
|
+
*/
|
|
142
|
+
interface BlockResolutionBudget {
|
|
143
|
+
claim: (kind: 'block', n: number) => number;
|
|
144
|
+
}
|
|
145
|
+
interface BlockResolverLogger {
|
|
146
|
+
debug: (obj: object, msg: string) => void;
|
|
147
|
+
warn: (obj: object, msg: string) => void;
|
|
148
|
+
}
|
|
149
|
+
/** Everything the pure resolver needs, injected so it is testable without an app. */
|
|
150
|
+
interface BlockResolverDeps {
|
|
151
|
+
/** Public-only media resolution. Omit and every media ref resolves to `null`. */
|
|
152
|
+
resolveMedia?: BlockMediaResolver | undefined;
|
|
153
|
+
/**
|
|
154
|
+
* PUBLISHED-only canonical paths for many ids of one entity type, in one
|
|
155
|
+
* locale, as `entityId → path`.
|
|
156
|
+
*
|
|
157
|
+
* **The publish gate is this function's responsibility and the whole reason
|
|
158
|
+
* it is batched rather than per-id.** An unpublished target must yield no
|
|
159
|
+
* entry, or a block link leaks an unpublished document's URL — the obligation
|
|
160
|
+
* `ResolveBlocks`' own docblock puts on the wiring.
|
|
161
|
+
*/
|
|
162
|
+
resolvePublishedPaths: (entityType: string, ids: readonly string[], locale: string) => Promise<Map<string, string>>;
|
|
163
|
+
logger?: BlockResolverLogger | undefined;
|
|
164
|
+
/** Collection resolution. Omit and collection blocks resolve no rows. */
|
|
165
|
+
collections?: BlockCollectionDeps | undefined;
|
|
166
|
+
/**
|
|
167
|
+
* Rebuild a CODED TEMPLATE and name the columns its blocks bind (editor
|
|
168
|
+
* D017, frontend-delivery F109). Defaults to the server block registry's
|
|
169
|
+
* `materializeServerTemplate`; injectable so a test need not seed a registry.
|
|
170
|
+
*
|
|
171
|
+
* `namespace` is the document the ids belong to (editor F124) — see
|
|
172
|
+
* `resolveBlockTree`, which derives it from the entity and the row's id.
|
|
173
|
+
*/
|
|
174
|
+
materializeTemplate?: ((template: string, namespace: TemplateNamespace) => {
|
|
175
|
+
readonly blocks: unknown[];
|
|
176
|
+
readonly boundColumns: readonly string[];
|
|
177
|
+
}) | undefined;
|
|
178
|
+
/**
|
|
179
|
+
* The columns the blocks in an ORDINARY stored tree bind. Defaults to the
|
|
180
|
+
* server registry's `serverBoundColumnsInTree`; injectable for the same reason
|
|
181
|
+
* as {@link materializeTemplate}.
|
|
182
|
+
*/
|
|
183
|
+
boundColumnsOf?: ((tree: readonly unknown[]) => readonly string[]) | undefined;
|
|
184
|
+
/**
|
|
185
|
+
* Resolve this tree's live-bound Price values (block-library PR03, F012).
|
|
186
|
+
* Omit and a live-bound Price draws nothing — same deny-by-default rule as
|
|
187
|
+
* `collections`. Defaults to `loadCanvasLiveValues`, the SAME resolver the
|
|
188
|
+
* canvas's own entry points call, so a price cannot bind through a
|
|
189
|
+
* different path (or a different source set) than the one the author saw
|
|
190
|
+
* while editing.
|
|
191
|
+
*/
|
|
192
|
+
resolveLiveValues?: ((roots: readonly unknown[], locale: string) => Promise<LiveValues>) | undefined;
|
|
193
|
+
/**
|
|
194
|
+
* The site's currency (ISO 4217), from `blocksPlugin({ currency })`
|
|
195
|
+
* (block-library F012). Independent of `resolveLiveValues`: a plain,
|
|
196
|
+
* typed-in Price needs it too, to show a currency symbol rather than a bare
|
|
197
|
+
* number.
|
|
198
|
+
*/
|
|
199
|
+
currency?: string | undefined;
|
|
200
|
+
/**
|
|
201
|
+
* Project the row a tree is drawn for through its entity's detail
|
|
202
|
+
* declaration (block-library PR16). Omit and no tree carries a `detail` —
|
|
203
|
+
* deny-by-default, like `collections`. Query-free: the row is the one the
|
|
204
|
+
* caller is already delivering, under the caller's own access.
|
|
205
|
+
*
|
|
206
|
+
* `parts` are the detail parts the TREE's blocks draw; only those are
|
|
207
|
+
* projected, and a tree that draws none is never asked about.
|
|
208
|
+
*/
|
|
209
|
+
resolveDetail?: ((entity: string, row: Readonly<Record<string, unknown>>, locale: string, parts: ReadonlySet<DocumentDetailPart>) => DocumentDetail | undefined) | undefined;
|
|
210
|
+
/**
|
|
211
|
+
* Would projecting `parts` for `entity` sanitise an HTML body? That is the
|
|
212
|
+
* one detail part with a real CPU cost, so the resolver charges the
|
|
213
|
+
* response's budget for it. Omit and no projection is charged.
|
|
214
|
+
*/
|
|
215
|
+
detailNeedsBody?: ((entity: string, parts: ReadonlySet<DocumentDetailPart>, row: Readonly<Record<string, unknown>>) => boolean) | undefined;
|
|
216
|
+
/**
|
|
217
|
+
* The breadcrumb trail of the row a tree is drawn for (block-library D003) —
|
|
218
|
+
* its published ancestors from the path index, then the row itself. Asked only
|
|
219
|
+
* for a tree that holds a `breadcrumbs` block, and charged one unit of the
|
|
220
|
+
* response's budget because it reads the index. Omit and no tree carries
|
|
221
|
+
* breadcrumbs. `ancestors` are the documents the trail was built from, so the
|
|
222
|
+
* page's cache entry depends on them.
|
|
223
|
+
*/
|
|
224
|
+
resolveBreadcrumbs?: ((entity: string, row: Readonly<Record<string, unknown>>, locale: string) => Promise<{
|
|
225
|
+
readonly steps: readonly DocumentBreadcrumb[];
|
|
226
|
+
readonly ancestors: readonly BlockTreeRef[];
|
|
227
|
+
} | undefined>) | undefined;
|
|
228
|
+
/**
|
|
229
|
+
* Resolve the plugin-owned data the tree's block types draw
|
|
230
|
+
* (`blocks.dataProviders`, davatoidud-toolkit PR04). Asked once per tree, only
|
|
231
|
+
* with the types the tree contains that have a provider, and charged against
|
|
232
|
+
* the response's collection allowance and budget like the breadcrumb trail —
|
|
233
|
+
* a provider reads a table, so a list response must not become one read per
|
|
234
|
+
* row. Omit and no tree carries `blockData`.
|
|
235
|
+
*/
|
|
236
|
+
resolveBlockData?: ((types: readonly string[], locale: string) => Promise<Record<string, unknown>>) | undefined;
|
|
237
|
+
/** The block types that have a provider — what {@link resolveBlockData} may be asked about. */
|
|
238
|
+
blockDataTypes?: ReadonlySet<string> | undefined;
|
|
239
|
+
/**
|
|
240
|
+
* Embedded documents (forms D002) — a block that names another document
|
|
241
|
+
* shows that document's PUBLISHED tree in its place. Omit and every embed
|
|
242
|
+
* shows nothing, deny-by-default like `collections`.
|
|
243
|
+
*/
|
|
244
|
+
embeddedDocuments?: EmbeddedDocumentDeps | undefined;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Resolve one stored block tree into a {@link ResolvedBlockTree}.
|
|
248
|
+
*
|
|
249
|
+
* **Throws when `blocks` is not an array**, which is how a corrupt stored value
|
|
250
|
+
* is handled rather than papered over — a resolver that returned `{ resolved: 1,
|
|
251
|
+
* blocks: [] }` for it would report success while deleting a page's content,
|
|
252
|
+
* and the caller could not tell the two apart.
|
|
253
|
+
*
|
|
254
|
+
* ⚠️ **What the caller does with that throw CHANGED** (editor D010, closing
|
|
255
|
+
* F056). It used to serve the stored tree unchanged, so a malformed row cost
|
|
256
|
+
* its own hrefs and nothing else. It no longer does: it re-calls this function
|
|
257
|
+
* with an exhausted budget and, if that fails too, serves an empty envelope. A
|
|
258
|
+
* non-array `blocks` fails this guard DETERMINISTICALLY, so a corrupt column is
|
|
259
|
+
* now precisely the case that reaches that third state and is blanked. That is
|
|
260
|
+
* the intended outcome rather than a regression — a non-array stored value
|
|
261
|
+
* cannot be rendered by any consumer, and it may still be an object carrying
|
|
262
|
+
* media refs whose urls were signed at author time.
|
|
263
|
+
*/
|
|
264
|
+
declare function resolveBlockTree(blocks: unknown, opts: {
|
|
265
|
+
locale: string;
|
|
266
|
+
budget: BlockResolutionBudget;
|
|
267
|
+
entity: string;
|
|
268
|
+
field: string; /** content-api's whitelist ∧ `view` gate for THIS caller. Absent → no collection rows. */
|
|
269
|
+
canReadEntity?: ((entityName: string) => boolean) | undefined; /** One slot of the response's collection allowance. Absent → no collection rows. */
|
|
270
|
+
claimCollection?: (() => boolean) | undefined; /** A CODED TEMPLATE's root block name — `blocks` is then ignored and the template rebuilt. */
|
|
271
|
+
template?: string | undefined; /** The row a templated document's bound blocks read. Read, never written. */
|
|
272
|
+
boundValues?: Readonly<Record<string, unknown>> | undefined;
|
|
273
|
+
/**
|
|
274
|
+
* `blocks` is a stored detail template drawn for `boundValues` (block-library
|
|
275
|
+
* D009). The row's detail is projected; its columns are NOT walked for links.
|
|
276
|
+
*/
|
|
277
|
+
rowIsForeign?: boolean | undefined; /** The page a paged collection in this tree shows (davatoidud-toolkit PR03). Absent = 1. */
|
|
278
|
+
page?: number | undefined;
|
|
279
|
+
}, deps: BlockResolverDeps): Promise<ResolvedBlockTree>;
|
|
280
|
+
/**
|
|
281
|
+
* Wire {@link resolveBlockTree} to the running app — the one line an app puts
|
|
282
|
+
* into `createContentApiHandler({ resolveBlocks })`.
|
|
283
|
+
*
|
|
284
|
+
* Pass the SAME `resolveMedia` value the handler gets, so a media ref inside a
|
|
285
|
+
* block and a top-level `media()` field resolve through one path and one gate.
|
|
286
|
+
*
|
|
287
|
+
* **The publish gate comes from `buildEnumerateDeps`' batched `filterPublished`,
|
|
288
|
+
* which reads through an elevated REACH context** (`createSystemReachResolver`,
|
|
289
|
+
* which grants `view` and withholds `view_unpublished`), so unpublished and
|
|
290
|
+
* trashed targets are dropped. That is deliberately the SAME gate `GET /_resolve`
|
|
291
|
+
* applies and no stricter one: the alternative — additionally gating on the
|
|
292
|
+
* caller's per-entity `view` — would make the same URL a working link on one
|
|
293
|
+
* endpoint and plain text on another, while the document behind the href stays
|
|
294
|
+
* whitelist- and permission-gated where it is actually fetched.
|
|
295
|
+
*/
|
|
296
|
+
declare function createBlockResolver(getApp: () => Promise<ToolkitApp> | ToolkitApp, options: {
|
|
297
|
+
/**
|
|
298
|
+
* The locale to resolve in when a request carried none. Pass the SAME value
|
|
299
|
+
* given to `createContentApiHandler({ defaultLocale })` — the handler
|
|
300
|
+
* already defaults every entity read's locale before it reaches a resolver,
|
|
301
|
+
* so this only binds on a path that somehow did not, and disagreeing with
|
|
302
|
+
* the handler would resolve a link in a locale the page is not rendered in.
|
|
303
|
+
*/
|
|
304
|
+
defaultLocale: string;
|
|
305
|
+
resolveMedia?: BlockMediaResolver | undefined;
|
|
306
|
+
}): (blocks: unknown, opts: {
|
|
307
|
+
locale?: string | undefined;
|
|
308
|
+
budget: BlockResolutionBudget;
|
|
309
|
+
entity: string;
|
|
310
|
+
field: string;
|
|
311
|
+
canReadEntity?: ((entityName: string) => boolean) | undefined;
|
|
312
|
+
claimCollection?: (() => boolean) | undefined;
|
|
313
|
+
template?: string | undefined;
|
|
314
|
+
boundValues?: Readonly<Record<string, unknown>> | undefined; /** See `resolveBlockTree`: a stored detail template drawn for a row it does not own. */
|
|
315
|
+
rowIsForeign?: boolean | undefined; /** See `resolveBlockTree`. */
|
|
316
|
+
page?: number | undefined;
|
|
317
|
+
}) => Promise<ResolvedBlockTree>;
|
|
318
|
+
//#endregion
|
|
319
|
+
export { BlockCollectionDeps, BlockMediaProjection, BlockMediaResolver, BlockResolutionBudget, BlockResolverDeps, BlockResolverLogger, BlockSpotSources, BlockTreeRef, ResolvedBlockTree, UnresolvedBlockRef, createBlockResolver, resolveBlockTree };
|
|
320
|
+
//# sourceMappingURL=block-resolver.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-resolver.d.mts","names":[],"sources":["../src/embedded-documents.ts","../src/block-resolver.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i as e,t}from"./resolve-context-DklN53BH.mjs";import{r as n}from"./enumerate-CYHJKQz6.mjs";import{n as r}from"./breadcrumbs-BqSuMNac.mjs";import{createSystemReachResolver as i}from"@murumets-ee/core";import{QueryClient as a}from"@murumets-ee/entity/query";import{inArray as o}from"drizzle-orm";import{collectLinkValues as s,linkValueKey as c}from"@murumets-ee/blocks/links";import{blockTypesInTree as l,breadcrumbLabelOf as u,canvasSiteFormat as d,collectBoundColumnsInTree as f,collectEmbeds as p,contributedBlockDataProviders as m,createCollectionReader as h,detailNeedsBody as g,detailPartsInTree as _,distinctEmbeddedDocuments as v,effectiveCollectionSources as y,effectiveDetailSpecs as b,embedReferenceKey as x,hasConfiguredLiveSources as S,loadCanvasLiveValues as C,materializeServerTemplate as w,readSpotCrop as T,resolveBlockData as E,resolveDocumentDetail as D,resolveLinks as O,resolveTreeCollections as k,sameSpotCrop as A,serverBlockStructureLookup as j,serverBoundColumnsInTree as ee,serverCollectionFieldLookup as M,serverEmbedLookup as N,serverTemplateMaterializer as P,templateNamespace as F}from"@murumets-ee/blocks/server";function I(e){return Array.isArray(e)}function L(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function R(e,t){let n=structuredClone(e),r=e=>{if(I(e))for(let n of e)L(n)&&(typeof n.id==`string`&&(n.id=`${t}${n.id}`),r(n.children))};return r(n),Array.isArray(n)?n:[]}async function te(e,t,n,r){if(n===void 0||n.lookup.size===0)return{expanded:[]};z(e,n.lookup);let i=p(e,n.lookup,{onDropped:e=>r?.warn({entity:t.entity,dropped:e},`content: a block tree embeds more documents than one page may show; the rest show nothing`)});if(i.length===0)return{expanded:[]};let a=v(i),o=t.budget.claim(`block`,a.length),s=a.slice(0,o);if(s.length<a.length&&r?.warn({entity:t.entity,dropped:a.length-s.length},`content: the response budget is spent; some embedded documents show nothing`),s.length===0)return{expanded:[]};let c;try{c=await n.load(s,t.locale)}catch(e){return r?.warn({err:e,entity:t.entity},`content: loading embedded documents failed; they show nothing`),{expanded:s}}let l=new Map(i.map(e=>[e.blockId,e])),u=e=>{if(I(e))for(let t of e){if(!L(t))continue;let e=typeof t.id==`string`?l.get(t.id):void 0;if(e===void 0){u(t.children);continue}let n=c.get(x(e));n!==void 0&&(t.children=R(n,`${e.blockId}~`))}};return u(e),{expanded:s}}function z(e,t){let n=(e,r)=>{if(!(!I(e)||r>64)){for(let i of e)if(L(i)){if(typeof i.type==`string`&&t.has(i.type)){i.children!==void 0&&(i.children=[]);continue}n(i.children,r+1)}}};n(e,0)}const B=i(`embed-resolver`);function V(e){for(let[t,n]of Object.entries(e.allFields))if(n.type===`blockTree`)return t}async function H(e,t,n){let r=new Map,i=new Map;for(let e of t){let t=i.get(e.entity)??[];t.push(e.id),i.set(e.entity,t)}for(let[t,s]of i){let i=e.entities.get(t),c=i===void 0?void 0:V(i);if(i===void 0||c===void 0)continue;let l;try{let r=new a({entity:i,db:e.db.readOnly,logger:e.logger.child({resolve:`embedded-document`,entity:t}),contextResolver:B}),c=r.getTable().id;l=await r.findMany({where:o(c,s),locale:n,limit:s.length})}catch(n){e.logger.warn({err:n,entity:t,count:s.length},`content: reading embedded documents failed — they render empty`);continue}for(let e of l){let n=e,i=n.id,a=n[c];typeof i==`string`&&Array.isArray(a)&&r.set(x({entity:t,id:i}),a)}}return r}const U=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function W(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function G(e){return Array.isArray(e)}const K=new Set(Object.keys({id:!0,url:!0,mimeType:!0,width:!0,height:!0,alt:!0,title:!0,variants:!0,sources:!0,avifSources:!0,focalPoint:!0,blurDataUrl:!0,crop:!0,spotSources:!0}));function q(e,t){if(!W(e))return null;let n=e.id;if(typeof n!=`string`||!U.test(n)||typeof e.url!=`string`||e.url===``)return null;let r=Object.keys(e).filter(e=>!K.has(e));return r.length>0?(t?.(r),null):!J(e.alt,`string`)||!J(e.title,`string`)||!J(e.mimeType,`string`)||!J(e.width,`number`)||!J(e.height,`number`)||!J(e.blurDataUrl,`string`)||e.variants!==void 0&&e.variants!==null&&!W(e.variants)||e.focalPoint!==void 0&&e.focalPoint!==null&&!W(e.focalPoint)||e.sources!==void 0&&e.sources!==null&&(!W(e.sources)||!Object.values(e.sources).every(e=>Array.isArray(e)))||e.avifSources!==void 0&&e.avifSources!==null&&(!W(e.avifSources)||!Object.values(e.avifSources).every(e=>Array.isArray(e)))||e.crop!==void 0&&e.crop!==null&&!W(e.crop)||e.spotSources!==void 0&&e.spotSources!==null&&!Array.isArray(e.spotSources)?null:n}function J(e,t){return e==null||typeof e===t}function Y(e,t,n,r){if(n>256){r.truncated=!0;return}let i=e=>{for(let t of e)r.nearMissKeys.add(t)};if(G(e)){for(let a=0;a<e.length;a+=1){let o=q(e[a],i);o===null?Y(e[a],t,n+1,r):t.push({container:e,key:a,id:o,crop:T(e[a])})}return}if(W(e))for(let[a,o]of Object.entries(e)){let s=q(o,i);s===null?Y(o,t,n+1,r):t.push({container:e,key:a,id:s,crop:T(o)})}}function X(e){let t={id:e.id,url:e.url,variants:e.variants,focalPoint:e.focalPoint,sources:e.sources};return e.avifSources!==void 0&&Object.keys(e.avifSources).length>0&&(t.avifSources=e.avifSources),e.mimeType!==null&&(t.mimeType=e.mimeType),e.width!==null&&(t.width=e.width),e.height!==null&&(t.height=e.height),e.alt!==null&&(t.alt=e.alt),e.title!==null&&(t.title=e.title),e.blurDataUrl!==null&&(t.blurDataUrl=e.blurDataUrl),t}function ne(e,t){if(!(t===void 0||e.spotSources===void 0))return e.spotSources.find(e=>A(e.crop,t)&&e.sources.length>0)}function re(e,t){let n=X(e),r=ne(e,t),i=r?.sources[r.sources.length-1];if(r!==void 0&&i!==void 0){n.url=i.url,n.width=i.width,n.height=i.height,n.focalPoint={x:.5,y:.5},n.sources={natural:r.sources};let e=r.avifSources;e!==void 0&&e.length===r.sources.length&&e.every((e,t)=>e.width===r.sources[t]?.width&&e.height===r.sources[t]?.height)?n.avifSources={natural:e}:delete n.avifSources}return n.crop={x:t.x,y:t.y,w:t.w,h:t.h},n}function ie(e,t){let n=t===null?null:e.crop===void 0?X(t):re(t,e.crop);if(Array.isArray(e.container)){typeof e.key==`number`&&(e.container[e.key]=n);return}typeof e.key==`string`&&(e.container[e.key]=n)}async function Z(e,t,n){let r=e,i=[];if(t.template!==void 0){let e=t.boundValues?.id;if(typeof e==`string`&&e!==``){let a=(n.materializeTemplate??w)(t.template,F(t.entity,e));r=a.blocks,i=a.boundColumns}else r=[],n.logger?.warn({entity:t.entity,field:t.field},`content: a templated document arrived without its row id, so its template is served EMPTY — its block ids could not be namespaced to it (editor F124)`)}else if(t.rowIsForeign!==!0&&t.boundValues!==void 0&&G(e))try{i=(n.boundColumnsOf??ee)(e)}catch(e){n.logger?.warn({err:e,entity:t.entity,field:t.field},`content: could not enumerate the bound columns of a block tree, so its bound-field links render plain`)}let a=t.boundValues??{},o=i.filter(e=>Object.hasOwn(a,e)).map(e=>a[e]);if(!G(r))throw n.logger?.warn({entity:t.entity,field:t.field},`content: stored block tree is not an array — nothing to resolve, so this field is served EMPTY rather than as stored (the caller retries this throw with an exhausted budget, and a non-array fails that identically)`),TypeError(`content: block tree \`${t.entity}.${t.field}\` is not an array; nothing to resolve.`);let{expanded:u}=await te(r,{locale:t.locale,budget:t.budget,entity:t.entity},n.embeddedDocuments,n.logger),d=[],f={truncated:!1,nearMissKeys:new Set};Y(r,d,0,f),f.truncated&&n.logger?.warn({entity:t.entity,field:t.field,cap:256},`content: block tree is nested deeper than the media walk descends — any media ref below the bound keeps the url recorded when an editor picked it, which may be signed`),f.nearMissKeys.size>0&&n.logger?.debug({entity:t.entity,field:t.field,keys:[...f.nearMissKeys].sort()},`content: a value looked like a media ref but carried keys outside the projection, so it was left alone. If these are media refs, they keep their stored urls`);let p=[...s(r,64),...s(o,64)],m=[],h=[];for(let e of p)e.kind===`media`&&(U.test(e.id)?m.push(e.id):h.push({kind:`link`,ref:c(e),reason:`unresolvable`}));let g=Q([...d.map(e=>e.id),...m]),v=[];for(let e of p)e.kind===`entity`&&(U.test(e.id)?v.push($(e.entity,e.id)):h.push({kind:`link`,ref:c(e),reason:`unresolvable`}));let y=[...h];!n.resolveMedia&&g.length>0&&n.logger?.debug({entity:t.entity,field:t.field,count:g.length},`content: no media resolver wired for block trees — every media ref resolves to null`);let b=n.resolveMedia?t.budget.claim(`block`,g.length):0,x=g.slice(0,b),S=n.resolveMedia?new Set(g.slice(b)):new Set;for(let e of g.slice(b))y.push({kind:`media`,ref:e,reason:S.has(e)?`budget`:`unresolvable`});let C=Q(v),T=t.budget.claim(`block`,C.length),E=C.slice(0,T),D=new Set(C.slice(T)),k=await le(x,t.locale,n,t.entity);for(let e of d)ie(e,k.get(e.id)??null);for(let e of x)k.has(e)||y.push({kind:`media`,ref:e,reason:`unresolvable`});let A=await ue(E,t.locale,n,t.entity),j=await O(p,{renderContext:`request`,locale:t.locale,resolveEntityHref:({entity:e,id:t})=>A.get($(e,t))??null,resolveMediaHref:e=>k.get(e)?.url??null}),M={};for(let[e,t]of j)M[e]=t.href;for(let e of p){if(e.kind!==`entity`&&e.kind!==`media`)continue;let t=c(e);M[t]!==null&&M[t]!==void 0||y.push({kind:`link`,ref:t,reason:se(e,D,S)})}let N=n.resolveLiveValues,P=N===void 0?0:t.budget.claim(`block`,1),[{results:I,cardMedia:L},R]=await Promise.all([ae(r,t,n),P>0&&N!==void 0?(async()=>N(r,t.locale))().catch(e=>{n.logger?.warn({err:e},`blocks: resolveLiveValues rejected; this tree draws no live values`)}):Promise.resolve(void 0)]),z,B=[];if(t.boundValues!==void 0){let e=_(G(r)?r:[]),i=new Set(e);if(i.delete(`breadcrumbs`),n.resolveDetail!==void 0)try{let e=i;if(e.has(`body`)&&n.detailNeedsBody?.(t.entity,e,t.boundValues)===!0&&t.budget.claim(`block`,1)<1){let r=new Set(e);r.delete(`body`),e=r,n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response budget is spent; this tree is delivered without its HTML body`)}e.size>0&&(z=n.resolveDetail(t.entity,t.boundValues,t.locale,e))}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveDetail threw; this tree carries no detail`)}if(e.has(`breadcrumbs`)&&n.resolveBreadcrumbs!==void 0)if(t.claimCollection?.()!==!0||t.budget.claim(`block`,1)<1)n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response’s allowance is spent; this tree is delivered without its breadcrumbs`);else try{let e=await n.resolveBreadcrumbs(t.entity,t.boundValues,t.locale);e!==void 0&&e.steps.length>0&&(z={...z,breadcrumbs:e.steps},B.push(...e.ancestors))}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveBreadcrumbs threw; this tree carries no breadcrumbs`)}}let V;if(n.resolveBlockData!==void 0&&n.blockDataTypes!==void 0){let e=[...l(r)].filter(e=>n.blockDataTypes?.has(e));if(e.length>0)if(t.claimCollection?.()!==!0||t.budget.claim(`block`,1)<1)n.logger?.warn({entity:t.entity,field:t.field},`blocks: the response’s allowance is spent; this tree is delivered without its block data`);else try{let r=await n.resolveBlockData(e,t.locale);Object.keys(r).length>0&&(V=r)}catch(e){n.logger?.warn({err:e,entity:t.entity},`blocks: resolveBlockData threw; this tree carries no block data`)}}let H=new Map;for(let e of k.keys())H.set($(`media`,e),{entity:`media`,id:e});for(let e of L)H.set($(`media`,e),{entity:`media`,id:e});for(let e of I.values())if(e.entity!==null)for(let t of e.rows)H.set($(e.entity,t.id),{entity:e.entity,id:t.id});for(let e of B)H.set($(e.entity,e.id),e);for(let e of u)H.set($(e.entity,e.id),{entity:e.entity,id:e.id});for(let e of A.keys()){if(H.has(e))continue;let t=e.indexOf(` `);if(t<=0)continue;let n=e.slice(0,t),r=e.slice(t+1);r!==``&&H.set(e,{entity:n,id:r})}return{resolved:1,blocks:r,links:M,...y.length>0&&{unresolved:ce(y)},...H.size>0&&{refs:[...H.values()]},...I.size>0&&{collections:Object.fromEntries(I)},...R!==void 0&&Object.keys(R.sources).length>0&&{live:{sources:R.sources,values:R.values}},...n.currency!==void 0&&{currency:n.currency},...z!==void 0&&{detail:z},...V!==void 0&&{blockData:V}}}async function ae(e,t,n){let r=n.collections,i=t.canReadEntity,a=t.claimCollection;if(r===void 0||i===void 0||a===void 0)return{results:new Map,cardMedia:new Set};let o=n.resolveMedia,s=new Set;return{results:await k(e,r.sourceFieldOf,{sources:r.sources,getEntity:r.getEntity,clientFor:r.clientFor,canRead:i,resolveImages:o===void 0?void 0:async e=>{let n=[...await o(e,{locale:t.locale})].filter(([,e])=>e.mimeType?.startsWith(`image/`)===!0);for(let[e]of n)s.add(e);return new Map(n.map(([e,t])=>[e,{url:t.url,alt:t.alt,width:t.width,height:t.height,focalPoint:t.focalPoint,sources:t.sources,...t.avifSources!==void 0&&Object.keys(t.avifSources).length>0&&{avifSources:t.avifSources},blurDataUrl:t.blurDataUrl}]))},resolveHrefs:(e,r)=>n.resolvePublishedPaths(e,r,t.locale),locale:t.locale,renderContext:`request`,current:oe(t.entity,t.boundValues),...t.page!==void 0&&{page:t.page},claim:()=>a()&&t.budget.claim(`block`,1)>=1,logger:n.logger}),cardMedia:s}}function oe(e,t){let n=t?.id;if(!(t===void 0||typeof n!=`string`||n===``))return{entity:e,id:n,values:t}}function se(e,t,n){return e.kind===`entity`&&t.has($(e.entity,e.id))||e.kind===`media`&&n.has(e.id)?`budget`:`unresolvable`}function Q(e){return[...new Set(e)]}function $(e,t){return[e,t].join(` `)}function ce(e){let t=new Set,n=[];for(let r of e){let e=`${r.kind} ${r.ref}`;t.has(e)||(t.add(e),n.push(r))}return n}async function le(e,t,n,r){if(e.length===0||!n.resolveMedia)return new Map;try{return await n.resolveMedia(e,{locale:t,spots:!0})}catch(e){return n.logger?.warn({entity:r,err:e},"content: media resolver threw while resolving a block tree — its media refs resolve to null (if this is a ForbiddenError, the requesting role is missing `media:view`)"),new Map}}async function ue(e,t,n,r){let i=new Map;if(e.length===0)return i;let a=new Map;for(let t of e){let e=t.indexOf(` `),n=t.slice(0,e),r=t.slice(e+1),i=a.get(n);i?i.push(r):a.set(n,[r])}for(let[e,o]of a)try{let r=await n.resolvePublishedPaths(e,o,t);for(let[t,n]of r)i.set($(e,t),n)}catch(t){n.logger?.warn({entity:r,entityType:e,err:t},`content: path resolution threw for a block-tree link target — its links render as plain text`)}return i}function de(i,a){let o=null,s=i=>{if(o?.app===i)return o.deps;let s=e(i),{sources:c}=y(i),l=c.length===0?void 0:{sources:c,sourceFieldOf:M(),getEntity:e=>i.entities.get(e),clientFor:e=>h(e,i)},p=P(),_=j(),v=b(i).specs,x={...l!==void 0&&{collections:l},boundColumnsOf:e=>f(e,_),materializeTemplate:(e,t)=>p(e,t),resolveMedia:a.resolveMedia,resolvePublishedPaths:async(e,t,r)=>{let i=await s.filterPublished(e,t,r);return i.size===0?new Map:n(e,[...i],r,s)},logger:i.logger,...S()&&{resolveLiveValues:(e,t)=>C(e,{locale:t,logger:i.logger})},currency:d(a.defaultLocale).currency,resolveBreadcrumbs:async(e,n,a)=>{let o=n.id;if(typeof o!=`string`||o===``)return;let s=i.entities.get(e);if(s===void 0)return;let c=u(s,n),l=await r({entityType:e,id:o,locale:a,...c!==void 0&&{label:c}},t(i));return l===void 0?void 0:{steps:l.steps,ancestors:l.ancestors}},...v.size>0&&{resolveDetail:(e,t,n,r)=>{let i=v.get(e);return i===void 0?void 0:D(i,t,n,r)},detailNeedsBody:(e,t,n)=>{let r=v.get(e);return r!==void 0&&g(r,t,n)}}},w=m();w.size>0&&(x.blockDataTypes=new Set(w.keys()),x.resolveBlockData=(e,t)=>E(e,{locale:t,surface:`site`,logger:i.logger,providers:w}));let T=N();return T.size>0&&(x.embeddedDocuments={lookup:T,load:(e,t)=>H(i,e,t)}),o={app:i,deps:x},x};return async(e,t)=>{let n=await i();return Z(e,{...t,locale:t.locale??a.defaultLocale},s(n))}}export{de as createBlockResolver,Z as resolveBlockTree};
|
|
2
|
+
//# sourceMappingURL=block-resolver.mjs.map
|