@pramen/cms 0.0.15 → 0.0.16
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 +2 -4
- package/dist/index.js +18 -0
- package/package.json +2 -2
- package/src/index.ts +20 -1
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { HandlerContext, Policy, FileRef } from "@pramen/server";
|
|
|
4
4
|
export interface FieldDefinition {
|
|
5
5
|
name: string;
|
|
6
6
|
label?: string;
|
|
7
|
-
type: "text" | "textarea" | "richtext" | "url" | "number" | "boolean" | "media" | "select" | "repeater" | "group";
|
|
7
|
+
type: "text" | "textarea" | "richtext" | "url" | "number" | "boolean" | "date" | "datetime" | "media" | "select" | "repeater" | "group";
|
|
8
8
|
required?: boolean;
|
|
9
9
|
default?: unknown;
|
|
10
10
|
/** repeater/group only — the nested fields. */
|
|
@@ -35,7 +35,7 @@ export type RichText = string | {
|
|
|
35
35
|
};
|
|
36
36
|
/** Map one FieldDefinition (as a const literal) to the TS type of its RENDERED value.
|
|
37
37
|
* Media resolves to `ResolvedMedia` (the assemble-time shape a component receives). */
|
|
38
|
-
export type FieldTsType<D extends FieldDefinition> = D["type"] extends "text" | "textarea" | "url" | "select" ? string : D["type"] extends "richtext" ? RichText : D["type"] extends "number" ? number : D["type"] extends "boolean" ? boolean : D["type"] extends "media" ? ResolvedMedia | null : D["type"] extends "group" ? InferBlockFields<NonNullable<D["fields"]>> : D["type"] extends "repeater" ? InferBlockFields<NonNullable<D["fields"]>>[] : unknown;
|
|
38
|
+
export type FieldTsType<D extends FieldDefinition> = D["type"] extends "text" | "textarea" | "url" | "select" | "date" | "datetime" ? string : D["type"] extends "richtext" ? RichText : D["type"] extends "number" ? number : D["type"] extends "boolean" ? boolean : D["type"] extends "media" ? ResolvedMedia | null : D["type"] extends "group" ? InferBlockFields<NonNullable<D["fields"]>> : D["type"] extends "repeater" ? InferBlockFields<NonNullable<D["fields"]>>[] : unknown;
|
|
39
39
|
/** Infer the `fields` object type from a const `FieldDefinition[]`. Required fields are
|
|
40
40
|
* present; optional ones are `| undefined`. */
|
|
41
41
|
export type InferBlockFields<T extends readonly FieldDefinition[]> = {
|
|
@@ -459,8 +459,6 @@ export interface ValidateOpts {
|
|
|
459
459
|
* required is only mandatory when publishing. Type checks always run. */
|
|
460
460
|
requireRequired?: boolean;
|
|
461
461
|
}
|
|
462
|
-
/** Validate a block/page's `fields` payload against a field schema, throwing a 400 on
|
|
463
|
-
* the first violation. Recursive (repeater/group). Lenient on unknown field types. */
|
|
464
462
|
export declare function validateFields(schema: FieldDefinition[] | undefined | null, values: unknown, path?: string, opts?: ValidateOpts): void;
|
|
465
463
|
export interface RenderedBlock {
|
|
466
464
|
/** The placement id (cms_page_blocks) — stable per position; used for reorder/remove. */
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,8 @@ function tsTypeOf(f) {
|
|
|
52
52
|
case "textarea":
|
|
53
53
|
case "url":
|
|
54
54
|
case "select":
|
|
55
|
+
case "date":
|
|
56
|
+
case "datetime":
|
|
55
57
|
return "string";
|
|
56
58
|
case "richtext":
|
|
57
59
|
return "RichText";
|
|
@@ -203,6 +205,14 @@ export const cmsSchema = {
|
|
|
203
205
|
};
|
|
204
206
|
/** Validate a block/page's `fields` payload against a field schema, throwing a 400 on
|
|
205
207
|
* the first violation. Recursive (repeater/group). Lenient on unknown field types. */
|
|
208
|
+
/** A calendar date, `YYYY-MM-DD` (what an <input type="date"> emits) — must also parse. */
|
|
209
|
+
function isDateString(v) {
|
|
210
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(v) && Number.isFinite(Date.parse(v));
|
|
211
|
+
}
|
|
212
|
+
/** A date-time: `YYYY-MM-DDTHH:MM[:SS[.sss]][Z|±HH:MM]` (ISO 8601 / datetime-local). */
|
|
213
|
+
function isDateTimeString(v) {
|
|
214
|
+
return /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2})?(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/.test(v) && Number.isFinite(Date.parse(v));
|
|
215
|
+
}
|
|
206
216
|
export function validateFields(schema, values, path = "", opts = {}) {
|
|
207
217
|
const requireRequired = opts.requireRequired !== false;
|
|
208
218
|
const defs = Array.isArray(schema) ? schema : [];
|
|
@@ -241,6 +251,14 @@ export function validateFields(schema, values, path = "", opts = {}) {
|
|
|
241
251
|
if (typeof v !== "boolean")
|
|
242
252
|
throw new BadRequest(`field '${at}' must be a boolean`);
|
|
243
253
|
break;
|
|
254
|
+
case "date":
|
|
255
|
+
if (typeof v !== "string" || !isDateString(v))
|
|
256
|
+
throw new BadRequest(`field '${at}' must be a date (YYYY-MM-DD)`);
|
|
257
|
+
break;
|
|
258
|
+
case "datetime":
|
|
259
|
+
if (typeof v !== "string" || !isDateTimeString(v))
|
|
260
|
+
throw new BadRequest(`field '${at}' must be a date-time (ISO 8601)`);
|
|
261
|
+
break;
|
|
244
262
|
case "media":
|
|
245
263
|
// Media ids are uuids (strings) — reject numbers so the value always resolves
|
|
246
264
|
// (collectMediaIds/resolveMediaFields only handle string ids).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pramen/cms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
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.16"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"react": ">=18"
|
package/src/index.ts
CHANGED
|
@@ -58,6 +58,8 @@ export interface FieldDefinition {
|
|
|
58
58
|
| "url"
|
|
59
59
|
| "number"
|
|
60
60
|
| "boolean"
|
|
61
|
+
| "date"
|
|
62
|
+
| "datetime"
|
|
61
63
|
| "media"
|
|
62
64
|
| "select"
|
|
63
65
|
| "repeater"
|
|
@@ -101,7 +103,7 @@ export type RichText = string | { type: string; content?: unknown[] };
|
|
|
101
103
|
|
|
102
104
|
/** Map one FieldDefinition (as a const literal) to the TS type of its RENDERED value.
|
|
103
105
|
* Media resolves to `ResolvedMedia` (the assemble-time shape a component receives). */
|
|
104
|
-
export type FieldTsType<D extends FieldDefinition> = D["type"] extends "text" | "textarea" | "url" | "select"
|
|
106
|
+
export type FieldTsType<D extends FieldDefinition> = D["type"] extends "text" | "textarea" | "url" | "select" | "date" | "datetime"
|
|
105
107
|
? string
|
|
106
108
|
: D["type"] extends "richtext"
|
|
107
109
|
? RichText
|
|
@@ -171,6 +173,8 @@ function tsTypeOf(f: FieldDefinition): string {
|
|
|
171
173
|
case "textarea":
|
|
172
174
|
case "url":
|
|
173
175
|
case "select":
|
|
176
|
+
case "date":
|
|
177
|
+
case "datetime":
|
|
174
178
|
return "string";
|
|
175
179
|
case "richtext":
|
|
176
180
|
return "RichText";
|
|
@@ -356,6 +360,15 @@ export interface ValidateOpts {
|
|
|
356
360
|
|
|
357
361
|
/** Validate a block/page's `fields` payload against a field schema, throwing a 400 on
|
|
358
362
|
* the first violation. Recursive (repeater/group). Lenient on unknown field types. */
|
|
363
|
+
/** A calendar date, `YYYY-MM-DD` (what an <input type="date"> emits) — must also parse. */
|
|
364
|
+
function isDateString(v: string): boolean {
|
|
365
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(v) && Number.isFinite(Date.parse(v));
|
|
366
|
+
}
|
|
367
|
+
/** A date-time: `YYYY-MM-DDTHH:MM[:SS[.sss]][Z|±HH:MM]` (ISO 8601 / datetime-local). */
|
|
368
|
+
function isDateTimeString(v: string): boolean {
|
|
369
|
+
return /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2})?(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/.test(v) && Number.isFinite(Date.parse(v));
|
|
370
|
+
}
|
|
371
|
+
|
|
359
372
|
export function validateFields(schema: FieldDefinition[] | undefined | null, values: unknown, path = "", opts: ValidateOpts = {}): void {
|
|
360
373
|
const requireRequired = opts.requireRequired !== false;
|
|
361
374
|
const defs = Array.isArray(schema) ? schema : [];
|
|
@@ -388,6 +401,12 @@ export function validateFields(schema: FieldDefinition[] | undefined | null, val
|
|
|
388
401
|
case "boolean":
|
|
389
402
|
if (typeof v !== "boolean") throw new BadRequest(`field '${at}' must be a boolean`);
|
|
390
403
|
break;
|
|
404
|
+
case "date":
|
|
405
|
+
if (typeof v !== "string" || !isDateString(v)) throw new BadRequest(`field '${at}' must be a date (YYYY-MM-DD)`);
|
|
406
|
+
break;
|
|
407
|
+
case "datetime":
|
|
408
|
+
if (typeof v !== "string" || !isDateTimeString(v)) throw new BadRequest(`field '${at}' must be a date-time (ISO 8601)`);
|
|
409
|
+
break;
|
|
391
410
|
case "media":
|
|
392
411
|
// Media ids are uuids (strings) — reject numbers so the value always resolves
|
|
393
412
|
// (collectMediaIds/resolveMediaFields only handle string ids).
|