@supernova-studio/client 0.9.1 → 0.9.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supernova-studio/client",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Supernova Data Models",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -37,15 +37,15 @@ import {
37
37
  PageBlockItemTableNode,
38
38
  PageBlockItemTableCell,
39
39
  PageBlockTableCellAlignment,
40
- DocumentationItemConfiguration,
41
40
  PageBlockAppearanceV2,
42
- PageBlockEditorModelV2,
43
41
  ColorValue,
42
+ nullishToOptional,
44
43
  } from "@supernova-studio/model";
45
44
  import { PageBlockEditorModel } from "./model/block";
46
45
  import { DocumentationPageEditorModel } from "./model/page";
47
46
  import { BlockDefinitionUtils } from "./utils";
48
47
  import { yXmlFragmentToProsemirrorJSON } from "y-prosemirror";
48
+ import { ZodAnyDef, ZodSchema, ZodTypeDef, z } from "zod";
49
49
 
50
50
  export function yDocToPage(yDoc: Y.Doc, definitions: PageBlockDefinition[]): DocumentationPageEditorModel {
51
51
  return yXmlFragmentToPage(yDoc.getXmlFragment("default"), definitions);
@@ -68,13 +68,23 @@ export function prosemirrorDocToPage(
68
68
  return {
69
69
  blocks: (prosemirrorDoc.content ?? [])
70
70
  .map(prosemirrorNode => {
71
- const definitionId = parseProsemirrorBlockAttribute(prosemirrorNode, "definitionId");
72
- if (!definitionId) throw new Error(`Node is missing defintion id`);
73
- if (typeof definitionId !== "string")
74
- throw new Error(`Definition id is ${typeof definitionId}, has to be string`);
71
+ const definitionId = getProsemirrorAttribute(prosemirrorNode, "definitionId", z.string());
72
+ if (!definitionId) {
73
+ console.warn(
74
+ `definitionId on ${prosemirrorNode.type} is required to be interpreted as a block, skipping node`
75
+ );
76
+
77
+ return null;
78
+ }
75
79
 
76
80
  const definition = definitionsById.get(definitionId);
77
- if (!definition) throw new Error(`Definition by id ${definitionId} doesn't exist`);
81
+ if (!definition) {
82
+ console.warn(
83
+ `Block definitionId "${definitionId}" (prosemirror node ${prosemirrorNode.type}) is not among available definitions`
84
+ );
85
+ console.warn(prosemirrorNode);
86
+ return null;
87
+ }
78
88
 
79
89
  return prosemirrorNodeToBlock(prosemirrorNode, definition);
80
90
  })
@@ -122,13 +132,11 @@ function parseAsRichText(
122
132
  definition: PageBlockDefinition,
123
133
  property: PageBlockDefinitionProperty
124
134
  ): PageBlockEditorModel | null {
125
- const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
126
- if (typeof id !== "string") return null;
127
-
128
- const variantIdRaw = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
129
- const variantId = typeof variantIdRaw === "string" ? variantIdRaw : undefined;
135
+ const id = getProsemirrorBlockId(prosemirrorNode);
136
+ if (!id) return null;
130
137
 
131
- const calloutType = parseCalloutType(parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "type"));
138
+ const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
139
+ const calloutType = parseCalloutType(getProsemirrorAttribute(prosemirrorNode, "type", z.string().optional()));
132
140
 
133
141
  return {
134
142
  // TODO Artem: indent
@@ -181,11 +189,10 @@ function parseAsMultiRichText(
181
189
  definition: PageBlockDefinition,
182
190
  property: PageBlockDefinitionProperty
183
191
  ): PageBlockEditorModel | null {
184
- const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
185
- if (typeof id !== "string") return null;
192
+ const id = getProsemirrorBlockId(prosemirrorNode);
193
+ if (!id) return null;
186
194
 
187
- const variantIdRaw = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
188
- const variantId = typeof variantIdRaw === "string" ? variantIdRaw : undefined;
195
+ const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
189
196
 
190
197
  return {
191
198
  // TODO Artem: indent
@@ -253,11 +260,8 @@ function parseRichTextAttribute(mark: ProsemirrorMark): PageBlockTextSpanAttribu
253
260
  case "code":
254
261
  return { type: "Code" };
255
262
  case "link":
256
- const itemIdRaw = parseProsemirrorOptionalBlockAttribute(mark, "itemId");
257
- const itemId = typeof itemIdRaw === "string" ? itemIdRaw : undefined;
258
-
259
- const hrefRaw = parseProsemirrorOptionalBlockAttribute(mark, "href");
260
- const href = typeof hrefRaw === "string" ? hrefRaw : undefined;
263
+ const itemId = getProsemirrorAttribute(mark, "itemId", z.string().optional());
264
+ const href = getProsemirrorAttribute(mark, "href", z.string().optional());
261
265
 
262
266
  return {
263
267
  type: "Link",
@@ -279,13 +283,11 @@ function parseAsTable(
279
283
  definition: PageBlockDefinition,
280
284
  property: PageBlockDefinitionProperty
281
285
  ): PageBlockEditorModel | null {
282
- const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
283
- if (typeof id !== "string") return null;
284
-
285
- const variantIdRaw = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
286
- const variantId = typeof variantIdRaw === "string" ? variantIdRaw : undefined;
286
+ const id = getProsemirrorBlockId(prosemirrorNode);
287
+ if (!id) return null;
287
288
 
288
- const hasBorder = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "hasBorder") !== false;
289
+ const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
290
+ const hasBorder = getProsemirrorAttribute(prosemirrorNode, "hasBorder", z.boolean().optional()) !== false;
289
291
 
290
292
  const tableChild = prosemirrorNode.content?.find(c => c.type === "table");
291
293
  if (!tableChild) {
@@ -338,14 +340,14 @@ function parseAsTable(
338
340
  }
339
341
 
340
342
  function parseAsTableCell(prosemirrorNode: ProsemirrorNode): PageBlockItemTableCell | null {
341
- const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
342
- if (typeof id !== "string") return null;
343
+ const id = getProsemirrorBlockId(prosemirrorNode);
344
+ if (!id) return null;
343
345
 
344
- const textAlign = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "textAlign");
346
+ const textAlign = getProsemirrorAttribute(prosemirrorNode, "textAlign", z.string().optional());
345
347
 
346
348
  let columnWidth: number | undefined;
347
- const columnWidthArray = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "colwidth");
348
- if (Array.isArray(columnWidthArray) && typeof columnWidthArray[0] === "number") {
349
+ const columnWidthArray = getProsemirrorAttribute(prosemirrorNode, "colwidth", z.array(z.number()).optional());
350
+ if (columnWidthArray) {
349
351
  columnWidth = columnWidthArray[0];
350
352
  }
351
353
 
@@ -384,8 +386,8 @@ function parseAsTableNode(prosemirrorNode: ProsemirrorNode): PageBlockItemTableN
384
386
  };
385
387
 
386
388
  case "image":
387
- const url = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "src");
388
- if (!url || typeof url !== "string") return null;
389
+ const url = getProsemirrorAttribute(prosemirrorNode, "src", z.string().optional());
390
+ if (!url) return null;
389
391
 
390
392
  return {
391
393
  type: "Image",
@@ -458,15 +460,14 @@ function parseAsEmbed(
458
460
  definition: PageBlockDefinition,
459
461
  property: PageBlockDefinitionProperty
460
462
  ): PageBlockEditorModel | null {
461
- const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
462
- if (typeof id !== "string") return null;
463
+ const id = getProsemirrorBlockId(prosemirrorNode);
464
+ if (!id) return null;
463
465
 
464
- const variantIdRaw = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
465
- const variantId = typeof variantIdRaw === "string" ? variantIdRaw : undefined;
466
+ const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
466
467
 
467
- const url = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "url");
468
- const caption = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "caption");
469
- const height = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "height");
468
+ const url = getProsemirrorAttribute(prosemirrorNode, "url", z.string().optional());
469
+ const caption = getProsemirrorAttribute(prosemirrorNode, "caption", z.string().optional());
470
+ const height = getProsemirrorAttribute(prosemirrorNode, "height", z.number().optional());
470
471
 
471
472
  return {
472
473
  id: id,
@@ -483,7 +484,7 @@ function parseAsEmbed(
483
484
  [property.id]: {
484
485
  value: url,
485
486
  caption: caption,
486
- height: height,
487
+ height: height ?? 200,
487
488
  } as PageBlockItemEmbedValue,
488
489
  },
489
490
  },
@@ -500,11 +501,10 @@ function parseAsDivider(
500
501
  prosemirrorNode: ProsemirrorNode,
501
502
  definition: PageBlockDefinition
502
503
  ): PageBlockEditorModel | null {
503
- const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
504
- if (typeof id !== "string") return null;
504
+ const id = getProsemirrorBlockId(prosemirrorNode);
505
+ if (!id) return null;
505
506
 
506
- const variantIdRaw = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
507
- const variantId = typeof variantIdRaw === "string" ? variantIdRaw : undefined;
507
+ const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
508
508
 
509
509
  return {
510
510
  id: id,
@@ -527,11 +527,10 @@ function parseAsCustomBlock(
527
527
  prosemirrorNode: ProsemirrorNode,
528
528
  definition: PageBlockDefinition
529
529
  ): PageBlockEditorModel | null {
530
- const id = parseProsemirrorBlockAttribute(prosemirrorNode, "id");
531
- if (typeof id !== "string") return null;
530
+ const id = getProsemirrorBlockId(prosemirrorNode);
531
+ if (!id) return null;
532
532
 
533
- const variantIdRaw = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "variantId");
534
- const variantId = typeof variantIdRaw === "string" ? variantIdRaw : undefined;
533
+ const variantId = getProsemirrorBlockVariantId(prosemirrorNode);
535
534
 
536
535
  const appearance = parseAppearance(prosemirrorNode);
537
536
  const parsedItems = parseBlockItems(prosemirrorNode, definition);
@@ -552,8 +551,8 @@ function parseAsCustomBlock(
552
551
  }
553
552
 
554
553
  function parseBlockItems(prosemirrorNode: ProsemirrorNode, definition: PageBlockDefinition): PageBlockItemV2[] | null {
555
- const itemsString = parseProsemirrorBlockAttribute(prosemirrorNode, "items");
556
- if (typeof itemsString !== "string") return null;
554
+ const itemsString = getProsemirrorAttribute(prosemirrorNode, "items", z.string());
555
+ if (!itemsString) return null;
557
556
 
558
557
  const itemsJson: unknown = JSON.parse(itemsString);
559
558
  if (!Array.isArray(itemsJson)) {
@@ -567,13 +566,13 @@ function parseBlockItems(prosemirrorNode: ProsemirrorNode, definition: PageBlock
567
566
  function parseAppearance(prosemirrorNode: ProsemirrorNode): PageBlockAppearanceV2 | undefined {
568
567
  const appearance: PageBlockAppearanceV2 = {};
569
568
 
570
- const columns: unknown = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "columns");
571
- if (typeof columns === "number") {
569
+ const columns = getProsemirrorAttribute(prosemirrorNode, "columns", z.number().optional());
570
+ if (columns) {
572
571
  appearance.numberOfColumns = columns;
573
572
  }
574
573
 
575
- const backgroundColor: unknown = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, "backgroundColor");
576
- if (typeof backgroundColor === "string") {
574
+ const backgroundColor = getProsemirrorAttribute(prosemirrorNode, "backgroundColor", z.string().optional());
575
+ if (backgroundColor) {
577
576
  const parsedColor = ColorValue.safeParse(JSON.parse(backgroundColor));
578
577
  if (parsedColor.success) {
579
578
  appearance.itemBackgroundColor = parsedColor.data;
@@ -673,17 +672,29 @@ function valueSchemaForPropertyType(type: PageBlockDefinitionPropertyType) {
673
672
  // Attributes
674
673
  //
675
674
 
676
- function parseProsemirrorBlockAttribute(prosemirrorNode: ProsemirrorNode, attributeName: string): unknown {
677
- const attributeValue = parseProsemirrorOptionalBlockAttribute(prosemirrorNode, attributeName);
678
- if (!attributeValue) {
679
- throw new Error(`Attribute ${attributeName} was not defined on node ${prosemirrorNode.type}`);
680
- }
675
+ function getProsemirrorBlockId(prosemirrorNode: ProsemirrorNode): string | undefined {
676
+ const id = getProsemirrorAttribute(prosemirrorNode, "id", z.string());
677
+ if (!id) console.warn(`Prosemirror attribute "id" on ${prosemirrorNode.type} is required`);
678
+ return id;
679
+ }
681
680
 
682
- return attributeValue;
681
+ function getProsemirrorBlockVariantId(prosemirrorNode: ProsemirrorNode): string | undefined {
682
+ return getProsemirrorAttribute(prosemirrorNode, "variantId", nullishToOptional(z.string()));
683
683
  }
684
684
 
685
- function parseProsemirrorOptionalBlockAttribute(prosemirrorNode: ProsemirrorNode, attributeName: string): unknown {
686
- return prosemirrorNode.attrs?.[attributeName] ?? undefined;
685
+ function getProsemirrorAttribute<I, O>(
686
+ prosemirrorNode: ProsemirrorNode,
687
+ attributeName: string,
688
+ validationSchema: ZodSchema<O, ZodTypeDef, I>
689
+ ): O | undefined {
690
+ const parsedAttr = validationSchema.safeParse(prosemirrorNode.attrs?.[attributeName]);
691
+ if (parsedAttr.success) {
692
+ return parsedAttr.data;
693
+ } else {
694
+ console.warn(`Prosemirror attribute ${attributeName} on ${prosemirrorNode.type} is invalid`);
695
+ console.warn(parsedAttr.error.flatten());
696
+ return undefined;
697
+ }
687
698
  }
688
699
 
689
700
  //