@memberjunction/core-entities-server 5.40.2 → 5.41.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.
- package/dist/custom/MJAIAgentCoAgentEntityServer.server.d.ts +78 -0
- package/dist/custom/MJAIAgentCoAgentEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIAgentCoAgentEntityServer.server.js +224 -0
- package/dist/custom/MJAIAgentCoAgentEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIAgentEntityServer.server.d.ts +50 -0
- package/dist/custom/MJAIAgentEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIAgentEntityServer.server.js +126 -0
- package/dist/custom/MJAIAgentEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIAgentNoteEntityServer.server.d.ts +6 -3
- package/dist/custom/MJAIAgentNoteEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJAIAgentNoteEntityServer.server.js +10 -7
- package/dist/custom/MJAIAgentNoteEntityServer.server.js.map +1 -1
- package/dist/custom/MJAIAgentSessionBridgeEntityServer.server.d.ts +50 -0
- package/dist/custom/MJAIAgentSessionBridgeEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIAgentSessionBridgeEntityServer.server.js +133 -0
- package/dist/custom/MJAIAgentSessionBridgeEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIAgentSessionBridgeParticipantEntityServer.server.d.ts +33 -0
- package/dist/custom/MJAIAgentSessionBridgeParticipantEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIAgentSessionBridgeParticipantEntityServer.server.js +90 -0
- package/dist/custom/MJAIAgentSessionBridgeParticipantEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIBridgeAgentIdentityEntityServer.server.d.ts +49 -0
- package/dist/custom/MJAIBridgeAgentIdentityEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIBridgeAgentIdentityEntityServer.server.js +152 -0
- package/dist/custom/MJAIBridgeAgentIdentityEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIBridgeProviderChannelEntityServer.server.d.ts +33 -0
- package/dist/custom/MJAIBridgeProviderChannelEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIBridgeProviderChannelEntityServer.server.js +90 -0
- package/dist/custom/MJAIBridgeProviderChannelEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIBridgeProviderEntityServer.server.d.ts +52 -0
- package/dist/custom/MJAIBridgeProviderEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIBridgeProviderEntityServer.server.js +154 -0
- package/dist/custom/MJAIBridgeProviderEntityServer.server.js.map +1 -0
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.d.ts +63 -0
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.js +212 -0
- package/dist/custom/MJAIRemoteBrowserProviderEntityServer.server.js.map +1 -0
- package/dist/custom/MJDuplicateRunEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJDuplicateRunEntityServer.server.js +6 -2
- package/dist/custom/MJDuplicateRunEntityServer.server.js.map +1 -1
- package/dist/custom/json-schema-lite.d.ts +36 -0
- package/dist/custom/json-schema-lite.d.ts.map +1 -0
- package/dist/custom/json-schema-lite.js +125 -0
- package/dist/custom/json-schema-lite.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/package.json +24 -24
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BaseEntity, RunView, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { MJAIBridgeProviderChannelEntity } from '@memberjunction/core-entities';
|
|
10
|
+
/**
|
|
11
|
+
* Server-side `MJ: AI Bridge Provider Channels` entity — the junction declaring which registered
|
|
12
|
+
* `AIAgentChannel`s a provider contributes (Zoom → Meeting Controls + Native Whiteboard, etc.).
|
|
13
|
+
*
|
|
14
|
+
* The only invariant worth enforcing is uniqueness of `(ProviderID, ChannelID)` — a provider
|
|
15
|
+
* shouldn't list the same channel twice. **The database already enforces this with a UNIQUE
|
|
16
|
+
* constraint (`UQ_AIBridgeProviderChannel (ProviderID, ChannelID)`)**, so this check is
|
|
17
|
+
* **defense-in-depth / friendly-message only**: it turns the raw SQL unique-violation error into a
|
|
18
|
+
* clear, field-attributed message at validation time (before the INSERT/UPDATE is attempted) and
|
|
19
|
+
* is consistent with how the sibling co-agent / identity servers surface their cross-record rules.
|
|
20
|
+
* No format/shape invariants exist for this purely-relational row, so there is nothing else to add.
|
|
21
|
+
*/
|
|
22
|
+
let MJAIBridgeProviderChannelEntityServer = class MJAIBridgeProviderChannelEntityServer extends MJAIBridgeProviderChannelEntity {
|
|
23
|
+
/** Enable async validation so the friendly-message uniqueness check runs. */
|
|
24
|
+
get DefaultSkipAsyncValidation() {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
async ValidateAsync() {
|
|
28
|
+
const result = await super.ValidateAsync();
|
|
29
|
+
if (!result.Success)
|
|
30
|
+
return result;
|
|
31
|
+
const providerDirty = this.GetFieldByName('ProviderID')?.Dirty ?? false;
|
|
32
|
+
const channelDirty = this.GetFieldByName('ChannelID')?.Dirty ?? false;
|
|
33
|
+
if (!this.IsSaved || providerDirty || channelDirty) {
|
|
34
|
+
const conflict = await this.countOtherProviderChannelRows();
|
|
35
|
+
const dupError = BuildDuplicateProviderChannelError(this.ProviderID, this.ChannelID, conflict);
|
|
36
|
+
if (dupError) {
|
|
37
|
+
result.Errors.push(dupError);
|
|
38
|
+
result.Success = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Counts OTHER rows pairing this provider with this channel (excluding this row when updating).
|
|
45
|
+
* A failed query counts as 0 (fail-open; the DB UNIQUE constraint is the hard backstop anyway).
|
|
46
|
+
*/
|
|
47
|
+
async countOtherProviderChannelRows() {
|
|
48
|
+
if (!this.ProviderID || !this.ChannelID) {
|
|
49
|
+
return 0; // required-field violations are sync Validate()'s job
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const md = this.ProviderToUse;
|
|
53
|
+
const selfExclusion = this.IsSaved ? ` AND ID <> '${this.safeLiteral(this.ID)}'` : '';
|
|
54
|
+
const filter = `ProviderID='${this.safeLiteral(this.ProviderID)}' AND ` +
|
|
55
|
+
`ChannelID='${this.safeLiteral(this.ChannelID)}'${selfExclusion}`;
|
|
56
|
+
const rv = RunView.FromMetadataProvider(md);
|
|
57
|
+
const queryResult = await rv.RunView({
|
|
58
|
+
EntityName: 'MJ: AI Bridge Provider Channels',
|
|
59
|
+
ExtraFilter: filter,
|
|
60
|
+
Fields: ['ID'],
|
|
61
|
+
ResultType: 'simple',
|
|
62
|
+
}, this.ContextCurrentUser);
|
|
63
|
+
return queryResult.Success ? (queryResult.Results?.length ?? 0) : 0;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Escapes a UUID/string for safe inclusion in an ExtraFilter literal. */
|
|
70
|
+
safeLiteral(value) {
|
|
71
|
+
return value.replace(/'/g, "''");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
MJAIBridgeProviderChannelEntityServer = __decorate([
|
|
75
|
+
RegisterClass(BaseEntity, 'MJ: AI Bridge Provider Channels')
|
|
76
|
+
], MJAIBridgeProviderChannelEntityServer);
|
|
77
|
+
export { MJAIBridgeProviderChannelEntityServer };
|
|
78
|
+
/**
|
|
79
|
+
* PURE invariant: a provider may list a given channel only once. Returns the friendly error when
|
|
80
|
+
* `existingCount` other rows already pair this `(ProviderID, ChannelID)`, else `null`.
|
|
81
|
+
* (Defense-in-depth over the DB `UQ_AIBridgeProviderChannel` UNIQUE constraint.)
|
|
82
|
+
*/
|
|
83
|
+
export function BuildDuplicateProviderChannelError(providerID, channelID, existingCount) {
|
|
84
|
+
if (existingCount <= 0) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return new ValidationErrorInfo('ChannelID', `This provider already contributes the selected channel (${existingCount} existing row(s)). ` +
|
|
88
|
+
'A provider can list each channel only once — edit the existing row instead.', channelID, ValidationErrorType.Failure);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=MJAIBridgeProviderChannelEntityServer.server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJAIBridgeProviderChannelEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJAIBridgeProviderChannelEntityServer.server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ValidationErrorInfo, ValidationResult } from '@memberjunction/core';
|
|
2
|
+
import { MJAIBridgeProviderEntity } from '@memberjunction/core-entities';
|
|
3
|
+
/**
|
|
4
|
+
* The complete set of known `IBridgeProviderFeatures` keys (see the generated
|
|
5
|
+
* `MJAIBridgeProviderEntity_IBridgeProviderFeatures` interface and
|
|
6
|
+
* `metadata/entities/JSONType-interfaces/IBridgeProviderFeatures.ts`). `SupportedFeatures` is a
|
|
7
|
+
* flat object of optional booleans, so the validation is: every present key must be in this set
|
|
8
|
+
* and every value must be a boolean. Keeping the list here (rather than reflecting off a runtime
|
|
9
|
+
* value) makes the validator a PURE, dependency-free, easily-unit-tested function and gives a
|
|
10
|
+
* friendly "unknown feature flag" message that a free-form `additionalProperties:false` schema
|
|
11
|
+
* would not.
|
|
12
|
+
*/
|
|
13
|
+
export declare const KNOWN_BRIDGE_PROVIDER_FEATURE_KEYS: ReadonlySet<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Server-side `MJ: AI Bridge Providers` entity enforcing the invariants the architecture documents
|
|
16
|
+
* (see `/plans/realtime/realtime-bridges-architecture.md` §3): the engine GATES every optional
|
|
17
|
+
* driver call on the `SupportedFeatures` JSON, so a malformed / mistyped flags blob is a latent
|
|
18
|
+
* runtime bug. We catch it at write time.
|
|
19
|
+
*
|
|
20
|
+
* Invariants:
|
|
21
|
+
* 1. **`SupportedFeatures` (when dirty/non-null) is well-formed `IBridgeProviderFeatures`** — it
|
|
22
|
+
* parses as a JSON object whose every key is a known feature flag and every value is a boolean.
|
|
23
|
+
* Unknown keys (typos like `OutbondDial`) and non-boolean values (`"true"`, `1`) are rejected
|
|
24
|
+
* with a precise message. PURE helper `ValidateSupportedFeaturesJson`.
|
|
25
|
+
* 2. **`DriverClass` is non-empty** — it is the `ClassFactory` key the engine resolves the bridge
|
|
26
|
+
* driver from; a blank value silently fails resolution. (The DB column is NOT NULL but allows
|
|
27
|
+
* empty/whitespace strings, so this adds the meaningful check.) PURE helper
|
|
28
|
+
* `ValidateDriverClass`.
|
|
29
|
+
*
|
|
30
|
+
* No cross-record `RunView` is needed here — both invariants are purely intra-row, so this is the
|
|
31
|
+
* cheapest possible `ValidateAsync` (no DB round trips).
|
|
32
|
+
*/
|
|
33
|
+
export declare class MJAIBridgeProviderEntityServer extends MJAIBridgeProviderEntity {
|
|
34
|
+
/** Enable async validation so these checks run on the server save path. */
|
|
35
|
+
get DefaultSkipAsyncValidation(): boolean;
|
|
36
|
+
ValidateAsync(): Promise<ValidationResult>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* PURE invariant: `DriverClass` must be a non-empty, non-whitespace string. Returns the error, or
|
|
40
|
+
* `null` when valid. (A `null`/`undefined` value is left to the sync required-field validator.)
|
|
41
|
+
*/
|
|
42
|
+
export declare function ValidateDriverClass(driverClass: string | null | undefined): ValidationErrorInfo | null;
|
|
43
|
+
/**
|
|
44
|
+
* PURE invariant core for `SupportedFeatures` (exported for unit tests): the raw column value must
|
|
45
|
+
* either be null/empty (unsupported — valid) or a JSON OBJECT whose keys are all known
|
|
46
|
+
* `IBridgeProviderFeatures` flags and whose values are all booleans.
|
|
47
|
+
*
|
|
48
|
+
* @param raw The raw `SupportedFeatures` column value.
|
|
49
|
+
* @returns The violations (empty array = valid).
|
|
50
|
+
*/
|
|
51
|
+
export declare function ValidateSupportedFeaturesJson(raw: string | null | undefined): ValidationErrorInfo[];
|
|
52
|
+
//# sourceMappingURL=MJAIBridgeProviderEntityServer.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJAIBridgeProviderEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJAIBridgeProviderEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,mBAAmB,EAAuB,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE;;;;;;;;;GASG;AACH,eAAO,MAAM,kCAAkC,EAAE,WAAW,CAAC,MAAM,CAoBjE,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,8BAA+B,SAAQ,wBAAwB;IACxE,2EAA2E;IAC3E,IAAoB,0BAA0B,IAAI,OAAO,CAExD;IAEqB,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC;CA0BnE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,mBAAmB,GAAG,IAAI,CAatG;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,mBAAmB,EAAE,CA8BnG"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BaseEntity, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { MJAIBridgeProviderEntity } from '@memberjunction/core-entities';
|
|
10
|
+
/**
|
|
11
|
+
* The complete set of known `IBridgeProviderFeatures` keys (see the generated
|
|
12
|
+
* `MJAIBridgeProviderEntity_IBridgeProviderFeatures` interface and
|
|
13
|
+
* `metadata/entities/JSONType-interfaces/IBridgeProviderFeatures.ts`). `SupportedFeatures` is a
|
|
14
|
+
* flat object of optional booleans, so the validation is: every present key must be in this set
|
|
15
|
+
* and every value must be a boolean. Keeping the list here (rather than reflecting off a runtime
|
|
16
|
+
* value) makes the validator a PURE, dependency-free, easily-unit-tested function and gives a
|
|
17
|
+
* friendly "unknown feature flag" message that a free-form `additionalProperties:false` schema
|
|
18
|
+
* would not.
|
|
19
|
+
*/
|
|
20
|
+
export const KNOWN_BRIDGE_PROVIDER_FEATURE_KEYS = new Set([
|
|
21
|
+
// Join methods
|
|
22
|
+
'OnDemandJoin',
|
|
23
|
+
'ScheduledJoin',
|
|
24
|
+
'InviteJoin',
|
|
25
|
+
'NativeInvite',
|
|
26
|
+
'InboundRouting',
|
|
27
|
+
'OutboundDial',
|
|
28
|
+
// Media tracks (directional)
|
|
29
|
+
'AudioIn',
|
|
30
|
+
'AudioOut',
|
|
31
|
+
'VideoIn',
|
|
32
|
+
'VideoOut',
|
|
33
|
+
'ScreenIn',
|
|
34
|
+
'ScreenOut',
|
|
35
|
+
// Signals & telephony
|
|
36
|
+
'SpeakerDiarization',
|
|
37
|
+
'DTMF',
|
|
38
|
+
'CallTransfer',
|
|
39
|
+
'Recording',
|
|
40
|
+
]);
|
|
41
|
+
/**
|
|
42
|
+
* Server-side `MJ: AI Bridge Providers` entity enforcing the invariants the architecture documents
|
|
43
|
+
* (see `/plans/realtime/realtime-bridges-architecture.md` §3): the engine GATES every optional
|
|
44
|
+
* driver call on the `SupportedFeatures` JSON, so a malformed / mistyped flags blob is a latent
|
|
45
|
+
* runtime bug. We catch it at write time.
|
|
46
|
+
*
|
|
47
|
+
* Invariants:
|
|
48
|
+
* 1. **`SupportedFeatures` (when dirty/non-null) is well-formed `IBridgeProviderFeatures`** — it
|
|
49
|
+
* parses as a JSON object whose every key is a known feature flag and every value is a boolean.
|
|
50
|
+
* Unknown keys (typos like `OutbondDial`) and non-boolean values (`"true"`, `1`) are rejected
|
|
51
|
+
* with a precise message. PURE helper `ValidateSupportedFeaturesJson`.
|
|
52
|
+
* 2. **`DriverClass` is non-empty** — it is the `ClassFactory` key the engine resolves the bridge
|
|
53
|
+
* driver from; a blank value silently fails resolution. (The DB column is NOT NULL but allows
|
|
54
|
+
* empty/whitespace strings, so this adds the meaningful check.) PURE helper
|
|
55
|
+
* `ValidateDriverClass`.
|
|
56
|
+
*
|
|
57
|
+
* No cross-record `RunView` is needed here — both invariants are purely intra-row, so this is the
|
|
58
|
+
* cheapest possible `ValidateAsync` (no DB round trips).
|
|
59
|
+
*/
|
|
60
|
+
let MJAIBridgeProviderEntityServer = class MJAIBridgeProviderEntityServer extends MJAIBridgeProviderEntity {
|
|
61
|
+
/** Enable async validation so these checks run on the server save path. */
|
|
62
|
+
get DefaultSkipAsyncValidation() {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
async ValidateAsync() {
|
|
66
|
+
const result = await super.ValidateAsync();
|
|
67
|
+
if (!result.Success)
|
|
68
|
+
return result;
|
|
69
|
+
// (2) DriverClass non-empty — cheap, only when new or DriverClass changed.
|
|
70
|
+
const driverDirty = this.GetFieldByName('DriverClass')?.Dirty ?? false;
|
|
71
|
+
if (!this.IsSaved || driverDirty) {
|
|
72
|
+
const driverError = ValidateDriverClass(this.DriverClass);
|
|
73
|
+
if (driverError) {
|
|
74
|
+
result.Errors.push(driverError);
|
|
75
|
+
result.Success = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// (1) SupportedFeatures JSON shape — only when new or the column changed.
|
|
79
|
+
const featuresDirty = this.GetFieldByName('SupportedFeatures')?.Dirty ?? false;
|
|
80
|
+
if (!this.IsSaved || featuresDirty) {
|
|
81
|
+
const featureErrors = ValidateSupportedFeaturesJson(this.SupportedFeatures);
|
|
82
|
+
if (featureErrors.length > 0) {
|
|
83
|
+
result.Errors.push(...featureErrors);
|
|
84
|
+
result.Success = false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
MJAIBridgeProviderEntityServer = __decorate([
|
|
91
|
+
RegisterClass(BaseEntity, 'MJ: AI Bridge Providers')
|
|
92
|
+
], MJAIBridgeProviderEntityServer);
|
|
93
|
+
export { MJAIBridgeProviderEntityServer };
|
|
94
|
+
/**
|
|
95
|
+
* PURE invariant: `DriverClass` must be a non-empty, non-whitespace string. Returns the error, or
|
|
96
|
+
* `null` when valid. (A `null`/`undefined` value is left to the sync required-field validator.)
|
|
97
|
+
*/
|
|
98
|
+
export function ValidateDriverClass(driverClass) {
|
|
99
|
+
if (driverClass == null) {
|
|
100
|
+
return null; // required-field violation is sync Validate()'s job
|
|
101
|
+
}
|
|
102
|
+
if (driverClass.trim().length === 0) {
|
|
103
|
+
return new ValidationErrorInfo('DriverClass', 'DriverClass cannot be blank — it is the ClassFactory key the bridge engine uses to resolve the driver.', driverClass, ValidationErrorType.Failure);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* PURE invariant core for `SupportedFeatures` (exported for unit tests): the raw column value must
|
|
109
|
+
* either be null/empty (unsupported — valid) or a JSON OBJECT whose keys are all known
|
|
110
|
+
* `IBridgeProviderFeatures` flags and whose values are all booleans.
|
|
111
|
+
*
|
|
112
|
+
* @param raw The raw `SupportedFeatures` column value.
|
|
113
|
+
* @returns The violations (empty array = valid).
|
|
114
|
+
*/
|
|
115
|
+
export function ValidateSupportedFeaturesJson(raw) {
|
|
116
|
+
if (raw == null || raw.trim().length === 0) {
|
|
117
|
+
return []; // omitted = "no features supported" — a valid state
|
|
118
|
+
}
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(raw);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return [buildFeaturesError('SupportedFeatures is not valid JSON.', raw)];
|
|
125
|
+
}
|
|
126
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
127
|
+
return [buildFeaturesError('SupportedFeatures must be a JSON object of feature flags (got ' + describeJsonType(parsed) + ').', raw)];
|
|
128
|
+
}
|
|
129
|
+
const errors = [];
|
|
130
|
+
const obj = parsed;
|
|
131
|
+
for (const key of Object.keys(obj)) {
|
|
132
|
+
if (!KNOWN_BRIDGE_PROVIDER_FEATURE_KEYS.has(key)) {
|
|
133
|
+
errors.push(buildFeaturesError(`SupportedFeatures contains unknown feature flag '${key}'.`, raw));
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (typeof obj[key] !== 'boolean') {
|
|
137
|
+
errors.push(buildFeaturesError(`SupportedFeatures flag '${key}' must be a boolean (got ${describeJsonType(obj[key])}).`, raw));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return errors;
|
|
141
|
+
}
|
|
142
|
+
/** Builds a `SupportedFeatures` failure with the standard source/type. */
|
|
143
|
+
function buildFeaturesError(message, value) {
|
|
144
|
+
return new ValidationErrorInfo('SupportedFeatures', message, value, ValidationErrorType.Failure);
|
|
145
|
+
}
|
|
146
|
+
/** Human-readable JSON type label used in error messages. */
|
|
147
|
+
function describeJsonType(value) {
|
|
148
|
+
if (value === null)
|
|
149
|
+
return 'null';
|
|
150
|
+
if (Array.isArray(value))
|
|
151
|
+
return 'array';
|
|
152
|
+
return typeof value;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=MJAIBridgeProviderEntityServer.server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJAIBridgeProviderEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJAIBridgeProviderEntityServer.server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ValidationErrorInfo, ValidationResult } from '@memberjunction/core';
|
|
2
|
+
import { MJAIRemoteBrowserProviderEntity } from '@memberjunction/core-entities';
|
|
3
|
+
/**
|
|
4
|
+
* The complete set of known `IRemoteBrowserProviderFeatures` keys (see the generated
|
|
5
|
+
* `MJAIRemoteBrowserProviderEntity_IRemoteBrowserProviderFeatures` interface and
|
|
6
|
+
* `metadata/entities/JSONType-interfaces/IRemoteBrowserProviderFeatures.ts`). `SupportedFeatures`
|
|
7
|
+
* is a flat object of optional booleans, so the validation is: every present key must be in this
|
|
8
|
+
* set and every value must be a boolean. Keeping the list here (rather than reflecting off a
|
|
9
|
+
* runtime value) makes the validator a PURE, dependency-free, easily-unit-tested function and
|
|
10
|
+
* gives a friendly "unknown feature flag" message that a free-form `additionalProperties:false`
|
|
11
|
+
* schema would not. Mirrors `KNOWN_BRIDGE_PROVIDER_FEATURE_KEYS`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const KNOWN_REMOTE_BROWSER_PROVIDER_FEATURE_KEYS: ReadonlySet<string>;
|
|
14
|
+
/** The valid `DefaultControlMode` values (mirrors the `RemoteBrowserControlMode` union + the DB CHECK). */
|
|
15
|
+
export declare const REMOTE_BROWSER_CONTROL_MODES: ReadonlySet<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Server-side `MJ: AI Remote Browser Providers` entity enforcing the invariants the Remote Browser
|
|
18
|
+
* channel relies on (see `/plans/realtime/realtime-bridges-architecture.md` §4d-i). The engine GATES
|
|
19
|
+
* every optional session method on the `SupportedFeatures` JSON and resolves the driver from
|
|
20
|
+
* `DriverClass`, so a malformed flags blob, a blank driver, or a control mode the backend cannot
|
|
21
|
+
* satisfy are all latent runtime bugs. We catch them at write time.
|
|
22
|
+
*
|
|
23
|
+
* Invariants (all purely intra-row — the cheapest possible `ValidateAsync`, no DB round trips):
|
|
24
|
+
* 1. **`SupportedFeatures` (when dirty/non-null) is well-formed `IRemoteBrowserProviderFeatures`** —
|
|
25
|
+
* it parses as a JSON object whose every key is a known feature flag and every value is a
|
|
26
|
+
* boolean. Unknown keys and non-boolean values are rejected. PURE `ValidateRemoteBrowserSupportedFeaturesJson`.
|
|
27
|
+
* 2. **`DriverClass` is non-empty** — the `ClassFactory` key the engine resolves the backend from.
|
|
28
|
+
* PURE `ValidateRemoteBrowserDriverClass`.
|
|
29
|
+
* 3. **`DefaultControlMode` is satisfiable by the declared features** — `Collaborative` requires
|
|
30
|
+
* `HumanTakeover`; `ViewOnly` and `Collaborative` require `LiveView`. A backend advertising a
|
|
31
|
+
* mode it cannot honor is a configuration error. PURE `ValidateControlModeAgainstFeatures`.
|
|
32
|
+
*/
|
|
33
|
+
export declare class MJAIRemoteBrowserProviderEntityServer extends MJAIRemoteBrowserProviderEntity {
|
|
34
|
+
/** Enable async validation so these checks run on the server save path. */
|
|
35
|
+
get DefaultSkipAsyncValidation(): boolean;
|
|
36
|
+
ValidateAsync(): Promise<ValidationResult>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* PURE invariant: `DriverClass` must be a non-empty, non-whitespace string. Returns the error, or
|
|
40
|
+
* `null` when valid. (A `null`/`undefined` value is left to the sync required-field validator.)
|
|
41
|
+
*/
|
|
42
|
+
export declare function ValidateRemoteBrowserDriverClass(driverClass: string | null | undefined): ValidationErrorInfo | null;
|
|
43
|
+
/**
|
|
44
|
+
* PURE invariant core for `SupportedFeatures` (exported for unit tests): the raw column value must
|
|
45
|
+
* either be null/empty (no features — valid) or a JSON OBJECT whose keys are all known
|
|
46
|
+
* `IRemoteBrowserProviderFeatures` flags and whose values are all booleans.
|
|
47
|
+
*
|
|
48
|
+
* @param raw The raw `SupportedFeatures` column value.
|
|
49
|
+
* @returns The violations (empty array = valid).
|
|
50
|
+
*/
|
|
51
|
+
export declare function ValidateRemoteBrowserSupportedFeaturesJson(raw: string | null | undefined): ValidationErrorInfo[];
|
|
52
|
+
/**
|
|
53
|
+
* PURE invariant: the `DefaultControlMode` must be satisfiable by the backend's declared
|
|
54
|
+
* `SupportedFeatures`. `Collaborative` needs `HumanTakeover`; `ViewOnly` and `Collaborative` need
|
|
55
|
+
* `LiveView`. `AgentOnly` (and an unrecognized mode, which the sync CHECK/value-list validator owns)
|
|
56
|
+
* impose no feature prerequisite here. Returns the error, or `null` when valid.
|
|
57
|
+
*
|
|
58
|
+
* @param mode The `DefaultControlMode` value.
|
|
59
|
+
* @param rawFeatures The raw `SupportedFeatures` column value.
|
|
60
|
+
* @returns The violation, or `null` when valid.
|
|
61
|
+
*/
|
|
62
|
+
export declare function ValidateControlModeAgainstFeatures(mode: string | null | undefined, rawFeatures: string | null | undefined): ValidationErrorInfo | null;
|
|
63
|
+
//# sourceMappingURL=MJAIRemoteBrowserProviderEntityServer.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJAIRemoteBrowserProviderEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJAIRemoteBrowserProviderEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,mBAAmB,EAAuB,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAEhF;;;;;;;;;GASG;AACH,eAAO,MAAM,0CAA0C,EAAE,WAAW,CAAC,MAAM,CAgBzE,CAAC;AAEH,2GAA2G;AAC3G,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,MAAM,CAA+D,CAAC;AAE7H;;;;;;;;;;;;;;;;GAgBG;AACH,qBACa,qCAAsC,SAAQ,+BAA+B;IACtF,2EAA2E;IAC3E,IAAoB,0BAA0B,IAAI,OAAO,CAExD;IAEqB,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAqCnE;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,mBAAmB,GAAG,IAAI,CAanH;AAED;;;;;;;GAOG;AACH,wBAAgB,0CAA0C,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,mBAAmB,EAAE,CA8BhH;AAED;;;;;;;;;GASG;AACH,wBAAgB,kCAAkC,CAC9C,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACvC,mBAAmB,GAAG,IAAI,CA2B5B"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BaseEntity, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { MJAIRemoteBrowserProviderEntity } from '@memberjunction/core-entities';
|
|
10
|
+
/**
|
|
11
|
+
* The complete set of known `IRemoteBrowserProviderFeatures` keys (see the generated
|
|
12
|
+
* `MJAIRemoteBrowserProviderEntity_IRemoteBrowserProviderFeatures` interface and
|
|
13
|
+
* `metadata/entities/JSONType-interfaces/IRemoteBrowserProviderFeatures.ts`). `SupportedFeatures`
|
|
14
|
+
* is a flat object of optional booleans, so the validation is: every present key must be in this
|
|
15
|
+
* set and every value must be a boolean. Keeping the list here (rather than reflecting off a
|
|
16
|
+
* runtime value) makes the validator a PURE, dependency-free, easily-unit-tested function and
|
|
17
|
+
* gives a friendly "unknown feature flag" message that a free-form `additionalProperties:false`
|
|
18
|
+
* schema would not. Mirrors `KNOWN_BRIDGE_PROVIDER_FEATURE_KEYS`.
|
|
19
|
+
*/
|
|
20
|
+
export const KNOWN_REMOTE_BROWSER_PROVIDER_FEATURE_KEYS = new Set([
|
|
21
|
+
// Control substrate & strategies
|
|
22
|
+
'RawCdpControl',
|
|
23
|
+
'NativeAIControl',
|
|
24
|
+
// Viewing & collaboration
|
|
25
|
+
'LiveView',
|
|
26
|
+
'HumanTakeover',
|
|
27
|
+
'ScreenStreaming',
|
|
28
|
+
// Operational
|
|
29
|
+
'Stealth',
|
|
30
|
+
'ProxyEgress',
|
|
31
|
+
'SessionRecording',
|
|
32
|
+
'PersistentContext',
|
|
33
|
+
'MultiTab',
|
|
34
|
+
'FileDownloads',
|
|
35
|
+
'CaptchaSolving',
|
|
36
|
+
]);
|
|
37
|
+
/** The valid `DefaultControlMode` values (mirrors the `RemoteBrowserControlMode` union + the DB CHECK). */
|
|
38
|
+
export const REMOTE_BROWSER_CONTROL_MODES = new Set(['AgentOnly', 'ViewOnly', 'Collaborative']);
|
|
39
|
+
/**
|
|
40
|
+
* Server-side `MJ: AI Remote Browser Providers` entity enforcing the invariants the Remote Browser
|
|
41
|
+
* channel relies on (see `/plans/realtime/realtime-bridges-architecture.md` §4d-i). The engine GATES
|
|
42
|
+
* every optional session method on the `SupportedFeatures` JSON and resolves the driver from
|
|
43
|
+
* `DriverClass`, so a malformed flags blob, a blank driver, or a control mode the backend cannot
|
|
44
|
+
* satisfy are all latent runtime bugs. We catch them at write time.
|
|
45
|
+
*
|
|
46
|
+
* Invariants (all purely intra-row — the cheapest possible `ValidateAsync`, no DB round trips):
|
|
47
|
+
* 1. **`SupportedFeatures` (when dirty/non-null) is well-formed `IRemoteBrowserProviderFeatures`** —
|
|
48
|
+
* it parses as a JSON object whose every key is a known feature flag and every value is a
|
|
49
|
+
* boolean. Unknown keys and non-boolean values are rejected. PURE `ValidateRemoteBrowserSupportedFeaturesJson`.
|
|
50
|
+
* 2. **`DriverClass` is non-empty** — the `ClassFactory` key the engine resolves the backend from.
|
|
51
|
+
* PURE `ValidateRemoteBrowserDriverClass`.
|
|
52
|
+
* 3. **`DefaultControlMode` is satisfiable by the declared features** — `Collaborative` requires
|
|
53
|
+
* `HumanTakeover`; `ViewOnly` and `Collaborative` require `LiveView`. A backend advertising a
|
|
54
|
+
* mode it cannot honor is a configuration error. PURE `ValidateControlModeAgainstFeatures`.
|
|
55
|
+
*/
|
|
56
|
+
let MJAIRemoteBrowserProviderEntityServer = class MJAIRemoteBrowserProviderEntityServer extends MJAIRemoteBrowserProviderEntity {
|
|
57
|
+
/** Enable async validation so these checks run on the server save path. */
|
|
58
|
+
get DefaultSkipAsyncValidation() {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
async ValidateAsync() {
|
|
62
|
+
const result = await super.ValidateAsync();
|
|
63
|
+
if (!result.Success)
|
|
64
|
+
return result;
|
|
65
|
+
// (2) DriverClass non-empty — cheap, only when new or DriverClass changed.
|
|
66
|
+
const driverDirty = this.GetFieldByName('DriverClass')?.Dirty ?? false;
|
|
67
|
+
if (!this.IsSaved || driverDirty) {
|
|
68
|
+
const driverError = ValidateRemoteBrowserDriverClass(this.DriverClass);
|
|
69
|
+
if (driverError) {
|
|
70
|
+
result.Errors.push(driverError);
|
|
71
|
+
result.Success = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// (1) SupportedFeatures JSON shape — only when new or the column changed.
|
|
75
|
+
const featuresDirty = this.GetFieldByName('SupportedFeatures')?.Dirty ?? false;
|
|
76
|
+
if (!this.IsSaved || featuresDirty) {
|
|
77
|
+
const featureErrors = ValidateRemoteBrowserSupportedFeaturesJson(this.SupportedFeatures);
|
|
78
|
+
if (featureErrors.length > 0) {
|
|
79
|
+
result.Errors.push(...featureErrors);
|
|
80
|
+
result.Success = false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// (3) DefaultControlMode must be satisfiable by the declared features — re-validate whenever
|
|
84
|
+
// either the mode or the features changed (a feature change can invalidate an existing mode).
|
|
85
|
+
const modeDirty = this.GetFieldByName('DefaultControlMode')?.Dirty ?? false;
|
|
86
|
+
if (!this.IsSaved || modeDirty || featuresDirty) {
|
|
87
|
+
const modeError = ValidateControlModeAgainstFeatures(this.DefaultControlMode, this.SupportedFeatures);
|
|
88
|
+
if (modeError) {
|
|
89
|
+
result.Errors.push(modeError);
|
|
90
|
+
result.Success = false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
MJAIRemoteBrowserProviderEntityServer = __decorate([
|
|
97
|
+
RegisterClass(BaseEntity, 'MJ: AI Remote Browser Providers')
|
|
98
|
+
], MJAIRemoteBrowserProviderEntityServer);
|
|
99
|
+
export { MJAIRemoteBrowserProviderEntityServer };
|
|
100
|
+
/**
|
|
101
|
+
* PURE invariant: `DriverClass` must be a non-empty, non-whitespace string. Returns the error, or
|
|
102
|
+
* `null` when valid. (A `null`/`undefined` value is left to the sync required-field validator.)
|
|
103
|
+
*/
|
|
104
|
+
export function ValidateRemoteBrowserDriverClass(driverClass) {
|
|
105
|
+
if (driverClass == null) {
|
|
106
|
+
return null; // required-field violation is sync Validate()'s job
|
|
107
|
+
}
|
|
108
|
+
if (driverClass.trim().length === 0) {
|
|
109
|
+
return new ValidationErrorInfo('DriverClass', 'DriverClass cannot be blank — it is the ClassFactory key the Remote Browser engine uses to resolve the backend driver.', driverClass, ValidationErrorType.Failure);
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* PURE invariant core for `SupportedFeatures` (exported for unit tests): the raw column value must
|
|
115
|
+
* either be null/empty (no features — valid) or a JSON OBJECT whose keys are all known
|
|
116
|
+
* `IRemoteBrowserProviderFeatures` flags and whose values are all booleans.
|
|
117
|
+
*
|
|
118
|
+
* @param raw The raw `SupportedFeatures` column value.
|
|
119
|
+
* @returns The violations (empty array = valid).
|
|
120
|
+
*/
|
|
121
|
+
export function ValidateRemoteBrowserSupportedFeaturesJson(raw) {
|
|
122
|
+
if (raw == null || raw.trim().length === 0) {
|
|
123
|
+
return []; // omitted = "no features supported" — a valid state
|
|
124
|
+
}
|
|
125
|
+
let parsed;
|
|
126
|
+
try {
|
|
127
|
+
parsed = JSON.parse(raw);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return [buildFeaturesError('SupportedFeatures is not valid JSON.', raw)];
|
|
131
|
+
}
|
|
132
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
133
|
+
return [buildFeaturesError('SupportedFeatures must be a JSON object of feature flags (got ' + describeJsonType(parsed) + ').', raw)];
|
|
134
|
+
}
|
|
135
|
+
const errors = [];
|
|
136
|
+
const obj = parsed;
|
|
137
|
+
for (const key of Object.keys(obj)) {
|
|
138
|
+
if (!KNOWN_REMOTE_BROWSER_PROVIDER_FEATURE_KEYS.has(key)) {
|
|
139
|
+
errors.push(buildFeaturesError(`SupportedFeatures contains unknown feature flag '${key}'.`, raw));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (typeof obj[key] !== 'boolean') {
|
|
143
|
+
errors.push(buildFeaturesError(`SupportedFeatures flag '${key}' must be a boolean (got ${describeJsonType(obj[key])}).`, raw));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return errors;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* PURE invariant: the `DefaultControlMode` must be satisfiable by the backend's declared
|
|
150
|
+
* `SupportedFeatures`. `Collaborative` needs `HumanTakeover`; `ViewOnly` and `Collaborative` need
|
|
151
|
+
* `LiveView`. `AgentOnly` (and an unrecognized mode, which the sync CHECK/value-list validator owns)
|
|
152
|
+
* impose no feature prerequisite here. Returns the error, or `null` when valid.
|
|
153
|
+
*
|
|
154
|
+
* @param mode The `DefaultControlMode` value.
|
|
155
|
+
* @param rawFeatures The raw `SupportedFeatures` column value.
|
|
156
|
+
* @returns The violation, or `null` when valid.
|
|
157
|
+
*/
|
|
158
|
+
export function ValidateControlModeAgainstFeatures(mode, rawFeatures) {
|
|
159
|
+
if (mode == null || !REMOTE_BROWSER_CONTROL_MODES.has(mode)) {
|
|
160
|
+
return null; // null / unrecognized mode is the sync value-list validator's job
|
|
161
|
+
}
|
|
162
|
+
if (mode === 'AgentOnly') {
|
|
163
|
+
return null; // no feature prerequisite
|
|
164
|
+
}
|
|
165
|
+
const features = parseFeatureFlags(rawFeatures);
|
|
166
|
+
const missing = [];
|
|
167
|
+
// ViewOnly + Collaborative both need a way for humans to watch.
|
|
168
|
+
if (features['LiveView'] !== true) {
|
|
169
|
+
missing.push('LiveView');
|
|
170
|
+
}
|
|
171
|
+
// Collaborative additionally needs humans to be able to take the wheel.
|
|
172
|
+
if (mode === 'Collaborative' && features['HumanTakeover'] !== true) {
|
|
173
|
+
missing.push('HumanTakeover');
|
|
174
|
+
}
|
|
175
|
+
if (missing.length === 0) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
return new ValidationErrorInfo('DefaultControlMode', `DefaultControlMode '${mode}' requires SupportedFeatures ${missing.map(m => `'${m}'`).join(' and ')} to be enabled.`, mode, ValidationErrorType.Failure);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Parses the raw `SupportedFeatures` JSON into a flag map for the control-mode check, tolerating
|
|
182
|
+
* malformed input (returns `{}` — invariant #1 reports the JSON shape error separately, so this
|
|
183
|
+
* check does not double-report).
|
|
184
|
+
*/
|
|
185
|
+
function parseFeatureFlags(raw) {
|
|
186
|
+
if (raw == null || raw.trim().length === 0) {
|
|
187
|
+
return {};
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const parsed = JSON.parse(raw);
|
|
191
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
192
|
+
return parsed;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// shape error is reported by ValidateRemoteBrowserSupportedFeaturesJson; treat as no flags here
|
|
197
|
+
}
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
/** Builds a `SupportedFeatures` failure with the standard source/type. */
|
|
201
|
+
function buildFeaturesError(message, value) {
|
|
202
|
+
return new ValidationErrorInfo('SupportedFeatures', message, value, ValidationErrorType.Failure);
|
|
203
|
+
}
|
|
204
|
+
/** Human-readable JSON type label used in error messages. */
|
|
205
|
+
function describeJsonType(value) {
|
|
206
|
+
if (value === null)
|
|
207
|
+
return 'null';
|
|
208
|
+
if (Array.isArray(value))
|
|
209
|
+
return 'array';
|
|
210
|
+
return typeof value;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=MJAIRemoteBrowserProviderEntityServer.server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MJAIRemoteBrowserProviderEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJAIRemoteBrowserProviderEntityServer.server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MJDuplicateRunEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJDuplicateRunEntityServer.server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAA0B,MAAM,+BAA+B,CAAC;AA4C7F,qBACa,0BAA2B,SAAQ,oBAAoB;IACnD,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAuBrC;;;OAGG;YACW,iBAAiB;
|
|
1
|
+
{"version":3,"file":"MJDuplicateRunEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJDuplicateRunEntityServer.server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAA0B,MAAM,+BAA+B,CAAC;AA4C7F,qBACa,0BAA2B,SAAQ,oBAAoB;IACnD,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAuBrC;;;OAGG;YACW,iBAAiB;IAuE/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;OAIG;IACH;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,2BAA2B;CA8BtC"}
|
|
@@ -73,12 +73,16 @@ let MJDuplicateRunEntityServer = class MJDuplicateRunEntityServer extends MJDupl
|
|
|
73
73
|
async runDetectionAsync() {
|
|
74
74
|
const startTime = Date.now();
|
|
75
75
|
try {
|
|
76
|
-
|
|
76
|
+
// Thread this entity's provider (request-scoped — each MJAPI call binds its own
|
|
77
|
+
// connection) down the detection stack instead of letting it fall back to the
|
|
78
|
+
// process-global default provider.
|
|
79
|
+
const provider = this.ProviderToUse;
|
|
80
|
+
const detector = new DuplicateRecordDetector(provider);
|
|
77
81
|
const request = new PotentialDuplicateRequest();
|
|
78
82
|
request.EntityID = this.EntityID;
|
|
79
83
|
request.ListID = this.SourceListID || undefined; // Optional — if not set, scans all entity records
|
|
80
84
|
// Find the first active entity document for this entity to get template/thresholds
|
|
81
|
-
const rv = new RunView();
|
|
85
|
+
const rv = new RunView(this.RunViewProviderToUse);
|
|
82
86
|
const edResult = await rv.RunView({
|
|
83
87
|
EntityName: 'MJ: Entity Documents',
|
|
84
88
|
ExtraFilter: `EntityID='${this.EntityID}' AND Status='Active'`,
|