@supernova-studio/client 0.9.0 → 0.9.2

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