@prismicio/types-internal 2.2.0-traverse.alpha-5 → 2.2.0-traverse.alpha-7

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.
Files changed (64) hide show
  1. package/lib/_internal/utils.d.ts +3 -3
  2. package/lib/content/Document.d.ts +1248 -32
  3. package/lib/content/Document.js +1 -1
  4. package/lib/content/fields/GroupContent.d.ts +273 -7
  5. package/lib/content/fields/WidgetContent.d.ts +1248 -32
  6. package/lib/content/fields/nestable/NestableContent.d.ts +156 -4
  7. package/lib/content/fields/nestable/RichTextContent/Blocks.d.ts +582 -28
  8. package/lib/content/fields/nestable/RichTextContent/Blocks.js +29 -46
  9. package/lib/content/fields/nestable/RichTextContent/index.d.ts +195 -5
  10. package/lib/content/fields/slices/Slice/CompositeSliceContent.d.ts +314 -10
  11. package/lib/content/fields/slices/Slice/CompositeSliceContent.js +3 -5
  12. package/lib/content/fields/slices/Slice/RepeatableContent.d.ts +117 -3
  13. package/lib/content/fields/slices/Slice/SharedSliceContent.d.ts +312 -8
  14. package/lib/content/fields/slices/Slice/SimpleSliceContent.d.ts +312 -8
  15. package/lib/content/fields/slices/Slice/index.d.ts +702 -18
  16. package/lib/content/fields/slices/SliceItem.d.ts +702 -18
  17. package/lib/content/fields/slices/SlicesContent.d.ts +940 -25
  18. package/lib/content/fields/slices/SlicesContent.js +3 -5
  19. package/lib/customtypes/widgets/slices/CompositeSlice.d.ts +0 -5
  20. package/lib/import/converters/fields/nestable/GeoPoint.d.ts +3 -0
  21. package/lib/import/converters/fields/nestable/GeoPoint.js +15 -0
  22. package/lib/import/converters/fields/nestable/Image.d.ts +25 -3
  23. package/lib/import/converters/fields/nestable/Image.js +21 -25
  24. package/lib/import/converters/fields/nestable/RichText/embedBlock.d.ts +4 -0
  25. package/lib/import/converters/fields/nestable/RichText/embedBlock.js +14 -0
  26. package/lib/import/converters/fields/nestable/RichText/imageBlock.d.ts +4 -0
  27. package/lib/import/converters/fields/nestable/RichText/imageBlock.js +12 -0
  28. package/lib/import/converters/fields/nestable/RichText/index.d.ts +4 -0
  29. package/lib/import/converters/fields/nestable/RichText/index.js +22 -0
  30. package/lib/import/converters/fields/nestable/RichText/textBlock.d.ts +4 -0
  31. package/lib/import/converters/fields/nestable/RichText/textBlock.js +41 -0
  32. package/lib/import/converters/fields/nestable/index.d.ts +1 -1
  33. package/lib/import/converters/fields/nestable/index.js +1 -1
  34. package/lib/import/validators/fields/ImportContent.js +3 -3
  35. package/lib/import/validators/fields/ImportField.d.ts +1 -1
  36. package/lib/import/validators/fields/ImportSlices/ImportSlices.js +4 -3
  37. package/lib/import/validators/fields/ImportSlices/SharedSlice/fields/SharedSliceContent/SharedSliceContentEntry.js +1 -1
  38. package/lib/import/validators/fields/nestable/ImportRichText/ImportRichText.d.ts +1 -1
  39. package/lib/import/validators/fields/nestable/ImportRichText/ImportRichText.js +20 -14
  40. package/lib/import/validators/fields/nestable/ImportRichText/blocks/ImportImageBlock.d.ts +16 -2
  41. package/lib/import/validators/fields/nestable/ImportRichText/blocks/ImportImageBlock.js +7 -5
  42. package/lib/import/validators/fields/nestable/Nestable.d.ts +1 -1
  43. package/lib/validators/DefaultOrElse.js +3 -3
  44. package/package.json +1 -1
  45. package/src/_internal/utils.ts +3 -3
  46. package/src/content/Document.ts +1 -1
  47. package/src/content/fields/nestable/RichTextContent/Blocks.ts +48 -58
  48. package/src/content/fields/slices/Slice/CompositeSliceContent.ts +4 -9
  49. package/src/content/fields/slices/SlicesContent.ts +11 -10
  50. package/src/customtypes/widgets/slices/CompositeSlice.ts +0 -6
  51. package/src/import/converters/fields/nestable/Image.ts +10 -22
  52. package/src/import/converters/fields/nestable/RichText/embedBlock.ts +17 -0
  53. package/src/import/converters/fields/nestable/RichText/imageBlock.ts +17 -0
  54. package/src/import/converters/fields/nestable/RichText/index.ts +23 -0
  55. package/src/import/converters/fields/nestable/RichText/textBlock.ts +48 -0
  56. package/src/import/converters/fields/nestable/index.ts +1 -1
  57. package/src/import/validators/fields/ImportContent.ts +3 -3
  58. package/src/import/validators/fields/ImportSlices/ImportSlices.ts +7 -5
  59. package/src/import/validators/fields/ImportSlices/SharedSlice/fields/SharedSliceContent/SharedSliceContentEntry.ts +1 -1
  60. package/src/import/validators/fields/nestable/ImportRichText/ImportRichText.ts +38 -25
  61. package/src/import/validators/fields/nestable/ImportRichText/blocks/ImportImageBlock.ts +9 -5
  62. package/src/validators/DefaultOrElse.ts +4 -4
  63. package/src/import/converters/fields/nestable/RichText.ts +0 -62
  64. /package/src/import/converters/fields/nestable/{GeooPoint.ts → GeoPoint.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { either } from "fp-ts"
2
- import { isLeft, isRight } from "fp-ts/lib/Either"
2
+ import { isRight } from "fp-ts/lib/Either"
3
3
  import { pipe } from "fp-ts/lib/function"
4
4
  import * as t from "io-ts"
5
5
 
@@ -18,66 +18,54 @@ import {
18
18
  import { ImageContentView } from "../ImageContent"
19
19
  import { Link, LinkLegacy } from "../LinkContent"
20
20
 
21
- export const Meta = t.exact(
22
- t.intersection([
23
- t.partial({
24
- data: t.unknown,
25
- }),
26
- t.type({
27
- start: t.number,
28
- end: t.number,
29
- type: t.union([RichTextNodeTypeCodec, t.literal("label")]),
30
- }),
31
- ]),
32
- )
33
- export type Meta = t.TypeOf<typeof Meta>
34
-
35
- // export alias for code clarity
36
- export const Span = Meta
37
- export type Span = Meta
38
-
39
- export const ValidatedMetas = (linkCodec: LinkCodec) =>
40
- new t.Type<Meta[], Meta[], unknown>(
41
- "ValidatedMetas",
42
- (metas): metas is Meta[] => Array.isArray(metas) && metas.every(Meta.is),
43
- (metas: unknown, c) => {
44
- if (Array.isArray(metas)) {
45
- const res = metas
46
- .reduce<Array<Meta>>((acc, maybeMeta) => {
47
- const maybeDecodedMeta = Meta.decode(maybeMeta)
48
- if (isRight(maybeDecodedMeta)) {
49
- const meta = maybeDecodedMeta.right
50
- // if it's an hyperlink it should have data validated as link otherwise we don't care and keep the meta
51
- if (meta.data && meta.type === "hyperlink") {
52
- const decoded = linkCodec.decode(meta.data)
53
- if (isLeft(decoded)) return acc
54
-
55
- return [...acc, { ...meta, data: decoded.right }]
56
- } else if (meta.type !== "hyperlink") {
57
- return [...acc, meta]
58
- }
59
- }
60
- return acc
21
+ const SpanFn = (linkCodec: typeof Link | typeof LinkLegacy) =>
22
+ t.exact(
23
+ t.intersection([
24
+ t.partial({
25
+ data: linkCodec,
26
+ }),
27
+ t.type({
28
+ start: t.number,
29
+ end: t.number,
30
+ type: t.union([RichTextNodeTypeCodec, t.literal("label")]),
31
+ }),
32
+ ]),
33
+ )
34
+
35
+ export const Span = SpanFn(Link)
36
+ export type Span = t.TypeOf<typeof Span>
37
+
38
+ export const SpanLegacy = SpanFn(LinkLegacy)
39
+ export type SpanLegacy = t.TypeOf<typeof SpanLegacy>
40
+
41
+ export const ValidatedSpans = <C extends typeof Span | typeof SpanLegacy>(
42
+ spanCodec: C,
43
+ ) => {
44
+ type S = t.TypeOf<C>
45
+
46
+ return new t.Type<S[], S[], unknown>(
47
+ "ValidatedSpans",
48
+ (spans): spans is S[] => Array.isArray(spans) && spans.every(spanCodec.is),
49
+ (spans: unknown, c) => {
50
+ if (Array.isArray(spans)) {
51
+ const res = spans
52
+ .reduce<Array<S>>((acc, maybeSpan) => {
53
+ const decodedSpan = spanCodec.decode(maybeSpan)
54
+ return isRight(decodedSpan) ? [...acc, decodedSpan.right] : acc
61
55
  }, [])
62
- .sort((m1: Meta, m2: Meta) => m1.start - m2.start)
56
+ .sort((m1: S, m2: S) => m1.start - m2.start)
63
57
  return t.success(res)
64
- } else return t.failure(metas, c)
58
+ } else return t.failure(spans, c)
65
59
  },
66
60
  (m) => {
67
- return m.reduce<Array<Meta>>((acc, meta) => {
68
- if (meta.type === "hyperlink" && meta.data) {
69
- // since the Span data are typed as unknown we have to redecode for type safety and then encode to either new format or legacy.
70
- const decoded = linkCodec.decode(meta.data)
71
- if (isLeft(decoded)) return acc
72
-
73
- return [...acc, { ...meta, data: linkCodec.encode(decoded.right) }]
74
- }
75
- return [...acc, Meta.encode(meta)]
61
+ return m.reduce<Array<S>>((acc, meta) => {
62
+ return [...acc, spanCodec.encode(meta)]
76
63
  }, [])
77
64
  },
78
65
  )
66
+ }
79
67
 
80
- const ImageBlockCodec = (linkCodec: LinkCodec) =>
68
+ const ImageBlockCodec = (linkCodec: typeof Link | typeof LinkLegacy) =>
81
69
  t.exact(
82
70
  t.intersection([
83
71
  t.type({
@@ -169,8 +157,9 @@ export function checkEmbedBlock(block: Block): block is EmbedBlock {
169
157
  return block.type === RichTextNodeType.embed
170
158
  }
171
159
 
172
- type LinkCodec = typeof Link | typeof LinkLegacy
173
- const TextBlockCodec = (linkCodec: LinkCodec) =>
160
+ const TextBlockCodec = <C extends typeof Span | typeof SpanLegacy>(
161
+ spanCodec: C,
162
+ ) =>
174
163
  t.exact(
175
164
  t.intersection([
176
165
  t.type({
@@ -184,7 +173,7 @@ const TextBlockCodec = (linkCodec: LinkCodec) =>
184
173
  text: t.string,
185
174
  }),
186
175
  t.partial({
187
- spans: ValidatedMetas(linkCodec),
176
+ spans: ValidatedSpans(spanCodec),
188
177
  }),
189
178
  ]),
190
179
  }),
@@ -195,10 +184,11 @@ const TextBlockCodec = (linkCodec: LinkCodec) =>
195
184
  ]),
196
185
  )
197
186
 
198
- export const TextBlock = TextBlockCodec(Link)
187
+ /* These Text block will decode codec A and encode from codec B to A */
188
+ export const TextBlock = TextBlockCodec(Span)
199
189
  export type TextBlock = t.TypeOf<typeof TextBlock>
200
190
 
201
- const TextBlockLegacy = TextBlockCodec(LinkLegacy)
191
+ const TextBlockLegacy = TextBlockCodec(SpanLegacy)
202
192
  type TextBlockLegacy = t.TypeOf<typeof TextBlockLegacy>
203
193
 
204
194
  const legacyBlockCodec = t.union([
@@ -8,10 +8,7 @@ import type {
8
8
  TraverseSliceContentFn,
9
9
  TraverseWidgetContentFn,
10
10
  } from "../../../../_internal/utils"
11
- import type {
12
- CompositeSliceFields,
13
- VariationFields,
14
- } from "../../../../customtypes"
11
+ import type { CompositeSlice, VariationFields } from "../../../../customtypes"
15
12
  import {
16
13
  getFieldCtx,
17
14
  LegacyContentCtx,
@@ -147,7 +144,7 @@ export function traverseCompositeSliceContent({
147
144
  sliceKey: string
148
145
  sliceName: string
149
146
  content: CompositeSliceItemContent
150
- model?: VariationFields | CompositeSliceFields | undefined
147
+ model?: VariationFields | CompositeSlice | undefined
151
148
  }) {
152
149
  return (
153
150
  transformWidget: TraverseWidgetContentFn,
@@ -159,7 +156,7 @@ export function traverseCompositeSliceContent({
159
156
  const fieldDef = (() => {
160
157
  return model?.type === "SharedSlice"
161
158
  ? model?.fields?.primary?.[fieldKey]
162
- : model?.fields?.repeat?.[fieldKey]
159
+ : model?.["non-repeat"]?.[fieldKey]
163
160
  })()
164
161
 
165
162
  const transformedField = transformWidget({
@@ -183,9 +180,7 @@ export function traverseCompositeSliceContent({
183
180
  const items = traverseGroupItemsContent({
184
181
  path: path.concat([{ key: "repeat", type: "items" }]),
185
182
  model:
186
- model?.type === "SharedSlice"
187
- ? model?.fields.items
188
- : model?.fields.repeat,
183
+ model?.type === "SharedSlice" ? model?.fields.items : model?.repeat,
189
184
  content: content.widget.repeat,
190
185
  })(transformWidget)
191
186
 
@@ -10,7 +10,7 @@ import {
10
10
  } from "../../../_internal/utils"
11
11
  import {
12
12
  type StaticSlices,
13
- CompositeSliceFields,
13
+ CompositeSlice,
14
14
  Group,
15
15
  isCompositeSlice,
16
16
  isLegacySlice,
@@ -146,7 +146,7 @@ function findSliceModel(
146
146
 
147
147
  const legacySliceModel = ():
148
148
  | NestableWidget
149
- | CompositeSliceFields
149
+ | CompositeSlice
150
150
  | Group
151
151
  | undefined => {
152
152
  if (!defaultModel) return
@@ -154,10 +154,8 @@ function findSliceModel(
154
154
  if (isCompositeSlice(defaultModel)) {
155
155
  return {
156
156
  type: "Slice",
157
- fields: {
158
- "non-repeat": defaultModel["non-repeat"] || {},
159
- repeat: defaultModel.repeat || {},
160
- },
157
+ "non-repeat": defaultModel["non-repeat"] || {},
158
+ repeat: defaultModel.repeat || {},
161
159
  }
162
160
  }
163
161
  if (isLegacySlice(defaultModel)) {
@@ -180,10 +178,13 @@ export function traverseSlices({
180
178
  content: SlicesContent
181
179
  model?: StaticSlices | undefined
182
180
  }) {
183
- return (
184
- transformWidget: TraverseWidgetContentFn,
185
- transformSlice: TraverseSliceContentFn,
186
- ): SlicesContent | undefined => {
181
+ return ({
182
+ transformWidget = ({ content }) => content,
183
+ transformSlice = ({ content }) => content,
184
+ }: {
185
+ transformWidget?: TraverseWidgetContentFn
186
+ transformSlice?: TraverseSliceContentFn
187
+ }): SlicesContent | undefined => {
187
188
  const value = content.value.reduce<SlicesContent["value"]>(
188
189
  (acc, sliceContent) => {
189
190
  const sliceModel = model && findSliceModel(path, model, sliceContent)
@@ -1,6 +1,5 @@
1
1
  import * as t from "io-ts"
2
2
 
3
- import type { PickOnly } from "../../../_internal/utils"
4
3
  import { WidgetKey } from "../../../common"
5
4
  import { StringOrNull } from "../../../validators"
6
5
  import { NestableWidget } from "../nestable/NestableWidget"
@@ -33,11 +32,6 @@ export const CompositeSlice = t.exact(
33
32
  )
34
33
  export type CompositeSlice = t.TypeOf<typeof CompositeSlice>
35
34
 
36
- export type CompositeSliceFields = {
37
- type: "Slice"
38
- fields: PickOnly<CompositeSlice, "non-repeat" | "repeat">
39
- }
40
-
41
35
  export function isCompositeSlice(
42
36
  slice: DynamicSlice | StaticSlice,
43
37
  ): slice is CompositeSlice {
@@ -5,22 +5,20 @@ import { withOptionals } from "../../../../utils/Objects"
5
5
  import type { ImageField, ImportImage } from "../../../validators"
6
6
  import type { ImportImageBlock } from "../../../validators/fields/nestable/ImportRichText/blocks"
7
7
 
8
- function convertImage<Input extends ImageField | ImportImageBlock>(
8
+ export function convertImageView<Input extends ImageField | ImportImageBlock>(
9
9
  inputImage: Input,
10
10
  image: Asset,
11
11
  ) {
12
12
  // We can do that because anyway all properties being read
13
13
  const imageField: ImageField = {
14
14
  ...inputImage,
15
- edit:
16
- "edit" in inputImage
17
- ? inputImage.edit
18
- : {
19
- x: 0,
20
- y: 0,
21
- zoom: 1,
22
- },
23
- dimensions: "dimensions" in inputImage ? inputImage.dimensions : {},
15
+ edit: {
16
+ x: 0,
17
+ y: 0,
18
+ zoom: 1,
19
+ ...inputImage.edit,
20
+ },
21
+ dimensions: inputImage.dimensions ?? {},
24
22
  }
25
23
  return withOptionals<ImageContentView>(
26
24
  {
@@ -64,7 +62,7 @@ function convertThumbnails(
64
62
  (acc, [thumbnailName, thumbnail]) => {
65
63
  return {
66
64
  ...acc,
67
- [thumbnailName]: convertImage(
65
+ [thumbnailName]: convertImageView(
68
66
  thumbnail,
69
67
  getAssetOrThrow(assets)(thumbnail.id),
70
68
  ),
@@ -74,16 +72,6 @@ function convertThumbnails(
74
72
  )
75
73
  }
76
74
 
77
- export const imageBlockConverter = (
78
- imageBlock: ImportImageBlock,
79
- assets: Record<Asset["id"], Asset | undefined>,
80
- ): ImageContentView => {
81
- return convertImage<ImportImageBlock>(
82
- imageBlock,
83
- getAssetOrThrow(assets)(imageBlock.id),
84
- )
85
- }
86
-
87
75
  // All assets from `imageField` must be present in `assets`
88
76
  // If not then function will throw an error
89
77
  export const imageConverter = (
@@ -93,7 +81,7 @@ export const imageConverter = (
93
81
  if (!imageField) return
94
82
 
95
83
  return {
96
- ...convertImage<ImageField>(
84
+ ...convertImageView<ImageField>(
97
85
  imageField,
98
86
  getAssetOrThrow(assets)(imageField.id),
99
87
  ),
@@ -0,0 +1,17 @@
1
+ import type { Embed } from "../../../../../common"
2
+ import type { EmbedBlock } from "../../../../../content"
3
+ import type { ImportEmbedBlock } from "../../../../validators/fields/nestable/ImportRichText/blocks"
4
+ import { embedConverter } from "../Embed"
5
+
6
+ export function embedBlockConverter(
7
+ block: ImportEmbedBlock,
8
+ embeds: Record<string, Embed | undefined>,
9
+ ): EmbedBlock {
10
+ const embedData = embedConverter(block.oembed, embeds)
11
+
12
+ if (!embedData) throw new Error("Failed to convert embed data")
13
+ return {
14
+ type: block.type,
15
+ data: embedData,
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ import { Asset, getAssetOrThrow } from "../../../../../common"
2
+ import type { ImageBlock } from "../../../../../content"
3
+ import type { ImportImageBlock } from "../../../../validators/fields/nestable/ImportRichText/blocks"
4
+ import { convertImageView } from "../Image"
5
+
6
+ export function imageBlockConverter(
7
+ block: ImportImageBlock,
8
+ assets: Record<Asset["id"], Asset | undefined>,
9
+ ): ImageBlock {
10
+ return {
11
+ type: block.type,
12
+ data: convertImageView<ImportImageBlock>(
13
+ block,
14
+ getAssetOrThrow(assets)(block.id),
15
+ ),
16
+ }
17
+ }
@@ -0,0 +1,23 @@
1
+ import type { Asset, Embed } from "../../../../../common"
2
+ import type { RichTextContent } from "../../../../../content"
3
+ import { RichTextContentType } from "../../../../../content"
4
+ import type { ImportRichText } from "../../../../validators"
5
+ import { embedBlockConverter } from "./embedBlock"
6
+ import { imageBlockConverter } from "./imageBlock"
7
+ import { textBlockConverter } from "./textBlock"
8
+
9
+ export const richTextConverter = (
10
+ richTextField: ImportRichText["value"],
11
+ assets: Record<Asset["id"], Asset | undefined>,
12
+ embeds: Record<string, Embed | undefined>,
13
+ ): RichTextContent | undefined => {
14
+ if (richTextField === null) return
15
+ return {
16
+ __TYPE__: RichTextContentType,
17
+ value: richTextField.map((block) => {
18
+ if (block.type === "image") return imageBlockConverter(block, assets)
19
+ else if (block.type === "embed") return embedBlockConverter(block, embeds)
20
+ return textBlockConverter(block, assets)
21
+ }),
22
+ }
23
+ }
@@ -0,0 +1,48 @@
1
+ import type { Asset } from "../../../../../common"
2
+ import type { TextBlock } from "../../../../../content"
3
+ import type { ImportTextBlock } from "../../../../validators/fields/nestable/ImportRichText/blocks"
4
+ import { linkConverter } from "../Link"
5
+
6
+ export function textBlockConverter(
7
+ block: ImportTextBlock,
8
+ assets: Record<Asset["id"], Asset | undefined>,
9
+ ): TextBlock {
10
+ return {
11
+ type: block.type,
12
+ content: {
13
+ text: block.text,
14
+ ...(() => {
15
+ if (block.spans) {
16
+ const convertedSpans = spansConverter(block.spans, assets)
17
+ if (!convertedSpans) return {}
18
+
19
+ return { spans: convertedSpans }
20
+ }
21
+ return {}
22
+ })(),
23
+ },
24
+ ...(block.direction ? { direction: block.direction } : {}),
25
+ }
26
+ }
27
+
28
+ function spansConverter(
29
+ spans: NonNullable<ImportTextBlock["spans"]>,
30
+ assets: Record<Asset["id"], Asset | undefined>,
31
+ ): TextBlock["content"]["spans"] {
32
+ return spans.map((span) => {
33
+ return {
34
+ type: span.type,
35
+ start: span.start,
36
+ end: span.end,
37
+ ...(() => {
38
+ if (span.type === "hyperlink") {
39
+ const convertedLinkContent = linkConverter(span.data, assets)
40
+ if (!convertedLinkContent) return {}
41
+
42
+ return { data: convertedLinkContent?.value }
43
+ }
44
+ return {}
45
+ })(),
46
+ }
47
+ })
48
+ }
@@ -2,7 +2,7 @@ export * from "./Boolean"
2
2
  export * from "./Color"
3
3
  export * from "./Date"
4
4
  export * from "./Embed"
5
- export * from "./GeooPoint"
5
+ export * from "./GeoPoint"
6
6
  export * from "./Image"
7
7
  export * from "./Link"
8
8
  export * from "./Nestable"
@@ -10,14 +10,14 @@ export const ImportContent = <T extends FieldType, A, O = A>(
10
10
  valueCodec: t.Type<A, O>,
11
11
  ) =>
12
12
  new t.Type<{ type: T; value: A }, O>(
13
- "ImportField",
13
+ `ImportField: ${valueCodec.name}`,
14
14
  (u: unknown): u is { type: T; value: A } => {
15
15
  if (!isObject(u)) return false
16
16
  return type === u["type"] && valueCodec.is(u["value"])
17
17
  },
18
- (u: unknown) => {
18
+ (u: unknown, c) => {
19
19
  return pipe(
20
- valueCodec.decode(u),
20
+ valueCodec.validate(u, c),
21
21
  either.map((decodedValue) => {
22
22
  return {
23
23
  type,
@@ -3,7 +3,7 @@ import { pipe } from "fp-ts/function"
3
3
  import * as t from "io-ts"
4
4
 
5
5
  import type { StaticSlices } from "../../../../customtypes"
6
- import { EmptyObjectOrElse } from "../../../../validators"
6
+ import { EmptyArrayOrElse } from "../../../../validators"
7
7
  import { ImportContent } from "../ImportContent"
8
8
  import { SharedSlice } from "./SharedSlice"
9
9
  import {
@@ -16,13 +16,14 @@ export const ImportSlices = (staticSlices: StaticSlices) => {
16
16
 
17
17
  // For now we only support the SharedSlice, however if we want to support more in the future
18
18
  // we would have to change the codec here to something like this: t.array(t.union([ImportSharedSlice(sharedSlices), NewSliceCodec(newSliceCustomTypes)])).
19
- const SlicesArrayCodec = t.array(SharedSlice(supportedSlices))
19
+ const SharedSliceCodec = SharedSlice(supportedSlices)
20
+ const SlicesArrayCodec = t.array(SharedSliceCodec)
20
21
 
21
22
  return ImportContent(
22
23
  "Slices",
23
- EmptyObjectOrElse(
24
- new t.Type<SharedSlice[]>(
25
- "ImportSlices",
24
+ EmptyArrayOrElse(
25
+ new t.ArrayType<typeof SharedSliceCodec, SharedSlice[]>(
26
+ SlicesArrayCodec.name,
26
27
  (u): u is SharedSlice[] => SlicesArrayCodec.is(u),
27
28
  (u, c) => {
28
29
  return pipe(
@@ -46,6 +47,7 @@ export const ImportSlices = (staticSlices: StaticSlices) => {
46
47
  )
47
48
  },
48
49
  t.identity,
50
+ SharedSliceCodec,
49
51
  ),
50
52
  ),
51
53
  )
@@ -83,8 +83,8 @@ export const SharedSliceContentEntry = (
83
83
  errors.map((error) => {
84
84
  const context = [
85
85
  ...c,
86
- ...error.context.filter(({ key }) => !!key), // We replace the "" key coming from ImportNestable decoder
87
86
  { key, actual: content, type: codec },
87
+ ...error.context.slice(1), // We ignore the first context element with "" key coming from ImportNestable decoder
88
88
  ]
89
89
 
90
90
  const updatedError: t.ValidationError = { ...error, context }
@@ -1,3 +1,5 @@
1
+ import * as E from "fp-ts/Either"
2
+ import { pipe } from "fp-ts/function"
1
3
  import * as t from "io-ts"
2
4
 
3
5
  import type { RichText as RichTextCustomType } from "../../../../../customtypes"
@@ -6,36 +8,47 @@ import { withCustomError } from "../../../../../validators/function"
6
8
  import { ImportContent } from "../../ImportContent"
7
9
  import { ImportBlock } from "./ImportBlock"
8
10
 
9
- export const ImportRichText = (customType?: RichTextCustomType) =>
10
- ImportContent(
11
+ export const ImportRichText = (customType?: RichTextCustomType) => {
12
+ const single = customType?.config?.single?.split(",")
13
+ const multi = customType?.config?.multi?.split(",")
14
+ const allowedBlockTypes = multi || single
15
+
16
+ const isSingleBlockAllowed = multi === undefined && single !== undefined
17
+
18
+ const BlockCodec = ImportBlock(allowedBlockTypes ?? [])
19
+ const RichTextArrayCodec = withCustomError(
20
+ t.array(BlockCodec),
21
+ () => "Rich text field must be an array",
22
+ )
23
+
24
+ return ImportContent(
11
25
  "StructuredText",
12
26
  EmptyArrayOrElse(
13
- withCustomError(
14
- t.array(t.unknown).pipe(
15
- new t.Type<ImportBlock[], ImportBlock[], unknown[]>(
16
- "RichTextField",
17
- (u): u is ImportBlock[] => t.array(ImportBlock([])).is(u),
18
- (u, c) => {
19
- const single = customType?.config?.single?.split(",")
20
- const multi = customType?.config?.multi?.split(",")
27
+ new t.ArrayType<typeof BlockCodec, ImportBlock[], ImportBlock[]>(
28
+ RichTextArrayCodec.name,
29
+ (u): u is ImportBlock[] => RichTextArrayCodec.is(u),
30
+ (u, c) => {
31
+ if (allowedBlockTypes === undefined) {
32
+ throw new Error(
33
+ "Invalid custom type for the rich text field: the model is missing a 'single' or 'multi' field defining a list of allowed block types",
34
+ )
35
+ }
21
36
 
22
- if (multi) {
23
- return t.array(ImportBlock(multi)).validate(u, c)
24
- } else if (single) {
25
- return u.length > 1
26
- ? t.failure(u, c, "This field only allows one block")
27
- : t.array(ImportBlock(single)).validate(u, c)
28
- } else {
29
- throw new Error(
30
- "Rich text config must have either a 'single' or 'multi' field defined",
31
- )
37
+ return pipe(
38
+ RichTextArrayCodec.validate(u, c),
39
+ E.chain((blocks) => {
40
+ if (isSingleBlockAllowed && blocks.length > 1) {
41
+ return t.failure(blocks, c, "This field only allows one block")
32
42
  }
33
- },
34
- t.identity,
35
- ),
36
- ),
37
- () => "Rich text field must be an array",
43
+ return t.success(blocks)
44
+ }),
45
+ )
46
+ },
47
+ t.identity,
48
+ BlockCodec,
38
49
  ),
39
50
  ),
40
51
  )
52
+ }
53
+
41
54
  export type ImportRichText = t.TypeOf<ReturnType<typeof ImportRichText>>
@@ -1,13 +1,17 @@
1
1
  import * as t from "io-ts"
2
2
 
3
- import { String } from "../../../../../../validators"
3
+ import { ImportImageView } from "../../Image/validators"
4
4
 
5
5
  export const ImportImageBlockType = t.literal("image")
6
6
  export type ImportImageBlockType = t.TypeOf<typeof ImportImageBlockType>
7
7
 
8
- export const ImportImageBlock = t.strict({
9
- type: ImportImageBlockType,
10
- id: String,
11
- })
8
+ export const ImportImageBlock = t.exact(
9
+ t.intersection([
10
+ t.type({
11
+ type: ImportImageBlockType,
12
+ }),
13
+ ImportImageView,
14
+ ]),
15
+ )
12
16
 
13
17
  export type ImportImageBlock = t.TypeOf<typeof ImportImageBlock>
@@ -6,11 +6,11 @@ import { EmptyArray, EmptyObject } from "./BasicTypes"
6
6
  export const DefaultOrElse = <I, A, O = A>(inputValidator: t.Type<I>) => {
7
7
  return (codec: t.Type<A, O>) =>
8
8
  new t.Type<A | null, O | undefined>(
9
- "DefaultOrElse",
9
+ `DefaultOrElse<${inputValidator.name}, ${codec.name}>`,
10
10
  (u: unknown): u is A | null => null === u || codec.is(u),
11
- (u: unknown) => {
12
- if (isRight(inputValidator.decode(u))) return t.success(null)
13
- return codec.decode(u)
11
+ (u: unknown, c) => {
12
+ if (isRight(inputValidator.validate(u, c))) return t.success(null)
13
+ return codec.validate(u, c)
14
14
  },
15
15
  (chunk) => (chunk ? codec.encode(chunk) : undefined),
16
16
  )