@lssm/example.integration-hub 1.41.0 → 1.42.1

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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -0
  3. package/dist/connection/connection.enum.d.ts +10 -0
  4. package/dist/connection/connection.enum.d.ts.map +1 -0
  5. package/dist/connection/connection.enum.js +17 -1
  6. package/dist/connection/connection.enum.js.map +1 -0
  7. package/dist/connection/connection.operation.d.ts +107 -0
  8. package/dist/connection/connection.operation.d.ts.map +1 -0
  9. package/dist/connection/connection.operation.js +63 -0
  10. package/dist/connection/connection.operation.js.map +1 -0
  11. package/dist/connection/connection.presentation.d.ts +8 -0
  12. package/dist/connection/connection.presentation.d.ts.map +1 -0
  13. package/dist/connection/connection.presentation.js +60 -1
  14. package/dist/connection/connection.presentation.js.map +1 -0
  15. package/dist/connection/connection.schema.d.ts +68 -0
  16. package/dist/connection/connection.schema.d.ts.map +1 -0
  17. package/dist/connection/connection.schema.js +76 -1
  18. package/dist/connection/connection.schema.js.map +1 -0
  19. package/dist/connection/index.d.ts +4 -0
  20. package/dist/connection/index.js +5 -1
  21. package/dist/docs/index.d.ts +1 -0
  22. package/dist/docs/index.js +1 -1
  23. package/dist/docs/integration-hub.docblock.d.ts +1 -0
  24. package/dist/docs/integration-hub.docblock.js +61 -5
  25. package/dist/docs/integration-hub.docblock.js.map +1 -0
  26. package/dist/events.d.ts +153 -0
  27. package/dist/events.d.ts.map +1 -0
  28. package/dist/events.js +289 -1
  29. package/dist/events.js.map +1 -0
  30. package/dist/example.d.ts +40 -0
  31. package/dist/example.d.ts.map +1 -0
  32. package/dist/example.js +51 -1
  33. package/dist/example.js.map +1 -0
  34. package/dist/index.d.ts +14 -0
  35. package/dist/index.js +15 -1
  36. package/dist/integration/index.d.ts +4 -0
  37. package/dist/integration/index.js +5 -1
  38. package/dist/integration/integration.enum.d.ts +10 -0
  39. package/dist/integration/integration.enum.d.ts.map +1 -0
  40. package/dist/integration/integration.enum.js +17 -1
  41. package/dist/integration/integration.enum.js.map +1 -0
  42. package/dist/integration/integration.operations.d.ts +99 -0
  43. package/dist/integration/integration.operations.d.ts.map +1 -0
  44. package/dist/integration/integration.operations.js +57 -0
  45. package/dist/integration/integration.operations.js.map +1 -0
  46. package/dist/integration/integration.presentation.d.ts +9 -0
  47. package/dist/integration/integration.presentation.d.ts.map +1 -0
  48. package/dist/integration/integration.presentation.js +77 -1
  49. package/dist/integration/integration.presentation.js.map +1 -0
  50. package/dist/integration/integration.schema.d.ts +68 -0
  51. package/dist/integration/integration.schema.d.ts.map +1 -0
  52. package/dist/integration/integration.schema.js +76 -1
  53. package/dist/integration/integration.schema.js.map +1 -0
  54. package/dist/integration-hub.feature.d.ts +8 -0
  55. package/dist/integration-hub.feature.d.ts.map +1 -0
  56. package/dist/integration-hub.feature.js +239 -1
  57. package/dist/integration-hub.feature.js.map +1 -0
  58. package/dist/sync/index.d.ts +4 -0
  59. package/dist/sync/index.js +5 -1
  60. package/dist/sync/sync.enum.d.ts +18 -0
  61. package/dist/sync/sync.enum.d.ts.map +1 -0
  62. package/dist/sync/sync.enum.js +35 -1
  63. package/dist/sync/sync.enum.js.map +1 -0
  64. package/dist/sync/sync.operations.d.ts +509 -0
  65. package/dist/sync/sync.operations.d.ts.map +1 -0
  66. package/dist/sync/sync.operations.js +203 -0
  67. package/dist/sync/sync.operations.js.map +1 -0
  68. package/dist/sync/sync.presentation.d.ts +12 -0
  69. package/dist/sync/sync.presentation.d.ts.map +1 -0
  70. package/dist/sync/sync.presentation.js +168 -1
  71. package/dist/sync/sync.presentation.js.map +1 -0
  72. package/dist/sync/sync.schema.d.ts +356 -0
  73. package/dist/sync/sync.schema.d.ts.map +1 -0
  74. package/dist/sync/sync.schema.js +304 -1
  75. package/dist/sync/sync.schema.js.map +1 -0
  76. package/dist/sync-engine/index.d.ts +109 -0
  77. package/dist/sync-engine/index.d.ts.map +1 -0
  78. package/dist/sync-engine/index.js +148 -1
  79. package/dist/sync-engine/index.js.map +1 -0
  80. package/package.json +47 -39
  81. package/dist/connection/connection.contracts.js +0 -1
  82. package/dist/integration/integration.contracts.js +0 -1
  83. package/dist/sync/sync.contracts.js +0 -1
  84. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,109 @@
1
+ //#region src/sync-engine/index.d.ts
2
+ /**
3
+ * Sync Engine
4
+ *
5
+ * Core sync logic for the Integration Hub.
6
+ */
7
+ interface FieldMapping {
8
+ sourceField: string;
9
+ targetField: string;
10
+ mappingType: 'DIRECT' | 'TRANSFORM' | 'LOOKUP' | 'CONSTANT' | 'COMPUTED';
11
+ transformExpression?: string;
12
+ lookupConfig?: LookupConfig;
13
+ constantValue?: unknown;
14
+ isRequired: boolean;
15
+ defaultValue?: unknown;
16
+ }
17
+ interface LookupConfig {
18
+ sourceObject: string;
19
+ lookupField: string;
20
+ returnField: string;
21
+ }
22
+ interface SyncConfig {
23
+ id: string;
24
+ direction: 'INBOUND' | 'OUTBOUND' | 'BIDIRECTIONAL';
25
+ sourceObject: string;
26
+ targetObject: string;
27
+ fieldMappings: FieldMapping[];
28
+ createNew: boolean;
29
+ updateExisting: boolean;
30
+ deleteRemoved: boolean;
31
+ sourceFilter?: Record<string, unknown>;
32
+ }
33
+ interface SyncContext {
34
+ runId: string;
35
+ config: SyncConfig;
36
+ connection: {
37
+ id: string;
38
+ authType: string;
39
+ credentials?: Record<string, unknown>;
40
+ };
41
+ }
42
+ interface SyncResult {
43
+ success: boolean;
44
+ recordsProcessed: number;
45
+ recordsCreated: number;
46
+ recordsUpdated: number;
47
+ recordsDeleted: number;
48
+ recordsFailed: number;
49
+ recordsSkipped: number;
50
+ errors: SyncError[];
51
+ }
52
+ interface SyncError {
53
+ recordId?: string;
54
+ field?: string;
55
+ message: string;
56
+ code: string;
57
+ }
58
+ interface SourceRecord {
59
+ id: string;
60
+ data: Record<string, unknown>;
61
+ checksum?: string;
62
+ }
63
+ interface TargetRecord {
64
+ id: string;
65
+ data: Record<string, unknown>;
66
+ checksum?: string;
67
+ }
68
+ interface ISyncEngine {
69
+ /**
70
+ * Execute a sync operation.
71
+ */
72
+ sync(context: SyncContext): Promise<SyncResult>;
73
+ /**
74
+ * Transform a source record to target format.
75
+ */
76
+ transformRecord(sourceRecord: SourceRecord, mappings: FieldMapping[], context: SyncContext): TargetRecord;
77
+ /**
78
+ * Validate a transformed record.
79
+ */
80
+ validateRecord(record: TargetRecord, mappings: FieldMapping[]): {
81
+ valid: boolean;
82
+ errors: SyncError[];
83
+ };
84
+ }
85
+ interface IFieldTransformer {
86
+ transform(value: unknown, expression: string): unknown;
87
+ }
88
+ declare class BasicFieldTransformer implements IFieldTransformer {
89
+ transform(value: unknown, expression: string): unknown;
90
+ }
91
+ declare class BasicSyncEngine implements ISyncEngine {
92
+ private transformer;
93
+ constructor(transformer?: IFieldTransformer);
94
+ sync(_context: SyncContext): Promise<SyncResult>;
95
+ transformRecord(sourceRecord: SourceRecord, mappings: FieldMapping[], _context: SyncContext): TargetRecord;
96
+ validateRecord(record: TargetRecord, mappings: FieldMapping[]): {
97
+ valid: boolean;
98
+ errors: SyncError[];
99
+ };
100
+ private getNestedValue;
101
+ private setNestedValue;
102
+ private evaluateComputed;
103
+ }
104
+ declare function createSyncEngine(transformer?: IFieldTransformer): ISyncEngine;
105
+ declare function computeChecksum(data: Record<string, unknown>): string;
106
+ declare function hasChanges(sourceChecksum: string | undefined, targetChecksum: string | undefined): boolean;
107
+ //#endregion
108
+ export { BasicFieldTransformer, BasicSyncEngine, FieldMapping, IFieldTransformer, ISyncEngine, LookupConfig, SourceRecord, SyncConfig, SyncContext, SyncError, SyncResult, TargetRecord, computeChecksum, createSyncEngine, hasChanges };
109
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/sync-engine/index.ts"],"sourcesContent":[],"mappings":";;AAQA;AAWA;AAMA;AAYA;AAUiB,UAvCA,YAAA,CA+CP;EAGO,WAAA,EAAS,MAAA;EAOT,WAAA,EAAA,MAAY;EAMZ,WAAA,EAAA,QAAY,GAAA,WAEf,GAAA,QAAA,GAAA,UAAA,GAAA,UAAA;EAMG,mBAAW,CAAA,EAAA,MAAA;EAIZ,YAAA,CAAA,EAtEC,YAsED;EAAsB,aAAA,CAAA,EAAA,OAAA;EAAR,UAAA,EAAA,OAAA;EAMZ,YAAA,CAAA,EAAA,OAAA;;AAEL,UAxEI,YAAA,CAwEJ;EACR,YAAA,EAAA,MAAA;EAMO,WAAA,EAAA,MAAA;EACE,WAAA,EAAA,MAAA;;AAC0B,UA3EvB,UAAA,CA2EuB;EAKvB,EAAA,EAAA,MAAA;EAIJ,SAAA,EAAA,SAAA,GAAA,UAAsB,GAAW,eAAA;EAoDjC,YAAA,EAAA,MAAgB;EAGD,YAAA,EAAA,MAAA;EAIL,aAAA,EA1IN,YA0IM,EAAA;EAAsB,SAAA,EAAA,OAAA;EAAR,cAAA,EAAA,OAAA;EAuBnB,aAAA,EAAA,OAAA;EACJ,YAAA,CAAA,EA9JG,MA8JH,CAAA,MAAA,EAAA,OAAA,CAAA;;AAET,UA7JY,WAAA,CA6JZ;EAgEO,KAAA,EAAA,MAAA;EACE,MAAA,EA5NJ,UA4NI;EACiB,UAAA,EAAA;IAnGS,EAAA,EAAA,MAAA;IAAW,QAAA,EAAA,MAAA;IAoLnC,WAAA,CAAA,EA1SE,MA0Sc,CAAA,MAAe,EAAA,OAAA,CAAA;EAM/B,CAAA;AAahB;UAzTiB,UAAA;;;;;;;;UAQP;;UAGO,SAAA;;;;;;UAOA,YAAA;;QAET;;;UAIS,YAAA;;QAET;;;UAMS,WAAA;;;;gBAID,cAAc,QAAQ;;;;gCAMpB,wBACJ,yBACD,cACR;;;;yBAMO,wBACE;;YACiB;;;UAKd,iBAAA;;;cAIJ,qBAAA,YAAiC;;;cAoDjC,eAAA,YAA2B;;4BAGZ;iBAIL,cAAc,QAAQ;gCAuB3B,wBACJ,0BACA,cACT;yBAgEO,wBACE;;YACiB;;;;;;iBAiFf,gBAAA,eAA+B,oBAAoB;iBAMnD,eAAA,OAAsB;iBAatB,UAAA"}
@@ -1 +1,148 @@
1
- var e=class{transform(e,t){try{if(t.startsWith(`uppercase`))return typeof e==`string`?e.toUpperCase():e;if(t.startsWith(`lowercase`))return typeof e==`string`?e.toLowerCase():e;if(t.startsWith(`trim`))return typeof e==`string`?e.trim():e;if(t.startsWith(`default:`)){let n=t.replace(`default:`,``);return e??JSON.parse(n)}if(t.startsWith(`concat:`)){let n=t.replace(`concat:`,``)||` `;return Array.isArray(e)?e.join(n):e}if(t.startsWith(`split:`)){let n=t.replace(`split:`,``)||`,`;return typeof e==`string`?e.split(n):e}return t.startsWith(`number`)?Number(e):t.startsWith(`boolean`)?!!e:t.startsWith(`string`)?String(e):e}catch{return e}}},t=class{transformer;constructor(t){this.transformer=t??new e}async sync(e){return{success:!0,recordsProcessed:0,recordsCreated:0,recordsUpdated:0,recordsDeleted:0,recordsFailed:0,recordsSkipped:0,errors:[]}}transformRecord(e,t,n){let r={};for(let n of t){let t;switch(n.mappingType){case`DIRECT`:t=this.getNestedValue(e.data,n.sourceField);break;case`TRANSFORM`:let r=this.getNestedValue(e.data,n.sourceField);t=n.transformExpression?this.transformer.transform(r,n.transformExpression):r;break;case`CONSTANT`:t=n.constantValue;break;case`LOOKUP`:t=this.getNestedValue(e.data,n.sourceField);break;case`COMPUTED`:t=n.transformExpression?this.evaluateComputed(e.data,n.transformExpression):null;break;default:t=this.getNestedValue(e.data,n.sourceField)}t??=n.defaultValue,this.setNestedValue(r,n.targetField,t)}return{id:e.id,data:r}}validateRecord(e,t){let n=[];for(let r of t)r.isRequired&&(this.getNestedValue(e.data,r.targetField)??n.push({recordId:e.id,field:r.targetField,message:`Required field ${r.targetField} is missing`,code:`REQUIRED_FIELD_MISSING`}));return{valid:n.length===0,errors:n}}getNestedValue(e,t){let n=t.split(`.`),r=e;for(let e of n){if(r==null)return;r=r[e]}return r}setNestedValue(e,t,n){let r=t.split(`.`),i=e;for(let e=0;e<r.length-1;e++){let t=r[e];t!==void 0&&(t in i||(i[t]={}),i=i[t])}let a=r[r.length-1];a!==void 0&&(i[a]=n)}evaluateComputed(e,t){try{return t.replace(/\$\{([^}]+)\}/g,(t,n)=>{let r=this.getNestedValue(e,n);return String(r??``)})}catch{return null}}};function n(e){return new t(e)}function r(e){let t=JSON.stringify(e,Object.keys(e).sort()),n=0;for(let e=0;e<t.length;e++){let r=t.charCodeAt(e);n=(n<<5)-n+r,n&=n}return n.toString(16)}function i(e,t){return!e||!t?!0:e!==t}export{e as BasicFieldTransformer,t as BasicSyncEngine,r as computeChecksum,n as createSyncEngine,i as hasChanges};
1
+ //#region src/sync-engine/index.ts
2
+ var BasicFieldTransformer = class {
3
+ transform(value, expression) {
4
+ try {
5
+ if (expression.startsWith("uppercase")) return typeof value === "string" ? value.toUpperCase() : value;
6
+ if (expression.startsWith("lowercase")) return typeof value === "string" ? value.toLowerCase() : value;
7
+ if (expression.startsWith("trim")) return typeof value === "string" ? value.trim() : value;
8
+ if (expression.startsWith("default:")) {
9
+ const defaultVal = expression.replace("default:", "");
10
+ return value ?? JSON.parse(defaultVal);
11
+ }
12
+ if (expression.startsWith("concat:")) {
13
+ const separator = expression.replace("concat:", "") || " ";
14
+ if (Array.isArray(value)) return value.join(separator);
15
+ return value;
16
+ }
17
+ if (expression.startsWith("split:")) {
18
+ const separator = expression.replace("split:", "") || ",";
19
+ if (typeof value === "string") return value.split(separator);
20
+ return value;
21
+ }
22
+ if (expression.startsWith("number")) return Number(value);
23
+ if (expression.startsWith("boolean")) return Boolean(value);
24
+ if (expression.startsWith("string")) return String(value);
25
+ return value;
26
+ } catch {
27
+ return value;
28
+ }
29
+ }
30
+ };
31
+ var BasicSyncEngine = class {
32
+ transformer;
33
+ constructor(transformer) {
34
+ this.transformer = transformer ?? new BasicFieldTransformer();
35
+ }
36
+ async sync(_context) {
37
+ return {
38
+ success: true,
39
+ recordsProcessed: 0,
40
+ recordsCreated: 0,
41
+ recordsUpdated: 0,
42
+ recordsDeleted: 0,
43
+ recordsFailed: 0,
44
+ recordsSkipped: 0,
45
+ errors: []
46
+ };
47
+ }
48
+ transformRecord(sourceRecord, mappings, _context) {
49
+ const targetData = {};
50
+ for (const mapping of mappings) {
51
+ let value;
52
+ let sourceValue;
53
+ switch (mapping.mappingType) {
54
+ case "DIRECT":
55
+ value = this.getNestedValue(sourceRecord.data, mapping.sourceField);
56
+ break;
57
+ case "TRANSFORM":
58
+ sourceValue = this.getNestedValue(sourceRecord.data, mapping.sourceField);
59
+ value = mapping.transformExpression ? this.transformer.transform(sourceValue, mapping.transformExpression) : sourceValue;
60
+ break;
61
+ case "CONSTANT":
62
+ value = mapping.constantValue;
63
+ break;
64
+ case "LOOKUP":
65
+ value = this.getNestedValue(sourceRecord.data, mapping.sourceField);
66
+ break;
67
+ case "COMPUTED":
68
+ value = mapping.transformExpression ? this.evaluateComputed(sourceRecord.data, mapping.transformExpression) : null;
69
+ break;
70
+ default: value = this.getNestedValue(sourceRecord.data, mapping.sourceField);
71
+ }
72
+ if (value === void 0 || value === null) value = mapping.defaultValue;
73
+ this.setNestedValue(targetData, mapping.targetField, value);
74
+ }
75
+ return {
76
+ id: sourceRecord.id,
77
+ data: targetData
78
+ };
79
+ }
80
+ validateRecord(record, mappings) {
81
+ const errors = [];
82
+ for (const mapping of mappings) if (mapping.isRequired) {
83
+ const value = this.getNestedValue(record.data, mapping.targetField);
84
+ if (value === void 0 || value === null) errors.push({
85
+ recordId: record.id,
86
+ field: mapping.targetField,
87
+ message: `Required field ${mapping.targetField} is missing`,
88
+ code: "REQUIRED_FIELD_MISSING"
89
+ });
90
+ }
91
+ return {
92
+ valid: errors.length === 0,
93
+ errors
94
+ };
95
+ }
96
+ getNestedValue(obj, path) {
97
+ const parts = path.split(".");
98
+ let current = obj;
99
+ for (const part of parts) {
100
+ if (current === null || current === void 0) return;
101
+ current = current[part];
102
+ }
103
+ return current;
104
+ }
105
+ setNestedValue(obj, path, value) {
106
+ const parts = path.split(".");
107
+ let current = obj;
108
+ for (let i = 0; i < parts.length - 1; i++) {
109
+ const part = parts[i];
110
+ if (part === void 0) continue;
111
+ if (!(part in current)) current[part] = {};
112
+ current = current[part];
113
+ }
114
+ const lastPart = parts[parts.length - 1];
115
+ if (lastPart !== void 0) current[lastPart] = value;
116
+ }
117
+ evaluateComputed(data, expression) {
118
+ try {
119
+ return expression.replace(/\$\{([^}]+)\}/g, (_, path) => {
120
+ const value = this.getNestedValue(data, path);
121
+ return String(value ?? "");
122
+ });
123
+ } catch {
124
+ return null;
125
+ }
126
+ }
127
+ };
128
+ function createSyncEngine(transformer) {
129
+ return new BasicSyncEngine(transformer);
130
+ }
131
+ function computeChecksum(data) {
132
+ const str = JSON.stringify(data, Object.keys(data).sort());
133
+ let hash = 0;
134
+ for (let i = 0; i < str.length; i++) {
135
+ const char = str.charCodeAt(i);
136
+ hash = (hash << 5) - hash + char;
137
+ hash = hash & hash;
138
+ }
139
+ return hash.toString(16);
140
+ }
141
+ function hasChanges(sourceChecksum, targetChecksum) {
142
+ if (!sourceChecksum || !targetChecksum) return true;
143
+ return sourceChecksum !== targetChecksum;
144
+ }
145
+
146
+ //#endregion
147
+ export { BasicFieldTransformer, BasicSyncEngine, computeChecksum, createSyncEngine, hasChanges };
148
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["targetData: Record<string, unknown>","value: unknown","sourceValue: unknown","errors: SyncError[]","current: unknown"],"sources":["../../src/sync-engine/index.ts"],"sourcesContent":["/**\n * Sync Engine\n *\n * Core sync logic for the Integration Hub.\n */\n\n// ============ Types ============\n\nexport interface FieldMapping {\n sourceField: string;\n targetField: string;\n mappingType: 'DIRECT' | 'TRANSFORM' | 'LOOKUP' | 'CONSTANT' | 'COMPUTED';\n transformExpression?: string;\n lookupConfig?: LookupConfig;\n constantValue?: unknown;\n isRequired: boolean;\n defaultValue?: unknown;\n}\n\nexport interface LookupConfig {\n sourceObject: string;\n lookupField: string;\n returnField: string;\n}\n\nexport interface SyncConfig {\n id: string;\n direction: 'INBOUND' | 'OUTBOUND' | 'BIDIRECTIONAL';\n sourceObject: string;\n targetObject: string;\n fieldMappings: FieldMapping[];\n createNew: boolean;\n updateExisting: boolean;\n deleteRemoved: boolean;\n sourceFilter?: Record<string, unknown>;\n}\n\nexport interface SyncContext {\n runId: string;\n config: SyncConfig;\n connection: {\n id: string;\n authType: string;\n credentials?: Record<string, unknown>;\n };\n}\n\nexport interface SyncResult {\n success: boolean;\n recordsProcessed: number;\n recordsCreated: number;\n recordsUpdated: number;\n recordsDeleted: number;\n recordsFailed: number;\n recordsSkipped: number;\n errors: SyncError[];\n}\n\nexport interface SyncError {\n recordId?: string;\n field?: string;\n message: string;\n code: string;\n}\n\nexport interface SourceRecord {\n id: string;\n data: Record<string, unknown>;\n checksum?: string;\n}\n\nexport interface TargetRecord {\n id: string;\n data: Record<string, unknown>;\n checksum?: string;\n}\n\n// ============ Sync Engine Interface ============\n\nexport interface ISyncEngine {\n /**\n * Execute a sync operation.\n */\n sync(context: SyncContext): Promise<SyncResult>;\n\n /**\n * Transform a source record to target format.\n */\n transformRecord(\n sourceRecord: SourceRecord,\n mappings: FieldMapping[],\n context: SyncContext\n ): TargetRecord;\n\n /**\n * Validate a transformed record.\n */\n validateRecord(\n record: TargetRecord,\n mappings: FieldMapping[]\n ): { valid: boolean; errors: SyncError[] };\n}\n\n// ============ Field Transformer ============\n\nexport interface IFieldTransformer {\n transform(value: unknown, expression: string): unknown;\n}\n\nexport class BasicFieldTransformer implements IFieldTransformer {\n transform(value: unknown, expression: string): unknown {\n // Simple expression evaluation\n // In production, use a proper expression language\n try {\n if (expression.startsWith('uppercase')) {\n return typeof value === 'string' ? value.toUpperCase() : value;\n }\n if (expression.startsWith('lowercase')) {\n return typeof value === 'string' ? value.toLowerCase() : value;\n }\n if (expression.startsWith('trim')) {\n return typeof value === 'string' ? value.trim() : value;\n }\n if (expression.startsWith('default:')) {\n const defaultVal = expression.replace('default:', '');\n return value ?? JSON.parse(defaultVal);\n }\n if (expression.startsWith('concat:')) {\n const separator = expression.replace('concat:', '') || ' ';\n if (Array.isArray(value)) {\n return value.join(separator);\n }\n return value;\n }\n if (expression.startsWith('split:')) {\n const separator = expression.replace('split:', '') || ',';\n if (typeof value === 'string') {\n return value.split(separator);\n }\n return value;\n }\n if (expression.startsWith('number')) {\n return Number(value);\n }\n if (expression.startsWith('boolean')) {\n return Boolean(value);\n }\n if (expression.startsWith('string')) {\n return String(value);\n }\n\n // Return as-is if no transformation matches\n return value;\n } catch {\n return value;\n }\n }\n}\n\n// ============ Basic Sync Engine ============\n\nexport class BasicSyncEngine implements ISyncEngine {\n private transformer: IFieldTransformer;\n\n constructor(transformer?: IFieldTransformer) {\n this.transformer = transformer ?? new BasicFieldTransformer();\n }\n\n async sync(_context: SyncContext): Promise<SyncResult> {\n const result: SyncResult = {\n success: true,\n recordsProcessed: 0,\n recordsCreated: 0,\n recordsUpdated: 0,\n recordsDeleted: 0,\n recordsFailed: 0,\n recordsSkipped: 0,\n errors: [],\n };\n\n // In a real implementation, this would:\n // 1. Fetch records from source\n // 2. Transform each record\n // 3. Validate each record\n // 4. Upsert to target\n // 5. Track sync records for deduplication\n\n return result;\n }\n\n transformRecord(\n sourceRecord: SourceRecord,\n mappings: FieldMapping[],\n _context: SyncContext\n ): TargetRecord {\n const targetData: Record<string, unknown> = {};\n\n for (const mapping of mappings) {\n let value: unknown;\n let sourceValue: unknown;\n\n switch (mapping.mappingType) {\n case 'DIRECT':\n value = this.getNestedValue(sourceRecord.data, mapping.sourceField);\n break;\n\n case 'TRANSFORM':\n sourceValue = this.getNestedValue(\n sourceRecord.data,\n mapping.sourceField\n );\n value = mapping.transformExpression\n ? this.transformer.transform(\n sourceValue,\n mapping.transformExpression\n )\n : sourceValue;\n break;\n\n case 'CONSTANT':\n value = mapping.constantValue;\n break;\n\n case 'LOOKUP':\n // In production, this would fetch from a lookup table\n value = this.getNestedValue(sourceRecord.data, mapping.sourceField);\n break;\n\n case 'COMPUTED':\n // In production, this would evaluate a computed expression\n value = mapping.transformExpression\n ? this.evaluateComputed(\n sourceRecord.data,\n mapping.transformExpression\n )\n : null;\n break;\n\n default:\n value = this.getNestedValue(sourceRecord.data, mapping.sourceField);\n }\n\n // Apply default value if needed\n if (value === undefined || value === null) {\n value = mapping.defaultValue;\n }\n\n // Set the target field\n this.setNestedValue(targetData, mapping.targetField, value);\n }\n\n return {\n id: sourceRecord.id,\n data: targetData,\n };\n }\n\n validateRecord(\n record: TargetRecord,\n mappings: FieldMapping[]\n ): { valid: boolean; errors: SyncError[] } {\n const errors: SyncError[] = [];\n\n for (const mapping of mappings) {\n if (mapping.isRequired) {\n const value = this.getNestedValue(record.data, mapping.targetField);\n if (value === undefined || value === null) {\n errors.push({\n recordId: record.id,\n field: mapping.targetField,\n message: `Required field ${mapping.targetField} is missing`,\n code: 'REQUIRED_FIELD_MISSING',\n });\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n }\n\n private getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n }\n\n private setNestedValue(\n obj: Record<string, unknown>,\n path: string,\n value: unknown\n ): void {\n const parts = path.split('.');\n let current = obj;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (part === undefined) continue;\n if (!(part in current)) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n\n const lastPart = parts[parts.length - 1];\n if (lastPart !== undefined) {\n current[lastPart] = value;\n }\n }\n\n private evaluateComputed(\n data: Record<string, unknown>,\n expression: string\n ): unknown {\n // Simple computed field evaluation\n // In production, use a proper expression evaluator\n try {\n // Support simple field references like ${field.path}\n const result = expression.replace(/\\$\\{([^}]+)\\}/g, (_, path) => {\n const value = this.getNestedValue(data, path);\n return String(value ?? '');\n });\n return result;\n } catch {\n return null;\n }\n }\n}\n\n// ============ Factory ============\n\nexport function createSyncEngine(transformer?: IFieldTransformer): ISyncEngine {\n return new BasicSyncEngine(transformer);\n}\n\n// ============ Checksum Utilities ============\n\nexport function computeChecksum(data: Record<string, unknown>): string {\n // Simple checksum based on JSON serialization\n // In production, use a proper hash function\n const str = JSON.stringify(data, Object.keys(data).sort());\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return hash.toString(16);\n}\n\nexport function hasChanges(\n sourceChecksum: string | undefined,\n targetChecksum: string | undefined\n): boolean {\n if (!sourceChecksum || !targetChecksum) {\n return true;\n }\n return sourceChecksum !== targetChecksum;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@lssm/example.integration-hub",
3
- "version": "1.41.0",
3
+ "version": "1.42.1",
4
4
  "description": "Integration Hub example with sync engine and field mappings for ContractSpec",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "type": "module",
8
8
  "scripts": {
9
9
  "publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
10
- "build": "bun build:bundle && bun build:types",
10
+ "publish:pkg:canary": "bun publish:pkg --tag canary",
11
+ "build": "bun build:types && bun build:bundle",
11
12
  "build:bundle": "tsdown",
12
13
  "build:types": "tsc --noEmit",
13
14
  "dev": "bun build:bundle --watch",
@@ -17,45 +18,45 @@
17
18
  "lint:check": "eslint src"
18
19
  },
19
20
  "dependencies": {
20
- "@lssm/lib.schema": "workspace:*",
21
- "@lssm/lib.contracts": "workspace:*",
22
- "@lssm/lib.bus": "workspace:*",
23
- "@lssm/lib.identity-rbac": "workspace:*",
24
- "@lssm/lib.files": "workspace:*",
25
- "@lssm/lib.feature-flags": "workspace:*",
26
- "@lssm/lib.jobs": "workspace:*",
27
- "@lssm/module.audit-trail": "workspace:*",
28
- "@lssm/module.notifications": "workspace:*",
21
+ "@lssm/lib.schema": "1.42.1",
22
+ "@lssm/lib.contracts": "1.42.1",
23
+ "@lssm/lib.bus": "1.42.1",
24
+ "@lssm/lib.identity-rbac": "1.42.1",
25
+ "@lssm/lib.files": "1.42.1",
26
+ "@lssm/lib.feature-flags": "1.42.1",
27
+ "@lssm/lib.jobs": "1.42.1",
28
+ "@lssm/module.audit-trail": "1.42.1",
29
+ "@lssm/module.notifications": "1.42.1",
29
30
  "zod": "^4.1.13"
30
31
  },
31
32
  "devDependencies": {
32
- "@lssm/tool.typescript": "workspace:*",
33
- "@lssm/tool.tsdown": "workspace:*",
33
+ "@lssm/tool.typescript": "1.42.1",
34
+ "@lssm/tool.tsdown": "1.42.1",
34
35
  "typescript": "^5.9.3"
35
36
  },
36
37
  "exports": {
37
- ".": "./src/index.ts",
38
- "./connection": "./src/connection/index.ts",
39
- "./connection/connection.contracts": "./src/connection/connection.contracts.ts",
40
- "./connection/connection.enum": "./src/connection/connection.enum.ts",
41
- "./connection/connection.presentation": "./src/connection/connection.presentation.ts",
42
- "./connection/connection.schema": "./src/connection/connection.schema.ts",
43
- "./docs": "./src/docs/index.ts",
44
- "./docs/integration-hub.docblock": "./src/docs/integration-hub.docblock.ts",
45
- "./events": "./src/events.ts",
46
- "./example": "./src/example.ts",
47
- "./integration": "./src/integration/index.ts",
48
- "./integration-hub.feature": "./src/integration-hub.feature.ts",
49
- "./integration/integration.contracts": "./src/integration/integration.contracts.ts",
50
- "./integration/integration.enum": "./src/integration/integration.enum.ts",
51
- "./integration/integration.presentation": "./src/integration/integration.presentation.ts",
52
- "./integration/integration.schema": "./src/integration/integration.schema.ts",
53
- "./sync": "./src/sync/index.ts",
54
- "./sync-engine": "./src/sync-engine/index.ts",
55
- "./sync/sync.contracts": "./src/sync/sync.contracts.ts",
56
- "./sync/sync.enum": "./src/sync/sync.enum.ts",
57
- "./sync/sync.presentation": "./src/sync/sync.presentation.ts",
58
- "./sync/sync.schema": "./src/sync/sync.schema.ts",
38
+ ".": "./dist/index.js",
39
+ "./connection": "./dist/connection/index.js",
40
+ "./connection/connection.enum": "./dist/connection/connection.enum.js",
41
+ "./connection/connection.operation": "./dist/connection/connection.operation.js",
42
+ "./connection/connection.presentation": "./dist/connection/connection.presentation.js",
43
+ "./connection/connection.schema": "./dist/connection/connection.schema.js",
44
+ "./docs": "./dist/docs/index.js",
45
+ "./docs/integration-hub.docblock": "./dist/docs/integration-hub.docblock.js",
46
+ "./events": "./dist/events.js",
47
+ "./example": "./dist/example.js",
48
+ "./integration": "./dist/integration/index.js",
49
+ "./integration-hub.feature": "./dist/integration-hub.feature.js",
50
+ "./integration/integration.enum": "./dist/integration/integration.enum.js",
51
+ "./integration/integration.operations": "./dist/integration/integration.operations.js",
52
+ "./integration/integration.presentation": "./dist/integration/integration.presentation.js",
53
+ "./integration/integration.schema": "./dist/integration/integration.schema.js",
54
+ "./sync": "./dist/sync/index.js",
55
+ "./sync-engine": "./dist/sync-engine/index.js",
56
+ "./sync/sync.enum": "./dist/sync/sync.enum.js",
57
+ "./sync/sync.operations": "./dist/sync/sync.operations.js",
58
+ "./sync/sync.presentation": "./dist/sync/sync.presentation.js",
59
+ "./sync/sync.schema": "./dist/sync/sync.schema.js",
59
60
  "./*": "./*"
60
61
  },
61
62
  "module": "./dist/index.js",
@@ -68,7 +69,7 @@
68
69
  "exports": {
69
70
  ".": "./dist/index.js",
70
71
  "./connection": "./dist/connection/index.js",
71
- "./connection/connection.contracts": "./dist/connection/connection.contracts.js",
72
+ "./connection/connection.contracts": "./dist/connection/connection.operations.js",
72
73
  "./connection/connection.enum": "./dist/connection/connection.enum.js",
73
74
  "./connection/connection.presentation": "./dist/connection/connection.presentation.js",
74
75
  "./connection/connection.schema": "./dist/connection/connection.schema.js",
@@ -78,17 +79,24 @@
78
79
  "./example": "./dist/example.js",
79
80
  "./integration": "./dist/integration/index.js",
80
81
  "./integration-hub.feature": "./dist/integration-hub.feature.js",
81
- "./integration/integration.contracts": "./dist/integration/integration.contracts.js",
82
+ "./integration/integration.contracts": "./dist/integration/integration.operations.js",
82
83
  "./integration/integration.enum": "./dist/integration/integration.enum.js",
83
84
  "./integration/integration.presentation": "./dist/integration/integration.presentation.js",
84
85
  "./integration/integration.schema": "./dist/integration/integration.schema.js",
85
86
  "./sync": "./dist/sync/index.js",
86
87
  "./sync-engine": "./dist/sync-engine/index.js",
87
- "./sync/sync.contracts": "./dist/sync/sync.contracts.js",
88
+ "./sync/sync.contracts": "./dist/sync/sync.operations.js",
88
89
  "./sync/sync.enum": "./dist/sync/sync.enum.js",
89
90
  "./sync/sync.presentation": "./dist/sync/sync.presentation.js",
90
91
  "./sync/sync.schema": "./dist/sync/sync.schema.js",
91
92
  "./*": "./*"
92
- }
93
+ },
94
+ "registry": "https://registry.npmjs.org/"
95
+ },
96
+ "license": "MIT",
97
+ "repository": {
98
+ "type": "git",
99
+ "url": "https://github.com/lssm-tech/contractspec.git",
100
+ "directory": "packages/examples/integration-hub"
93
101
  }
94
102
  }
@@ -1 +0,0 @@
1
- import{ConnectionModel as e,CreateConnectionInputModel as t}from"./connection.schema.js";import{defineCommand as n}from"@lssm/lib.contracts/spec";const r=n({meta:{name:`integration.connection.create`,version:1,stability:`stable`,owners:[...[`@example.integration-hub`]],tags:[`integration`,`connection`,`create`],description:`Create a connection to an external system.`,goal:`Authenticate with external systems.`,context:`Connection setup.`},io:{input:t,output:e},policy:{auth:`user`},sideEffects:{emits:[{name:`integration.connection.created`,version:1,when:`Connection created`,payload:e}],audit:[`integration.connection.created`]}});export{r as CreateConnectionContract};
@@ -1 +0,0 @@
1
- import{CreateIntegrationInputModel as e,IntegrationModel as t}from"./integration.schema.js";import{defineCommand as n}from"@lssm/lib.contracts/spec";const r=n({meta:{name:`integration.create`,version:1,stability:`stable`,owners:[...[`@example.integration-hub`]],tags:[`integration`,`create`],description:`Create a new integration.`,goal:`Allow users to set up integrations with external systems.`,context:`Integration setup.`},io:{input:e,output:t},policy:{auth:`user`},sideEffects:{emits:[{name:`integration.created`,version:1,when:`Integration created`,payload:t}],audit:[`integration.created`]}});export{r as CreateIntegrationContract};
@@ -1 +0,0 @@
1
- import{AddFieldMappingInputModel as e,CreateSyncConfigInputModel as t,FieldMappingModel as n,ListSyncRunsInputModel as r,ListSyncRunsOutputModel as i,SyncConfigModel as a,SyncRunModel as o,TriggerSyncInputModel as s}from"./sync.schema.js";import{defineCommand as c,defineQuery as l}from"@lssm/lib.contracts/spec";const u=[`@example.integration-hub`],d=c({meta:{name:`integration.syncConfig.create`,version:1,stability:`stable`,owners:[...u],tags:[`integration`,`sync`,`config`,`create`],description:`Create a sync configuration.`,goal:`Define how data should be synchronized.`,context:`Sync setup.`},io:{input:t,output:a},policy:{auth:`user`},sideEffects:{emits:[{name:`integration.syncConfig.created`,version:1,when:`Sync config created`,payload:a}],audit:[`integration.syncConfig.created`]}}),f=c({meta:{name:`integration.fieldMapping.add`,version:1,stability:`stable`,owners:[...u],tags:[`integration`,`mapping`,`field`],description:`Add a field mapping to a sync config.`,goal:`Map fields between systems.`,context:`Mapping configuration.`},io:{input:e,output:n},policy:{auth:`user`},sideEffects:{emits:[{name:`integration.fieldMapping.added`,version:1,when:`Mapping added`,payload:n}]}}),p=c({meta:{name:`integration.sync.trigger`,version:1,stability:`stable`,owners:[...u],tags:[`integration`,`sync`,`trigger`],description:`Trigger a manual sync.`,goal:`Start data synchronization.`,context:`Manual sync or webhook trigger.`},io:{input:s,output:o},policy:{auth:`user`},sideEffects:{emits:[{name:`integration.sync.started`,version:1,when:`Sync starts`,payload:o}],audit:[`integration.sync.triggered`]}}),m=l({meta:{name:`integration.syncRun.list`,version:1,stability:`stable`,owners:[...u],tags:[`integration`,`sync`,`run`,`list`],description:`List sync run history.`,goal:`View sync history and status.`,context:`Sync monitoring.`},io:{input:r,output:i},policy:{auth:`user`}});export{f as AddFieldMappingContract,d as CreateSyncConfigContract,m as ListSyncRunsContract,p as TriggerSyncContract};