@mj-biz-apps/contracts-entities 0.1.1 → 0.3.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.
@@ -57,6 +57,26 @@ export declare function IsSameContractLevel(myParentID: string | null | undefine
57
57
  * provision is one problem rather than two.
58
58
  */
59
59
  export declare function FindDuplicateProvisionIDs(provisionIDs: readonly unknown[]): string[];
60
+ /**
61
+ * R-12 — `CK_Contract_Dates` as a predicate: is this pair of dates definitely out of order?
62
+ *
63
+ * MIRRORS THE CONSTRAINT EXACTLY, which is `EndDate IS NULL OR EffectiveDate IS NULL OR EndDate >=
64
+ * EffectiveDate`. So this returns true ONLY when both dates are readable and the end really does fall
65
+ * before the start. Three consequences worth stating, because each is a way the obvious version would
66
+ * be wrong:
67
+ *
68
+ * * EQUAL DATES PASS. A contract that starts and ends on the same day is a real one-day agreement.
69
+ * Refusing it would reject rows the database accepts — worse than the error being replaced.
70
+ * * EITHER MISSING PASSES. Half-filled dates are the normal state of a form mid-edit, and the
71
+ * constraint itself allows them.
72
+ * * UNREADABLE PASSES. Typed `Date | null`, but a form binding can put a string here, and comparing
73
+ * a string to a Date compares nonsense rather than throwing. Anything that will not parse is
74
+ * treated as "not yet known" rather than as an error on a field still being typed.
75
+ *
76
+ * Exported and pure so it can be asserted directly, the way `IsSameContractLevel` above is — not
77
+ * restated in a test, which is a copy that can drift from the rule it claims to check.
78
+ */
79
+ export declare function IsEndBeforeEffective(effectiveDate: unknown, endDate: unknown): boolean;
60
80
  export declare class ContractEntity extends mjBizAppsContractsContractEntity {
61
81
  /**
62
82
  * `HasModifications` is MONOTONIC: it must be true when modification rows exist, and it is never
@@ -73,6 +93,22 @@ export declare class ContractEntity extends mjBizAppsContractsContractEntity {
73
93
  * on a contract whose paper really was negotiated. A human sets it false, or it stays true.
74
94
  */
75
95
  Validate(): ValidationResult;
96
+ /**
97
+ * R-12 — `CK_Contract_Dates` in words, before the database says it in SQL.
98
+ *
99
+ * The constraint has always existed, so the data was never at risk; what the user got was the raw
100
+ * violation text naming a constraint and no field. This runs in the browser AND on the server
101
+ * (`Validate()` is shared), so the message arrives on the End Date field before a save is attempted.
102
+ *
103
+ * THE PREDICATE MIRRORS THE CONSTRAINT EXACTLY — `EndDate IS NULL OR EffectiveDate IS NULL OR
104
+ * EndDate >= EffectiveDate`. Both-null and either-null pass, and equal dates pass: a contract that
105
+ * starts and ends on the same day is a real one-day agreement, and refusing it here would reject
106
+ * rows the database accepts, which is worse than the error it replaces.
107
+ *
108
+ * The constraint STAYS. This is the friendly path, not the enforcement: a write that reaches the
109
+ * table by some other route still meets the check.
110
+ */
111
+ private refuseEndBeforeEffective;
76
112
  /**
77
113
  * R-10 — two modifications of the SAME provision, caught among the rows in hand.
78
114
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ContractEntity.d.ts","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAwD,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AAEjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAM5H;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE,CAWpF;AAED,qBACa,cAAe,SAAQ,gCAAgC;IAChE;;;;;;;;;;;;;OAaG;IACa,QAAQ,IAAI,gBAAgB;IAuB5C;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kCAAkC;IAkB1C;;;;;;;;;;;;;;;;;;;OAmBG;IACH;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,4BAA4B;IAmBpC;;;;OAIG;IACH,SAAS,CAAC,uBAAuB,UAAS;IAE1C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IAIjC;;;;;;;;;;;OAWG;IACI,SAAS,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI;CAgBpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
1
+ {"version":3,"file":"ContractEntity.d.ts","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAwD,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE9G,OAAO,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AAEjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAM5H;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE,CAWpF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAUtF;AAED,qBACa,cAAe,SAAQ,gCAAgC;IAChE;;;;;;;;;;;;;OAaG;IACa,QAAQ,IAAI,gBAAgB;IAwB5C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,wBAAwB;IAchC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kCAAkC;IAkB1C;;;;;;;;;;;;;;;;;;;OAmBG;IACH;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,4BAA4B;IAmBpC;;;;OAIG;IACH,SAAS,CAAC,uBAAuB,UAAS;IAE1C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IAIjC;;;;;;;;;;;OAWG;IACI,SAAS,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI;CAgBpD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -85,6 +85,38 @@ export function FindDuplicateProvisionIDs(provisionIDs) {
85
85
  }
86
86
  return duplicates;
87
87
  }
88
+ /**
89
+ * R-12 — `CK_Contract_Dates` as a predicate: is this pair of dates definitely out of order?
90
+ *
91
+ * MIRRORS THE CONSTRAINT EXACTLY, which is `EndDate IS NULL OR EffectiveDate IS NULL OR EndDate >=
92
+ * EffectiveDate`. So this returns true ONLY when both dates are readable and the end really does fall
93
+ * before the start. Three consequences worth stating, because each is a way the obvious version would
94
+ * be wrong:
95
+ *
96
+ * * EQUAL DATES PASS. A contract that starts and ends on the same day is a real one-day agreement.
97
+ * Refusing it would reject rows the database accepts — worse than the error being replaced.
98
+ * * EITHER MISSING PASSES. Half-filled dates are the normal state of a form mid-edit, and the
99
+ * constraint itself allows them.
100
+ * * UNREADABLE PASSES. Typed `Date | null`, but a form binding can put a string here, and comparing
101
+ * a string to a Date compares nonsense rather than throwing. Anything that will not parse is
102
+ * treated as "not yet known" rather than as an error on a field still being typed.
103
+ *
104
+ * Exported and pure so it can be asserted directly, the way `IsSameContractLevel` above is — not
105
+ * restated in a test, which is a copy that can drift from the rule it claims to check.
106
+ */
107
+ export function IsEndBeforeEffective(effectiveDate, endDate) {
108
+ const at = (v) => {
109
+ if (v === null || v === undefined || v === '')
110
+ return null;
111
+ const t = v instanceof Date ? v.getTime() : new Date(v).getTime();
112
+ return Number.isFinite(t) ? t : null;
113
+ };
114
+ const effective = at(effectiveDate);
115
+ const end = at(endDate);
116
+ if (effective === null || end === null)
117
+ return false;
118
+ return end < effective;
119
+ }
88
120
  let ContractEntity = class ContractEntity extends mjBizAppsContractsContractEntity {
89
121
  constructor() {
90
122
  super(...arguments);
@@ -119,8 +151,30 @@ let ContractEntity = class ContractEntity extends mjBizAppsContractsContractEnti
119
151
  `the agreement really is standard.`, this.HasModifications, ValidationErrorType.Failure));
120
152
  }
121
153
  this.refuseDuplicateStagedModifications(result);
154
+ this.refuseEndBeforeEffective(result);
122
155
  return result;
123
156
  }
157
+ /**
158
+ * R-12 — `CK_Contract_Dates` in words, before the database says it in SQL.
159
+ *
160
+ * The constraint has always existed, so the data was never at risk; what the user got was the raw
161
+ * violation text naming a constraint and no field. This runs in the browser AND on the server
162
+ * (`Validate()` is shared), so the message arrives on the End Date field before a save is attempted.
163
+ *
164
+ * THE PREDICATE MIRRORS THE CONSTRAINT EXACTLY — `EndDate IS NULL OR EffectiveDate IS NULL OR
165
+ * EndDate >= EffectiveDate`. Both-null and either-null pass, and equal dates pass: a contract that
166
+ * starts and ends on the same day is a real one-day agreement, and refusing it here would reject
167
+ * rows the database accepts, which is worse than the error it replaces.
168
+ *
169
+ * The constraint STAYS. This is the friendly path, not the enforcement: a write that reaches the
170
+ * table by some other route still meets the check.
171
+ */
172
+ refuseEndBeforeEffective(result) {
173
+ if (!IsEndBeforeEffective(this.EffectiveDate, this.EndDate))
174
+ return;
175
+ result.Success = false;
176
+ result.Errors.push(new ValidationErrorInfo('EndDate', 'End Date must be on or after the Effective Date.', this.EndDate, ValidationErrorType.Failure));
177
+ }
124
178
  /**
125
179
  * R-10 — two modifications of the SAME provision, caught among the rows in hand.
126
180
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ContractEntity.js","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ContractEntity.js","sourceRoot":"","sources":["../src/ContractEntity.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -27,4 +27,6 @@ export * from './required-field-prose.js';
27
27
  export * from './ContractTypeEntity.js';
28
28
  export * from './value-list-validation.js';
29
29
  export * from './contract-state.js';
30
+ /** Which contracts the Supersedes picker may offer, and how it names them (#28 item 4). */
31
+ export * from './supersede-candidates.js';
30
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAc,+BAA+B,CAAC;AAI9C,cAAc,kBAAkB,CAAC;AAIjC,cAAc,sCAAsC,CAAC;AAIrD,cAAc,mCAAmC,CAAC;AAIlD,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AAIvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AAMxC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAc,+BAA+B,CAAC;AAI9C,cAAc,kBAAkB,CAAC;AAIjC,cAAc,sCAAsC,CAAC;AAIrD,cAAc,mCAAmC,CAAC;AAIlD,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AAIvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AAMxC,cAAc,kBAAkB,CAAC;AAEjC,2FAA2F;AAC3F,cAAc,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -41,6 +41,8 @@ export * from './value-list-validation.js';
41
41
  * would contradict the form on screen — and because StateSQL() is what the migration's CASE is checked
42
42
  * against by contract-state.test.ts. */
43
43
  export * from './contract-state.js';
44
+ /** Which contracts the Supersedes picker may offer, and how it names them (#28 item 4). */
45
+ export * from './supersede-candidates.js';
44
46
  /* NOTE on `src/generated/remote_operations.ts`: CodeGen writes it, and it is deliberately NOT
45
47
  * re-exported. v2 ships zero remote operations (plan §6.3), so every symbol in that file is an
46
48
  * MJ-CORE operation (AISkill, PredictiveStudio, TaskGraph, …) emitted into every app's file
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAc,+BAA+B,CAAC;AAE9C;kFACkF;AAClF,cAAc,kBAAkB,CAAC;AAEjC;qGACqG;AACrG,cAAc,sCAAsC,CAAC;AAErD;wEACwE;AACxE,cAAc,mCAAmC,CAAC;AAElD;wGACwG;AACxG,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AAEvC;2FAC2F;AAC3F,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AAExC;;;wCAGwC;AACxC,cAAc,kBAAkB,CAAC;AAEjC;;;;oFAIoF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAc,+BAA+B,CAAC;AAE9C;kFACkF;AAClF,cAAc,kBAAkB,CAAC;AAEjC;qGACqG;AACrG,cAAc,sCAAsC,CAAC;AAErD;wEACwE;AACxE,cAAc,mCAAmC,CAAC;AAElD;wGACwG;AACxG,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AAEvC;2FAC2F;AAC3F,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AAExC;;;wCAGwC;AACxC,cAAc,kBAAkB,CAAC;AAEjC,2FAA2F;AAC3F,cAAc,wBAAwB,CAAC;AAEvC;;;;oFAIoF"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @fileoverview Which contracts the Supersedes picker may offer, and how it names them.
3
+ *
4
+ * contracts#28 item 4 states the rule as four conditions, all of which must hold. A candidate is a
5
+ * contract that is:
6
+ *
7
+ * 1. the SAME CUSTOMER as this contract — {@link SameCustomerClause}
8
+ * 2. at the SAME LEVEL — `IsSameContractLevel`, in `ContractEntity.ts`, where it already lived
9
+ * 3. NOT already superseded — a plain `SupersededByContractID IS NULL`
10
+ * 4. not this contract — a plain `ID <> …`
11
+ *
12
+ * WHY THESE LIVE HERE AND NOT ON THE PANEL. `supersede.panel.ts` is an Angular component, so anything
13
+ * declared inside it can only be asserted by standing up Angular's DI — importing it from a unit test
14
+ * fails with "the injectable 'PlatformLocation' needs to be compiled using the JIT compiler". Rules 3
15
+ * and 4 have nothing to decide, but 1 and the label format both do, and both are stated as final in
16
+ * the issue, which makes them worth pinning. Rule 2 was already here for the same reason.
17
+ *
18
+ * The panel still owns the QUERY. These own the decisions inside it.
19
+ *
20
+ * @module @mj-biz-apps/contracts-entities
21
+ */
22
+ /**
23
+ * The same-customer clause — rule 1 — or one that deliberately matches nothing.
24
+ *
25
+ * A re-papering replaces one customer's agreement with another agreement for THAT customer. Offering
26
+ * another customer's contract is not merely untidy: linking it points one organisation's lineage at
27
+ * another's, so the successor's terms read as governing a party that never signed them, and the
28
+ * predecessor's customer loses the trail to whatever replaced their agreement.
29
+ *
30
+ * AN ABSENT CUSTOMER YIELDS NOTHING, NOT EVERYTHING, and the direction is the whole point.
31
+ * `CustomerOrganizationID` is NOT NULL on Contract, so a missing value never means "this contract has
32
+ * no customer" — it means the record could not be read. Widening to every contract at that moment
33
+ * would offer the whole book precisely when the caller knows least, which is the failure rule 1
34
+ * exists to prevent.
35
+ */
36
+ export declare function SameCustomerClause(customerID: string | null | undefined): string;
37
+ /**
38
+ * The option label: `<ContractNumber> — <Description>`, falling back to the contract type.
39
+ *
40
+ * Quoted as final in the issue. The description is what a person recognises — "Platform licence, 2026
41
+ * renewal" — while the type is a category several contracts share, so a list labelled by type reads
42
+ * as several copies of the same row.
43
+ *
44
+ * The description is TRIMMED before the emptiness test, because blank descriptions are common on
45
+ * imported records and a label reading `CTR-0007 — ` looks like missing data rather than an
46
+ * uncategorised agreement. The second fallback to "Contract" exists so a row with neither can never
47
+ * render a dangling dash.
48
+ */
49
+ export declare function ContractOptionLabel(c: {
50
+ ContractNumber: string;
51
+ ContractType: string | null;
52
+ Description: string | null;
53
+ }): string;
54
+ //# sourceMappingURL=supersede-candidates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supersede-candidates.d.ts","sourceRoot":"","sources":["../src/supersede-candidates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAMhF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,MAAM,CAGT"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @fileoverview Which contracts the Supersedes picker may offer, and how it names them.
3
+ *
4
+ * contracts#28 item 4 states the rule as four conditions, all of which must hold. A candidate is a
5
+ * contract that is:
6
+ *
7
+ * 1. the SAME CUSTOMER as this contract — {@link SameCustomerClause}
8
+ * 2. at the SAME LEVEL — `IsSameContractLevel`, in `ContractEntity.ts`, where it already lived
9
+ * 3. NOT already superseded — a plain `SupersededByContractID IS NULL`
10
+ * 4. not this contract — a plain `ID <> …`
11
+ *
12
+ * WHY THESE LIVE HERE AND NOT ON THE PANEL. `supersede.panel.ts` is an Angular component, so anything
13
+ * declared inside it can only be asserted by standing up Angular's DI — importing it from a unit test
14
+ * fails with "the injectable 'PlatformLocation' needs to be compiled using the JIT compiler". Rules 3
15
+ * and 4 have nothing to decide, but 1 and the label format both do, and both are stated as final in
16
+ * the issue, which makes them worth pinning. Rule 2 was already here for the same reason.
17
+ *
18
+ * The panel still owns the QUERY. These own the decisions inside it.
19
+ *
20
+ * @module @mj-biz-apps/contracts-entities
21
+ */
22
+ /**
23
+ * The same-customer clause — rule 1 — or one that deliberately matches nothing.
24
+ *
25
+ * A re-papering replaces one customer's agreement with another agreement for THAT customer. Offering
26
+ * another customer's contract is not merely untidy: linking it points one organisation's lineage at
27
+ * another's, so the successor's terms read as governing a party that never signed them, and the
28
+ * predecessor's customer loses the trail to whatever replaced their agreement.
29
+ *
30
+ * AN ABSENT CUSTOMER YIELDS NOTHING, NOT EVERYTHING, and the direction is the whole point.
31
+ * `CustomerOrganizationID` is NOT NULL on Contract, so a missing value never means "this contract has
32
+ * no customer" — it means the record could not be read. Widening to every contract at that moment
33
+ * would offer the whole book precisely when the caller knows least, which is the failure rule 1
34
+ * exists to prevent.
35
+ */
36
+ export function SameCustomerClause(customerID) {
37
+ const id = (customerID ?? '').trim();
38
+ if (!id)
39
+ return '1 = 0';
40
+ // The value reaches an `ExtraFilter` as a string. It is a UUID by construction, so this should
41
+ // never fire — which is exactly why dropping it would go unnoticed.
42
+ return `CustomerOrganizationID = '${id.replace(/'/g, "''")}'`;
43
+ }
44
+ /**
45
+ * The option label: `<ContractNumber> — <Description>`, falling back to the contract type.
46
+ *
47
+ * Quoted as final in the issue. The description is what a person recognises — "Platform licence, 2026
48
+ * renewal" — while the type is a category several contracts share, so a list labelled by type reads
49
+ * as several copies of the same row.
50
+ *
51
+ * The description is TRIMMED before the emptiness test, because blank descriptions are common on
52
+ * imported records and a label reading `CTR-0007 — ` looks like missing data rather than an
53
+ * uncategorised agreement. The second fallback to "Contract" exists so a row with neither can never
54
+ * render a dangling dash.
55
+ */
56
+ export function ContractOptionLabel(c) {
57
+ const described = (c.Description ?? '').trim();
58
+ return `${c.ContractNumber} — ${described || c.ContractType || 'Contract'}`;
59
+ }
60
+ //# sourceMappingURL=supersede-candidates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supersede-candidates.js","sourceRoot":"","sources":["../src/supersede-candidates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAqC;IACpE,MAAM,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,EAAE;QAAE,OAAO,OAAO,CAAC;IACxB,+FAA+F;IAC/F,oEAAoE;IACpE,OAAO,6BAA6B,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAInC;IACG,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,OAAO,GAAG,CAAC,CAAC,cAAc,MAAM,SAAS,IAAI,CAAC,CAAC,YAAY,IAAI,UAAU,EAAE,CAAC;AAChF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mj-biz-apps/contracts-entities",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Generated entity subclasses for the app schema. TODO(template): rename package + description.",
6
6
  "main": "dist/index.js",
@@ -14,11 +14,12 @@
14
14
  "zod": "^3.25.76"
15
15
  },
16
16
  "peerDependencies": {
17
- "@memberjunction/core": "^6.1.0-edge.3",
18
- "@memberjunction/global": "^6.1.0-edge.3"
17
+ "@memberjunction/core": "^6.1.0-edge.4",
18
+ "@memberjunction/global": "^6.1.0-edge.4"
19
19
  },
20
20
  "devDependencies": {
21
- "typescript": "^5.9.3"
21
+ "typescript": "^5.9.3",
22
+ "vitest": "^4.0.18"
22
23
  },
23
24
  "files": [
24
25
  "/dist"
@@ -28,6 +29,6 @@
28
29
  },
29
30
  "scripts": {
30
31
  "build": "tsc && tsc-alias -f",
31
- "test": "echo \"No tests configured yet\""
32
+ "test": "vitest run --passWithNoTests"
32
33
  }
33
34
  }