@izara_project/izara-core-generate-service-code 1.0.67 → 1.0.69

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 (128) hide show
  1. package/.prettierrc +11 -0
  2. package/package.json +16 -12
  3. package/scripts/format-ejs.js +104 -0
  4. package/src/compile/compileProject.js +40 -9
  5. package/src/compile/loadSchemas.js +120 -11
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +188 -41
  7. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +34 -18
  8. package/src/emit/crud/generateCrudApplicationArtifacts.js +14 -6
  9. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +4 -1
  10. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +296 -105
  11. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +222 -144
  12. package/src/emit/crud/templates/FlowEndpointBeforeLogical_Main.ejs +22 -6
  13. package/src/emit/crud/templates/FlowEndpointComplete_Main.ejs +134 -43
  14. package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +51 -31
  15. package/src/emit/crud/templates/FlowHandler_HdrApi.ejs +53 -45
  16. package/src/emit/crud/templates/FlowHandler_HdrInv.ejs +28 -26
  17. package/src/emit/crud/templates/FlowHandler_HdrSqs.ejs +15 -11
  18. package/src/emit/crud/templates/FlowMain_Wbs.ejs +53 -38
  19. package/src/emit/crud/templates/GetEndpoint_Main.ejs +6 -3
  20. package/src/emit/crud/templates/GetRangeEndpoint_Main.ejs +2 -2
  21. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +402 -249
  22. package/src/emit/custom/customFlows.js +68 -28
  23. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +520 -123
  24. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +28 -8
  25. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +48 -36
  26. package/src/emit/custom/templates/FlowStep_Main.ejs +7 -9
  27. package/src/emit/custom/templates/s3/ConfirmReserved_HdrSqs.ejs +59 -54
  28. package/src/emit/custom/templates/s3/ConfirmReserved_Main.ejs +33 -23
  29. package/src/emit/custom/templates/s3/CreateObject_HdrWbs.ejs +30 -23
  30. package/src/emit/custom/templates/s3/CreateObject_Main.ejs +56 -39
  31. package/src/emit/custom/templates/s3/CreatePreSignUrl_HdrSqs.ejs +56 -51
  32. package/src/emit/custom/templates/s3/CreatePreSignUrl_Main.ejs +112 -75
  33. package/src/emit/custom/templates/s3/FlowHandler_HdrS3.ejs +14 -9
  34. package/src/emit/custom/templates/s3/ProcessS3File_Main.ejs +11 -8
  35. package/src/emit/custom/templates/s3/ReservedLimit_HdrSqs.ejs +65 -56
  36. package/src/emit/custom/templates/s3/ReservedLimit_Main.ejs +74 -52
  37. package/src/emit/custom/templates/triggerCache/CheckTriggerCache_HdrSqs.ejs +58 -14
  38. package/src/emit/custom/templates/triggerCache/CheckTriggerCache_Main.ejs +12 -17
  39. package/src/emit/custom/templates/triggerCache/TriggerCacheComplete_Main.ejs +20 -13
  40. package/src/emit/custom/templates/triggerCache/TriggerCache_Main.ejs +4 -1
  41. package/src/emit/function/functionYamlArtifact.js +272 -79
  42. package/src/emit/function/templates/ApiFunction_Yaml.ejs +7 -0
  43. package/src/emit/function/templates/EventBridgeFunction_Yaml.ejs +6 -0
  44. package/src/emit/function/templates/InvFunction_Yaml.ejs +5 -0
  45. package/src/emit/function/templates/S3Function_Yaml.ejs +16 -0
  46. package/src/emit/function/templates/SqsFunction_Yaml.ejs +10 -0
  47. package/src/emit/function/templates/WbsFunction_Yaml.ejs +10 -0
  48. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +40 -29
  49. package/src/emit/rbac/emitRbacApplicationArtifacts.js +171 -72
  50. package/src/emit/rbac/generateCreateTargetRoleHandler.js +6 -4
  51. package/src/emit/rbac/generateDynamicRbacFlowSchemas.js +8 -2
  52. package/src/emit/rbac/rbacFlowTag.js +3 -1
  53. package/src/emit/rbac/templates/CreateTargetRole_HdrSqs.ejs +41 -18
  54. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +23 -3
  55. package/src/emit/rbac/templates/ListRbac_HdrApi.ejs +27 -5
  56. package/src/emit/rbac/templates/ListRbac_HdrSqs.ejs +41 -8
  57. package/src/emit/rbac/templates/ListRbac_Main.ejs +26 -6
  58. package/src/emit/rbac/templates/RbacMutation_Main.ejs +23 -3
  59. package/src/emit/rbac/templates/UserRbacFlow_HdrSqs.ejs +46 -0
  60. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +152 -33
  61. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +24 -4
  62. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +26 -11
  63. package/src/emit/relationship/emitRelationshipApplicationArtifacts.js +179 -37
  64. package/src/emit/relationship/generateDynamicRelationshipFlowSchemas.js +8 -2
  65. package/src/emit/relationship/generateRelationshipApplicationArtifacts.js +15 -6
  66. package/src/emit/relationship/templates/ChangeRelationshipComplete_Main.ejs +64 -37
  67. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +258 -142
  68. package/src/emit/relationship/templates/CreateRelationshipComplete_Main.ejs +68 -43
  69. package/src/emit/relationship/templates/CreateRelationship_Main.ejs +315 -171
  70. package/src/emit/relationship/templates/DeleteRelationshipComplete_Main.ejs +67 -41
  71. package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +226 -115
  72. package/src/emit/relationship/templates/FlowHandler_HdrApi.ejs +41 -33
  73. package/src/emit/relationship/templates/FlowHandler_HdrDsq.ejs +15 -11
  74. package/src/emit/relationship/templates/FlowHandler_HdrInv.ejs +28 -26
  75. package/src/emit/relationship/templates/FlowHandler_HdrSqs.ejs +15 -11
  76. package/src/emit/relationship/templates/GetRelationshipComplete_Main.ejs +46 -26
  77. package/src/emit/relationship/templates/GetRelationship_Main.ejs +233 -126
  78. package/src/emit/relationship/templates/MoveRelationshipComplete_Main.ejs +72 -44
  79. package/src/emit/relationship/templates/MoveRelationship_Main.ejs +266 -150
  80. package/src/emit/relationship/templates/UpdateRelationshipComplete_Main.ejs +74 -45
  81. package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +285 -152
  82. package/src/emit/resource/emitResourceArtifacts.js +274 -105
  83. package/src/emit/resource/generateResourceArtifacts.js +15 -6
  84. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +139 -0
  85. package/src/emit/resource/templates/S3Bucket_Yaml.ejs +19 -0
  86. package/src/emit/resource/templates/S3Subscription_Yaml.ejs +7 -0
  87. package/src/emit/resource/templates/WebSocketSubscription_Yaml.ejs +7 -0
  88. package/src/emit/system/emitSystemWebSocketArtifacts.js +61 -165
  89. package/src/emit/system/templates/WebSocketComplete_HdrSqs.ejs +32 -0
  90. package/src/emit/system/templates/WebSocketComplete_Main.ejs +46 -0
  91. package/src/emit/system/templates/WebSocketComplete_Yaml.ejs +19 -0
  92. package/src/emit/system/templates/WebSocketConnect.ejs +25 -0
  93. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +11 -0
  94. package/src/generate.js +141 -34
  95. package/src/generateIntTest.js +76 -28
  96. package/src/generateResourceIntTest.js +35 -12
  97. package/src/generateResources/IntTest/generateResources/generateResources.js +5 -2
  98. package/src/generateResources/IntTest/generateResources/templateResources.ejs +18 -30
  99. package/src/generateResources/IntTest/generateTests/events/events.js +1 -1
  100. package/src/generateResources/IntTest/generateTests/events/templateEvents.ejs +13 -10
  101. package/src/generateResources/IntTest/generateTests/generateTemplate.js +2 -2
  102. package/src/generateResources/IntTest/generateTests/pathIntTest/pathIntTest.js +2 -1
  103. package/src/generateResources/IntTest/generateTests/pathIntTest/template.ejs +1 -3
  104. package/src/generateResources/IntTest/generateTests/tests/templateTests.ejs +1 -1
  105. package/src/generateResources/IntTest/generateTests/tests/tests.js +7 -3
  106. package/src/generateResources/IntTest/libs/{libs.js → IntTestGenerateLib.js} +34 -23
  107. package/src/generateResources/IntTest/upload/uploadIntTest.js +33 -19
  108. package/src/intTestGeneratorShared.js +15 -10
  109. package/src/output/commitArtifacts.js +99 -39
  110. package/src/output/mergeHooks.js +24 -10
  111. package/src/output/validateArtifacts.js +37 -27
  112. package/src/plan/buildGenerationPlan.js +24 -13
  113. package/test/core.test.js +116 -49
  114. package/test/createTargetRoleHandler.test.js +63 -14
  115. package/test/crudApplicationArtifacts.test.js +128 -23
  116. package/test/customFlowApplicationArtifacts.test.js +391 -84
  117. package/test/dynamicCrudFlowSchemas.test.js +74 -26
  118. package/test/dynamicRbacFlowSchemas.test.js +63 -20
  119. package/test/dynamicRelationshipFlowSchemas.test.js +65 -18
  120. package/test/functionYamlArtifact.test.js +67 -10
  121. package/test/intTestApi.test.js +9 -10
  122. package/test/publicApi.test.js +184 -44
  123. package/test/relationshipApplicationArtifacts.test.js +90 -19
  124. package/test/resourceArtifacts.test.js +183 -33
  125. package/docs/test.png +0 -0
  126. package/src/emit/rbac/templates/UserRbacFlow_HdrInv.ejs +0 -10
  127. package/src/emit/relationship/dynamicRelationshipFlowSchemaTemplate.js +0 -11
  128. /package/docs/{test.mmd → TriggeredCacheFlow.mmd} +0 -0
@@ -1,24 +1,33 @@
1
- import { normalizeArtifactPaths, validateArtifacts } from '../output/validateArtifacts.js';
1
+ import {
2
+ normalizeArtifactPaths,
3
+ validateArtifacts
4
+ } from '../output/validateArtifacts.js';
2
5
 
3
- export function buildGenerationPlan(project, artifacts = [], { managedPaths = [], managedScope, managedScopes } = {}) {
6
+ export function buildGenerationPlan(
7
+ project,
8
+ artifacts = [],
9
+ { managedPaths = [], managedScope, managedScopes } = {}
10
+ ) {
4
11
  if (!project?.rootPath || !project?.outputPath) {
5
12
  throw new TypeError('ProjectModel must include rootPath and outputPath');
6
13
  }
7
14
  if (managedScope !== undefined && managedScopes !== undefined) {
8
- throw new TypeError('GenerationPlan must use managedScope or managedScopes, not both');
15
+ throw new TypeError(
16
+ 'GenerationPlan must use managedScope or managedScopes, not both'
17
+ );
9
18
  }
10
19
  return Object.freeze({
11
20
  project,
12
21
  artifacts: validateArtifacts(artifacts),
13
22
  managedPaths: normalizeArtifactPaths(managedPaths),
14
- managedScope: normalizeManagedScope(managedScope),
15
- managedScopes: normalizeManagedScopes(managedScopes)
23
+ managedScope: _normalizeManagedScope(managedScope),
24
+ managedScopes: _normalizeManagedScopes(managedScopes)
16
25
  });
17
26
  }
18
27
 
19
- function normalizeManagedScope(scope) {
20
- if (scope === undefined) {
21
- return undefined;
28
+ function _normalizeManagedScope(scope) {
29
+ if (scope === undefined || scope === null) {
30
+ return null;
22
31
  }
23
32
  if (typeof scope !== 'string' || scope.trim() === '') {
24
33
  throw new TypeError('managedScope must be a non-empty string');
@@ -26,9 +35,9 @@ function normalizeManagedScope(scope) {
26
35
  return scope;
27
36
  }
28
37
 
29
- function normalizeManagedScopes(scopes) {
30
- if (scopes === undefined) {
31
- return undefined;
38
+ function _normalizeManagedScopes(scopes) {
39
+ if (scopes === undefined || scopes === null) {
40
+ return null;
32
41
  }
33
42
  if (!scopes || Array.isArray(scopes) || typeof scopes !== 'object') {
34
43
  throw new TypeError('managedScopes must be an object of scope paths');
@@ -36,11 +45,13 @@ function normalizeManagedScopes(scopes) {
36
45
  const seenPaths = new Set();
37
46
  const normalized = {};
38
47
  for (const [scope, paths] of Object.entries(scopes)) {
39
- const scopeName = normalizeManagedScope(scope);
48
+ const scopeName = _normalizeManagedScope(scope);
40
49
  const normalizedPaths = normalizeArtifactPaths(paths);
41
50
  for (const path of normalizedPaths) {
42
51
  if (seenPaths.has(path)) {
43
- throw new Error(`Artifact path belongs to multiple managed scopes: ${path}`);
52
+ throw new Error(
53
+ `Artifact path belongs to multiple managed scopes: ${path}`
54
+ );
44
55
  }
45
56
  seenPaths.add(path);
46
57
  }
package/test/core.test.js CHANGED
@@ -1,5 +1,12 @@
1
1
  import assert from 'node:assert/strict';
2
- import { mkdir, mkdtemp, readFile, readdir, symlink, writeFile } from 'node:fs/promises';
2
+ import {
3
+ mkdir,
4
+ mkdtemp,
5
+ readFile,
6
+ readdir,
7
+ symlink,
8
+ writeFile
9
+ } from 'node:fs/promises';
3
10
  import { tmpdir } from 'node:os';
4
11
  import { dirname, join } from 'node:path';
5
12
  import test from 'node:test';
@@ -15,7 +22,9 @@ test('compiles a request and commits only declared relative artifacts', async ()
15
22
  { path: 'app/generated/example.js', content: 'export default 1;\n' }
16
23
  ]);
17
24
 
18
- assert.deepEqual(await commitArtifacts(plan), [{ path: 'app/generated/example.js' }]);
25
+ assert.deepEqual(await commitArtifacts(plan), [
26
+ { path: 'app/generated/example.js' }
27
+ ]);
19
28
  assert.equal(
20
29
  await readFile(join(outputPath, 'app/generated/example.js'), 'utf8'),
21
30
  'export default 1;\n'
@@ -25,25 +34,35 @@ test('compiles a request and commits only declared relative artifacts', async ()
25
34
  test('rejects invalid generation inputs before writing files', async () => {
26
35
  assert.throws(() => compileProject({}), /rootPath/);
27
36
 
28
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-invalid-'));
37
+ const outputPath = await mkdtemp(
38
+ join(tmpdir(), 'generate-service-code-v2-invalid-')
39
+ );
29
40
  const project = compileProject({ rootPath: outputPath });
30
41
  await assert.rejects(
31
- Promise.resolve().then(() => buildGenerationPlan(project, [{ path: '../outside.js', content: '' }])),
42
+ Promise.resolve().then(() =>
43
+ buildGenerationPlan(project, [{ path: '../outside.js', content: '' }])
44
+ ),
32
45
  /escapes output root/
33
46
  );
34
47
  await assert.rejects(
35
- Promise.resolve().then(() => buildGenerationPlan(project, [
36
- { path: 'same.js', content: '' },
37
- { path: 'same.js', content: '' }
38
- ])),
48
+ Promise.resolve().then(() =>
49
+ buildGenerationPlan(project, [
50
+ { path: 'same.js', content: '' },
51
+ { path: 'same.js', content: '' }
52
+ ])
53
+ ),
39
54
  /Duplicate artifact path/
40
55
  );
41
56
  await assert.rejects(readFile(join(outputPath, 'same.js')));
42
57
  });
43
58
 
44
59
  test('rejects a symlinked output directory', async () => {
45
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-output-'));
46
- const outsidePath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-outside-'));
60
+ const outputPath = await mkdtemp(
61
+ join(tmpdir(), 'generate-service-code-v2-output-')
62
+ );
63
+ const outsidePath = await mkdtemp(
64
+ join(tmpdir(), 'generate-service-code-v2-outside-')
65
+ );
47
66
  await symlink(outsidePath, join(outputPath, 'linked'));
48
67
 
49
68
  const plan = buildGenerationPlan(compileProject({ rootPath: outputPath }), [
@@ -55,64 +74,104 @@ test('rejects a symlinked output directory', async () => {
55
74
  });
56
75
 
57
76
  test('does not delete a legacy file before a managed scope has established ownership', async () => {
58
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-managed-scope-'));
77
+ const outputPath = await mkdtemp(
78
+ join(tmpdir(), 'generate-service-code-v2-managed-scope-')
79
+ );
59
80
  const project = compileProject({ rootPath: outputPath });
60
81
  await writeFile(join(outputPath, 'legacy.js'), 'legacy\n');
61
82
 
62
- await commitArtifacts(buildGenerationPlan(project, [], {
63
- managedPaths: ['legacy.js'],
64
- managedScope: 'example-scope'
65
- }));
66
- assert.equal(await readFile(join(outputPath, 'legacy.js'), 'utf8'), 'legacy\n');
67
-
68
- await commitArtifacts(buildGenerationPlan(project, [{ path: 'legacy.js', content: 'v2\n' }], {
69
- managedPaths: ['legacy.js'],
70
- managedScope: 'example-scope'
71
- }));
72
- await commitArtifacts(buildGenerationPlan(project, [], {
73
- managedPaths: ['legacy.js'],
74
- managedScope: 'example-scope'
75
- }));
83
+ await commitArtifacts(
84
+ buildGenerationPlan(project, [], {
85
+ managedPaths: ['legacy.js'],
86
+ managedScope: 'example-scope'
87
+ })
88
+ );
89
+ assert.equal(
90
+ await readFile(join(outputPath, 'legacy.js'), 'utf8'),
91
+ 'legacy\n'
92
+ );
93
+
94
+ await commitArtifacts(
95
+ buildGenerationPlan(project, [{ path: 'legacy.js', content: 'v2\n' }], {
96
+ managedPaths: ['legacy.js'],
97
+ managedScope: 'example-scope'
98
+ })
99
+ );
100
+ await commitArtifacts(
101
+ buildGenerationPlan(project, [], {
102
+ managedPaths: ['legacy.js'],
103
+ managedScope: 'example-scope'
104
+ })
105
+ );
76
106
  await assert.rejects(readFile(join(outputPath, 'legacy.js')));
77
107
  });
78
108
 
79
109
  test('commits and cleans multiple managed scopes in one transaction', async () => {
80
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-multi-scope-'));
110
+ const outputPath = await mkdtemp(
111
+ join(tmpdir(), 'generate-service-code-v2-multi-scope-')
112
+ );
81
113
  const project = compileProject({ rootPath: outputPath });
82
- const managedScopes = { schemas: ['schemas.js'], application: ['application.js'] };
83
- await commitArtifacts(buildGenerationPlan(project, [
84
- { path: 'schemas.js', content: 'schema\n' },
85
- { path: 'application.js', content: 'application\n' }
86
- ], { managedScopes }));
114
+ const managedScopes = {
115
+ schemas: ['schemas.js'],
116
+ application: ['application.js']
117
+ };
118
+ await commitArtifacts(
119
+ buildGenerationPlan(
120
+ project,
121
+ [
122
+ { path: 'schemas.js', content: 'schema\n' },
123
+ { path: 'application.js', content: 'application\n' }
124
+ ],
125
+ { managedScopes }
126
+ )
127
+ );
87
128
 
88
- await commitArtifacts(buildGenerationPlan(project, [
89
- { path: 'application.js', content: 'application next\n' }
90
- ], { managedScopes }));
129
+ await commitArtifacts(
130
+ buildGenerationPlan(
131
+ project,
132
+ [{ path: 'application.js', content: 'application next\n' }],
133
+ { managedScopes }
134
+ )
135
+ );
91
136
 
92
137
  await assert.rejects(readFile(join(outputPath, 'schemas.js')));
93
- assert.equal(await readFile(join(outputPath, 'application.js'), 'utf8'), 'application next\n');
138
+ assert.equal(
139
+ await readFile(join(outputPath, 'application.js'), 'utf8'),
140
+ 'application next\n'
141
+ );
94
142
  });
95
143
 
96
144
  test('merges preserved hook blocks into staged source without changing the hook', async () => {
97
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-hooks-'));
145
+ const outputPath = await mkdtemp(
146
+ join(tmpdir(), 'generate-service-code-v2-hooks-')
147
+ );
98
148
  const hookPath = join(outputPath, 'app/generated/hook/example.js');
99
149
  await mkdir(dirname(hookPath), { recursive: true });
100
150
  await writeFile(hookPath, '//(<hookCode>)\ncustom();\n//(</hookCode>)\n');
101
151
 
102
- await commitArtifacts(buildGenerationPlan(compileProject({ rootPath: outputPath }), [{
103
- path: 'app/generated/source/example.js',
104
- content: '//(<hookCode>)\ndefault();\n//(</hookCode>)\n'
105
- }]));
152
+ await commitArtifacts(
153
+ buildGenerationPlan(compileProject({ rootPath: outputPath }), [
154
+ {
155
+ path: 'app/generated/source/example.js',
156
+ content: '//(<hookCode>)\ndefault();\n//(</hookCode>)\n'
157
+ }
158
+ ])
159
+ );
106
160
 
107
161
  assert.equal(
108
162
  await readFile(join(outputPath, 'app/generated/source/example.js'), 'utf8'),
109
163
  '//(<hookCode>)\ncustom();\n//(</hookCode>)\n'
110
164
  );
111
- assert.equal(await readFile(hookPath, 'utf8'), '//(<hookCode>)\ncustom();\n//(</hookCode>)\n');
165
+ assert.equal(
166
+ await readFile(hookPath, 'utf8'),
167
+ '//(<hookCode>)\ncustom();\n//(</hookCode>)\n'
168
+ );
112
169
  });
113
170
 
114
171
  test('rejects an invalid rendered artifact before staging output', async () => {
115
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-invalid-render-'));
172
+ const outputPath = await mkdtemp(
173
+ join(tmpdir(), 'generate-service-code-v2-invalid-render-')
174
+ );
116
175
  const sourcePath = join(outputPath, 'app/generated/source/example.js');
117
176
  await mkdir(dirname(sourcePath), { recursive: true });
118
177
  await writeFile(sourcePath, 'existing source\n');
@@ -126,13 +185,17 @@ test('rejects an invalid rendered artifact before staging output', async () => {
126
185
  );
127
186
  assert.equal(await readFile(sourcePath, 'utf8'), 'existing source\n');
128
187
  assert.deepEqual(
129
- (await readdir(outputPath)).filter(name => name.startsWith('.generate-service-code-v2-stage-')),
188
+ (await readdir(outputPath)).filter((name) =>
189
+ name.startsWith('.generate-service-code-v2-stage-')
190
+ ),
130
191
  []
131
192
  );
132
193
  });
133
194
 
134
195
  test('rejects a failed output transaction without partial files, hooks, or staging residue', async () => {
135
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-transaction-'));
196
+ const outputPath = await mkdtemp(
197
+ join(tmpdir(), 'generate-service-code-v2-transaction-')
198
+ );
136
199
  const project = compileProject({ rootPath: outputPath });
137
200
  const sourcePath = join(outputPath, 'app/generated/source/first.js');
138
201
  const hookPath = join(outputPath, 'app/generated/hook/first.js');
@@ -143,16 +206,20 @@ test('rejects a failed output transaction without partial files, hooks, or stagi
143
206
  await mkdir(join(outputPath, 'app/generated/source/second.js'));
144
207
 
145
208
  await assert.rejects(
146
- commitArtifacts(buildGenerationPlan(project, [
147
- { path: 'app/generated/source/first.js', content: 'new source\n' },
148
- { path: 'app/generated/source/second.js', content: 'never committed\n' }
149
- ])),
209
+ commitArtifacts(
210
+ buildGenerationPlan(project, [
211
+ { path: 'app/generated/source/first.js', content: 'new source\n' },
212
+ { path: 'app/generated/source/second.js', content: 'never committed\n' }
213
+ ])
214
+ ),
150
215
  /not a file/
151
216
  );
152
217
  assert.equal(await readFile(sourcePath, 'utf8'), 'old source\n');
153
218
  assert.equal(await readFile(hookPath, 'utf8'), 'custom hook\n');
154
219
  assert.deepEqual(
155
- (await readdir(outputPath)).filter(name => name.startsWith('.generate-service-code-v2-stage-')),
220
+ (await readdir(outputPath)).filter((name) =>
221
+ name.startsWith('.generate-service-code-v2-stage-')
222
+ ),
156
223
  []
157
224
  );
158
225
  assert.equal((await readdir(outputPath)).includes('.bak'), false);
@@ -15,27 +15,55 @@ const OWN_TOPIC_FLOWS = [
15
15
  'CreateUserRole',
16
16
  'DeleteUserFromRole'
17
17
  ];
18
- after(() => Promise.all([...roots].map(root => rm(root, { recursive: true, force: true }))));
18
+ after(() =>
19
+ Promise.all(
20
+ [...roots].map((root) => rm(root, { recursive: true, force: true }))
21
+ )
22
+ );
19
23
 
20
24
  test('generates RBAC own-topic WebSocket entries alongside their SQS handlers', async () => {
21
25
  const rootPath = await createProject(true);
22
26
  const result = await generateCreateTargetRoleHandler(rootPath);
23
27
 
24
28
  assert.equal(result.files.length, 36);
25
- assert.match(await readFile(join(rootPath, result.files[0].path), 'utf8'), /ProcessCreateTargetRole/);
26
- assert.match(await readFile(join(rootPath, result.files[1].path), 'utf8'), /ProcessCreateTargetRoleHdrSqs/);
27
29
  assert.match(
28
- await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml'), 'utf8'),
30
+ await readFile(join(rootPath, result.files[0].path), 'utf8'),
31
+ /CreateTargetRole/
32
+ );
33
+ assert.match(
34
+ await readFile(join(rootPath, result.files[1].path), 'utf8'),
35
+ /CreateTargetRoleHdrSqs/
36
+ );
37
+ assert.match(
38
+ await readFile(
39
+ join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml'),
40
+ 'utf8'
41
+ ),
29
42
  /FlowRbacRole/
30
43
  );
31
44
  assert.match(
32
- await readFile(join(rootPath, 'app/src/generatedCode/FlowRbac/ListTargetRole/source/ProcessListTargetRole_HdrApi.js'), 'utf8'),
33
- /ProcessListTargetRole/
45
+ await readFile(
46
+ join(
47
+ rootPath,
48
+ 'app/src/generatedCode/FlowRbac/ListTargetRole/source/ListTargetRole_HdrApi.js'
49
+ ),
50
+ 'utf8'
51
+ ),
52
+ /ListTargetRole/
53
+ );
54
+ const yaml = await readFile(
55
+ join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml'),
56
+ 'utf8'
34
57
  );
35
- const yaml = await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml'), 'utf8');
36
58
  for (const flowTag of OWN_TOPIC_FLOWS) {
37
59
  assert.match(
38
- await readFile(join(rootPath, `app/src/generatedCode/FlowRbac/${flowTag}/source/${flowTag}_HdrWbs.js`), 'utf8'),
60
+ await readFile(
61
+ join(
62
+ rootPath,
63
+ `app/src/generatedCode/FlowRbac/${flowTag}/source/${flowTag}_HdrWbs.js`
64
+ ),
65
+ 'utf8'
66
+ ),
39
67
  new RegExp(`import ${flowTag}`)
40
68
  );
41
69
  assert.match(yaml, new RegExp(`${flowTag}HdrWbs:\\n[\\s\\S]*websocket:`));
@@ -46,23 +74,44 @@ test('removes CreateTargetRole handler when RBAC is removed', async () => {
46
74
  const rootPath = await createProject(true);
47
75
  const objectPath = join(rootPath, 'app/src/schemas/ObjectSchemas/User.js');
48
76
  await generateCreateTargetRoleHandler(rootPath);
49
- await writeFile(objectPath, "export default { objectType: 'user', addOnDataStructure: [] };\n");
77
+ await writeFile(
78
+ objectPath,
79
+ "export default { objectType: 'user', addOnDataStructure: [] };\n"
80
+ );
50
81
 
51
82
  assert.deepEqual((await generateCreateTargetRoleHandler(rootPath)).files, []);
52
- await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowRbac/CreateTargetRole/source/ProcessCreateTargetRole_Main.js')));
53
- await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_HdrWbs.js')));
54
- await assert.rejects(readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml')));
83
+ await assert.rejects(
84
+ readFile(
85
+ join(
86
+ rootPath,
87
+ 'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_Main.js'
88
+ )
89
+ )
90
+ );
91
+ await assert.rejects(
92
+ readFile(
93
+ join(
94
+ rootPath,
95
+ 'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_HdrWbs.js'
96
+ )
97
+ )
98
+ );
99
+ await assert.rejects(
100
+ readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-rbac.yml'))
101
+ );
55
102
  });
56
103
 
57
104
  async function createProject(hasRbac) {
58
- const rootPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-rbac-handler-'));
105
+ const rootPath = await mkdtemp(
106
+ join(tmpdir(), 'generate-service-code-v2-rbac-handler-')
107
+ );
59
108
  roots.add(rootPath);
60
109
  const directory = join(rootPath, 'app/src/schemas/ObjectSchemas');
61
110
  const addOns = hasRbac ? "[{ type: 'rbac' }]" : '[]';
62
111
  await mkdir(directory, { recursive: true });
63
112
  await writeFile(
64
113
  join(directory, 'User.js'),
65
- `export default { objectType: 'user', addOnDataStructure: ${addOns} };\n`
114
+ `export default { objectType: 'user', fieldNames: [], identifiers: [], addOnDataStructure: ${addOns} };\n`
66
115
  );
67
116
  return rootPath;
68
117
  }
@@ -5,26 +5,49 @@ import { tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
6
  import test, { after } from 'node:test';
7
7
 
8
+ process.env.NODE_ENV = 'test';
9
+
8
10
  import { generateCrudApplicationArtifacts } from '../src/emit/crud/generateCrudApplicationArtifacts.js';
9
11
 
10
12
  const roots = new Set();
11
- after(() => Promise.all([...roots].map(root => rm(root, { recursive: true, force: true }))));
13
+ after(() =>
14
+ Promise.all(
15
+ [...roots].map((root) => rm(root, { recursive: true, force: true }))
16
+ )
17
+ );
12
18
 
13
19
  test('emits CRUD handlers and function YAML from schema data without reading generated output', async () => {
14
20
  const rootPath = await createProject(true);
15
- const result = await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
21
+ const result = await generateCrudApplicationArtifacts(rootPath, {
22
+ serviceTag: 'orders'
23
+ });
16
24
 
17
25
  assert.equal(result.files.length, 26);
18
- assert.ok(result.files.every(file => !file.path.includes('/Process')));
26
+ assert.ok(result.files.every((file) => !file.path.includes('/Process')));
19
27
  assert.match(
20
- await readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Get/source/Get_Main.js'), 'utf8'),
28
+ await readFile(
29
+ join(
30
+ rootPath,
31
+ 'app/src/generatedCode/FlowObjects/Get/source/Get_Main.js'
32
+ ),
33
+ 'utf8'
34
+ ),
21
35
  /dataFromGraphV2/
22
36
  );
23
37
  assert.match(
24
- await readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'), 'utf8'),
38
+ await readFile(
39
+ join(
40
+ rootPath,
41
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
42
+ ),
43
+ 'utf8'
44
+ ),
25
45
  /beforeLogical_order/
26
46
  );
27
- const yaml = await readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml'), 'utf8');
47
+ const yaml = await readFile(
48
+ join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml'),
49
+ 'utf8'
50
+ );
28
51
  assert.match(yaml, /CreateHdrWbs:/);
29
52
  assert.ok(yaml.indexOf('CreateHdrWbs:') < yaml.indexOf('CreateHdrSqs:'));
30
53
  assert.doesNotMatch(yaml, /handler: .*\/Process/);
@@ -34,24 +57,68 @@ test('emits CRUD handlers and function YAML from schema data without reading gen
34
57
  assert.match(yaml, /#<#CreateCompleteHdrSqsFunctionSetting#>/);
35
58
  assert.match(yaml, /filterPatterns:[\s\S]*CreateCompleteHdrSqs/);
36
59
  assert.match(
37
- await readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/CreateComplete_Main.js'), 'utf8'),
60
+ await readFile(
61
+ join(
62
+ rootPath,
63
+ 'app/src/generatedCode/FlowObjects/Create/source/CreateComplete_Main.js'
64
+ ),
65
+ 'utf8'
66
+ ),
38
67
  /checkAllAwaitingStepsFinishedWithReturnParams/
39
68
  );
40
69
  assert.doesNotMatch(
41
- await readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/Create_WbsMain.js'), 'utf8'),
70
+ await readFile(
71
+ join(
72
+ rootPath,
73
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_WbsMain.js'
74
+ ),
75
+ 'utf8'
76
+ ),
42
77
  /from '@izara_project\/izara-core-generate-service-code'/
43
78
  );
44
- await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/ProcessCreate_Main.js')));
79
+ await assert.rejects(
80
+ readFile(
81
+ join(
82
+ rootPath,
83
+ 'app/src/generatedCode/FlowObjects/Create/source/ProcessCreate_Main.js'
84
+ )
85
+ )
86
+ );
45
87
  for (const action of ['Create', 'Update', 'Delete']) {
46
- execFileSync(process.execPath, ['--check', join(rootPath, `app/src/generatedCode/FlowObjects/${action}/source/${action}Complete_Main.js`)], { stdio: 'inherit' });
47
- execFileSync(process.execPath, ['--check', join(rootPath, `app/src/generatedCode/FlowObjects/${action}/source/${action}Complete_HdrSqs.js`)], { stdio: 'inherit' });
88
+ execFileSync(
89
+ process.execPath,
90
+ [
91
+ '--check',
92
+ join(
93
+ rootPath,
94
+ `app/src/generatedCode/FlowObjects/${action}/source/${action}Complete_Main.js`
95
+ )
96
+ ],
97
+ { stdio: 'inherit' }
98
+ );
99
+ execFileSync(
100
+ process.execPath,
101
+ [
102
+ '--check',
103
+ join(
104
+ rootPath,
105
+ `app/src/generatedCode/FlowObjects/${action}/source/${action}Complete_HdrSqs.js`
106
+ )
107
+ ],
108
+ { stdio: 'inherit' }
109
+ );
48
110
  }
49
111
  });
50
112
 
51
113
  test('preserves function-setting hooks across CRUD regeneration', async () => {
52
114
  const rootPath = await createProject(true);
53
- const hookPath = join(rootPath, 'app/sls_yaml/generatedCode/hook/flow-objects.yml');
54
- await mkdir(join(rootPath, 'app/sls_yaml/generatedCode/hook'), { recursive: true });
115
+ const hookPath = join(
116
+ rootPath,
117
+ 'app/sls_yaml/generatedCode/hook/flow-objects.yml'
118
+ );
119
+ await mkdir(join(rootPath, 'app/sls_yaml/generatedCode/hook'), {
120
+ recursive: true
121
+ });
55
122
  await writeFile(
56
123
  hookPath,
57
124
  ' #<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#/CreateCompleteHdrSqsFunctionSetting#>\n'
@@ -60,15 +127,27 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
60
127
  await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
61
128
  await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
62
129
 
63
- const yamlPath = join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml');
130
+ const yamlPath = join(
131
+ rootPath,
132
+ 'app/sls_yaml/generatedCode/source/flow-objects.yml'
133
+ );
64
134
  const yaml = await readFile(yamlPath, 'utf8');
65
- assert.match(yaml, /#<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#\/CreateCompleteHdrSqsFunctionSetting#>/);
135
+ assert.match(
136
+ yaml,
137
+ /#<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#\/CreateCompleteHdrSqsFunctionSetting#>/
138
+ );
66
139
 
67
- const conflictPath = join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js');
140
+ const conflictPath = join(
141
+ rootPath,
142
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
143
+ );
68
144
  await rm(conflictPath);
69
145
  await mkdir(conflictPath);
70
146
 
71
- await assert.rejects(generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' }), /not a file/);
147
+ await assert.rejects(
148
+ generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' }),
149
+ /not a file/
150
+ );
72
151
  assert.equal(await readFile(yamlPath, 'utf8'), yaml);
73
152
  assert.equal(
74
153
  await readFile(hookPath, 'utf8'),
@@ -79,16 +158,42 @@ test('preserves function-setting hooks across CRUD regeneration', async () => {
79
158
  test('removes managed CRUD artifacts when no applicable object remains', async () => {
80
159
  const rootPath = await createProject(true);
81
160
  await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
82
- await writeFile(join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'), "export default { objectType: 'order' };\n");
161
+ await writeFile(
162
+ join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'),
163
+ "export default { objectType: 'order' };\n"
164
+ );
83
165
 
84
- assert.deepEqual((await generateCrudApplicationArtifacts(rootPath)).files, []);
85
- await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js')));
86
- await assert.rejects(readFile(join(rootPath, 'app/src/generatedCode/FlowObjects/Create/source/CreateComplete_Main.js')));
87
- await assert.rejects(readFile(join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml')));
166
+ assert.deepEqual(
167
+ (await generateCrudApplicationArtifacts(rootPath)).files,
168
+ []
169
+ );
170
+ await assert.rejects(
171
+ readFile(
172
+ join(
173
+ rootPath,
174
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
175
+ )
176
+ )
177
+ );
178
+ await assert.rejects(
179
+ readFile(
180
+ join(
181
+ rootPath,
182
+ 'app/src/generatedCode/FlowObjects/Create/source/CreateComplete_Main.js'
183
+ )
184
+ )
185
+ );
186
+ await assert.rejects(
187
+ readFile(
188
+ join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml')
189
+ )
190
+ );
88
191
  });
89
192
 
90
193
  async function createProject(withCrudObject) {
91
- const rootPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-crud-application-'));
194
+ const rootPath = await mkdtemp(
195
+ join(tmpdir(), 'generate-service-code-v2-crud-application-')
196
+ );
92
197
  roots.add(rootPath);
93
198
  const schemaRoot = join(rootPath, 'app/src/schemas');
94
199
  await mkdir(join(schemaRoot, 'ObjectSchemas'), { recursive: true });