@prismicio/types-internal 2.1.0-alpha.2 → 2.1.0-alpha.4

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 (39) hide show
  1. package/lib/content/Document.d.ts +1 -1
  2. package/lib/content/Document.js +1 -6
  3. package/lib/customtypes/CustomType.d.ts +1046 -1180
  4. package/lib/customtypes/CustomType.js +13 -74
  5. package/lib/customtypes/Section.d.ts +1047 -1171
  6. package/lib/customtypes/Section.js +23 -1
  7. package/lib/customtypes/_internal/utils.d.ts +3 -0
  8. package/lib/customtypes/_internal/utils.js +20 -0
  9. package/lib/customtypes/diff/SharedSlice.d.ts +184 -220
  10. package/lib/customtypes/diff/Variation.d.ts +183 -219
  11. package/lib/customtypes/widgets/Group.d.ts +6 -40
  12. package/lib/customtypes/widgets/Group.js +20 -1
  13. package/lib/customtypes/widgets/Widget.d.ts +778 -976
  14. package/lib/customtypes/widgets/nestable/Link/ContentRelationshipResolver.d.ts +4 -22
  15. package/lib/customtypes/widgets/nestable/Link/ContentRelationshipResolver.js +5 -34
  16. package/lib/customtypes/widgets/nestable/Link/index.d.ts +4 -40
  17. package/lib/customtypes/widgets/nestable/Link/index.js +1 -1
  18. package/lib/customtypes/widgets/nestable/NestableWidget.d.ts +2 -20
  19. package/lib/customtypes/widgets/slices/CompositeSlice.d.ts +6 -40
  20. package/lib/customtypes/widgets/slices/CompositeSlice.js +26 -1
  21. package/lib/customtypes/widgets/slices/LegacySlice.d.ts +4 -40
  22. package/lib/customtypes/widgets/slices/SharedSlice.d.ts +10 -80
  23. package/lib/customtypes/widgets/slices/SharedSlice.js +35 -1
  24. package/lib/customtypes/widgets/slices/Slices.d.ts +924 -1246
  25. package/lib/customtypes/widgets/slices/Slices.js +34 -1
  26. package/lib/validators/StringOrT.d.ts +3 -0
  27. package/lib/validators/StringOrT.js +15 -0
  28. package/package.json +1 -1
  29. package/src/content/Document.ts +2 -6
  30. package/src/customtypes/CustomType.ts +24 -97
  31. package/src/customtypes/Section.ts +28 -1
  32. package/src/customtypes/_internal/utils.ts +25 -0
  33. package/src/customtypes/widgets/Group.ts +24 -0
  34. package/src/customtypes/widgets/nestable/Link/ContentRelationshipResolver.ts +5 -54
  35. package/src/customtypes/widgets/nestable/Link/index.ts +2 -2
  36. package/src/customtypes/widgets/slices/CompositeSlice.ts +33 -0
  37. package/src/customtypes/widgets/slices/SharedSlice.ts +47 -0
  38. package/src/customtypes/widgets/slices/Slices.ts +44 -1
  39. package/src/validators/StringOrT.ts +21 -0
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Slices = exports.DynamicSlices = exports.StaticSlices = exports.slicesReader = exports.DynamicSlicesConfig = exports.StaticSlicesConfig = exports.slicesConfigReader = exports.SlicesLabels = exports.SlicesFieldType = exports.LegacySlicesFieldType = void 0;
3
+ exports.traverseSlices = exports.Slices = exports.DynamicSlices = exports.StaticSlices = exports.slicesReader = exports.DynamicSlicesConfig = exports.StaticSlicesConfig = exports.slicesConfigReader = exports.SlicesLabels = exports.SlicesFieldType = exports.LegacySlicesFieldType = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const t = (0, tslib_1.__importStar)(require("io-ts"));
6
6
  const common_1 = require("../../../common");
7
7
  const validators_1 = require("../../../validators");
8
+ const utils_1 = require("../../_internal/utils");
9
+ const Group_1 = require("../Group");
8
10
  const CompositeSlice_1 = require("./CompositeSlice");
9
11
  const LegacySlice_1 = require("./LegacySlice");
10
12
  const SharedSlice_1 = require("./SharedSlice");
@@ -78,3 +80,34 @@ exports.Slices = {
78
80
  }
79
81
  },
80
82
  };
83
+ function traverseSlices(path, slices, f) {
84
+ var _a, _b, _c, _d;
85
+ const entries = ((_a = slices.config) === null || _a === void 0 ? void 0 : _a.choices) &&
86
+ Object.entries(slices.config.choices).map(([sliceKey, sliceValue]) => {
87
+ const slicePath = path.concat(sliceKey);
88
+ switch (sliceValue.type) {
89
+ case "SharedSlice":
90
+ return [sliceKey, f(sliceValue, sliceKey, slicePath)];
91
+ case "Slice":
92
+ return [
93
+ sliceKey,
94
+ f((0, CompositeSlice_1.traverseCompositeSlice)(slicePath, sliceValue, f), sliceKey, slicePath),
95
+ ];
96
+ case "Group":
97
+ return [sliceKey, (0, Group_1.traverseGroup)(slicePath, sliceValue, f)];
98
+ default:
99
+ return [sliceKey, f(sliceValue, sliceKey, slicePath)];
100
+ }
101
+ });
102
+ return {
103
+ ...slices,
104
+ config: {
105
+ ...(((_b = slices.config) === null || _b === void 0 ? void 0 : _b.label) ? { label: slices.config.label } : {}),
106
+ ...(((_c = slices.config) === null || _c === void 0 ? void 0 : _c.labels) ? { labels: slices.config.labels } : {}),
107
+ ...(((_d = slices.config) === null || _d === void 0 ? void 0 : _d.choices) && entries
108
+ ? { choices: (0, utils_1.fromEntriesWithValue)(entries) }
109
+ : {}),
110
+ },
111
+ };
112
+ }
113
+ exports.traverseSlices = traverseSlices;
@@ -0,0 +1,3 @@
1
+ import * as t from "io-ts";
2
+ declare const _default: <T>(codec: t.Type<T, T, unknown>) => (asObject: (strValue: string) => T) => t.Type<T, T, unknown>;
3
+ export default _default;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const fp_ts_1 = require("fp-ts");
5
+ const function_1 = require("fp-ts/function");
6
+ const t = (0, tslib_1.__importStar)(require("io-ts"));
7
+ exports.default = (codec) => {
8
+ return (asObject) => new t.Type("StringOrT", (u) => u instanceof Object, (u) => {
9
+ return (0, function_1.pipe)(t.union([t.string, codec]).decode(u), fp_ts_1.either.map((decoded) => {
10
+ if (typeof decoded === "string")
11
+ return asObject(decoded);
12
+ return decoded;
13
+ }));
14
+ }, (f) => f);
15
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismicio/types-internal",
3
- "version": "2.1.0-alpha.2",
3
+ "version": "2.1.0-alpha.4",
4
4
  "description": "Prismic types for Custom Types and Prismic Data",
5
5
  "keywords": [
6
6
  "typescript",
@@ -4,7 +4,7 @@ import { pipe } from "fp-ts/lib/function"
4
4
  import * as t from "io-ts"
5
5
 
6
6
  import { WidgetKey } from "../common"
7
- import { isUIDContent, WidgetContent, WidgetLegacy } from "./fields"
7
+ import { WidgetContent, WidgetLegacy } from "./fields"
8
8
  import {
9
9
  defaultCtx,
10
10
  FieldOrSliceType,
@@ -97,11 +97,7 @@ function extractMetadata(data: { [p: string]: unknown }): {
97
97
  )
98
98
 
99
99
  const slugs = (data["slugs_INTERNAL"] as string[]) || []
100
- const uid = (() => {
101
- const rawUID = data["uid"]
102
- if (isUIDContent(rawUID)) return rawUID.value
103
- return
104
- })()
100
+ const uid = data["uid"] as string | undefined
105
101
 
106
102
  return {
107
103
  widgets,
@@ -1,12 +1,16 @@
1
- import { Either, left, right } from "fp-ts/lib/Either"
2
1
  import * as t from "io-ts"
3
2
  import { withFallback } from "io-ts-types/lib/withFallback"
4
3
 
5
4
  import { StringOrNull } from "../validators"
6
- import { DynamicSection, Sections, StaticSection } from "./Section"
5
+ import { fromEntriesWithValue, TraverseFn } from "./_internal/utils"
6
+ import {
7
+ DynamicSection,
8
+ Sections,
9
+ StaticSection,
10
+ traverseSection,
11
+ } from "./Section"
7
12
  import type { SharedSlice } from "./widgets/slices/SharedSlice"
8
13
  import type { DynamicSlice } from "./widgets/slices/Slice"
9
- import type { DynamicSlices } from "./widgets/slices/Slices"
10
14
  import type { DynamicWidget } from "./widgets/Widget"
11
15
 
12
16
  export const CustomTypeFormat = {
@@ -14,24 +18,6 @@ export const CustomTypeFormat = {
14
18
  custom: "custom",
15
19
  }
16
20
 
17
- class CustomTypeSlicesError extends Error {
18
- slices: Array<string>
19
- override message: string
20
-
21
- constructor(slices: Array<string>) {
22
- super()
23
- this.slices = slices
24
- this.message = this._formatError(slices)
25
- }
26
-
27
- _formatError(slicesRefs: Array<string>) {
28
- const slicesMsg = slicesRefs.map((ref) => `\t - ${ref}`)
29
- return `The following slices doesn't exists in your Prismic repository:
30
- ${slicesMsg.join("\n")}
31
- `
32
- }
33
- }
34
-
35
21
  function customTypeReader<T extends StaticSection | DynamicSection>(
36
22
  codec: t.Type<T, unknown>,
37
23
  ) {
@@ -73,39 +59,6 @@ export function flattenWidgets(
73
59
  [],
74
60
  )
75
61
  }
76
- function _retrieveSharedSlicesRef(customType: CustomType): Array<string> {
77
- const slicezones = flattenWidgets(customType).filter(
78
- ([, widget]: [string, DynamicWidget]) => widget.type === "Slices",
79
- ) as Array<[string, DynamicSlices]>
80
-
81
- const allSharedRefs = slicezones.reduce(
82
- (acc: Array<string>, [, slicezone]) => {
83
- const sharedRefs = Object.entries(
84
- slicezone.config && slicezone.config.choices
85
- ? slicezone.config.choices
86
- : {},
87
- )
88
- .filter(
89
- ([, slice]: [string, DynamicSlice]) => slice.type === "SharedSlice",
90
- )
91
- .map(([key]) => key)
92
- return acc.concat(sharedRefs)
93
- },
94
- [],
95
- )
96
-
97
- return allSharedRefs
98
- }
99
-
100
- function _mapSharedSlicesRefs<A>(
101
- customType: CustomType,
102
- ): (mapFn: (ref: string) => A) => Array<A> {
103
- const refs = _retrieveSharedSlicesRef(customType)
104
-
105
- return function (mapFn: (ref: string) => A): Array<A> {
106
- return refs.map(mapFn)
107
- }
108
- }
109
62
 
110
63
  export function toStatic(
111
64
  customType: CustomType,
@@ -127,53 +80,11 @@ export function toStatic(
127
80
  return { ...customType, json } as StaticCustomType
128
81
  }
129
82
 
130
- export function validateSlices(
131
- customType: CustomType,
132
- sharedSlices: Map<string, SharedSlice>,
133
- ): Either<CustomTypeSlicesError, CustomType> {
134
- const missingSlices = _mapSharedSlicesRefs<string | undefined>(customType)(
135
- (ref: string) => {
136
- const slice: SharedSlice | undefined = sharedSlices.get(ref)
137
- const isMissing = !slice
138
- if (isMissing) return ref
139
- return
140
- },
141
- ).filter(Boolean) as Array<string>
142
-
143
- if (missingSlices.length > 0)
144
- return left(new CustomTypeSlicesError(missingSlices))
145
- else return right(customType)
146
- }
147
-
148
- export function collectWidgets(
149
- customType: CustomType,
150
- f: (ref: string, widget: DynamicWidget) => DynamicWidget | undefined,
151
- ): CustomType {
152
- const json = Object.entries(customType.json).reduce(
153
- (acc, [sectionId, section]: [string, DynamicSection]) => {
154
- const updatedSection = Object.entries(section).reduce(
155
- (acc, [ref, widget]) => {
156
- const updatedWidget = f(ref, widget)
157
- if (updatedWidget) {
158
- return { ...acc, [ref]: updatedWidget }
159
- }
160
- return acc
161
- },
162
- {},
163
- )
164
- return { ...acc, [sectionId]: updatedSection }
165
- },
166
- {},
167
- )
168
-
169
- return { ...customType, json }
170
- }
171
-
172
83
  export function filterMissingSharedSlices(
173
84
  customType: CustomType,
174
85
  sharedSlices: Map<string, SharedSlice>,
175
86
  ): CustomType {
176
- return collectWidgets(customType, (_widgetId, widget) => {
87
+ return traverseCustomType(customType, (widget) => {
177
88
  if (widget.type === "Slices") {
178
89
  if (!widget.config || !widget.config.choices) return widget
179
90
 
@@ -194,3 +105,19 @@ export function filterMissingSharedSlices(
194
105
  return widget
195
106
  })
196
107
  }
108
+
109
+ export function traverseCustomType(
110
+ customType: CustomType,
111
+ f: TraverseFn,
112
+ ): CustomType {
113
+ const ctEntries = Object.entries(customType.json).map(
114
+ ([sectionId, section]: [string, DynamicSection]) => {
115
+ return [sectionId, traverseSection([sectionId], section, f)]
116
+ },
117
+ )
118
+
119
+ return {
120
+ ...customType,
121
+ json: fromEntriesWithValue(ctEntries),
122
+ }
123
+ }
@@ -1,7 +1,9 @@
1
1
  import * as t from "io-ts"
2
2
 
3
3
  import { WidgetKey } from "../common"
4
- import type { SharedSlice } from "./widgets/slices"
4
+ import { fromEntriesWithValue, TraverseFn } from "./_internal/utils"
5
+ import { traverseGroup } from "./widgets"
6
+ import { SharedSlice, traverseSlices } from "./widgets/slices"
5
7
  import { DynamicWidget, StaticWidget, Widgets } from "./widgets/Widget"
6
8
 
7
9
  export function sectionReader<T extends StaticWidget | DynamicWidget>(
@@ -33,3 +35,28 @@ export const Sections = {
33
35
  return section as StaticSection
34
36
  },
35
37
  }
38
+
39
+ export function traverseSection(
40
+ path: ReadonlyArray<string>,
41
+ section: DynamicSection,
42
+ f: TraverseFn,
43
+ ): DynamicSection {
44
+ const updatedSection = Object.entries(section).map(([key, widget]) => {
45
+ const widgetPath = path.concat(key)
46
+
47
+ const widgetOutput = (() => {
48
+ switch (widget.type) {
49
+ case "Choice":
50
+ case "Slices":
51
+ return traverseSlices(widgetPath, widget, f)
52
+ case "Group":
53
+ return traverseGroup(widgetPath, widget, f)
54
+ default:
55
+ return f(widget, key, widgetPath)
56
+ }
57
+ })()
58
+ return [key, widgetOutput && f(widgetOutput, key, widgetPath)]
59
+ })
60
+
61
+ return fromEntriesWithValue(updatedSection)
62
+ }
@@ -0,0 +1,25 @@
1
+ import type { DynamicSlice, DynamicWidget } from "../widgets"
2
+
3
+ export type TraverseFn = <T extends DynamicWidget | DynamicSlice>(
4
+ widget: T,
5
+ key: string,
6
+ path: ReadonlyArray<string>,
7
+ ) => DynamicWidget | DynamicSlice | undefined
8
+
9
+ export function fromEntriesWithValue<T>(entries: (string | T)[][]) {
10
+ return Array.from(entries).reduce((acc, entry) => {
11
+ /**
12
+ * Replace the untyped default `fromEntries`
13
+ * Role: filter pair with undefined value
14
+ * Error: Throw an error if malformed: key[string]/value[T]
15
+ */
16
+ if (entry.length !== 2 || typeof entry[0] !== "string")
17
+ throw new Error("Malformed entry, should be a key[string]/value[T] pair.")
18
+
19
+ const [key, value] = entry
20
+ if (value !== undefined) {
21
+ return { ...acc, [key]: entry[1] }
22
+ }
23
+ return acc
24
+ }, {})
25
+ }
@@ -2,6 +2,7 @@ import * as t from "io-ts"
2
2
 
3
3
  import { WidgetKey } from "../../common"
4
4
  import { StringOrNull } from "../../validators"
5
+ import { fromEntriesWithValue, TraverseFn } from "../_internal/utils"
5
6
  import { NestableWidget } from "./nestable/NestableWidget"
6
7
 
7
8
  export const GroupFieldType = "Group"
@@ -28,3 +29,26 @@ export const Group = t.exact(
28
29
  ]),
29
30
  )
30
31
  export type Group = t.TypeOf<typeof Group>
32
+
33
+ export function traverseGroup(
34
+ path: ReadonlyArray<string>,
35
+ group: Group,
36
+ f: TraverseFn,
37
+ ): Group | undefined {
38
+ const entries =
39
+ group.config?.fields &&
40
+ Object.entries(group.config.fields).map(([widgetKey, widgetValue]) => {
41
+ return [widgetKey, f(widgetValue, widgetKey, path.concat(widgetKey))]
42
+ })
43
+
44
+ const config = group.config && {
45
+ ...group.config,
46
+ ...(group.config.fields && entries
47
+ ? { fields: fromEntriesWithValue(entries) }
48
+ : {}),
49
+ }
50
+ return {
51
+ ...group,
52
+ ...(config ? { config } : {}),
53
+ }
54
+ }
@@ -1,55 +1,6 @@
1
- import { either } from "fp-ts"
2
- import { pipe } from "fp-ts/function"
3
1
  import * as t from "io-ts"
4
2
 
5
- // This allow to parse an element that is either a string or a type T and convert the string to T if necessary so we get a consistent output.
6
- const StringOrT = <T>(codec: t.Type<T>) => {
7
- return (asObject: (strValue: string) => T) =>
8
- new t.Type<T>(
9
- "StringOrT",
10
- (u: unknown): u is T => u instanceof Object,
11
- (u: unknown) => {
12
- return pipe(
13
- t.union([t.string, codec]).decode(u),
14
- either.map((decoded) => {
15
- if (typeof decoded === "string") return asObject(decoded)
16
- return decoded
17
- }),
18
- )
19
- },
20
- (f) => f,
21
- )
22
- }
23
-
24
- /*
25
- * The models have slight differences if they are nested or not to avoid recursion and so an infinite level of nesting which would be incompatible with the Content API mechanism to resolve nested content.
26
- */
27
-
28
- const SimpleFieldResolver = StringOrT(
29
- t.type({
30
- fieldId: t.string,
31
- }),
32
- )((id) => ({ fieldId: id }))
33
-
34
- type SimpleFieldResolver = t.TypeOf<typeof SimpleFieldResolver>
35
-
36
- const NestedCustomTypeResolver = t.type({
37
- customTypeId: t.string,
38
- fields: t.readonlyArray(SimpleFieldResolver),
39
- })
40
- type NestedCustomTypeResolver = t.TypeOf<typeof NestedCustomTypeResolver>
41
-
42
- const FieldResolver = StringOrT(
43
- t.intersection([
44
- t.type({
45
- fieldId: t.string,
46
- }),
47
- t.partial({
48
- customTypes: t.readonlyArray(NestedCustomTypeResolver),
49
- }),
50
- ]),
51
- )((id) => ({ fieldId: id }))
52
- type FieldResolver = t.TypeOf<typeof FieldResolver>
3
+ import StringOrT from "../../../../validators/StringOrT"
53
4
 
54
5
  const CustomTypeResolver = StringOrT(
55
6
  t.intersection([
@@ -57,13 +8,13 @@ const CustomTypeResolver = StringOrT(
57
8
  customTypeId: t.string,
58
9
  }),
59
10
  t.partial({
60
- fields: t.readonlyArray(FieldResolver),
11
+ fetchFields: t.boolean,
61
12
  }),
62
13
  ]),
63
14
  )((id) => ({ customTypeId: id }))
64
15
  type CustomTypeResolver = t.TypeOf<typeof CustomTypeResolver>
65
16
 
66
- export const ContentRelationShipResolver = t.readonlyArray(CustomTypeResolver)
67
- export type ContentRelationShipResolver = t.TypeOf<
68
- typeof ContentRelationShipResolver
17
+ export const ContentRelationshipResolver = t.readonlyArray(CustomTypeResolver)
18
+ export type ContentRelationshipResolver = t.TypeOf<
19
+ typeof ContentRelationshipResolver
69
20
  >
@@ -3,7 +3,7 @@ import * as t from "io-ts"
3
3
  import { withFallback } from "io-ts-types/lib/withFallback"
4
4
 
5
5
  import { StringOrNull } from "../../../../validators"
6
- import { ContentRelationShipResolver } from "./ContentRelationshipResolver"
6
+ import { ContentRelationshipResolver } from "./ContentRelationshipResolver"
7
7
 
8
8
  const arrayString = (
9
9
  entries:
@@ -75,7 +75,7 @@ export const LinkConfig = t.exact(
75
75
  ]),
76
76
  null,
77
77
  ),
78
- customtypes: ContentRelationShipResolver,
78
+ customtypes: ContentRelationshipResolver,
79
79
  masks: MasksArrayString,
80
80
  tags: MasksArrayString,
81
81
  allowTargetBlank: t.boolean,
@@ -2,6 +2,7 @@ import * as t from "io-ts"
2
2
 
3
3
  import { WidgetKey } from "../../../common"
4
4
  import { StringOrNull } from "../../../validators"
5
+ import { fromEntriesWithValue, TraverseFn } from "../../_internal/utils"
5
6
  import { NestableWidget } from "../nestable/NestableWidget"
6
7
 
7
8
  export const CompositeSliceType = "Slice"
@@ -30,3 +31,35 @@ export const CompositeSlice = t.exact(
30
31
  ]),
31
32
  )
32
33
  export type CompositeSlice = t.TypeOf<typeof CompositeSlice>
34
+
35
+ export function traverseCompositeSlice(
36
+ path: ReadonlyArray<string>,
37
+ slice: CompositeSlice,
38
+ f: TraverseFn,
39
+ ): CompositeSlice {
40
+ const primaryEntries =
41
+ slice["non-repeat"] &&
42
+ Object.entries(slice["non-repeat"]).map(([widgetKey, widgetValue]) => {
43
+ return [
44
+ widgetKey,
45
+ f(widgetValue, widgetKey, path.concat(["non-repeat", widgetKey])),
46
+ ]
47
+ })
48
+
49
+ const itemsEntries =
50
+ slice.repeat &&
51
+ Object.entries(slice.repeat).map(([widgetKey, widgetValue]) => {
52
+ return [
53
+ widgetKey,
54
+ f(widgetValue, widgetKey, path.concat(["repeat", widgetKey])),
55
+ ]
56
+ })
57
+
58
+ return {
59
+ ...slice,
60
+ ...(primaryEntries
61
+ ? { "non-repeat": fromEntriesWithValue(primaryEntries) }
62
+ : {}),
63
+ ...(itemsEntries ? { repeat: fromEntriesWithValue(itemsEntries) } : {}),
64
+ }
65
+ }
@@ -2,6 +2,7 @@ import * as t from "io-ts"
2
2
  import { withFallback } from "io-ts-types/lib/withFallback"
3
3
 
4
4
  import { WidgetKey } from "../../../common"
5
+ import { fromEntriesWithValue, TraverseFn } from "../../_internal/utils"
5
6
  import { NestableWidget } from "../nestable/NestableWidget"
6
7
 
7
8
  const IMAGE_PLACEHOLDER_URL =
@@ -44,3 +45,49 @@ export const SharedSlice = t.exact(
44
45
  )
45
46
 
46
47
  export type SharedSlice = t.TypeOf<typeof SharedSlice>
48
+
49
+ function traverseVariation(
50
+ path: ReadonlyArray<string>,
51
+ variation: Variation,
52
+ f: TraverseFn,
53
+ ): Variation {
54
+ const primaryEntries =
55
+ variation.primary &&
56
+ Object.entries(variation.primary).map(([widgetKey, widgetValue]) => {
57
+ return [
58
+ widgetKey,
59
+ f(widgetValue, widgetKey, path.concat(["primary", widgetKey])),
60
+ ]
61
+ })
62
+
63
+ const itemsEntries =
64
+ variation.items &&
65
+ Object.entries(variation.items).map(([widgetKey, widgetValue]) => {
66
+ return [
67
+ widgetKey,
68
+ f(widgetValue, widgetKey, path.concat(["items", widgetKey])),
69
+ ]
70
+ })
71
+
72
+ return {
73
+ ...variation,
74
+ ...(primaryEntries
75
+ ? { primary: fromEntriesWithValue(primaryEntries) }
76
+ : {}),
77
+ ...(itemsEntries ? { items: fromEntriesWithValue(itemsEntries) } : {}),
78
+ }
79
+ }
80
+
81
+ export function traverseSharedSlice(
82
+ slice: SharedSlice,
83
+ f: TraverseFn,
84
+ ): SharedSlice {
85
+ const variations = slice.variations.reduce<Array<Variation>>((acc, v) => {
86
+ return acc.concat(traverseVariation([v.id], v, f))
87
+ }, [])
88
+
89
+ return {
90
+ ...slice,
91
+ variations,
92
+ }
93
+ }
@@ -2,7 +2,9 @@ import * as t from "io-ts"
2
2
 
3
3
  import { WidgetKey } from "../../../common"
4
4
  import { StringOrNull } from "../../../validators"
5
- import { CompositeSlice } from "./CompositeSlice"
5
+ import { fromEntriesWithValue, TraverseFn } from "../../_internal/utils"
6
+ import { traverseGroup } from "../Group"
7
+ import { CompositeSlice, traverseCompositeSlice } from "./CompositeSlice"
6
8
  import { LegacySlice } from "./LegacySlice"
7
9
  import { SharedSlice } from "./SharedSlice"
8
10
  import { SharedSliceRef } from "./SharedSliceRef"
@@ -110,3 +112,44 @@ export const Slices = {
110
112
  }
111
113
  },
112
114
  }
115
+
116
+ export function traverseSlices(
117
+ path: ReadonlyArray<string>,
118
+ slices: DynamicSlices,
119
+ f: TraverseFn,
120
+ ): DynamicSlices {
121
+ const entries =
122
+ slices.config?.choices &&
123
+ Object.entries(slices.config.choices).map(([sliceKey, sliceValue]) => {
124
+ const slicePath = path.concat(sliceKey)
125
+
126
+ switch (sliceValue.type) {
127
+ case "SharedSlice":
128
+ return [sliceKey, f(sliceValue, sliceKey, slicePath)]
129
+ case "Slice":
130
+ return [
131
+ sliceKey,
132
+ f(
133
+ traverseCompositeSlice(slicePath, sliceValue, f),
134
+ sliceKey,
135
+ slicePath,
136
+ ),
137
+ ]
138
+ case "Group":
139
+ return [sliceKey, traverseGroup(slicePath, sliceValue, f)]
140
+ default:
141
+ return [sliceKey, f(sliceValue, sliceKey, slicePath)]
142
+ }
143
+ })
144
+
145
+ return {
146
+ ...slices,
147
+ config: {
148
+ ...(slices.config?.label ? { label: slices.config.label } : {}),
149
+ ...(slices.config?.labels ? { labels: slices.config.labels } : {}),
150
+ ...(slices.config?.choices && entries
151
+ ? { choices: fromEntriesWithValue(entries) }
152
+ : {}),
153
+ },
154
+ }
155
+ }
@@ -0,0 +1,21 @@
1
+ import { either } from "fp-ts"
2
+ import { pipe } from "fp-ts/function"
3
+ import * as t from "io-ts"
4
+
5
+ export default <T>(codec: t.Type<T>) => {
6
+ return (asObject: (strValue: string) => T) =>
7
+ new t.Type<T>(
8
+ "StringOrT",
9
+ (u: unknown): u is T => u instanceof Object,
10
+ (u: unknown) => {
11
+ return pipe(
12
+ t.union([t.string, codec]).decode(u),
13
+ either.map((decoded) => {
14
+ if (typeof decoded === "string") return asObject(decoded)
15
+ return decoded
16
+ }),
17
+ )
18
+ },
19
+ (f) => f,
20
+ )
21
+ }