@izara_project/izara-core-generate-service-code 1.0.98 → 1.0.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +1 -1
  2. package/src/compile/compileProject.js +16 -5
  3. package/src/compile/flowValidator.js +47 -8
  4. package/src/compile/loadSchemas.js +18 -7
  5. package/src/compile/schemaValidatorSetup.js +16 -0
  6. package/src/emit/application/coreFunction/emitCoreFunctionArtifacts.js +123 -109
  7. package/src/emit/application/coreFunction/templates/InitialSetupConfig.ejs +8 -4
  8. package/src/emit/application/coreFunction/templates/InitialSetupRunner.ejs +2 -2
  9. package/src/emit/application/crud/templates/CreateComplete_Main.ejs +2 -0
  10. package/src/emit/application/crud/templates/CreateEndpoint_Main.ejs +4 -4
  11. package/src/emit/application/crud/templates/CreateLogicalLib.ejs +74 -15
  12. package/src/emit/application/crud/templates/DeleteComplete_Main.ejs +2 -0
  13. package/src/emit/application/crud/templates/DeleteEndpoint_Main.ejs +4 -4
  14. package/src/emit/application/crud/templates/FlowEndpoint_HdrWbs.ejs +7 -5
  15. package/src/emit/application/crud/templates/UpdateComplete_Main.ejs +2 -0
  16. package/src/emit/application/crud/templates/UpdateEndpoint_Main.ejs +4 -4
  17. package/src/emit/application/custom/customFlowPaths.js +13 -16
  18. package/src/emit/application/custom/customFlowS3.js +277 -66
  19. package/src/emit/application/custom/customFlows.js +131 -0
  20. package/src/emit/application/custom/emitCustomFlowApplicationArtifacts.js +3 -1
  21. package/src/emit/application/custom/templates/FlowEndpoint_Main.ejs +2 -2
  22. package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_HdrSqs.ejs +110 -0
  23. package/src/emit/application/custom/templates/s3/ConfirmAfterUpload_Main.ejs +89 -0
  24. package/src/emit/application/custom/templates/s3/ConfirmComplete_HdrSqs.ejs +110 -0
  25. package/src/emit/application/custom/templates/s3/ConfirmComplete_Main.ejs +89 -0
  26. package/src/emit/application/custom/templates/s3/CreateObject_HdrWbs.ejs +2 -1
  27. package/src/emit/application/custom/templates/s3/CreateObject_Main.ejs +4 -0
  28. package/src/emit/application/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +42 -0
  29. package/src/emit/application/custom/templates/s3/CreatePreSignUrl_Main.ejs +23 -0
  30. package/src/emit/application/custom/templates/s3/ProcessS3File_Main.ejs +135 -27
  31. package/src/emit/application/custom/templates/s3/Start_Main.ejs +197 -0
  32. package/src/emit/application/rbac/emitDynamicRbacFlowSchemas.js +8 -1
  33. package/src/emit/application/rbac/emitRbacApplicationArtifacts.js +107 -25
  34. package/src/emit/application/rbac/templates/ReservedDynamicUsage_Main.ejs +29 -9
  35. package/src/emit/application/rbac/templates/ReservedUsageTtlExpired_HdrDss.ejs +176 -0
  36. package/src/emit/application/rbac/templates/UserRbacFlow_Main.ejs +4 -3
  37. package/src/emit/application/system/templates/Register_HdrWbs.ejs +2 -1
  38. package/src/emit/infrastructure/functions/functionYamlArtifact.js +31 -8
  39. package/src/emit/infrastructure/functions/templates/DssFunction_Yaml.ejs +21 -0
  40. package/src/emit/infrastructure/functions/templates/S3Function_Yaml.ejs +2 -3
  41. package/src/emit/infrastructure/resources/iamResourceArtifacts.js +67 -5
  42. package/src/emit/infrastructure/resources/resourceState.js +15 -5
  43. package/src/emit/infrastructure/resources/s3ResourceArtifacts.js +19 -3
  44. package/src/emit/infrastructure/resources/templates/GeneratedLambdaRoleServiceConfig.ejs +5 -3
  45. package/src/emit/infrastructure/resources/templates/S3Bucket_Yaml.ejs +1 -1
  46. package/src/emit/infrastructure/resources/templates/SystemDynamoDB_Yaml.ejs +2 -2
  47. package/src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js +62 -1
  48. package/src/emit/schemas/error/emitErrorSchemas.js +120 -10
  49. package/src/emit/schemas/error/templates/errorConsts.ejs +12 -0
  50. package/src/generate.js +32 -34
  51. package/src/output/mergeHooks.js +105 -5
  52. package/src/plan/domainRegistry.js +0 -10
  53. package/task.md +40 -96
  54. package/test/integration/graphDrivenFlowSchema.test.js +58 -0
  55. package/test/integration/publicApi.test.js +33 -24
  56. package/test/unit/application/createTargetRoleHandler.test.js +101 -9
  57. package/test/unit/application/crudApplicationArtifacts.test.js +4 -0
  58. package/test/unit/application/customFlowApplicationArtifacts.test.js +6 -6
  59. package/test/unit/application/customFlowS3.test.js +730 -0
  60. package/test/unit/infrastructure/functionYamlArtifact.test.js +6 -4
  61. package/test/unit/infrastructure/resourceArtifacts.test.js +103 -0
  62. package/test/unit/output/mergeHooks.test.js +137 -0
  63. package/test/unit/schemas/attributeTreeSchemas.test.js +32 -3
  64. package/test/unit/schemas/attributeTreeSingleService.test.js +175 -0
  65. package/test/unit/schemas/dynamicRbacFlowSchemas.test.js +8 -6
  66. package/test/unit/schemas/dynamicRelationshipFlowSchemas.test.js +51 -0
  67. package/test/unit/schemas/errorSchemas.test.js +84 -5
  68. package/test/unit/templates/hookTagsValidation.test.js +83 -0
  69. package/src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js +0 -132
  70. package/test/unit/schemas/attributeTreeDirectives.test.js +0 -177
package/src/generate.js CHANGED
@@ -1,10 +1,12 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+
1
4
  import { buildGenerationPlan } from './plan/buildGenerationPlan.js';
2
5
  import { resolveEmissionGroups } from './plan/domainRegistry.js';
3
6
  import { commitArtifacts } from './output/commitArtifacts.js';
4
7
  import { compileProject } from './compile/compileProject.js';
5
8
  import {
6
9
  loadLocalSchemaDirectory,
7
- loadGenerateDirectives,
8
10
  resolveExtendedObjectSchemas,
9
11
  resolveGraphServiceTags
10
12
  } from './compile/loadSchemas.js';
@@ -14,34 +16,32 @@ export async function generate(rootPath, options = {}) {
14
16
  console.log(`[INFO] [Generate] STATUS=STARTED | rootPath=${rootPath}`);
15
17
  try {
16
18
  const project = compileProject({ ...options, rootPath });
19
+ await mkdir(join(project.schemaRoot, 'ErrorSchemas'), {
20
+ recursive: true
21
+ });
17
22
  const schemaLoadOptions = {
18
23
  skipValidation: options.skipValidation
19
24
  };
20
- const [
21
- rawObjects,
22
- relationships,
23
- flows,
24
- notifications,
25
- errors,
26
- directives
27
- ] = await Promise.all([
28
- loadLocalSchemaDirectory(project, 'ObjectSchemas', schemaLoadOptions),
29
- loadLocalSchemaDirectory(
30
- project,
31
- 'RelationshipSchemas',
32
- schemaLoadOptions
33
- ),
34
- loadLocalSchemaDirectory(project, 'FlowSchemas', schemaLoadOptions),
35
- loadLocalSchemaDirectory(
36
- project,
37
- 'NotificationSchemas',
38
- schemaLoadOptions
39
- ),
40
- loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions),
41
- loadGenerateDirectives(project, schemaLoadOptions)
42
- ]);
25
+ const [rawObjects, relationships, flows, notifications, errors] =
26
+ await Promise.all([
27
+ loadLocalSchemaDirectory(project, 'ObjectSchemas', schemaLoadOptions),
28
+ loadLocalSchemaDirectory(
29
+ project,
30
+ 'RelationshipSchemas',
31
+ schemaLoadOptions
32
+ ),
33
+ loadLocalSchemaDirectory(project, 'FlowSchemas', schemaLoadOptions),
34
+ loadLocalSchemaDirectory(
35
+ project,
36
+ 'NotificationSchemas',
37
+ schemaLoadOptions
38
+ ),
39
+ loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions)
40
+ ]);
43
41
  const schemaBucketName =
44
- options.schemaBucketName || process.env.iz_serviceSchemaBucketName;
42
+ options.schemaBucketName ||
43
+ project.schemaBucketName ||
44
+ process.env.iz_serviceSchemaBucketName;
45
45
  const objects = await resolveExtendedObjectSchemas(
46
46
  rawObjects,
47
47
  schemaBucketName
@@ -61,17 +61,17 @@ export async function generate(rootPath, options = {}) {
61
61
  }
62
62
  console.log(
63
63
  `[INFO] [Generate] STATUS=SCHEMAS_LOADED | ` +
64
- `objects=${objects.length}${objectSummary} ` +
65
- `relationships=${relationships.length} flows=${flows.length} ` +
66
- `notifications=${notifications.length} ` +
67
- `errors=${errors.length}`
64
+ `objects=${objects.length}${objectSummary} ` +
65
+ `relationships=${relationships.length} flows=${flows.length} ` +
66
+ `notifications=${notifications.length} ` +
67
+ `errors=${errors.length}`
68
68
  );
69
69
  if (options.verbose || process.env.GENERATE_VERBOSE) {
70
70
  for (const objectSchema of objects) {
71
71
  const objectType = objectSchema.objectType || 'unknown';
72
72
  console.log(
73
73
  `[DEBUG] [Generate] OBJECT_SCHEMA | objectType=${objectType}\n` +
74
- JSON.stringify(objectSchema, null, 2)
74
+ JSON.stringify(objectSchema, null, 2)
75
75
  );
76
76
  }
77
77
  }
@@ -82,7 +82,6 @@ export async function generate(rootPath, options = {}) {
82
82
  flows,
83
83
  notifications,
84
84
  errors,
85
- directives,
86
85
  objectGraphServiceTags,
87
86
  relationshipGraphServiceTags,
88
87
  logger: options.logger
@@ -111,17 +110,16 @@ export async function generate(rootPath, options = {}) {
111
110
  const durationMs = Date.now() - startedAt;
112
111
  console.log(
113
112
  `[INFO] [Generate] STATUS=FINISHED | ` +
114
- `files=${files.length} durationMs=${durationMs}`
113
+ `files=${files.length} durationMs=${durationMs}`
115
114
  );
116
115
 
117
116
  return { project, durationMs, files };
118
117
  } catch (error) {
119
118
  console.log(
120
119
  `[ERROR] [Generate] STATUS=FAILED | ` +
121
- `durationMs=${Date.now() - startedAt}`,
120
+ `durationMs=${Date.now() - startedAt}`,
122
121
  error
123
122
  );
124
123
  throw error;
125
124
  }
126
125
  }
127
-
@@ -40,6 +40,14 @@ async function mergeHook(outputRoot, artifact) {
40
40
  return artifact.content;
41
41
  }
42
42
 
43
+ _validateHookIntegrity(artifact.path, sourceContent);
44
+ _warnOrphanedHooks(
45
+ artifact.path,
46
+ sourceContent,
47
+ artifact.content,
48
+ matchers
49
+ );
50
+
43
51
  const tagOccurrences = new Map();
44
52
  return artifact.content.replace(
45
53
  matchers.anyTag,
@@ -60,13 +68,105 @@ async function mergeHook(outputRoot, artifact) {
60
68
  }
61
69
  const hookBlock = matches[count];
62
70
  if (hookBlock !== null && hookBlock !== undefined) {
63
- return hookBlock;
71
+ return _normalizeHookBlock(hookBlock, tagName);
64
72
  }
65
73
  return _match;
66
74
  }
67
75
  );
68
76
  }
69
77
 
78
+ function _validateHookIntegrity(filePath, content) {
79
+ const lower = filePath.toLowerCase();
80
+ let openCount = 0;
81
+ let closeCount = 0;
82
+
83
+ if (
84
+ lower.endsWith('.js') ||
85
+ lower.endsWith('.mjs') ||
86
+ lower.endsWith('.cjs')
87
+ ) {
88
+ const openP1 = content.match(/\/\/\s*\(<\s*[\w.-]+/g);
89
+ const closeP1 = content.match(/\/\/\s*\(<\/\s*[\w.-]+/g);
90
+ const openP2 = content.match(/\/\/\s*<#\s*[\w.-]+/g);
91
+ const closeP2 = content.match(/\/\/\s*<#\/\s*[\w.-]+/g);
92
+
93
+ if (openP1) {
94
+ openCount += openP1.length;
95
+ }
96
+ if (closeP1) {
97
+ closeCount += closeP1.length;
98
+ }
99
+ if (openP2) {
100
+ openCount += openP2.length;
101
+ }
102
+ if (closeP2) {
103
+ closeCount += closeP2.length;
104
+ }
105
+ } else if (lower.endsWith('.yml') || lower.endsWith('.yaml')) {
106
+ const openYml = content.match(/#\s*<#\s*[\w.-]+/g);
107
+ const closeYml = content.match(/#\s*<#\/\s*[\w.-]+/g);
108
+
109
+ if (openYml) {
110
+ openCount += openYml.length;
111
+ }
112
+ if (closeYml) {
113
+ closeCount += closeYml.length;
114
+ }
115
+ }
116
+
117
+ if (openCount !== closeCount) {
118
+ throw new Error(
119
+ `[mergeHooks] Malformed hook tags in '${filePath}': found ` +
120
+ `${openCount} open tag(s) but ${closeCount} close tag(s). ` +
121
+ `Aborting to prevent code loss.`
122
+ );
123
+ }
124
+ }
125
+
126
+ function _warnOrphanedHooks(
127
+ filePath,
128
+ sourceContent,
129
+ artifactContent,
130
+ matchers
131
+ ) {
132
+ const anyTagRegex = new RegExp(matchers.anyTag.source, 'gm');
133
+ let match = anyTagRegex.exec(sourceContent);
134
+
135
+ while (match !== null) {
136
+ const tagName = _extractTagName(match[1], match[3]);
137
+ let body = '';
138
+ if (typeof match[2] === 'string') {
139
+ body = match[2];
140
+ } else if (typeof match[4] === 'string') {
141
+ body = match[4];
142
+ }
143
+
144
+ if (tagName && body.trim().length > 0) {
145
+ const templatePattern = matchers.tagPattern(tagName);
146
+ const existsInTemplate = templatePattern.test(artifactContent);
147
+ if (!existsInTemplate) {
148
+ console.warn(
149
+ `[WARN] [mergeHooks] Hook '${tagName}' in '${filePath}' ` +
150
+ `contains custom code but is no longer present in template.`
151
+ );
152
+ }
153
+ }
154
+ match = anyTagRegex.exec(sourceContent);
155
+ }
156
+ }
157
+
158
+ function _normalizeHookBlock(hookBlock, tagName) {
159
+ if (typeof hookBlock !== 'string' || hookBlock.length === 0) {
160
+ return hookBlock;
161
+ }
162
+ const esc = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
163
+ const openRegex = new RegExp(`(//\\s*\\(<\\s*${esc}\\s*)(?!>)\\)`);
164
+ const closeRegex = new RegExp(`(//\\s*\\(<\\/\\s*${esc}\\s*)(?!>)\\)`);
165
+ let normalized = hookBlock.replace(openRegex, '$1>)');
166
+ normalized = normalized.replace(closeRegex, '$1>)');
167
+ return normalized;
168
+ }
169
+
70
170
  function _extractTagName(p1, p2) {
71
171
  if (typeof p1 === 'string' && p1.length > 0) {
72
172
  return p1;
@@ -85,8 +185,8 @@ function hookMatchers(filePath) {
85
185
  lower.endsWith('.cjs')
86
186
  ) {
87
187
  const jsP1 =
88
- '\\/\\/\\s*\\(<\\s*([\\w.-]+)\\s*>\\)([\\s\\S]*?)' +
89
- '\\/\\/\\s*\\(<\\/\\s*\\1\\s*>\\)';
188
+ '\\/\\/\\s*\\(<\\s*([\\w.-]+)\\s*>?\\s*\\)([\\s\\S]*?)' +
189
+ '\\/\\/\\s*\\(<\\/\\s*\\1\\s*>?\\s*\\)';
90
190
  const jsP2 =
91
191
  '\\/\\/\\s*<#\\s*([\\w.-]+)\\s*#>([\\s\\S]*?)' +
92
192
  '\\/\\/\\s*<#\\/\\s*\\3\\s*#>';
@@ -95,8 +195,8 @@ function hookMatchers(filePath) {
95
195
  tagPattern: (name) => {
96
196
  const esc = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
97
197
  const p1 =
98
- `\\/\\/\\s*\\(<\\s*${esc}\\s*>\\)([\\s\\S]*?)` +
99
- `\\/\\/\\s*\\(<\\/\\s*${esc}\\s*>\\)`;
198
+ `\\/\\/\\s*\\(<\\s*${esc}\\s*>?\\s*\\)([\\s\\S]*?)` +
199
+ `\\/\\/\\s*\\(<\\/\\s*${esc}\\s*>?\\s*\\)`;
100
200
  const p2 =
101
201
  `\\/\\/\\s*<#\\s*${esc}\\s*#>([\\s\\S]*?)` +
102
202
  `\\/\\/\\s*<#\\/\\s*${esc}\\s*#>`;
@@ -52,10 +52,6 @@ import {
52
52
  attributeTreeObjectSchemaManagedPaths,
53
53
  attributeTreeRelationshipSchemaManagedPaths
54
54
  } from '../emit/schemas/attributeTree/emitAttributeTreeSchemas.js';
55
- import {
56
- emitAttributeTreeTargetValueStandard,
57
- attributeTreeTargetValueStandardManagedPaths
58
- } from '../emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js';
59
55
  import {
60
56
  emitLedgerObjectSchemas,
61
57
  emitLedgerRelationshipSchemas,
@@ -81,7 +77,6 @@ export async function resolveEmissionGroups(context) {
81
77
  flows,
82
78
  notifications,
83
79
  errors,
84
- directives = [],
85
80
  objectGraphServiceTags,
86
81
  relationshipGraphServiceTags,
87
82
  logger
@@ -206,11 +201,6 @@ export async function resolveEmissionGroups(context) {
206
201
  managedPaths: rbacFlowSchemaManagedPaths(objects),
207
202
  artifacts: rbacFlowArtifacts
208
203
  },
209
- {
210
- scope: 'attribute-tree-target-value-standard',
211
- managedPaths: attributeTreeTargetValueStandardManagedPaths(directives),
212
- artifacts: emitAttributeTreeTargetValueStandard({ directives })
213
- },
214
204
  {
215
205
  scope: 'attribute-tree-object-schemas',
216
206
  managedPaths: attributeTreeObjectSchemaManagedPaths(objects),
package/task.md CHANGED
@@ -1,132 +1,76 @@
1
- # Task Checklist: Attribute Tree Per-Repo Code Generation Refactoring
1
+ # Task Checklist: Attribute Tree Single-Service Code Generation Consolidation
2
2
 
3
3
  > **Canonical Architecture Plan**: [[10.Systems/GenerateServiceCode/30-Generation/Task Roadmap - Attribute Tree Per-Repo Generation Refactoring|Task Roadmap in Obsidian]]
4
- > **Selected Design**: **Option C (`GenerateDirectives/`)** + **Style 1 (`generatedCode/<domain>/`)**
4
+ > **Selected Design**: **Single Main Service (Self-Repo Consolidation)** + **Style 1 (`generatedCode/<domain>/`)**
5
5
  > **Repository**: `IzaraCore-SupportServices-GenerateServiceCode-Seagame`
6
6
 
7
7
  ---
8
8
 
9
- ## 🎯 Target Architecture: Style 1 (`generatedCode/<domain>/`)
9
+ ## 🎯 Target Architecture: Single Unified Service (Self-Repo)
10
+
11
+ All schemas for an `attributeTree` (Tree nodes, Standard Value Handler node, and cascading PropertyValue nodes) are emitted directly inside the **Main Service** workspace:
10
12
 
11
- ### Target Service (e.g. `SellOfferTermValueStandard`)
12
13
  ```text
13
14
  app/src/schemas/
14
- ├── GenerateDirectives/
15
- │ └── sellOfferTermValueStandard.js <-- [Directive] Developer creates this
16
- │
17
15
  ├── ObjectSchemas/
16
+ │ ├── sellOffer.js <-- Source ObjectSchema
18
17
  │ └── generatedCode/
19
18
  │ ├── attributeTree/
20
- │ │ └── sellOfferTermValueStandard.js <-- [Emitted by TASK-02]
21
- │ └── propertyValue/
22
- │ ├── sellOfferTermValueStandardPropertyLabel.js <-- [Emitted by PropertyValue addOn]
23
- │ └── sellOfferTermValueStandardPropertyValue.js <-- [Emitted by PropertyValue addOn]
19
+ │ │ ├── sellOfferTermLink.js <-- Link Node
20
+ │ │ ├── sellOfferTermValue.js <-- Value Node
21
+ │ │ ├── sellOfferTermLabel.js <-- Label Node
22
+ │ │ └── sellOfferTermValueStandard.js <-- Handler Node (Self-Repo)
23
+ │ │
24
+ │ └── propertyValue/ <-- Cascading from ValueStandard
25
+ │ ├── sellOfferTermValueStandardPropertyLabel.js
26
+ │ └── sellOfferTermValueStandardPropertyValue.js
24
27
  │
25
28
  └── RelationshipSchemas/
26
29
  └── generatedCode/
30
+ ├── attributeTree/
31
+ │ ├── hasSellOfferTermLink.js
32
+ │ ├── disabledSellOfferTermLink.js
33
+ │ ├── isSellOfferTerm.js
34
+ │ └── isSellOfferTermLabel.js
35
+ │
27
36
  └── propertyValue/
28
37
  ├── hasSellOfferTermValueStandardPropertyValue.js
29
38
  ├── disabledSellOfferTermValueStandardPropertyValue.js
30
39
  └── isSellOfferTermValueStandardPropertyLabel.js
31
40
  ```
32
41
 
33
- ### Source Service (Self-Repo)
34
- ```text
35
- app/src/schemas/
36
- ├── ObjectSchemas/
37
- │ ├── sellOffer.js <-- Source ObjectSchema
38
- │ └── generatedCode/
39
- │ └── attributeTree/
40
- │ ├── sellOfferTermLink.js
41
- │ ├── sellOfferTermValue.js
42
- │ └── sellOfferTermLabel.js
43
- │
44
- └── RelationshipSchemas/
45
- └── generatedCode/
46
- └── attributeTree/
47
- ├── hasSellOfferTermLink.js
48
- ├── disabledSellOfferTermLink.js
49
- ├── isSellOfferTerm.js
50
- └── isSellOfferTermLabel.js
51
- ```
52
-
53
42
  ---
54
43
 
55
44
  ## 📋 Implementation Checklist
56
45
 
57
46
  - [x] **TASK-01: Recursive Schema Loading & Directives Loader**
58
47
  - **File**: `src/compile/loadSchemas.js`
59
- - **Action**:
60
- 1. Update `loadLocalSchemaDirectory(project, directoryName, options)` to recursively find all `.js` files within subdirectories (supporting `ObjectSchemas/generatedCode/**` and `RelationshipSchemas/generatedCode/**`).
61
- 2. Export `loadGenerateDirectives(project, options)` calling `loadLocalSchemaDirectory(project, 'GenerateDirectives', options)`.
62
- - **Acceptance Criteria**: `loadLocalSchemaDirectory(project, 'ObjectSchemas')` discovers and validates schemas nested inside `generatedCode/attributeTree/` and `generatedCode/propertyValue/`. Returns `[]` when `GenerateDirectives` folder is missing.
48
+ - **Acceptance Criteria**: `loadLocalSchemaDirectory(project, 'ObjectSchemas')` discovers and validates schemas nested inside `generatedCode/**`.
63
49
 
64
- - [x] **TASK-02: Target ValueStandard Schema Emitter**
65
- - **File**: `src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js` (NEW)
66
- - **Action**:
67
- 1. Filter `directives` for `directive.type === 'attributeTreeTarget'`.
68
- 2. Emit `{tag}ValueStandard.js` to `app/src/schemas/ObjectSchemas/generatedCode/attributeTree/${tag}ValueStandard.js`.
69
- 3. Include preservation hook `//(<addOnDataStructure${tag}ValueStandard>)` with commented `propertyValue` addOn template.
70
- 4. Export `attributeTreeTargetValueStandardManagedPaths(directives)`.
71
- - **Acceptance Criteria**: Emitted file adheres strictly to Izara backend syntax rules and matches Style 1 output path.
72
-
73
- - [x] **TASK-03: Align AttributeTree Self-Repo Emitter to Style 1**
50
+ - [x] **TASK-02: Consolidate ValueStandard into `emitAttributeTreeSchemas.js`**
74
51
  - **File**: `src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js`
75
52
  - **Action**:
76
- 1. Update ObjectSchema emission paths from `generatedAttributeTree/` to `generatedCode/attributeTree/`.
77
- 2. Update RelationshipSchema emission paths from `generatedAttributeTree/` to `generatedCode/attributeTree/`.
78
- 3. Update `attributeTreeObjectSchemaManagedPaths` and `attributeTreeRelationshipSchemaManagedPaths`.
79
- - **Acceptance Criteria**: Self-repo attributeTree schemas output cleanly into `generatedCode/attributeTree/`.
80
-
81
- - [x] **TASK-04: Align PropertyValue Emitter to Style 1**
82
- - **File**: `src/emit/schemas/propertyValue/emitPropertyValueSchemas.js`
83
- - **Action**:
84
- 1. Update ObjectSchema emission paths from `generatedPropertyValue/` to `generatedCode/propertyValue/`.
85
- 2. Update RelationshipSchema emission paths from `generatedPropertyValue/` to `generatedCode/propertyValue/`.
86
- 3. Update `propertyValueObjectSchemaManagedPaths` and `propertyValueRelationshipSchemaManagedPaths`.
87
- - **Acceptance Criteria**: PropertyValue schemas output cleanly into `generatedCode/propertyValue/`.
88
-
89
- - [x] **TASK-05: Register Managed Scopes in Domain Registry**
90
- - **File**: `src/plan/domainRegistry.js`
91
- - **Action**:
92
- 1. Import `emitAttributeTreeTargetValueStandard` and `attributeTreeTargetValueStandardManagedPaths`.
93
- 2. Register new scope `attribute-tree-target-value-standard`.
94
- 3. Pass `directives` into `resolveEmissionGroups(context)`.
95
- - **Acceptance Criteria**: All 5 dynamic schema scopes registered and tracked in `.generate-service-code-v2-managed.json`.
96
-
97
- - [x] **TASK-06: Decouple Generation Pipeline in `src/generate.js`**
98
- - **File**: `src/generate.js`
99
- - **Action**:
100
- 1. Load directives: `const directives = await loadGenerateDirectives(project, schemaLoadOptions);`.
101
- 2. Pass `directives` to `resolveEmissionGroups`.
102
- 3. Delete lines 104-111 (`targetFiles = await _emitTargetServiceAttributeTreeSchemas(...)`).
103
- 4. Delete lines 129-186 (`_emitTargetServiceAttributeTreeSchemas` function definition).
104
- - **Acceptance Criteria**: Zero cross-repo filesystem writes. Generator operates strictly within `workspaceOutputRoot`.
105
-
106
- - [x] **TASK-07: Delete Legacy Cross-Repo Files**
107
- - **Files to Delete**:
108
- - `src/emit/schemas/attributeTree/resolveTargetServicePath.js`
109
- - `src/emit/schemas/attributeTree/emitAttributeTreeTargetSchemas.js`
110
- - **Action**: Remove dead files and any unused imports.
111
- - **Acceptance Criteria**: `git status` shows clean file deletions.
53
+ 1. Emit `${tag}ValueStandard.js` directly within `emitAttributeTreeObjectSchemas`.
54
+ 2. Add `${tag}ValueStandard.js` to `attributeTreeObjectSchemaManagedPaths`.
55
+ 3. Include preservation hook `//(<addOnDataStructure${tag}ValueStandard>)` with commented `propertyValue` addOn template.
56
+ - **Acceptance Criteria**: Single-service attributeTree generates all 4 ObjectSchemas (`Link`, `Value`, `Label`, `ValueStandard`).
112
57
 
113
- - [x] **TASK-08: Unit & Integration Test Verification**
58
+ - [x] **TASK-03: Remove Target Directive Scope & Dead Code**
114
59
  - **Files**:
115
- - `test/unit/schemas/attributeTreeDirectives.test.js` (NEW)
116
- - `test/unit/schemas/attributeTreeSchemas.test.js` (Update paths)
117
- - `test/unit/schemas/propertyValueSchemas.test.js` (Update paths)
118
- - **Verification Command**: `npm test`
119
- - **Acceptance Criteria**: All 198+ tests pass with zero regression.
120
-
121
- - [x] **TASK-09: End-to-End Cascading Verification**
122
- - **Action**: Verify two-phase generation locally:
123
- 1. Target service with directive -> generates `sellOfferTermValueStandard.js`.
124
- 2. Target service uncomments `propertyValue` -> generates PropertyLabel, PropertyValue, and 3 Relationship schemas.
125
- - **Acceptance Criteria**: Zero cross-repo dependency required.
126
-
127
- - [x] **TASK-10: Synchronize Vault & Taiga Documentation**
60
+ - `src/plan/domainRegistry.js` (Removed `attribute-tree-target-value-standard` scope & unused `directives` param)
61
+ - `src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js` (Deleted dead file)
62
+ - `src/compile/loadSchemas.js` (Removed obsolete `loadGenerateDirectives`)
63
+ - `src/generate.js` (Removed `loadGenerateDirectives` call & `directives` passing)
64
+ - `test/unit/schemas/attributeTreeSingleService.test.js` (Removed obsolete directive loader tests)
65
+ - **Acceptance Criteria**: Clean scope tracking under `attribute-tree-object-schemas` with zero dead directive loading.
66
+
67
+ - [x] **TASK-04: Single-Service & Cascading Integration Verification**
68
+ - **File**: `test/unit/schemas/attributeTreeSingleService.test.js`
69
+ - **Action**: Verifies single-service generation and cascading generation to `propertyValue` within the same repository.
70
+ - **Acceptance Criteria**: 100% passing tests with zero cross-repo write or external directive needed.
71
+
72
+ - [x] **TASK-05: Synchronize Vault & Documentation**
128
73
  - **Vault Files**:
129
74
  - `10.Systems/GenerateServiceCode/20-Schema/Dynamic Schema.md`
130
75
  - `40.Vault/Operations/timeline-log.md`
131
76
  - `40.Vault/Memory/active-context.md`
132
- - **Taiga Wiki**: Update `generatecode` wiki page.
@@ -544,6 +544,64 @@ test('validateFlowSchema passes for s3 event with valid bucketName', () => {
544
544
  );
545
545
  });
546
546
 
547
+ test('validateFlowSchema passes for s3 event with explicit S3 flowSteps', () => {
548
+ assert.equal(
549
+ validateFlowSchema({
550
+ flowTag: 'uploadFlow',
551
+ event: ['s3'],
552
+ bucketName: 'MyUploadBucket',
553
+ flowSteps: [
554
+ { stepName: 'CreateObject', event: ['wbs'] },
555
+ { stepName: 'ReservedLimit', event: ['sqs'] },
556
+ { stepName: 'CreatePreSignUrl', event: ['sqs'] },
557
+ { stepName: 'ProcessAfterUpload', event: ['s3'] },
558
+ { stepName: 'ConfirmAfterUpload', event: ['sqs'] }
559
+ ]
560
+ }),
561
+ true
562
+ );
563
+ });
564
+
565
+ test('validateFlowSchema passes for s3 event without flowSteps', () => {
566
+ assert.equal(
567
+ validateFlowSchema({
568
+ flowTag: 'uploadFlow',
569
+ event: ['s3'],
570
+ bucketName: 'MyUploadBucket'
571
+ }),
572
+ true
573
+ );
574
+ });
575
+
576
+ test('validateFlowSchema rejects s3 event with invalid entry stepName', () => {
577
+ assert.throws(
578
+ () =>
579
+ validateFlowSchema({
580
+ flowTag: 'uploadFlow',
581
+ event: ['s3'],
582
+ bucketName: 'MyUploadBucket',
583
+ flowSteps: [{ stepName: 'InvalidEntry' }]
584
+ }),
585
+ /Entry step flowSteps\[0\] for s3 flow must have stepName 'CreateObject' or 'Start'/
586
+ );
587
+ });
588
+
589
+ test('validateFlowSchema rejects s3 event with invalid non-entry stepName', () => {
590
+ assert.throws(
591
+ () =>
592
+ validateFlowSchema({
593
+ flowTag: 'uploadFlow',
594
+ event: ['s3'],
595
+ bucketName: 'MyUploadBucket',
596
+ flowSteps: [
597
+ { stepName: 'CreateObject' },
598
+ { stepName: 'InvalidStep' }
599
+ ]
600
+ }),
601
+ /is invalid for s3 flow/
602
+ );
603
+ });
604
+
547
605
  test('validateFlowSchema rejects event with storedCache or triggerCache as invalid event', () => {
548
606
  assert.throws(
549
607
  () =>
@@ -23,7 +23,7 @@ test('generate commits schemas, application artifacts, and resources in one publ
23
23
  const rootPath = await createCrudProject();
24
24
  const result = await generate(rootPath);
25
25
 
26
- assert.strictEqual(result.files.length, 74);
26
+ assert.strictEqual(result.files.length, 59);
27
27
  assert.equal(result.project.rootPath, rootPath);
28
28
  assert.match(
29
29
  await readFile(
@@ -366,7 +366,7 @@ test('generate removes stale CRUD schemas and application artifacts', async () =
366
366
 
367
367
  const result = await generate(rootPath);
368
368
 
369
- assert.equal(result.files.length, 38);
369
+ assert.equal(result.files.length, 23);
370
370
  await assert.rejects(
371
371
  readFile(
372
372
  join(
@@ -448,36 +448,45 @@ async function createTriggerCacheProject() {
448
448
  }
449
449
 
450
450
  test(
451
- 'emits ignoreExternalServices hooks in UploadSchema_Main.js and ' +
452
- 'commands/uploadSchema.js',
451
+ 'emits ignoreExternalServices hooks in commands/uploadSchema.js',
453
452
  async () => {
454
453
  const rootPath = await createCrudProject();
455
454
  await generate(rootPath);
456
455
 
457
- const uploadSchemaMain = await readFile(
456
+ // const uploadSchemaMain = await readFile(
457
+ // join(
458
+ // rootPath,
459
+ // 'app/src/generatedCode/CoreFunctions/UploadSchema_Main.js'
460
+ // ),
461
+ // 'utf8'
462
+ // );
463
+ // assert.match(uploadSchemaMain, /\/\/\(<ignoreExternalServices>\)/);
464
+ // assert.match(uploadSchemaMain, /\/\/\(<\/ignoreExternalServices>\)/);
465
+
466
+ // const coreResource = await readFile(
467
+ // join(
468
+ // rootPath,
469
+ // 'app/sls_yaml/generatedCode/source/core-resource.yml'
470
+ // ),
471
+ // 'utf8'
472
+ // );
473
+ // assert.match(
474
+ // coreResource,
475
+ // /UserAccount\$\{self:custom\.iz_stage\}AddLinkCreateByRelHdrInv/
476
+ // );
477
+ // assert.match(
478
+ // coreResource,
479
+ // /UserAccount\$\{self:custom\.iz_stage\}AddLinkBelongToHdrInv/
480
+ // );
481
+
482
+ const uploadSchemaCommand = await readFile(
458
483
  join(
459
484
  rootPath,
460
- 'app/src/generatedCode/CoreFunctions/UploadSchema_Main.js'
485
+ 'app/initial_setup/generatedCode/source/commands/uploadSchema.js'
461
486
  ),
462
487
  'utf8'
463
488
  );
464
- assert.match(uploadSchemaMain, /\/\/\(<ignoreExternalServices>\)/);
465
- assert.match(uploadSchemaMain, /\/\/\(<\/ignoreExternalServices>\)/);
466
-
467
- const coreResource = await readFile(
468
- join(
469
- rootPath,
470
- 'app/sls_yaml/generatedCode/source/core-resource.yml'
471
- ),
472
- 'utf8'
473
- );
474
- assert.match(
475
- coreResource,
476
- /UserAccount\$\{self:custom\.iz_stage\}AddLinkCreateByRelHdrInv/
477
- );
478
- assert.match(
479
- coreResource,
480
- /UserAccount\$\{self:custom\.iz_stage\}AddLinkBelongToHdrInv/
481
- );
489
+ assert.match(uploadSchemaCommand, /\/\/\(<ignoreExternalServices>\)/);
490
+ assert.match(uploadSchemaCommand, /\/\/\(<\/ignoreExternalServices>\)/);
482
491
  }
483
492
  );