nesoi 3.0.19 → 3.0.21

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.
@@ -1,6 +1,6 @@
1
1
  import { $Module, $Space } from "../../schema";
2
2
  import { $BlockOutput, $BlockType } from './block.schema';
3
- import { MultiMessageTemplateDef } from "../entities/message/template/message_template.builder";
3
+ import { MessageTemplateDef, MultiMessageTemplateDef } from "../entities/message/template/message_template.builder";
4
4
  import { $Dependency, BuilderNode } from "../../engine/dependency";
5
5
  /**
6
6
  * @category Builders
@@ -19,11 +19,20 @@ export declare abstract class BlockBuilder<Space extends $Space, Module extends
19
19
  /** Block "human" name */
20
20
  as(alias: string): this;
21
21
  /**
22
+ * Inline messages. This messages is exposed to the module,
23
+ * with a name prefixed by the block name.
24
+ * @param def A method which takes a field factory as input and outputs a template builder
25
+ * @returns The Builder, for call-chaining
26
+ */
27
+ protected message<Name extends string, Def extends MessageTemplateDef<Space, Module, Name>>(name: Name, def: Def): unknown;
28
+ /**
29
+ * @deprecated
22
30
  * Inline messages. These messages are exposed to the module,
23
31
  * with a name prefixed by the block name.
24
32
  * @param def A method which takes a field factory as input and outputs a template builder
25
33
  * @returns The Builder, for call-chaining
26
34
  */
35
+ /** @deprecated Use `.message` instead. Will be removed on 3.1 */
27
36
  protected messages<Def extends MultiMessageTemplateDef<Space, Module>>(def: Def): unknown;
28
37
  authn(...providers: string[]): unknown;
29
38
  protected _input(...names: string[]): unknown;
@@ -26,11 +26,37 @@ class BlockBuilder {
26
26
  }
27
27
  // Inline Messages
28
28
  /**
29
+ * Inline messages. This messages is exposed to the module,
30
+ * with a name prefixed by the block name.
31
+ * @param def A method which takes a field factory as input and outputs a template builder
32
+ * @returns The Builder, for call-chaining
33
+ */
34
+ message(name, def) {
35
+ const msgName = `${this.name}${name.length ? ('.' + name) : ''}`;
36
+ this._inputMsgs.push(new dependency_1.$Dependency(this.module, 'message', msgName));
37
+ const builder = new message_builder_1.MessageBuilder(this.module, msgName)
38
+ .template(def);
39
+ this._inlineNodes.push(new dependency_1.BuilderNode({
40
+ module: this.module,
41
+ type: 'message',
42
+ name: msgName,
43
+ builder,
44
+ isInline: true,
45
+ filepath: [], // This is added later by Treeshake.blockInlineNodes()
46
+ dependencies: [] // This is added later by Treeshake.*()
47
+ }));
48
+ const dep = new dependency_1.$Dependency(this.module, 'message', msgName);
49
+ this._inputMsgs.push(dep);
50
+ return this;
51
+ }
52
+ /**
53
+ * @deprecated
29
54
  * Inline messages. These messages are exposed to the module,
30
55
  * with a name prefixed by the block name.
31
56
  * @param def A method which takes a field factory as input and outputs a template builder
32
57
  * @returns The Builder, for call-chaining
33
58
  */
59
+ /** @deprecated Use `.message` instead. Will be removed on 3.1 */
34
60
  messages(def) {
35
61
  const factory = new message_template_field_builder_1.MessageTemplateFieldFactory(this.module);
36
62
  const schema = def(factory);
@@ -1,7 +1,7 @@
1
1
  import { $Module, $Space, ScopedMessage, ScopedMessageName } from "../../../schema";
2
2
  import { $Job, $JobAssert, $JobMethod } from './job.schema';
3
3
  import { BlockBuilder } from '../block.builder';
4
- import { MultiMessageTemplateDef } from "../../entities/message/template/message_template.builder";
4
+ import { MessageTemplateDef, MultiMessageTemplateDef } from "../../entities/message/template/message_template.builder";
5
5
  import { Overlay } from "../../../engine/util/type";
6
6
  import { $MessageInfer } from "../../entities/message/message.infer";
7
7
  import { TrxNode } from "../../../engine/transaction/trx_node";
@@ -33,6 +33,12 @@ export declare class JobBuilder<Space extends $Space, Module extends $Module, Jo
33
33
  from: keyof Machine["states"] & string;
34
34
  to: keyof Machine["states"] & string;
35
35
  }>;
36
+ message<Name extends string, Def extends MessageTemplateDef<Space, Module, Name>, FullName extends string = `${Job['name']}${Name extends '' ? '' : '.'}${Name & string}`, Msg extends $Message = $MessageInfer<FullName, ($: any) => ReturnType<Def>>>(name: Name, def: Def): JobBuilder<Space, Overlay<Module, {
37
+ messages: Overlay<Module["messages"], { [K in FullName]: Msg; }>;
38
+ }>, Overlay<Job, {
39
+ "#input": Msg;
40
+ }>, Ctx>;
41
+ /** @deprecated Use `.message` instead. Will be removed on 3.1 */
36
42
  messages<Def extends MultiMessageTemplateDef<Space, Module>>(def: Def): JobBuilder<Space, Overlay<Module, {
37
43
  messages: Overlay<Module["messages"], { [K in keyof ReturnType<Def> as `${Job["name"]}${K extends "" ? "" : "."}${K & string}`]: $MessageInfer<`${Job["name"]}${K extends "" ? "" : "."}${K & string}`, ($: any) => ReturnType<Def>[K], ReturnType<Def>[K], import("../../entities/message/message.infer").$MessageInputInfer<ReturnType<Def>[K], ReturnType<Def>[K] extends infer T ? { [K_1 in keyof T]: ReturnType<Def>[K][K_1] extends import("../../entities/message/template/message_template_field.builder").MessageTemplateFieldBuilder<any, any, infer I, any, any, infer Opt extends [undefined, undefined], infer Nul extends [null, null]> ? {
38
44
  path: `${K_1 & string}${keyof I & string}`;
@@ -31,6 +31,10 @@ class JobBuilder extends block_builder_1.BlockBuilder {
31
31
  return this;
32
32
  }
33
33
  /* [Job] */
34
+ message(name, def) {
35
+ return super.message(name, def);
36
+ }
37
+ /** @deprecated Use `.message` instead. Will be removed on 3.1 */
34
38
  messages(def) {
35
39
  return super.messages(def);
36
40
  }
@@ -36,7 +36,7 @@ function convertToView(model, name, fields = model.fields, path) {
36
36
  * */
37
37
  function convertToMessage(module, model, name, alias, include = [], exclude = []) {
38
38
  const convertField = (field) => {
39
- return new message_template_schema_1.$MessageTemplateField(field.type, field.name, field.alias, field.path, field.path, field.array, field.required, undefined, false, [], {
39
+ return new message_template_schema_1.$MessageTemplateField(field.type, field.name, field.alias, field.alias, field.path, field.path, field.array, field.required, undefined, false, [], [], {
40
40
  enum: field.meta?.enum ? {
41
41
  options: field.meta.enum.options,
42
42
  dep: field.meta.enum.dep ? new dependency_1.$Dependency(module, 'constants', `${field.meta.enum.dep.module}::${field.meta.enum.dep.name}`) : undefined
@@ -21,8 +21,8 @@ class MessageBuilder {
21
21
  return this;
22
22
  }
23
23
  template($) {
24
- const fieldBuilder = new message_template_field_builder_1.MessageTemplateFieldFactory(this.module);
25
- const fields = $(fieldBuilder);
24
+ const factory = new message_template_field_builder_1.MessageTemplateFieldFactory(this.module);
25
+ const fields = $(factory);
26
26
  this._template.fields(fields);
27
27
  return this;
28
28
  }
@@ -11,6 +11,5 @@ export declare class MessageParser<$ extends $Message> {
11
11
  constructor(schema: $);
12
12
  static parseWithTrxModule(trx: AnyTrxNode, raw: RawMessageInput<any, any>, sigKey?: string): Promise<any>;
13
13
  parse(trx: AnyTrxNode, raw: $['#raw'], sigKey?: string): Promise<Message<$>>;
14
- private sanitize;
15
14
  }
16
15
  export type AnyMessageParser = MessageParser<$Message>;
@@ -28,55 +28,11 @@ class MessageParser {
28
28
  }
29
29
  return parser.parse(trx, raw, sigKey);
30
30
  }
31
- // TODO: OPTIMIZATION
32
- // Parse everything that's static first, then move on to
33
- // parsing ids etc.
34
31
  async parse(trx, raw, sigKey) {
35
32
  log_1.Log.debug('trx', trx.globalId, `${(0, log_1.scopeTag)('message', this.schema.name)} Parse${sigKey ? ' (signed)' : ''}`, raw);
36
- const parseFields = async (trx, fields, obj) => {
37
- const parsedObj = {};
38
- for (const k in fields) {
39
- const field = fields[k];
40
- parsedObj[k] = await parseField(trx, field, obj[k]);
41
- }
42
- return parsedObj;
43
- };
44
- const parseField = async (trx, field, value) => {
45
- // 1. Sanitize input
46
- this.sanitize(value);
47
- // 2. Check for required fields
48
- return (0, message_template_parser_1.MessageTemplateFieldParser)(raw, trx, field, value);
49
- };
50
- const applyRules = async (fields, parsed, parsedValue = parsed) => {
51
- for (const f in fields) {
52
- const field = fields[f];
53
- for (const r in field.rules) {
54
- const rule = field.rules[r];
55
- const value = parsedValue?.[field.name];
56
- const res = await rule({ field, value, msg: parsed });
57
- if (typeof res === 'object') {
58
- parsedValue ?? (parsedValue = {});
59
- parsedValue[field.name] = res.set;
60
- }
61
- else if (res !== true) {
62
- throw error_1.NesoiError.Message.RuleFailed({ rule, error: res });
63
- }
64
- }
65
- if (field.children) {
66
- await applyRules(field.children, parsed, parsedValue?.[field.name]);
67
- }
68
- }
69
- };
70
- const parsed = await parseFields(trx, this.schema.template.fields, raw);
71
- await applyRules(this.schema.template.fields, parsed);
33
+ const fields = this.schema.template.fields;
34
+ const parsed = await (0, message_template_parser_1.MessageTemplateFieldParser)(trx, fields, raw);
72
35
  return message_1.Message.new(this.schema.name, parsed, sigKey);
73
36
  }
74
- sanitize(value) {
75
- if (typeof value === 'function') {
76
- throw error_1.NesoiError.Message.UnsanitaryValue({
77
- details: 'Functions not allowed as message inputs.'
78
- });
79
- }
80
- }
81
37
  }
82
38
  exports.MessageParser = MessageParser;
@@ -3,8 +3,10 @@ import { $BucketModelFieldType } from "../../bucket/model/bucket_model.schema";
3
3
  import { $Dependency } from "../../../../engine/dependency";
4
4
  export type $MessageTemplateRule = (def: {
5
5
  field: $MessageTemplateField;
6
+ path: string;
6
7
  value: any;
7
8
  msg: $Message['#raw'];
9
+ inject: Record<string, any>;
8
10
  }) => {
9
11
  set: any;
10
12
  } | true | string | Promise<{
@@ -36,23 +38,53 @@ export type $MessageTemplateFieldType = $BucketModelFieldType | 'string_or_numbe
36
38
  * @subcategory Entity
37
39
  * */
38
40
  export declare class $MessageTemplateField {
41
+ /** A string representing the type of data carried by the field */
39
42
  type: $MessageTemplateFieldType;
43
+ /**
44
+ * - A machine name for the field, unique inside it's parent (either another field or a message).
45
+ * - Matches the relative path for writing the field value on the parent parsed object (key_parsed).
46
+ */
40
47
  name: string;
48
+ /** A human name for the field */
41
49
  alias: string;
50
+ /** A human name for the field union, assigned before the type.
51
+ * This is only relevant when using .obj.or, to allow for different
52
+ * aliases for the union and the root object */
53
+ preAlias: string;
54
+ /** The absolute path for reading the field value on the raw object */
42
55
  path_raw: string;
56
+ /** The absolute path for writing the field value on the parsed object */
43
57
  path_parsed: string;
44
58
  array: boolean;
45
59
  required: boolean;
46
60
  defaultValue: any;
47
61
  nullable: boolean;
48
62
  rules: $MessageTemplateRule[];
63
+ arrayRules: $MessageTemplateRule[];
49
64
  meta: $MessageTemplateFieldMeta;
50
65
  children?: $MessageTemplateFields | undefined;
51
66
  or?: $MessageTemplateField | undefined;
52
67
  '#raw': unknown;
53
68
  '#parsed': unknown;
54
69
  $t: string;
55
- constructor(type: $MessageTemplateFieldType, name: string, alias: string, path_raw: string, path_parsed: string, array: boolean, required: boolean, defaultValue: any, nullable: boolean, rules: $MessageTemplateRule[], meta: $MessageTemplateFieldMeta, children?: $MessageTemplateFields | undefined, or?: $MessageTemplateField | undefined);
70
+ constructor(
71
+ /** A string representing the type of data carried by the field */
72
+ type: $MessageTemplateFieldType,
73
+ /**
74
+ * - A machine name for the field, unique inside it's parent (either another field or a message).
75
+ * - Matches the relative path for writing the field value on the parent parsed object (key_parsed).
76
+ */
77
+ name: string,
78
+ /** A human name for the field */
79
+ alias: string,
80
+ /** A human name for the field union, assigned before the type.
81
+ * This is only relevant when using .obj.or, to allow for different
82
+ * aliases for the union and the root object */
83
+ preAlias: string,
84
+ /** The absolute path for reading the field value on the raw object */
85
+ path_raw: string,
86
+ /** The absolute path for writing the field value on the parsed object */
87
+ path_parsed: string, array: boolean, required: boolean, defaultValue: any, nullable: boolean, rules: $MessageTemplateRule[], arrayRules: $MessageTemplateRule[], meta: $MessageTemplateFieldMeta, children?: $MessageTemplateFields | undefined, or?: $MessageTemplateField | undefined);
56
88
  }
57
89
  export type $MessageTemplateFields = {
58
90
  [x: string]: $MessageTemplateField;
@@ -6,10 +6,28 @@ exports.$MessageTemplate = exports.$MessageTemplateField = void 0;
6
6
  * @subcategory Entity
7
7
  * */
8
8
  class $MessageTemplateField {
9
- constructor(type, name, alias, path_raw, path_parsed, array, required, defaultValue, nullable, rules, meta, children, or) {
9
+ constructor(
10
+ /** A string representing the type of data carried by the field */
11
+ type,
12
+ /**
13
+ * - A machine name for the field, unique inside it's parent (either another field or a message).
14
+ * - Matches the relative path for writing the field value on the parent parsed object (key_parsed).
15
+ */
16
+ name,
17
+ /** A human name for the field */
18
+ alias,
19
+ /** A human name for the field union, assigned before the type.
20
+ * This is only relevant when using .obj.or, to allow for different
21
+ * aliases for the union and the root object */
22
+ preAlias,
23
+ /** The absolute path for reading the field value on the raw object */
24
+ path_raw,
25
+ /** The absolute path for writing the field value on the parsed object */
26
+ path_parsed, array, required, defaultValue, nullable, rules, arrayRules, meta, children, or) {
10
27
  this.type = type;
11
28
  this.name = name;
12
29
  this.alias = alias;
30
+ this.preAlias = preAlias;
13
31
  this.path_raw = path_raw;
14
32
  this.path_parsed = path_parsed;
15
33
  this.array = array;
@@ -17,6 +35,7 @@ class $MessageTemplateField {
17
35
  this.defaultValue = defaultValue;
18
36
  this.nullable = nullable;
19
37
  this.rules = rules;
38
+ this.arrayRules = arrayRules;
20
39
  this.meta = meta;
21
40
  this.children = children;
22
41
  this.or = or;
@@ -18,6 +18,13 @@ export declare class MessageTemplateFieldFactory<Space extends $Space, Module ex
18
18
  private module;
19
19
  private alias?;
20
20
  constructor(module: string);
21
+ /**
22
+ * Specifies an alias for the field.
23
+ * - If this field is a union (.or), this alias is used when
24
+ * referring to the union and it's first option.
25
+ * - You can specify a different alias for the first options
26
+ * by also using the .as() after the type
27
+ */
21
28
  as(alias: string): Omit<typeof this, "as">;
22
29
  get any(): MessageTemplateFieldBuilder<Module, Message, {
23
30
  '': any;
@@ -227,7 +234,9 @@ Nullable extends [null | never, null | never] = [never, never]> {
227
234
  private _defaultValue?;
228
235
  private _nullable;
229
236
  private _rules;
237
+ private _arrayRules;
230
238
  private _or?;
239
+ private preAlias?;
231
240
  constructor(type: $MessageTemplateFieldType, value: $MessageTemplateFieldMeta, alias?: string | undefined, children?: Children | undefined);
232
241
  as(alias: string): this;
233
242
  get optional(): MessageTemplateFieldBuilder<Module, Message, DefinedInput, DefinedOutput, Children, [undefined, undefined], Nullable>;
@@ -245,8 +254,10 @@ export type MessageTemplateFieldBuilders = {
245
254
  export type AnyMessageTemplateFieldBuilder = MessageTemplateFieldBuilder<any, any, any, any, any>;
246
255
  export type MessageTemplateRuleDef<I, Msg> = (def: {
247
256
  field: $MessageTemplateField;
257
+ path: string;
248
258
  value: I;
249
259
  msg: Msg;
260
+ inject: Record<string, any>;
250
261
  }) => {
251
262
  set: I;
252
263
  } | true | string | Promise<{
@@ -12,9 +12,17 @@ class MessageTemplateFieldFactory {
12
12
  constructor(module) {
13
13
  this.module = module;
14
14
  }
15
+ /**
16
+ * Specifies an alias for the field.
17
+ * - If this field is a union (.or), this alias is used when
18
+ * referring to the union and it's first option.
19
+ * - You can specify a different alias for the first options
20
+ * by also using the .as() after the type
21
+ */
15
22
  as(alias) {
16
- this.alias = alias;
17
- return this;
23
+ const chain = new MessageTemplateFieldFactory(this.module);
24
+ chain.alias = alias;
25
+ return chain;
18
26
  }
19
27
  get any() {
20
28
  return new MessageTemplateFieldBuilder('unknown', {}, this.alias);
@@ -106,6 +114,8 @@ class MessageTemplateFieldBuilder {
106
114
  this._defaultValue = undefined;
107
115
  this._nullable = true;
108
116
  this._rules = [];
117
+ this._arrayRules = [];
118
+ this.preAlias = alias;
109
119
  }
110
120
  as(alias) {
111
121
  this.alias = alias;
@@ -136,7 +146,12 @@ class MessageTemplateFieldBuilder {
136
146
  return this;
137
147
  }
138
148
  rule(rule) {
139
- this._rules.push(rule);
149
+ if (this._array) {
150
+ this._arrayRules.push(rule);
151
+ }
152
+ else {
153
+ this._rules.push(rule);
154
+ }
140
155
  return this;
141
156
  }
142
157
  get array() {
@@ -149,11 +164,12 @@ class MessageTemplateFieldBuilder {
149
164
  }
150
165
  or(def) {
151
166
  this._or = def;
167
+ this._or.preAlias = this.preAlias;
152
168
  this._or._array = this._array;
153
169
  this._or._defaultValue = this._defaultValue;
154
170
  this._or._nullable = this._nullable;
155
171
  this._or._required = this._required;
156
- this._or._rules = this._rules;
172
+ this._or._arrayRules = this._arrayRules;
157
173
  return this;
158
174
  }
159
175
  // Build
@@ -163,15 +179,15 @@ class MessageTemplateFieldBuilder {
163
179
  : undefined;
164
180
  const pathParsed = basePathParsed + name;
165
181
  const pathRaw = basePathParsed + (builder.type === 'id'
166
- ? builder.array ? `${name.slice(0, -1)}_ids` : `${name}_id`
182
+ ? builder._array ? `${name}_ids` : `${name}_id`
167
183
  : name);
168
- const childrenBasePathRaw = pathRaw + (builder._array ? '#.' : '');
169
- const childrenBasePathParsed = pathParsed + (builder._array ? '#.' : '');
184
+ const childrenBasePathRaw = pathRaw + (builder._array ? '.#.' : '.');
185
+ const childrenBasePathParsed = pathParsed + (builder._array ? '.#.' : '.');
170
186
  if (builder.value.id) {
171
187
  const bucket = tree.getSchema(builder.value.id.bucket);
172
188
  builder.value.id.type = bucket.model.fields.id.type;
173
189
  }
174
- return new message_template_schema_1.$MessageTemplateField(builder.type, name, builder.alias || name, pathRaw, pathParsed, builder._array, builder._required, builder._defaultValue, builder._nullable, builder._rules, builder.value, builder.children ? MessageTemplateFieldBuilder.buildChildren(builder.children, tree, module, childrenBasePathRaw, childrenBasePathParsed) : undefined, or);
190
+ return new message_template_schema_1.$MessageTemplateField(builder.type, name, builder.alias || name, builder.preAlias || name, pathRaw, pathParsed, builder._array, builder._required, builder._defaultValue, builder._nullable, builder._rules, builder._arrayRules, builder.value, builder.children ? MessageTemplateFieldBuilder.buildChildren(builder.children, tree, module, childrenBasePathRaw, childrenBasePathParsed) : undefined, or);
175
191
  }
176
192
  static buildChildren(fields, tree, module, basePathRaw = '', basePathParsed = '') {
177
193
  const schema = {};
@@ -1,6 +1,6 @@
1
- import { $MessageTemplateField } from './message_template.schema';
1
+ import { $MessageTemplateFields } from './message_template.schema';
2
2
  import { AnyTrxNode } from "../../../../engine/transaction/trx_node";
3
- export declare function MessageTemplateFieldParser(raw: Record<string, any>, trx: AnyTrxNode, field: $MessageTemplateField, value: any): Promise<any>;
3
+ export declare function MessageTemplateFieldParser(trx: AnyTrxNode, fields: $MessageTemplateFields, raw: Record<string, any>): Promise<Record<string, any>>;
4
4
  /**
5
5
  * Empty values: `{}`, `[]`, `''`, `null`, `undefined`
6
6
  */
@@ -4,19 +4,34 @@ exports.MessageTemplateFieldParser = MessageTemplateFieldParser;
4
4
  exports.isEmpty = isEmpty;
5
5
  const parse_1 = require("../../../../engine/util/parse");
6
6
  const error_1 = require("../../../../engine/data/error");
7
- async function MessageTemplateFieldParser(raw, trx, field, value) {
8
- return parseFieldValue(trx, field, field.path_raw, raw, value);
7
+ // TODO: OPTIMIZATION
8
+ // Parse everything that's static first, then move on to
9
+ // parsing ids etc.
10
+ async function MessageTemplateFieldParser(trx, fields, raw) {
11
+ const parsed = {};
12
+ const inject = {};
13
+ for (const k in fields) {
14
+ const field = fields[k];
15
+ const key_raw = field.path_raw.split('.')[0];
16
+ const key_parsed = field.path_parsed.split('.')[0];
17
+ const value = raw[key_raw];
18
+ parsed[key_parsed] = await parseFieldValue(trx, field, [field.name], raw, value, inject);
19
+ }
20
+ Object.assign(parsed, inject);
21
+ return parsed;
9
22
  }
10
- // Attempt to parse a field value
11
- // - If field is an array, this method is run for each value, sequentially
12
- // - If not, it's run for the original value, once
13
- //
14
- // - This method stacks with the .or options
15
- //
16
- async function parseFieldValue(trx, field, path, raw, value) {
23
+ /**
24
+ * [Parser Step 1]
25
+ *
26
+ * - Check for empty fields ({}, [], '', null, undefined)
27
+ * - If it's array, run step 2 for each value (with this field and path+i)
28
+ * - If not, run step 2 for the original value (with this field and path)
29
+ */
30
+ async function parseFieldValue(trx, field, path, raw, value, inject) {
31
+ sanitize(field, path, value);
17
32
  if (isEmpty(value)) {
18
33
  if (field.required) {
19
- throw error_1.NesoiError.Message.FieldIsRequired({ field: field.alias, path, value });
34
+ throw error_1.NesoiError.Message.FieldIsRequired({ alias: field.alias, path: path.join('.'), value });
20
35
  }
21
36
  else if (field.defaultValue !== undefined) {
22
37
  return field.defaultValue;
@@ -25,46 +40,72 @@ async function parseFieldValue(trx, field, path, raw, value) {
25
40
  return undefined;
26
41
  }
27
42
  }
43
+ let output;
28
44
  if (field.array) {
29
45
  if (!Array.isArray(value)) {
30
- throw error_1.NesoiError.Message.InvalidFieldType({ field: field.alias, path, value, type: 'list' });
31
- }
32
- if (field.required && !value.length) {
33
- throw error_1.NesoiError.Message.FieldIsRequired({ field: field.alias, path, value });
46
+ throw error_1.NesoiError.Message.InvalidFieldType({ alias: field.alias, path: path.join('.'), value, type: 'list' });
34
47
  }
35
- const parsedValue = [];
48
+ output = [];
36
49
  for (let i = 0; i < value.length; i++) {
37
50
  const v = value[i];
38
- const parsed = await _attemptUnion(trx, field, `${path}.${i}`, raw, v);
39
- parsedValue.push(parsed);
51
+ const parsedValue = await _attemptUnion(trx, field, [...path, i.toString()], raw, v, inject);
52
+ output.push(parsedValue);
40
53
  }
41
- return parsedValue;
54
+ output = await applyFieldRules('array', field, path, raw, output, inject);
42
55
  }
43
- return _attemptUnion(trx, field, path, raw, value);
56
+ else {
57
+ output = await _attemptUnion(trx, field, path, raw, value, inject);
58
+ }
59
+ return output;
44
60
  }
45
- async function _attemptUnion(trx, field, path, raw, value, unionErrors = []) {
61
+ /**
62
+ * [Parser Step 2]
63
+ *
64
+ * - Attempt to run parse method (step 3) for field.
65
+ * - If it fails, attempt other union options (step 2) (if available), with same path
66
+ * - If it works, apply field rules.
67
+ */
68
+ async function _attemptUnion(trx, field, path, raw, value, inject, unionErrors = []) {
69
+ let output = undefined;
46
70
  try {
47
- return await _runParseMethod(trx, field, path, raw, value);
71
+ output = await _runParseMethod(trx, field, path, raw, value, inject);
48
72
  }
49
73
  catch (e) {
74
+ const ue = [
75
+ ...unionErrors,
76
+ {
77
+ option: field.alias,
78
+ name: e.name,
79
+ status: e.status,
80
+ message: e.message,
81
+ data: e.data,
82
+ }
83
+ ];
50
84
  // If failed and there's a second option, atempt it
51
85
  if (field.or) {
52
- return await _attemptUnion(trx, field.or, path, raw, value, [...unionErrors, e]);
86
+ return await _attemptUnion(trx, field.or, path, raw, value, inject, ue);
53
87
  }
54
88
  // If this error was not the first attempt, and we have no other option
55
89
  // we throw a specific error
56
90
  // This avoid confusion for the client when parsing unions
57
91
  if (unionErrors.length) {
58
- throw error_1.NesoiError.Message.ValueDoesntMatchUnion({ field: field.alias, path, value, unionErrors: [...unionErrors, e] });
92
+ throw error_1.NesoiError.Message.ValueDoesntMatchUnion({ alias: field.preAlias, path: path.join('.'), value, unionErrors: ue });
59
93
  }
60
94
  throw e;
61
95
  }
96
+ output = await applyFieldRules('item', field, path, raw, output, inject);
97
+ return output;
62
98
  }
63
- async function _runParseMethod(trx, field, path, raw, value) {
99
+ /**
100
+ * [Parser Step 3]
101
+ *
102
+ * - Run a specific parsing method based on the field type
103
+ */
104
+ async function _runParseMethod(trx, field, path, raw, value, inject) {
64
105
  switch (field.type) {
65
106
  case 'obj':
66
107
  case 'dict':
67
- return await parseParentField(trx, field, path, raw, value);
108
+ return await parseParentField(trx, field, path, raw, value, inject);
68
109
  case 'unknown':
69
110
  return value;
70
111
  case 'boolean':
@@ -94,7 +135,13 @@ async function _runParseMethod(trx, field, path, raw, value) {
94
135
  }
95
136
  throw error_1.NesoiError.Builder.Message.UnknownTemplateFieldType(field.type);
96
137
  }
97
- async function parseParentField(trx, field, path, raw, value) {
138
+ /**
139
+ * [Parser Step 3-b]: 'obj' or 'dict'
140
+ *
141
+ * - The parser methods only return a tuple of field and value, to be parsed again by (step 1)
142
+ * - When calling step 1, the child property name is appended to the path
143
+ */
144
+ async function parseParentField(trx, field, path, raw, value, inject) {
98
145
  let children;
99
146
  if (field.type === 'obj') {
100
147
  children = (0, parse_1.parseObj)(field, path, value);
@@ -102,13 +149,18 @@ async function parseParentField(trx, field, path, raw, value) {
102
149
  else {
103
150
  children = (0, parse_1.parseDict)(field, path, value);
104
151
  }
105
- const parsed = {};
152
+ const parsedParent = {};
106
153
  for (const key in children) {
107
154
  const child = children[key];
108
- parsed[key] = await parseFieldValue(trx, child.field, `${path}.${key}`, raw, child.value);
155
+ parsedParent[key] = await parseFieldValue(trx, child.field, [...path, key], raw, child.value, inject);
109
156
  }
110
- return parsed;
157
+ return parsedParent;
111
158
  }
159
+ /**
160
+ * [Parser Step 3-b]: 'id'
161
+ *
162
+ * - Gathers the data for parsing a id
163
+ */
112
164
  async function parseIdField(trx, field, path, value) {
113
165
  const bucket = field.meta.id.bucket;
114
166
  const type = field.meta.id.type;
@@ -121,6 +173,13 @@ async function parseIdField(trx, field, path, value) {
121
173
  return parsed.obj;
122
174
  }
123
175
  }
176
+ function sanitize(field, path, value) {
177
+ if (typeof value === 'function') {
178
+ throw error_1.NesoiError.Message.UnsanitaryValue({
179
+ alias: field.alias, path: path.join('.'), details: 'Functions not allowed as message inputs.'
180
+ });
181
+ }
182
+ }
124
183
  /**
125
184
  * Empty values: `{}`, `[]`, `''`, `null`, `undefined`
126
185
  */
@@ -139,3 +198,25 @@ function isEmpty(value) {
139
198
  }
140
199
  return false;
141
200
  }
201
+ /**
202
+ * Rules
203
+ */
204
+ async function applyFieldRules(mode, field, path, raw, value, inject) {
205
+ let output = value;
206
+ // If mode is item, the value received is not an array
207
+ // - This comes from a .rule *before* .array
208
+ // If mode is array, the value received is an array
209
+ // - This comes from a .rule *after* .array
210
+ const rules = mode === 'item' ? field.rules : field.arrayRules;
211
+ for (const r in rules) {
212
+ const rule = rules[r];
213
+ const res = await rule({ field, value, path: path.join('.'), msg: raw, inject });
214
+ if (typeof res === 'object') {
215
+ output = res.set;
216
+ }
217
+ else if (res !== true) {
218
+ throw error_1.NesoiError.Message.RuleFailed({ alias: field.alias, path: path.join('.'), rule, error: res });
219
+ }
220
+ }
221
+ return output;
222
+ }