@jini-ai/cms 0.5.7 → 0.5.8
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/CHANGELOG.md +6 -0
- package/README.md +7 -0
- package/dist/core/transform-format.d.ts +19 -0
- package/dist/core/transform-format.js +19 -0
- package/dist/media/transform-types.d.ts +2 -13
- package/dist/media/transform-types.js +2 -15
- package/dist/navigation/index.d.ts +1 -1
- package/dist/navigation/menu-html.d.ts +25 -1
- package/dist/navigation/menu-html.js +9 -20
- package/dist/navigation/menu-service.d.ts +5 -0
- package/dist/navigation/menu-service.js +4 -4
- package/dist/navigation/node/index.d.ts +30 -0
- package/dist/navigation/node/index.js +43 -0
- package/dist/navigation/tool-registrations.d.ts +3 -0
- package/dist/navigation/tool-registrations.js +1 -1
- package/dist/seo/media.js +1 -1
- package/package.json +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## 0.5.8 — 2026-10-13
|
|
2
|
+
|
|
3
|
+
- New `./navigation/node` subpath: re-exports the full `./navigation` API with parse5 menu-HTML normalization bound into `createMenu`, `updateMenuTree`, `mergeMenuHtml` and `buildMenusRegistrations`. Node hosts writing HTML-mode menus should import those from `./navigation/node`.
|
|
4
|
+
- `./navigation` no longer loads the optional peer `parse5`, so it is universal again. Every name is still exported; an HTML-mode write through it needs an injected `normalizeMenuHtml` (`normalizeHtml` for `mergeMenuHtml`) and otherwise fails before saving. Item-only writes are unchanged.
|
|
5
|
+
- `./seo` no longer reaches the media transform module for rendition MIME types; `TransformFormat` and `mimeForTransformFormat` remain exported from the media entries.
|
|
6
|
+
|
|
1
7
|
## 0.5.7 — 2026-10-12
|
|
2
8
|
|
|
3
9
|
- New render-only leaf subpaths: `./forms/html-render`, `./forms/submission-attempts`, `./widgets/page-embed-types`, `./widgets/html/html-embeds`, `./widgets/html/gallery-render`, `./navigation/types`, `./navigation/dropdown` and `./media/slug` (the media slug rule, still re-exported from `./media`). Existing barrels are unchanged.
|
package/README.md
CHANGED
|
@@ -78,6 +78,13 @@ coordination.
|
|
|
78
78
|
`taxonomy`, `workspace`, and `trash`. `settings` is now a compatibility shim, with implementation
|
|
79
79
|
in `@jini-ai/core/settings`. `./core/tools` re-exports the shared core permission helpers; `./media/import` exports bounded image fetching with injected policies and ports.
|
|
80
80
|
|
|
81
|
+
`./navigation` stays universal: submitted menu HTML requires an injected `normalizeMenuHtml`
|
|
82
|
+
dependency (`normalizeHtml` for `mergeMenuHtml`). Item-only writes and retained markup need no
|
|
83
|
+
parser. Node hosts use `./navigation/node`, which re-exports the navigation API and supplies
|
|
84
|
+
parse5 normalization to create/update, merge and tool registration. The existing validation,
|
|
85
|
+
raw-HTML permission checks, OCC and outbox owners remain unchanged; an unconfigured universal
|
|
86
|
+
HTML write fails before saving rather than persisting markup that could escape its theme container.
|
|
87
|
+
|
|
81
88
|
**Still to move:** `post`, `seo`, `comments`, `redirects`, `widgets`, `newsletter`,
|
|
82
89
|
`members`. The unimplemented `./widgets` export has been removed.
|
|
83
90
|
The nested forms package is maintained by its separate owner and is excluded from this parent package.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Shared rendition format metadata. Media validation and SEO use this leaf without loading
|
|
2
|
+
* either domain's runtime; media/transform-types.ts preserves its existing public exports.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Output re-encode format. Matches `media-service.ts`'s
|
|
6
|
+
* `DEFAULT_ALLOWED_MIME_TYPES` image set minus SVG (never a transform output —
|
|
7
|
+
* SVG has no raster pixels to resize).
|
|
8
|
+
*/
|
|
9
|
+
export type TransformFormat = "jpeg" | "png" | "webp" | "gif";
|
|
10
|
+
/** Maps a {@link TransformFormat} to the MIME type served on the rendition response. */
|
|
11
|
+
export declare const MIME_BY_TRANSFORM_FORMAT: Record<TransformFormat, string>;
|
|
12
|
+
/**
|
|
13
|
+
* @complexity O(1).
|
|
14
|
+
* @overallScore 100
|
|
15
|
+
*/
|
|
16
|
+
export declare function mimeForTransformFormat(requiredArgs: {
|
|
17
|
+
format: TransformFormat;
|
|
18
|
+
}, optionalArgs?: Record<string, never>): string;
|
|
19
|
+
//# sourceMappingURL=transform-format.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Shared rendition format metadata. Media validation and SEO use this leaf without loading
|
|
2
|
+
* either domain's runtime; media/transform-types.ts preserves its existing public exports.
|
|
3
|
+
*/
|
|
4
|
+
/** Maps a {@link TransformFormat} to the MIME type served on the rendition response. */
|
|
5
|
+
export const MIME_BY_TRANSFORM_FORMAT = {
|
|
6
|
+
jpeg: "image/jpeg",
|
|
7
|
+
png: "image/png",
|
|
8
|
+
webp: "image/webp",
|
|
9
|
+
gif: "image/gif",
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* @complexity O(1).
|
|
13
|
+
* @overallScore 100
|
|
14
|
+
*/
|
|
15
|
+
export function mimeForTransformFormat(requiredArgs, optionalArgs = {}) {
|
|
16
|
+
const { format } = requiredArgs;
|
|
17
|
+
return MIME_BY_TRANSFORM_FORMAT[format];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=transform-format.js.map
|
|
@@ -22,14 +22,10 @@
|
|
|
22
22
|
* permission catalog.
|
|
23
23
|
*/
|
|
24
24
|
import type { ISODateTime, UUID } from "@jini-ai/core/primitives";
|
|
25
|
+
import { type TransformFormat } from "../core/transform-format.js";
|
|
26
|
+
export { mimeForTransformFormat, type TransformFormat } from "../core/transform-format.js";
|
|
25
27
|
/** Resize behavior, mirrors `sharp`'s `fit` vocabulary (a deliberately small subset). */
|
|
26
28
|
export type TransformFit = "cover" | "contain" | "fill" | "inside" | "outside";
|
|
27
|
-
/**
|
|
28
|
-
* Output re-encode format. Matches `media-service.ts`'s
|
|
29
|
-
* `DEFAULT_ALLOWED_MIME_TYPES` image set minus SVG (never a transform output —
|
|
30
|
-
* SVG has no raster pixels to resize).
|
|
31
|
-
*/
|
|
32
|
-
export type TransformFormat = "jpeg" | "png" | "webp" | "gif";
|
|
33
29
|
/**
|
|
34
30
|
* Declarative transform parameters (named definitions only, never
|
|
35
31
|
* caller-supplied dimensions). `width`/`height` are optional so a
|
|
@@ -66,13 +62,6 @@ export declare class TransformValidationError extends Error {
|
|
|
66
62
|
message: string;
|
|
67
63
|
}, optionalArgs?: ErrorOptions);
|
|
68
64
|
}
|
|
69
|
-
/**
|
|
70
|
-
* @complexity O(1).
|
|
71
|
-
* @overallScore 100
|
|
72
|
-
*/
|
|
73
|
-
export declare function mimeForTransformFormat(requiredArgs: {
|
|
74
|
-
format: TransformFormat;
|
|
75
|
-
}, optionalArgs?: Record<string, never>): string;
|
|
76
65
|
/** Upper bound on a requested resize dimension — an output-side resource-use guard (not the
|
|
77
66
|
* full pixel-bomb ingest cap, which polices upload input; this polices the transform
|
|
78
67
|
* definition itself so a registered definition can't ask the image transformer to allocate an
|
|
@@ -1,23 +1,10 @@
|
|
|
1
|
+
import { MIME_BY_TRANSFORM_FORMAT } from "../core/transform-format.js";
|
|
2
|
+
export { mimeForTransformFormat } from "../core/transform-format.js";
|
|
1
3
|
export class TransformValidationError extends Error {
|
|
2
4
|
constructor({ message }, optionalArgs = {}) {
|
|
3
5
|
super(message, optionalArgs);
|
|
4
6
|
}
|
|
5
7
|
}
|
|
6
|
-
/** Maps a {@link TransformFormat} to the MIME type served on the rendition response. */
|
|
7
|
-
const MIME_BY_TRANSFORM_FORMAT = {
|
|
8
|
-
jpeg: "image/jpeg",
|
|
9
|
-
png: "image/png",
|
|
10
|
-
webp: "image/webp",
|
|
11
|
-
gif: "image/gif",
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* @complexity O(1).
|
|
15
|
-
* @overallScore 100
|
|
16
|
-
*/
|
|
17
|
-
export function mimeForTransformFormat(requiredArgs, optionalArgs = {}) {
|
|
18
|
-
const { format } = requiredArgs;
|
|
19
|
-
return MIME_BY_TRANSFORM_FORMAT[format];
|
|
20
|
-
}
|
|
21
8
|
/** Upper bound on a requested resize dimension — an output-side resource-use guard (not the
|
|
22
9
|
* full pixel-bomb ingest cap, which polices upload input; this polices the transform
|
|
23
10
|
* definition itself so a registered definition can't ask the image transformer to allocate an
|
|
@@ -27,7 +27,7 @@ export { InMemoryMenuRepo, InMemoryNavLocationBindingRepo, menuVersionConflictEr
|
|
|
27
27
|
*/
|
|
28
28
|
export { createMenu, updateMenuTree, assignLocation, deleteMenu, validateAndCloneTree, isAllowedHref, ALLOWED_HREF_SHAPES_DESCRIPTION, MenuNotFoundError, MenuValidationError, MenuConflictError, MenuVersionConflictError, MenuLocationBoundError, DEFAULT_MAX_TREE_DEPTH, DEFAULT_MAX_ITEM_COUNT, type TreeValidationLimits, type CreateMenuDeps, type CreateMenuServiceInput, type CreateMenuRequired, type CreateMenuOptional, type UpdateMenuTreeDeps, type UpdateMenuTreeServiceInput, type UpdateMenuTreeRequired, type UpdateMenuTreeOptional, type AssignLocationDeps, type AssignLocationServiceInput, type AssignLocationRequired, type AssignLocationOptional, type DeleteMenuDeps, type DeleteMenuServiceInput, type DeleteMenuRequired, type DeleteMenuOptional, } from "./menu-service.js";
|
|
29
29
|
/** HTML mode (2026-10-08): validation/normalization for a menu's author-written markup. */
|
|
30
|
-
export { MAX_MENU_HTML_LENGTH, isMenuHtmlAuthoring, menuHtmlInputProblem, mergeMenuHtml, type MenuHtmlAuthoring, } from "./menu-html.js";
|
|
30
|
+
export { MAX_MENU_HTML_LENGTH, isMenuHtmlAuthoring, menuHtmlInputProblem, mergeMenuHtml, type MenuHtmlAuthoring, type MenuHtmlNormalizer, type MenuHtmlOptions, } from "./menu-html.js";
|
|
31
31
|
export { resolveForLocation, resolveMenuDoc, type ResolveTargetHrefFn, type ResolvedTargetHref, type ResolveForLocationDeps, type ResolveForLocationServiceInput, type ResolveForLocationRequired, type ResolveForLocationOptional, } from "./resolver.js";
|
|
32
32
|
export { rebuildNavLocationBindings, type RebuildNavLocationBindingsDeps, type RebuildNavLocationBindingsResult, } from "./reconcile.js";
|
|
33
33
|
/** Dropdown menus (2026-10-11): toggle markup + the inline keyboard/touch script (see `dropdown.ts`). */
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file HTML mode for a menu (2026-10-08): the author writes the menu's markup by hand instead of
|
|
3
|
+
* building an item tree, so it can be styled however they want.
|
|
4
|
+
*
|
|
5
|
+
* Same trust model as HTML-mode forms (`forms/html/html-authoring.ts`) and HTML Pages: the markup is
|
|
6
|
+
* admin/owner-trusted and NOT sanitized. Hosts gate it behind their raw-HTML permission (Tovu binds
|
|
7
|
+
* `pages.edit_html`, the one forms use); this module only validates and merges. The injected
|
|
8
|
+
* normalizer balances the fragment so a stray `</nav>` or an unclosed `<div>` cannot close or
|
|
9
|
+
* swallow the theme element the menu renders inside. The Node adapter supplies parse5; the
|
|
10
|
+
* universal entry never loads a parser or assumes a browser DOM.
|
|
11
|
+
*
|
|
12
|
+
* Both modes' data live side by side in the doc: switching mode never deletes the other one, so an
|
|
13
|
+
* author can flip back and find their items (or their HTML) still there.
|
|
14
|
+
*/
|
|
1
15
|
import type { NavMenuDoc, NavMenuMode } from "./types.js";
|
|
2
16
|
/** Same ceiling HTML-mode forms use. */
|
|
3
17
|
export declare const MAX_MENU_HTML_LENGTH = 200000;
|
|
@@ -12,6 +26,14 @@ export interface MenuHtmlAuthoring {
|
|
|
12
26
|
* @complexity O(1).
|
|
13
27
|
*/
|
|
14
28
|
export declare function isMenuHtmlAuthoring({ mode, html }: MenuHtmlAuthoring, _optional?: Record<string, never>): boolean;
|
|
29
|
+
/** Balance a trusted HTML fragment while preserving scripts and authored styling. */
|
|
30
|
+
export type MenuHtmlNormalizer = (required: {
|
|
31
|
+
html: string;
|
|
32
|
+
}, optional?: Record<string, never>) => string;
|
|
33
|
+
/** A host supplies its environment's fragment parser; omitted is valid for item-only writes. */
|
|
34
|
+
export interface MenuHtmlOptions {
|
|
35
|
+
normalizeHtml?: MenuHtmlNormalizer | undefined;
|
|
36
|
+
}
|
|
15
37
|
/**
|
|
16
38
|
* Why a write's `mode`/`html` is invalid, or `null` when it is fine. A message rather than a throw so
|
|
17
39
|
* `menu-service.ts` raises its own `MenuValidationError` without this file importing it back.
|
|
@@ -22,10 +44,12 @@ export declare function menuHtmlInputProblem({ mode, html }: MenuHtmlAuthoring,
|
|
|
22
44
|
* The doc's `mode`/`html` after a valid write: submitted values win, omitted ones keep what is
|
|
23
45
|
* stored, and a menu that never used HTML mode gets neither key, so its stored doc (and publish
|
|
24
46
|
* hash) is unchanged. Call {@link menuHtmlInputProblem} first.
|
|
47
|
+
* @param optional Host fragment normalizer, required when input.html is supplied.
|
|
48
|
+
* @throws {Error} Submitted markup has no configured normalizer; never saves unbalanced HTML.
|
|
25
49
|
* @complexity O(h) in the submitted html length (one parse + serialize).
|
|
26
50
|
*/
|
|
27
51
|
export declare function mergeMenuHtml({ input, existing }: {
|
|
28
52
|
input: MenuHtmlAuthoring;
|
|
29
53
|
existing?: Pick<NavMenuDoc, "mode" | "html"> | undefined;
|
|
30
|
-
},
|
|
54
|
+
}, optional?: MenuHtmlOptions): Pick<NavMenuDoc, "mode" | "html">;
|
|
31
55
|
//# sourceMappingURL=menu-html.d.ts.map
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file HTML mode for a menu (2026-10-08): the author writes the menu's markup by hand instead of
|
|
3
|
-
* building an item tree, so it can be styled however they want.
|
|
4
|
-
*
|
|
5
|
-
* Same trust model as HTML-mode forms (`forms/html/html-authoring.ts`) and HTML Pages: the markup is
|
|
6
|
-
* admin/owner-trusted and NOT sanitized. Hosts gate it behind their raw-HTML permission (Tovu binds
|
|
7
|
-
* `pages.edit_html`, the one forms use); this module only validates and normalizes. parse5 balances
|
|
8
|
-
* the fragment so a stray `</nav>` or an unclosed `<div>` cannot close or swallow the theme element
|
|
9
|
-
* the menu renders inside.
|
|
10
|
-
*
|
|
11
|
-
* Both modes' data live side by side in the doc: switching mode never deletes the other one, so an
|
|
12
|
-
* author can flip back and find their items (or their HTML) still there.
|
|
13
|
-
*/
|
|
14
|
-
import { parseFragment, serialize } from "parse5";
|
|
15
1
|
/** Same ceiling HTML-mode forms use. */
|
|
16
2
|
export const MAX_MENU_HTML_LENGTH = 200_000;
|
|
17
3
|
/**
|
|
@@ -22,10 +8,6 @@ export const MAX_MENU_HTML_LENGTH = 200_000;
|
|
|
22
8
|
export function isMenuHtmlAuthoring({ mode, html }, _optional = {}) {
|
|
23
9
|
return html !== undefined || mode === "html";
|
|
24
10
|
}
|
|
25
|
-
/** Balance the fragment; scripts and authored styling stay as written (trusted markup). */
|
|
26
|
-
function normalizeMenuHtml(html) {
|
|
27
|
-
return serialize(parseFragment(html));
|
|
28
|
-
}
|
|
29
11
|
/**
|
|
30
12
|
* Why a write's `mode`/`html` is invalid, or `null` when it is fine. A message rather than a throw so
|
|
31
13
|
* `menu-service.ts` raises its own `MenuValidationError` without this file importing it back.
|
|
@@ -43,11 +25,18 @@ export function menuHtmlInputProblem({ mode, html }, _optional = {}) {
|
|
|
43
25
|
* The doc's `mode`/`html` after a valid write: submitted values win, omitted ones keep what is
|
|
44
26
|
* stored, and a menu that never used HTML mode gets neither key, so its stored doc (and publish
|
|
45
27
|
* hash) is unchanged. Call {@link menuHtmlInputProblem} first.
|
|
28
|
+
* @param optional Host fragment normalizer, required when input.html is supplied.
|
|
29
|
+
* @throws {Error} Submitted markup has no configured normalizer; never saves unbalanced HTML.
|
|
46
30
|
* @complexity O(h) in the submitted html length (one parse + serialize).
|
|
47
31
|
*/
|
|
48
|
-
export function mergeMenuHtml({ input, existing },
|
|
32
|
+
export function mergeMenuHtml({ input, existing }, optional = {}) {
|
|
49
33
|
const mode = input.mode ?? existing?.mode;
|
|
50
|
-
|
|
34
|
+
let html = existing?.html;
|
|
35
|
+
if (input.html !== undefined) {
|
|
36
|
+
if (!optional.normalizeHtml)
|
|
37
|
+
throw new Error("Menu HTML normalization is not configured");
|
|
38
|
+
html = optional.normalizeHtml({ html: input.html });
|
|
39
|
+
}
|
|
51
40
|
return { ...(mode !== undefined ? { mode } : {}), ...(html !== undefined ? { html } : {}) };
|
|
52
41
|
}
|
|
53
42
|
//# sourceMappingURL=menu-html.js.map
|
|
@@ -2,6 +2,7 @@ import type { Clock, IdGenerator, UUID } from "@jini-ai/core/primitives";
|
|
|
2
2
|
import type { OutboxPort } from "../core/ports.js";
|
|
3
3
|
import type { MenuRepoPort } from "./repo.memory.js";
|
|
4
4
|
import type { NavLocationBindingRepoPort } from "./ports.js";
|
|
5
|
+
import { type MenuHtmlNormalizer } from "./menu-html.js";
|
|
5
6
|
import { type NavItemNode, type NavLocationBindingRow, type NavLocationKey, type NavMenuEntry, type NavMenuMode } from "./types.js";
|
|
6
7
|
export declare class MenuNotFoundError extends Error {
|
|
7
8
|
constructor({ message }: {
|
|
@@ -135,6 +136,8 @@ export declare function validateAndCloneTree({ items }: {
|
|
|
135
136
|
items: readonly NavItemNode[];
|
|
136
137
|
}, limits?: TreeValidationLimits): NavItemNode[];
|
|
137
138
|
export interface CreateMenuDeps {
|
|
139
|
+
/** Required for submitted HTML; Node hosts can use the navigation/node adapter. */
|
|
140
|
+
normalizeMenuHtml?: MenuHtmlNormalizer | undefined;
|
|
138
141
|
repo: MenuRepoPort;
|
|
139
142
|
clock: Clock;
|
|
140
143
|
idGen: IdGenerator;
|
|
@@ -177,6 +180,8 @@ export declare function createMenu(required: CreateMenuRequired, optional?: Crea
|
|
|
177
180
|
menu: NavMenuEntry;
|
|
178
181
|
}>;
|
|
179
182
|
export interface UpdateMenuTreeDeps {
|
|
183
|
+
/** Required for submitted HTML; omitted markup retains the already-normalized stored value. */
|
|
184
|
+
normalizeMenuHtml?: MenuHtmlNormalizer | undefined;
|
|
180
185
|
repo: MenuRepoPort;
|
|
181
186
|
clock: Clock;
|
|
182
187
|
/** Not previously present on this deps bag — needed to mint the outbox event's id. */
|
|
@@ -283,11 +283,11 @@ function assertValidTitleAndSlug(title, slug) {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
/** Validated, merged HTML-mode doc fields — `menu-html.ts`'s problem check raised as this file's own error. */
|
|
286
|
-
function menuHtmlFields({ input, existing }) {
|
|
286
|
+
function menuHtmlFields({ input, existing, normalizeHtml }) {
|
|
287
287
|
const problem = menuHtmlInputProblem(input);
|
|
288
288
|
if (problem)
|
|
289
289
|
throw new MenuValidationError({ message: problem });
|
|
290
|
-
return mergeMenuHtml({ input, existing });
|
|
290
|
+
return mergeMenuHtml({ input, existing }, { normalizeHtml });
|
|
291
291
|
}
|
|
292
292
|
/**
|
|
293
293
|
* Creates a new `menu` entry in `published` status with no location
|
|
@@ -313,7 +313,7 @@ export async function createMenu(required, optional = {}) {
|
|
|
313
313
|
if (duplicate)
|
|
314
314
|
throw new MenuConflictError({ message: `slug '${slug}' already exists` });
|
|
315
315
|
const items = validateAndCloneTree({ items: input.items ?? [] }, optional.limits);
|
|
316
|
-
const htmlFields = menuHtmlFields({ input });
|
|
316
|
+
const htmlFields = menuHtmlFields({ input, normalizeHtml: deps.normalizeMenuHtml });
|
|
317
317
|
const menu = {
|
|
318
318
|
id: deps.idGen.newId(),
|
|
319
319
|
workspaceId: input.workspaceId,
|
|
@@ -422,7 +422,7 @@ export async function updateMenuTree(required, optional = {}) {
|
|
|
422
422
|
});
|
|
423
423
|
// Every writer (admin, agent tool, publish repoint, reverters) comes through here, so the stored
|
|
424
424
|
// HTML-mode fields are carried forward unless this write sets them — an items-only save never drops them.
|
|
425
|
-
const htmlFields = menuHtmlFields({ input, existing: existing.doc });
|
|
425
|
+
const htmlFields = menuHtmlFields({ input, existing: existing.doc, normalizeHtml: deps.normalizeMenuHtml });
|
|
426
426
|
const menu = {
|
|
427
427
|
...existing,
|
|
428
428
|
title,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { mergeMenuHtml as mergeUniversalMenuHtml, type MenuHtmlOptions } from "../menu-html.js";
|
|
2
|
+
import { createMenu as createUniversalMenu, updateMenuTree as updateUniversalMenuTree, type CreateMenuRequired, type CreateMenuOptional, type UpdateMenuTreeRequired, type UpdateMenuTreeOptional } from "../menu-service.js";
|
|
3
|
+
import { buildMenusRegistrations as buildUniversalMenusRegistrations, type MenusToolDeps } from "../tool-registrations.js";
|
|
4
|
+
export * from "../index.js";
|
|
5
|
+
/** Balance the fragment; scripts and authored styling stay as written (trusted markup).
|
|
6
|
+
* @param required Trusted authored markup; the service enforces the length cap before calling.
|
|
7
|
+
* @returns Balanced fragment, without dangling outer closers.
|
|
8
|
+
* @complexity O(h) time and space in submitted HTML length.
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeMenuHtml({ html }: {
|
|
11
|
+
html: string;
|
|
12
|
+
}, _optional?: Record<string, never>): string;
|
|
13
|
+
/** Supply Node normalization to the universal create owner; validation, errors, writes and
|
|
14
|
+
* outbox publication are unchanged. Caller-provided normalization takes precedence.
|
|
15
|
+
* @complexity Same tree/slug scans as the universal owner, plus O(h) HTML time and space.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createMenu(required: CreateMenuRequired, optional?: CreateMenuOptional): ReturnType<typeof createUniversalMenu>;
|
|
18
|
+
/** Supply Node normalization to the universal update owner, retaining its OCC and event contract.
|
|
19
|
+
* @complexity Same tree/slug scans as the universal owner, plus O(h) HTML time and space.
|
|
20
|
+
*/
|
|
21
|
+
export declare function updateMenuTree(required: UpdateMenuTreeRequired, optional?: UpdateMenuTreeOptional): ReturnType<typeof updateUniversalMenuTree>;
|
|
22
|
+
/** Preserve the merge API for Node consumers, balancing only submitted HTML.
|
|
23
|
+
* @complexity O(h) time and space in submitted HTML length; omitted HTML is O(1).
|
|
24
|
+
*/
|
|
25
|
+
export declare function mergeMenuHtml(required: Parameters<typeof mergeUniversalMenuHtml>[0], optional?: MenuHtmlOptions): ReturnType<typeof mergeUniversalMenuHtml>;
|
|
26
|
+
/** Bind Node normalization once for the existing tool builder; permission checks and saves
|
|
27
|
+
* remain in their universal owners. Construction is O(1) time and space (fixed catalog).
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildMenusRegistrations(required: MenusToolDeps, _optional?: Record<string, never>): ReturnType<typeof buildUniversalMenusRegistrations>;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Node navigation adapter: the public barrel stays compatible, while HTML writes receive
|
|
2
|
+
* parse5 normalization. Item services and all persistence/permission policy stay universal.
|
|
3
|
+
* @example import { createMenu } from "@jini-ai/cms/navigation/node";
|
|
4
|
+
*/
|
|
5
|
+
import { parseFragment, serialize } from "parse5";
|
|
6
|
+
import { mergeMenuHtml as mergeUniversalMenuHtml } from "../menu-html.js";
|
|
7
|
+
import { createMenu as createUniversalMenu, updateMenuTree as updateUniversalMenuTree, } from "../menu-service.js";
|
|
8
|
+
import { buildMenusRegistrations as buildUniversalMenusRegistrations } from "../tool-registrations.js";
|
|
9
|
+
export * from "../index.js";
|
|
10
|
+
/** Balance the fragment; scripts and authored styling stay as written (trusted markup).
|
|
11
|
+
* @param required Trusted authored markup; the service enforces the length cap before calling.
|
|
12
|
+
* @returns Balanced fragment, without dangling outer closers.
|
|
13
|
+
* @complexity O(h) time and space in submitted HTML length.
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeMenuHtml({ html }, _optional = {}) {
|
|
16
|
+
return serialize(parseFragment(html));
|
|
17
|
+
}
|
|
18
|
+
/** Supply Node normalization to the universal create owner; validation, errors, writes and
|
|
19
|
+
* outbox publication are unchanged. Caller-provided normalization takes precedence.
|
|
20
|
+
* @complexity Same tree/slug scans as the universal owner, plus O(h) HTML time and space.
|
|
21
|
+
*/
|
|
22
|
+
export function createMenu(required, optional = {}) {
|
|
23
|
+
return createUniversalMenu({ ...required, deps: { ...required.deps, normalizeMenuHtml: required.deps.normalizeMenuHtml ?? normalizeMenuHtml } }, optional);
|
|
24
|
+
}
|
|
25
|
+
/** Supply Node normalization to the universal update owner, retaining its OCC and event contract.
|
|
26
|
+
* @complexity Same tree/slug scans as the universal owner, plus O(h) HTML time and space.
|
|
27
|
+
*/
|
|
28
|
+
export function updateMenuTree(required, optional = {}) {
|
|
29
|
+
return updateUniversalMenuTree({ ...required, deps: { ...required.deps, normalizeMenuHtml: required.deps.normalizeMenuHtml ?? normalizeMenuHtml } }, optional);
|
|
30
|
+
}
|
|
31
|
+
/** Preserve the merge API for Node consumers, balancing only submitted HTML.
|
|
32
|
+
* @complexity O(h) time and space in submitted HTML length; omitted HTML is O(1).
|
|
33
|
+
*/
|
|
34
|
+
export function mergeMenuHtml(required, optional = {}) {
|
|
35
|
+
return mergeUniversalMenuHtml(required, { ...optional, normalizeHtml: optional.normalizeHtml ?? normalizeMenuHtml });
|
|
36
|
+
}
|
|
37
|
+
/** Bind Node normalization once for the existing tool builder; permission checks and saves
|
|
38
|
+
* remain in their universal owners. Construction is O(1) time and space (fixed catalog).
|
|
39
|
+
*/
|
|
40
|
+
export function buildMenusRegistrations(required, _optional = {}) {
|
|
41
|
+
return buildUniversalMenusRegistrations({ ...required, normalizeMenuHtml: required.normalizeMenuHtml ?? normalizeMenuHtml });
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -3,6 +3,7 @@ import type { Clock } from "@jini-ai/core/primitives";
|
|
|
3
3
|
import type { AuthorizeFn } from "../core/commands/command.js";
|
|
4
4
|
import type { OutboxPort } from "../core/ports.js";
|
|
5
5
|
import { type DerivedRiskByToolId, type ToolRegistration } from "@jini-ai/core";
|
|
6
|
+
import { type MenuHtmlNormalizer } from "./menu-html.js";
|
|
6
7
|
import type { NavLocationBindingRepoPort } from "./ports.js";
|
|
7
8
|
import type { MenuRepoPort } from "./repo.memory.js";
|
|
8
9
|
/**
|
|
@@ -12,6 +13,8 @@ import type { MenuRepoPort } from "./repo.memory.js";
|
|
|
12
13
|
* route-deps object after binding the kernel clock/ID contracts.
|
|
13
14
|
*/
|
|
14
15
|
export interface MenusToolDeps {
|
|
16
|
+
/** Environment-owned fragment normalizer, forwarded to the audited menu write services. */
|
|
17
|
+
normalizeMenuHtml?: MenuHtmlNormalizer | undefined;
|
|
15
18
|
authorize: AuthorizeFn;
|
|
16
19
|
workspaceId: string;
|
|
17
20
|
clock: Clock;
|
|
@@ -80,7 +80,7 @@ async function requireHtmlAuthoringPermission({ routeDeps, principalId, authorin
|
|
|
80
80
|
await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId, permission: routeDeps.rawHtmlPermission }, { entityType: "menu", ...(menuId ? { entityId: menuId } : {}) });
|
|
81
81
|
}
|
|
82
82
|
function menusDeps(routeDeps) {
|
|
83
|
-
return { repo: routeDeps.menuRepo, clock: routeDeps.clock, idGen: routeDeps.idGen, outbox: routeDeps.outbox };
|
|
83
|
+
return { repo: routeDeps.menuRepo, clock: routeDeps.clock, idGen: routeDeps.idGen, outbox: routeDeps.outbox, normalizeMenuHtml: routeDeps.normalizeMenuHtml };
|
|
84
84
|
}
|
|
85
85
|
export function buildMenusRegistrations(routeDeps) {
|
|
86
86
|
const handlers = {
|
package/dist/seo/media.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mimeForTransformFormat } from "../
|
|
1
|
+
import { mimeForTransformFormat } from "../core/transform-format.js";
|
|
2
2
|
/**
|
|
3
3
|
* @file `resolveSeoImageRef` (ADR-PIPE-008 Decision §6, C-013, EC-07;
|
|
4
4
|
* EC-07's "never generates" clause AMENDED 2026-09-05 — see the ADR's
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/cms",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "Content-model capability for Jini-hosted products: framework-free contracts, ports, and domain services in `/core` and per-domain subpaths; Node-bound adapters in domain-specific subpaths.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"./widgets/html/gallery-render": "universal",
|
|
37
37
|
"./navigation/types": "universal",
|
|
38
38
|
"./navigation/dropdown": "universal",
|
|
39
|
+
"./navigation/node": "node",
|
|
39
40
|
"./media/slug": "universal",
|
|
40
41
|
"./forms": "universal",
|
|
41
42
|
"./forms/html": "node",
|
|
@@ -257,6 +258,11 @@
|
|
|
257
258
|
"import": "./dist/navigation/dropdown.js",
|
|
258
259
|
"default": "./dist/navigation/dropdown.js"
|
|
259
260
|
},
|
|
261
|
+
"./navigation/node": {
|
|
262
|
+
"types": "./dist/navigation/node/index.d.ts",
|
|
263
|
+
"import": "./dist/navigation/node/index.js",
|
|
264
|
+
"default": "./dist/navigation/node/index.js"
|
|
265
|
+
},
|
|
260
266
|
"./media/slug": {
|
|
261
267
|
"types": "./dist/media/slug.d.ts",
|
|
262
268
|
"import": "./dist/media/slug.js",
|
|
@@ -413,8 +419,8 @@
|
|
|
413
419
|
"@electric-sql/pglite": "0.5.8",
|
|
414
420
|
"jsdom": "^25.0.1",
|
|
415
421
|
"@jini-ai/core": "0.4.4",
|
|
416
|
-
"@jini-ai/
|
|
417
|
-
"@jini-ai/
|
|
422
|
+
"@jini-ai/http-kit": "0.4.1",
|
|
423
|
+
"@jini-ai/db": "0.3.3"
|
|
418
424
|
},
|
|
419
425
|
"dependencies": {
|
|
420
426
|
"@jini-ai/core": "^0.4.4"
|