@izara_project/izara-core-generate-service-code 1.0.68 → 1.0.70

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 (129) hide show
  1. package/package.json +1 -1
  2. package/scripts/format-ejs.js +36 -32
  3. package/src/compile/compileProject.js +26 -10
  4. package/src/compile/loadSchemas.js +122 -59
  5. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +163 -0
  6. package/src/emit/coreFunction/templates/CoreFunction_Yaml.ejs +38 -0
  7. package/src/emit/coreFunction/templates/CoreResource_Yaml.ejs +235 -0
  8. package/src/emit/coreFunction/templates/GenerateSeedDataDynamo_HdrInv.ejs +16 -0
  9. package/src/emit/coreFunction/templates/GenerateSeedDataDynamo_Main.ejs +38 -0
  10. package/src/emit/coreFunction/templates/GenerateSeedDataGraphData_HdrInv.ejs +16 -0
  11. package/src/emit/coreFunction/templates/GenerateSeedDataGraphData_Main.ejs +32 -0
  12. package/src/emit/coreFunction/templates/GenerateSeedDataGraphSchema_HdrInv.ejs +16 -0
  13. package/src/emit/coreFunction/templates/GenerateSeedDataGraphSchema_Main.ejs +71 -0
  14. package/src/emit/coreFunction/templates/GeneratedGraphNodeSchemas.ejs +47 -0
  15. package/src/emit/coreFunction/templates/GeneratedGraphRelSchemas.ejs +27 -0
  16. package/src/emit/coreFunction/templates/GeneratedTableConfigs.ejs +34 -0
  17. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +108 -0
  18. package/src/emit/coreFunction/templates/InitialSetup_HdrInv.ejs +12 -0
  19. package/src/emit/coreFunction/templates/InitialSetup_Main.ejs +51 -0
  20. package/src/emit/coreFunction/templates/RemoveSeedDataDynamo_HdrInv.ejs +16 -0
  21. package/src/emit/coreFunction/templates/RemoveSeedDataDynamo_Main.ejs +31 -0
  22. package/src/emit/coreFunction/templates/UploadSchema_HdrInv.ejs +37 -0
  23. package/src/emit/coreFunction/templates/UploadSchema_Main.ejs +52 -0
  24. package/src/emit/crud/emitCrudApplicationArtifacts.js +194 -41
  25. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +34 -17
  26. package/src/emit/crud/generateCrudApplicationArtifacts.js +14 -6
  27. package/src/emit/crud/generateDynamicCrudFlowSchemas.js +4 -1
  28. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +1 -1
  29. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +1 -1
  30. package/src/emit/crud/templates/GetEndpoint_Main.ejs +2 -2
  31. package/src/emit/crud/templates/GetRangeEndpoint_Main.ejs +1 -1
  32. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +1 -1
  33. package/src/emit/custom/customFlows.js +23 -13
  34. package/src/emit/custom/emitCustomFlowApplicationArtifacts.js +535 -114
  35. package/src/emit/custom/generateCustomFlowApplicationArtifacts.js +28 -8
  36. package/src/emit/custom/templates/FlowEndpoint_Main.ejs +2 -2
  37. package/src/emit/custom/templates/FlowStep_Main.ejs +1 -1
  38. package/src/emit/custom/templates/s3/CreateObject_Main.ejs +1 -1
  39. package/src/emit/custom/templates/s3/FlowHandler_HdrS3.ejs +2 -2
  40. package/src/emit/custom/templates/s3/ReservedLimit_HdrSqs.ejs +1 -1
  41. package/src/emit/custom/templates/s3/ReservedLimit_Main.ejs +1 -1
  42. package/src/emit/custom/templates/triggerCache/CheckTriggerCache_HdrSqs.ejs +4 -4
  43. package/src/emit/function/functionYamlArtifact.js +273 -97
  44. package/src/emit/function/templates/ApiFunction_Yaml.ejs +7 -0
  45. package/src/emit/function/templates/EventBridgeFunction_Yaml.ejs +6 -0
  46. package/src/emit/function/templates/InvFunction_Yaml.ejs +5 -0
  47. package/src/emit/function/templates/S3Function_Yaml.ejs +16 -0
  48. package/src/emit/function/templates/SqsFunction_Yaml.ejs +10 -0
  49. package/src/emit/function/templates/WbsFunction_Yaml.ejs +10 -0
  50. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +28 -30
  51. package/src/emit/rbac/emitRbacApplicationArtifacts.js +6 -6
  52. package/src/emit/rbac/generateCreateTargetRoleHandler.js +6 -4
  53. package/src/emit/rbac/generateDynamicRbacFlowSchemas.js +8 -2
  54. package/src/emit/rbac/rbacFlowTag.js +3 -1
  55. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +1 -1
  56. package/src/emit/relationship/emitDynamicRelationshipFlowSchemas.js +26 -10
  57. package/src/emit/relationship/emitRelationshipApplicationArtifacts.js +189 -37
  58. package/src/emit/relationship/generateDynamicRelationshipFlowSchemas.js +8 -2
  59. package/src/emit/relationship/generateRelationshipApplicationArtifacts.js +15 -6
  60. package/src/emit/relationship/templates/ChangeRelationship_Main.ejs +1 -1
  61. package/src/emit/relationship/templates/CreateRelationship_Main.ejs +1 -1
  62. package/src/emit/relationship/templates/DeleteRelationship_Main.ejs +1 -1
  63. package/src/emit/relationship/templates/GetRelationship_Main.ejs +1 -1
  64. package/src/emit/relationship/templates/MoveRelationship_Main.ejs +1 -1
  65. package/src/emit/relationship/templates/UpdateRelationship_Main.ejs +1 -1
  66. package/src/emit/resource/emitResourceArtifacts.js +249 -123
  67. package/src/emit/resource/generateResourceArtifacts.js +15 -6
  68. package/src/emit/resource/templates/DynamoDBTable_Yaml.ejs +18 -18
  69. package/src/emit/resource/templates/FlowOut_Yaml.ejs +5 -5
  70. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +10 -9
  71. package/src/emit/resource/templates/ResourceBeforeLogicalYaml.ejs +44 -44
  72. package/src/emit/resource/templates/ResourceSqsYaml.ejs +36 -36
  73. package/src/emit/resource/templates/ResourceYaml.ejs +48 -48
  74. package/src/emit/resource/templates/S3Subscription_Yaml.ejs +7 -0
  75. package/src/emit/resource/templates/SharedResource_Yaml.ejs +11 -11
  76. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +122 -105
  77. package/src/emit/resource/templates/WebSocketSubscription_Yaml.ejs +7 -0
  78. package/src/emit/shared/templates/FindData_HdrSqs.ejs +120 -0
  79. package/src/emit/shared/templates/FindData_Main.ejs +345 -0
  80. package/src/emit/{crud → shared}/templates/FlowHandler_HdrApi.ejs +1 -1
  81. package/src/emit/{relationship → shared}/templates/FlowHandler_HdrDsq.ejs +4 -4
  82. package/src/emit/{crud → shared}/templates/FlowHandler_HdrInv.ejs +1 -1
  83. package/src/emit/{crud → shared}/templates/FlowHandler_HdrSqs.ejs +4 -4
  84. package/src/emit/shared/templates/PaginateProcessLogical_HdrDsq.ejs +162 -0
  85. package/src/emit/shared/templates/PaginateProcessLogical_HdrSqs.ejs +140 -0
  86. package/src/emit/shared/templates/PaginateProcessLogical_Main.ejs +268 -0
  87. package/src/emit/{relationship/templates/FlowHandler_HdrSqs.ejs → shared/templates/ProcessLogical_HdrSqs.ejs} +40 -55
  88. package/src/emit/shared/templates/ProcessLogical_Main.ejs +448 -0
  89. package/src/emit/system/emitSystemFindDataArtifacts.js +266 -0
  90. package/src/emit/system/emitSystemWebSocketArtifacts.js +61 -165
  91. package/src/emit/system/templates/WebSocketComplete_HdrSqs.ejs +48 -0
  92. package/src/emit/system/templates/WebSocketComplete_Main.ejs +91 -0
  93. package/src/emit/system/templates/WebSocketComplete_Yaml.ejs +19 -0
  94. package/src/emit/system/templates/WebSocketConnect.ejs +45 -0
  95. package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +11 -0
  96. package/src/generate.js +166 -36
  97. package/src/generateIntTest.js +76 -28
  98. package/src/generateResourceIntTest.js +35 -12
  99. package/src/generateResources/IntTest/generateResources/generateResources.js +5 -2
  100. package/src/generateResources/IntTest/generateResources/templateResources.ejs +18 -30
  101. package/src/generateResources/IntTest/generateTests/events/events.js +1 -1
  102. package/src/generateResources/IntTest/generateTests/events/templateEvents.ejs +13 -10
  103. package/src/generateResources/IntTest/generateTests/generateTemplate.js +2 -2
  104. package/src/generateResources/IntTest/generateTests/pathIntTest/pathIntTest.js +2 -1
  105. package/src/generateResources/IntTest/generateTests/pathIntTest/template.ejs +1 -3
  106. package/src/generateResources/IntTest/generateTests/tests/templateTests.ejs +1 -1
  107. package/src/generateResources/IntTest/generateTests/tests/tests.js +7 -3
  108. package/src/generateResources/IntTest/libs/{libs.js → IntTestGenerateLib.js} +34 -23
  109. package/src/generateResources/IntTest/upload/uploadIntTest.js +33 -19
  110. package/src/intTestGeneratorShared.js +15 -10
  111. package/src/output/commitArtifacts.js +99 -39
  112. package/src/output/mergeHooks.js +34 -21
  113. package/src/output/validateArtifacts.js +37 -27
  114. package/src/plan/buildGenerationPlan.js +24 -13
  115. package/test/core.test.js +117 -58
  116. package/test/createTargetRoleHandler.test.js +1 -1
  117. package/test/crudApplicationArtifacts.test.js +132 -31
  118. package/test/customFlowApplicationArtifacts.test.js +353 -71
  119. package/test/dynamicCrudFlowSchemas.test.js +74 -26
  120. package/test/dynamicRbacFlowSchemas.test.js +61 -14
  121. package/test/dynamicRelationshipFlowSchemas.test.js +65 -18
  122. package/test/functionYamlArtifact.test.js +67 -10
  123. package/test/intTestApi.test.js +9 -10
  124. package/test/publicApi.test.js +178 -36
  125. package/test/relationshipApplicationArtifacts.test.js +90 -19
  126. package/test/resourceArtifacts.test.js +173 -33
  127. package/src/emit/relationship/dynamicRelationshipFlowSchemaTemplate.js +0 -11
  128. package/src/emit/relationship/templates/FlowHandler_HdrApi.ejs +0 -59
  129. package/src/emit/relationship/templates/FlowHandler_HdrInv.ejs +0 -47
@@ -6,24 +6,32 @@ export function normalizeArtifacts(artifacts) {
6
6
  }
7
7
 
8
8
  const paths = new Set();
9
- return Object.freeze(artifacts.map(artifact => {
10
- if (!artifact || typeof artifact.path !== 'string' || artifact.path.trim() === '') {
11
- throw new TypeError('Artifact.path must be a non-empty relative path');
12
- }
13
- if (isAbsolute(artifact.path)) {
14
- throw new TypeError(`Artifact.path must be relative: ${artifact.path}`);
15
- }
16
- if (typeof artifact.content !== 'string') {
17
- throw new TypeError(`Artifact.content must be a string: ${artifact.path}`);
18
- }
19
-
20
- const path = normalizeArtifactPath(artifact.path);
21
- if (paths.has(path)) {
22
- throw new Error(`Duplicate artifact path: ${path}`);
23
- }
24
- paths.add(path);
25
- return Object.freeze({ path, content: artifact.content });
26
- }));
9
+ return Object.freeze(
10
+ artifacts.map((artifact) => {
11
+ if (
12
+ !artifact ||
13
+ typeof artifact.path !== 'string' ||
14
+ artifact.path.trim() === ''
15
+ ) {
16
+ throw new TypeError('Artifact.path must be a non-empty relative path');
17
+ }
18
+ if (isAbsolute(artifact.path)) {
19
+ throw new TypeError(`Artifact.path must be relative: ${artifact.path}`);
20
+ }
21
+ if (typeof artifact.content !== 'string') {
22
+ throw new TypeError(
23
+ `Artifact.content must be a string: ${artifact.path}`
24
+ );
25
+ }
26
+
27
+ const path = _normalizeArtifactPath(artifact.path);
28
+ if (paths.has(path)) {
29
+ throw new Error(`Duplicate artifact path: ${path}`);
30
+ }
31
+ paths.add(path);
32
+ return Object.freeze({ path, content: artifact.content });
33
+ })
34
+ );
27
35
  }
28
36
 
29
37
  export function validateArtifacts(artifacts) {
@@ -36,17 +44,19 @@ export function normalizeArtifactPaths(paths) {
36
44
  }
37
45
 
38
46
  const seenPaths = new Set();
39
- return Object.freeze(paths.map(path => {
40
- const normalizedPath = normalizeArtifactPath(path);
41
- if (seenPaths.has(normalizedPath)) {
42
- throw new Error(`Duplicate artifact path: ${normalizedPath}`);
43
- }
44
- seenPaths.add(normalizedPath);
45
- return normalizedPath;
46
- }));
47
+ return Object.freeze(
48
+ paths.map((path) => {
49
+ const normalizedPath = _normalizeArtifactPath(path);
50
+ if (seenPaths.has(normalizedPath)) {
51
+ throw new Error(`Duplicate artifact path: ${normalizedPath}`);
52
+ }
53
+ seenPaths.add(normalizedPath);
54
+ return normalizedPath;
55
+ })
56
+ );
47
57
  }
48
58
 
49
- function normalizeArtifactPath(path) {
59
+ function _normalizeArtifactPath(path) {
50
60
  if (typeof path !== 'string' || path.trim() === '') {
51
61
  throw new TypeError('Artifact.path must be a non-empty relative path');
52
62
  }
@@ -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,100 @@ 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-'));
98
- const hookPath = join(outputPath, 'app/generated/hook/example.js');
99
- await mkdir(dirname(hookPath), { recursive: true });
100
- await writeFile(hookPath, '//(<hookCode>)\ncustom();\n//(</hookCode>)\n');
101
-
102
- await commitArtifacts(buildGenerationPlan(compileProject({ rootPath: outputPath }), [{
103
- path: 'app/generated/source/example.js',
104
- content: '//(<hookCode>)\ndefault();\n//(</hookCode>)\n'
105
- }]));
145
+ const outputPath = await mkdtemp(
146
+ join(tmpdir(), 'generate-service-code-v2-hooks-')
147
+ );
148
+ const sourcePath = join(outputPath, 'app/generated/source/example.js');
149
+ await mkdir(dirname(sourcePath), { recursive: true });
150
+ await writeFile(sourcePath, '//(<hookCode>)\ncustom();\n//(</hookCode>)\n');
151
+
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
- await readFile(join(outputPath, 'app/generated/source/example.js'), 'utf8'),
162
+ await readFile(sourcePath, 'utf8'),
109
163
  '//(<hookCode>)\ncustom();\n//(</hookCode>)\n'
110
164
  );
111
- assert.equal(await readFile(hookPath, 'utf8'), '//(<hookCode>)\ncustom();\n//(</hookCode>)\n');
112
165
  });
113
166
 
114
167
  test('rejects an invalid rendered artifact before staging output', async () => {
115
- const outputPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-invalid-render-'));
168
+ const outputPath = await mkdtemp(
169
+ join(tmpdir(), 'generate-service-code-v2-invalid-render-')
170
+ );
116
171
  const sourcePath = join(outputPath, 'app/generated/source/example.js');
117
172
  await mkdir(dirname(sourcePath), { recursive: true });
118
173
  await writeFile(sourcePath, 'existing source\n');
@@ -126,33 +181,37 @@ test('rejects an invalid rendered artifact before staging output', async () => {
126
181
  );
127
182
  assert.equal(await readFile(sourcePath, 'utf8'), 'existing source\n');
128
183
  assert.deepEqual(
129
- (await readdir(outputPath)).filter(name => name.startsWith('.generate-service-code-v2-stage-')),
184
+ (await readdir(outputPath)).filter((name) =>
185
+ name.startsWith('.generate-service-code-v2-stage-')
186
+ ),
130
187
  []
131
188
  );
132
189
  });
133
190
 
134
191
  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-'));
192
+ const outputPath = await mkdtemp(
193
+ join(tmpdir(), 'generate-service-code-v2-transaction-')
194
+ );
136
195
  const project = compileProject({ rootPath: outputPath });
137
196
  const sourcePath = join(outputPath, 'app/generated/source/first.js');
138
- const hookPath = join(outputPath, 'app/generated/hook/first.js');
139
197
  await mkdir(dirname(sourcePath), { recursive: true });
140
- await mkdir(dirname(hookPath), { recursive: true });
141
198
  await writeFile(sourcePath, 'old source\n');
142
- await writeFile(hookPath, 'custom hook\n');
143
199
  await mkdir(join(outputPath, 'app/generated/source/second.js'));
144
200
 
145
201
  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
- ])),
202
+ commitArtifacts(
203
+ buildGenerationPlan(project, [
204
+ { path: 'app/generated/source/first.js', content: 'new source\n' },
205
+ { path: 'app/generated/source/second.js', content: 'never committed\n' }
206
+ ])
207
+ ),
150
208
  /not a file/
151
209
  );
152
210
  assert.equal(await readFile(sourcePath, 'utf8'), 'old source\n');
153
- assert.equal(await readFile(hookPath, 'utf8'), 'custom hook\n');
154
211
  assert.deepEqual(
155
- (await readdir(outputPath)).filter(name => name.startsWith('.generate-service-code-v2-stage-')),
212
+ (await readdir(outputPath)).filter((name) =>
213
+ name.startsWith('.generate-service-code-v2-stage-')
214
+ ),
156
215
  []
157
216
  );
158
217
  assert.equal((await readdir(outputPath)).includes('.bak'), false);
@@ -17,7 +17,7 @@ const OWN_TOPIC_FLOWS = [
17
17
  ];
18
18
  after(() =>
19
19
  Promise.all(
20
- [...roots].map(root => rm(root, { recursive: true, force: true }))
20
+ [...roots].map((root) => rm(root, { recursive: true, force: true }))
21
21
  )
22
22
  );
23
23
 
@@ -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,61 +57,139 @@ 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 });
55
- await writeFile(
56
- hookPath,
57
- ' #<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#/CreateCompleteHdrSqsFunctionSetting#>\n'
115
+ const yamlPath = join(
116
+ rootPath,
117
+ 'app/sls_yaml/generatedCode/source/flow-objects.yml'
58
118
  );
59
119
 
60
120
  await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
121
+
122
+ const hookCode =
123
+ ' #<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#/CreateCompleteHdrSqsFunctionSetting#>\n';
124
+ let yaml = await readFile(yamlPath, 'utf8');
125
+ const withHook = yaml.replace(
126
+ / #<#CreateCompleteHdrSqsFunctionSetting#>[\s\S]*?#<#\/CreateCompleteHdrSqsFunctionSetting#>/,
127
+ hookCode.trim()
128
+ );
129
+ assert.notEqual(yaml, withHook, 'hook placeholder must exist in generated YAML');
130
+ await writeFile(yamlPath, withHook);
131
+
61
132
  await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
62
133
 
63
- const yamlPath = join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml');
64
- const yaml = await readFile(yamlPath, 'utf8');
65
- assert.match(yaml, /#<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#\/CreateCompleteHdrSqsFunctionSetting#>/);
134
+ yaml = await readFile(yamlPath, 'utf8');
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/);
72
- assert.equal(await readFile(yamlPath, 'utf8'), yaml);
73
- assert.equal(
74
- await readFile(hookPath, 'utf8'),
75
- ' #<#CreateCompleteHdrSqsFunctionSetting#>\n memorySize: 512\n #<#/CreateCompleteHdrSqsFunctionSetting#>\n'
147
+ await assert.rejects(
148
+ generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' }),
149
+ /not a file/
76
150
  );
151
+ assert.equal(await readFile(yamlPath, 'utf8'), yaml);
77
152
  });
78
153
 
79
154
  test('removes managed CRUD artifacts when no applicable object remains', async () => {
80
155
  const rootPath = await createProject(true);
81
156
  await generateCrudApplicationArtifacts(rootPath, { serviceTag: 'orders' });
82
- await writeFile(join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'), "export default { objectType: 'order' };\n");
157
+ await writeFile(
158
+ join(rootPath, 'app/src/schemas/ObjectSchemas/Order.js'),
159
+ "export default { objectType: 'order' };\n"
160
+ );
83
161
 
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')));
162
+ assert.deepEqual(
163
+ (await generateCrudApplicationArtifacts(rootPath)).files,
164
+ []
165
+ );
166
+ await assert.rejects(
167
+ readFile(
168
+ join(
169
+ rootPath,
170
+ 'app/src/generatedCode/FlowObjects/Create/source/Create_Main.js'
171
+ )
172
+ )
173
+ );
174
+ await assert.rejects(
175
+ readFile(
176
+ join(
177
+ rootPath,
178
+ 'app/src/generatedCode/FlowObjects/Create/source/CreateComplete_Main.js'
179
+ )
180
+ )
181
+ );
182
+ await assert.rejects(
183
+ readFile(
184
+ join(rootPath, 'app/sls_yaml/generatedCode/source/flow-objects.yml')
185
+ )
186
+ );
88
187
  });
89
188
 
90
189
  async function createProject(withCrudObject) {
91
- const rootPath = await mkdtemp(join(tmpdir(), 'generate-service-code-v2-crud-application-'));
190
+ const rootPath = await mkdtemp(
191
+ join(tmpdir(), 'generate-service-code-v2-crud-application-')
192
+ );
92
193
  roots.add(rootPath);
93
194
  const schemaRoot = join(rootPath, 'app/src/schemas');
94
195
  await mkdir(join(schemaRoot, 'ObjectSchemas'), { recursive: true });