@memberjunction/integration-schema-builder 0.0.1 → 5.8.0

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 (42) hide show
  1. package/dist/AccessControl.d.ts +26 -0
  2. package/dist/AccessControl.d.ts.map +1 -0
  3. package/dist/AccessControl.js +68 -0
  4. package/dist/AccessControl.js.map +1 -0
  5. package/dist/DDLGenerator.d.ts +30 -0
  6. package/dist/DDLGenerator.d.ts.map +1 -0
  7. package/dist/DDLGenerator.js +107 -0
  8. package/dist/DDLGenerator.js.map +1 -0
  9. package/dist/MetadataEmitter.d.ts +23 -0
  10. package/dist/MetadataEmitter.d.ts.map +1 -0
  11. package/dist/MetadataEmitter.js +52 -0
  12. package/dist/MetadataEmitter.js.map +1 -0
  13. package/dist/MigrationFileWriter.d.ts +25 -0
  14. package/dist/MigrationFileWriter.d.ts.map +1 -0
  15. package/dist/MigrationFileWriter.js +67 -0
  16. package/dist/MigrationFileWriter.js.map +1 -0
  17. package/dist/SchemaBuilder.d.ts +27 -0
  18. package/dist/SchemaBuilder.d.ts.map +1 -0
  19. package/dist/SchemaBuilder.js +167 -0
  20. package/dist/SchemaBuilder.js.map +1 -0
  21. package/dist/SchemaEvolution.d.ts +27 -0
  22. package/dist/SchemaEvolution.d.ts.map +1 -0
  23. package/dist/SchemaEvolution.js +91 -0
  24. package/dist/SchemaEvolution.js.map +1 -0
  25. package/dist/SoftFKConfigEmitter.d.ts +46 -0
  26. package/dist/SoftFKConfigEmitter.d.ts.map +1 -0
  27. package/dist/SoftFKConfigEmitter.js +109 -0
  28. package/dist/SoftFKConfigEmitter.js.map +1 -0
  29. package/dist/TypeMapper.d.ts +25 -0
  30. package/dist/TypeMapper.d.ts.map +1 -0
  31. package/dist/TypeMapper.js +72 -0
  32. package/dist/TypeMapper.js.map +1 -0
  33. package/dist/index.d.ts +10 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +11 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/interfaces.d.ts +172 -0
  38. package/dist/interfaces.d.ts.map +1 -0
  39. package/dist/interfaces.js +8 -0
  40. package/dist/interfaces.js.map +1 -0
  41. package/package.json +28 -7
  42. package/README.md +0 -45
@@ -0,0 +1,26 @@
1
+ /**
2
+ * AccessControl — enforces which MJ entities can be targeted by integrations.
3
+ * Two layers: hardcoded blocklist + EntitySetting check for __mj entities.
4
+ */
5
+ import type { AccessControlResult } from './interfaces.js';
6
+ /**
7
+ * Check if an entity is on the hardcoded blocklist.
8
+ */
9
+ export declare function IsEntityBlocked(entityName: string): boolean;
10
+ /**
11
+ * Check if an integration is allowed to write to a given entity.
12
+ *
13
+ * Rules:
14
+ * 1. Blocked entities → always denied
15
+ * 2. Non-__mj schema entities → always allowed (custom schemas are user-controlled)
16
+ * 3. __mj entities → allowed only if IntegrationWriteAllowed = 'true' in EntitySettings
17
+ */
18
+ export declare function IsIntegrationWriteAllowed(entityName: string, schemaName: string, entitySettings: Array<{
19
+ Name: string;
20
+ Value: string;
21
+ }>): AccessControlResult;
22
+ /**
23
+ * Get the list of blocked entity names.
24
+ */
25
+ export declare function GetBlockedEntities(): ReadonlyArray<string>;
26
+ //# sourceMappingURL=AccessControl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessControl.d.ts","sourceRoot":"","sources":["../src/AccessControl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAuB3D;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACrC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GACvD,mBAAmB,CA6BrB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,aAAa,CAAC,MAAM,CAAC,CAE1D"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * System entities that can NEVER be integration targets, regardless of settings.
3
+ */
4
+ const BLOCKED_ENTITIES = [
5
+ 'MJ: Entities',
6
+ 'MJ: Entity Fields',
7
+ 'MJ: Entity Relationships',
8
+ 'MJ: Entity Permissions',
9
+ 'Users',
10
+ 'Roles',
11
+ 'User Roles',
12
+ 'Role Permissions',
13
+ 'Applications',
14
+ 'Application Entities',
15
+ 'MJ: Entity Settings',
16
+ 'MJ: Encryption Keys',
17
+ 'MJ: Encryption Algorithms',
18
+ 'MJ: Audit Log Types',
19
+ 'MJ: Audit Logs',
20
+ ];
21
+ /**
22
+ * Check if an entity is on the hardcoded blocklist.
23
+ */
24
+ export function IsEntityBlocked(entityName) {
25
+ return BLOCKED_ENTITIES.includes(entityName);
26
+ }
27
+ /**
28
+ * Check if an integration is allowed to write to a given entity.
29
+ *
30
+ * Rules:
31
+ * 1. Blocked entities → always denied
32
+ * 2. Non-__mj schema entities → always allowed (custom schemas are user-controlled)
33
+ * 3. __mj entities → allowed only if IntegrationWriteAllowed = 'true' in EntitySettings
34
+ */
35
+ export function IsIntegrationWriteAllowed(entityName, schemaName, entitySettings) {
36
+ if (IsEntityBlocked(entityName)) {
37
+ return {
38
+ Allowed: false,
39
+ Reason: `Entity "${entityName}" is on the system blocklist and cannot be an integration target.`,
40
+ };
41
+ }
42
+ // Non-__mj schemas are always allowed
43
+ if (schemaName !== '__mj') {
44
+ return {
45
+ Allowed: true,
46
+ Reason: `Entity "${entityName}" is in custom schema "${schemaName}" — integration writes allowed.`,
47
+ };
48
+ }
49
+ // __mj entity — check for IntegrationWriteAllowed setting
50
+ const setting = entitySettings.find(s => s.Name === 'IntegrationWriteAllowed');
51
+ if (setting && setting.Value === 'true') {
52
+ return {
53
+ Allowed: true,
54
+ Reason: `Entity "${entityName}" has IntegrationWriteAllowed=true.`,
55
+ };
56
+ }
57
+ return {
58
+ Allowed: false,
59
+ Reason: `Entity "${entityName}" is in __mj schema and does not have IntegrationWriteAllowed=true.`,
60
+ };
61
+ }
62
+ /**
63
+ * Get the list of blocked entity names.
64
+ */
65
+ export function GetBlockedEntities() {
66
+ return BLOCKED_ENTITIES;
67
+ }
68
+ //# sourceMappingURL=AccessControl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessControl.js","sourceRoot":"","sources":["../src/AccessControl.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,gBAAgB,GAA0B;IAC5C,cAAc;IACd,mBAAmB;IACnB,0BAA0B;IAC1B,wBAAwB;IACxB,OAAO;IACP,OAAO;IACP,YAAY;IACZ,kBAAkB;IAClB,cAAc;IACd,sBAAsB;IACtB,qBAAqB;IACrB,qBAAqB;IACrB,2BAA2B;IAC3B,qBAAqB;IACrB,gBAAgB;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAC9C,OAAO,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CACrC,UAAkB,EAClB,UAAkB,EAClB,cAAsD;IAEtD,IAAI,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,OAAO;YACH,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,WAAW,UAAU,mEAAmE;SACnG,CAAC;IACN,CAAC;IAED,sCAAsC;IACtC,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO;YACH,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,WAAW,UAAU,0BAA0B,UAAU,iCAAiC;SACrG,CAAC;IACN,CAAC;IAED,0DAA0D;IAC1D,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,yBAAyB,CAAC,CAAC;IAC/E,IAAI,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;QACtC,OAAO;YACH,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,WAAW,UAAU,qCAAqC;SACrE,CAAC;IACN,CAAC;IAED,OAAO;QACH,OAAO,EAAE,KAAK;QACd,MAAM,EAAE,WAAW,UAAU,qEAAqE;KACrG,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAC9B,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * DDLGenerator — produces CREATE TABLE / ALTER TABLE SQL strings.
3
+ * File-emission only — never executes SQL.
4
+ */
5
+ import type { DatabasePlatform, TargetColumnConfig, TargetTableConfig, ColumnModification } from './interfaces.js';
6
+ /**
7
+ * Generates DDL SQL for creating and altering integration tables.
8
+ */
9
+ export declare class DDLGenerator {
10
+ /**
11
+ * Generate CREATE SCHEMA IF NOT EXISTS statement.
12
+ */
13
+ GenerateCreateSchema(schemaName: string, platform: DatabasePlatform): string;
14
+ /**
15
+ * Generate a full CREATE TABLE statement with standard integration columns.
16
+ */
17
+ GenerateCreateTable(config: TargetTableConfig, platform: DatabasePlatform): string;
18
+ /**
19
+ * Generate ALTER TABLE ADD COLUMN statement.
20
+ */
21
+ GenerateAlterTableAddColumn(schemaName: string, tableName: string, column: TargetColumnConfig, platform: DatabasePlatform): string;
22
+ /**
23
+ * Generate ALTER TABLE ALTER COLUMN statement for type/nullability changes.
24
+ */
25
+ GenerateAlterTableAlterColumn(schemaName: string, tableName: string, mod: ColumnModification, platform: DatabasePlatform): string;
26
+ private StandardColumns;
27
+ }
28
+ /** Validates that an identifier contains only safe characters. */
29
+ export declare function ValidateIdentifier(name: string, kind: string): void;
30
+ //# sourceMappingURL=DDLGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DDLGenerator.d.ts","sourceRoot":"","sources":["../src/DDLGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAKnH;;GAEG;AACH,qBAAa,YAAY;IACrB;;OAEG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM;IAQ5E;;OAEG;IACH,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM;IA8BlF;;OAEG;IACH,2BAA2B,CACvB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,gBAAgB,GAC3B,MAAM;IAgBT;;OAEG;IACH,6BAA6B,CACzB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,kBAAkB,EACvB,QAAQ,EAAE,gBAAgB,GAC3B,MAAM;IAeT,OAAO,CAAC,eAAe;CAmB1B;AAYD,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAInE"}
@@ -0,0 +1,107 @@
1
+ /** Characters allowed in identifiers (schema, table, column names). */
2
+ const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
3
+ /**
4
+ * Generates DDL SQL for creating and altering integration tables.
5
+ */
6
+ export class DDLGenerator {
7
+ /**
8
+ * Generate CREATE SCHEMA IF NOT EXISTS statement.
9
+ */
10
+ GenerateCreateSchema(schemaName, platform) {
11
+ ValidateIdentifier(schemaName, 'schema');
12
+ if (platform === 'sqlserver') {
13
+ return `IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = '${schemaName}')\n EXEC('CREATE SCHEMA [${schemaName}]');\nGO`;
14
+ }
15
+ return `CREATE SCHEMA IF NOT EXISTS "${schemaName}";`;
16
+ }
17
+ /**
18
+ * Generate a full CREATE TABLE statement with standard integration columns.
19
+ */
20
+ GenerateCreateTable(config, platform) {
21
+ ValidateIdentifier(config.SchemaName, 'schema');
22
+ ValidateIdentifier(config.TableName, 'table');
23
+ const q = platform === 'sqlserver' ? QuoteSqlServer : QuotePostgres;
24
+ const fullTable = `${q(config.SchemaName)}.${q(config.TableName)}`;
25
+ const lines = [];
26
+ // Standard columns
27
+ lines.push(...this.StandardColumns(platform));
28
+ // User-configured columns
29
+ for (const col of config.Columns) {
30
+ ValidateIdentifier(col.TargetColumnName, 'column');
31
+ const nullable = col.IsNullable ? 'NULL' : 'NOT NULL';
32
+ const defaultExpr = col.DefaultValue != null ? ` DEFAULT ${col.DefaultValue}` : '';
33
+ lines.push(` ${q(col.TargetColumnName)} ${col.TargetSqlType} ${nullable}${defaultExpr}`);
34
+ }
35
+ // Constraints
36
+ const pkName = `PK_${config.SchemaName}_${config.TableName}`;
37
+ const uqName = `UQ_${config.SchemaName}_${config.TableName}_SourceRecordID`;
38
+ lines.push(` CONSTRAINT ${q(pkName)} PRIMARY KEY (${q('ID')})`);
39
+ lines.push(` CONSTRAINT ${q(uqName)} UNIQUE (${q('SourceRecordID')})`);
40
+ const body = lines.join(',\n');
41
+ return `CREATE TABLE ${fullTable} (\n${body}\n);`;
42
+ }
43
+ /**
44
+ * Generate ALTER TABLE ADD COLUMN statement.
45
+ */
46
+ GenerateAlterTableAddColumn(schemaName, tableName, column, platform) {
47
+ ValidateIdentifier(schemaName, 'schema');
48
+ ValidateIdentifier(tableName, 'table');
49
+ ValidateIdentifier(column.TargetColumnName, 'column');
50
+ const q = platform === 'sqlserver' ? QuoteSqlServer : QuotePostgres;
51
+ const fullTable = `${q(schemaName)}.${q(tableName)}`;
52
+ const nullable = column.IsNullable ? 'NULL' : 'NOT NULL';
53
+ const defaultExpr = column.DefaultValue != null ? ` DEFAULT ${column.DefaultValue}` : '';
54
+ if (platform === 'sqlserver') {
55
+ return `ALTER TABLE ${fullTable}\n ADD ${q(column.TargetColumnName)} ${column.TargetSqlType} ${nullable}${defaultExpr};`;
56
+ }
57
+ return `ALTER TABLE ${fullTable}\n ADD COLUMN ${q(column.TargetColumnName)} ${column.TargetSqlType} ${nullable}${defaultExpr};`;
58
+ }
59
+ /**
60
+ * Generate ALTER TABLE ALTER COLUMN statement for type/nullability changes.
61
+ */
62
+ GenerateAlterTableAlterColumn(schemaName, tableName, mod, platform) {
63
+ ValidateIdentifier(schemaName, 'schema');
64
+ ValidateIdentifier(tableName, 'table');
65
+ ValidateIdentifier(mod.ColumnName, 'column');
66
+ const q = platform === 'sqlserver' ? QuoteSqlServer : QuotePostgres;
67
+ const fullTable = `${q(schemaName)}.${q(tableName)}`;
68
+ const nullable = mod.NewNullable ? 'NULL' : 'NOT NULL';
69
+ if (platform === 'sqlserver') {
70
+ return `ALTER TABLE ${fullTable}\n ALTER COLUMN ${q(mod.ColumnName)} ${mod.NewType} ${nullable};`;
71
+ }
72
+ return `ALTER TABLE ${fullTable}\n ALTER COLUMN ${q(mod.ColumnName)} TYPE ${mod.NewType},\n ALTER COLUMN ${q(mod.ColumnName)} ${mod.NewNullable ? 'DROP NOT NULL' : 'SET NOT NULL'};`;
73
+ }
74
+ StandardColumns(platform) {
75
+ const q = platform === 'sqlserver' ? QuoteSqlServer : QuotePostgres;
76
+ if (platform === 'sqlserver') {
77
+ return [
78
+ ` ${q('ID')} UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID()`,
79
+ ` ${q('SourceRecordID')} NVARCHAR(255) NOT NULL`,
80
+ ` ${q('SourceJSON')} NVARCHAR(MAX) NULL`,
81
+ ` ${q('SyncStatus')} NVARCHAR(50) NOT NULL DEFAULT 'Active'`,
82
+ ` ${q('LastSyncedAt')} DATETIMEOFFSET NULL`,
83
+ ];
84
+ }
85
+ return [
86
+ ` ${q('ID')} UUID NOT NULL DEFAULT gen_random_uuid()`,
87
+ ` ${q('SourceRecordID')} VARCHAR(255) NOT NULL`,
88
+ ` ${q('SourceJSON')} TEXT NULL`,
89
+ ` ${q('SyncStatus')} VARCHAR(50) NOT NULL DEFAULT 'Active'`,
90
+ ` ${q('LastSyncedAt')} TIMESTAMPTZ NULL`,
91
+ ];
92
+ }
93
+ }
94
+ // ─── Helpers ────────────────────────────────────────────────────────
95
+ function QuoteSqlServer(name) {
96
+ return `[${name}]`;
97
+ }
98
+ function QuotePostgres(name) {
99
+ return `"${name}"`;
100
+ }
101
+ /** Validates that an identifier contains only safe characters. */
102
+ export function ValidateIdentifier(name, kind) {
103
+ if (!IDENTIFIER_RE.test(name)) {
104
+ throw new Error(`Invalid ${kind} name "${name}": must match ${IDENTIFIER_RE.source}`);
105
+ }
106
+ }
107
+ //# sourceMappingURL=DDLGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DDLGenerator.js","sourceRoot":"","sources":["../src/DDLGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * MetadataEmitter — produces /metadata/ JSON files for mj-sync.
3
+ * Generates EntitySettings (e.g., IntegrationWriteAllowed) and mj-sync config.
4
+ */
5
+ import type { EmittedFile } from './interfaces.js';
6
+ /**
7
+ * Produces metadata JSON files for the mj-sync push pipeline.
8
+ */
9
+ export declare class MetadataEmitter {
10
+ /**
11
+ * Generate a single EntitySettings JSON object for an entity.
12
+ */
13
+ GenerateEntitySettingsRecord(entityName: string, settings: Record<string, string>): Record<string, unknown>;
14
+ /**
15
+ * Emit the IntegrationWriteAllowed EntitySettings file for a set of entities.
16
+ */
17
+ EmitEntitySettingsFile(entityNames: string[], metadataDir: string): EmittedFile;
18
+ /**
19
+ * Emit the .mj-sync.json config for the entity-settings directory.
20
+ */
21
+ EmitMjSyncConfig(metadataDir: string): EmittedFile;
22
+ }
23
+ //# sourceMappingURL=MetadataEmitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetadataEmitter.d.ts","sourceRoot":"","sources":["../src/MetadataEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;GAEG;AACH,qBAAa,eAAe;IACxB;;OAEG;IACH,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAoB3G;;OAEG;IACH,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,WAAW;IAY/E;;OAEG;IACH,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW;CAYrD"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Produces metadata JSON files for the mj-sync push pipeline.
3
+ */
4
+ export class MetadataEmitter {
5
+ /**
6
+ * Generate a single EntitySettings JSON object for an entity.
7
+ */
8
+ GenerateEntitySettingsRecord(entityName, settings) {
9
+ const settingsRecords = Object.entries(settings).map(([name, value]) => ({
10
+ fields: {
11
+ Name: name,
12
+ Value: value,
13
+ Comments: `Managed by Integration Schema Builder`,
14
+ },
15
+ }));
16
+ return {
17
+ fields: { Name: entityName },
18
+ relatedEntities: {
19
+ 'MJ: Entity Settings': settingsRecords,
20
+ },
21
+ primaryKey: {
22
+ ID: `@lookup:MJ: Entities.Name=${entityName}`,
23
+ },
24
+ };
25
+ }
26
+ /**
27
+ * Emit the IntegrationWriteAllowed EntitySettings file for a set of entities.
28
+ */
29
+ EmitEntitySettingsFile(entityNames, metadataDir) {
30
+ const records = entityNames.map(name => this.GenerateEntitySettingsRecord(name, { IntegrationWriteAllowed: 'true' }));
31
+ return {
32
+ FilePath: `${metadataDir}/entity-settings/.integration-write-allowed.json`,
33
+ Content: JSON.stringify(records, null, 2) + '\n',
34
+ Description: `EntitySettings: IntegrationWriteAllowed=true for ${entityNames.join(', ')}`,
35
+ };
36
+ }
37
+ /**
38
+ * Emit the .mj-sync.json config for the entity-settings directory.
39
+ */
40
+ EmitMjSyncConfig(metadataDir) {
41
+ const config = {
42
+ entity: 'MJ: Entities',
43
+ filePattern: '**/.*.json',
44
+ };
45
+ return {
46
+ FilePath: `${metadataDir}/entity-settings/.mj-sync.json`,
47
+ Content: JSON.stringify(config, null, 2) + '\n',
48
+ Description: 'mj-sync configuration for entity-settings directory',
49
+ };
50
+ }
51
+ }
52
+ //# sourceMappingURL=MetadataEmitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetadataEmitter.js","sourceRoot":"","sources":["../src/MetadataEmitter.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,OAAO,eAAe;IACxB;;OAEG;IACH,4BAA4B,CAAC,UAAkB,EAAE,QAAgC;QAC7E,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YACrE,MAAM,EAAE;gBACJ,IAAI,EAAE,IAAI;gBACV,KAAK,EAAE,KAAK;gBACZ,QAAQ,EAAE,uCAAuC;aACpD;SACJ,CAAC,CAAC,CAAC;QAEJ,OAAO;YACH,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;YAC5B,eAAe,EAAE;gBACb,qBAAqB,EAAE,eAAe;aACzC;YACD,UAAU,EAAE;gBACR,EAAE,EAAE,6BAA6B,UAAU,EAAE;aAChD;SACJ,CAAC;IACN,CAAC;IAED;;OAEG;IACH,sBAAsB,CAAC,WAAqB,EAAE,WAAmB;QAC7D,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CACnC,IAAI,CAAC,4BAA4B,CAAC,IAAI,EAAE,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,CAC/E,CAAC;QAEF,OAAO;YACH,QAAQ,EAAE,GAAG,WAAW,kDAAkD;YAC1E,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI;YAChD,WAAW,EAAE,oDAAoD,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SAC5F,CAAC;IACN,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,WAAmB;QAChC,MAAM,MAAM,GAAG;YACX,MAAM,EAAE,cAAc;YACtB,WAAW,EAAE,YAAY;SAC5B,CAAC;QAEF,OAAO;YACH,QAAQ,EAAE,GAAG,WAAW,gCAAgC;YACxD,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI;YAC/C,WAAW,EAAE,qDAAqD;SACrE,CAAC;IACN,CAAC;CACJ"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * MigrationFileWriter — wraps DDL in properly-named Flyway migration files.
3
+ * File-emission only — produces EmittedFile objects, never writes to disk.
4
+ */
5
+ import type { EmittedFile, MigrationMetadata } from './interfaces.js';
6
+ /**
7
+ * Produces Flyway-compatible migration file names and content.
8
+ */
9
+ export declare class MigrationFileWriter {
10
+ /**
11
+ * Generate a Flyway-compatible migration file name.
12
+ * Format: V{YYYYMMDDHHMM}__v{VERSION}.x_Integration_{Source}_{Action}.sql
13
+ */
14
+ GenerateMigrationFileName(sourceType: string, action: string, mjVersion: string, migrationsDir: string, timestamp: Date): string;
15
+ /**
16
+ * Wrap DDL content in a migration file with a header comment.
17
+ */
18
+ WrapInMigrationFile(ddl: string, metadata: MigrationMetadata, filePath: string): EmittedFile;
19
+ /**
20
+ * Generate a sequence of timestamps, each 1 minute apart,
21
+ * for ordering multiple migrations correctly.
22
+ */
23
+ GenerateTimestampSequence(count: number, startTime?: Date): Date[];
24
+ }
25
+ //# sourceMappingURL=MigrationFileWriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationFileWriter.d.ts","sourceRoot":"","sources":["../src/MigrationFileWriter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;GAEG;AACH,qBAAa,mBAAmB;IAC5B;;;OAGG;IACH,yBAAyB,CACrB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,IAAI,GAChB,MAAM;IAOT;;OAEG;IACH,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW;IAwB5F;;;OAGG;IACH,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,IAAiB,GAAG,IAAI,EAAE;CAQjF"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Produces Flyway-compatible migration file names and content.
3
+ */
4
+ export class MigrationFileWriter {
5
+ /**
6
+ * Generate a Flyway-compatible migration file name.
7
+ * Format: V{YYYYMMDDHHMM}__v{VERSION}.x_Integration_{Source}_{Action}.sql
8
+ */
9
+ GenerateMigrationFileName(sourceType, action, mjVersion, migrationsDir, timestamp) {
10
+ const ts = FormatTimestamp(timestamp);
11
+ const safeSrc = SanitizeForFileName(sourceType);
12
+ const safeAction = SanitizeForFileName(action);
13
+ return `${migrationsDir}/V${ts}__v${mjVersion}.x_Integration_${safeSrc}_${safeAction}.sql`;
14
+ }
15
+ /**
16
+ * Wrap DDL content in a migration file with a header comment.
17
+ */
18
+ WrapInMigrationFile(ddl, metadata, filePath) {
19
+ const header = [
20
+ `-- ============================================================`,
21
+ `-- Auto-generated by MJ Integration Schema Builder`,
22
+ `-- Source: ${metadata.SourceType} | Object: ${metadata.ObjectName}`,
23
+ `-- Action: ${metadata.Action}`,
24
+ `-- Generated: ${metadata.Timestamp}`,
25
+ `-- Generated By: ${metadata.GeneratedBy}`,
26
+ `-- ============================================================`,
27
+ `-- NOTE: __mj_CreatedAt, __mj_UpdatedAt, FK indexes, views, and SPs`,
28
+ `-- are created by CodeGen — do NOT add them here.`,
29
+ `-- NOTE: Foreign keys are soft FKs defined in additionalSchemaInfo,`,
30
+ `-- applied by CodeGen — no FK constraints in this migration.`,
31
+ `-- ============================================================`,
32
+ ``,
33
+ ].join('\n');
34
+ return {
35
+ FilePath: filePath,
36
+ Content: header + ddl + '\n',
37
+ Description: `Migration: ${metadata.Action} for ${metadata.SourceType}.${metadata.ObjectName}`,
38
+ };
39
+ }
40
+ /**
41
+ * Generate a sequence of timestamps, each 1 minute apart,
42
+ * for ordering multiple migrations correctly.
43
+ */
44
+ GenerateTimestampSequence(count, startTime = new Date()) {
45
+ const result = [];
46
+ for (let i = 0; i < count; i++) {
47
+ const ts = new Date(startTime.getTime() + i * 60_000);
48
+ result.push(ts);
49
+ }
50
+ return result;
51
+ }
52
+ }
53
+ // ─── Helpers ────────────────────────────────────────────────────────
54
+ /** Format a Date as YYYYMMDDHHMM for Flyway versioning. */
55
+ function FormatTimestamp(date) {
56
+ const y = date.getUTCFullYear();
57
+ const mo = String(date.getUTCMonth() + 1).padStart(2, '0');
58
+ const d = String(date.getUTCDate()).padStart(2, '0');
59
+ const h = String(date.getUTCHours()).padStart(2, '0');
60
+ const mi = String(date.getUTCMinutes()).padStart(2, '0');
61
+ return `${y}${mo}${d}${h}${mi}`;
62
+ }
63
+ /** Remove characters unsafe for file names. */
64
+ function SanitizeForFileName(input) {
65
+ return input.replace(/[^A-Za-z0-9_]/g, '');
66
+ }
67
+ //# sourceMappingURL=MigrationFileWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationFileWriter.js","sourceRoot":"","sources":["../src/MigrationFileWriter.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAC5B;;;OAGG;IACH,yBAAyB,CACrB,UAAkB,EAClB,MAAc,EACd,SAAiB,EACjB,aAAqB,EACrB,SAAe;QAEf,MAAM,EAAE,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC/C,OAAO,GAAG,aAAa,KAAK,EAAE,MAAM,SAAS,kBAAkB,OAAO,IAAI,UAAU,MAAM,CAAC;IAC/F,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,GAAW,EAAE,QAA2B,EAAE,QAAgB;QAC1E,MAAM,MAAM,GAAG;YACX,iEAAiE;YACjE,oDAAoD;YACpD,cAAc,QAAQ,CAAC,UAAU,cAAc,QAAQ,CAAC,UAAU,EAAE;YACpE,cAAc,QAAQ,CAAC,MAAM,EAAE;YAC/B,iBAAiB,QAAQ,CAAC,SAAS,EAAE;YACrC,oBAAoB,QAAQ,CAAC,WAAW,EAAE;YAC1C,iEAAiE;YACjE,qEAAqE;YACrE,yDAAyD;YACzD,qEAAqE;YACrE,oEAAoE;YACpE,iEAAiE;YACjE,EAAE;SACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,OAAO;YACH,QAAQ,EAAE,QAAQ;YAClB,OAAO,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;YAC5B,WAAW,EAAE,cAAc,QAAQ,CAAC,MAAM,QAAQ,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,EAAE;SACjG,CAAC;IACN,CAAC;IAED;;;OAGG;IACH,yBAAyB,CAAC,KAAa,EAAE,YAAkB,IAAI,IAAI,EAAE;QACjE,MAAM,MAAM,GAAW,EAAE,CAAC;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AAED,uEAAuE;AAEvE,2DAA2D;AAC3D,SAAS,eAAe,CAAC,IAAU;IAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzD,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC;AACpC,CAAC;AAED,+CAA+C;AAC/C,SAAS,mBAAmB,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * SchemaBuilder — orchestrates the full schema generation pipeline.
3
+ * Pure function: takes input → produces output files. Never touches the database.
4
+ */
5
+ import type { SchemaBuilderInput, SchemaBuilderOutput } from './interfaces.js';
6
+ /**
7
+ * Main entry point for the Integration Schema Builder.
8
+ * Coordinates DDL generation, soft FK emission, metadata emission, and evolution diffs.
9
+ */
10
+ export declare class SchemaBuilder {
11
+ private readonly DDL;
12
+ private readonly MigrationWriter;
13
+ private readonly SoftFKEmitter;
14
+ private readonly MetaEmitter;
15
+ private readonly Evolution;
16
+ /**
17
+ * Build all artifacts for a set of integration target tables.
18
+ */
19
+ BuildSchema(input: SchemaBuilderInput): SchemaBuilderOutput;
20
+ private ValidateAccessControl;
21
+ private CheckCollisions;
22
+ private ClassifyConfigs;
23
+ private GetUniqueSchemas;
24
+ private CollectSoftFKs;
25
+ private MakeMigrationMeta;
26
+ }
27
+ //# sourceMappingURL=SchemaBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaBuilder.d.ts","sourceRoot":"","sources":["../src/SchemaBuilder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAKR,kBAAkB,EAClB,mBAAmB,EAGtB,MAAM,iBAAiB,CAAC;AAQzB;;;GAGG;AACH,qBAAa,aAAa;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsB;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAEnD;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB;IAmG3D,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,iBAAiB;CAS5B"}
@@ -0,0 +1,167 @@
1
+ import { DDLGenerator } from './DDLGenerator.js';
2
+ import { MigrationFileWriter } from './MigrationFileWriter.js';
3
+ import { SoftFKConfigEmitter } from './SoftFKConfigEmitter.js';
4
+ import { MetadataEmitter } from './MetadataEmitter.js';
5
+ import { SchemaEvolution } from './SchemaEvolution.js';
6
+ import { IsIntegrationWriteAllowed } from './AccessControl.js';
7
+ /**
8
+ * Main entry point for the Integration Schema Builder.
9
+ * Coordinates DDL generation, soft FK emission, metadata emission, and evolution diffs.
10
+ */
11
+ export class SchemaBuilder {
12
+ constructor() {
13
+ this.DDL = new DDLGenerator();
14
+ this.MigrationWriter = new MigrationFileWriter();
15
+ this.SoftFKEmitter = new SoftFKConfigEmitter();
16
+ this.MetaEmitter = new MetadataEmitter();
17
+ this.Evolution = new SchemaEvolution();
18
+ }
19
+ /**
20
+ * Build all artifacts for a set of integration target tables.
21
+ */
22
+ BuildSchema(input) {
23
+ const output = {
24
+ MigrationFiles: [],
25
+ AdditionalSchemaInfoUpdate: null,
26
+ MetadataFiles: [],
27
+ Warnings: [],
28
+ Errors: [],
29
+ };
30
+ // Step 1: Validate access control
31
+ const accessErrors = this.ValidateAccessControl(input);
32
+ if (accessErrors.length > 0) {
33
+ output.Errors.push(...accessErrors);
34
+ return output;
35
+ }
36
+ // Step 2: Pre-flight collision checks
37
+ const collisionWarnings = this.CheckCollisions(input);
38
+ output.Warnings.push(...collisionWarnings);
39
+ // Step 3: Separate new tables vs evolution
40
+ const { newConfigs, evolutionConfigs } = this.ClassifyConfigs(input);
41
+ // Step 4: Generate DDL migrations
42
+ const now = new Date();
43
+ const allMigrations = [];
44
+ // Schema creation migrations (deduplicate by schema name)
45
+ const schemasToCreate = this.GetUniqueSchemas(newConfigs);
46
+ const timestamps = this.MigrationWriter.GenerateTimestampSequence(schemasToCreate.length + newConfigs.length + evolutionConfigs.length, now);
47
+ let tsIdx = 0;
48
+ for (const schemaName of schemasToCreate) {
49
+ const ddl = this.DDL.GenerateCreateSchema(schemaName, input.Platform);
50
+ const meta = this.MakeMigrationMeta(input, 'CreateSchema', schemaName);
51
+ const filePath = this.MigrationWriter.GenerateMigrationFileName(input.SourceType, `CreateSchema_${schemaName}`, input.MJVersion, input.MigrationsDir, timestamps[tsIdx++]);
52
+ allMigrations.push(this.MigrationWriter.WrapInMigrationFile(ddl, meta, filePath));
53
+ }
54
+ // Table creation migrations
55
+ for (const config of newConfigs) {
56
+ const ddl = this.DDL.GenerateCreateTable(config, input.Platform);
57
+ const meta = this.MakeMigrationMeta(input, `Create${config.TableName}Table`, config.TableName);
58
+ const filePath = this.MigrationWriter.GenerateMigrationFileName(input.SourceType, `Create${config.TableName}Table`, input.MJVersion, input.MigrationsDir, timestamps[tsIdx++]);
59
+ allMigrations.push(this.MigrationWriter.WrapInMigrationFile(ddl, meta, filePath));
60
+ }
61
+ // Evolution migrations (ALTER TABLE)
62
+ for (const { config, existing } of evolutionConfigs) {
63
+ const sourceObj = input.SourceSchema.Objects.find(o => o.ExternalName === config.SourceObjectName);
64
+ if (!sourceObj)
65
+ continue;
66
+ const diff = this.Evolution.DiffSchema(sourceObj, config, existing, input.Platform);
67
+ if (diff.AddedColumns.length === 0 && diff.ModifiedColumns.length === 0 && diff.RemovedColumns.length === 0) {
68
+ continue; // No changes
69
+ }
70
+ const ddl = this.Evolution.GenerateEvolutionMigration(diff, config.SchemaName, config.TableName, input.Platform);
71
+ const meta = this.MakeMigrationMeta(input, `Alter${config.TableName}`, config.TableName);
72
+ const filePath = this.MigrationWriter.GenerateMigrationFileName(input.SourceType, `Alter${config.TableName}`, input.MJVersion, input.MigrationsDir, timestamps[tsIdx++]);
73
+ allMigrations.push(this.MigrationWriter.WrapInMigrationFile(ddl, meta, filePath));
74
+ }
75
+ output.MigrationFiles = allMigrations;
76
+ // Step 5: Generate soft FK config
77
+ const allSoftFKs = this.CollectSoftFKs(input, evolutionConfigs);
78
+ if (allSoftFKs.length > 0) {
79
+ const existingConfig = this.SoftFKEmitter.ParseExistingConfig(null); // Caller provides existing content via input
80
+ const merged = this.SoftFKEmitter.MergeSchemaConfig(existingConfig, allSoftFKs);
81
+ output.AdditionalSchemaInfoUpdate = this.SoftFKEmitter.EmitConfigFile(input.AdditionalSchemaInfoPath, merged);
82
+ }
83
+ // Step 6: Generate metadata files for __mj entity targets
84
+ const mjTargets = input.TargetConfigs.filter(c => c.SchemaName === '__mj');
85
+ if (mjTargets.length > 0) {
86
+ const entityNames = mjTargets.map(c => c.EntityName);
87
+ output.MetadataFiles.push(this.MetaEmitter.EmitEntitySettingsFile(entityNames, input.MetadataDir));
88
+ output.MetadataFiles.push(this.MetaEmitter.EmitMjSyncConfig(input.MetadataDir));
89
+ }
90
+ return output;
91
+ }
92
+ ValidateAccessControl(input) {
93
+ const errors = [];
94
+ for (const config of input.TargetConfigs) {
95
+ if (config.SchemaName === '__mj') {
96
+ const settings = input.EntitySettingsForTargets[config.EntityName] ?? [];
97
+ const result = IsIntegrationWriteAllowed(config.EntityName, config.SchemaName, settings);
98
+ if (!result.Allowed) {
99
+ errors.push(result.Reason);
100
+ }
101
+ }
102
+ }
103
+ return errors;
104
+ }
105
+ CheckCollisions(input) {
106
+ const warnings = [];
107
+ const existingNames = new Set(input.ExistingTables.map(t => `${t.SchemaName}.${t.TableName}`.toLowerCase()));
108
+ for (const config of input.TargetConfigs) {
109
+ const key = `${config.SchemaName}.${config.TableName}`.toLowerCase();
110
+ if (existingNames.has(key)) {
111
+ // Not necessarily an error — could be an evolution scenario
112
+ warnings.push(`Table ${config.SchemaName}.${config.TableName} already exists — will generate ALTER TABLE if fields differ.`);
113
+ }
114
+ }
115
+ return warnings;
116
+ }
117
+ ClassifyConfigs(input) {
118
+ const existingMap = new Map();
119
+ for (const t of input.ExistingTables) {
120
+ existingMap.set(`${t.SchemaName}.${t.TableName}`.toLowerCase(), t);
121
+ }
122
+ const newConfigs = [];
123
+ const evolutionConfigs = [];
124
+ for (const config of input.TargetConfigs) {
125
+ const key = `${config.SchemaName}.${config.TableName}`.toLowerCase();
126
+ const existing = existingMap.get(key);
127
+ if (existing) {
128
+ evolutionConfigs.push({ config, existing });
129
+ }
130
+ else {
131
+ newConfigs.push(config);
132
+ }
133
+ }
134
+ return { newConfigs, evolutionConfigs };
135
+ }
136
+ GetUniqueSchemas(configs) {
137
+ const schemas = new Set();
138
+ for (const config of configs) {
139
+ schemas.add(config.SchemaName);
140
+ }
141
+ return Array.from(schemas);
142
+ }
143
+ CollectSoftFKs(input, evolutionConfigs) {
144
+ // From source relationships
145
+ const fromSource = this.SoftFKEmitter.GenerateConfigEntries(input.SourceSchema, input.TargetConfigs);
146
+ // From evolution (new FK columns)
147
+ for (const { config, existing } of evolutionConfigs) {
148
+ const sourceObj = input.SourceSchema.Objects.find(o => o.ExternalName === config.SourceObjectName);
149
+ if (!sourceObj)
150
+ continue;
151
+ const diff = this.Evolution.DiffSchema(sourceObj, config, existing, input.Platform);
152
+ const newFKs = this.Evolution.GenerateEvolutionSoftFKUpdates(diff, config);
153
+ fromSource.push(...newFKs);
154
+ }
155
+ return fromSource;
156
+ }
157
+ MakeMigrationMeta(input, action, objectName) {
158
+ return {
159
+ SourceType: input.SourceType,
160
+ ObjectName: objectName,
161
+ Action: action,
162
+ GeneratedBy: 'MJ Integration Schema Builder',
163
+ Timestamp: new Date().toISOString(),
164
+ };
165
+ }
166
+ }
167
+ //# sourceMappingURL=SchemaBuilder.js.map