@memberjunction/codegen-lib 6.1.0-edge.4 → 6.1.0-edge.5

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 (71) hide show
  1. package/dist/Config/config.d.ts +172 -10
  2. package/dist/Config/config.d.ts.map +1 -1
  3. package/dist/Config/config.js +57 -1
  4. package/dist/Config/config.js.map +1 -1
  5. package/dist/Config/schema-output.d.ts +33 -0
  6. package/dist/Config/schema-output.d.ts.map +1 -0
  7. package/dist/Config/schema-output.js +52 -0
  8. package/dist/Config/schema-output.js.map +1 -0
  9. package/dist/Database/codeGenDatabaseProvider.d.ts +49 -0
  10. package/dist/Database/codeGenDatabaseProvider.d.ts.map +1 -1
  11. package/dist/Database/codeGenDatabaseProvider.js +105 -0
  12. package/dist/Database/codeGenDatabaseProvider.js.map +1 -1
  13. package/dist/Database/exclude-tables.d.ts +26 -0
  14. package/dist/Database/exclude-tables.d.ts.map +1 -0
  15. package/dist/Database/exclude-tables.js +38 -0
  16. package/dist/Database/exclude-tables.js.map +1 -0
  17. package/dist/Database/manage-metadata.d.ts +52 -2
  18. package/dist/Database/manage-metadata.d.ts.map +1 -1
  19. package/dist/Database/manage-metadata.js +95 -14
  20. package/dist/Database/manage-metadata.js.map +1 -1
  21. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts +2 -0
  22. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.d.ts.map +1 -1
  23. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js +10 -0
  24. package/dist/Database/providers/sqlserver/SQLServerCodeGenProvider.js.map +1 -1
  25. package/dist/Database/providers/sqlserver/__tests__/SQLServerCodeGenProvider.test.js +17 -0
  26. package/dist/Database/providers/sqlserver/__tests__/SQLServerCodeGenProvider.test.js.map +1 -1
  27. package/dist/Database/sql_codegen.d.ts +15 -0
  28. package/dist/Database/sql_codegen.d.ts.map +1 -1
  29. package/dist/Database/sql_codegen.js +55 -15
  30. package/dist/Database/sql_codegen.js.map +1 -1
  31. package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts +22 -0
  32. package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts.map +1 -1
  33. package/dist/Manifest/GenerateClassRegistrationsManifest.js +112 -5
  34. package/dist/Manifest/GenerateClassRegistrationsManifest.js.map +1 -1
  35. package/dist/Misc/codegen-reporter.d.ts +3 -0
  36. package/dist/Misc/codegen-reporter.d.ts.map +1 -1
  37. package/dist/Misc/codegen-reporter.js +3 -0
  38. package/dist/Misc/codegen-reporter.js.map +1 -1
  39. package/dist/Misc/emit-stats.d.ts +28 -0
  40. package/dist/Misc/emit-stats.d.ts.map +1 -0
  41. package/dist/Misc/emit-stats.js +43 -0
  42. package/dist/Misc/emit-stats.js.map +1 -0
  43. package/dist/Misc/entity_subclasses_codegen.d.ts +20 -2
  44. package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
  45. package/dist/Misc/entity_subclasses_codegen.js +92 -33
  46. package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
  47. package/dist/Misc/file-write.d.ts +15 -0
  48. package/dist/Misc/file-write.d.ts.map +1 -0
  49. package/dist/Misc/file-write.js +33 -0
  50. package/dist/Misc/file-write.js.map +1 -0
  51. package/dist/Misc/graphql_server_codegen.d.ts +12 -2
  52. package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
  53. package/dist/Misc/graphql_server_codegen.js +61 -12
  54. package/dist/Misc/graphql_server_codegen.js.map +1 -1
  55. package/dist/Misc/runCommand.d.ts +5 -0
  56. package/dist/Misc/runCommand.d.ts.map +1 -1
  57. package/dist/Misc/runCommand.js +43 -17
  58. package/dist/Misc/runCommand.js.map +1 -1
  59. package/dist/Misc/schema-emit.d.ts +123 -0
  60. package/dist/Misc/schema-emit.d.ts.map +1 -0
  61. package/dist/Misc/schema-emit.js +220 -0
  62. package/dist/Misc/schema-emit.js.map +1 -0
  63. package/dist/index.d.ts +6 -0
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +6 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/runCodeGen.d.ts +20 -2
  68. package/dist/runCodeGen.d.ts.map +1 -1
  69. package/dist/runCodeGen.js +147 -21
  70. package/dist/runCodeGen.js.map +1 -1
  71. package/package.json +29 -29
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Schema is the incremental unit for CodeGen file emit.
3
+ *
4
+ * A 2,000-entity database is almost never one schema with 2,000 tables. It is
5
+ * two-to-three dozen schemas of 100–150 tables each. Entity identity stays the
6
+ * entity (names, FKs, metadata rows). Everything that can be incremental —
7
+ * emit, dirty regen, parallelism, output-directory routing, agent context —
8
+ * keys off the schema.
9
+ */
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { writeFileIfChanged } from './file-write.js';
13
+ /**
14
+ * Case-insensitive schema match. `%` is the only wildcard (SQL `_` is a
15
+ * literal — schema names like `bsd_crm` are common and must not glob).
16
+ */
17
+ /** Case-insensitive schema/entity key. Null/undefined become ''. */
18
+ export function schemaKey(name) {
19
+ return (name ?? '').trim().toLowerCase();
20
+ }
21
+ export function schemaNameMatches(pattern, schemaName) {
22
+ const p = schemaKey(pattern);
23
+ const s = schemaKey(schemaName);
24
+ if (!p.includes('%')) {
25
+ return p === s;
26
+ }
27
+ const escaped = p.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/%/g, '.*');
28
+ return new RegExp(`^${escaped}$`).test(s);
29
+ }
30
+ /**
31
+ * Turn a SQL schema name into a safe TypeScript file stem.
32
+ * `__mj` stays `__mj`; `bsd_crm` stays `bsd_crm`; `Sales.Analytics` becomes `Sales_Analytics`.
33
+ */
34
+ export function sanitizeSchemaFileName(schemaName) {
35
+ // Keep leading underscores — `__mj` is a real schema name and a valid file stem.
36
+ const cleaned = (schemaName ?? '').trim().replace(/[^A-Za-z0-9_]+/g, '_').replace(/_+$/g, '');
37
+ return cleaned.length > 0 ? cleaned : 'schema';
38
+ }
39
+ /**
40
+ * Group entities by their raw `SchemaName` (trimmed, original casing of the
41
+ * first occurrence). Order inside each group is the input order.
42
+ */
43
+ export function groupEntitiesBySchema(entities) {
44
+ const map = new Map();
45
+ for (const entity of entities) {
46
+ const key = (entity.SchemaName ?? '').trim() || 'unknown';
47
+ const list = map.get(key);
48
+ if (list) {
49
+ list.push(entity);
50
+ }
51
+ else {
52
+ map.set(key, [entity]);
53
+ }
54
+ }
55
+ return map;
56
+ }
57
+ /**
58
+ * Map dirty entity names (from `newEntityList` ∪ `modifiedEntityList`) back to
59
+ * the schemas those entities live in. Comparison is case-insensitive.
60
+ */
61
+ /**
62
+ * Resolve which schemas a file-emit pass should rebuild.
63
+ * `--skipdb` and `dirtySchemaOnly === false` rebuild every schema (write-if-changed
64
+ * still keeps mtimes stable). Otherwise only schemas that contain a new/modified
65
+ * entity are dirty — missing files are added later by {@link schemasToEmit}.
66
+ */
67
+ export function resolveDirtySchemasForEmit(entities, dirtyEntityNames, skipDB, dirtySchemaOnly, deletedEntitySchemas = []) {
68
+ if (skipDB || !dirtySchemaOnly) {
69
+ return 'all';
70
+ }
71
+ const schemas = collectDirtySchemas(entities, dirtyEntityNames);
72
+ // Deletion arrives as a schema, not an entity name: by the time this runs the entity is gone
73
+ // from `entities`, so `collectDirtySchemas` could never resolve it. Union it in directly, or
74
+ // the schema is not rebuilt and the dead class survives on disk.
75
+ for (const schema of deletedEntitySchemas) {
76
+ const trimmed = (schema ?? '').trim();
77
+ if (trimmed.length > 0) {
78
+ schemas.add(trimmed);
79
+ }
80
+ }
81
+ return schemas;
82
+ }
83
+ export function collectDirtySchemas(entities, dirtyEntityNames) {
84
+ const dirty = new Set();
85
+ for (const name of dirtyEntityNames) {
86
+ const key = schemaKey(name);
87
+ if (key.length > 0) {
88
+ dirty.add(key);
89
+ }
90
+ }
91
+ const schemas = new Set();
92
+ if (dirty.size === 0) {
93
+ return schemas;
94
+ }
95
+ for (const entity of entities) {
96
+ if (dirty.has(schemaKey(entity.Name))) {
97
+ const schema = (entity.SchemaName ?? '').trim();
98
+ if (schema.length > 0) {
99
+ schemas.add(schema);
100
+ }
101
+ }
102
+ }
103
+ return schemas;
104
+ }
105
+ /**
106
+ * Decide which schema files to rebuild. Missing files are always rebuilt so a
107
+ * fresh clone / first run is complete. When `dirty` is `'all'` every schema is
108
+ * rebuilt (callers still use write-if-changed to keep mtimes stable).
109
+ */
110
+ export function schemasToEmit(allSchemas, dirty, fileExists) {
111
+ if (dirty === 'all' || dirty == null) {
112
+ return [...allSchemas];
113
+ }
114
+ const dirtyLower = new Set([...dirty].map((s) => s.trim().toLowerCase()));
115
+ return allSchemas.filter((schema) => {
116
+ if (!fileExists(schema)) {
117
+ return true;
118
+ }
119
+ return dirtyLower.has(schema.trim().toLowerCase());
120
+ });
121
+ }
122
+ /**
123
+ * Bounded parallel map. Order of results matches `items`. A limit of 1 is serial.
124
+ */
125
+ export async function mapLimit(items, limit, mapper) {
126
+ const concurrency = Math.max(1, Math.floor(limit));
127
+ const results = new Array(items.length);
128
+ let next = 0;
129
+ const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
130
+ while (next < items.length) {
131
+ const index = next;
132
+ next += 1;
133
+ results[index] = await mapper(items[index], index);
134
+ }
135
+ });
136
+ await Promise.all(workers);
137
+ return results;
138
+ }
139
+ /**
140
+ * Barrel that re-exports every per-schema file. Uses `.js` specifiers so the
141
+ * file is valid in `"type": "module"` packages (MJCoreEntities, MJServer).
142
+ */
143
+ export function buildSchemaBarrel(schemas, relativeDir, extraHeader) {
144
+ const dir = relativeDir.replace(/\\/g, '/').replace(/\/+$/, '');
145
+ const exports = [...schemas]
146
+ .sort((a, b) => a.localeCompare(b))
147
+ .map((schema) => `export * from './${dir}/${sanitizeSchemaFileName(schema)}.js';`)
148
+ .join('\n');
149
+ return `${extraHeader}${exports}\n`;
150
+ }
151
+ /**
152
+ * Names of files in a per-schema emit directory that no live schema claims.
153
+ *
154
+ * Kept pure and separate from the deletion so the decision is testable without a
155
+ * filesystem. `fileNames` is a plain directory listing; `schemas` is every schema in the
156
+ * run, not just the dirty subset.
157
+ */
158
+ export function selectOrphanedSchemaFiles(fileNames, schemas) {
159
+ const live = new Set(schemas.map((schema) => `${sanitizeSchemaFileName(schema)}.ts`));
160
+ return fileNames.filter((name) => name.endsWith('.ts') && !live.has(name)).sort();
161
+ }
162
+ /**
163
+ * Delete per-schema files that no live schema claims, returning what was removed.
164
+ *
165
+ * A schema that empties out — its last table dropped — leaves its file on disk. The barrel
166
+ * stops exporting it, which is not enough: `tsc` still compiles it through `include`, the
167
+ * class-registration manifest still globs it into the registry, and the CodeGen-tail guard
168
+ * reads every `.ts` in the directory regardless of what the barrel names. So a deleted
169
+ * entity keeps a live `@RegisterClass` registration and inflates the roster the guard
170
+ * compares. The old monolith could not drift this way; it was rewritten in full every run.
171
+ *
172
+ * Safe under dirty-schema regen: `schemas` is the full set for the run, so a schema that
173
+ * simply was not dirty this time is still live and is never pruned.
174
+ */
175
+ export function pruneOrphanedSchemaFiles(directory, schemas) {
176
+ if (!fs.existsSync(directory)) {
177
+ return [];
178
+ }
179
+ const orphans = selectOrphanedSchemaFiles(fs.readdirSync(directory), schemas);
180
+ for (const name of orphans) {
181
+ fs.unlinkSync(path.join(directory, name));
182
+ }
183
+ return orphans;
184
+ }
185
+ /**
186
+ * Fill in a partial {@link SchemaEmitOptions} from config defaults.
187
+ *
188
+ * Shared by both generators so the defaults cannot drift between entity subclasses and
189
+ * GraphQL resolvers — they must agree, or one emits per-schema while the other emits a
190
+ * monolith and the two rosters stop lining up.
191
+ *
192
+ * `parallel` / `concurrency` are resolved for every caller even though only the entity
193
+ * generator uses them: its per-schema assembly is async (each file awaits its entities),
194
+ * while GraphQL assembly is synchronous, so there is nothing to overlap there.
195
+ */
196
+ export function resolveSchemaEmitOptions(options, defaults) {
197
+ return {
198
+ perSchema: options?.perSchema ?? defaults?.perSchema ?? true,
199
+ dirtySchemas: options?.dirtySchemas ?? 'all',
200
+ parallel: options?.parallel ?? defaults?.parallel ?? true,
201
+ concurrency: options?.concurrency ?? defaults?.concurrency ?? 8,
202
+ writeIfChanged: options?.writeIfChanged ?? defaults?.writeIfChanged ?? true,
203
+ };
204
+ }
205
+ /**
206
+ * Write one emitted file, honouring the write-if-changed setting.
207
+ *
208
+ * Shared for the same reason as {@link resolveSchemaEmitOptions}: both generators must treat
209
+ * an unchanged file identically, or one leaves mtimes alone while the other churns them and
210
+ * downstream incremental builds see phantom work.
211
+ */
212
+ export function emitSchemaFile(filePath, content, useWriteIfChanged) {
213
+ if (useWriteIfChanged) {
214
+ writeFileIfChanged(filePath, content);
215
+ return;
216
+ }
217
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
218
+ fs.writeFileSync(filePath, content);
219
+ }
220
+ //# sourceMappingURL=schema-emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-emit.js","sourceRoot":"","sources":["../../src/Misc/schema-emit.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -48,6 +48,12 @@ export * from './Database/dbSchema.js';
48
48
  export * from './Database/manage-metadata.js';
49
49
  export * from './Database/sql_codegen.js';
50
50
  export * from './Database/sql.js';
51
+ export * from './Database/schema-scope.js';
52
+ export * from './Database/exclude-tables.js';
53
+ export * from './Config/schema-output.js';
54
+ export * from './Misc/file-write.js';
55
+ export * from './Misc/emit-stats.js';
56
+ export * from './Misc/schema-emit.js';
51
57
  export * from './Misc/entity_subclasses_codegen.js';
52
58
  export * from './Misc/action_subclasses_codegen.js';
53
59
  export * from './Misc/remote_operations_codegen.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAGtC,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAG9B,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAA;AAG7C,cAAc,2BAA2B,CAAA;AACzC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qDAAqD,CAAC;AACpE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,6CAA6C,CAAC;AAG5D,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,yBAAyB,CAAA;AAGvC,cAAc,+CAA+C,CAAA;AAG7D,cAAc,uCAAuC,CAAA;AACrD,cAAc,yCAAyC,CAAA;AACvD,cAAc,2CAA2C,CAAA;AAGzD,cAAc,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAGtC,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,yBAAyB,CAAA;AACvC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAGlC,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAA;AAG7C,cAAc,2BAA2B,CAAA;AACzC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qDAAqD,CAAC;AACpE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,6CAA6C,CAAC;AAG5D,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,yBAAyB,CAAA;AAGvC,cAAc,+CAA+C,CAAA;AAG7D,cAAc,uCAAuC,CAAA;AACrD,cAAc,yCAAyC,CAAA;AACvD,cAAc,2CAA2C,CAAA;AAGzD,cAAc,cAAc,CAAA"}
package/dist/index.js CHANGED
@@ -50,6 +50,12 @@ export * from './Database/dbSchema.js';
50
50
  export * from './Database/manage-metadata.js';
51
51
  export * from './Database/sql_codegen.js';
52
52
  export * from './Database/sql.js';
53
+ export * from './Database/schema-scope.js';
54
+ export * from './Database/exclude-tables.js';
55
+ export * from './Config/schema-output.js';
56
+ export * from './Misc/file-write.js';
57
+ export * from './Misc/emit-stats.js';
58
+ export * from './Misc/schema-emit.js';
53
59
  // Code generation exports
54
60
  export * from './Misc/entity_subclasses_codegen.js';
55
61
  export * from './Misc/action_subclasses_codegen.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,wBAAwB;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAEtC,mBAAmB;AACnB,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAE9B,0BAA0B;AAC1B,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAA;AAE7C,kBAAkB;AAClB,cAAc,2BAA2B,CAAA;AACzC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qDAAqD,CAAC;AACpE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,6CAA6C,CAAC;AAE5D,kBAAkB;AAClB,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,yBAAyB,CAAA;AAEvC,sBAAsB;AACtB,cAAc,+CAA+C,CAAA;AAE7D,uBAAuB;AACvB,cAAc,uCAAuC,CAAA;AACrD,cAAc,yCAAyC,CAAA;AACvD,cAAc,2CAA2C,CAAA;AAEzD,cAAc;AACd,cAAc,cAAc,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,wBAAwB;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AAEtC,mBAAmB;AACnB,cAAc,oCAAoC,CAAA;AAClD,cAAc,yDAAyD,CAAA;AACvE,cAAc,2DAA2D,CAAA;AACzE,cAAc,2DAA2D,CAAA;AACzE,cAAc,6DAA6D,CAAA;AAC3E,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,wBAAwB,CAAA;AACtC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,yBAAyB,CAAA;AACvC,cAAc,2BAA2B,CAAA;AACzC,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAElC,0BAA0B;AAC1B,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAA;AAE7C,kBAAkB;AAClB,cAAc,2BAA2B,CAAA;AACzC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qDAAqD,CAAC;AACpE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,6CAA6C,CAAC;AAE5D,kBAAkB;AAClB,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,yBAAyB,CAAA;AAEvC,sBAAsB;AACtB,cAAc,+CAA+C,CAAA;AAE7D,uBAAuB;AACvB,cAAc,uCAAuC,CAAA;AACrD,cAAc,yCAAyC,CAAA;AACvD,cAAc,2CAA2C,CAAA;AAEzD,cAAc;AACd,cAAc,cAAc,CAAA"}
@@ -7,6 +7,7 @@
7
7
  * Supports both SQL Server and PostgreSQL database platforms via the dbPlatform configuration.
8
8
  */
9
9
  import { CommandInfo } from './Config/config.js';
10
+ import { SchemaEmitOptions } from './Misc/schema-emit.js';
10
11
  import * as MJ from '@memberjunction/core';
11
12
  import { CommandExecutionResult } from './Misc/runCommand.js';
12
13
  import { CodeGenConnection, DataSourceResult as ProviderDataSourceResult } from './Database/codeGenDatabaseProvider.js';
@@ -27,8 +28,17 @@ export declare class RunCodeGenBase {
27
28
  context: string;
28
29
  message: string;
29
30
  }>;
30
- /** Record any failed entries from a BEFORE/AFTER command batch, paired by index. */
31
- protected recordCommandFailures(phase: string, cmds: CommandInfo[], results: CommandExecutionResult[]): void;
31
+ /**
32
+ * Record any failed entries from a BEFORE/AFTER command batch, paired by index.
33
+ *
34
+ * Returns just this batch's failures. Callers log what they get back rather than walking
35
+ * `commandFailures`, which accumulates across phases — iterating the whole array in the
36
+ * AFTER handler re-logged every BEFORE failure a second time.
37
+ */
38
+ protected recordCommandFailures(phase: string, cmds: CommandInfo[], results: CommandExecutionResult[]): Array<{
39
+ context: string;
40
+ message: string;
41
+ }>;
32
42
  /**
33
43
  * Acquire the per-platform data source for a CodeGen run.
34
44
  *
@@ -65,6 +75,14 @@ export declare class RunCodeGenBase {
65
75
  * Accepts an existing data source — does NOT create connections or call process.exit().
66
76
  */
67
77
  protected executeCodeGenPipeline(dataSource: DataSourceResult, skipDatabaseGeneration?: boolean, skipFileGeneration?: boolean): Promise<boolean>;
78
+ /**
79
+ * Resolve dirty-schema emit options for one generator call.
80
+ * `--skipdb` (files only) and `fileEmit.dirtySchemaOnly === false` rebuild
81
+ * every schema; write-if-changed still keeps mtimes stable. A full run with
82
+ * no new/modified entities yields an empty Set — the generator then only
83
+ * writes schema files that are missing.
84
+ */
85
+ protected buildSchemaEmitOptions(entities: MJ.EntityInfo[], skipDB: boolean): SchemaEmitOptions;
68
86
  /**
69
87
  * Runs the file-generation phase of CodeGen: GraphQL resolvers, entity
70
88
  * subclasses (core + non-core), Angular components, DB schema JSON, and
@@ -1 +1 @@
1
- {"version":3,"file":"runCodeGen.d.ts","sourceRoot":"","sources":["../src/runCodeGen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EAA4G,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGxJ,OAAO,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3C,OAAO,EAAmB,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAS5E,OAAO,EAAE,iBAAiB,EAA2B,gBAAgB,IAAI,wBAAwB,EAAkC,MAAM,oCAAoC,CAAC;AAI9K,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAG5D,OAAO,8DAA8D,CAAC;AAStE,KAAK,gBAAgB,GAAG,wBAAwB,CAAC;AAEjD;;GAEG;AACH,qBAAa,cAAc;IACzB;;;;OAIG;IACH,SAAS,CAAC,eAAe,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAE5E,oFAAoF;IACpF,SAAS,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,GAAG,IAAI;IAc5G;;;;;;;;;;;;;OAaG;IACU,eAAe,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAIzD;;OAEG;IACH;;;;OAIG;IACU,YAAY,CAAC,UAAU,EAAE,gBAAgB,EAAE,sBAAsB,GAAE,OAAe,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBrK,GAAG,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe;IAQ7F;;;;OAIG;IACU,aAAa,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAgD9H;;;OAGG;cACa,sBAAsB,CAAC,UAAU,EAAE,gBAAgB,EAAE,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IA2RpK;;;;;;;;OAQG;cACa,sBAAsB,CACpC,IAAI,EAAE,iBAAiB,EACvB,WAAW,EAAE,QAAQ,EACrB,EAAE,EAAE,EAAE,CAAC,QAAQ,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC;CAsMpB;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,iBAGjI;AAED;;;;;GAKG;AACH,wBAAsB,yCAAyC,CAC7D,sBAAsB,GAAE,OAAe,EACvC,kBAAkB,GAAE,OAAe,GAClC,OAAO,CAAC,WAAW,CAAC,CAGtB"}
1
+ {"version":3,"file":"runCodeGen.d.ts","sourceRoot":"","sources":["../src/runCodeGen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,EAA4G,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACxJ,OAAO,EAAyC,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI9F,OAAO,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3C,OAAO,EAAmB,sBAAsB,EAA8B,MAAM,mBAAmB,CAAC;AASxG,OAAO,EAAE,iBAAiB,EAA2B,gBAAgB,IAAI,wBAAwB,EAAkC,MAAM,oCAAoC,CAAC;AAI9K,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAG5D,OAAO,8DAA8D,CAAC;AAStE,KAAK,gBAAgB,GAAG,wBAAwB,CAAC;AAEjD;;GAEG;AACH,qBAAa,cAAc;IACzB;;;;OAIG;IACH,SAAS,CAAC,eAAe,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAE5E;;;;;;OAMG;IACH,SAAS,CAAC,qBAAqB,CAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,WAAW,EAAE,EACnB,OAAO,EAAE,sBAAsB,EAAE,GAChC,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAkB9C;;;;;;;;;;;;;OAaG;IACU,eAAe,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAIzD;;OAEG;IACH;;;;OAIG;IACU,YAAY,CAAC,UAAU,EAAE,gBAAgB,EAAE,sBAAsB,GAAE,OAAe,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBrK,GAAG,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe;IAQ7F;;;;OAIG;IACU,aAAa,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAiD9H;;;OAGG;cACa,sBAAsB,CAAC,UAAU,EAAE,gBAAgB,EAAE,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAyVpK;;;;;;OAMG;IACH,SAAS,CAAC,sBAAsB,CAAC,QAAQ,EAAE,EAAE,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,iBAAiB;IAa/F;;;;;;;;OAQG;cACa,sBAAsB,CACpC,IAAI,EAAE,iBAAiB,EACvB,WAAW,EAAE,QAAQ,EACrB,EAAE,EAAE,EAAE,CAAC,QAAQ,EACf,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC;CA+QpB;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CAAC,sBAAsB,GAAE,OAAe,EAAE,kBAAkB,GAAE,OAAe,iBAGjI;AAED;;;;;GAKG;AACH,wBAAsB,yCAAyC,CAC7D,sBAAsB,GAAE,OAAe,EACvC,kBAAkB,GAAE,OAAe,GAClC,OAAO,CAAC,WAAW,CAAC,CAGtB"}
@@ -11,11 +11,14 @@ import { SQLCodeGenBase } from './Database/sql_codegen.js';
11
11
  import { EntitySubClassGeneratorBase } from './Misc/entity_subclasses_codegen.js';
12
12
  import { ManageMetadataBase } from './Database/manage-metadata.js';
13
13
  import { applyIncludeSchemaScope } from './Database/schema-scope.js';
14
+ import { partitionEntitiesByOutputDirectory } from './Config/schema-output.js';
14
15
  import { outputDir, commands, configInfo, getSettingValue, dbPlatform, getExternalEntitySchemas, initializeConfig } from './Config/config.js';
16
+ import { resolveDirtySchemasForEmit, schemaKey } from './Misc/schema-emit.js';
17
+ import { EmitStats } from './Misc/emit-stats.js';
15
18
  import { logError, logStatus, logWarning, startSpinner, updateSpinner, succeedSpinner, failSpinner, warnSpinner } from './Misc/status_logging.js';
16
19
  import { CodeGenReporter } from './Misc/codegen-reporter.js';
17
20
  import * as MJ from '@memberjunction/core';
18
- import { RunCommandsBase } from './Misc/runCommand.js';
21
+ import { RunCommandsBase, formatCommandFailureDetail } from './Misc/runCommand.js';
19
22
  import { DBSchemaGeneratorBase } from './Database/dbSchema.js';
20
23
  import { AngularClientGeneratorBase } from './Angular/angular-codegen.js';
21
24
  import { CreateNewUserBase } from './Misc/createNewUser.js';
@@ -43,19 +46,29 @@ export class RunCodeGenBase {
43
46
  */
44
47
  this.commandFailures = [];
45
48
  }
46
- /** Record any failed entries from a BEFORE/AFTER command batch, paired by index. */
49
+ /**
50
+ * Record any failed entries from a BEFORE/AFTER command batch, paired by index.
51
+ *
52
+ * Returns just this batch's failures. Callers log what they get back rather than walking
53
+ * `commandFailures`, which accumulates across phases — iterating the whole array in the
54
+ * AFTER handler re-logged every BEFORE failure a second time.
55
+ */
47
56
  recordCommandFailures(phase, cmds, results) {
57
+ const recorded = [];
48
58
  results.forEach((r, i) => {
49
59
  if (!r.success) {
50
60
  const cmd = cmds[i];
51
61
  const cmdText = cmd ? [cmd.command, ...(cmd.args ?? [])].join(' ').trim() : `command #${i + 1}`;
52
- const detail = (r.error || r.output || '').trim();
53
- this.commandFailures.push({
62
+ const detail = formatCommandFailureDetail(r);
63
+ const failure = {
54
64
  context: `${phase} command`,
55
65
  message: detail ? `\`${cmdText}\` failed: ${detail}` : `\`${cmdText}\` failed`,
56
- });
66
+ };
67
+ this.commandFailures.push(failure);
68
+ recorded.push(failure);
57
69
  }
58
70
  });
71
+ return recorded;
59
72
  }
60
73
  /**
61
74
  * Acquire the per-platform data source for a CodeGen run.
@@ -149,8 +162,9 @@ export class RunCodeGenBase {
149
162
  };
150
163
  }
151
164
  catch (e) {
165
+ const message = e instanceof Error ? (e.stack ?? e.message) : String(e);
152
166
  failSpinner('CodeGen failed: ' + e);
153
- logError(e);
167
+ logError(message);
154
168
  return {
155
169
  success: false,
156
170
  command: 'codegen',
@@ -205,7 +219,12 @@ export class RunCodeGenBase {
205
219
  const results = await runCommandsObject.runCommands(beforeCommands);
206
220
  if (results.some((r) => !r.success)) {
207
221
  logError('ERROR running one or more BEFORE commands');
208
- this.recordCommandFailures('BEFORE', beforeCommands, results);
222
+ const recorded = this.recordCommandFailures('BEFORE', beforeCommands, results);
223
+ pipelineSuccess = false;
224
+ for (const failure of recorded) {
225
+ logError(failure.message);
226
+ reporter.note(failure.message);
227
+ }
209
228
  }
210
229
  }
211
230
  updateSpinner('Executing before-all SQL Scripts...');
@@ -355,6 +374,57 @@ export class RunCodeGenBase {
355
374
  return false;
356
375
  }
357
376
  }
377
+ // Metadata/view coherence. The CRUD validator asks "can the runtime WRITE this
378
+ // entity"; this asks the prior question — "can it READ it at all".
379
+ //
380
+ // A field the metadata declares but the base view does not produce makes every
381
+ // read of that entity fail with `column "X" does not exist`. Grids render that
382
+ // as an empty result rather than an error, so the entity looks like it holds no
383
+ // data. Nothing else in the pipeline notices, and the condition is permanent for
384
+ // any schema in `excludeSchemas` (CodeGen emits permissions only for those, so
385
+ // there is no regeneration pass to repair the view) — which by convention
386
+ // includes `__mj` on essentially every install.
387
+ //
388
+ // Reported, not fatal, by default: existing installs may already carry this drift
389
+ // and failing their next codegen run would be worse than telling them about it.
390
+ // Set MJ_CODEGEN_STRICT_FIELD_RESOLUTION=true to make it a hard gate; that is the
391
+ // intended default once fleets are clean.
392
+ try {
393
+ startSpinner('Validating entity fields resolve against their base views...');
394
+ const baseline = md.Entities.filter(e => e.IncludeInAPI);
395
+ const gaps = await sqlCodeGenObject.DBProvider.validateEntityFieldsResolve(conn, baseline);
396
+ if (gaps.length > 0) {
397
+ const affected = new Set(gaps.map(g => `${g.schema}.${g.entity}`));
398
+ const list = gaps
399
+ .map(g => ` - [${g.schema}] ${g.entity} → ${g.baseView} does not produce "${g.field}"${g.isVirtual ? ' (virtual)' : ''}`)
400
+ .join('\n');
401
+ const strict = process.env.MJ_CODEGEN_STRICT_FIELD_RESOLUTION === 'true';
402
+ const summary = `${gaps.length} declared field(s) across ${affected.size} entity(ies) are not produced by their base view.\n` +
403
+ `Every read of these entities fails with 'column ... does not exist'. A grid shows that as\n` +
404
+ `"no data" rather than an error, so the entity appears empty while its table is full.\n` +
405
+ `${list}\n\n` +
406
+ `Cause: a migration added the column to the base TABLE and registered the EntityField, but did\n` +
407
+ `not rebuild the base VIEW. For schemas listed in excludeSchemas (typically __mj) CodeGen never\n` +
408
+ `regenerates the view, so this does not self-heal.\n` +
409
+ `Fix: rebuild the affected base view(s). PostgreSQL permits appending columns in place via\n` +
410
+ `CREATE OR REPLACE VIEW, so no DROP/CASCADE is required.`;
411
+ if (strict) {
412
+ failSpinner(`Field-resolution validation FAILED: ${gaps.length} unreadable field(s)`);
413
+ logError(summary);
414
+ return false;
415
+ }
416
+ warnSpinner(`Field-resolution validation found ${gaps.length} unreadable field(s)`);
417
+ logWarning(summary);
418
+ }
419
+ else {
420
+ succeedSpinner('Entity fields all resolve against their base views');
421
+ }
422
+ }
423
+ catch (e) {
424
+ // Never fail the run on the validator's own error — it is a reporter.
425
+ warnSpinner('Field-resolution validation could not run');
426
+ logWarning(`Field-resolution validator threw: ${e instanceof Error ? e.message : String(e)}`);
427
+ }
358
428
  // Surface upstream SQL-pipeline failure even if validator passed: a
359
429
  // green validator just means whatever DID get generated is consistent;
360
430
  // it doesn't whitewash an upstream batch error.
@@ -394,7 +464,12 @@ export class RunCodeGenBase {
394
464
  const results = await runCommandsObject.runCommands(afterCommands);
395
465
  if (results.some((r) => !r.success)) {
396
466
  failSpinner('ERROR running one or more AFTER commands');
397
- this.recordCommandFailures('AFTER', afterCommands, results);
467
+ const recorded = this.recordCommandFailures('AFTER', afterCommands, results);
468
+ pipelineSuccess = false;
469
+ for (const failure of recorded) {
470
+ logError(failure.message);
471
+ reporter.note(failure.message);
472
+ }
398
473
  }
399
474
  else
400
475
  succeedSpinner('AFTER commands completed');
@@ -415,7 +490,8 @@ export class RunCodeGenBase {
415
490
  logStatus('MJ CodeGen Complete! ' + md.Entities.length + ' entities processed in ' + totalSeconds + 's @ ' + endTime.toLocaleString());
416
491
  // A BEFORE/AFTER command failure fails the run (success=false, exit 1) so it
417
492
  // isn't silently swallowed — details flow into the structured result below.
418
- return this.commandFailures.length === 0;
493
+ pipelineSuccess = pipelineSuccess && this.commandFailures.length === 0;
494
+ return pipelineSuccess;
419
495
  }
420
496
  catch (err) {
421
497
  pipelineSuccess = false;
@@ -439,6 +515,19 @@ export class RunCodeGenBase {
439
515
  logStatus('CodeGen report written: ' + filePath);
440
516
  }
441
517
  }
518
+ /**
519
+ * Resolve dirty-schema emit options for one generator call.
520
+ * `--skipdb` (files only) and `fileEmit.dirtySchemaOnly === false` rebuild
521
+ * every schema; write-if-changed still keeps mtimes stable. A full run with
522
+ * no new/modified entities yields an empty Set — the generator then only
523
+ * writes schema files that are missing.
524
+ */
525
+ buildSchemaEmitOptions(entities, skipDB) {
526
+ const fileEmit = configInfo.fileEmit;
527
+ return {
528
+ dirtySchemas: resolveDirtySchemasForEmit(entities, [...ManageMetadataBase.newEntityList, ...ManageMetadataBase.modifiedEntityList], skipDB, fileEmit?.dirtySchemaOnly !== false, ManageMetadataBase.deletedEntitySchemaList),
529
+ };
530
+ }
442
531
  /**
443
532
  * Runs the file-generation phase of CodeGen: GraphQL resolvers, entity
444
533
  * subclasses (core + non-core), Angular components, DB schema JSON, and
@@ -450,15 +539,16 @@ export class RunCodeGenBase {
450
539
  */
451
540
  async runFileGenerationPhase(conn, currentUser, md, skipDB) {
452
541
  const reporter = CodeGenReporter.Instance;
542
+ EmitStats.Reset();
453
543
  const apiEntities = md.Entities.filter((e) => e.IncludeInAPI);
454
- const excludedSchemaNames = configInfo.excludeSchemas.map(s => s.toLowerCase());
455
- const includedEntities = apiEntities.filter((e) => !excludedSchemaNames.includes(e.SchemaName.trim().toLowerCase()));
544
+ const excludedSchemaNames = configInfo.excludeSchemas.map(s => schemaKey(s));
545
+ const includedEntities = apiEntities.filter((e) => !excludedSchemaNames.includes(schemaKey(e.SchemaName)));
456
546
  const excludedCount = apiEntities.length - includedEntities.length;
457
547
  if (excludedCount > 0) {
458
548
  const excludedBySchema = apiEntities
459
- .filter((e) => excludedSchemaNames.includes(e.SchemaName.trim().toLowerCase()))
549
+ .filter((e) => excludedSchemaNames.includes(schemaKey(e.SchemaName)))
460
550
  .reduce((acc, e) => {
461
- const schema = e.SchemaName.trim();
551
+ const schema = (e.SchemaName ?? '').trim() || '(none)';
462
552
  acc[schema] = (acc[schema] || 0) + 1;
463
553
  return acc;
464
554
  }, {});
@@ -467,8 +557,8 @@ export class RunCodeGenBase {
467
557
  .join(', ');
468
558
  logStatus('Excluded ' + excludedCount + ' entities from code generation by schema: ' + schemaDetails);
469
559
  }
470
- const coreEntities = includedEntities.filter((e) => e.SchemaName.trim().toLowerCase() === mjCoreSchema.trim().toLowerCase());
471
- const nonCoreEntities = includedEntities.filter((e) => e.SchemaName.trim().toLowerCase() !== mjCoreSchema.trim().toLowerCase());
560
+ const coreEntities = includedEntities.filter((e) => schemaKey(e.SchemaName) === schemaKey(mjCoreSchema));
561
+ const nonCoreEntities = includedEntities.filter((e) => schemaKey(e.SchemaName) !== schemaKey(mjCoreSchema));
472
562
  // Entities whose schemas are owned by OTHER packages (see entityPackageName map). They must be
473
563
  // excluded from every artifact this package emits — GraphQL ObjectTypes included. If two packages
474
564
  // both emit an ObjectType for the same entity, graphql-js rejects the unified schema at boot with
@@ -476,7 +566,7 @@ export class RunCodeGenBase {
476
566
  // crash-loops.
477
567
  const externalSchemas = getExternalEntitySchemas().map(s => s.toLowerCase());
478
568
  const localNonCoreEntities = externalSchemas.length > 0
479
- ? nonCoreEntities.filter(e => !externalSchemas.includes(e.SchemaName.toLowerCase()))
569
+ ? nonCoreEntities.filter(e => !externalSchemas.includes(schemaKey(e.SchemaName)))
480
570
  : nonCoreEntities;
481
571
  const isVerbose = configInfo?.verboseOutput ?? false;
482
572
  if (!isVerbose)
@@ -486,7 +576,7 @@ export class RunCodeGenBase {
486
576
  if (isVerbose)
487
577
  startSpinner('Generating CORE Entity GraphQL Resolver Code...');
488
578
  const graphQLGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(GraphQLServerGeneratorBase);
489
- const ok = await reporter.phase('generateGraphQLCore', async () => graphQLGenerator.generateGraphQLServerCode(coreEntities, graphQLCoreResolversOutputDir, '@memberjunction/core-entities', true));
579
+ const ok = await reporter.phase('generateGraphQLCore', async () => graphQLGenerator.generateGraphQLServerCode(coreEntities, graphQLCoreResolversOutputDir, '@memberjunction/core-entities', true, this.buildSchemaEmitOptions(coreEntities, skipDB)));
490
580
  if (!ok) {
491
581
  failSpinner('Error generating GraphQL server code');
492
582
  return false;
@@ -502,7 +592,16 @@ export class RunCodeGenBase {
502
592
  const entityPackageName = typeof configInfo.entityPackageName === 'string'
503
593
  ? (configInfo.entityPackageName || 'mj_generatedentities')
504
594
  : 'mj_generatedentities';
505
- const ok = await reporter.phase('generateGraphQL', async () => graphQLGenerator.generateGraphQLServerCode(localNonCoreEntities, graphqlOutputDir, entityPackageName, false));
595
+ const graphqlGroups = partitionEntitiesByOutputDirectory(localNonCoreEntities, 'GraphQLServer', graphqlOutputDir, configInfo.schemaOutput);
596
+ const ok = await reporter.phase('generateGraphQL', async () => {
597
+ for (const [dir, group] of graphqlGroups) {
598
+ const groupOk = graphQLGenerator.generateGraphQLServerCode(group, dir, entityPackageName, false, this.buildSchemaEmitOptions(group, skipDB));
599
+ if (!groupOk) {
600
+ return false;
601
+ }
602
+ }
603
+ return true;
604
+ });
506
605
  if (!ok) {
507
606
  failSpinner('Error generating GraphQL Resolver code');
508
607
  return false;
@@ -517,7 +616,7 @@ export class RunCodeGenBase {
517
616
  if (isVerbose)
518
617
  startSpinner('Generating CORE Entity Subclass Code...');
519
618
  const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
520
- const ok = await reporter.phase('generateEntitySubclassesCore', () => entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, coreEntities, coreEntitySubClassOutputDir, skipDB));
619
+ const ok = await reporter.phase('generateEntitySubclassesCore', () => entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, coreEntities, coreEntitySubClassOutputDir, skipDB, this.buildSchemaEmitOptions(coreEntities, skipDB)));
521
620
  if (!ok) {
522
621
  failSpinner('Error generating entity subclass code');
523
622
  return false;
@@ -530,7 +629,16 @@ export class RunCodeGenBase {
530
629
  if (isVerbose)
531
630
  startSpinner('Generating Entity Subclass Code...');
532
631
  const entitySubClassGeneratorObject = MJGlobal.Instance.ClassFactory.CreateInstance(EntitySubClassGeneratorBase);
533
- const ok = await reporter.phase('generateEntitySubclasses', () => entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, localNonCoreEntities, entitySubClassOutputDir, skipDB));
632
+ const entityGroups = partitionEntitiesByOutputDirectory(localNonCoreEntities, 'EntitySubClasses', entitySubClassOutputDir, configInfo.schemaOutput);
633
+ const ok = await reporter.phase('generateEntitySubclasses', async () => {
634
+ for (const [dir, group] of entityGroups) {
635
+ const groupOk = await entitySubClassGeneratorObject.generateAllEntitySubClasses(conn, group, dir, skipDB, this.buildSchemaEmitOptions(group, skipDB));
636
+ if (!groupOk) {
637
+ return false;
638
+ }
639
+ }
640
+ return true;
641
+ });
534
642
  if (!ok) {
535
643
  failSpinner('Error generating entity subclass code');
536
644
  return false;
@@ -558,7 +666,16 @@ export class RunCodeGenBase {
558
666
  if (isVerbose)
559
667
  startSpinner('Generating Angular Code...');
560
668
  const angularGenerator = MJGlobal.Instance.ClassFactory.CreateInstance(AngularClientGeneratorBase);
561
- const ok = await reporter.phase('generateAngular', () => angularGenerator.generateAngularCode(localNonCoreEntities, angularOutputDir, '', currentUser));
669
+ const angularGroups = partitionEntitiesByOutputDirectory(localNonCoreEntities, 'Angular', angularOutputDir, configInfo.schemaOutput);
670
+ const ok = await reporter.phase('generateAngular', async () => {
671
+ for (const [dir, group] of angularGroups) {
672
+ const groupOk = angularGenerator.generateAngularCode(group, dir, '', currentUser);
673
+ if (!groupOk) {
674
+ return false;
675
+ }
676
+ }
677
+ return true;
678
+ });
562
679
  if (!ok) {
563
680
  failSpinner('Error generating Angular code');
564
681
  return false;
@@ -644,6 +761,15 @@ export class RunCodeGenBase {
644
761
  }
645
762
  else if (isVerbose)
646
763
  warnSpinner('Remote Operations output directory NOT found in config file, skipping...');
764
+ const emit = EmitStats.Snapshot();
765
+ reporter.counter('filesWritten', emit.filesWritten);
766
+ reporter.counter('filesSkipped', emit.filesSkipped);
767
+ reporter.counter('schemasEmitted', emit.schemasEmitted);
768
+ reporter.counter('schemasSkipped', emit.schemasSkipped);
769
+ reporter.mark('emitStats', emit);
770
+ logStatus(`File emit: wrote ${emit.filesWritten}, skipped ${emit.filesSkipped}, ` +
771
+ `schemas emitted ${emit.schemasEmitted} / skipped ${emit.schemasSkipped} ` +
772
+ `(assemble ${emit.assembleMs}ms)`);
647
773
  SQLLogging.finishSQLLogging();
648
774
  if (!isVerbose)
649
775
  succeedSpinner('TypeScript code generation completed');