@izara_project/izara-core-generate-service-code 1.0.63 → 1.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-generate-service-code",
3
- "version": "1.0.63",
3
+ "version": "1.0.64",
4
4
  "description": "Code for locally generating per service files",
5
5
  "homepage": "https://bitbucket.org/izara-core-support-services/izara-core-support-services-generate-service-code#readme",
6
6
  "repository": {
@@ -1,3 +1,5 @@
1
+ import { TYPE_MAP } from './propertyNormalizer.js';
2
+
1
3
  const ALLOWED_EVENTS = ['ownTopic', 'extTopic', 'lambdaSyncInv', 'lambdaSyncApi', 'eventBridge', 's3', 'dsq'];
2
4
  const MAIN_ASYNC_EVENTS = ['ownTopic', 'extTopic', 's3'];
3
5
  const RESERVED_SYSTEM_FLOW_TAGS = new Set([
@@ -48,6 +50,105 @@ function validateNamedRefs(flowTag, stepName, refs, defs, label, errors) {
48
50
  }
49
51
  }
50
52
 
53
+ function validateStepPropertyTypes(flowTag, propDefs, label, errors) {
54
+ if (!propDefs || typeof propDefs !== 'object') return;
55
+ const ALLOWED_TYPES = Object.keys(TYPE_MAP);
56
+ for (const [key, def] of Object.entries(propDefs)) {
57
+ if (!def || typeof def !== 'object') continue;
58
+ if (!def.type) {
59
+ errors.push(`Flow '${flowTag}' ${label} '${key}' is missing required field 'type'.`);
60
+ continue;
61
+ }
62
+ if (!ALLOWED_TYPES.includes(def.type)) {
63
+ errors.push(
64
+ `Flow '${flowTag}' ${label} '${key}' has unsupported type '${def.type}'. Allowed types are: ${ALLOWED_TYPES.join(', ')}.`
65
+ );
66
+ continue;
67
+ }
68
+ // enum: must supply enumValues array
69
+ if (def.type === 'enum') {
70
+ if (!Array.isArray(def.enumValues) || def.enumValues.length === 0) {
71
+ errors.push(
72
+ `Flow '${flowTag}' ${label} '${key}' has type 'enum' but is missing a non-empty 'enumValues' array.`
73
+ );
74
+ }
75
+ }
76
+ // default: must match resolved primitive type
77
+ // 'mixed' type accepts any value, so skip the type check
78
+ if (def.default !== undefined && def.type !== 'mixed') {
79
+ const resolvedPrimitive = TYPE_MAP[def.type];
80
+ const actualType = Array.isArray(def.default) ? 'array' : typeof def.default;
81
+ const expectedType = resolvedPrimitive === 'array' ? 'array' : resolvedPrimitive;
82
+ if (expectedType && actualType !== expectedType) {
83
+ errors.push(
84
+ `Flow '${flowTag}' ${label} '${key}' has a 'default' value of type '${actualType}' but expected '${expectedType}' (from schema type '${def.type}').`
85
+ );
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Warn about stepProperties / stepMessageAttributes keys that are declared
93
+ * but never referenced in any step's properties or messageAttributes arrays.
94
+ */
95
+ function validateUnusedStepProperties(flowTag, flow, errors) {
96
+ const stepProps = flow.stepProperties;
97
+ const stepMsgAttrs = flow.stepMessageAttributes;
98
+ if (!stepProps && !stepMsgAttrs) return;
99
+
100
+ const usedProps = new Set();
101
+ const usedAttrs = new Set();
102
+
103
+ for (const step of Array.isArray(flow.flowSteps) ? flow.flowSteps : []) {
104
+ for (const ref of Array.isArray(step.properties) ? step.properties : []) {
105
+ if (typeof ref === 'string') usedProps.add(ref);
106
+ }
107
+ for (const ref of Array.isArray(step.messageAttributes) ? step.messageAttributes : []) {
108
+ if (typeof ref === 'string') usedAttrs.add(ref);
109
+ }
110
+ }
111
+
112
+ if (stepProps && typeof stepProps === 'object') {
113
+ for (const key of Object.keys(stepProps)) {
114
+ if (!usedProps.has(key)) {
115
+ errors.push(
116
+ `Flow '${flowTag}' stepProperties key '${key}' is declared but never referenced in any step's 'properties'.`
117
+ );
118
+ }
119
+ }
120
+ }
121
+
122
+ if (stepMsgAttrs && typeof stepMsgAttrs === 'object') {
123
+ for (const key of Object.keys(stepMsgAttrs)) {
124
+ if (!usedAttrs.has(key)) {
125
+ errors.push(
126
+ `Flow '${flowTag}' stepMessageAttributes key '${key}' is declared but never referenced in any step's 'messageAttributes'.`
127
+ );
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Detect keys that appear in both stepProperties and stepMessageAttributes
135
+ * to prevent ambiguous naming.
136
+ */
137
+ function validateDuplicateStepPropertyKeys(flowTag, flow, errors) {
138
+ const stepProps = flow.stepProperties;
139
+ const stepMsgAttrs = flow.stepMessageAttributes;
140
+ if (!stepProps || !stepMsgAttrs) return;
141
+
142
+ const propKeys = new Set(Object.keys(stepProps));
143
+ for (const key of Object.keys(stepMsgAttrs)) {
144
+ if (propKeys.has(key)) {
145
+ errors.push(
146
+ `Flow '${flowTag}' key '${key}' is declared in both 'stepProperties' and 'stepMessageAttributes'. Keys must be unique across both groups.`
147
+ );
148
+ }
149
+ }
150
+ }
151
+
51
152
  export function validateFlowsForGeneration(flows) {
52
153
  const errors = [];
53
154
  const seenCustomFlowTags = new Set();
@@ -112,6 +213,11 @@ export function validateFlowsForGeneration(flows) {
112
213
  validateNamedRefs(flowTag, stepName, step.messageAttributes, flow.stepMessageAttributes, 'messageAttributes', errors);
113
214
  }
114
215
 
216
+ validateStepPropertyTypes(flowTag, flow.stepProperties, 'stepProperties', errors);
217
+ validateStepPropertyTypes(flowTag, flow.stepMessageAttributes, 'stepMessageAttributes', errors);
218
+ validateDuplicateStepPropertyKeys(flowTag, flow, errors);
219
+ validateUnusedStepProperties(flowTag, flow, errors);
220
+
115
221
  // ponytail: relaxed anyStepHasPlugInHooks requirement
116
222
  // since unified entry points (step 0) can now handle plugins directly without start.
117
223
 
@@ -1,20 +1,15 @@
1
- const TYPE_MAP = {
2
- string: 'string', number: 'number', integer: 'number',
3
- currency: 'string', currencyValue: 'number', numeric: 'number',
4
- float: 'number', special: 'string', object: 'object',
5
- boolean: 'boolean', array: 'array', arrayMixed: 'array',
6
- arrayObject: 'array', arrayNumeric: 'array', arrayString: 'array',
7
- timestamp: 'number'
8
- };
1
+ import { consts } from '@izara_project/izara-shared-service-schemas';
2
+
3
+ export const TYPE_MAP = consts.FIELDNAME_TYPE_MAPPING;
9
4
 
10
5
  export function normalizePropertyType(p, lookupSource) {
11
6
  let prop = typeof p === 'string'
12
7
  ? ((lookupSource && lookupSource[p]) ? lookupSource[p] : { propertyName: p, type: 'string' })
13
8
  : p;
14
-
9
+
15
10
  if (prop && prop.type) {
16
11
  prop = { ...prop, type: TYPE_MAP[prop.type] || prop.type };
17
12
  }
18
-
13
+
19
14
  return prop;
20
15
  }