ballerina-core 1.0.49 → 1.0.51

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
@@ -2,7 +2,7 @@
2
2
  "name": "ballerina-core",
3
3
  "author": "Dr. Giuseppe Maggiore",
4
4
  "private": false,
5
- "version": "1.0.49",
5
+ "version": "1.0.51",
6
6
  "main": "main.ts",
7
7
  "dependencies": {
8
8
  "immutable": "^5.0.0-beta.5",
@@ -1,30 +1,31 @@
1
1
  import { List } from "immutable";
2
2
  import { BasicFun, BasicFun2, Fun } from "../../../fun/state";
3
3
  import { Value } from "../../../value/state";
4
+ import { Updater } from "../../../../main";
4
5
 
5
6
  export type ValueOrErrors<v, e> = (
6
7
  | (Value<v> & { kind: "value" })
7
8
  | { errors: List<e>; kind: "errors" }
8
9
  ) & {
9
- map: <a, b, e>(
10
+ Map: <a, b, e>(
10
11
  this: ValueOrErrors<a, e>,
11
12
  f: BasicFun<a, b>
12
13
  ) => ValueOrErrors<b, e>;
13
- mapErrors: <a, e, e2>(
14
+ MapErrors: <a, e, e2>(
14
15
  this: ValueOrErrors<a, e>,
15
16
  f: BasicFun<List<e>, List<e2>>
16
17
  ) => ValueOrErrors<a, e2>;
17
- flatten: <a, e>(
18
+ Flatten: <a, e>(
18
19
  this: ValueOrErrors<ValueOrErrors<a, e>, e>
19
20
  ) => ValueOrErrors<a, e>;
20
- then: <a, b, e>(
21
+ Then: <a, b, e>(
21
22
  this: ValueOrErrors<a, e>,
22
23
  k: BasicFun<a, ValueOrErrors<b, e>>
23
24
  ) => ValueOrErrors<b, e>;
24
25
  };
25
26
 
26
27
  const operations = {
27
- map: function <a, b, e>(
28
+ Map: function <a, b, e>(
28
29
  this: ValueOrErrors<a, e>,
29
30
  f: BasicFun<a, b>
30
31
  ): ValueOrErrors<b, e> {
@@ -33,7 +34,7 @@ const operations = {
33
34
  }
34
35
  return ValueOrErrors.Default.return(f(this.value));
35
36
  },
36
- mapErrors: function <a, e, e2>(
37
+ MapErrors: function <a, e, e2>(
37
38
  this: ValueOrErrors<a, e>,
38
39
  f: BasicFun<List<e>, List<e2>>
39
40
  ): ValueOrErrors<a, e2> {
@@ -42,7 +43,7 @@ const operations = {
42
43
  }
43
44
  return this;
44
45
  },
45
- flatten: function <a, e>(
46
+ Flatten: function <a, e>(
46
47
  this: ValueOrErrors<ValueOrErrors<a, e>, e>
47
48
  ): ValueOrErrors<a, e> {
48
49
  if (this.kind == "errors") {
@@ -53,11 +54,11 @@ const operations = {
53
54
  return this.value;
54
55
  }
55
56
  },
56
- then: function <a, b, e>(
57
+ Then: function <a, b, e>(
57
58
  this: ValueOrErrors<a, e>,
58
59
  k: BasicFun<a, ValueOrErrors<b, e>>
59
60
  ): ValueOrErrors<b, e> {
60
- return this.map(k).flatten();
61
+ return this.Map(k).Flatten();
61
62
  },
62
63
  };
63
64
 
@@ -75,23 +76,23 @@ export const ValueOrErrors = {
75
76
  }),
76
77
  },
77
78
  Operations: {
78
- return: <v, e>(_: v): ValueOrErrors<v, e> =>
79
+ Return: <v, e>(_: v): ValueOrErrors<v, e> =>
79
80
  ValueOrErrors.Default.return(_),
80
- throw: <v, e>(_: e): ValueOrErrors<v, e> =>
81
- ValueOrErrors.Default.throw(List([_])),
82
- map: <a, b, e>(
81
+ Throw: <v, e>(_: List<e>): ValueOrErrors<v, e> =>
82
+ ValueOrErrors.Default.throw(_),
83
+ Map: <a, b, e>(
83
84
  f: BasicFun<a, b>
84
85
  ): Fun<ValueOrErrors<a, e>, ValueOrErrors<b, e>> =>
85
86
  Fun((_) =>
86
87
  _.kind == "errors" ? _ : ValueOrErrors.Default.return(f(_.value))
87
88
  ),
88
- mapErrors: <a, e, e2>(
89
+ MapErrors: <a, e, e2>(
89
90
  f: BasicFun<List<e>, List<e2>>
90
91
  ): BasicFun<ValueOrErrors<a, e>, ValueOrErrors<a, e2>> =>
91
92
  Fun((_) =>
92
93
  _.kind == "errors" ? ValueOrErrors.Default.throw(f(_.errors)) : _
93
94
  ),
94
- flatten: <a, e>(): Fun<
95
+ Flatten: <a, e>(): Fun<
95
96
  ValueOrErrors<ValueOrErrors<a, e>, e>,
96
97
  ValueOrErrors<a, e>
97
98
  > =>
@@ -104,30 +105,30 @@ export const ValueOrErrors = {
104
105
  return _.value;
105
106
  }
106
107
  }),
107
- then: <a, b, e>(
108
+ Then: <a, b, e>(
108
109
  k: BasicFun<a, ValueOrErrors<b, e>>
109
110
  ): Fun<ValueOrErrors<a, e>, ValueOrErrors<b, e>> =>
110
111
  Fun((_: ValueOrErrors<a, e>) =>
111
- ValueOrErrors.Operations.flatten<b, e>()(
112
- ValueOrErrors.Operations.map<a, ValueOrErrors<b, e>, e>(k)(_)
112
+ ValueOrErrors.Operations.Flatten<b, e>()(
113
+ ValueOrErrors.Operations.Map<a, ValueOrErrors<b, e>, e>(k)(_)
113
114
  )
114
115
  ),
115
- fold: <v, e, c>(
116
+ Fold: <v, e, c>(
116
117
  l: BasicFun<v, c>,
117
118
  r: BasicFun<List<e>, c>
118
119
  ): Fun<ValueOrErrors<v, e>, c> =>
119
120
  Fun((_) => (_.kind == "value" ? l(_.value) : r(_.errors))),
120
- all: <v, e>(_: List<ValueOrErrors<v, e>>): ValueOrErrors<List<v>, e> =>
121
+ All: <v, e>(_: List<ValueOrErrors<v, e>>): ValueOrErrors<List<v>, e> =>
121
122
  _.reduce(
122
123
  (reduction, value) =>
123
- ValueOrErrors.Operations.fold<v, e, ValueOrErrors<List<v>, e>>(
124
+ ValueOrErrors.Operations.Fold<v, e, ValueOrErrors<List<v>, e>>(
124
125
  (v: v) =>
125
126
  reduction.kind == "errors"
126
127
  ? reduction
127
- : reduction.map((_) => _.concat(v)),
128
+ : reduction.Map((_) => _.concat(v)),
128
129
  (es: List<e>) =>
129
130
  reduction.kind == "errors"
130
- ? reduction.mapErrors((_) => _.concat(es))
131
+ ? reduction.MapErrors((_) => _.concat(es))
131
132
  : ValueOrErrors.Default.throw(es)
132
133
  )(value),
133
134
  ValueOrErrors.Default.return(List<v>())
@@ -1,3 +1,4 @@
1
+ import { List } from "immutable"
1
2
  import { FormRunnerErrorsTemplate, id, Mapping, replaceWith, Sum, unit } from "../../../../../main"
2
3
  import { AsyncState } from "../../../../async/state"
3
4
  import { CoTypedFactory } from "../../../../coroutines/builder"
@@ -27,7 +28,7 @@ export const FormRunnerLoader = () => {
27
28
  return FormRunnerState.Updaters.form(
28
29
  replaceWith(
29
30
  Sum.Default.left(
30
- FormRunnerErrorsTemplate(Sum.Default.right([`Cannot find form '${formRef.formName}'`]))
31
+ FormRunnerErrorsTemplate(Sum.Default.right(List([`Cannot find form '${formRef.formName}'`])))
31
32
  )
32
33
  )
33
34
  )
@@ -47,7 +48,7 @@ export const FormRunnerLoader = () => {
47
48
  return FormRunnerState.Updaters.form(
48
49
  replaceWith(
49
50
  Sum.Default.left(
50
- FormRunnerErrorsTemplate(Sum.Default.right([`Cannot find form '${formRef.formName}'`]))
51
+ FormRunnerErrorsTemplate(Sum.Default.right(List([`Cannot find form '${formRef.formName}'`])))
51
52
  )
52
53
  )
53
54
  )
@@ -67,7 +68,7 @@ export const FormRunnerLoader = () => {
67
68
  return FormRunnerState.Updaters.form(
68
69
  replaceWith(
69
70
  Sum.Default.left(
70
- FormRunnerErrorsTemplate(Sum.Default.right([`Cannot find form '${formRef.formName}'`]))
71
+ FormRunnerErrorsTemplate(Sum.Default.right(List([`Cannot find form '${formRef.formName}'`])))
71
72
  )
72
73
  )
73
74
  )
@@ -37,15 +37,27 @@ export const editFormRunner = <E, FS>() => {
37
37
  EditFormState<E, FS>().Updaters.Template.toUnchecked()
38
38
  ),
39
39
  Debounce<Synchronized<Unit, ApiErrors>, EditFormWritableState<E, FS>>(
40
- Synchronize<Unit, ApiErrors, EditFormWritableState<E, FS>>(
41
- (_) =>
42
- current.entity.sync.kind == "loaded"
43
- ? current.api.update(current.entityId, current.entity.sync.value, current.formState)
44
- : Promise.resolve([]),
45
- (_) => "transient failure",
46
- 5,
47
- 50
48
- ),
40
+ (() => {
41
+ if(current.entity.sync.kind != "loaded") return Synchronize<Unit, ApiErrors, EditFormWritableState<E, FS>>(
42
+ (_) => Promise.resolve([]),
43
+ (_) => "transient failure",
44
+ 5,
45
+ 50
46
+ )
47
+ const parsed = current.parser(current.entity.sync.value, current.formState)
48
+
49
+ return Synchronize<Unit, ApiErrors, EditFormWritableState<E, FS>>(
50
+ (_) =>
51
+ {
52
+ if(parsed.kind == "errors") return Promise.reject(parsed.errors)
53
+ return current.api.update(current.entityId, parsed)
54
+ },
55
+ (_) => "transient failure",
56
+ parsed.kind == "errors" ? 1 : 5,
57
+ 50
58
+ )
59
+ })(),
60
+
49
61
  15
50
62
  ).embed(
51
63
  (_) => ({ ..._, ..._.apiRunner }),
@@ -1,4 +1,5 @@
1
1
  import { ApiResponseChecker, AsyncState, BasicUpdater, Debounced, ForeignMutationsInput, Guid, id, SimpleCallback, simpleUpdater, Synchronized, Template, unit, Unit, Updater, Value } from "../../../../../../main"
2
+ import { ValueOrErrors } from "../../../../../collections/domains/valueOrErrors/state"
2
3
  import { BasicFun } from "../../../../../fun/state"
3
4
 
4
5
  export type ApiErrors = Array<string>
@@ -7,8 +8,9 @@ export type EditFormContext<E,FS> = {
7
8
  entityId:string,
8
9
  api:{
9
10
  get:(id: Guid) => Promise<E>,
10
- update:(id: Guid, entity:E, formstate: FS) => Promise<ApiErrors>
11
+ update:(id: Guid, raw: any) => Promise<ApiErrors>
11
12
  },
13
+ parser: (entity:E, formstate: FS) => ValueOrErrors<E, ApiErrors>
12
14
  actualForm:Template<Value<E> & FS, FS, { onChange:SimpleCallback<BasicUpdater<E>>}>
13
15
  }
14
16
 
@@ -12,9 +12,9 @@ export const LoadValidateAndParseFormsConfig = <T extends {[key in keyof T] : {t
12
12
  const formsConfig = replaceKeywords(rawFormsConfig, "from api")
13
13
  const builtIns = builtInsFromFieldViews(current.fieldViews)
14
14
  const injectedPrimitives = current.injectedPrimitives ? injectablesFromFieldViews(current.fieldViews, current.injectedPrimitives) : undefined
15
- const validationResult = FormsConfig.Default.validateAndParseAPIResponse(builtIns, current.fieldTypeConverters, injectedPrimitives)(formsConfig)
16
- if (validationResult.kind == "r")
17
- return Sum.Default.right(validationResult.value)
15
+ const validationResult = FormsConfig.Default.validateAndParseFormConfig(builtIns, current.fieldTypeConverters, injectedPrimitives)(formsConfig)
16
+ if (validationResult.kind == "errors")
17
+ return Sum.Default.right(validationResult.errors)
18
18
  return parseForms(
19
19
  builtIns,
20
20
  injectedPrimitives,
@@ -2,7 +2,7 @@ import { Map, List, Set, OrderedMap } from "immutable"
2
2
  import { CollectionReference } from "../../../collection/domains/reference/state";
3
3
  import { CollectionSelection } from "../../../collection/domains/selection/state";
4
4
  import { BasicFun } from "../../../../../fun/state";
5
- import { InjectedPrimitives, Maybe, replaceKeyword, replaceKeywords, revertKeyword, Type, TypeDefinition, TypeName, Unit, Value } from "../../../../../../main";
5
+ import { InjectedPrimitives, Maybe, replaceKeyword, replaceKeywords, revertKeyword, Sum, Type, TypeDefinition, TypeName, Unit, Value } from "../../../../../../main";
6
6
  import { ValueOrErrors } from "../../../../../collections/domains/valueOrErrors/state";
7
7
 
8
8
  export const PrimitiveTypes =
@@ -152,15 +152,6 @@ export const defaultValue = <T>(types: Map<TypeName, TypeDefinition>, builtIns:
152
152
  }
153
153
  }
154
154
 
155
-
156
- const parseTypeIShouldBePartOfFormValidation = (t:any) : TypeName | Type => {
157
- if (typeof t == "string") return t
158
- if ("fun" in t && "args" in t && Array.isArray(t.args)) {
159
- return { kind:"application", value:t.fun, args:t.args }
160
- }
161
- return null!
162
- }
163
-
164
155
  export const fromAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters: BuiltInApiConverters, isKeywordsReplaced: boolean = false, injectedPrimitives?: InjectedPrimitives<T>) => (raw: any): any => {
165
156
  // alert(JSON.stringify(t))
166
157
  if (raw == undefined) {
@@ -196,25 +187,25 @@ export const fromAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>
196
187
  }
197
188
  if (t.value == "Map" && t.args.length == 2) {
198
189
  let result = converters[t.value].fromAPIRawValue(obj)
199
- let t_args = t.args.map(parseTypeIShouldBePartOfFormValidation)
200
- const isKeyPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
201
- const isValuePrimitive = PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
190
+
191
+ const isKeyPrimitive = typeof t.args[0] == "string" && PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
192
+ const isValuePrimitive = typeof t.args[1] == "string" && PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
202
193
  result = result.map(keyValue => ([
203
194
  fromAPIRawValue(
204
- typeof t_args[0] == "string" ?
195
+ typeof t.args[0] == "string" ?
205
196
  isKeyPrimitive ?
206
- { kind: "primitive", value: t_args[0] as PrimitiveType }
207
- : { kind: "lookup", name: t_args[0] }
197
+ { kind: "primitive", value: t.args[0] as PrimitiveType }
198
+ : { kind: "lookup", name: t.args[0] }
208
199
  :
209
- t_args[0],
200
+ t.args[0],
210
201
  types, builtIns, converters, true, injectedPrimitives)(keyValue[0]),
211
202
  fromAPIRawValue(
212
- typeof t_args[1] == "string" ?
203
+ typeof t.args[1] == "string" ?
213
204
  isValuePrimitive ?
214
- { kind: "primitive", value: t_args[1] as PrimitiveType }
215
- : { kind: "lookup", name: t_args[1] }
205
+ { kind: "primitive", value: t.args[1] as PrimitiveType }
206
+ : { kind: "lookup", name: t.args[1] }
216
207
  :
217
- t_args[1],
208
+ t.args[1],
218
209
  types, builtIns, converters, true, injectedPrimitives)(keyValue[1]),
219
210
  ])
220
211
  )
@@ -237,24 +228,24 @@ export const fromAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>
237
228
  export const toAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters: BuiltInApiConverters, isKeywordsReverted: boolean = false, injectedPrimitives?: InjectedPrimitives<T>) => (raw: any, formState: any) : ValueOrErrors<any, string> => {
238
229
  const obj = !isKeywordsReverted ? replaceKeywords(raw, "to api") : raw
239
230
  if (t.kind == "primitive") {
240
- return ValueOrErrors.Operations.return(converters[t.value].toAPIRawValue([obj, formState.modifiedByUser] as never))
231
+ return ValueOrErrors.Operations.Return(converters[t.value].toAPIRawValue([obj, formState.modifiedByUser] as never))
241
232
  } else if (t.kind == "application") { // application here means "generic type application"
242
233
  if (t.value == "SingleSelection" && t.args.length == 1) {
243
234
  const result = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
244
- if(typeof result != "object") return ValueOrErrors.Operations.return(result)
235
+ if(typeof result != "object") return ValueOrErrors.Operations.Return(result)
245
236
 
246
237
  return toAPIRawValue({ kind:"lookup", name:t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(result, formState)
247
238
  }
248
239
  if ((t.value == "Multiselection" || t.value == "MultiSelection") && t.args.length == 1) {
249
240
  const result = converters["MultiSelection"].toAPIRawValue([obj, formState.modifiedByUser])
250
241
 
251
- return ValueOrErrors.Operations.all(List<ValueOrErrors<any, string>>(result.map((_:any) =>
252
- typeof _ == "object" ? toAPIRawValue({ kind:"lookup", name: t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(_, formState) : ValueOrErrors.Operations.return(_))))
242
+ return ValueOrErrors.Operations.All(List<ValueOrErrors<any, string>>(result.map((_:any) =>
243
+ typeof _ == "object" ? toAPIRawValue({ kind:"lookup", name: t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(_, formState) : ValueOrErrors.Operations.Return(_))))
253
244
  }
254
245
  if (t.value == "List" && t.args.length == 1) {
255
246
  const converterResult = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
256
247
  const isPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
257
- return ValueOrErrors.Operations.all(List<ValueOrErrors<any, string>>(converterResult.map((item: any, index: number) =>
248
+ return ValueOrErrors.Operations.All(List<ValueOrErrors<any, string>>(converterResult.map((item: any, index: number) =>
258
249
  toAPIRawValue(
259
250
  isPrimitive ?
260
251
  { kind:"primitive", value:t.args[0] as PrimitiveType }
@@ -264,62 +255,53 @@ export const toAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>,
264
255
  ))))
265
256
  }
266
257
  if (t.value == "Map" && t.args.length == 2) {
267
- const converterResult = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
258
+ const [converterResult, toIdentiferAndDisplayName] = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
268
259
  const isKeyPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
269
260
  const isValuePrimitive = PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
270
- let t_args = t.args.map(parseTypeIShouldBePartOfFormValidation)
271
261
 
272
- const parsedMap: ValueOrErrors<{key: ValueOrErrors<any, any>, value: ValueOrErrors<any, any>}, any>[] = converterResult.map((keyValue: any, index: number) => {
273
- const key = toAPIRawValue(
274
- typeof t_args[0] == "string" ?
262
+ const parsedMap: List<ValueOrErrors<{key: ValueOrErrors<any, any>, value: ValueOrErrors<any, any>}, any>> = converterResult.map((keyValue: any, index: number) => {
263
+ const possiblyUndefinedKey = toAPIRawValue(
264
+ typeof t.args[0] == "string" ?
275
265
  isKeyPrimitive ?
276
- { kind: "primitive", value: t_args[0] as PrimitiveType }
277
- : { kind: "lookup", name: t_args[0] }
266
+ { kind: "primitive", value: t.args[0] as PrimitiveType }
267
+ : { kind: "lookup", name: t.args[0] }
278
268
  :
279
- t_args[0],
269
+ t.args[0],
280
270
  types, builtIns, converters, true, injectedPrimitives)(keyValue[0], formState.elementFormStates.get(index).KeyFormState
281
271
  )
282
272
 
283
- if(key.kind == "value" && (key.value == undefined || key.value == null)) {
284
- return ValueOrErrors.Operations.throw([`A mapped key is undefined for type ${JSON.stringify(t.args[0])}`])
285
- } else if ( key.kind == "errors"){
286
- return key
287
- }
273
+ const key: ValueOrErrors<any, string> = (() => {
274
+ if(possiblyUndefinedKey.kind == "value" && (possiblyUndefinedKey.value == undefined || possiblyUndefinedKey.value == null || possiblyUndefinedKey.value == "" || (typeof possiblyUndefinedKey.value == "object" && Object.keys(possiblyUndefinedKey.value).length == 0))) {
275
+ return ValueOrErrors.Operations.Throw(List([`A mapped key is undefined for type ${JSON.stringify(t.args[0])}`]))
276
+ }
277
+ return possiblyUndefinedKey
278
+ })()
288
279
 
289
280
  const value = toAPIRawValue(
290
- typeof t_args[1] == "string" ?
281
+ typeof t.args[1] == "string" ?
291
282
  isValuePrimitive ?
292
- { kind: "primitive", value: t_args[1] as PrimitiveType }
293
- : { kind: "lookup", name: t_args[1] }
283
+ { kind: "primitive", value: t.args[1] as PrimitiveType }
284
+ : { kind: "lookup", name: t.args[1] }
294
285
  :
295
- t_args[1],
286
+ t.args[1],
296
287
  types, builtIns, converters, true, injectedPrimitives)(keyValue[1], formState.elementFormStates.get(index).ValueFormState)
297
288
 
298
- if(value.kind == "errors") return value
299
-
300
- return ValueOrErrors.Operations.return({key, value})
289
+ return key.kind == "errors" || value.kind == "errors" ? ValueOrErrors.Operations.All(List([key, value])) : ValueOrErrors.Default.return({key: key.value, value: value.value})
301
290
  }
302
291
  )
303
-
304
- if(parsedMap.length > 0 && parsedMap.some((_: ValueOrErrors<any, any>) => _.kind == "errors")) {
305
- return ValueOrErrors.Operations.all(List(parsedMap))
306
- }
307
- // TODO this needs improvement
308
- const allKeysStringified = parsedMap.map((_) => _.kind == "value" ? JSON.stringify((_.value.key as any).value) : "")
309
- const allKeysUnique = Set(allKeysStringified).size == allKeysStringified.length
310
-
311
- if(allKeysStringified.length > 0 && !allKeysUnique) {
312
- return ValueOrErrors.Operations.throw(`Keys in the map are not unique: ${JSON.stringify(allKeysStringified)}`)
313
- }
314
292
 
315
- // return ValueOrErrors.Operations.return(parsedMap.map(_ => _.flatten()))
316
- return ValueOrErrors.Operations.return(parsedMap.map(_ => (_ as any).map(({key, value}: {key: any, value: any}) => ({key: key.value, value: value.value}))).map((_: any) => _.value))
293
+ const nonUniqueKeyErrors = parsedMap.filter(_ => _.kind == "value").reduce((acc, _) => {
294
+ const [id, displayName] = toIdentiferAndDisplayName(_.value.key)
295
+ acc.ids.contains(id) ? acc.errors = acc.errors.push(ValueOrErrors.Default.throw(List([`Keys in the map are not unique: ${displayName}`]))) : acc.ids = acc.ids.push(id)
296
+ return acc
297
+ }, {ids: List<string>(), errors: List<ValueOrErrors<any, string>>()}).errors
317
298
 
299
+ return ValueOrErrors.Operations.All(parsedMap.concat(nonUniqueKeyErrors))
318
300
  }
319
301
  } else { // t.kind == lookup: we are dealing with a record/object or extended type
320
302
  const tDef = types.get(t.name)!
321
303
  if("extends" in tDef && tDef.extends.length == 1) {
322
- return ValueOrErrors.Operations.return(converters[(tDef.extends[0] as keyof BuiltInApiConverters)].toAPIRawValue([obj, formState.modifiedByUser] as never))
304
+ return ValueOrErrors.Operations.Return(converters[(tDef.extends[0] as keyof BuiltInApiConverters)].toAPIRawValue([obj, formState.modifiedByUser] as never))
323
305
  }
324
306
  const convertedMap = tDef.fields.mapEntries(([fieldName, fieldType] ) => {
325
307
  const revertedFieldName = revertKeyword(fieldName)
@@ -329,10 +311,10 @@ export const toAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>,
329
311
  })
330
312
  if(convertedMap.some((valueOrError) => valueOrError.kind == "errors")) {
331
313
  const propertiesWithErrors = convertedMap.filter((valueOrError) => valueOrError.kind == "errors")
332
- const namedErrors = propertiesWithErrors.map((value, key) => value.mapErrors(_ => _.map((_: string) => `${key}: ${_}`)))
333
- return ValueOrErrors.Operations.all(List<ValueOrErrors<any, string>>(namedErrors.valueSeq().toList()))
314
+ const namedErrors = propertiesWithErrors.map((value, key) => value.MapErrors(_ => _.map((_: string) => `${key}: ${_}`)))
315
+ return ValueOrErrors.Operations.All(List<ValueOrErrors<any, string>>(namedErrors.valueSeq().toList()))
334
316
  }
335
- return ValueOrErrors.Operations.return(convertedMap.map(valueOrError => valueOrError.kind == "value" ? valueOrError.value : valueOrError.errors).toJS())
317
+ return ValueOrErrors.Operations.Return(convertedMap.map(valueOrError => valueOrError.kind == "value" ? valueOrError.value : valueOrError.errors).toJS())
336
318
  }
337
- return ValueOrErrors.Operations.return(defaultValue(types, builtIns, injectedPrimitives)(t.value))
319
+ return ValueOrErrors.Operations.Return(defaultValue(types, builtIns, injectedPrimitives)(t.value))
338
320
  }
@@ -1,5 +1,6 @@
1
- import { Set, Map, OrderedMap } from "immutable";
2
- import { ApiConverters, BoolExpr, BuiltIns, FieldName, FormsConfigMerger, InjectedPrimitives, MappingPaths, revertKeyword, Sum, Type, TypeDefinition, TypeName } from "../../../../../../main";
1
+ import { Set, Map, OrderedMap, List } from "immutable";
2
+ import { ApiConverters, BoolExpr, BuiltIns, FieldName, FormsConfigMerger, InjectedPrimitives, MappingPaths, revertKeyword, Sum, Type, TypeDefinition, TypeName, unit, Value } from "../../../../../../main";
3
+ import { ValueOrErrors } from "../../../../../collections/domains/valueOrErrors/state";
3
4
 
4
5
  export type FieldConfig = {
5
6
  renderer: string;
@@ -67,23 +68,46 @@ export type FormsConfig = {
67
68
  };
68
69
  export type FormValidationError = string;
69
70
 
70
- export type FormValidationResult = Sum<FormsConfig, Array<FormValidationError>>
71
+
72
+ export type FormConfigValidationAndParseResult = ValueOrErrors<FormsConfig, FormValidationError>
71
73
  export const FormsConfig = {
72
74
  Default: {
73
- validateAndParseAPIResponse: <T extends {[key in keyof T]: {type: any, state: any}}>(builtIns: BuiltIns, apiConverters: ApiConverters<T>, injectedPrimitives?: InjectedPrimitives<T>) => (fc: any): FormValidationResult => {
74
- let errors: Array<FormValidationError> = [];
75
+ validateAndParseFormConfig: <T extends {[key in keyof T]: {type: any, state: any}}>(builtIns: BuiltIns, apiConverters: ApiConverters<T>, injectedPrimitives?: InjectedPrimitives<T>) => (fc: any): FormConfigValidationAndParseResult => {
76
+ let errors: List<FormValidationError> = List();
75
77
  const formsConfig = Array.isArray(fc) ? FormsConfigMerger.Default.merge(fc) : fc;
76
- let types: Map<TypeName, TypeDefinition> = Map();
77
- if ("types" in formsConfig == false) {
78
- errors.push("the formsConfig does not contain a 'types' field");
79
- return Sum.Default.right(errors);
78
+
79
+ const hasApis = "apis" in formsConfig;
80
+ const apiProps = List(["enumOptions", "searchableStreams", "entities"]);
81
+ const formPropertyChecks = List<[string, boolean]>(
82
+ [
83
+ ['types', "types" in formsConfig],
84
+ ['forms', "forms" in formsConfig],
85
+ ['apis', hasApis],
86
+ ['enumOptions', hasApis && "enumOptions" in formsConfig.apis],
87
+ ['searchableStreams', hasApis && "searchableStreams" in formsConfig.apis],
88
+ ['entities', hasApis && "entities" in formsConfig.apis],
89
+ ['mappings', "mappings" in formsConfig],
90
+ ['launchers', "launchers" in formsConfig]
91
+ ]
92
+ );
93
+
94
+ if(formPropertyChecks.some(([_, hasProp]) => !hasProp)){
95
+ const formPropertyErrors = formPropertyChecks.filter(([_, hasProp]) => !hasProp)
96
+ .map(([prop, _]) => apiProps.includes(prop) ?
97
+ `the formsConfig.apis does not contain a '${prop}' field` :
98
+ `the formsConfig does not contain a '${prop}' field`);
99
+ return ValueOrErrors.Default.throw(formPropertyErrors);
80
100
  }
101
+
81
102
  if(injectedPrimitives){
82
103
  injectedPrimitives?.injectedPrimitives.keySeq().toArray().some((injectedPrimitiveName) => {
83
104
  if(!Object.keys(apiConverters).includes(injectedPrimitiveName as string)){
84
- errors.push(`the formsConfig does not contain an Api Converter for injected primitive: ${injectedPrimitiveName as string}`);
105
+ errors = errors.push(`the formsConfig does not contain an Api Converter for injected primitive: ${injectedPrimitiveName as string}`);
106
+
85
107
  }})
86
108
  }
109
+
110
+ let types: Map<TypeName, TypeDefinition> = Map();
87
111
  Object.keys(formsConfig["types"]).forEach((typeName: any) => {
88
112
  let typeDef: TypeDefinition = { name: typeName, extends: [], fields: OrderedMap() };
89
113
  types = types.set(typeName, typeDef);
@@ -93,17 +117,17 @@ export const FormsConfig = {
93
117
  typeDef.extends.push(...configTypeDef["extends"]);
94
118
 
95
119
  else
96
- errors.push(`invalid 'extends' clause in type ${typeName}: expected string[]`);
120
+ errors = errors.push(`invalid 'extends' clause in type ${typeName}: expected string[]`);
97
121
  }
98
122
  if ("fields" in configTypeDef == false)
99
- errors.push(`missing 'fields' in type ${typeName}: expected object`);
123
+ errors = errors.push(`missing 'fields' in type ${typeName}: expected object`);
100
124
 
101
125
  Object.keys(configTypeDef["fields"]).forEach((fieldName: any) => {
102
126
  let configFieldType = configTypeDef["fields"][fieldName];
103
127
  if (typeof configFieldType == "string") {
104
128
  if (injectedPrimitives?.injectedPrimitives.has(configFieldType as keyof T) &&
105
129
  (builtIns.primitives.has(configFieldType) || builtIns.generics.has(configFieldType))) {
106
- errors.push(`field ${fieldName} in type ${typeName}: injectedPrimitive cannot have same name as builtIn primitive`);
130
+ errors = errors.push(`field ${fieldName} in type ${typeName}: injectedPrimitive cannot have same name as builtIn primitive`);
107
131
  } else {
108
132
  if (builtIns.primitives.has(configFieldType) || injectedPrimitives?.injectedPrimitives.has(configFieldType as keyof T))
109
133
  typeDef.fields = typeDef.fields.set(fieldName, { kind: "primitive", value: configFieldType as any });
@@ -114,97 +138,93 @@ export const FormsConfig = {
114
138
  if ("fun" in configFieldType && "args" in configFieldType &&
115
139
  typeof configFieldType["fun"] == "string" &&
116
140
  Array.isArray(configFieldType["args"])
117
- // &&
118
- // configFieldType["args"].every(_ => typeof (_) == "string")
119
141
  ) {
142
+ const args = configFieldType["fun"] == "Map" ?
143
+ configFieldType["args"].map((arg:any) => (typeof arg == "string" ? arg : { kind:"application", value: arg.fun, args: arg.args })) as any :
144
+ configFieldType["args"] as any;
120
145
  const fieldType: Type = {
121
146
  kind: "application",
122
147
  value: configFieldType["fun"] as any,
123
- args: configFieldType["args"] as any,
148
+ args,
124
149
  }
125
150
  typeDef.fields = typeDef.fields.set(fieldName, fieldType);
126
151
  }
127
152
  else
128
- errors.push(`field ${fieldName} in type ${typeName}: expected application, found ${JSON.stringify(configFieldType)}`);
153
+ errors = errors.push(`field ${fieldName} in type ${typeName}: expected application, found ${JSON.stringify(configFieldType)}`);
129
154
  }
130
155
  });
131
156
  });
157
+
132
158
  types.forEach((typeDef, typeName) => {
133
159
  typeDef.extends.forEach(extendedTypeName => {
134
160
  if ((!builtIns.primitives.has(extendedTypeName) && !injectedPrimitives?.injectedPrimitives.has(extendedTypeName as keyof T)) && !types.has(extendedTypeName))
135
- errors.push(`type ${typeName} extends non-existent type ${extendedTypeName}`);
161
+ errors = errors.push(`type ${typeName} extends non-existent type ${extendedTypeName}`);
136
162
  });
137
163
  typeDef.fields.forEach((fieldDef, fieldName) => {
138
164
  if (fieldDef.kind == "primitive" && (!builtIns.primitives.has(fieldDef.value) && !injectedPrimitives?.injectedPrimitives.has(fieldDef.value as keyof T) ))
139
- errors.push(`field ${fieldName} of type ${typeName} is non-existent primitive type ${fieldDef.value}`);
165
+ errors = errors.push(`field ${fieldName} of type ${typeName} is non-existent primitive type ${fieldDef.value}`);
140
166
  if (fieldDef.kind == "lookup" && !types.has(fieldDef.name))
141
- errors.push(`field ${fieldName} of type ${typeName} is non-existent type ${fieldDef.name}`);
142
- // if (fieldDef.kind == "application" && !builtIns.generics.has(fieldDef.value))
143
- // errors.push(`field ${fieldName} of type ${typeName} applies non-existent generic type ${fieldDef.value}`);
144
- // if (fieldDef.kind == "application" && fieldDef.args.some(argType => !builtIns.primitives.has(argType) && !types.has(argType)))
145
- // errors.push(`field ${fieldName} of type ${typeName} applies non-existent type arguments ${JSON.stringify(fieldDef.args.filter(argType => !builtIns.primitives.has(argType) && !types.has(argType)))}`);
167
+ errors = errors.push(`field ${fieldName} of type ${typeName} is non-existent type ${fieldDef.name}`);
146
168
  if (fieldDef.kind == "application" && fieldDef.value == "SingleSelection") {
147
169
  if (fieldDef.args.length != 1)
148
- errors.push(`field ${fieldName} in type ${typeName}: SingleSelection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
170
+ errors = errors.push(`field ${fieldName} in type ${typeName}: SingleSelection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
149
171
  else {
150
172
  const argType = types.get(fieldDef.args[0])!
173
+ if(argType == undefined){
174
+ errors = errors.push(`arg ${fieldDef.args[0]} in type ${typeName} references non existent type`);
175
+ return
176
+ }
151
177
  if (argType.extends.length != 1 || argType.extends[0] != "CollectionReference")
152
- errors.push(`field ${fieldName} in type ${typeName}: SingleSelection requires ${argType.name} to 'extend CollectionReference'`);
178
+ errors = errors.push(`field ${fieldName} in type ${typeName}: SingleSelection requires ${argType.name} to 'extend CollectionReference'`);
153
179
  }
154
180
  }
155
181
  if (fieldDef.kind == "application" && fieldDef.value == "Multiselection") {
156
182
  if (fieldDef.args.length != 1)
157
- errors.push(`field ${fieldName} in type ${typeName}: Multiselection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
183
+ errors = errors.push(`field ${fieldName} in type ${typeName}: Multiselection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
158
184
  else {
159
185
  const argType = types.get(fieldDef.args[0])!
186
+ if(argType == undefined){
187
+ errors = errors.push(`arg ${fieldDef.args[0]} in type ${typeName} references non existent type`);
188
+ return errors
189
+ }
160
190
  if (argType.extends.length != 1 || argType.extends[0] != "CollectionReference")
161
- errors.push(`field ${fieldName} in type ${typeName}: Multiselection requires ${argType.name} to 'extend CollectionReference'`);
191
+ errors = errors.push(`field ${fieldName} in type ${typeName}: Multiselection requires ${argType.name} to 'extend CollectionReference'`);
162
192
  }
163
193
  }
194
+ if (fieldDef.kind == "application" && fieldDef.value == "List") {
195
+ if (fieldDef.args.length != 1)
196
+ errors = errors.push(`field ${fieldName} in type ${typeName}: List should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`)
197
+ }
198
+ if (fieldDef.kind == "application" && fieldDef.value == "Map") {
199
+ if (fieldDef.args.length != 2)
200
+ errors = errors.push(`field ${fieldName} in type ${typeName}: Map should have exactly two type arguments, found ${JSON.stringify(fieldDef.args)}`)
201
+ }
164
202
  });
165
203
  });
166
204
 
167
- if ("forms" in formsConfig == false) {
168
- errors.push("the formsConfig does not contain a 'forms' field");
169
- return Sum.Default.right(errors);
170
- }
171
- if ("apis" in formsConfig == false) {
172
- errors.push("the formsConfig does not contain an 'apis' field");
173
- return Sum.Default.right(errors);
174
- }
175
- if ("mappings" in formsConfig == false) {
176
- errors.push("the formsConfig does not contain a 'mappings' field");
177
- return Sum.Default.right(errors);
178
- }
179
- if ("enumOptions" in formsConfig["apis"] == false) {
180
- errors.push("formsConfig.apis does not contain an 'enumOptions' field");
181
- return Sum.Default.right(errors);
182
- }
183
- if ("entities" in formsConfig["apis"] == false) {
184
- errors.push("formsConfig.apis does not contain an 'entities' field");
185
- return Sum.Default.right(errors);
186
- }
187
205
  let enums: Map<string, TypeName> = Map();
188
206
  Object.keys(formsConfig["apis"]["enumOptions"]).forEach((enumOptionsName: any) => {
189
207
  if (!types.has(formsConfig["apis"]["enumOptions"][enumOptionsName])) {
190
- errors.push(`formsConfig.apis.enumOptions refers to non-existent type ${formsConfig['apis']['enumOptions'][enumOptionsName]}`);
208
+ errors = errors.push(`formsConfig.apis.enumOptions refers to non-existent type ${formsConfig['apis']['enumOptions'][enumOptionsName]}`);
191
209
  } else {
192
210
  enums = enums.set(enumOptionsName, formsConfig["apis"]["enumOptions"][enumOptionsName])
193
211
  }
194
212
  })
213
+
195
214
  let streams: Map<string, TypeName> = Map();
196
215
  Object.keys(formsConfig["apis"]["searchableStreams"]).forEach((searchableStreamName: any) => {
197
216
  if (!types.has(formsConfig["apis"]["searchableStreams"][searchableStreamName])) {
198
- errors.push(`formsConfig.apis.searchableStreams refers to non-existent type ${formsConfig['apis']['searchableStreams'][searchableStreamName]}`);
217
+ errors = errors.push(`formsConfig.apis.searchableStreams refers to non-existent type ${formsConfig['apis']['searchableStreams'][searchableStreamName]}`);
199
218
  } else {
200
219
  streams = streams.set(searchableStreamName, formsConfig["apis"]["searchableStreams"][searchableStreamName])
201
220
  }
202
221
  })
222
+
203
223
  let entities: Map<string, EntityApi> = Map();
204
224
  Object.keys(formsConfig["apis"]["entities"]).forEach((entityApiName: any) => {
205
225
  const entityApiConfig = formsConfig["apis"]["entities"][entityApiName]
206
226
  if (!types.has(formsConfig["apis"]["entities"][entityApiName]["type"])) {
207
- errors.push(`formsConfig.apis.entities refers to non-existent type ${formsConfig['apis']['entities'][entityApiName]["type"]}`);
227
+ errors = errors.push(`formsConfig.apis.entities refers to non-existent type ${formsConfig['apis']['entities'][entityApiName]["type"]}`);
208
228
  } else {
209
229
  entities = entities.set(entityApiName, {
210
230
  type: entityApiConfig["type"],
@@ -222,23 +242,23 @@ export const FormsConfig = {
222
242
  Object.keys(formsConfig["mappings"]).forEach((mappingName: any) => {
223
243
  const mapping = formsConfig["mappings"][mappingName]
224
244
  if (!("source" in mapping && "target" in mapping && "paths" in mapping))
225
- errors.push(`formsConfig.mappings.${mappingName} does not have all the required fields ('source', 'target', and 'path')`);
245
+ errors = errors.push(`formsConfig.mappings.${mappingName} does not have all the required fields ('source', 'target', and 'path')`);
226
246
  else {
227
247
  const source = mapping.source as string
228
248
  const target = mapping.target as string
229
249
  const paths = mapping.paths
230
250
  if (!types.has(source))
231
- errors.push(`formsConfig.mappings.source refers to non-existent type ${source}`)
251
+ errors = errors.push(`formsConfig.mappings.source refers to non-existent type ${source}`)
232
252
  if (!types.has(target))
233
- errors.push(`formsConfig.mappings.target refers to non-existent type ${target}`)
253
+ errors = errors.push(`formsConfig.mappings.target refers to non-existent type ${target}`)
234
254
  const lookupPath = (source: Type, path: Array<string>): Type | undefined => {
235
255
  if (path.length <= 0) return source
236
256
  if (source.kind != "lookup" || !types.has(source.name)) {
237
- errors.push(`formsConfig.mappings ${JSON.stringify(source)} is not a lookup or does not exist`)
257
+ errors = errors.push(`formsConfig.mappings ${JSON.stringify(source)} is not a lookup or does not exist`)
238
258
  } else {
239
259
  const sourceTypeDef = types.get(source.name)!
240
260
  if (!sourceTypeDef.fields.has(path[0])) {
241
- errors.push(`path ${JSON.stringify(path)} refers to non-existent field ${path[0]}`)
261
+ errors = errors.push(`path ${JSON.stringify(path)} refers to non-existent field ${path[0]}`)
242
262
  }
243
263
  return lookupPath(sourceTypeDef.fields.get(path[0])!, path.slice(1))
244
264
  }
@@ -248,21 +268,21 @@ export const FormsConfig = {
248
268
  if (Array.isArray(path)) {
249
269
  const sourceType = lookupPath(source, path)
250
270
  if (!sourceType || Type.Operations.Equals(sourceType, target) == false) {
251
- errors.push(`path ${JSON.stringify(path)} connects different types ${JSON.stringify(sourceType)} and ${JSON.stringify(target)}`)
271
+ errors = errors.push(`path ${JSON.stringify(path)} connects different types ${JSON.stringify(sourceType)} and ${JSON.stringify(target)}`)
252
272
  }
253
273
  } else {
254
274
  if (target.kind != "lookup" || !types.has(target.name)) {
255
- errors.push(`path ${JSON.stringify(path)} refers to an object but ${JSON.stringify(target)} is not`)
275
+ errors = errors.push(`path ${JSON.stringify(path)} refers to an object but ${JSON.stringify(target)} is not`)
256
276
  } else {
257
277
  const targetTypeDef = types.get(target.name)!
258
278
  const pathFieldNames = Set(Object.keys(path))
259
279
  const targetFieldNames = targetTypeDef.fields.keySeq().toSet()
260
280
  if (!pathFieldNames.equals(targetFieldNames))
261
- errors.push(`path ${JSON.stringify(pathFieldNames.toArray())} does not cover all fields of target ${JSON.stringify(targetFieldNames.toArray())}, ${JSON.stringify(targetFieldNames.subtract(pathFieldNames).toArray())} are missing`)
281
+ errors = errors.push(`path ${JSON.stringify(pathFieldNames.toArray())} does not cover all fields of target ${JSON.stringify(targetFieldNames.toArray())}, ${JSON.stringify(targetFieldNames.subtract(pathFieldNames).toArray())} are missing`)
262
282
  else
263
283
  Object.keys(path).forEach(fieldName => {
264
284
  if (!targetTypeDef.fields.has(fieldName)) {
265
- errors.push(`path ${JSON.stringify(path)} refers to non-existing field ${fieldName} on ${JSON.stringify(target)} is not`)
285
+ errors = errors.push(`path ${JSON.stringify(path)} refers to non-existing field ${fieldName} on ${JSON.stringify(target)} is not`)
266
286
  }
267
287
  mappingPathValidator(source, targetTypeDef.fields.get(fieldName)!, path[fieldName])
268
288
  })
@@ -280,33 +300,33 @@ export const FormsConfig = {
280
300
  forms = forms.set(formName, formDef);
281
301
  const configFormDef = formsConfig["forms"][formName];
282
302
  if ("type" in configFormDef == false) {
283
- errors.push(`form ${formName} is missing the required 'type' attribute`);
303
+ errors = errors.push(`form ${formName} is missing the required 'type' attribute`);
284
304
  } else {
285
305
  if (types.has(configFormDef["type"])) {
286
306
  formDef.type = configFormDef["type"];
287
307
  formDef.typeDef = types.get(configFormDef["type"])!
288
308
  } else
289
- errors.push(`form ${formName} references non-existing type ${configFormDef["type"]}`);
309
+ errors = errors.push(`form ${formName} references non-existing type ${configFormDef["type"]}`);
290
310
  }
291
311
  const formTypeDef = types.get(configFormDef["type"])
292
312
  if ("fields" in configFormDef == false) {
293
- errors.push(`form ${formName} is missing the required 'fields' attribute`);
313
+ errors = errors.push(`form ${formName} is missing the required 'fields' attribute`);
294
314
  } else {
295
315
  formTypeDef?.fields.forEach((fieldType, fieldName) => {
296
316
  if (!Object.keys(configFormDef["fields"]).includes(fieldName))
297
- errors.push(`form ${formName} is missing a renderer for field ${fieldName} which is defined on type ${formDef.type}`);
317
+ errors = errors.push(`form ${formName} is missing a renderer for field ${fieldName} which is defined on type ${formDef.type}`);
298
318
  })
299
319
  Object.keys(configFormDef["fields"]).forEach(fieldName => {
300
320
  const fieldConfig = configFormDef["fields"][fieldName]
301
321
  if (!formTypeDef?.fields.has(fieldName)) {
302
- errors.push(`form ${formName} references field ${fieldName} which does not exist on type ${formDef.type}`);
322
+ errors = errors.push(`form ${formName} references field ${fieldName} which does not exist on type ${formDef.type}`);
303
323
  } else {
304
324
  if ("renderer" in fieldConfig == false) {
305
- errors.push(`field ${fieldName} of form ${formName} has no 'renderer' attribute`);
325
+ errors = errors.push(`field ${fieldName} of form ${formName} has no 'renderer' attribute`);
306
326
  }
307
327
  const fieldTypeDef = formTypeDef?.fields.get(fieldName)
308
328
  if (!fieldTypeDef)
309
- errors.push(`field ${fieldName} of form ${formName} has no corresponding type `);
329
+ errors = errors.push(`field ${fieldName} of form ${formName} has no corresponding type `);
310
330
  }
311
331
  })
312
332
  }
@@ -316,66 +336,64 @@ export const FormsConfig = {
316
336
  if (fieldTypeDef?.kind == "primitive") {
317
337
  if(injectedPrimitives?.injectedPrimitives.has(fieldTypeDef.value as keyof T)){
318
338
  if (!injectedPrimitives.renderers[fieldTypeDef.value as keyof T].has(fieldConfig["renderer"]))
319
- errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
339
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
320
340
  }
321
341
  else if (fieldTypeDef.value == "maybeBoolean") {
322
- // alert(JSON.stringify(fieldConfig["renderer"]))
323
- // alert(JSON.stringify(builtIns.renderers.MaybeBooleanViews))
324
342
  if (!builtIns.renderers.maybeBoolean.has(fieldConfig["renderer"]))
325
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
343
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
326
344
  } else if (fieldTypeDef.value == "boolean") {
327
345
  if (!builtIns.renderers.boolean.has(fieldConfig["renderer"]))
328
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
346
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
329
347
  } else if (fieldTypeDef.value == "number") {
330
348
  if (!builtIns.renderers.number.has(fieldConfig["renderer"]))
331
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
349
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
332
350
  } else if (fieldTypeDef.value == "string") {
333
351
  if (!builtIns.renderers.string.has(fieldConfig["renderer"]))
334
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
352
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
335
353
  } else if (fieldTypeDef.value == "Date") {
336
354
  if (!builtIns.renderers.date.has(fieldConfig["renderer"])) {
337
- alert(JSON.stringify(builtIns.renderers))
338
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
355
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
339
356
  }
340
357
  } else {
341
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
358
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
342
359
  }
343
360
  if(injectedPrimitives?.injectedPrimitives.has(fieldTypeDef.value as keyof T)){
344
361
  if (!injectedPrimitives.renderers[fieldTypeDef.value as keyof T].has(fieldConfig["renderer"]))
345
- errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
362
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
346
363
  }
347
364
  } else if (fieldTypeDef?.kind == "application") {
348
365
  if (fieldTypeDef?.value == "SingleSelection") {
349
366
  if (!builtIns.renderers.enumSingleSelection.has(fieldConfig["renderer"]) &&
350
367
  !builtIns.renderers.streamSingleSelection.has(fieldConfig["renderer"]))
351
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
368
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
352
369
  } else if (fieldTypeDef?.value == "Multiselection") {
353
370
  if (!builtIns.renderers.enumMultiSelection.has(fieldConfig["renderer"]) &&
354
371
  !builtIns.renderers.streamMultiSelection.has(fieldConfig["renderer"]))
355
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
372
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
356
373
  } else if (fieldTypeDef?.value == "List") {
357
374
  if (!builtIns.renderers.list.has(fieldConfig["renderer"]) ){
358
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`)
375
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`)
359
376
  }
360
377
  } else if (fieldTypeDef?.value == "Map") {
361
378
  if (!builtIns.renderers.map.has(fieldConfig["renderer"]))
362
- errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
379
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
363
380
  if ("keyRenderer" in fieldConfig != true || "valueRenderer" in fieldConfig != true)
364
- errors.push(`field ${fieldName} of form ${formName} must have both a keyRenderer and a valueRenderer`);
381
+ errors = errors.push(`field ${fieldName} of form ${formName} must have both a keyRenderer and a valueRenderer`);
365
382
  if (fieldTypeDef.args.length != 2)
366
- errors.push(`field ${fieldName} of form ${formName} should have exactly two type arguments`);
383
+ errors = errors.push(`field ${fieldName} of form ${formName} should have exactly two type arguments`);
367
384
  else {
368
385
  const typeDefToType = (typeDef:any) : Type | undefined =>
369
- "fun" in typeDef == false ? undefined
386
+ "kind" in typeDef == false ? undefined
387
+ : "value" in typeDef == false ? undefined
370
388
  : "args" in typeDef == false ? undefined
371
389
  : Array.isArray(typeDef.args) == false ? undefined
372
- : Type.Default.application(typeDef.fun, typeDef.args)
390
+ : Type.Default.application(typeDef.fun, typeDef.args.map((arg:any) => (typeof arg == "string" ? arg : { kind:"application", value: arg.fun, args: arg.args })) as any)
373
391
  const keyType:Type | undefined = typeof fieldTypeDef.args[0] == "string" ? Type.Operations.FromName(types, builtIns, injectedPrimitives)(fieldTypeDef.args[0]) : typeDefToType(fieldTypeDef.args[0] as any)
374
392
  const valueType:Type | undefined = typeof fieldTypeDef.args[1] == "string" ? Type.Operations.FromName(types, builtIns, injectedPrimitives)(fieldTypeDef.args[1]) : typeDefToType(fieldTypeDef.args[1] as any)
375
393
  if (!keyType) {
376
- errors.push(`field ${fieldName} of form ${formName} references non-existing key type ${JSON.stringify(fieldTypeDef.args[0])}`);
394
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing key type ${JSON.stringify(fieldTypeDef.args[0])}`);
377
395
  } else if (!valueType) {
378
- errors.push(`field ${fieldName} of form ${formName} references non-existing value type ${JSON.stringify(fieldTypeDef.args[1])}`);
396
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing value type ${JSON.stringify(fieldTypeDef.args[1])}`);
379
397
  } else {
380
398
  rendererMatchesType(formName, fieldName)(keyType, fieldConfig.keyRenderer)
381
399
  rendererMatchesType(formName, fieldName)(valueType, fieldConfig.valueRenderer)
@@ -383,42 +401,41 @@ export const FormsConfig = {
383
401
  }
384
402
  }
385
403
  if (fieldTypeDef.args.length < 1)
386
- errors.push(`field ${fieldName} of form ${formName} should have one type argument}`);
404
+ errors = errors.push(`field ${fieldName} of form ${formName} should have one type argument}`);
387
405
  else if (
388
406
  (builtIns.renderers.list.has(fieldConfig["renderer"])) && "elementRenderer" in fieldConfig != true)
389
- errors.push(`field ${fieldName} of form ${formName} is missing the 'elementRenderer' property`);
407
+ errors = errors.push(`field ${fieldName} of form ${formName} is missing the 'elementRenderer' property`);
390
408
  else if (
391
409
  (builtIns.renderers.enumMultiSelection.has(fieldConfig["renderer"]) ||
392
410
  builtIns.renderers.enumSingleSelection.has(fieldConfig["renderer"])) && "options" in fieldConfig != true)
393
- errors.push(`field ${fieldName} of form ${formName} is missing the 'options' property`);
411
+ errors = errors.push(`field ${fieldName} of form ${formName} is missing the 'options' property`);
394
412
  else if (
395
413
  (builtIns.renderers.streamSingleSelection.has(fieldConfig["renderer"]) ||
396
414
  builtIns.renderers.streamMultiSelection.has(fieldConfig["renderer"])) && "stream" in fieldConfig != true)
397
- errors.push(`field ${fieldName} of form ${formName} is missing the 'stream' property`);
415
+ errors = errors.push(`field ${fieldName} of form ${formName} is missing the 'stream' property`);
398
416
  else if ((builtIns.renderers.enumMultiSelection.has(fieldConfig["renderer"]) ||
399
417
  builtIns.renderers.enumSingleSelection.has(fieldConfig["renderer"])) && (enums.get(fieldConfig["options"])) != fieldTypeDef.args[0]) {
400
418
  if (enums.has(fieldConfig["options"]))
401
- errors.push(`field ${fieldName} of form ${formName} references an enum api with type ${enums.get(fieldConfig["options"])} when ${fieldTypeDef.args[0]} was expected`);
419
+ errors = errors.push(`field ${fieldName} of form ${formName} references an enum api with type ${enums.get(fieldConfig["options"])} when ${fieldTypeDef.args[0]} was expected`);
402
420
  else
403
- errors.push(`field ${fieldName} of form ${formName} references a non-existing enum api`);
421
+ errors = errors.push(`field ${fieldName} of form ${formName} references a non-existing enum api`);
404
422
  } else if ((builtIns.renderers.streamMultiSelection.has(fieldConfig["renderer"]) ||
405
423
  builtIns.renderers.streamSingleSelection.has(fieldConfig["renderer"])) && (streams.get(fieldConfig["stream"])) != fieldTypeDef.args[0]) {
406
424
  if (streams.has(fieldConfig["stream"]))
407
- errors.push(`field ${fieldName} of form ${formName} references an api with type ${streams.get(fieldConfig["stream"])} when ${fieldTypeDef.args[0]} was expected`);
425
+ errors = errors.push(`field ${fieldName} of form ${formName} references an api with type ${streams.get(fieldConfig["stream"])} when ${fieldTypeDef.args[0]} was expected`);
408
426
  else
409
- errors.push(`field ${fieldName} of form ${formName} references a non-existing stream api`);
427
+ errors = errors.push(`field ${fieldName} of form ${formName} references a non-existing stream api`);
410
428
  }
411
429
  } else {
412
430
  const formTypeDef = types.get(fieldTypeDef.name)
413
431
  if (!formTypeDef) {
414
- alert(JSON.stringify([fieldName, fieldTypeDef, fieldConfig]))
415
- errors.push(`field ${fieldName} of form ${formName} references a non-existing type ${fieldTypeDef.name}`);
432
+ errors = errors.push(`field ${fieldName} of form ${formName} references a non-existing type ${fieldTypeDef.name}`);
416
433
  } else {
417
434
  const form = forms.get(fieldConfig.renderer ?? "")
418
435
  if (!form) {
419
- errors.push(`field ${fieldName} of form ${formName} references non-existing form ${fieldConfig.renderer}`);
436
+ errors = errors.push(`field ${fieldName} of form ${formName} references non-existing form ${fieldConfig.renderer}`);
420
437
  } else if (fieldTypeDef.name != form.typeDef.name) {
421
- errors.push(`field ${fieldName} of form ${formName} expected renderer for ${fieldTypeDef.name} but instead found a renderer for ${form.typeDef.name}`);
438
+ errors = errors.push(`field ${fieldName} of form ${formName} expected renderer for ${fieldTypeDef.name} but instead found a renderer for ${form.typeDef.name}`);
422
439
  }
423
440
  }
424
441
  }
@@ -464,15 +481,15 @@ export const FormsConfig = {
464
481
  }
465
482
  let elementErrors = rendererHasType(elementRenderer, elementType)
466
483
  if (elementErrors.length > 0)
467
- errors.push(...elementErrors)
484
+ errors = errors.push(...elementErrors)
468
485
  }
469
486
  if (fieldTypeDef?.kind == "lookup") {
470
487
  if (!forms.has(fieldConfig.renderer))
471
- errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references non-existing form ${fieldConfig["renderer"]}`);
488
+ errors = errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references non-existing form ${fieldConfig["renderer"]}`);
472
489
  else {
473
490
  const otherForm = forms.get(fieldConfig.renderer)!
474
491
  if (otherForm.type != fieldTypeDef.name)
475
- errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references form ${fieldConfig["renderer"]}, which has type ${otherForm.type} whereas ${fieldTypeDef.name} was expected`);
492
+ errors = errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references form ${fieldConfig["renderer"]}, which has type ${otherForm.type} whereas ${fieldTypeDef.name} was expected`);
476
493
  }
477
494
  }
478
495
  formDef.fields = formDef.fields.set(
@@ -498,23 +515,22 @@ export const FormsConfig = {
498
515
 
499
516
  Object.keys(formsConfig["forms"]).forEach((formName: any) => {
500
517
  let formDef = forms.get(formName)!
501
- // const formTypeDef = types.get(formDef.type)
502
518
  const configFormDef = formsConfig["forms"][formName];
503
519
  if ("tabs" in configFormDef == false)
504
- errors.push(`form ${formName} is missing required attribute 'tabs'`);
520
+ errors = errors.push(`form ${formName} is missing required attribute 'tabs'`);
505
521
  else {
506
522
  let tabs: FormLayout = OrderedMap()
507
523
  Object.keys(configFormDef.tabs).forEach(tabName => {
508
524
  const tabConfig = configFormDef.tabs[tabName]
509
525
  if ("columns" in tabConfig == false)
510
- errors.push(`tab ${tabName} in form ${formName} is missing required attribute 'columns'`);
526
+ errors = errors.push(`tab ${tabName} in form ${formName} is missing required attribute 'columns'`);
511
527
  else {
512
528
  let cols: TabLayout = { columns: OrderedMap() }
513
529
  tabs = tabs.set(tabName, cols)
514
530
  Object.keys(tabConfig.columns).forEach(colName => {
515
531
  const colConfig = tabConfig.columns[colName]
516
532
  if ("groups" in colConfig == false)
517
- errors.push(`column ${colName} in tab ${tabName} in form ${formName} is missing required attribute 'groups'`);
533
+ errors = errors.push(`column ${colName} in tab ${tabName} in form ${formName} is missing required attribute 'groups'`);
518
534
  else {
519
535
  let column: ColumnLayout = { groups: OrderedMap() }
520
536
  cols.columns = cols.columns.set(colName, column)
@@ -523,11 +539,11 @@ export const FormsConfig = {
523
539
  let group: GroupLayout = []
524
540
  column.groups = column.groups.set(groupName, group)
525
541
  if (!Array.isArray(groupConfig))
526
- errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} should be an array of field names`);
542
+ errors = errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} should be an array of field names`);
527
543
  else
528
544
  groupConfig.forEach((fieldName: any) => {
529
545
  if (!formDef.fields.has(fieldName))
530
- errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} references non-existing field '${fieldName}'`);
546
+ errors = errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} references non-existing field '${fieldName}'`);
531
547
  else {
532
548
  group.push(fieldName)
533
549
  }
@@ -541,40 +557,39 @@ export const FormsConfig = {
541
557
  }
542
558
  });
543
559
 
544
- if ("launchers" in formsConfig == false) {
545
- errors.push("the formsConfig does not contain a 'launchers' field");
546
- return Sum.Default.right(errors);
547
- }
548
560
  let launchers: FormsConfig["launchers"] = {
549
561
  create: Map<string, Launcher>(),
550
562
  edit: Map<string, Launcher>(),
551
563
  mappings: Map<string, MappingLauncher>(),
552
564
  }
553
565
  Object.keys(formsConfig["launchers"]).forEach((launcherName: any) => {
554
- // let launcherConfig = formsConfig["launchers"][launcherName]
555
566
  const launcherKinds = ["create", "edit", "mapping"]
556
567
  if (launcherKinds.includes(formsConfig["launchers"][launcherName]["kind"]) == false) {
557
- errors.push(`launcher '${launcherName}' has invalid 'kind': expected any of ${JSON.stringify(launcherKinds)}`);
568
+ errors = errors.push(`launcher '${launcherName}' has invalid 'kind': expected any of ${JSON.stringify(launcherKinds)}`);
558
569
  return
559
570
  }
560
571
  const launcherKind = formsConfig["launchers"][launcherName]["kind"] as Launcher["kind"] | MappingLauncher["kind"]
561
- if (forms.has(formsConfig["launchers"][launcherName]["form"]) == false)
562
- errors.push(`launcher '${launcherName}' references non-existing form '${formsConfig.launchers[launcherName].form}'`);
572
+ if (forms.has(formsConfig["launchers"][launcherName]["form"]) == false) {
573
+ errors = errors.push(`launcher '${launcherName}' references non-existing form '${formsConfig.launchers[launcherName].form}'`);
574
+ return
575
+ }
563
576
  const form = forms.get(formsConfig["launchers"][launcherName]["form"])!
564
577
  if (launcherKind != "mapping") {
565
- if (entities.has(formsConfig["launchers"][launcherName]["api"]) == false)
566
- errors.push(`launcher '${launcherName}' references non-existing entity api '${formsConfig.launchers[launcherName].api}'`);
578
+ if (entities.has(formsConfig["launchers"][launcherName]["api"]) == false) {
579
+ errors = errors.push(`launcher '${launcherName}' references non-existing entity api '${formsConfig.launchers[launcherName].api}'`);
580
+ return
581
+ }
567
582
  const api = entities.get(formsConfig["launchers"][launcherName]["api"])!
568
583
  if (form.type != api.type)
569
- errors.push(`form and api in launcher '${launcherName}' reference different types '${form.type}' and '${api.type}'`);
584
+ errors = errors.push(`form and api in launcher '${launcherName}' reference different types '${form.type}' and '${api.type}'`);
570
585
  if (formsConfig["launchers"][launcherName]["kind"] == "create" &&
571
586
  !(api.methods.create && api.methods.default)
572
587
  )
573
- errors.push(`launcher '${launcherName}' requires api methods 'create' and 'default'`);
588
+ errors = errors.push(`launcher '${launcherName}' requires api methods 'create' and 'default'`);
574
589
  if (formsConfig["launchers"][launcherName]["kind"] == "edit" &&
575
590
  !(api.methods.get && api.methods.update)
576
591
  )
577
- errors.push(`launcher '${launcherName}' requires api methods 'get' and 'update'`);
592
+ errors = errors.push(`launcher '${launcherName}' requires api methods 'get' and 'update'`);
578
593
  let launcher: Launcher = {
579
594
  name: launcherName,
580
595
  kind: formsConfig["launchers"][launcherName]["kind"],
@@ -589,24 +604,24 @@ export const FormsConfig = {
589
604
  const launcherConfig = formsConfig["launchers"][launcherName]
590
605
  const mappingName = launcherConfig["mapping"]
591
606
  if (mappingName in formsConfig["mappings"] == false)
592
- errors.push(`launcher '${launcherName}' references non-existing mapping '${mappingName}'`);
607
+ errors = errors.push(`launcher '${launcherName}' references non-existing mapping '${mappingName}'`);
593
608
  else {
594
609
  const mapping = formsConfig["mappings"][mappingName]
595
610
  if (mapping["target"] != form.type)
596
- errors.push(`launcher '${launcherName}' has a form over '${form.type}' but a mapping to a different type '${mapping["target"]}'`);
611
+ errors = errors.push(`launcher '${launcherName}' has a form over '${form.type}' but a mapping to a different type '${mapping["target"]}'`);
597
612
  else
598
613
  launchers.mappings = launchers.mappings.set(launcherName, launcherConfig)
599
614
  }
600
615
  }
601
616
  })
602
617
 
603
- if (errors.length > 0) {
618
+ if (errors.size > 0) {
604
619
  console.error("parsing errors")
605
620
  console.error(errors)
606
- return Sum.Default.right(errors);
621
+ return ValueOrErrors.Default.throw(errors);
607
622
  }
608
623
 
609
- return Sum.Default.left({
624
+ return ValueOrErrors.Default.return({
610
625
  types: types,
611
626
  forms: forms,
612
627
  apis: {
@@ -1,5 +1,5 @@
1
1
  import { List, Map, OrderedMap, OrderedSet, Set } from "immutable";
2
- import { BoolExpr, Unit, Guid, LeafPredicatesEvaluators, Predicate, FormsConfig, BuiltIns, FormDef, Sum, BasicFun, Template, unit, EditFormState, EditFormTemplate, ApiErrors, CreateFormTemplate, EntityFormTemplate, SharedFormState, CreateFormState, Entity, EditFormContext, CreateFormContext, MappedEntityFormTemplate, Mapping, Synchronized, simpleUpdater, TypeName, ListFieldState, ListForm, TypeDefinition, BuiltInApiConverters, defaultValue, fromAPIRawValue, toAPIRawValue, EditFormForeignMutationsExpected, MapFieldState, MapForm, Type, FieldConfig, Base64FileForm, SecretForm, InjectedPrimitives, Injectables, ApiConverters, Maybe } from "../../../../main";
2
+ import { BoolExpr, Unit, Guid, LeafPredicatesEvaluators, Predicate, FormsConfig, BuiltIns, FormDef, Sum, BasicFun, Template, unit, EditFormState, EditFormTemplate, ApiErrors, CreateFormTemplate, EntityFormTemplate, SharedFormState, CreateFormState, Entity, EditFormContext, CreateFormContext, MappedEntityFormTemplate, Mapping, Synchronized, simpleUpdater, TypeName, ListFieldState, ListForm, TypeDefinition, BuiltInApiConverters, defaultValue, fromAPIRawValue, toAPIRawValue, EditFormForeignMutationsExpected, MapFieldState, MapForm, Type, FieldConfig, Base64FileForm, SecretForm, InjectedPrimitives, Injectables, ApiConverters, Maybe, FormValidationError, FormConfigValidationAndParseResult } from "../../../../main";
3
3
  import { Value } from "../../../value/state";
4
4
  import { CollectionReference } from "../collection/domains/reference/state";
5
5
  import { CollectionSelection } from "../collection/domains/selection/state";
@@ -209,7 +209,7 @@ export const ParseForm = <T,>(
209
209
  .mapContext<any>(_ => ({ ..._, label, tooltip }))
210
210
  } else { // the list argument is a primitive
211
211
  const elementForm = FieldView(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, elementLabel, elementTooltip, EnumOptionsSources, fieldsOptionsConfig, leafPredicates, injectedPrimitives)
212
- const initialFormState = FieldFormState(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, InfiniteStreamSources, fieldsInfiniteStreamsConfig, injectedPrimitives);
212
+ const initialFormState = FieldFormState(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig, injectedPrimitives);
213
213
  formConfig[fieldName] = ListForm<any, any, any & FormLabel, Unit>(
214
214
  { Default: () => initialFormState },
215
215
  { Default: () => initialElementValue },
@@ -220,21 +220,12 @@ export const ParseForm = <T,>(
220
220
  } else {
221
221
  if (viewType == "map") {
222
222
  const field = type.fields.get(fieldName)!
223
-
224
- const parseType = (t:any) : TypeName | Type | undefined => {
225
- if (typeof t == "string") return t
226
- if ("fun" in t && "args" in t && Array.isArray(t.args)) {
227
- return { kind:"application", value:t.fun, args:t.args }
228
- }
229
- return undefined
230
- }
231
-
232
- const [keyType, valueType] = field.kind == "application" ? [parseType(field.args[0]), parseType(field.args[1])] : (() => {
233
- throw `error processing field type ${JSON.stringify(field)} of ${fieldName}`
234
- })()
235
- if (!keyType || !valueType) {
223
+ // TODO - reconsider error handling approach here in general
224
+ if(field.kind != "application") {
236
225
  throw `error processing field type ${JSON.stringify(field)} of ${fieldName}`
237
226
  }
227
+
228
+ const [keyType, valueType] = [field.args[0], field.args[1]]
238
229
  const initialKeyValue = defaultValue(keyType)
239
230
  const initialValueValue = defaultValue(valueType)
240
231
  const getFormAndInitialState = (elementRenderers:any, rendererName:any, fieldConfig:FieldConfig) => {
@@ -314,7 +305,7 @@ export type EditLauncherContext<Entity, FormState, ExtraContext> =
314
305
  extraContext: ExtraContext,
315
306
  containerFormView: any,
316
307
  submitButtonWrapper: any
317
- }, "api" | "actualForm">
308
+ }, "api" | "parser" | "actualForm">
318
309
 
319
310
  export type CreateLauncherContext<Entity, FormState, ExtraContext> =
320
311
  Omit<
@@ -350,7 +341,7 @@ export type ParsedLaunchers = {
350
341
  }>
351
342
  }
352
343
  export type ParsedForms = Map<string, ParsedForm & { form: EntityFormTemplate<any, any, any, any, any> }>
353
- export type FormParsingErrors = Array<string>
344
+ export type FormParsingErrors = List<string>
354
345
  export type FormParsingResult = Sum<ParsedLaunchers, FormParsingErrors>
355
346
  export type StreamName = string
356
347
  export type InfiniteStreamSources = BasicFun<StreamName, SearchableInfiniteStreamState<CollectionReference>["getChunk"]>
@@ -379,7 +370,7 @@ export const parseForms =
379
370
  leafPredicates: LeafPredicates) =>
380
371
  (formsConfig: FormsConfig):
381
372
  FormParsingResult => {
382
- let errors: FormParsingErrors = []
373
+ let errors: FormParsingErrors = List()
383
374
  let seen = Set<string>()
384
375
  let formProcessingOrder = OrderedSet<string>()
385
376
 
@@ -474,7 +465,7 @@ export const parseForms =
474
465
  }
475
466
  })
476
467
 
477
- if (errors.length > 0) {
468
+ if (errors.size > 0) {
478
469
  return Sum.Default.right(errors)
479
470
  }
480
471
 
@@ -486,9 +477,8 @@ export const parseForms =
486
477
  get: (id: string) => entityApis.get(launcher.api)(id).then((raw: any) => {
487
478
  return fromAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(raw)
488
479
  }),
489
- update: (id: Guid, value: any, formState: any) => {
490
- const raw = toAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(value, formState)
491
- return raw.kind == "errors" ? Promise.reject(raw.errors) : entityApis.update(launcher.api)(id, raw.value)
480
+ update: (id: any, parsed: any) => {
481
+ return entityApis.update(launcher.api)(id, parsed)
492
482
  }
493
483
  }
494
484
  parsedLaunchers.edit = parsedLaunchers.edit.set(
@@ -498,6 +488,7 @@ export const parseForms =
498
488
  ({
499
489
  ...parentContext,
500
490
  api: api,
491
+ parser: (value: any, formState: any) => toAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(value, formState),
501
492
  actualForm: form.withView(containerFormView).mapContext((_: any) => ({ ..._, rootValue: _.value, ...parentContext.extraContext }))
502
493
  }) as any)
503
494
  .withViewFromProps(props => props.context.submitButtonWrapper)
@@ -565,7 +556,7 @@ export const parseForms =
565
556
  )
566
557
  })
567
558
 
568
- if (errors.length > 0) {
559
+ if (errors.size > 0) {
569
560
  return Sum.Default.right(errors)
570
561
  }
571
562
  return Sum.Default.left(parsedLaunchers)
@@ -8,44 +8,44 @@ import { ListFieldState, ListFieldView } from "./state";
8
8
 
9
9
  export const ListForm = <Element, ElementFormState, Context extends FormLabel, ForeignMutationsExpected>(
10
10
  ElementFormState: { Default: () => ElementFormState },
11
- Element: { Default: () => Element },
11
+ Element: { Default: () => Element | undefined},
12
12
  elementTemplate: Template<
13
- Context & Value<Element> & ElementFormState,
13
+ Context & Value<Element | undefined> & ElementFormState,
14
14
  ElementFormState,
15
15
  ForeignMutationsExpected & {
16
- onChange: OnChange<Element>;
16
+ onChange: OnChange<Element | undefined>;
17
17
  }>,
18
- validation?: BasicFun<List<Element>, Promise<FieldValidation>>,
18
+ validation?: BasicFun<List<Element | undefined>, Promise<FieldValidation>>,
19
19
  ) => {
20
20
  const embeddedElementTemplate = (elementIndex:number) =>
21
21
  elementTemplate
22
22
  .mapForeignMutationsFromProps<ForeignMutationsExpected & {
23
- onChange: OnChange<List<Element>>;
23
+ onChange: OnChange<List<Element| undefined>>;
24
24
  add: SimpleCallback<Unit>;
25
- remove: SimpleCallback<number>;}>((props): ForeignMutationsExpected & {onChange: OnChange<Element>} => ({
25
+ remove: SimpleCallback<number>;}>((props): ForeignMutationsExpected & {onChange: OnChange<Element | undefined>} => ({
26
26
  ...props.foreignMutations,
27
27
  onChange: (elementUpdater, path) => {
28
- props.foreignMutations.onChange(Updater((elements:List<Element>) =>
29
- elements.update(elementIndex, (_:Element | undefined) => _ == undefined ? _ : elementUpdater(_))), path)
28
+ props.foreignMutations.onChange(Updater((elements:List<Element | undefined>) =>
29
+ elements.has(elementIndex) ? elements.update(elementIndex, undefined, elementUpdater) : elements), path)
30
30
  props.setState(_ => ({..._,
31
31
  modifiedByUser:true,
32
32
  })) },
33
33
  add: (newElement: Element) => { },
34
34
  remove: (elementIndex: number) => { }
35
35
  }))
36
- .mapContext((_: Context & Value<List<Element>> & ListFieldState<Element, ElementFormState>) : (Context & Value<Element> & ElementFormState) | undefined => {
36
+ .mapContext((_: Context & Value<List<Element | undefined>> & ListFieldState<Element | undefined, ElementFormState>) : (Context & Value<Element | undefined> & ElementFormState) | undefined => {
37
+ if(!_.value.has(elementIndex)) return undefined
37
38
  const element = _.value.get(elementIndex)
38
- if (element == undefined) return undefined
39
39
  const elementFormState = _.elementFormStates.get(elementIndex) || ElementFormState.Default()
40
- const elementContext : Context & Value<Element> & ElementFormState = ({ ..._, ...elementFormState, value: element })
40
+ const elementContext : Context & Value<Element | undefined> & ElementFormState = ({ ..._, ...elementFormState, value: element })
41
41
  return elementContext
42
42
  })
43
- .mapState((_:BasicUpdater<ElementFormState>) : Updater<ListFieldState<Element, ElementFormState>> =>
44
- ListFieldState<Element, ElementFormState>().Updaters.Core.elementFormStates(
43
+ .mapState((_:BasicUpdater<ElementFormState>) : Updater<ListFieldState<Element | undefined, ElementFormState>> =>
44
+ ListFieldState<Element | undefined, ElementFormState>().Updaters.Core.elementFormStates(
45
45
  MapRepo.Updaters.upsert(elementIndex, () => ElementFormState.Default(), _)
46
46
  ))
47
- return Template.Default<Context & Value<List<Element>> & { disabled: boolean }, ListFieldState<Element, ElementFormState>, ForeignMutationsExpected & { onChange: OnChange<List<Element>>; },
48
- ListFieldView<Element, ElementFormState, Context, ForeignMutationsExpected>>(props => <>
47
+ return Template.Default<Context & Value<List<Element | undefined>> & { disabled: boolean }, ListFieldState<Element | undefined, ElementFormState>, ForeignMutationsExpected & { onChange: OnChange<List<Element | undefined>>; },
48
+ ListFieldView<Element | undefined, ElementFormState, Context, ForeignMutationsExpected>>(props => <>
49
49
  <props.view {...props}
50
50
  context={{
51
51
  ...props.context,
@@ -54,7 +54,7 @@ export const ListForm = <Element, ElementFormState, Context extends FormLabel, F
54
54
  ...props.foreignMutations,
55
55
  add: (_) => {
56
56
  props.foreignMutations.onChange(
57
- ListRepo.Updaters.push(Element.Default()), List()
57
+ ListRepo.Updaters.push<Element | undefined>(Element.Default()), List()
58
58
  )
59
59
  },
60
60
  remove: (_) => {
@@ -67,7 +67,7 @@ export const ListForm = <Element, ElementFormState, Context extends FormLabel, F
67
67
  />
68
68
  </>
69
69
  ).any([
70
- ValidateRunner<Context & { disabled: boolean }, ListFieldState<Element, ElementFormState>, ForeignMutationsExpected, List<Element>>(
70
+ ValidateRunner<Context & { disabled: boolean }, ListFieldState<Element | undefined, ElementFormState>, ForeignMutationsExpected, List<Element | undefined>>(
71
71
  validation ? _ => validation(_).then(FieldValidationWithPath.Default.fromFieldValidation) : undefined
72
72
  ),
73
73
  ]);