riverbankcms 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,233 @@
1
1
  #!/usr/bin/env node
2
- import { S as normalizeLinkValue, d as classifySdkEventBookingOffer, l as sdkEventUpsertOptionalFieldSchemas, n as createRichTextDoc, s as sdkCourseBookingSettingsSchema, u as classifySdkEventSchedule, x as linkSchema } from "./richTextSchema-DTvrDDF5.mjs";
3
- import { u as EVENT_PRESET_VALUES } from "./src-Dixcv4PB.mjs";
2
+ import { _ as normalizeLinkValue, a as classifySdkEventSchedule, g as linkSchema, i as sdkEventUpsertOptionalFieldSchemas, n as sdkCourseBookingSettingsSchema, o as classifySdkEventBookingOffer } from "./sdk-event-upsert-CrOuJrxo.mjs";
3
+ import { u as EVENT_PRESET_VALUES } from "./src-BENi4Q5k.mjs";
4
4
  import { z } from "zod";
5
5
 
6
+ //#region ../blocks/src/lib/typeGuards.ts
7
+ /**
8
+ * Strict record guard: excludes null, primitives, and arrays.
9
+ */
10
+ function isRecord$3(value) {
11
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12
+ }
13
+ /**
14
+ * Non-null object guard: includes arrays for behavior parity with legacy helpers.
15
+ *
16
+ * NOTE: This intentionally narrows to UnknownRecord for compatibility with existing
17
+ * callsites even though arrays pass at runtime. Follow-up cleanup should replace
18
+ * this API with a more precise predicate type and migrate consumers.
19
+ */
20
+ function isObjectRecord(value) {
21
+ return typeof value === "object" && value !== null;
22
+ }
23
+ /**
24
+ * Coerces unknown input into a strict record, returning an empty object fallback.
25
+ */
26
+ function asRecord(value) {
27
+ return isRecord$3(value) ? value : {};
28
+ }
29
+
30
+ //#endregion
31
+ //#region ../blocks/src/system/manifest/richTextSchema.ts
32
+ /**
33
+ * Shared Rich Text Schema
34
+ *
35
+ * This module provides a single source of truth for TipTap rich text validation
36
+ * across both the block-form editor and the block rendering system.
37
+ *
38
+ * Rich-text fields use one canonical root shape:
39
+ * `{ type: 'doc', content: [...] }`.
40
+ */
41
+ /**
42
+ * TipTap document node schema (recursive structure)
43
+ *
44
+ * Supports the full TipTap document structure including:
45
+ * - type: Node type (e.g., 'doc', 'paragraph', 'text')
46
+ * - content: Nested array of child nodes
47
+ * - text: Text content for text nodes
48
+ * - attrs: Node attributes (e.g., alignment, color)
49
+ * - marks: Text formatting marks (e.g., bold, italic)
50
+ */
51
+ const TipTapMarkSchema = z.object({
52
+ type: z.string().min(1),
53
+ attrs: z.record(z.string(), z.unknown()).optional()
54
+ }).strict();
55
+ const TipTapNodeSchema = z.lazy(() => z.object({
56
+ type: z.string().min(1),
57
+ content: z.array(TipTapNodeSchema).optional(),
58
+ text: z.string().optional(),
59
+ attrs: z.record(z.string(), z.unknown()).optional(),
60
+ marks: z.array(TipTapMarkSchema).optional()
61
+ }).strict().superRefine((node, ctx) => {
62
+ if (node.type === "doc") {
63
+ ctx.addIssue({
64
+ code: "custom",
65
+ path: ["type"],
66
+ message: "Document nodes are only valid at the rich-text root"
67
+ });
68
+ }
69
+ if (node.type === "text") {
70
+ if (node.text === undefined || node.text.length === 0) {
71
+ ctx.addIssue({
72
+ code: "custom",
73
+ path: ["text"],
74
+ message: "Text nodes require a non-empty text value"
75
+ });
76
+ }
77
+ if (node.content !== undefined) {
78
+ ctx.addIssue({
79
+ code: "custom",
80
+ path: ["content"],
81
+ message: "Text nodes cannot contain child nodes"
82
+ });
83
+ }
84
+ return;
85
+ }
86
+ if (node.text !== undefined) {
87
+ ctx.addIssue({
88
+ code: "custom",
89
+ path: ["text"],
90
+ message: "Only text nodes can contain a text value"
91
+ });
92
+ }
93
+ }));
94
+ /**
95
+ * Canonical schema for a TipTap rich-text document root.
96
+ *
97
+ * Stricter than {@link TipTapNodeSchema}: `type` is literally `'doc'` and
98
+ * `content` is required (possibly empty). This is the shape every producer
99
+ * (dummy content, editor output, migrations) must emit, and the shape every
100
+ * non-legacy consumer can rely on.
101
+ *
102
+ */
103
+ const RichTextDocSchema = z.object({
104
+ type: z.literal("doc"),
105
+ content: z.array(TipTapNodeSchema)
106
+ }).strict();
107
+ /**
108
+ * Type guard for {@link RichTextDoc}.
109
+ *
110
+ * Returns `true` only for the canonical unwrapped form. The shared structural
111
+ * contract lives in `@riverbankcms/core`; parity tests keep its dependency-free
112
+ * guard aligned with this detailed Zod validator.
113
+ */
114
+ function isRichTextDoc(value) {
115
+ return RichTextDocSchema.safeParse(value).success;
116
+ }
117
+ /**
118
+ * Build a canonical {@link RichTextDoc} from plain-text paragraphs.
119
+ *
120
+ * Single source of truth for constructing a TipTap doc from strings.
121
+ * Used by:
122
+ * - `@riverbankcms/dummy-content` (fixture body generators)
123
+ * - `@riverbankcms/content-model` (default template `cta-full` content)
124
+ *
125
+ * Empty strings produce a paragraph with no `content` array — matching
126
+ * the TipTap convention for empty paragraphs. The shape is exactly what
127
+ * {@link RichTextDocSchema} validates, so the type and runtime check
128
+ * never drift from the constructor.
129
+ */
130
+ function createRichTextDoc(paragraphs) {
131
+ return {
132
+ type: "doc",
133
+ content: paragraphs.map((text) => text ? {
134
+ type: "paragraph",
135
+ content: [{
136
+ type: "text",
137
+ text
138
+ }]
139
+ } : { type: "paragraph" })
140
+ };
141
+ }
142
+ function createHardBreakNode() {
143
+ return { type: "hardBreak" };
144
+ }
145
+ /**
146
+ * Parse a small markdown subset into TipTap inline nodes.
147
+ *
148
+ * This intentionally supports only bold (`**text**`) because the SDK/content
149
+ * import paths use it for lightweight default content, not full markdown.
150
+ */
151
+ function parseRichTextInlineMarkdown(text) {
152
+ const out = [];
153
+ const pushText = (value, marks) => {
154
+ if (!value) return;
155
+ out.push(marks && marks.length > 0 ? {
156
+ type: "text",
157
+ text: value,
158
+ marks
159
+ } : {
160
+ type: "text",
161
+ text: value
162
+ });
163
+ };
164
+ let rest = text;
165
+ while (rest.length > 0) {
166
+ const start = rest.indexOf("**");
167
+ if (start === -1) {
168
+ pushText(rest);
169
+ break;
170
+ }
171
+ const end = rest.indexOf("**", start + 2);
172
+ if (end === -1) {
173
+ pushText(rest);
174
+ break;
175
+ }
176
+ pushText(rest.slice(0, start));
177
+ pushText(rest.slice(start + 2, end), [{ type: "bold" }]);
178
+ rest = rest.slice(end + 2);
179
+ }
180
+ return out;
181
+ }
182
+ function parseRichTextParagraphInlineMarkdown(text) {
183
+ return text.split("\n").flatMap((line, index) => {
184
+ const lineNodes = parseRichTextInlineMarkdown(line.trim());
185
+ return index === 0 ? lineNodes : [createHardBreakNode(), ...lineNodes];
186
+ });
187
+ }
188
+ /**
189
+ * Convert a plain string with a tiny markdown subset to canonical TipTap JSON.
190
+ *
191
+ * Double newlines create semantic paragraphs. Single newlines remain inside the
192
+ * current paragraph as TipTap `hardBreak` nodes for line-oriented content such
193
+ * as addresses, opening hours, and contact details.
194
+ */
195
+ function createRichTextDocFromMarkdown(markdown) {
196
+ const normalized = markdown.replace(/\r\n/g, "\n").trim();
197
+ if (!normalized) return {
198
+ type: "doc",
199
+ content: []
200
+ };
201
+ const paragraphs = normalized.split(/\n{2,}/).map((paragraph) => paragraph.trim()).filter(Boolean);
202
+ return {
203
+ type: "doc",
204
+ content: paragraphs.map((paragraph) => {
205
+ const inline = parseRichTextParagraphInlineMarkdown(paragraph);
206
+ return inline.length > 0 ? {
207
+ type: "paragraph",
208
+ content: inline
209
+ } : { type: "paragraph" };
210
+ })
211
+ };
212
+ }
213
+ function buildRichTextSchema(options) {
214
+ let schema = RichTextDocSchema;
215
+ if (options?.required) {
216
+ const label = options.label ?? "This field";
217
+ schema = schema.refine((val) => {
218
+ const content = val.content;
219
+ if (!Array.isArray(content) || content.length === 0) return false;
220
+ const first = content[0];
221
+ if (content.length === 1 && isRecord$3(first) && first.type === "paragraph" && (!("content" in first) || first.content == null)) {
222
+ return false;
223
+ }
224
+ return true;
225
+ }, { message: `${label} is required` });
226
+ }
227
+ return schema;
228
+ }
229
+
230
+ //#endregion
6
231
  //#region ../content-model/src/title-resolution.ts
7
232
  /**
8
233
  * Content title resolution utilities.
@@ -4107,13 +4332,14 @@ const FIELD_TYPE_CLASSIFICATION = {
4107
4332
  repeater: "whole",
4108
4333
  presetOrCustom: "opaque",
4109
4334
  contentTypeSelect: "opaque",
4110
- entryPicker: "opaque",
4335
+ entryPicker: "leaf",
4111
4336
  time: "opaque"
4112
4337
  };
4113
4338
  function classify(type) {
4114
4339
  return FIELD_TYPE_CLASSIFICATION[type] ?? "unknown";
4115
4340
  }
4116
4341
  function leafToBlockFieldType(type) {
4342
+ if (type === "entryPicker") return "reference";
4117
4343
  return classify(type) === "leaf" ? type : null;
4118
4344
  }
4119
4345
  /**
@@ -4125,10 +4351,11 @@ function collectLeaves(fields, prefix, out) {
4125
4351
  for (const field of fields) {
4126
4352
  const id = prefix ? `${prefix}.${field.id}` : field.id;
4127
4353
  const cls = classify(field.type);
4128
- if (cls === "leaf") {
4354
+ const leafType = leafToBlockFieldType(field.type);
4355
+ if (leafType) {
4129
4356
  out.push({
4130
4357
  id,
4131
- type: field.type,
4358
+ type: leafType,
4132
4359
  required: Boolean(field.required)
4133
4360
  });
4134
4361
  continue;
@@ -4149,7 +4376,6 @@ function collectLeaves(fields, prefix, out) {
4149
4376
  if (tab.fields) collectLeaves(tab.fields, id, out);
4150
4377
  }
4151
4378
  }
4152
- void leafToBlockFieldType;
4153
4379
  }
4154
4380
  }
4155
4381
  function buildManifestRepeaterShape(field) {
@@ -4581,4 +4807,4 @@ const eventUpsertInputSchema = z.object({
4581
4807
  }));
4582
4808
 
4583
4809
  //#endregion
4584
- export { findUnfilledSentinels as i, buildEntryScaffold as n, validateEntryInstance as r, eventUpsertInputSchema as t };
4810
+ export { RichTextDocSchema as a, isRecord$3 as c, findUnfilledSentinels as i, buildEntryScaffold as n, isRichTextDoc as o, validateEntryInstance as r, isObjectRecord as s, eventUpsertInputSchema as t };
@@ -7,8 +7,8 @@ import { existsSync as existsSync$1 } from "fs";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  //#region package.json
10
- var version = "0.20.0";
11
- var peerDependencies = { "@riverbankcms/sdk": ">=0.139.0 <1.0.0" };
10
+ var version = "0.21.0";
11
+ var peerDependencies = { "@riverbankcms/sdk": ">=0.156.0 <1.0.0" };
12
12
  var package_default = {
13
13
  name: "riverbankcms",
14
14
  version,
@@ -83,7 +83,7 @@ var package_default = {
83
83
  //#endregion
84
84
  //#region src/version.ts
85
85
  /** The package manifest is the source-run fallback; builds inject this value. */
86
- const CLI_VERSION = typeof "0.20.0" === "string" ? "0.20.0" : version;
86
+ const CLI_VERSION = typeof "0.21.0" === "string" ? "0.21.0" : version;
87
87
  /**
88
88
  * The CLI's SDK peer range is bundled from the CLI manifest alongside its
89
89
  * version. Runtime config loading uses this value as the single compatibility
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { t as loadConfigFile } from "./load-config-B68gtN45.mjs";
3
+
4
+ export { loadConfigFile };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Nn as err, Pn as ok, tn as parseSiteId } from "./src-Dixcv4PB.mjs";
2
+ import { In as err, Ln as ok, in as parseSiteId } from "./src-BENi4Q5k.mjs";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -1199,10 +1199,16 @@ function globalsCssTemplate(config) {
1199
1199
  @import "@riverbankcms/sdk/preview-next/tailwind-sources.css";
1200
1200
  @import "@riverbankcms/sdk/preview-next/chrome.css";
1201
1201
 
1202
+ ` : "";
1203
+ const portalBlock = config.withPortalAuth ? `/* Customer portal pages (layout, tabs, modals, tickets) use Riverbank's
1204
+ * packaged styles. The portal route helpers supply the site theme scope.
1205
+ */
1206
+ @import "@riverbankcms/sdk/styles.css";
1207
+
1202
1208
  ` : "";
1203
1209
  return {
1204
1210
  path: "src/app/globals.css",
1205
- content: `${previewBlock}/* Add host-owned styles below. */
1211
+ content: `${previewBlock}${portalBlock}/* Add host-owned styles below. */
1206
1212
  `
1207
1213
  };
1208
1214
  }
@@ -1311,6 +1317,29 @@ export default function NotFound() {
1311
1317
  };
1312
1318
  }
1313
1319
 
1320
+ //#endregion
1321
+ //#region src/scaffold/templates/portal-pages.ts
1322
+ function portalPageTemplate(path$1, helper, configPath) {
1323
+ return {
1324
+ path: `src/app/${path$1}/page.tsx`,
1325
+ content: `import { ${helper} } from '@riverbankcms/sdk/next';
1326
+ import { getRiverbankClient } from '@/lib/riverbank-client';
1327
+ import config from '${configPath}';
1328
+
1329
+ export const dynamic = 'force-dynamic';
1330
+ export default ${helper}({ getClient: getRiverbankClient, config });
1331
+ `
1332
+ };
1333
+ }
1334
+ function portalPageTemplates() {
1335
+ return [
1336
+ portalPageTemplate("portal", "createPortalPage", "../../../riverbank.config"),
1337
+ portalPageTemplate("portal/tickets/[registrationId]", "createPortalTicketPage", "../../../../../riverbank.config"),
1338
+ portalPageTemplate("portal/digital-products/[entitlementId]", "createPortalDigitalProductPage", "../../../../../riverbank.config"),
1339
+ portalPageTemplate("portal/digital-products/[entitlementId]/files/[assetId]", "createPortalDigitalProductFilePage", "../../../../../../../riverbank.config")
1340
+ ];
1341
+ }
1342
+
1314
1343
  //#endregion
1315
1344
  //#region src/scaffold/templates/portal-callback-route.ts
1316
1345
  function portalCallbackRouteTemplate() {
@@ -2050,7 +2079,11 @@ function starterContentTemplates(config) {
2050
2079
  //#region src/scaffold/templates/index.ts
2051
2080
  function generateAllTemplates(config) {
2052
2081
  const preview = config.withPreviewMode ? [previewKeepAliveRouteTemplate(), previewModeClientTemplate()] : [];
2053
- const portal = config.withPortalAuth ? [portalCallbackRouteTemplate(), portalLogoutRouteTemplate()] : [];
2082
+ const portal = config.withPortalAuth ? [
2083
+ portalCallbackRouteTemplate(),
2084
+ portalLogoutRouteTemplate(),
2085
+ ...portalPageTemplates()
2086
+ ] : [];
2054
2087
  return [
2055
2088
  gitignoreTemplate(),
2056
2089
  envConfigTemplate(config),
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "../src-Dixcv4PB.mjs";
3
- import { a as prepareScaffoldExecution, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, l as deriveGeneratedPackageRanges, o as writeScaffoldFiles, r as preflightScaffoldPlan, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan, u as findPackageManifestPath } from "../plan-HAEMHc5T.mjs";
2
+ import "../src-BENi4Q5k.mjs";
3
+ import { a as prepareScaffoldExecution, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, l as deriveGeneratedPackageRanges, o as writeScaffoldFiles, r as preflightScaffoldPlan, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan, u as findPackageManifestPath } from "../plan-CkMANumv.mjs";
4
4
 
5
5
  export { deriveGeneratedPackageRanges, deriveScaffoldLocalManifest, deriveScaffoldPlan, findPackageManifestPath, loadGeneratedPackageRanges, preflightScaffoldPlan, preflightScaffoldTargets, prepareScaffoldExecution, writeScaffoldFiles };