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.
- package/dist/acceptance/matrix.mjs +4 -4
- package/dist/cli/index.mjs +62123 -699
- package/dist/cli/init-docs/content/getting-started.md +27 -0
- package/dist/entryCommands-DSQZqznu.mjs +23110 -0
- package/dist/{eventUpsertInputSchema-VVBVGA6x.mjs → eventUpsertInputSchema-zCTx0fdF.mjs} +233 -7
- package/dist/{load-config-gqUEjIyB.mjs → load-config-B68gtN45.mjs} +3 -3
- package/dist/load-config-ghkXBKim.mjs +4 -0
- package/dist/{plan-HAEMHc5T.mjs → plan-CkMANumv.mjs} +36 -3
- package/dist/scaffold/index.mjs +2 -2
- package/dist/{richTextSchema-DTvrDDF5.mjs → sdk-event-upsert-CrOuJrxo.mjs} +46 -271
- package/dist/{src-Dixcv4PB.mjs → src-BENi4Q5k.mjs} +28 -17
- package/dist/testing.mjs +4 -4
- package/package.json +2 -2
- package/dist/entryCommands-8kDjOex0.mjs +0 -111695
- package/dist/load-config-Cut7_6t_.mjs +0 -4
|
@@ -1,8 +1,233 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { u as EVENT_PRESET_VALUES } from "./src-
|
|
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: "
|
|
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
|
-
|
|
4354
|
+
const leafType = leafToBlockFieldType(field.type);
|
|
4355
|
+
if (leafType) {
|
|
4129
4356
|
out.push({
|
|
4130
4357
|
id,
|
|
4131
|
-
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.
|
|
11
|
-
var peerDependencies = { "@riverbankcms/sdk": ">=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.
|
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
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 ? [
|
|
2082
|
+
const portal = config.withPortalAuth ? [
|
|
2083
|
+
portalCallbackRouteTemplate(),
|
|
2084
|
+
portalLogoutRouteTemplate(),
|
|
2085
|
+
...portalPageTemplates()
|
|
2086
|
+
] : [];
|
|
2054
2087
|
return [
|
|
2055
2088
|
gitignoreTemplate(),
|
|
2056
2089
|
envConfigTemplate(config),
|
package/dist/scaffold/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "../src-
|
|
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-
|
|
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 };
|