@pramen/cms 0.0.48 → 0.0.49
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/index.d.ts +26 -17
- package/dist/index.js +3 -1
- package/package.json +2 -2
- package/src/index.ts +41 -28
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { HandlerContext, Policy, FileRef, BootstrapFn } from "@pramen/server";
|
|
1
|
+
import type { HandlerContext, Policy, FileRef, BootstrapFn, JsonValue } from "@pramen/server";
|
|
2
|
+
import type { EnvBag } from "@pramen/server";
|
|
2
3
|
/** A field in a block type's (or content type's) field schema. Recursive: a `repeater`
|
|
3
4
|
* or `group` nests `fields`. Mirrors WollyCMS's FieldDefinition. */
|
|
4
5
|
export interface FieldDefinition {
|
|
@@ -69,7 +70,7 @@ export interface RegionDefinition {
|
|
|
69
70
|
export interface DefaultBlockDefinition {
|
|
70
71
|
region: string;
|
|
71
72
|
blockTypeSlug: string;
|
|
72
|
-
fields?:
|
|
73
|
+
fields?: FieldValues;
|
|
73
74
|
}
|
|
74
75
|
/** A rich-text value — a serialized editor document (or a plain string). */
|
|
75
76
|
export type RichText = string | {
|
|
@@ -548,7 +549,7 @@ export interface ValidateOpts {
|
|
|
548
549
|
export declare function validateFields(schema: FieldDefinition[] | undefined | null, values: unknown, path?: string, opts?: ValidateOpts): void;
|
|
549
550
|
/** Deep-sanitize the richtext fields in a values object against a field schema (recursing
|
|
550
551
|
* into group/repeater). Returns a sanitized copy; non-richtext fields pass through. */
|
|
551
|
-
export declare function sanitizeFields(schema: FieldDefinition[] | undefined | null, values:
|
|
552
|
+
export declare function sanitizeFields(schema: FieldDefinition[] | undefined | null, values: FieldValues): FieldValues;
|
|
552
553
|
export interface RenderedBlock {
|
|
553
554
|
/** The placement id (cms_page_blocks) — stable per position; used for reorder/remove. */
|
|
554
555
|
id: string;
|
|
@@ -556,7 +557,7 @@ export interface RenderedBlock {
|
|
|
556
557
|
block_id: string;
|
|
557
558
|
block_type: string;
|
|
558
559
|
title: string | null;
|
|
559
|
-
fields:
|
|
560
|
+
fields: FieldValues;
|
|
560
561
|
is_shared: boolean;
|
|
561
562
|
}
|
|
562
563
|
export interface PageTranslation {
|
|
@@ -586,7 +587,7 @@ export interface AssembledPage {
|
|
|
586
587
|
translationGroupId: string | null;
|
|
587
588
|
/** Published sibling locales of this page (for hreflang alternates). */
|
|
588
589
|
translations: PageTranslation[];
|
|
589
|
-
fields:
|
|
590
|
+
fields: FieldValues | null;
|
|
590
591
|
/** Back-compat: mirrors seo.metaTitle/metaDescription. */
|
|
591
592
|
metaTitle: string | null;
|
|
592
593
|
metaDescription: string | null;
|
|
@@ -594,6 +595,14 @@ export interface AssembledPage {
|
|
|
594
595
|
};
|
|
595
596
|
regions: Record<string, RenderedBlock[]>;
|
|
596
597
|
}
|
|
598
|
+
/** One authored field value inside a block / collection / page `fields` bag. Stored
|
|
599
|
+
* as JSON; a `"media"` field is resolved from its stored id to a `ResolvedMedia` at
|
|
600
|
+
* assemble time, and `group`/`repeater` fields nest further bags. */
|
|
601
|
+
export type FieldValue = JsonValue | ResolvedMedia | FieldValues | FieldValue[];
|
|
602
|
+
/** A block / collection / page `fields` bag — field name -> authored value. */
|
|
603
|
+
export interface FieldValues {
|
|
604
|
+
[field: string]: FieldValue;
|
|
605
|
+
}
|
|
597
606
|
/** A `"media"` block field, resolved from a stored media id to a servable shape at
|
|
598
607
|
* assemble time. `url` is the raw (full-size) serving path; pass `key` to `imageUrl()`
|
|
599
608
|
* for on-the-fly transforms. `null` when the referenced media was deleted. */
|
|
@@ -737,7 +746,7 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
737
746
|
title?: string;
|
|
738
747
|
slug?: string;
|
|
739
748
|
locale?: string;
|
|
740
|
-
fields?:
|
|
749
|
+
fields?: FieldValues;
|
|
741
750
|
}, {
|
|
742
751
|
ok: boolean;
|
|
743
752
|
page: Record<string, unknown>;
|
|
@@ -748,7 +757,7 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
748
757
|
title: string;
|
|
749
758
|
slug: string;
|
|
750
759
|
locale?: string;
|
|
751
|
-
fields?:
|
|
760
|
+
fields?: FieldValues;
|
|
752
761
|
}, Record<string, unknown>>;
|
|
753
762
|
/** Create a translation of an existing page: a new page in `locale` sharing the
|
|
754
763
|
* source's translationGroupId (and content type). Content starts empty — the editor
|
|
@@ -778,7 +787,7 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
778
787
|
pageId: string;
|
|
779
788
|
blockTypeSlug: string;
|
|
780
789
|
region: string;
|
|
781
|
-
fields?:
|
|
790
|
+
fields?: FieldValues;
|
|
782
791
|
title?: string;
|
|
783
792
|
position?: number;
|
|
784
793
|
isReusable?: boolean;
|
|
@@ -797,7 +806,7 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
797
806
|
blockId: string;
|
|
798
807
|
region: string;
|
|
799
808
|
position?: number;
|
|
800
|
-
overrides?:
|
|
809
|
+
overrides?: FieldValues;
|
|
801
810
|
}, Record<string, unknown>>;
|
|
802
811
|
/** Fetch a block's RAW content (media fields as ids, not resolved) — for editing. */
|
|
803
812
|
getBlock: import("@pramen/server").Handler<{
|
|
@@ -806,7 +815,7 @@ export declare function createCmsHandlers(opts?: CmsHandlerOpts): {
|
|
|
806
815
|
/** Update a block's content (re-validated against its type's field schema). */
|
|
807
816
|
updateBlock: import("@pramen/server").Handler<{
|
|
808
817
|
blockId: string;
|
|
809
|
-
fields?:
|
|
818
|
+
fields?: FieldValues;
|
|
810
819
|
title?: string;
|
|
811
820
|
}, Record<string, unknown> | undefined>;
|
|
812
821
|
/** Reorder a region: `order` is the page_block ids in their new order. It must cover
|
|
@@ -1007,7 +1016,7 @@ export declare const cmsHandlers: {
|
|
|
1007
1016
|
title?: string;
|
|
1008
1017
|
slug?: string;
|
|
1009
1018
|
locale?: string;
|
|
1010
|
-
fields?:
|
|
1019
|
+
fields?: FieldValues;
|
|
1011
1020
|
}, {
|
|
1012
1021
|
ok: boolean;
|
|
1013
1022
|
page: Record<string, unknown>;
|
|
@@ -1018,7 +1027,7 @@ export declare const cmsHandlers: {
|
|
|
1018
1027
|
title: string;
|
|
1019
1028
|
slug: string;
|
|
1020
1029
|
locale?: string;
|
|
1021
|
-
fields?:
|
|
1030
|
+
fields?: FieldValues;
|
|
1022
1031
|
}, Record<string, unknown>>;
|
|
1023
1032
|
/** Create a translation of an existing page: a new page in `locale` sharing the
|
|
1024
1033
|
* source's translationGroupId (and content type). Content starts empty — the editor
|
|
@@ -1048,7 +1057,7 @@ export declare const cmsHandlers: {
|
|
|
1048
1057
|
pageId: string;
|
|
1049
1058
|
blockTypeSlug: string;
|
|
1050
1059
|
region: string;
|
|
1051
|
-
fields?:
|
|
1060
|
+
fields?: FieldValues;
|
|
1052
1061
|
title?: string;
|
|
1053
1062
|
position?: number;
|
|
1054
1063
|
isReusable?: boolean;
|
|
@@ -1067,7 +1076,7 @@ export declare const cmsHandlers: {
|
|
|
1067
1076
|
blockId: string;
|
|
1068
1077
|
region: string;
|
|
1069
1078
|
position?: number;
|
|
1070
|
-
overrides?:
|
|
1079
|
+
overrides?: FieldValues;
|
|
1071
1080
|
}, Record<string, unknown>>;
|
|
1072
1081
|
/** Fetch a block's RAW content (media fields as ids, not resolved) — for editing. */
|
|
1073
1082
|
getBlock: import("@pramen/server").Handler<{
|
|
@@ -1076,7 +1085,7 @@ export declare const cmsHandlers: {
|
|
|
1076
1085
|
/** Update a block's content (re-validated against its type's field schema). */
|
|
1077
1086
|
updateBlock: import("@pramen/server").Handler<{
|
|
1078
1087
|
blockId: string;
|
|
1079
|
-
fields?:
|
|
1088
|
+
fields?: FieldValues;
|
|
1080
1089
|
title?: string;
|
|
1081
1090
|
}, Record<string, unknown> | undefined>;
|
|
1082
1091
|
/** Reorder a region: `order` is the page_block ids in their new order. It must cover
|
|
@@ -1336,7 +1345,7 @@ export declare function robotsTxt(opts: {
|
|
|
1336
1345
|
interface RouteCtx {
|
|
1337
1346
|
callPrivileged: (opts: {
|
|
1338
1347
|
name: string;
|
|
1339
|
-
input?:
|
|
1348
|
+
input?: JsonValue;
|
|
1340
1349
|
tenant?: string;
|
|
1341
1350
|
roles?: string[];
|
|
1342
1351
|
}) => Promise<Response>;
|
|
@@ -1344,7 +1353,7 @@ interface RouteCtx {
|
|
|
1344
1353
|
interface CmsRoute {
|
|
1345
1354
|
method: string;
|
|
1346
1355
|
path: string;
|
|
1347
|
-
handler: (request: Request, env:
|
|
1356
|
+
handler: (request: Request, env: EnvBag, ctx: RouteCtx) => Promise<Response>;
|
|
1348
1357
|
}
|
|
1349
1358
|
/** Turnkey public routes for `GET /sitemap.xml` and `GET /robots.txt`. Spread into
|
|
1350
1359
|
* `app.routes`. The sitemap pulls published pages via `callPrivileged(listPublishedPages)`.
|
package/dist/index.js
CHANGED
|
@@ -504,7 +504,9 @@ async function assembleLive(db, page) {
|
|
|
504
504
|
// fields (id → ResolvedMedia) in one batched lookup across the whole page.
|
|
505
505
|
const merged = placements.map((p) => {
|
|
506
506
|
const block = asObj(p.block);
|
|
507
|
-
const fields =
|
|
507
|
+
const fields = p.isShared
|
|
508
|
+
? { ...asObj(block.fields), ...asObj(p.overrides) }
|
|
509
|
+
: { ...asObj(block.fields) };
|
|
508
510
|
return { p, block, fields, schema: typeById.get(String(block.typeId))?.fieldsSchema };
|
|
509
511
|
});
|
|
510
512
|
const mediaIds = new Set();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pramen/cms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.49",
|
|
4
4
|
"description": "Optional block/page builder for pramen — Drupal-Paragraphs-style typed blocks in named regions, reusable blocks, scheduled publishing, built entirely from pramen primitives.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@pramen/server": "0.0.
|
|
44
|
+
"@pramen/server": "0.0.49",
|
|
45
45
|
"xss": "^1.0.15"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -42,8 +42,9 @@ import {
|
|
|
42
42
|
Forbidden,
|
|
43
43
|
PramenError,
|
|
44
44
|
} from "@pramen/server";
|
|
45
|
-
import type { HandlerContext, Policy, FileRef, BootstrapFn } from "@pramen/server";
|
|
45
|
+
import type { HandlerContext, Policy, FileRef, BootstrapFn, JsonValue } from "@pramen/server";
|
|
46
46
|
import { filterXSS } from "xss";
|
|
47
|
+
import type { EnvBag } from "@pramen/server";
|
|
47
48
|
|
|
48
49
|
// --- field schema DSL (the block-editor field language) ---------------------
|
|
49
50
|
|
|
@@ -131,7 +132,7 @@ export interface RegionDefinition {
|
|
|
131
132
|
export interface DefaultBlockDefinition {
|
|
132
133
|
region: string;
|
|
133
134
|
blockTypeSlug: string;
|
|
134
|
-
fields?:
|
|
135
|
+
fields?: FieldValues;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
// --- hybrid typed blocks: compile-time inference over a const FieldDefinition[] --------
|
|
@@ -529,7 +530,7 @@ function isDateTimeString(v: string): boolean {
|
|
|
529
530
|
export function validateFields(schema: FieldDefinition[] | undefined | null, values: unknown, path = "", opts: ValidateOpts = {}): void {
|
|
530
531
|
const requireRequired = opts.requireRequired !== false;
|
|
531
532
|
const defs = Array.isArray(schema) ? schema : [];
|
|
532
|
-
const obj = (values ?? {}) as
|
|
533
|
+
const obj = (values ?? {}) as FieldValues;
|
|
533
534
|
if (typeof obj !== "object" || Array.isArray(obj)) throw new BadRequest(`${path || "fields"} must be an object`);
|
|
534
535
|
for (const def of defs) {
|
|
535
536
|
const at = path ? `${path}.${def.name}` : def.name;
|
|
@@ -616,15 +617,15 @@ function sanitizeRichText(html: string): string {
|
|
|
616
617
|
|
|
617
618
|
/** Deep-sanitize the richtext fields in a values object against a field schema (recursing
|
|
618
619
|
* into group/repeater). Returns a sanitized copy; non-richtext fields pass through. */
|
|
619
|
-
export function sanitizeFields(schema: FieldDefinition[] | undefined | null, values:
|
|
620
|
+
export function sanitizeFields(schema: FieldDefinition[] | undefined | null, values: FieldValues): FieldValues {
|
|
620
621
|
const defs = Array.isArray(schema) ? schema : [];
|
|
621
|
-
const out:
|
|
622
|
+
const out: FieldValues = { ...values };
|
|
622
623
|
for (const def of defs) {
|
|
623
624
|
const v = out[def.name];
|
|
624
625
|
if (v == null) continue;
|
|
625
626
|
if (def.type === "richtext" && typeof v === "string") out[def.name] = sanitizeRichText(v);
|
|
626
|
-
else if (def.type === "group" && typeof v === "object" && !Array.isArray(v)) out[def.name] = sanitizeFields(def.fields, v as
|
|
627
|
-
else if (def.type === "repeater" && Array.isArray(v)) out[def.name] = v.map((it) => (it && typeof it === "object" ? sanitizeFields(def.fields, it as
|
|
627
|
+
else if (def.type === "group" && typeof v === "object" && !Array.isArray(v)) out[def.name] = sanitizeFields(def.fields, v as FieldValues);
|
|
628
|
+
else if (def.type === "repeater" && Array.isArray(v)) out[def.name] = v.map((it) => (it && typeof it === "object" ? sanitizeFields(def.fields, it as FieldValues) : it));
|
|
628
629
|
}
|
|
629
630
|
return out;
|
|
630
631
|
}
|
|
@@ -638,7 +639,7 @@ export interface RenderedBlock {
|
|
|
638
639
|
block_id: string;
|
|
639
640
|
block_type: string;
|
|
640
641
|
title: string | null;
|
|
641
|
-
fields:
|
|
642
|
+
fields: FieldValues;
|
|
642
643
|
is_shared: boolean;
|
|
643
644
|
}
|
|
644
645
|
|
|
@@ -671,7 +672,7 @@ export interface AssembledPage {
|
|
|
671
672
|
translationGroupId: string | null;
|
|
672
673
|
/** Published sibling locales of this page (for hreflang alternates). */
|
|
673
674
|
translations: PageTranslation[];
|
|
674
|
-
fields:
|
|
675
|
+
fields: FieldValues | null;
|
|
675
676
|
/** Back-compat: mirrors seo.metaTitle/metaDescription. */
|
|
676
677
|
metaTitle: string | null;
|
|
677
678
|
metaDescription: string | null;
|
|
@@ -682,6 +683,16 @@ export interface AssembledPage {
|
|
|
682
683
|
|
|
683
684
|
// --- media -------------------------------------------------------------------
|
|
684
685
|
|
|
686
|
+
/** One authored field value inside a block / collection / page `fields` bag. Stored
|
|
687
|
+
* as JSON; a `"media"` field is resolved from its stored id to a `ResolvedMedia` at
|
|
688
|
+
* assemble time, and `group`/`repeater` fields nest further bags. */
|
|
689
|
+
export type FieldValue = JsonValue | ResolvedMedia | FieldValues | FieldValue[];
|
|
690
|
+
|
|
691
|
+
/** A block / collection / page `fields` bag — field name -> authored value. */
|
|
692
|
+
export interface FieldValues {
|
|
693
|
+
[field: string]: FieldValue;
|
|
694
|
+
}
|
|
695
|
+
|
|
685
696
|
/** A `"media"` block field, resolved from a stored media id to a servable shape at
|
|
686
697
|
* assemble time. `url` is the raw (full-size) serving path; pass `key` to `imageUrl()`
|
|
687
698
|
* for on-the-fly transforms. `null` when the referenced media was deleted. */
|
|
@@ -720,7 +731,7 @@ export function imageUrl(
|
|
|
720
731
|
}
|
|
721
732
|
|
|
722
733
|
/** Collect the media ids referenced by a fields payload, walking group/repeater nesting. */
|
|
723
|
-
function collectMediaIds(fields:
|
|
734
|
+
function collectMediaIds(fields: FieldValues, schema: FieldDefinition[] | undefined, acc: Set<string>): void {
|
|
724
735
|
if (!Array.isArray(schema)) return;
|
|
725
736
|
for (const def of schema) {
|
|
726
737
|
const v = fields[def.name];
|
|
@@ -738,12 +749,12 @@ function collectMediaIds(fields: Record<string, unknown>, schema: FieldDefinitio
|
|
|
738
749
|
/** Return a copy of `fields` with every `"media"` field resolved from its id to a
|
|
739
750
|
* `ResolvedMedia` (or null), recursing into group/repeater nesting. */
|
|
740
751
|
function resolveMediaFields(
|
|
741
|
-
fields:
|
|
752
|
+
fields: FieldValues,
|
|
742
753
|
schema: FieldDefinition[] | undefined,
|
|
743
754
|
mediaById: Map<string, ResolvedMedia>,
|
|
744
|
-
):
|
|
755
|
+
): FieldValues {
|
|
745
756
|
if (!Array.isArray(schema)) return fields;
|
|
746
|
-
const out:
|
|
757
|
+
const out: FieldValues = { ...fields };
|
|
747
758
|
for (const def of schema) {
|
|
748
759
|
const v = out[def.name];
|
|
749
760
|
if (v == null) continue;
|
|
@@ -782,7 +793,7 @@ interface CmsDb {
|
|
|
782
793
|
const cdb = (ctx: HandlerContext): CmsDb => ctx.db as unknown as CmsDb;
|
|
783
794
|
|
|
784
795
|
const notFound = (what: string) => new PramenError(`${what} not found`, 404, "not_found");
|
|
785
|
-
const asObj = (v: unknown):
|
|
796
|
+
const asObj = (v: unknown): FieldValues => (v && typeof v === "object" ? (v as FieldValues) : {});
|
|
786
797
|
// Timestamps in the SAME shape as the `expr.now()` column default (`datetime('now')`:
|
|
787
798
|
// "YYYY-MM-DD HH:MM:SS", UTC, second precision) so a column's insert-default and its
|
|
788
799
|
// handler-written updates stay lexically comparable (an ISO `T`/`Z` string sorts wrong).
|
|
@@ -816,7 +827,9 @@ async function assembleLive(db: CmsDb, page: Record<string, unknown>): Promise<A
|
|
|
816
827
|
// fields (id → ResolvedMedia) in one batched lookup across the whole page.
|
|
817
828
|
const merged = placements.map((p) => {
|
|
818
829
|
const block = asObj(p.block);
|
|
819
|
-
const fields =
|
|
830
|
+
const fields = p.isShared
|
|
831
|
+
? { ...asObj(block.fields), ...asObj(p.overrides) }
|
|
832
|
+
: { ...asObj(block.fields) };
|
|
820
833
|
return { p, block, fields, schema: typeById.get(String(block.typeId))?.fieldsSchema };
|
|
821
834
|
});
|
|
822
835
|
const mediaIds = new Set<string>();
|
|
@@ -908,7 +921,7 @@ function pageMeta(page: Record<string, unknown>, translations: PageTranslation[]
|
|
|
908
921
|
contentType,
|
|
909
922
|
translationGroupId: (page.translationGroupId as string | null) ?? null,
|
|
910
923
|
translations,
|
|
911
|
-
fields: (page.fields as
|
|
924
|
+
fields: (page.fields as FieldValues | null) ?? null,
|
|
912
925
|
metaTitle,
|
|
913
926
|
metaDescription,
|
|
914
927
|
seo: {
|
|
@@ -1237,7 +1250,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1237
1250
|
* page record itself, which was previously only settable at createPage. A slug/locale
|
|
1238
1251
|
* change re-checks (slug, locale) uniqueness (excluding this page); `fields` is validated
|
|
1239
1252
|
* + sanitized against the content type's fieldsSchema, exactly like createPage. */
|
|
1240
|
-
updatePage: mutation(async (ctx, input: { pageId: string; title?: string; slug?: string; locale?: string; fields?:
|
|
1253
|
+
updatePage: mutation(async (ctx, input: { pageId: string; title?: string; slug?: string; locale?: string; fields?: FieldValues }) => {
|
|
1241
1254
|
const db = cdb(ctx);
|
|
1242
1255
|
const rows = await db.find({ from: "cms_pages", where: { id: input.pageId }, limit: 1 });
|
|
1243
1256
|
const page = rows[0];
|
|
@@ -1262,7 +1275,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1262
1275
|
return { ok: true, page: updated };
|
|
1263
1276
|
}, {
|
|
1264
1277
|
...editor,
|
|
1265
|
-
input: (raw): { pageId: string; title?: string; slug?: string; locale?: string; fields?:
|
|
1278
|
+
input: (raw): { pageId: string; title?: string; slug?: string; locale?: string; fields?: FieldValues } => {
|
|
1266
1279
|
const o = asObj(raw);
|
|
1267
1280
|
if (typeof o.pageId !== "string") throw new BadRequest("pageId is required");
|
|
1268
1281
|
for (const k of ["title", "slug", "locale"] as const) {
|
|
@@ -1273,7 +1286,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1273
1286
|
}),
|
|
1274
1287
|
|
|
1275
1288
|
/** Create a page and auto-scaffold its content type's default blocks. */
|
|
1276
|
-
createPage: mutation(async (ctx, input: { typeId: string; title: string; slug: string; locale?: string; fields?:
|
|
1289
|
+
createPage: mutation(async (ctx, input: { typeId: string; title: string; slug: string; locale?: string; fields?: FieldValues }) => {
|
|
1277
1290
|
const db = cdb(ctx);
|
|
1278
1291
|
const ctRows = await db.find({ from: "cms_content_types", where: { id: input.typeId }, limit: 1 });
|
|
1279
1292
|
const ct = ctRows[0];
|
|
@@ -1314,7 +1327,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1314
1327
|
return page;
|
|
1315
1328
|
}, {
|
|
1316
1329
|
...editor,
|
|
1317
|
-
input: (raw): { typeId: string; title: string; slug: string; locale?: string; fields?:
|
|
1330
|
+
input: (raw): { typeId: string; title: string; slug: string; locale?: string; fields?: FieldValues } => {
|
|
1318
1331
|
const o = asObj(raw);
|
|
1319
1332
|
if (typeof o.typeId !== "string" || typeof o.title !== "string" || typeof o.slug !== "string") {
|
|
1320
1333
|
throw new BadRequest("typeId, title and slug are required");
|
|
@@ -1393,7 +1406,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1393
1406
|
/** Create a block instance and place it into a page region in one call (the common
|
|
1394
1407
|
* editor action). Validates the fields against the block type's schema and the region
|
|
1395
1408
|
* against the content type's allow-list. */
|
|
1396
|
-
addBlock: mutation(async (ctx, input: { pageId: string; blockTypeSlug: string; region: string; fields?:
|
|
1409
|
+
addBlock: mutation(async (ctx, input: { pageId: string; blockTypeSlug: string; region: string; fields?: FieldValues; title?: string; position?: number; isReusable?: boolean }) => {
|
|
1397
1410
|
const db = cdb(ctx);
|
|
1398
1411
|
const pages = await db.find({ from: "cms_pages", where: { id: input.pageId }, limit: 1 });
|
|
1399
1412
|
const page = pages[0];
|
|
@@ -1420,7 +1433,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1420
1433
|
return { block, placement };
|
|
1421
1434
|
}, {
|
|
1422
1435
|
...editor,
|
|
1423
|
-
input: (raw): { pageId: string; blockTypeSlug: string; region: string; fields?:
|
|
1436
|
+
input: (raw): { pageId: string; blockTypeSlug: string; region: string; fields?: FieldValues; title?: string; position?: number; isReusable?: boolean } => {
|
|
1424
1437
|
const o = asObj(raw);
|
|
1425
1438
|
if (typeof o.pageId !== "string" || typeof o.blockTypeSlug !== "string" || typeof o.region !== "string") {
|
|
1426
1439
|
throw new BadRequest("pageId, blockTypeSlug and region are required");
|
|
@@ -1435,7 +1448,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1435
1448
|
* can be placed on several pages; editing it updates them all, while `overrides` let one
|
|
1436
1449
|
* placement diverge. The merged (base + overrides) result is validated against the
|
|
1437
1450
|
* block type's field schema. */
|
|
1438
|
-
placeBlock: mutation(async (ctx, input: { pageId: string; blockId: string; region: string; position?: number; overrides?:
|
|
1451
|
+
placeBlock: mutation(async (ctx, input: { pageId: string; blockId: string; region: string; position?: number; overrides?: FieldValues }) => {
|
|
1439
1452
|
const db = cdb(ctx);
|
|
1440
1453
|
const pages = await db.find({ from: "cms_pages", where: { id: input.pageId }, limit: 1 });
|
|
1441
1454
|
const page = pages[0];
|
|
@@ -1462,7 +1475,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1462
1475
|
});
|
|
1463
1476
|
}, {
|
|
1464
1477
|
...editor,
|
|
1465
|
-
input: (raw): { pageId: string; blockId: string; region: string; position?: number; overrides?:
|
|
1478
|
+
input: (raw): { pageId: string; blockId: string; region: string; position?: number; overrides?: FieldValues } => {
|
|
1466
1479
|
const o = asObj(raw);
|
|
1467
1480
|
if (typeof o.pageId !== "string" || typeof o.blockId !== "string" || typeof o.region !== "string") {
|
|
1468
1481
|
throw new BadRequest("pageId, blockId and region are required");
|
|
@@ -1485,7 +1498,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1485
1498
|
}),
|
|
1486
1499
|
|
|
1487
1500
|
/** Update a block's content (re-validated against its type's field schema). */
|
|
1488
|
-
updateBlock: mutation(async (ctx, input: { blockId: string; fields?:
|
|
1501
|
+
updateBlock: mutation(async (ctx, input: { blockId: string; fields?: FieldValues; title?: string }) => {
|
|
1489
1502
|
const db = cdb(ctx);
|
|
1490
1503
|
const rows = await db.find({ from: "cms_blocks", where: { id: input.blockId }, limit: 1 });
|
|
1491
1504
|
const block = rows[0];
|
|
@@ -1502,7 +1515,7 @@ export function createCmsHandlers(opts: CmsHandlerOpts = {}) {
|
|
|
1502
1515
|
return db.update("cms_blocks", input.blockId, patch);
|
|
1503
1516
|
}, {
|
|
1504
1517
|
...editor,
|
|
1505
|
-
input: (raw): { blockId: string; fields?:
|
|
1518
|
+
input: (raw): { blockId: string; fields?: FieldValues; title?: string } => {
|
|
1506
1519
|
const o = asObj(raw);
|
|
1507
1520
|
if (typeof o.blockId !== "string") throw new BadRequest("blockId is required");
|
|
1508
1521
|
return o as never;
|
|
@@ -2089,12 +2102,12 @@ export function robotsTxt(opts: { origin: string; disallow?: string[] }): string
|
|
|
2089
2102
|
|
|
2090
2103
|
// Minimal shape of a pramen public route (see @pramen/server/worker app.routes).
|
|
2091
2104
|
interface RouteCtx {
|
|
2092
|
-
callPrivileged: (opts: { name: string; input?:
|
|
2105
|
+
callPrivileged: (opts: { name: string; input?: JsonValue; tenant?: string; roles?: string[] }) => Promise<Response>;
|
|
2093
2106
|
}
|
|
2094
2107
|
interface CmsRoute {
|
|
2095
2108
|
method: string;
|
|
2096
2109
|
path: string;
|
|
2097
|
-
handler: (request: Request, env:
|
|
2110
|
+
handler: (request: Request, env: EnvBag, ctx: RouteCtx) => Promise<Response>;
|
|
2098
2111
|
}
|
|
2099
2112
|
|
|
2100
2113
|
/** Turnkey public routes for `GET /sitemap.xml` and `GET /robots.txt`. Spread into
|