@memberjunction/ng-react 5.43.0 → 5.45.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ *
4
+ * Focused tests for the ML tools surface (`ComponentUtilities.ml`) built by RuntimeUtilities.
5
+ * The provider statics and the Predictive Studio Remote Operation are mocked so we can exercise
6
+ * the listModels RunView mapping and the score Remote-Op marshalling without a live backend.
7
+ */
8
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
9
+ // Hoisted so the `vi.mock` factories (which are themselves hoisted above the imports) can
10
+ // safely reference these without a "before initialization" error.
11
+ const { MockGraphQLDataProvider, mockProviderInstance, mockRunView, mockScoreExecute } = vi.hoisted(() => {
12
+ class MockGraphQLDataProvider {
13
+ }
14
+ return {
15
+ // Mock the GraphQL provider so `BaseEntity.Provider instanceof GraphQLDataProvider` is true.
16
+ MockGraphQLDataProvider,
17
+ mockProviderInstance: new MockGraphQLDataProvider(),
18
+ // Controllable RunView mock.
19
+ mockRunView: vi.fn(),
20
+ // Controllable Remote Operation mock.
21
+ mockScoreExecute: vi.fn()
22
+ };
23
+ });
24
+ vi.mock('@memberjunction/core', async (importOriginal) => {
25
+ const actual = await importOriginal();
26
+ return {
27
+ ...actual,
28
+ LogError: vi.fn(),
29
+ BaseEntity: { Provider: mockProviderInstance },
30
+ Metadata: class {
31
+ constructor() {
32
+ this.Entities = [];
33
+ this.GetEntityObject = vi.fn();
34
+ }
35
+ },
36
+ RunView: class {
37
+ constructor() {
38
+ this.RunView = mockRunView;
39
+ this.RunViews = vi.fn();
40
+ }
41
+ },
42
+ RunQuery: class {
43
+ constructor() {
44
+ this.RunQuery = vi.fn();
45
+ }
46
+ }
47
+ };
48
+ });
49
+ vi.mock('@memberjunction/graphql-dataprovider', () => ({
50
+ GraphQLDataProvider: MockGraphQLDataProvider
51
+ }));
52
+ vi.mock('@memberjunction/core-entities', () => ({
53
+ GeoDataEngine: { Instance: undefined },
54
+ MJMLModelEntity: class {
55
+ },
56
+ PredictiveStudioScoreRecordSetOperation: class {
57
+ constructor() {
58
+ this.Execute = mockScoreExecute;
59
+ }
60
+ }
61
+ }));
62
+ vi.mock('@memberjunction/ai-vectors-memory', () => ({
63
+ SimpleVectorService: class {
64
+ }
65
+ }));
66
+ vi.mock('@memberjunction/global', async (importOriginal) => {
67
+ const actual = await importOriginal();
68
+ return { ...actual };
69
+ });
70
+ import { RuntimeUtilities } from '../lib/utilities/runtime-utilities';
71
+ describe('RuntimeUtilities — SimpleMLTools', () => {
72
+ beforeEach(() => {
73
+ vi.spyOn(console, 'log').mockImplementation(() => { });
74
+ vi.spyOn(console, 'error').mockImplementation(() => { });
75
+ mockRunView.mockReset();
76
+ mockScoreExecute.mockReset();
77
+ });
78
+ it('exposes an `ml` capability when a GraphQL provider is present', () => {
79
+ const u = new RuntimeUtilities().buildUtilities();
80
+ expect(u.ml).toBeDefined();
81
+ expect(typeof u.ml.listModels).toBe('function');
82
+ expect(typeof u.ml.score).toBe('function');
83
+ });
84
+ it('listModels maps MJ: ML Models rows and parses JSON metrics', async () => {
85
+ mockRunView.mockResolvedValue({
86
+ Success: true,
87
+ Results: [
88
+ {
89
+ ID: 'M1',
90
+ Pipeline: 'Renewal Pipeline',
91
+ Version: 3,
92
+ TargetVariable: 'Renewed',
93
+ ProblemType: 'classification',
94
+ Status: 'Published',
95
+ Metrics: '{"auc":0.91}',
96
+ HoldoutMetrics: 'not-json'
97
+ }
98
+ ]
99
+ });
100
+ const u = new RuntimeUtilities().buildUtilities();
101
+ const models = await u.ml.listModels();
102
+ expect(mockRunView).toHaveBeenCalledTimes(1);
103
+ const params = mockRunView.mock.calls[0][0];
104
+ expect(params.EntityName).toBe('MJ: ML Models');
105
+ expect(params.ExtraFilter).toBe("Status='Published'");
106
+ expect(params.OrderBy).toBe('Version DESC');
107
+ expect(models).toHaveLength(1);
108
+ expect(models[0]).toMatchObject({
109
+ id: 'M1',
110
+ pipeline: 'Renewal Pipeline',
111
+ version: 3,
112
+ targetVariable: 'Renewed',
113
+ problemType: 'classification',
114
+ status: 'Published',
115
+ metrics: { auc: 0.91 }
116
+ });
117
+ // Invalid JSON is defensively dropped to undefined.
118
+ expect(models[0].holdoutMetrics).toBeUndefined();
119
+ });
120
+ it('listModels applies status/targetVariable/maxResults filter and returns [] on failure', async () => {
121
+ mockRunView.mockResolvedValue({ Success: false, ErrorMessage: 'boom', Results: [] });
122
+ const u = new RuntimeUtilities().buildUtilities();
123
+ const models = await u.ml.listModels({ status: 'Validated', targetVariable: "O'Brien", maxResults: 5 });
124
+ const params = mockRunView.mock.calls[0][0];
125
+ expect(params.ExtraFilter).toBe("Status='Validated' AND TargetVariable='O''Brien'");
126
+ expect(params.MaxRows).toBe(5);
127
+ expect(models).toEqual([]);
128
+ });
129
+ it('score normalizes record keys, requests ephemeral predictions, and maps the result', async () => {
130
+ mockScoreExecute.mockResolvedValue({
131
+ Success: true,
132
+ Output: {
133
+ scored: 2,
134
+ failed: 0,
135
+ skipped: 1,
136
+ wroteBack: false,
137
+ predictions: [
138
+ { recordId: 'R1', score: 0.8, class: 'Yes' },
139
+ { recordId: 'R2', score: 0.2, class: 'No' }
140
+ ]
141
+ }
142
+ });
143
+ const u = new RuntimeUtilities().buildUtilities();
144
+ const result = await u.ml.score('M1', ['R1', { ID: 'R2' }, { Other: 'R3' }], { primaryKeyField: 'ID' });
145
+ expect(mockScoreExecute).toHaveBeenCalledTimes(1);
146
+ const [input, ctx] = mockScoreExecute.mock.calls[0];
147
+ expect(input.modelId).toBe('M1');
148
+ expect(input.scope).toEqual({ records: ['R1', 'R2'] }); // Other-keyed object dropped (no ID)
149
+ expect(input.writeBack).toBeUndefined(); // ephemeral
150
+ expect(ctx.provider).toBe(mockProviderInstance);
151
+ expect(result).toEqual({
152
+ scoredCount: 2,
153
+ failedCount: 0,
154
+ skippedCount: 1,
155
+ predictions: [
156
+ { recordId: 'R1', score: 0.8, class: 'Yes' },
157
+ { recordId: 'R2', score: 0.2, class: 'No' }
158
+ ]
159
+ });
160
+ });
161
+ it('score returns a zeroed result with records counted as failed on error', async () => {
162
+ mockScoreExecute.mockResolvedValue({ Success: false, ErrorMessage: 'no model' });
163
+ const u = new RuntimeUtilities().buildUtilities();
164
+ const result = await u.ml.score('M1', ['R1', 'R2']);
165
+ expect(result).toEqual({ scoredCount: 0, failedCount: 2, skippedCount: 0, predictions: [] });
166
+ });
167
+ });
168
+ //# sourceMappingURL=runtime-utilities-ml.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-utilities-ml.test.js","sourceRoot":"","sources":["../../src/__tests__/runtime-utilities-ml.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @vitest-environment jsdom\n *\n * Focused tests for the ML tools surface (`ComponentUtilities.ml`) built by RuntimeUtilities.\n * The provider statics and the Predictive Studio Remote Operation are mocked so we can exercise\n * the listModels RunView mapping and the score Remote-Op marshalling without a live backend.\n */\nimport { describe, it, expect, vi, beforeEach } from 'vitest';\n\n// Hoisted so the `vi.mock` factories (which are themselves hoisted above the imports) can\n// safely reference these without a \"before initialization\" error.\nconst { MockGraphQLDataProvider, mockProviderInstance, mockRunView, mockScoreExecute } = vi.hoisted(() => {\n class MockGraphQLDataProvider {}\n return {\n // Mock the GraphQL provider so `BaseEntity.Provider instanceof GraphQLDataProvider` is true.\n MockGraphQLDataProvider,\n mockProviderInstance: new MockGraphQLDataProvider(),\n // Controllable RunView mock.\n mockRunView: vi.fn(),\n // Controllable Remote Operation mock.\n mockScoreExecute: vi.fn()\n };\n});\n\nvi.mock('@memberjunction/core', async (importOriginal) => {\n const actual = await importOriginal<typeof import('@memberjunction/core')>();\n return {\n ...actual,\n LogError: vi.fn(),\n BaseEntity: { Provider: mockProviderInstance },\n Metadata: class {\n Entities = [];\n GetEntityObject = vi.fn();\n },\n RunView: class {\n RunView = mockRunView;\n RunViews = vi.fn();\n },\n RunQuery: class {\n RunQuery = vi.fn();\n }\n };\n});\n\nvi.mock('@memberjunction/graphql-dataprovider', () => ({\n GraphQLDataProvider: MockGraphQLDataProvider\n}));\n\nvi.mock('@memberjunction/core-entities', () => ({\n GeoDataEngine: { Instance: undefined },\n MJMLModelEntity: class {},\n PredictiveStudioScoreRecordSetOperation: class {\n Execute = mockScoreExecute;\n }\n}));\n\nvi.mock('@memberjunction/ai-vectors-memory', () => ({\n SimpleVectorService: class {}\n}));\n\nvi.mock('@memberjunction/global', async (importOriginal) => {\n const actual = await importOriginal<typeof import('@memberjunction/global')>();\n return { ...actual };\n});\n\nimport { RuntimeUtilities } from '../lib/utilities/runtime-utilities';\n\ndescribe('RuntimeUtilities — SimpleMLTools', () => {\n beforeEach(() => {\n vi.spyOn(console, 'log').mockImplementation(() => {});\n vi.spyOn(console, 'error').mockImplementation(() => {});\n mockRunView.mockReset();\n mockScoreExecute.mockReset();\n });\n\n it('exposes an `ml` capability when a GraphQL provider is present', () => {\n const u = new RuntimeUtilities().buildUtilities();\n expect(u.ml).toBeDefined();\n expect(typeof u.ml!.listModels).toBe('function');\n expect(typeof u.ml!.score).toBe('function');\n });\n\n it('listModels maps MJ: ML Models rows and parses JSON metrics', async () => {\n mockRunView.mockResolvedValue({\n Success: true,\n Results: [\n {\n ID: 'M1',\n Pipeline: 'Renewal Pipeline',\n Version: 3,\n TargetVariable: 'Renewed',\n ProblemType: 'classification',\n Status: 'Published',\n Metrics: '{\"auc\":0.91}',\n HoldoutMetrics: 'not-json'\n }\n ]\n });\n\n const u = new RuntimeUtilities().buildUtilities();\n const models = await u.ml!.listModels();\n\n expect(mockRunView).toHaveBeenCalledTimes(1);\n const params = mockRunView.mock.calls[0][0];\n expect(params.EntityName).toBe('MJ: ML Models');\n expect(params.ExtraFilter).toBe(\"Status='Published'\");\n expect(params.OrderBy).toBe('Version DESC');\n\n expect(models).toHaveLength(1);\n expect(models[0]).toMatchObject({\n id: 'M1',\n pipeline: 'Renewal Pipeline',\n version: 3,\n targetVariable: 'Renewed',\n problemType: 'classification',\n status: 'Published',\n metrics: { auc: 0.91 }\n });\n // Invalid JSON is defensively dropped to undefined.\n expect(models[0].holdoutMetrics).toBeUndefined();\n });\n\n it('listModels applies status/targetVariable/maxResults filter and returns [] on failure', async () => {\n mockRunView.mockResolvedValue({ Success: false, ErrorMessage: 'boom', Results: [] });\n\n const u = new RuntimeUtilities().buildUtilities();\n const models = await u.ml!.listModels({ status: 'Validated', targetVariable: \"O'Brien\", maxResults: 5 });\n\n const params = mockRunView.mock.calls[0][0];\n expect(params.ExtraFilter).toBe(\"Status='Validated' AND TargetVariable='O''Brien'\");\n expect(params.MaxRows).toBe(5);\n expect(models).toEqual([]);\n });\n\n it('score normalizes record keys, requests ephemeral predictions, and maps the result', async () => {\n mockScoreExecute.mockResolvedValue({\n Success: true,\n Output: {\n scored: 2,\n failed: 0,\n skipped: 1,\n wroteBack: false,\n predictions: [\n { recordId: 'R1', score: 0.8, class: 'Yes' },\n { recordId: 'R2', score: 0.2, class: 'No' }\n ]\n }\n });\n\n const u = new RuntimeUtilities().buildUtilities();\n const result = await u.ml!.score('M1', ['R1', { ID: 'R2' }, { Other: 'R3' }], { primaryKeyField: 'ID' });\n\n expect(mockScoreExecute).toHaveBeenCalledTimes(1);\n const [input, ctx] = mockScoreExecute.mock.calls[0];\n expect(input.modelId).toBe('M1');\n expect(input.scope).toEqual({ records: ['R1', 'R2'] }); // Other-keyed object dropped (no ID)\n expect(input.writeBack).toBeUndefined(); // ephemeral\n expect(ctx.provider).toBe(mockProviderInstance);\n\n expect(result).toEqual({\n scoredCount: 2,\n failedCount: 0,\n skippedCount: 1,\n predictions: [\n { recordId: 'R1', score: 0.8, class: 'Yes' },\n { recordId: 'R2', score: 0.2, class: 'No' }\n ]\n });\n });\n\n it('score returns a zeroed result with records counted as failed on error', async () => {\n mockScoreExecute.mockResolvedValue({ Success: false, ErrorMessage: 'no model' });\n\n const u = new RuntimeUtilities().buildUtilities();\n const result = await u.ml!.score('M1', ['R1', 'R2']);\n\n expect(result).toEqual({ scoredCount: 0, failedCount: 2, skippedCount: 0, predictions: [] });\n });\n});\n"]}
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { CommonModule } from '@angular/common';
4
+ import { MJReactComponent } from './mj-react-component.component';
5
+ import { ReactBridgeService } from '../services/react-bridge.service';
6
+ import { AngularAdapterService } from '../services/angular-adapter.service';
7
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
8
+ import { ComponentRegistry } from '@memberjunction/react-runtime';
9
+ /**
10
+ * DOM tests for MJReactComponent.
11
+ *
12
+ * MJReactComponent hosts a React component compiled at runtime via @babel/standalone.
13
+ * The real React bootstrap (ReactBridgeService.getReactContext / babel compilation /
14
+ * reactRootManager) is NOT exercisable in jsdom and is out of scope for a DOM unit
15
+ * test — that is the live/integration surface. What IS unit-testable is the component's
16
+ * own Angular *template contract*: the loading-overlay gating
17
+ * @if (!isInitialized && !hasError) { ...overlay... }
18
+ * and the container's conditional class
19
+ * [class.loading]="!isInitialized"
20
+ *
21
+ * To isolate that template contract we stub ReactBridgeService at the DI seam with a
22
+ * never-resolving getReactContext(), so ngAfterViewInit's async initializeComponent()
23
+ * starts but never flips isInitialized — leaving the component in its deterministic
24
+ * initial render state. The internal isInitialized / hasError state for the other two
25
+ * states is set BEFORE the first detectChanges() per the zoneless CD rule
26
+ * (guides/ANGULAR_TESTING_GUIDE.md §5).
27
+ */
28
+ describe('MJReactComponent (DOM)', () => {
29
+ beforeEach(() => {
30
+ // Never-resolving promise so initializeComponent() begins but never completes,
31
+ // keeping the component in its initial (isInitialized=false) render state.
32
+ const bridgeStub = {
33
+ // Promise<never> never resolves AND is assignable to the methods' Promise<RuntimeContext>/Promise<void> returns.
34
+ getReactContext: vi.fn(() => new Promise(() => { })),
35
+ waitForReactReady: vi.fn(() => new Promise(() => { })),
36
+ };
37
+ // getRegistry() is called during ngOnDestroy -> cleanup(); return a real
38
+ // (empty) registry so teardown's registry.cleanup() runs harmlessly.
39
+ const registry = new ComponentRegistry();
40
+ const adapterStub = {
41
+ isInitialized: vi.fn(() => false),
42
+ getRegistry: vi.fn(() => registry),
43
+ };
44
+ const notificationStub = {};
45
+ TestBed.configureTestingModule({
46
+ imports: [CommonModule],
47
+ declarations: [MJReactComponent],
48
+ providers: [
49
+ { provide: ReactBridgeService, useValue: bridgeStub },
50
+ { provide: AngularAdapterService, useValue: adapterStub },
51
+ { provide: MJNotificationService, useValue: notificationStub },
52
+ ],
53
+ });
54
+ });
55
+ function createFixture() {
56
+ return TestBed.createComponent(MJReactComponent);
57
+ }
58
+ it('renders the loading overlay and marks the container loading in the initial state', () => {
59
+ const fixture = createFixture();
60
+ fixture.detectChanges();
61
+ const overlay = fixture.nativeElement.querySelector('.loading-overlay');
62
+ expect(overlay).not.toBeNull();
63
+ expect(overlay.querySelector('.loading-spinner')).not.toBeNull();
64
+ expect(overlay.querySelector('.loading-text')?.textContent).toContain('Loading component');
65
+ const container = fixture.nativeElement.querySelector('.react-component-container');
66
+ expect(container).not.toBeNull();
67
+ expect(container.classList.contains('loading')).toBe(true);
68
+ });
69
+ it('always renders the react-component-wrapper host structure', () => {
70
+ const fixture = createFixture();
71
+ fixture.detectChanges();
72
+ expect(fixture.nativeElement.querySelector('.react-component-wrapper')).not.toBeNull();
73
+ // The #container ViewChild target is always present (static: true).
74
+ expect(fixture.componentInstance.container?.nativeElement).toBeTruthy();
75
+ });
76
+ it('hides the loading overlay and clears the loading class once initialized', () => {
77
+ const fixture = createFixture();
78
+ // Set internal state BEFORE first CD (zoneless-safe).
79
+ fixture.componentInstance.isInitialized = true;
80
+ fixture.detectChanges();
81
+ expect(fixture.nativeElement.querySelector('.loading-overlay')).toBeNull();
82
+ const container = fixture.nativeElement.querySelector('.react-component-container');
83
+ expect(container.classList.contains('loading')).toBe(false);
84
+ });
85
+ it('hides the loading overlay when an error occurs (even before initialization)', () => {
86
+ const fixture = createFixture();
87
+ // isInitialized stays false; hasError gates the overlay off.
88
+ fixture.componentInstance.hasError = true;
89
+ fixture.detectChanges();
90
+ expect(fixture.nativeElement.querySelector('.loading-overlay')).toBeNull();
91
+ // Container is still in the loading (opacity:0) class because not yet initialized.
92
+ const container = fixture.nativeElement.querySelector('.react-component-container');
93
+ expect(container.classList.contains('loading')).toBe(true);
94
+ });
95
+ });
96
+ //# sourceMappingURL=mj-react-component.component.dom.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mj-react-component.component.dom.test.js","sourceRoot":"","sources":["../../../src/lib/components/mj-react-component.component.dom.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, vi, beforeEach } from 'vitest';\nimport { ComponentFixture, TestBed } from '@angular/core/testing';\nimport { CommonModule } from '@angular/common';\nimport { MJReactComponent } from './mj-react-component.component';\nimport { ReactBridgeService } from '../services/react-bridge.service';\nimport { AngularAdapterService } from '../services/angular-adapter.service';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { ComponentRegistry } from '@memberjunction/react-runtime';\n\n/**\n * DOM tests for MJReactComponent.\n *\n * MJReactComponent hosts a React component compiled at runtime via @babel/standalone.\n * The real React bootstrap (ReactBridgeService.getReactContext / babel compilation /\n * reactRootManager) is NOT exercisable in jsdom and is out of scope for a DOM unit\n * test — that is the live/integration surface. What IS unit-testable is the component's\n * own Angular *template contract*: the loading-overlay gating\n * @if (!isInitialized && !hasError) { ...overlay... }\n * and the container's conditional class\n * [class.loading]=\"!isInitialized\"\n *\n * To isolate that template contract we stub ReactBridgeService at the DI seam with a\n * never-resolving getReactContext(), so ngAfterViewInit's async initializeComponent()\n * starts but never flips isInitialized — leaving the component in its deterministic\n * initial render state. The internal isInitialized / hasError state for the other two\n * states is set BEFORE the first detectChanges() per the zoneless CD rule\n * (guides/ANGULAR_TESTING_GUIDE.md §5).\n */\ndescribe('MJReactComponent (DOM)', () => {\n beforeEach(() => {\n // Never-resolving promise so initializeComponent() begins but never completes,\n // keeping the component in its initial (isInitialized=false) render state.\n const bridgeStub: Pick<ReactBridgeService, 'getReactContext' | 'waitForReactReady'> = {\n // Promise<never> never resolves AND is assignable to the methods' Promise<RuntimeContext>/Promise<void> returns.\n getReactContext: vi.fn(() => new Promise<never>(() => {})),\n waitForReactReady: vi.fn(() => new Promise<never>(() => {})),\n };\n // getRegistry() is called during ngOnDestroy -> cleanup(); return a real\n // (empty) registry so teardown's registry.cleanup() runs harmlessly.\n const registry = new ComponentRegistry();\n const adapterStub: Pick<AngularAdapterService, 'isInitialized' | 'getRegistry'> = {\n isInitialized: vi.fn(() => false),\n getRegistry: vi.fn(() => registry),\n };\n const notificationStub: Partial<MJNotificationService> = {};\n\n TestBed.configureTestingModule({\n imports: [CommonModule],\n declarations: [MJReactComponent],\n providers: [\n { provide: ReactBridgeService, useValue: bridgeStub },\n { provide: AngularAdapterService, useValue: adapterStub },\n { provide: MJNotificationService, useValue: notificationStub },\n ],\n });\n });\n\n function createFixture(): ComponentFixture<MJReactComponent> {\n return TestBed.createComponent(MJReactComponent);\n }\n\n it('renders the loading overlay and marks the container loading in the initial state', () => {\n const fixture = createFixture();\n fixture.detectChanges();\n\n const overlay = fixture.nativeElement.querySelector('.loading-overlay');\n expect(overlay).not.toBeNull();\n expect(overlay!.querySelector('.loading-spinner')).not.toBeNull();\n expect(overlay!.querySelector('.loading-text')?.textContent).toContain('Loading component');\n\n const container = fixture.nativeElement.querySelector('.react-component-container');\n expect(container).not.toBeNull();\n expect(container!.classList.contains('loading')).toBe(true);\n });\n\n it('always renders the react-component-wrapper host structure', () => {\n const fixture = createFixture();\n fixture.detectChanges();\n\n expect(fixture.nativeElement.querySelector('.react-component-wrapper')).not.toBeNull();\n // The #container ViewChild target is always present (static: true).\n expect(fixture.componentInstance.container?.nativeElement).toBeTruthy();\n });\n\n it('hides the loading overlay and clears the loading class once initialized', () => {\n const fixture = createFixture();\n // Set internal state BEFORE first CD (zoneless-safe).\n fixture.componentInstance.isInitialized = true;\n fixture.detectChanges();\n\n expect(fixture.nativeElement.querySelector('.loading-overlay')).toBeNull();\n\n const container = fixture.nativeElement.querySelector('.react-component-container');\n expect(container!.classList.contains('loading')).toBe(false);\n });\n\n it('hides the loading overlay when an error occurs (even before initialization)', () => {\n const fixture = createFixture();\n // isInitialized stays false; hasError gates the overlay off.\n fixture.componentInstance.hasError = true;\n fixture.detectChanges();\n\n expect(fixture.nativeElement.querySelector('.loading-overlay')).toBeNull();\n // Container is still in the loading (opacity:0) class because not yet initialized.\n const container = fixture.nativeElement.querySelector('.react-component-container');\n expect(container!.classList.contains('loading')).toBe(true);\n });\n});\n"]}
@@ -20,6 +20,42 @@ export declare class RuntimeUtilities {
20
20
  */
21
21
  private SetupUtilities;
22
22
  private CreateSimpleAITools;
23
+ /**
24
+ * Creates the ML tools surface for components — listing trained models and scoring records.
25
+ * `listModels` reads the `MJ: ML Models` catalog via RunView; `score` marshals the
26
+ * `PredictiveStudio.ScoreRecordSet` Remote Operation over GraphQL to the server engine (the
27
+ * Python sidecar lives server-side and cannot run in the browser). Returns `undefined` when no
28
+ * GraphQL provider is available, so the `ml` capability degrades cleanly.
29
+ */
30
+ private CreateSimpleMLTools;
31
+ /**
32
+ * Loads the trained-model catalog from `MJ: ML Models`, newest version first, mapping each row
33
+ * to a {@link SimpleMLModelInfo}. Resilient — logs and returns `[]` on any failure.
34
+ */
35
+ private listMLModels;
36
+ /**
37
+ * Builds the ExtraFilter clause for {@link listMLModels}. Defaults to `Status='Published'` so
38
+ * components only see promoted models unless the caller overrides the status.
39
+ */
40
+ private buildMLModelsFilter;
41
+ /** Maps a single `MJ: ML Models` row to the component-facing {@link SimpleMLModelInfo} shape. */
42
+ private mapMLModel;
43
+ /** Defensively parses a JSON metrics blob; returns `undefined` for null/empty/invalid JSON. */
44
+ private parseMLMetrics;
45
+ /**
46
+ * Scores records with a trained model by invoking the `PredictiveStudio.ScoreRecordSet` Remote
47
+ * Operation. Normalizes `records` to primary-key strings, requests ephemeral predictions (no
48
+ * write-back), and maps the result. Resilient — logs and returns a zeroed result with the input
49
+ * records counted as failed on any error.
50
+ */
51
+ private scoreMLRecords;
52
+ /**
53
+ * Normalizes a mixed array of primary-key strings and row objects into an array of primary-key
54
+ * strings, reading `primaryKeyField` from objects. Drops entries without a resolvable key.
55
+ */
56
+ private normalizeRecordKeys;
57
+ /** Escapes single quotes for safe inlining into a RunView ExtraFilter SQL string literal. */
58
+ private escapeSqlLiteral;
23
59
  private CreateSimpleMetadata;
24
60
  private CreateSimpleGeoDataEngine;
25
61
  private CreateSimpleRunQuery;
@@ -13,6 +13,7 @@ import { MJGlobal, RegisterClass } from '@memberjunction/global';
13
13
  import { GeoDataEngine } from '@memberjunction/core-entities';
14
14
  import { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';
15
15
  import { SimpleVectorService } from '@memberjunction/ai-vectors-memory';
16
+ import { PredictiveStudioScoreRecordSetOperation } from '@memberjunction/core-entities';
16
17
  /**
17
18
  * Base class for providing runtime utilities to React components in Angular.
18
19
  * This class can be extended and registered with MJ's ClassFactory
@@ -42,7 +43,8 @@ let RuntimeUtilities = class RuntimeUtilities {
42
43
  rv: this.CreateSimpleRunView(rv),
43
44
  rq: this.CreateSimpleRunQuery(rq),
44
45
  ai: this.CreateSimpleAITools(),
45
- geoDataEngine: this.CreateSimpleGeoDataEngine()
46
+ geoDataEngine: this.CreateSimpleGeoDataEngine(),
47
+ ml: this.CreateSimpleMLTools()
46
48
  };
47
49
  return u;
48
50
  }
@@ -115,6 +117,149 @@ let RuntimeUtilities = class RuntimeUtilities {
115
117
  VectorService: new SimpleVectorService()
116
118
  };
117
119
  }
120
+ /**
121
+ * Creates the ML tools surface for components — listing trained models and scoring records.
122
+ * `listModels` reads the `MJ: ML Models` catalog via RunView; `score` marshals the
123
+ * `PredictiveStudio.ScoreRecordSet` Remote Operation over GraphQL to the server engine (the
124
+ * Python sidecar lives server-side and cannot run in the browser). Returns `undefined` when no
125
+ * GraphQL provider is available, so the `ml` capability degrades cleanly.
126
+ */
127
+ CreateSimpleMLTools() {
128
+ const provider = BaseEntity.Provider;
129
+ // Scoring requires a GraphQL provider to route the Remote Operation to the server engine.
130
+ if (!(provider instanceof GraphQLDataProvider)) {
131
+ return undefined;
132
+ }
133
+ const graphQLProvider = provider;
134
+ return {
135
+ listModels: (filter, contextUser) => this.listMLModels(filter, contextUser),
136
+ score: (modelId, records, options) => this.scoreMLRecords(graphQLProvider, modelId, records, options)
137
+ };
138
+ }
139
+ /**
140
+ * Loads the trained-model catalog from `MJ: ML Models`, newest version first, mapping each row
141
+ * to a {@link SimpleMLModelInfo}. Resilient — logs and returns `[]` on any failure.
142
+ */
143
+ async listMLModels(filter, contextUser) {
144
+ try {
145
+ const rv = new RunView();
146
+ const result = await rv.RunView({
147
+ EntityName: 'MJ: ML Models',
148
+ ExtraFilter: this.buildMLModelsFilter(filter),
149
+ OrderBy: 'Version DESC',
150
+ MaxRows: filter?.maxResults,
151
+ ResultType: 'entity_object'
152
+ }, contextUser);
153
+ if (!result.Success) {
154
+ console.error(`❌ listModels failed for MJ: ML Models: ${result.ErrorMessage}`);
155
+ return [];
156
+ }
157
+ return result.Results.map((m) => this.mapMLModel(m));
158
+ }
159
+ catch (error) {
160
+ LogError(error);
161
+ return [];
162
+ }
163
+ }
164
+ /**
165
+ * Builds the ExtraFilter clause for {@link listMLModels}. Defaults to `Status='Published'` so
166
+ * components only see promoted models unless the caller overrides the status.
167
+ */
168
+ buildMLModelsFilter(filter) {
169
+ const clauses = [];
170
+ const status = filter?.status ?? 'Published';
171
+ clauses.push(`Status='${this.escapeSqlLiteral(status)}'`);
172
+ if (filter?.targetVariable) {
173
+ clauses.push(`TargetVariable='${this.escapeSqlLiteral(filter.targetVariable)}'`);
174
+ }
175
+ return clauses.join(' AND ');
176
+ }
177
+ /** Maps a single `MJ: ML Models` row to the component-facing {@link SimpleMLModelInfo} shape. */
178
+ mapMLModel(m) {
179
+ return {
180
+ id: m.ID,
181
+ pipeline: m.Pipeline,
182
+ version: m.Version,
183
+ targetVariable: m.TargetVariable,
184
+ problemType: m.ProblemType,
185
+ status: m.Status,
186
+ metrics: this.parseMLMetrics(m.Metrics),
187
+ holdoutMetrics: this.parseMLMetrics(m.HoldoutMetrics)
188
+ };
189
+ }
190
+ /** Defensively parses a JSON metrics blob; returns `undefined` for null/empty/invalid JSON. */
191
+ parseMLMetrics(raw) {
192
+ if (!raw) {
193
+ return undefined;
194
+ }
195
+ try {
196
+ const parsed = JSON.parse(raw);
197
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
198
+ }
199
+ catch {
200
+ return undefined;
201
+ }
202
+ }
203
+ /**
204
+ * Scores records with a trained model by invoking the `PredictiveStudio.ScoreRecordSet` Remote
205
+ * Operation. Normalizes `records` to primary-key strings, requests ephemeral predictions (no
206
+ * write-back), and maps the result. Resilient — logs and returns a zeroed result with the input
207
+ * records counted as failed on any error.
208
+ */
209
+ async scoreMLRecords(provider, modelId, records, options) {
210
+ const keys = this.normalizeRecordKeys(records, options?.primaryKeyField ?? 'ID');
211
+ try {
212
+ const input = {
213
+ modelId,
214
+ scope: { records: keys }
215
+ // No writeBack → predictions are returned ephemerally.
216
+ };
217
+ const op = new PredictiveStudioScoreRecordSetOperation();
218
+ const result = await op.Execute(input, { provider, user: options?.contextUser });
219
+ if (!result.Success || !result.Output) {
220
+ console.error(`❌ score failed for model ${modelId}: ${result.ErrorMessage}`);
221
+ return { scoredCount: 0, failedCount: keys.length, skippedCount: 0, predictions: [] };
222
+ }
223
+ const out = result.Output;
224
+ return {
225
+ scoredCount: out.scored,
226
+ failedCount: out.failed,
227
+ skippedCount: out.skipped,
228
+ predictions: (out.predictions ?? []).map((p) => ({
229
+ recordId: p.recordId,
230
+ score: p.score,
231
+ class: p.class
232
+ }))
233
+ };
234
+ }
235
+ catch (error) {
236
+ LogError(error);
237
+ return { scoredCount: 0, failedCount: keys.length, skippedCount: 0, predictions: [] };
238
+ }
239
+ }
240
+ /**
241
+ * Normalizes a mixed array of primary-key strings and row objects into an array of primary-key
242
+ * strings, reading `primaryKeyField` from objects. Drops entries without a resolvable key.
243
+ */
244
+ normalizeRecordKeys(records, primaryKeyField) {
245
+ const keys = [];
246
+ for (const r of records) {
247
+ if (typeof r === 'string') {
248
+ keys.push(r);
249
+ }
250
+ else if (r != null) {
251
+ const value = r[primaryKeyField];
252
+ if (value != null) {
253
+ keys.push(String(value));
254
+ }
255
+ }
256
+ }
257
+ return keys;
258
+ }
259
+ /** Escapes single quotes for safe inlining into a RunView ExtraFilter SQL string literal. */
260
+ escapeSqlLiteral(value) {
261
+ return value.replace(/'/g, "''");
262
+ }
118
263
  CreateSimpleMetadata(md) {
119
264
  return {
120
265
  Entities: md.Entities,
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-utilities.js","sourceRoot":"","sources":["../../../src/lib/utilities/runtime-utilities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Runtime utilities for React components providing access to MemberJunction core functionality\n * @module @memberjunction/ng-react/utilities\n */\n\nimport { \n Metadata, \n RunView, \n RunQuery, \n RunViewParams, \n RunQueryParams,\n LogError,\n BaseEntity,\n IEntityDataProvider\n} from '@memberjunction/core';\n\nimport { MJGlobal, RegisterClass } from '@memberjunction/global';\nimport { GeoDataEngine } from '@memberjunction/core-entities';\nimport {\n ComponentUtilities,\n SimpleAITools,\n SimpleGeoDataEngine,\n SimpleMetadata,\n SimpleRunQuery,\n SimpleRunView,\n SimpleExecutePromptParams,\n SimpleExecutePromptResult,\n SimpleEmbedTextParams,\n SimpleEmbedTextResult\n} from '@memberjunction/interactive-component-types';\nimport { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';\nimport { SimpleVectorService } from '@memberjunction/ai-vectors-memory';\n\n/**\n * Base class for providing runtime utilities to React components in Angular.\n * This class can be extended and registered with MJ's ClassFactory\n * to provide custom implementations of data access methods.\n */\n@RegisterClass(RuntimeUtilities, 'RuntimeUtilities')\nexport class RuntimeUtilities {\n private debug: boolean = false;\n \n /**\n * Builds the complete utilities object for React components\n * This is the main method that components will use\n */\n public buildUtilities(debug: boolean = false): ComponentUtilities {\n this.debug = debug;\n const md = new Metadata(); // global-provider-ok: utility — single-provider context\n return this.SetupUtilities(md);\n }\n\n /**\n * Sets up the utilities object - copied from skip-chat implementation\n */\n private SetupUtilities(md: Metadata): ComponentUtilities {\n const rv = new RunView();\n const rq = new RunQuery();\n const u: ComponentUtilities = {\n md: this.CreateSimpleMetadata(md),\n rv: this.CreateSimpleRunView(rv),\n rq: this.CreateSimpleRunQuery(rq),\n ai: this.CreateSimpleAITools(),\n geoDataEngine: this.CreateSimpleGeoDataEngine()\n };\n return u;\n }\n\n private CreateSimpleAITools(): SimpleAITools {\n // Get the GraphQL provider - it's the same as the BaseEntity provider\n const provider = BaseEntity.Provider;\n \n // Check if it's a GraphQLDataProvider\n if (!(provider instanceof GraphQLDataProvider)) {\n throw new Error('Current data provider is not a GraphQLDataProvider. AI tools require GraphQL provider.');\n }\n\n const graphQLProvider = provider as GraphQLDataProvider;\n \n return {\n ExecutePrompt: async (params: SimpleExecutePromptParams): Promise<SimpleExecutePromptResult> => {\n try {\n // Use the AI client from GraphQLDataProvider to execute simple prompt\n const result = await graphQLProvider.AI.ExecuteSimplePrompt({\n systemPrompt: params.systemPrompt,\n messages: params.messages,\n preferredModels: params.preferredModels,\n modelPower: params.modelPower\n });\n\n console.log(`🤖 ExecutePrompt succeeded!`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n\n return {\n success: result.success,\n result: result.result || '',\n resultObject: result.resultObject,\n modelName: result.modelName || ''\n };\n } catch (error) {\n LogError(error);\n return {\n success: false,\n result: 'Failed to execute prompt: ' + (error instanceof Error ? error.message : String(error)),\n modelName: ''\n };\n }\n },\n \n EmbedText: async (params: SimpleEmbedTextParams): Promise<SimpleEmbedTextResult> => {\n try {\n // Use the AI client from GraphQLDataProvider to generate embeddings\n const result = await graphQLProvider.AI.EmbedText({\n textToEmbed: params.textToEmbed,\n modelSize: params.modelSize\n });\n \n if (result.error) {\n throw new Error(result.error || 'Failed to generate embeddings');\n }\n\n const numEmbeddings: number = Array.isArray(params.textToEmbed) ? result.embeddings?.length : 1;\n console.log(`🤖 EmbedText succeeded! ${numEmbeddings} embeddings returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n return {\n result: result.embeddings,\n modelName: result.modelName,\n vectorDimensions: result.vectorDimensions\n };\n } catch (error) {\n LogError(error);\n throw error; // Re-throw for embeddings as they're critical\n }\n },\n \n VectorService: new SimpleVectorService()\n };\n }\n\n private CreateSimpleMetadata(md: Metadata): SimpleMetadata {\n return {\n Entities: md.Entities,\n GetEntityObject: (entityName: string) => {\n return md.GetEntityObject(entityName)\n }\n }\n }\n\n private CreateSimpleGeoDataEngine(): SimpleGeoDataEngine | undefined {\n try {\n const geo = GeoDataEngine.Instance;\n if (!geo) return undefined;\n return {\n ResolvePointToLocation: (lat: number, lng: number) => {\n return geo.ResolvePointToLocation(lat, lng);\n },\n // GeoDataEngine is on-demand load — callers must await before ResolvePointToLocation works.\n EnsureLoaded: () => geo.EnsureLoaded(),\n get Loaded() {\n return geo.Loaded;\n }\n };\n } catch {\n // GeoDataEngine may not be configured yet — return undefined\n return undefined;\n }\n }\n\n private CreateSimpleRunQuery(rq: RunQuery): SimpleRunQuery {\n return {\n RunQuery: async (params: RunQueryParams) => {\n // Run a single query and return the results\n try {\n const result = await rq.RunQuery(params);\n if (result.Success) {\n console.log(`✅ RunQuery \"${params.QueryName}\" succeeded: ${result.RowCount} rows returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n } else {\n console.error(`❌ RunQuery failed: ${result.ErrorMessage}`);\n }\n return result;\n } catch (error) {\n console.error(`❌ RunQuery threw exception:`, error);\n LogError(error);\n throw error; // Re-throw to handle it in the caller\n }\n }\n }\n }\n\n private CreateSimpleRunView(rv: RunView): SimpleRunView {\n return {\n RunView: async (params: RunViewParams) => {\n // Run a single view and return the results\n try {\n const result = await rv.RunView(params);\n if (result.Success) {\n console.log(`✅ RunView succeeded for ${params.EntityName}: ${result.TotalRowCount} rows returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n } else {\n console.error(`❌ RunView failed for ${params.EntityName}: ${result.ErrorMessage}`);\n }\n return result;\n } catch (error) {\n console.error(`❌ RunView threw exception:`, error);\n LogError(error);\n throw error; // Re-throw to handle it in the caller\n }\n },\n RunViews: async (params: RunViewParams[]) => {\n // Runs multiple views and returns the results\n try {\n const results = await rv.RunViews(params);\n const entityNames = params.map(p => p.EntityName).join(', ');\n const totalRows = results.reduce((sum, r) => sum + (r.TotalRowCount || 0), 0);\n console.log(`✅ RunViews succeeded for [${entityNames}]: ${totalRows} total rows returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > results:', results);\n }\n return results;\n } catch (error) {\n console.error(`❌ RunViews threw exception:`, error);\n LogError(error);\n throw error; // Re-throw to handle it in the caller\n }\n }\n }\n }\n}\n\n/**\n * Factory function to create RuntimeUtilities\n * In a Node.js environment, this will use MJ's ClassFactory for runtime substitution\n * In a browser environment, it will use the base class directly\n */\nexport function createRuntimeUtilities(): RuntimeUtilities {\n // Check if we're in a Node.js environment with MJGlobal available\n if (typeof window === 'undefined') {\n try {\n // Use ClassFactory to get the registered class, defaulting to base RuntimeUtilities\n const obj = MJGlobal.Instance.ClassFactory.CreateInstance<RuntimeUtilities>(RuntimeUtilities);\n if (!obj) {\n throw new Error('Failed to create RuntimeUtilities instance');\n }\n\n // Ensure the object is an instance of RuntimeUtilities\n return obj;\n } catch (e) {\n // Fall through to default\n }\n }\n \n // Default: just use the base class\n return new RuntimeUtilities();\n}"]}
1
+ {"version":3,"file":"runtime-utilities.js","sourceRoot":"","sources":["../../../src/lib/utilities/runtime-utilities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Runtime utilities for React components providing access to MemberJunction core functionality\n * @module @memberjunction/ng-react/utilities\n */\n\nimport {\n Metadata,\n RunView,\n RunQuery,\n RunViewParams,\n RunQueryParams,\n LogError,\n BaseEntity,\n IEntityDataProvider,\n UserInfo\n} from '@memberjunction/core';\n\nimport { MJGlobal, RegisterClass } from '@memberjunction/global';\nimport { GeoDataEngine } from '@memberjunction/core-entities';\nimport {\n ComponentUtilities,\n SimpleAITools,\n SimpleGeoDataEngine,\n SimpleMetadata,\n SimpleMLTools,\n SimpleMLModelInfo,\n SimpleMLListModelsFilter,\n SimpleMLScoreResult,\n SimpleRunQuery,\n SimpleRunView,\n SimpleExecutePromptParams,\n SimpleExecutePromptResult,\n SimpleEmbedTextParams,\n SimpleEmbedTextResult\n} from '@memberjunction/interactive-component-types';\nimport { GraphQLDataProvider } from '@memberjunction/graphql-dataprovider';\nimport { SimpleVectorService } from '@memberjunction/ai-vectors-memory';\nimport {\n MJMLModelEntity,\n PredictiveStudioScoreRecordSetOperation,\n PredictiveStudioScoreRecordSetInput\n} from '@memberjunction/core-entities';\n\n/**\n * Base class for providing runtime utilities to React components in Angular.\n * This class can be extended and registered with MJ's ClassFactory\n * to provide custom implementations of data access methods.\n */\n@RegisterClass(RuntimeUtilities, 'RuntimeUtilities')\nexport class RuntimeUtilities {\n private debug: boolean = false;\n \n /**\n * Builds the complete utilities object for React components\n * This is the main method that components will use\n */\n public buildUtilities(debug: boolean = false): ComponentUtilities {\n this.debug = debug;\n const md = new Metadata(); // global-provider-ok: utility — single-provider context\n return this.SetupUtilities(md);\n }\n\n /**\n * Sets up the utilities object - copied from skip-chat implementation\n */\n private SetupUtilities(md: Metadata): ComponentUtilities {\n const rv = new RunView();\n const rq = new RunQuery();\n const u: ComponentUtilities = {\n md: this.CreateSimpleMetadata(md),\n rv: this.CreateSimpleRunView(rv),\n rq: this.CreateSimpleRunQuery(rq),\n ai: this.CreateSimpleAITools(),\n geoDataEngine: this.CreateSimpleGeoDataEngine(),\n ml: this.CreateSimpleMLTools()\n };\n return u;\n }\n\n private CreateSimpleAITools(): SimpleAITools {\n // Get the GraphQL provider - it's the same as the BaseEntity provider\n const provider = BaseEntity.Provider;\n \n // Check if it's a GraphQLDataProvider\n if (!(provider instanceof GraphQLDataProvider)) {\n throw new Error('Current data provider is not a GraphQLDataProvider. AI tools require GraphQL provider.');\n }\n\n const graphQLProvider = provider as GraphQLDataProvider;\n \n return {\n ExecutePrompt: async (params: SimpleExecutePromptParams): Promise<SimpleExecutePromptResult> => {\n try {\n // Use the AI client from GraphQLDataProvider to execute simple prompt\n const result = await graphQLProvider.AI.ExecuteSimplePrompt({\n systemPrompt: params.systemPrompt,\n messages: params.messages,\n preferredModels: params.preferredModels,\n modelPower: params.modelPower\n });\n\n console.log(`🤖 ExecutePrompt succeeded!`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n\n return {\n success: result.success,\n result: result.result || '',\n resultObject: result.resultObject,\n modelName: result.modelName || ''\n };\n } catch (error) {\n LogError(error);\n return {\n success: false,\n result: 'Failed to execute prompt: ' + (error instanceof Error ? error.message : String(error)),\n modelName: ''\n };\n }\n },\n \n EmbedText: async (params: SimpleEmbedTextParams): Promise<SimpleEmbedTextResult> => {\n try {\n // Use the AI client from GraphQLDataProvider to generate embeddings\n const result = await graphQLProvider.AI.EmbedText({\n textToEmbed: params.textToEmbed,\n modelSize: params.modelSize\n });\n \n if (result.error) {\n throw new Error(result.error || 'Failed to generate embeddings');\n }\n\n const numEmbeddings: number = Array.isArray(params.textToEmbed) ? result.embeddings?.length : 1;\n console.log(`🤖 EmbedText succeeded! ${numEmbeddings} embeddings returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n return {\n result: result.embeddings,\n modelName: result.modelName,\n vectorDimensions: result.vectorDimensions\n };\n } catch (error) {\n LogError(error);\n throw error; // Re-throw for embeddings as they're critical\n }\n },\n \n VectorService: new SimpleVectorService()\n };\n }\n\n /**\n * Creates the ML tools surface for components — listing trained models and scoring records.\n * `listModels` reads the `MJ: ML Models` catalog via RunView; `score` marshals the\n * `PredictiveStudio.ScoreRecordSet` Remote Operation over GraphQL to the server engine (the\n * Python sidecar lives server-side and cannot run in the browser). Returns `undefined` when no\n * GraphQL provider is available, so the `ml` capability degrades cleanly.\n */\n private CreateSimpleMLTools(): SimpleMLTools | undefined {\n const provider = BaseEntity.Provider;\n // Scoring requires a GraphQL provider to route the Remote Operation to the server engine.\n if (!(provider instanceof GraphQLDataProvider)) {\n return undefined;\n }\n const graphQLProvider = provider as GraphQLDataProvider;\n\n return {\n listModels: (filter?: SimpleMLListModelsFilter, contextUser?: UserInfo): Promise<SimpleMLModelInfo[]> =>\n this.listMLModels(filter, contextUser),\n\n score: (\n modelId: string,\n records: Array<Record<string, unknown> | string>,\n options?: { primaryKeyField?: string; contextUser?: UserInfo }\n ): Promise<SimpleMLScoreResult> => this.scoreMLRecords(graphQLProvider, modelId, records, options)\n };\n }\n\n /**\n * Loads the trained-model catalog from `MJ: ML Models`, newest version first, mapping each row\n * to a {@link SimpleMLModelInfo}. Resilient — logs and returns `[]` on any failure.\n */\n private async listMLModels(filter?: SimpleMLListModelsFilter, contextUser?: UserInfo): Promise<SimpleMLModelInfo[]> {\n try {\n const rv = new RunView();\n const result = await rv.RunView<MJMLModelEntity>(\n {\n EntityName: 'MJ: ML Models',\n ExtraFilter: this.buildMLModelsFilter(filter),\n OrderBy: 'Version DESC',\n MaxRows: filter?.maxResults,\n ResultType: 'entity_object'\n },\n contextUser\n );\n if (!result.Success) {\n console.error(`❌ listModels failed for MJ: ML Models: ${result.ErrorMessage}`);\n return [];\n }\n return result.Results.map((m) => this.mapMLModel(m));\n } catch (error) {\n LogError(error);\n return [];\n }\n }\n\n /**\n * Builds the ExtraFilter clause for {@link listMLModels}. Defaults to `Status='Published'` so\n * components only see promoted models unless the caller overrides the status.\n */\n private buildMLModelsFilter(filter?: SimpleMLListModelsFilter): string {\n const clauses: string[] = [];\n const status = filter?.status ?? 'Published';\n clauses.push(`Status='${this.escapeSqlLiteral(status)}'`);\n if (filter?.targetVariable) {\n clauses.push(`TargetVariable='${this.escapeSqlLiteral(filter.targetVariable)}'`);\n }\n return clauses.join(' AND ');\n }\n\n /** Maps a single `MJ: ML Models` row to the component-facing {@link SimpleMLModelInfo} shape. */\n private mapMLModel(m: MJMLModelEntity): SimpleMLModelInfo {\n return {\n id: m.ID,\n pipeline: m.Pipeline,\n version: m.Version,\n targetVariable: m.TargetVariable,\n problemType: m.ProblemType,\n status: m.Status,\n metrics: this.parseMLMetrics(m.Metrics),\n holdoutMetrics: this.parseMLMetrics(m.HoldoutMetrics)\n };\n }\n\n /** Defensively parses a JSON metrics blob; returns `undefined` for null/empty/invalid JSON. */\n private parseMLMetrics(raw: string | null): Record<string, unknown> | undefined {\n if (!raw) {\n return undefined;\n }\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Scores records with a trained model by invoking the `PredictiveStudio.ScoreRecordSet` Remote\n * Operation. Normalizes `records` to primary-key strings, requests ephemeral predictions (no\n * write-back), and maps the result. Resilient — logs and returns a zeroed result with the input\n * records counted as failed on any error.\n */\n private async scoreMLRecords(\n provider: GraphQLDataProvider,\n modelId: string,\n records: Array<Record<string, unknown> | string>,\n options?: { primaryKeyField?: string; contextUser?: UserInfo }\n ): Promise<SimpleMLScoreResult> {\n const keys = this.normalizeRecordKeys(records, options?.primaryKeyField ?? 'ID');\n try {\n const input: PredictiveStudioScoreRecordSetInput = {\n modelId,\n scope: { records: keys }\n // No writeBack → predictions are returned ephemerally.\n };\n const op = new PredictiveStudioScoreRecordSetOperation();\n const result = await op.Execute(input, { provider, user: options?.contextUser });\n if (!result.Success || !result.Output) {\n console.error(`❌ score failed for model ${modelId}: ${result.ErrorMessage}`);\n return { scoredCount: 0, failedCount: keys.length, skippedCount: 0, predictions: [] };\n }\n const out = result.Output;\n return {\n scoredCount: out.scored,\n failedCount: out.failed,\n skippedCount: out.skipped,\n predictions: (out.predictions ?? []).map((p) => ({\n recordId: p.recordId,\n score: p.score,\n class: p.class\n }))\n };\n } catch (error) {\n LogError(error);\n return { scoredCount: 0, failedCount: keys.length, skippedCount: 0, predictions: [] };\n }\n }\n\n /**\n * Normalizes a mixed array of primary-key strings and row objects into an array of primary-key\n * strings, reading `primaryKeyField` from objects. Drops entries without a resolvable key.\n */\n private normalizeRecordKeys(records: Array<Record<string, unknown> | string>, primaryKeyField: string): string[] {\n const keys: string[] = [];\n for (const r of records) {\n if (typeof r === 'string') {\n keys.push(r);\n } else if (r != null) {\n const value = r[primaryKeyField];\n if (value != null) {\n keys.push(String(value));\n }\n }\n }\n return keys;\n }\n\n /** Escapes single quotes for safe inlining into a RunView ExtraFilter SQL string literal. */\n private escapeSqlLiteral(value: string): string {\n return value.replace(/'/g, \"''\");\n }\n\n private CreateSimpleMetadata(md: Metadata): SimpleMetadata {\n return {\n Entities: md.Entities,\n GetEntityObject: (entityName: string) => {\n return md.GetEntityObject(entityName)\n }\n }\n }\n\n private CreateSimpleGeoDataEngine(): SimpleGeoDataEngine | undefined {\n try {\n const geo = GeoDataEngine.Instance;\n if (!geo) return undefined;\n return {\n ResolvePointToLocation: (lat: number, lng: number) => {\n return geo.ResolvePointToLocation(lat, lng);\n },\n // GeoDataEngine is on-demand load — callers must await before ResolvePointToLocation works.\n EnsureLoaded: () => geo.EnsureLoaded(),\n get Loaded() {\n return geo.Loaded;\n }\n };\n } catch {\n // GeoDataEngine may not be configured yet — return undefined\n return undefined;\n }\n }\n\n private CreateSimpleRunQuery(rq: RunQuery): SimpleRunQuery {\n return {\n RunQuery: async (params: RunQueryParams) => {\n // Run a single query and return the results\n try {\n const result = await rq.RunQuery(params);\n if (result.Success) {\n console.log(`✅ RunQuery \"${params.QueryName}\" succeeded: ${result.RowCount} rows returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n } else {\n console.error(`❌ RunQuery failed: ${result.ErrorMessage}`);\n }\n return result;\n } catch (error) {\n console.error(`❌ RunQuery threw exception:`, error);\n LogError(error);\n throw error; // Re-throw to handle it in the caller\n }\n }\n }\n }\n\n private CreateSimpleRunView(rv: RunView): SimpleRunView {\n return {\n RunView: async (params: RunViewParams) => {\n // Run a single view and return the results\n try {\n const result = await rv.RunView(params);\n if (result.Success) {\n console.log(`✅ RunView succeeded for ${params.EntityName}: ${result.TotalRowCount} rows returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > result:', result);\n }\n } else {\n console.error(`❌ RunView failed for ${params.EntityName}: ${result.ErrorMessage}`);\n }\n return result;\n } catch (error) {\n console.error(`❌ RunView threw exception:`, error);\n LogError(error);\n throw error; // Re-throw to handle it in the caller\n }\n },\n RunViews: async (params: RunViewParams[]) => {\n // Runs multiple views and returns the results\n try {\n const results = await rv.RunViews(params);\n const entityNames = params.map(p => p.EntityName).join(', ');\n const totalRows = results.reduce((sum, r) => sum + (r.TotalRowCount || 0), 0);\n console.log(`✅ RunViews succeeded for [${entityNames}]: ${totalRows} total rows returned`);\n if (this.debug) {\n console.log(' > params', params);\n console.log(' > results:', results);\n }\n return results;\n } catch (error) {\n console.error(`❌ RunViews threw exception:`, error);\n LogError(error);\n throw error; // Re-throw to handle it in the caller\n }\n }\n }\n }\n}\n\n/**\n * Factory function to create RuntimeUtilities\n * In a Node.js environment, this will use MJ's ClassFactory for runtime substitution\n * In a browser environment, it will use the base class directly\n */\nexport function createRuntimeUtilities(): RuntimeUtilities {\n // Check if we're in a Node.js environment with MJGlobal available\n if (typeof window === 'undefined') {\n try {\n // Use ClassFactory to get the registered class, defaulting to base RuntimeUtilities\n const obj = MJGlobal.Instance.ClassFactory.CreateInstance<RuntimeUtilities>(RuntimeUtilities);\n if (!obj) {\n throw new Error('Failed to create RuntimeUtilities instance');\n }\n\n // Ensure the object is an instance of RuntimeUtilities\n return obj;\n } catch (e) {\n // Fall through to default\n }\n }\n \n // Default: just use the base class\n return new RuntimeUtilities();\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-react",
3
- "version": "5.43.0",
3
+ "version": "5.45.0",
4
4
  "description": "Angular components for hosting React components in MemberJunction applications",
5
5
  "scripts": {
6
6
  "build": "ngc -p tsconfig.json",
@@ -41,14 +41,14 @@
41
41
  "styles"
42
42
  ],
43
43
  "dependencies": {
44
- "@memberjunction/ai-vectors-memory": "5.43.0",
45
- "@memberjunction/core": "5.43.0",
46
- "@memberjunction/core-entities": "5.43.0",
47
- "@memberjunction/global": "5.43.0",
48
- "@memberjunction/graphql-dataprovider": "5.43.0",
49
- "@memberjunction/interactive-component-types": "5.43.0",
50
- "@memberjunction/ng-notifications": "5.43.0",
51
- "@memberjunction/react-runtime": "5.43.0",
44
+ "@memberjunction/ai-vectors-memory": "5.45.0",
45
+ "@memberjunction/core": "5.45.0",
46
+ "@memberjunction/core-entities": "5.45.0",
47
+ "@memberjunction/global": "5.45.0",
48
+ "@memberjunction/graphql-dataprovider": "5.45.0",
49
+ "@memberjunction/interactive-component-types": "5.45.0",
50
+ "@memberjunction/ng-notifications": "5.45.0",
51
+ "@memberjunction/react-runtime": "5.45.0",
52
52
  "@angular/common": "21.1.3",
53
53
  "@angular/core": "21.1.3",
54
54
  "@angular/platform-browser": "21.1.3",
@@ -58,7 +58,7 @@
58
58
  "rxjs": "^7.8.2",
59
59
  "@types/react": "^19.2.13",
60
60
  "@types/react-dom": "^19.2.3",
61
- "@memberjunction/ng-base-types": "5.43.0"
61
+ "@memberjunction/ng-base-types": "5.45.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@angular/compiler": "21.1.3",