@memberjunction/core-entities-server 5.51.0 → 6.1.0-edge.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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2023 MemberJunction
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,16 @@
1
+ import { ValidationResult } from '@memberjunction/core';
2
+ import { MJAPIApplicationScopeEntity } from '@memberjunction/core-entities';
3
+ /**
4
+ * Server-side `MJ: API Application Scopes` entity enforcing the same row-filter
5
+ * authoring rules as `MJAPIKeyScopeEntityServer` (plan §5.3 checks 1-6 +
6
+ * §5.6.1), scoped to the owning APPLICATION instead of an API key. Application
7
+ * ceiling-filter ENFORCEMENT is deferred to v2 (plan §9.3) — the column ships
8
+ * unused — but authoring validation applies from day one so no invalid
9
+ * configuration can accumulate before enforcement lands.
10
+ */
11
+ export declare class MJAPIApplicationScopeEntityServer extends MJAPIApplicationScopeEntity {
12
+ /** Enable async validation so the row-filter checks (which query referrers) run on Save. */
13
+ get DefaultSkipAsyncValidation(): boolean;
14
+ ValidateAsync(): Promise<ValidationResult>;
15
+ }
16
+ //# sourceMappingURL=MJAPIApplicationScopeEntityServer.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MJAPIApplicationScopeEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJAPIApplicationScopeEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAG5E;;;;;;;GAOG;AACH,qBACa,iCAAkC,SAAQ,2BAA2B;IAC9E,4FAA4F;IAC5F,IAAoB,0BAA0B,IAAI,OAAO,CAExD;IAEqB,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAyBnE"}
@@ -0,0 +1,48 @@
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 } from '@memberjunction/core';
8
+ import { RegisterClass } from '@memberjunction/global';
9
+ import { MJAPIApplicationScopeEntity } from '@memberjunction/core-entities';
10
+ import { ToValidationErrors, ValidateScopeRuleRowFilter } from './rowFilterValidation.js';
11
+ /**
12
+ * Server-side `MJ: API Application Scopes` entity enforcing the same row-filter
13
+ * authoring rules as `MJAPIKeyScopeEntityServer` (plan §5.3 checks 1-6 +
14
+ * §5.6.1), scoped to the owning APPLICATION instead of an API key. Application
15
+ * ceiling-filter ENFORCEMENT is deferred to v2 (plan §9.3) — the column ships
16
+ * unused — but authoring validation applies from day one so no invalid
17
+ * configuration can accumulate before enforcement lands.
18
+ */
19
+ let MJAPIApplicationScopeEntityServer = class MJAPIApplicationScopeEntityServer extends MJAPIApplicationScopeEntity {
20
+ /** Enable async validation so the row-filter checks (which query referrers) run on Save. */
21
+ get DefaultSkipAsyncValidation() {
22
+ return false;
23
+ }
24
+ async ValidateAsync() {
25
+ const result = await super.ValidateAsync();
26
+ const errors = await ValidateScopeRuleRowFilter({
27
+ RowId: this.IsSaved ? this.ID : '',
28
+ RuleEntityName: 'MJ: API Application Scopes',
29
+ OwnerFilterClause: `ApplicationID='${this.ApplicationID}'`,
30
+ OwnerLabel: 'application',
31
+ ScopeID: this.ScopeID,
32
+ ResourcePattern: this.ResourcePattern,
33
+ PatternType: this.PatternType,
34
+ IsDeny: this.IsDeny,
35
+ RowFilterID: this.RowFilterID
36
+ }, this.ProviderToUse, this.ContextCurrentUser);
37
+ if (errors.length > 0) {
38
+ result.Errors.push(...ToValidationErrors('RowFilterID', errors));
39
+ result.Success = false;
40
+ }
41
+ return result;
42
+ }
43
+ };
44
+ MJAPIApplicationScopeEntityServer = __decorate([
45
+ RegisterClass(BaseEntity, 'MJ: API Application Scopes')
46
+ ], MJAPIApplicationScopeEntityServer);
47
+ export { MJAPIApplicationScopeEntityServer };
48
+ //# sourceMappingURL=MJAPIApplicationScopeEntityServer.server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MJAPIApplicationScopeEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJAPIApplicationScopeEntityServer.server.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAuC,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEvF;;;;;;;GAOG;AAEI,IAAM,iCAAiC,GAAvC,MAAM,iCAAkC,SAAQ,2BAA2B;IAC9E,4FAA4F;IAC5F,IAAoB,0BAA0B;QAC1C,OAAO,KAAK,CAAC;IACjB,CAAC;IAEe,KAAK,CAAC,aAAa;QAC/B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC;QAE3C,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAC3C;YACI,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;YAClC,cAAc,EAAE,4BAA4B;YAC5C,iBAAiB,EAAE,kBAAkB,IAAI,CAAC,aAAa,GAAG;YAC1D,UAAU,EAAE,aAAa;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;SAChC,EACD,IAAI,CAAC,aAA6C,EAClD,IAAI,CAAC,kBAAkB,CAC1B,CAAC;QAEF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AA/BY,iCAAiC;IAD7C,aAAa,CAAC,UAAU,EAAE,4BAA4B,CAAC;GAC3C,iCAAiC,CA+B7C"}
@@ -0,0 +1,28 @@
1
+ import { ValidationResult } from '@memberjunction/core';
2
+ import { MJAPIKeyScopeEntity } from '@memberjunction/core-entities';
3
+ /**
4
+ * Server-side `MJ: API Key Scopes` entity enforcing the API-key row-filter
5
+ * authoring rules (plan §5.3 checks 1-6 + §5.6.1) whenever a rule carries a
6
+ * `RowFilterID` — or IS a `full_access` grant on a key that has filtered rules:
7
+ *
8
+ * 1. `ResourcePattern` must name a single EXACT entity (no `*`/`?`/`,`).
9
+ * 2. That name must resolve via `EntityByName`.
10
+ * 3. Every `{{Token}}` in the filter's text must be in the registered vocabulary.
11
+ * 4. Every column identifier in the filter's text must resolve to a real
12
+ * non-virtual field on that entity (STRICT — unknown identifiers reject).
13
+ * 5. The rule must not be `IsDeny` or `PatternType='Exclude'`.
14
+ * 6. Every OTHER referrer of the same filter record must resolve to the same
15
+ * entity (same-entity invariant — a filter carries no entity binding of its
16
+ * own).
17
+ * §5.6.1: `full_access` + row filter on the same key is invalid configuration,
18
+ * rejected in BOTH directions (filter added to a key holding full_access;
19
+ * full_access granted to a key holding a filtered rule).
20
+ *
21
+ * Unfiltered rules keep full pattern support — zero behavior change.
22
+ */
23
+ export declare class MJAPIKeyScopeEntityServer extends MJAPIKeyScopeEntity {
24
+ /** Enable async validation so the row-filter checks (which query referrers) run on Save. */
25
+ get DefaultSkipAsyncValidation(): boolean;
26
+ ValidateAsync(): Promise<ValidationResult>;
27
+ }
28
+ //# sourceMappingURL=MJAPIKeyScopeEntityServer.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MJAPIKeyScopeEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJAPIKeyScopeEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAGpE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBACa,yBAA0B,SAAQ,mBAAmB;IAC9D,4FAA4F;IAC5F,IAAoB,0BAA0B,IAAI,OAAO,CAExD;IAEqB,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAyBnE"}
@@ -0,0 +1,60 @@
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 } from '@memberjunction/core';
8
+ import { RegisterClass } from '@memberjunction/global';
9
+ import { MJAPIKeyScopeEntity } from '@memberjunction/core-entities';
10
+ import { ToValidationErrors, ValidateScopeRuleRowFilter } from './rowFilterValidation.js';
11
+ /**
12
+ * Server-side `MJ: API Key Scopes` entity enforcing the API-key row-filter
13
+ * authoring rules (plan §5.3 checks 1-6 + §5.6.1) whenever a rule carries a
14
+ * `RowFilterID` — or IS a `full_access` grant on a key that has filtered rules:
15
+ *
16
+ * 1. `ResourcePattern` must name a single EXACT entity (no `*`/`?`/`,`).
17
+ * 2. That name must resolve via `EntityByName`.
18
+ * 3. Every `{{Token}}` in the filter's text must be in the registered vocabulary.
19
+ * 4. Every column identifier in the filter's text must resolve to a real
20
+ * non-virtual field on that entity (STRICT — unknown identifiers reject).
21
+ * 5. The rule must not be `IsDeny` or `PatternType='Exclude'`.
22
+ * 6. Every OTHER referrer of the same filter record must resolve to the same
23
+ * entity (same-entity invariant — a filter carries no entity binding of its
24
+ * own).
25
+ * §5.6.1: `full_access` + row filter on the same key is invalid configuration,
26
+ * rejected in BOTH directions (filter added to a key holding full_access;
27
+ * full_access granted to a key holding a filtered rule).
28
+ *
29
+ * Unfiltered rules keep full pattern support — zero behavior change.
30
+ */
31
+ let MJAPIKeyScopeEntityServer = class MJAPIKeyScopeEntityServer extends MJAPIKeyScopeEntity {
32
+ /** Enable async validation so the row-filter checks (which query referrers) run on Save. */
33
+ get DefaultSkipAsyncValidation() {
34
+ return false;
35
+ }
36
+ async ValidateAsync() {
37
+ const result = await super.ValidateAsync();
38
+ const errors = await ValidateScopeRuleRowFilter({
39
+ RowId: this.IsSaved ? this.ID : '',
40
+ RuleEntityName: 'MJ: API Key Scopes',
41
+ OwnerFilterClause: `APIKeyID='${this.APIKeyID}'`,
42
+ OwnerLabel: 'API key',
43
+ ScopeID: this.ScopeID,
44
+ ResourcePattern: this.ResourcePattern,
45
+ PatternType: this.PatternType,
46
+ IsDeny: this.IsDeny,
47
+ RowFilterID: this.RowFilterID
48
+ }, this.ProviderToUse, this.ContextCurrentUser);
49
+ if (errors.length > 0) {
50
+ result.Errors.push(...ToValidationErrors('RowFilterID', errors));
51
+ result.Success = false;
52
+ }
53
+ return result;
54
+ }
55
+ };
56
+ MJAPIKeyScopeEntityServer = __decorate([
57
+ RegisterClass(BaseEntity, 'MJ: API Key Scopes')
58
+ ], MJAPIKeyScopeEntityServer);
59
+ export { MJAPIKeyScopeEntityServer };
60
+ //# sourceMappingURL=MJAPIKeyScopeEntityServer.server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MJAPIKeyScopeEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJAPIKeyScopeEntityServer.server.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAuC,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;GAmBG;AAEI,IAAM,yBAAyB,GAA/B,MAAM,yBAA0B,SAAQ,mBAAmB;IAC9D,4FAA4F;IAC5F,IAAoB,0BAA0B;QAC1C,OAAO,KAAK,CAAC;IACjB,CAAC;IAEe,KAAK,CAAC,aAAa;QAC/B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC;QAE3C,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAC3C;YACI,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;YAClC,cAAc,EAAE,oBAAoB;YACpC,iBAAiB,EAAE,aAAa,IAAI,CAAC,QAAQ,GAAG;YAChD,UAAU,EAAE,SAAS;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;SAChC,EACD,IAAI,CAAC,aAA6C,EAClD,IAAI,CAAC,kBAAkB,CAC1B,CAAC;QAEF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AA/BY,yBAAyB;IADrC,aAAa,CAAC,UAAU,EAAE,oBAAoB,CAAC;GACnC,yBAAyB,CA+BrC"}
@@ -0,0 +1,31 @@
1
+ import { ValidationResult } from '@memberjunction/core';
2
+ import { MJRowLevelSecurityFilterEntity } from '@memberjunction/core-entities';
3
+ /**
4
+ * Server-side `MJ: Row Level Security Filters` entity — the SECOND enforcement
5
+ * point of the row-filter validation (plan §5.3). The scope-rule subclasses
6
+ * validate `FilterText` (check 4) and the same-entity invariant (check 6)
7
+ * against the rule's entity when the RULE is saved; without an equivalent
8
+ * check when the FILTER is saved, both are trivially bypassable: attach a
9
+ * valid filter, then edit its text (or acquire a new, different-entity
10
+ * referrer) to reference anything. On a `FilterText` change — or whenever
11
+ * more than one referrer now exists — this subclass re-runs BOTH checks
12
+ * against EVERY current referrer (EntityPermission rows via their four
13
+ * `*RLSFilterID` columns, plus both API scope tables via `RowFilterID`) and
14
+ * rejects if any fails — including referrers whose entity cannot be resolved
15
+ * (fail closed). Check 6 has no single rule to anchor on here (a filter
16
+ * record itself carries no entity binding — see plan §5.1), so it anchors on
17
+ * the first referrer that DOES resolve to an entity and requires every other
18
+ * referrer to agree with it; zero or one resolvable referrer trivially
19
+ * satisfies the invariant.
20
+ *
21
+ * Note this makes the filter edit path stricter than it historically was for
22
+ * pure-EntityPermission filters, which had no such validation. That is a
23
+ * deliberate tightening: an invalid RLS filter fails closed at query time with
24
+ * an unexplainable SQL error; rejecting at save is the diagnosable version.
25
+ */
26
+ export declare class MJRowLevelSecurityFilterEntityServer extends MJRowLevelSecurityFilterEntity {
27
+ /** Enable async validation so the referrer re-validation (which queries three tables) runs on Save. */
28
+ get DefaultSkipAsyncValidation(): boolean;
29
+ ValidateAsync(): Promise<ValidationResult>;
30
+ }
31
+ //# sourceMappingURL=MJRowLevelSecurityFilterEntityServer.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MJRowLevelSecurityFilterEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJRowLevelSecurityFilterEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEvF,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAG/E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,oCAAqC,SAAQ,8BAA8B;IACpF,uGAAuG;IACvG,IAAoB,0BAA0B,IAAI,OAAO,CAExD;IAEqB,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC;CA+CnE"}
@@ -0,0 +1,84 @@
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 } from '@memberjunction/core';
8
+ import { RegisterClass } from '@memberjunction/global';
9
+ import { MJRowLevelSecurityFilterEntity } from '@memberjunction/core-entities';
10
+ import { BuildSameEntityErrors, CollectRowFilterReferrers, ToValidationErrors, ValidateRowFilterTextAgainstEntity } from './rowFilterValidation.js';
11
+ /**
12
+ * Server-side `MJ: Row Level Security Filters` entity — the SECOND enforcement
13
+ * point of the row-filter validation (plan §5.3). The scope-rule subclasses
14
+ * validate `FilterText` (check 4) and the same-entity invariant (check 6)
15
+ * against the rule's entity when the RULE is saved; without an equivalent
16
+ * check when the FILTER is saved, both are trivially bypassable: attach a
17
+ * valid filter, then edit its text (or acquire a new, different-entity
18
+ * referrer) to reference anything. On a `FilterText` change — or whenever
19
+ * more than one referrer now exists — this subclass re-runs BOTH checks
20
+ * against EVERY current referrer (EntityPermission rows via their four
21
+ * `*RLSFilterID` columns, plus both API scope tables via `RowFilterID`) and
22
+ * rejects if any fails — including referrers whose entity cannot be resolved
23
+ * (fail closed). Check 6 has no single rule to anchor on here (a filter
24
+ * record itself carries no entity binding — see plan §5.1), so it anchors on
25
+ * the first referrer that DOES resolve to an entity and requires every other
26
+ * referrer to agree with it; zero or one resolvable referrer trivially
27
+ * satisfies the invariant.
28
+ *
29
+ * Note this makes the filter edit path stricter than it historically was for
30
+ * pure-EntityPermission filters, which had no such validation. That is a
31
+ * deliberate tightening: an invalid RLS filter fails closed at query time with
32
+ * an unexplainable SQL error; rejecting at save is the diagnosable version.
33
+ */
34
+ let MJRowLevelSecurityFilterEntityServer = class MJRowLevelSecurityFilterEntityServer extends MJRowLevelSecurityFilterEntity {
35
+ /** Enable async validation so the referrer re-validation (which queries three tables) runs on Save. */
36
+ get DefaultSkipAsyncValidation() {
37
+ return false;
38
+ }
39
+ async ValidateAsync() {
40
+ const result = await super.ValidateAsync();
41
+ // Only re-validate when the text changes (or on create — a new filter has
42
+ // no referrers yet, so the loop below is a no-op there anyway).
43
+ const filterTextDirty = this.GetFieldByName('FilterText')?.Dirty ?? false;
44
+ if (this.IsSaved && !filterTextDirty) {
45
+ return result;
46
+ }
47
+ const errors = [];
48
+ const md = this.ProviderToUse;
49
+ const filterText = this.FilterText ?? '';
50
+ try {
51
+ const referrers = await CollectRowFilterReferrers(this.ID, md, this.ContextCurrentUser);
52
+ // Check 4 — FilterText's columns must resolve against every referrer's entity.
53
+ for (const referrer of referrers) {
54
+ if (!referrer.Entity) {
55
+ errors.push(`${referrer.Description} references this filter but does not resolve to an entity — the new ` +
56
+ `FilterText cannot be validated against it; rejecting (fail closed).`);
57
+ continue;
58
+ }
59
+ const validation = ValidateRowFilterTextAgainstEntity(filterText, referrer.Entity);
60
+ errors.push(...validation.errors.map(e => `${referrer.Description}: ${e}`));
61
+ }
62
+ // Check 6 — every referrer must agree on the same entity. No single referrer is
63
+ // "the rule" here (unlike the scope-rule save path), so anchor on the first
64
+ // referrer that resolves to an entity and require every other referrer to match it.
65
+ const anchor = referrers.find(r => r.Entity)?.Entity;
66
+ if (anchor) {
67
+ errors.push(...BuildSameEntityErrors(referrers, anchor));
68
+ }
69
+ }
70
+ catch (e) {
71
+ errors.push(e instanceof Error ? e.message : String(e));
72
+ }
73
+ if (errors.length > 0) {
74
+ result.Errors.push(...ToValidationErrors('FilterText', errors));
75
+ result.Success = false;
76
+ }
77
+ return result;
78
+ }
79
+ };
80
+ MJRowLevelSecurityFilterEntityServer = __decorate([
81
+ RegisterClass(BaseEntity, 'MJ: Row Level Security Filters')
82
+ ], MJRowLevelSecurityFilterEntityServer);
83
+ export { MJRowLevelSecurityFilterEntityServer };
84
+ //# sourceMappingURL=MJRowLevelSecurityFilterEntityServer.server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MJRowLevelSecurityFilterEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJRowLevelSecurityFilterEntityServer.server.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAuC,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,kCAAkC,EAAE,MAAM,uBAAuB,CAAC;AAEjJ;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEI,IAAM,oCAAoC,GAA1C,MAAM,oCAAqC,SAAQ,8BAA8B;IACpF,uGAAuG;IACvG,IAAoB,0BAA0B;QAC1C,OAAO,KAAK,CAAC;IACjB,CAAC;IAEe,KAAK,CAAC,aAAa;QAC/B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC;QAE3C,0EAA0E;QAC1E,gEAAgE;QAChE,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC;QAC1E,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,eAAe,EAAE,CAAC;YACnC,OAAO,MAAM,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,aAA6C,CAAC;QAC9D,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;QAEzC,IAAI,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,yBAAyB,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAExF,+EAA+E;YAC/E,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBAC/B,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;oBACnB,MAAM,CAAC,IAAI,CACP,GAAG,QAAQ,CAAC,WAAW,sEAAsE;wBAC7F,qEAAqE,CACxE,CAAC;oBACF,SAAS;gBACb,CAAC;gBACD,MAAM,UAAU,GAAG,kCAAkC,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACnF,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,WAAW,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAChF,CAAC;YAED,gFAAgF;YAChF,4EAA4E;YAC5E,oFAAoF;YACpF,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YACrD,IAAI,MAAM,EAAE,CAAC;gBACT,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;YAC7D,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;YAChE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AArDY,oCAAoC;IADhD,aAAa,CAAC,UAAU,EAAE,gCAAgC,CAAC;GAC/C,oCAAoC,CAqDhD"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Shared save-time validation for API-key row filters (plan §5.3 checks 1-6 +
3
+ * §5.6.1 authoring rejection).
4
+ *
5
+ * Used by three server-side entity subclasses:
6
+ * - MJAPIKeyScopeEntityServer / MJAPIApplicationScopeEntityServer — validate a
7
+ * scope rule that carries a RowFilterID at rule save.
8
+ * - MJRowLevelSecurityFilterEntityServer — re-validate a filter's FilterText
9
+ * against EVERY current referrer when the text changes (without this second
10
+ * enforcement point, rule-save validation is bypassable by editing the
11
+ * filter after attaching it).
12
+ *
13
+ * All checks fail closed: anything unresolvable rejects the save.
14
+ */
15
+ import { EntityInfo, IMetadataProvider, UserInfo, ValidationErrorInfo } from '@memberjunction/core';
16
+ /** Result of a row-filter text validation. */
17
+ export interface RowFilterTextValidationResult {
18
+ valid: boolean;
19
+ errors: string[];
20
+ }
21
+ /**
22
+ * Validates a filter's FilterText against a specific entity (plan §5.3 checks
23
+ * 3 + 4, STRICT):
24
+ * - dangerous SQL patterns (DDL/DML/EXEC, statement-terminating `;`, comment
25
+ * markers, UNION, etc.) are rejected via the shared `SQLExpressionValidator`
26
+ * in `where_clause` context — the same injection-hardening every other
27
+ * user-provided SQL fragment in MJ goes through, not a parallel definition;
28
+ * - every `{{Token}}` must be a member of the registered vocabulary;
29
+ * - every bare column identifier must resolve to a real, NON-VIRTUAL,
30
+ * non-computed field on the entity (unknown identifiers reject — no lenient
31
+ * "maybe it's a computed column" pass). `entityFields` is deliberately not
32
+ * passed to the shared validator's own (lenient, warn-only) field check —
33
+ * this function's own strict pass below is what enforces column identity;
34
+ * the shared validator here contributes dangerous-pattern blocking only.
35
+ */
36
+ export declare function ValidateRowFilterTextAgainstEntity(filterText: string, entity: EntityInfo): RowFilterTextValidationResult;
37
+ /**
38
+ * True when a ResourcePattern is a single EXACT resource name — no `*` / `?`
39
+ * wildcards and no comma-separated list (plan §5.3 check 1).
40
+ */
41
+ export declare function IsExactResourceName(pattern: string | null): pattern is string;
42
+ /** One referrer of a RowLevelSecurityFilter record, resolved to its entity. */
43
+ export interface RowFilterReferrer {
44
+ /** Human-readable description of the referring row, for error messages. */
45
+ Description: string;
46
+ /** The entity the referrer binds the filter to, or null when unresolvable. */
47
+ Entity: EntityInfo | null;
48
+ }
49
+ /**
50
+ * Collects EVERY referrer of a RowLevelSecurityFilter record (plan §5.3 check 6):
51
+ * - EntityPermission rows via their four `*RLSFilterID` columns (entity via EntityID),
52
+ * - APIKeyScope / APIApplicationScope rows via RowFilterID (entity via their
53
+ * exact ResourcePattern).
54
+ * An optional `exclude` predicate lets the row being saved skip itself.
55
+ */
56
+ export declare function CollectRowFilterReferrers(filterId: string, md: IMetadataProvider, contextUser: UserInfo, exclude?: {
57
+ entityName: string;
58
+ rowId: string;
59
+ }): Promise<RowFilterReferrer[]>;
60
+ /**
61
+ * Enforces the same-entity invariant (plan §5.1/§5.3 check 6): every referrer
62
+ * of a shared filter must resolve to the SAME entity the current save binds it
63
+ * to. A filter record carries no entity binding of its own — all entity context
64
+ * lives in the referrers — so cross-entity sharing would let one row's edit
65
+ * silently break another entity's filter. Unresolvable referrers reject too
66
+ * (fail closed).
67
+ */
68
+ export declare function BuildSameEntityErrors(referrers: RowFilterReferrer[], targetEntity: EntityInfo): string[];
69
+ /** Convenience: wraps validation failures as ValidationErrorInfo entries. */
70
+ export declare function ToValidationErrors(fieldName: string, messages: string[]): ValidationErrorInfo[];
71
+ /**
72
+ * The scope-rule fields the shared row-filter validation needs, abstracted over
73
+ * the two scope tables (key rules are owned by an API key, application rules by
74
+ * an application).
75
+ */
76
+ export interface ScopeRuleForRowFilterValidation {
77
+ /** The saved row's ID ('' for a new record — used only to exclude self from referrer checks). */
78
+ RowId: string;
79
+ /** Which scope table the rule lives in. */
80
+ RuleEntityName: 'MJ: API Key Scopes' | 'MJ: API Application Scopes';
81
+ /** SQL filter selecting ALL rules of the same owner (e.g. `APIKeyID='...'`). */
82
+ OwnerFilterClause: string;
83
+ /** 'API key' | 'application' — for error messages. */
84
+ OwnerLabel: string;
85
+ ScopeID: string;
86
+ ResourcePattern: string | null;
87
+ PatternType: string;
88
+ IsDeny: boolean;
89
+ RowFilterID: string | null;
90
+ }
91
+ /**
92
+ * Full save-time validation for a scope rule (plan §5.3 checks 1-6 and the
93
+ * §5.6.1 full_access ⊕ row-filter authoring rejection). Returns error messages;
94
+ * empty = valid. Shared by MJAPIKeyScopeEntityServer and
95
+ * MJAPIApplicationScopeEntityServer.
96
+ */
97
+ export declare function ValidateScopeRuleRowFilter(rule: ScopeRuleForRowFilterValidation, md: IMetadataProvider, contextUser: UserInfo): Promise<string[]>;
98
+ //# sourceMappingURL=rowFilterValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rowFilterValidation.d.ts","sourceRoot":"","sources":["../../src/custom/rowFilterValidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACH,UAAU,EACV,iBAAiB,EAEjB,QAAQ,EACR,mBAAmB,EAEtB,MAAM,sBAAsB,CAAC;AAiC9B,8CAA8C;AAC9C,MAAM,WAAW,6BAA6B;IAC1C,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,6BAA6B,CA4DxH;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,IAAI,MAAM,CAQ7E;AAED,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAC9B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC3C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,QAAQ,EACrB,OAAO,CAAC,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA8D9B;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,UAAU,GAAG,MAAM,EAAE,CAiBxG;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,mBAAmB,EAAE,CAE/F;AAED;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC5C,iGAAiG;IACjG,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,cAAc,EAAE,oBAAoB,GAAG,4BAA4B,CAAC;IACpE,gFAAgF;IAChF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC5C,IAAI,EAAE,+BAA+B,EACrC,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC,CAgInB"}
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Shared save-time validation for API-key row filters (plan §5.3 checks 1-6 +
3
+ * §5.6.1 authoring rejection).
4
+ *
5
+ * Used by three server-side entity subclasses:
6
+ * - MJAPIKeyScopeEntityServer / MJAPIApplicationScopeEntityServer — validate a
7
+ * scope rule that carries a RowFilterID at rule save.
8
+ * - MJRowLevelSecurityFilterEntityServer — re-validate a filter's FilterText
9
+ * against EVERY current referrer when the text changes (without this second
10
+ * enforcement point, rule-save validation is bypassable by editing the
11
+ * filter after attaching it).
12
+ *
13
+ * All checks fail closed: anything unresolvable rejects the save.
14
+ */
15
+ import { RunView, ValidationErrorInfo, ValidationErrorType } from '@memberjunction/core';
16
+ import { SQLExpressionValidator, UUIDsEqual } from '@memberjunction/global';
17
+ /**
18
+ * Registered token vocabulary for row-filter FilterText (plan §5.2). `{{User*}}`
19
+ * is a family (any scalar UserInfo property), matched by pattern; the rest are
20
+ * exact names. Underscore-led User tokens (backing fields like
21
+ * `{{User_IsMagicLinkAnonymous}}`) are deliberately NOT registered.
22
+ */
23
+ const EXACT_REGISTERED_TOKENS = new Set([
24
+ 'ScopeResourceID',
25
+ 'ScopeResourceType',
26
+ 'ActingOrganizationID',
27
+ 'ActingPersonID',
28
+ 'ActingScopeID',
29
+ 'ActingCompanyIDs'
30
+ ]);
31
+ const USER_TOKEN_PATTERN = /^User[A-Za-z][A-Za-z0-9_]*$/;
32
+ /**
33
+ * SQL keywords / operators / literals ignored by the STRICT column-reference
34
+ * check (emulating SQLExpressionValidator.checkFieldReferences, but rejecting
35
+ * instead of warning). Function names are handled separately (identifier
36
+ * followed by `(`), so this list only needs bare-word SQL vocabulary that
37
+ * legitimately appears in a WHERE-clause expression.
38
+ */
39
+ const SQL_BARE_KEYWORDS = new Set([
40
+ 'AND', 'OR', 'NOT', 'IN', 'IS', 'NULL', 'LIKE', 'BETWEEN', 'EXISTS',
41
+ 'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 'ESCAPE', 'ASC', 'DESC',
42
+ 'TRUE', 'FALSE', 'DISTINCT', 'SOME', 'ANY', 'ALL', 'AS'
43
+ ]);
44
+ /**
45
+ * Validates a filter's FilterText against a specific entity (plan §5.3 checks
46
+ * 3 + 4, STRICT):
47
+ * - dangerous SQL patterns (DDL/DML/EXEC, statement-terminating `;`, comment
48
+ * markers, UNION, etc.) are rejected via the shared `SQLExpressionValidator`
49
+ * in `where_clause` context — the same injection-hardening every other
50
+ * user-provided SQL fragment in MJ goes through, not a parallel definition;
51
+ * - every `{{Token}}` must be a member of the registered vocabulary;
52
+ * - every bare column identifier must resolve to a real, NON-VIRTUAL,
53
+ * non-computed field on the entity (unknown identifiers reject — no lenient
54
+ * "maybe it's a computed column" pass). `entityFields` is deliberately not
55
+ * passed to the shared validator's own (lenient, warn-only) field check —
56
+ * this function's own strict pass below is what enforces column identity;
57
+ * the shared validator here contributes dangerous-pattern blocking only.
58
+ */
59
+ export function ValidateRowFilterTextAgainstEntity(filterText, entity) {
60
+ const errors = [];
61
+ // 0. Dangerous-pattern check (DDL/DML/EXEC/UNION/comments/semicolons) via
62
+ // the shared validator, before any of this function's own tokenizing.
63
+ const dangerCheck = SQLExpressionValidator.Instance.validate(filterText, { context: 'where_clause' });
64
+ if (!dangerCheck.valid) {
65
+ errors.push(dangerCheck.error ?? 'FilterText failed SQL expression validation.');
66
+ }
67
+ // 1. Token vocabulary check, then strip tokens so their contents don't hit
68
+ // the identifier scan.
69
+ let stripped = filterText.replace(/\{\{(\w+)\}\}/g, (_full, token) => {
70
+ if (!EXACT_REGISTERED_TOKENS.has(token) && !USER_TOKEN_PATTERN.test(token)) {
71
+ errors.push(`Token {{${token}}} is not in the registered vocabulary ` +
72
+ `({{User*}}, {{ScopeResourceID}}, {{ScopeResourceType}}, {{ActingOrganizationID}}, ` +
73
+ `{{ActingPersonID}}, {{ActingScopeID}}, {{ActingCompanyIDs}}).`);
74
+ }
75
+ return "''"; // replace with an empty string literal so surrounding quotes stay balanced
76
+ });
77
+ // 2. Strip string literals (handles '' escapes) so their contents are not
78
+ // scanned as identifiers.
79
+ stripped = stripped.replace(/'(?:[^']|'')*'/g, '');
80
+ const writableFields = new Set(entity.Fields.filter(f => !f.IsVirtual).map(f => f.Name.toUpperCase()));
81
+ const checkIdentifier = (identifier) => {
82
+ if (!writableFields.has(identifier.toUpperCase())) {
83
+ errors.push(`Column '${identifier}' does not resolve to a real non-virtual field on entity '${entity.Name}'.`);
84
+ }
85
+ };
86
+ // 3. Bracketed identifiers ([Col]) always denote columns — check each, then
87
+ // remove them from the identifier scan.
88
+ stripped = stripped.replace(/\[([^\]]+)\]/g, (_full, identifier) => {
89
+ checkIdentifier(identifier);
90
+ return '';
91
+ });
92
+ // 4. Bare identifiers not followed by '(' (function calls are SQL functions,
93
+ // not columns) and not SQL keywords must be entity columns. Purely
94
+ // numeric literals never match (pattern requires a leading letter/_).
95
+ const identifierPattern = /\b([A-Za-z_][A-Za-z0-9_]*)\b(?!\s*\()/g;
96
+ let match;
97
+ while ((match = identifierPattern.exec(stripped)) !== null) {
98
+ const word = match[1];
99
+ if (SQL_BARE_KEYWORDS.has(word.toUpperCase())) {
100
+ continue;
101
+ }
102
+ checkIdentifier(word);
103
+ }
104
+ return { valid: errors.length === 0, errors };
105
+ }
106
+ /**
107
+ * True when a ResourcePattern is a single EXACT resource name — no `*` / `?`
108
+ * wildcards and no comma-separated list (plan §5.3 check 1).
109
+ */
110
+ export function IsExactResourceName(pattern) {
111
+ return (pattern != null &&
112
+ pattern.trim().length > 0 &&
113
+ !pattern.includes('*') &&
114
+ !pattern.includes('?') &&
115
+ !pattern.includes(','));
116
+ }
117
+ /**
118
+ * Collects EVERY referrer of a RowLevelSecurityFilter record (plan §5.3 check 6):
119
+ * - EntityPermission rows via their four `*RLSFilterID` columns (entity via EntityID),
120
+ * - APIKeyScope / APIApplicationScope rows via RowFilterID (entity via their
121
+ * exact ResourcePattern).
122
+ * An optional `exclude` predicate lets the row being saved skip itself.
123
+ */
124
+ export async function CollectRowFilterReferrers(filterId, md, contextUser, exclude) {
125
+ const rv = new RunView();
126
+ const [permissions, keyScopes, appScopes] = await rv.RunViews([
127
+ {
128
+ EntityName: 'MJ: Entity Permissions',
129
+ ExtraFilter: `ReadRLSFilterID='${filterId}' OR CreateRLSFilterID='${filterId}' OR ` +
130
+ `UpdateRLSFilterID='${filterId}' OR DeleteRLSFilterID='${filterId}'`,
131
+ Fields: ['ID', 'EntityID'],
132
+ ResultType: 'simple'
133
+ },
134
+ {
135
+ EntityName: 'MJ: API Key Scopes',
136
+ ExtraFilter: `RowFilterID='${filterId}'`,
137
+ Fields: ['ID', 'ResourcePattern'],
138
+ ResultType: 'simple'
139
+ },
140
+ {
141
+ EntityName: 'MJ: API Application Scopes',
142
+ ExtraFilter: `RowFilterID='${filterId}'`,
143
+ Fields: ['ID', 'ResourcePattern'],
144
+ ResultType: 'simple'
145
+ }
146
+ ], contextUser);
147
+ const failed = [permissions, keyScopes, appScopes].find(r => !r.Success);
148
+ if (failed) {
149
+ // Fail closed: an unreadable referrer set cannot prove the same-entity invariant.
150
+ throw new Error(`Unable to load referrers of Row Level Security Filter ${filterId}: ${failed.ErrorMessage}`);
151
+ }
152
+ const referrers = [];
153
+ for (const row of permissions.Results) {
154
+ const entity = (row.EntityID ? md.EntityByID(row.EntityID) : undefined) ?? null;
155
+ referrers.push({
156
+ Description: `Entity Permission ${row.ID} (entity ${entity?.Name ?? row.EntityID})`,
157
+ Entity: entity
158
+ });
159
+ }
160
+ const addScopeReferrers = (rows, entityName, label) => {
161
+ for (const row of rows) {
162
+ if (exclude && exclude.entityName === entityName && UUIDsEqual(exclude.rowId, row.ID)) {
163
+ continue;
164
+ }
165
+ const entity = IsExactResourceName(row.ResourcePattern) ? (md.EntityByName(row.ResourcePattern) ?? null) : null;
166
+ referrers.push({
167
+ Description: `${label} ${row.ID} (resource '${row.ResourcePattern ?? ''}')`,
168
+ Entity: entity
169
+ });
170
+ }
171
+ };
172
+ addScopeReferrers(keyScopes.Results, 'MJ: API Key Scopes', 'API Key Scope rule');
173
+ addScopeReferrers(appScopes.Results, 'MJ: API Application Scopes', 'API Application Scope rule');
174
+ return referrers;
175
+ }
176
+ /**
177
+ * Enforces the same-entity invariant (plan §5.1/§5.3 check 6): every referrer
178
+ * of a shared filter must resolve to the SAME entity the current save binds it
179
+ * to. A filter record carries no entity binding of its own — all entity context
180
+ * lives in the referrers — so cross-entity sharing would let one row's edit
181
+ * silently break another entity's filter. Unresolvable referrers reject too
182
+ * (fail closed).
183
+ */
184
+ export function BuildSameEntityErrors(referrers, targetEntity) {
185
+ const errors = [];
186
+ for (const referrer of referrers) {
187
+ if (!referrer.Entity) {
188
+ errors.push(`${referrer.Description} references the same row filter but does not resolve to an entity — ` +
189
+ `cannot prove the same-entity invariant; rejecting (fail closed).`);
190
+ }
191
+ else if (!UUIDsEqual(referrer.Entity.ID, targetEntity.ID)) {
192
+ errors.push(`${referrer.Description} binds the same row filter to entity '${referrer.Entity.Name}', but this rule ` +
193
+ `binds it to '${targetEntity.Name}'. Every referrer of a filter must resolve to the same entity — ` +
194
+ `use a separate filter record per entity.`);
195
+ }
196
+ }
197
+ return errors;
198
+ }
199
+ /** Convenience: wraps validation failures as ValidationErrorInfo entries. */
200
+ export function ToValidationErrors(fieldName, messages) {
201
+ return messages.map(m => new ValidationErrorInfo(fieldName, m, null, ValidationErrorType.Failure));
202
+ }
203
+ /**
204
+ * Full save-time validation for a scope rule (plan §5.3 checks 1-6 and the
205
+ * §5.6.1 full_access ⊕ row-filter authoring rejection). Returns error messages;
206
+ * empty = valid. Shared by MJAPIKeyScopeEntityServer and
207
+ * MJAPIApplicationScopeEntityServer.
208
+ */
209
+ export async function ValidateScopeRuleRowFilter(rule, md, contextUser) {
210
+ const rv = new RunView();
211
+ // Resolve the full_access scope ID (needed for §5.6.1 in both directions).
212
+ const fullAccessResult = await rv.RunView({
213
+ EntityName: 'MJ: API Scopes',
214
+ ExtraFilter: `FullPath='full_access'`,
215
+ Fields: ['ID'],
216
+ ResultType: 'simple'
217
+ }, contextUser);
218
+ if (!fullAccessResult.Success) {
219
+ return [`Unable to resolve the full_access scope: ${fullAccessResult.ErrorMessage}. Rejecting (fail closed).`];
220
+ }
221
+ const fullAccessScopeId = fullAccessResult.Results[0]?.ID?.toLowerCase();
222
+ const isFullAccessGrant = fullAccessScopeId != null && rule.ScopeID?.toLowerCase() === fullAccessScopeId && !rule.IsDeny;
223
+ // Nothing row-filter-related on this rule and it is not a full_access grant → nothing to check.
224
+ if (rule.RowFilterID == null && !isFullAccessGrant) {
225
+ return [];
226
+ }
227
+ // Sibling rules of the same owner (for §5.6.1 both directions).
228
+ const siblingsResult = await rv.RunView({
229
+ EntityName: rule.RuleEntityName,
230
+ ExtraFilter: rule.OwnerFilterClause,
231
+ Fields: ['ID', 'ScopeID', 'RowFilterID', 'IsDeny'],
232
+ ResultType: 'simple'
233
+ }, contextUser);
234
+ if (!siblingsResult.Success) {
235
+ return [`Unable to load the ${rule.OwnerLabel}'s other scope rules: ${siblingsResult.ErrorMessage}. Rejecting (fail closed).`];
236
+ }
237
+ const siblings = siblingsResult.Results.filter(s => s.ID.toLowerCase() !== rule.RowId.toLowerCase());
238
+ const errors = [];
239
+ // §5.6.1 — a full_access grant on an owner that has (or is getting) a filtered rule.
240
+ if (isFullAccessGrant) {
241
+ const filteredSibling = siblings.find(s => s.RowFilterID != null);
242
+ if (rule.RowFilterID != null) {
243
+ errors.push(`Cannot set a row filter on a full_access grant: full_access is unrestricted by definition, so a row ` +
244
+ `filter alongside it is incoherent. Remove the full_access grant or the row filter — or split the ` +
245
+ `${rule.OwnerLabel}'s access across two keys.`);
246
+ }
247
+ if (filteredSibling) {
248
+ errors.push(`Cannot grant full_access to this ${rule.OwnerLabel}: scope rule ${filteredSibling.ID} already carries a ` +
249
+ `row filter, and full_access is unrestricted by definition — one of the two would be a lie about what ` +
250
+ `the ${rule.OwnerLabel} can reach. Remove the row filter or do not grant full_access (split the key).`);
251
+ }
252
+ }
253
+ if (rule.RowFilterID == null) {
254
+ return errors;
255
+ }
256
+ // §5.6.1 (other direction) — setting a row filter while the owner holds a full_access grant.
257
+ if (!isFullAccessGrant && fullAccessScopeId != null) {
258
+ const fullAccessSibling = siblings.find(s => s.ScopeID?.toLowerCase() === fullAccessScopeId && !s.IsDeny);
259
+ if (fullAccessSibling) {
260
+ errors.push(`Cannot set a row filter on this scope rule: the ${rule.OwnerLabel} also holds a full_access grant ` +
261
+ `(rule ${fullAccessSibling.ID}), which is unrestricted by definition. Remove the full_access grant or ` +
262
+ `the row filter.`);
263
+ }
264
+ }
265
+ // §5.3 check 5 — a filter narrows an ALLOW; deny/Exclude rules cannot carry one.
266
+ if (rule.IsDeny) {
267
+ errors.push(`A row filter cannot be attached to a deny rule — a filter narrows an allow; attaching it to a deny has no coherent meaning.`);
268
+ }
269
+ if (rule.PatternType === 'Exclude') {
270
+ errors.push(`A row filter cannot be attached to an Exclude rule — a filter narrows an allow; attaching it to an exclusion has no coherent meaning.`);
271
+ }
272
+ // §5.3 checks 1-2 — exact, resolvable entity.
273
+ if (!IsExactResourceName(rule.ResourcePattern)) {
274
+ errors.push(`A scope rule with a row filter must name a single exact entity in ResourcePattern — no '*' or '?' ` +
275
+ `wildcards and no comma-separated lists (current value: '${rule.ResourcePattern ?? ''}').`);
276
+ return errors; // no entity to validate the filter against
277
+ }
278
+ const entity = md.EntityByName(rule.ResourcePattern);
279
+ if (!entity) {
280
+ errors.push(`ResourcePattern '${rule.ResourcePattern}' does not resolve to an entity.`);
281
+ return errors;
282
+ }
283
+ // §5.3 checks 3-4 — the filter must exist and its text must pass the strict token/column validation.
284
+ const filterResult = await rv.RunView({
285
+ EntityName: 'MJ: Row Level Security Filters',
286
+ ExtraFilter: `ID='${rule.RowFilterID}'`,
287
+ Fields: ['ID', 'Name', 'FilterText'],
288
+ ResultType: 'simple'
289
+ }, contextUser);
290
+ if (!filterResult.Success) {
291
+ errors.push(`Unable to load Row Level Security Filter ${rule.RowFilterID}: ${filterResult.ErrorMessage}. Rejecting (fail closed).`);
292
+ return errors;
293
+ }
294
+ const filter = filterResult.Results[0];
295
+ if (!filter) {
296
+ errors.push(`Row Level Security Filter ${rule.RowFilterID} does not exist.`);
297
+ return errors;
298
+ }
299
+ if (!filter.FilterText || filter.FilterText.trim().length === 0) {
300
+ errors.push(`Row Level Security Filter '${filter.Name}' has no FilterText — an empty filter cannot restrict anything and is rejected (fail closed).`);
301
+ return errors;
302
+ }
303
+ const textValidation = ValidateRowFilterTextAgainstEntity(filter.FilterText, entity);
304
+ errors.push(...textValidation.errors.map(e => `Filter '${filter.Name}': ${e}`));
305
+ // §5.3 check 6 — same-entity invariant across every other referrer.
306
+ try {
307
+ const referrers = await CollectRowFilterReferrers(rule.RowFilterID, md, contextUser, {
308
+ entityName: rule.RuleEntityName,
309
+ rowId: rule.RowId
310
+ });
311
+ errors.push(...BuildSameEntityErrors(referrers, entity));
312
+ }
313
+ catch (e) {
314
+ errors.push(e instanceof Error ? e.message : String(e));
315
+ }
316
+ return errors;
317
+ }
318
+ //# sourceMappingURL=rowFilterValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rowFilterValidation.js","sourceRoot":"","sources":["../../src/custom/rowFilterValidation.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -35,6 +35,10 @@ export * from './custom/MJAISkillEntityServer.server.js';
35
35
  export * from './custom/MJMLTrainingPipelineEntityServer.server.js';
36
36
  export * from './custom/MJUserRoutineEntityServer.server.js';
37
37
  export * from './custom/MJUserRoutineRecipientEntityServer.server.js';
38
+ export * from './custom/MJAPIKeyScopeEntityServer.server.js';
39
+ export * from './custom/MJAPIApplicationScopeEntityServer.server.js';
40
+ export * from './custom/MJRowLevelSecurityFilterEntityServer.server.js';
41
+ export * from './custom/rowFilterValidation.js';
38
42
  export * from './custom/json-schema-lite.js';
39
43
  export * from './custom/IntegrationLLMPKCallback.js';
40
44
  export * from './custom/util.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wCAAwC,CAAC;AACvD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,kDAAkD,CAAC;AACjE,cAAc,4DAA4D,CAAC;AAC3E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qCAAqC,CAAC;AACpD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,kDAAkD,CAAC;AACjE,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,qDAAqD,CAAC;AACpE,cAAc,uDAAuD,CAAC;AACtE,cAAc,oDAAoD,CAAC;AACnE,cAAc,+DAA+D,CAAC;AAC9E,cAAc,uDAAuD,CAAC;AACtE,cAAc,iDAAiD,CAAC;AAChE,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AACjE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oDAAoD,CAAC;AACnE,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAE9B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yCAAyC,CAAC;AAAA,cAAc,+CAA+C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wCAAwC,CAAC;AACvD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,kDAAkD,CAAC;AACjE,cAAc,4DAA4D,CAAC;AAC3E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qCAAqC,CAAC;AACpD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,kDAAkD,CAAC;AACjE,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,qDAAqD,CAAC;AACpE,cAAc,uDAAuD,CAAC;AACtE,cAAc,oDAAoD,CAAC;AACnE,cAAc,+DAA+D,CAAC;AAC9E,cAAc,uDAAuD,CAAC;AACtE,cAAc,iDAAiD,CAAC;AAChE,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AACjE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oDAAoD,CAAC;AACnE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mDAAmD,CAAC;AAClE,cAAc,sDAAsD,CAAC;AACrE,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAE9B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yCAAyC,CAAC;AAAA,cAAc,+CAA+C,CAAC"}
package/dist/index.js CHANGED
@@ -35,6 +35,10 @@ export * from './custom/MJAISkillEntityServer.server.js';
35
35
  export * from './custom/MJMLTrainingPipelineEntityServer.server.js';
36
36
  export * from './custom/MJUserRoutineEntityServer.server.js';
37
37
  export * from './custom/MJUserRoutineRecipientEntityServer.server.js';
38
+ export * from './custom/MJAPIKeyScopeEntityServer.server.js';
39
+ export * from './custom/MJAPIApplicationScopeEntityServer.server.js';
40
+ export * from './custom/MJRowLevelSecurityFilterEntityServer.server.js';
41
+ export * from './custom/rowFilterValidation.js';
38
42
  export * from './custom/json-schema-lite.js';
39
43
  export * from './custom/IntegrationLLMPKCallback.js';
40
44
  export * from './custom/util.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wCAAwC,CAAC;AACvD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,kDAAkD,CAAC;AACjE,cAAc,4DAA4D,CAAC;AAC3E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qCAAqC,CAAC;AACpD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,kDAAkD,CAAC;AACjE,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,qDAAqD,CAAC;AACpE,cAAc,uDAAuD,CAAC;AACtE,cAAc,oDAAoD,CAAC;AACnE,cAAc,+DAA+D,CAAC;AAC9E,cAAc,uDAAuD,CAAC;AACtE,cAAc,iDAAiD,CAAC;AAChE,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AACjE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oDAAoD,CAAC;AACnE,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAE9B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yCAAyC,CAAC;AAAA,cAAc,+CAA+C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wCAAwC,CAAC;AACvD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,kDAAkD,CAAC;AACjE,cAAc,4DAA4D,CAAC;AAC3E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,qCAAqC,CAAC;AACpD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,kDAAkD,CAAC;AACjE,cAAc,uCAAuC,CAAC;AACtD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,gDAAgD,CAAC;AAC/D,cAAc,qDAAqD,CAAC;AACpE,cAAc,uDAAuD,CAAC;AACtE,cAAc,oDAAoD,CAAC;AACnE,cAAc,+DAA+D,CAAC;AAC9E,cAAc,uDAAuD,CAAC;AACtE,cAAc,iDAAiD,CAAC;AAChE,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AACjE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oDAAoD,CAAC;AACnE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mDAAmD,CAAC;AAClE,cAAc,sDAAsD,CAAC;AACrE,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,eAAe,CAAC;AAE9B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yCAAyC,CAAC;AAAA,cAAc,+CAA+C,CAAC"}
package/package.json CHANGED
@@ -1,19 +1,13 @@
1
1
  {
2
2
  "name": "@memberjunction/core-entities-server",
3
3
  "type": "module",
4
- "version": "5.51.0",
4
+ "version": "6.1.0-edge.0",
5
5
  "description": "Entity subclasses for the metadata layer of MemberJunction that are only used on server side applications like MJAPI, distributed as part of each release of MemberJunction",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "/dist"
10
10
  ],
11
- "scripts": {
12
- "start": "ts-node-dev src/index.ts",
13
- "build": "tsc && tsc-alias -f",
14
- "test": "vitest run",
15
- "test:watch": "vitest"
16
- },
17
11
  "author": "MemberJunction.com",
18
12
  "license": "ISC",
19
13
  "devDependencies": {
@@ -21,30 +15,30 @@
21
15
  "typescript": "^5.9.3"
22
16
  },
23
17
  "dependencies": {
24
- "@memberjunction/actions-base": "5.51.0",
25
- "@memberjunction/ai": "5.51.0",
26
- "@memberjunction/ai-core-plus": "5.51.0",
27
- "@memberjunction/ai-engine-base": "5.51.0",
28
- "@memberjunction/ai-prompts": "5.51.0",
29
- "@memberjunction/ai-provider-bundle": "5.51.0",
30
- "@memberjunction/ai-vector-dupe": "5.51.0",
31
- "@memberjunction/ai-vectordb": "5.51.0",
32
- "@memberjunction/ai-vectors-memory": "5.51.0",
33
- "@memberjunction/aiengine": "5.51.0",
34
- "@memberjunction/core": "5.51.0",
35
- "@memberjunction/core-entities": "5.51.0",
36
- "@memberjunction/predictive-studio-core": "5.51.0",
37
- "@memberjunction/doc-utils": "5.51.0",
38
- "@memberjunction/generic-database-provider": "5.51.0",
39
- "@memberjunction/global": "5.51.0",
40
- "@memberjunction/integration-engine": "5.51.0",
41
- "@memberjunction/integration-pk-classifier": "5.51.0",
42
- "@memberjunction/scheduling-engine": "5.51.0",
43
- "@memberjunction/sql-converter": "5.51.0",
44
- "@memberjunction/sql-dialect": "5.51.0",
45
- "@memberjunction/sql-parser": "5.51.0",
46
- "@memberjunction/sqlserver-dataprovider": "5.51.0",
47
- "@memberjunction/tag-engine": "5.51.0",
18
+ "@memberjunction/actions-base": "6.1.0-edge.0",
19
+ "@memberjunction/ai": "6.1.0-edge.0",
20
+ "@memberjunction/ai-core-plus": "6.1.0-edge.0",
21
+ "@memberjunction/ai-engine-base": "6.1.0-edge.0",
22
+ "@memberjunction/ai-prompts": "6.1.0-edge.0",
23
+ "@memberjunction/ai-provider-bundle": "6.1.0-edge.0",
24
+ "@memberjunction/ai-vector-dupe": "6.1.0-edge.0",
25
+ "@memberjunction/ai-vectordb": "6.1.0-edge.0",
26
+ "@memberjunction/ai-vectors-memory": "6.1.0-edge.0",
27
+ "@memberjunction/aiengine": "6.1.0-edge.0",
28
+ "@memberjunction/core": "6.1.0-edge.0",
29
+ "@memberjunction/core-entities": "6.1.0-edge.0",
30
+ "@memberjunction/predictive-studio-core": "6.1.0-edge.0",
31
+ "@memberjunction/doc-utils": "6.1.0-edge.0",
32
+ "@memberjunction/generic-database-provider": "6.1.0-edge.0",
33
+ "@memberjunction/global": "6.1.0-edge.0",
34
+ "@memberjunction/integration-engine": "6.1.0-edge.0",
35
+ "@memberjunction/integration-pk-classifier": "6.1.0-edge.0",
36
+ "@memberjunction/scheduling-engine": "6.1.0-edge.0",
37
+ "@memberjunction/sql-converter": "6.1.0-edge.0",
38
+ "@memberjunction/sql-dialect": "6.1.0-edge.0",
39
+ "@memberjunction/sql-parser": "6.1.0-edge.0",
40
+ "@memberjunction/sqlserver-dataprovider": "6.1.0-edge.0",
41
+ "@memberjunction/tag-engine": "6.1.0-edge.0",
48
42
  "nunjucks": "3.2.4",
49
43
  "rxjs": "~7.8.1",
50
44
  "zod": "^3.25.0"
@@ -52,5 +46,11 @@
52
46
  "repository": {
53
47
  "type": "git",
54
48
  "url": "https://github.com/MemberJunction/MJ"
49
+ },
50
+ "scripts": {
51
+ "start": "ts-node-dev src/index.ts",
52
+ "build": "tsc && tsc-alias -f",
53
+ "test": "vitest run",
54
+ "test:watch": "vitest"
55
55
  }
56
- }
56
+ }