@mj-biz-apps/contracts-core-entities-server 0.1.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.
Files changed (34) hide show
  1. package/LICENSE +183 -0
  2. package/dist/ContractEntityServer.d.ts +352 -0
  3. package/dist/ContractEntityServer.d.ts.map +1 -0
  4. package/dist/ContractEntityServer.js +729 -0
  5. package/dist/ContractEntityServer.js.map +1 -0
  6. package/dist/ContractTemplateEntityServer.d.ts +39 -0
  7. package/dist/ContractTemplateEntityServer.d.ts.map +1 -0
  8. package/dist/ContractTemplateEntityServer.js +99 -0
  9. package/dist/ContractTemplateEntityServer.js.map +1 -0
  10. package/dist/ContractTemplateModificationEntityServer.d.ts +119 -0
  11. package/dist/ContractTemplateModificationEntityServer.d.ts.map +1 -0
  12. package/dist/ContractTemplateModificationEntityServer.js +242 -0
  13. package/dist/ContractTemplateModificationEntityServer.js.map +1 -0
  14. package/dist/ContractTemplateProvisionEntityServer.d.ts +74 -0
  15. package/dist/ContractTemplateProvisionEntityServer.d.ts.map +1 -0
  16. package/dist/ContractTemplateProvisionEntityServer.js +171 -0
  17. package/dist/ContractTemplateProvisionEntityServer.js.map +1 -0
  18. package/dist/ContractTypeEntityServers.d.ts +28 -0
  19. package/dist/ContractTypeEntityServers.d.ts.map +1 -0
  20. package/dist/ContractTypeEntityServers.js +65 -0
  21. package/dist/ContractTypeEntityServers.js.map +1 -0
  22. package/dist/SupersedeOperation.d.ts +53 -0
  23. package/dist/SupersedeOperation.d.ts.map +1 -0
  24. package/dist/SupersedeOperation.js +112 -0
  25. package/dist/SupersedeOperation.js.map +1 -0
  26. package/dist/delete-guard.d.ts +89 -0
  27. package/dist/delete-guard.d.ts.map +1 -0
  28. package/dist/delete-guard.js +107 -0
  29. package/dist/delete-guard.js.map +1 -0
  30. package/dist/index.d.ts +15 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +49 -0
  33. package/dist/index.js.map +1 -0
  34. package/package.json +36 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * @fileoverview R-8 — turning a foreign-key violation into a sentence.
3
+ *
4
+ * Every FK in this schema is `NO_ACTION`, which is the correct behaviour: a delete that would orphan
5
+ * data is refused. The problem is only that it reaches the user as a constraint name. Four entities
6
+ * need the same treatment and differ ONLY in which rows to count, so this is one guard parameterised
7
+ * by the dependencies rather than four copies of the same shape drifting apart — the review test from
8
+ * ORCHESTRATION.md ("if I had to change this logic, how many places would I have to find?").
9
+ *
10
+ * ## Why `Delete()` and not a validation hook
11
+ *
12
+ * `BaseEntity` has no delete-side counterpart to `Validate()` — no `ValidateDelete`, no `BeforeDelete`,
13
+ * no vetoable event. Verified by reading `_InnerDelete`, which runs no validation at all where
14
+ * `_InnerSave` runs `Validate()`/`ValidateAsync()` first. So the only seam is overriding `Delete()`.
15
+ * Filed upstream as [MJ#3971](https://github.com/MemberJunction/MJ/issues/3971), and a fix is already
16
+ * authored there; when it lands, these overrides become `ValidateDeleteAsync()` bodies and this file
17
+ * shrinks rather than disappears.
18
+ *
19
+ * ## Why it REGISTERS A RESULT and returns false, instead of throwing
20
+ *
21
+ * `bizapps-accounting`'s two precedents (`JournalEntryTypeEntityServer`, `JournalEntryLineEntityServer`)
22
+ * **throw**. That works, and it is not what this does, deliberately:
23
+ *
24
+ * · `Delete()` is declared `Promise<boolean>`. A caller written against that signature does
25
+ * `if (!await entity.Delete())` and gets an exception instead of `false`.
26
+ * · `LatestResult` is how MJ carries the reason for a refused write — it is what `ResolverBase`
27
+ * reads to build the GraphQL error, and what a refused `Save()` already populates. Throwing routes
28
+ * around the one channel built for this.
29
+ * · `RegisterResultHistoryEntry` is public precisely so a subclass can do this.
30
+ *
31
+ * ⚠ **This depends on an MJ fix to be VISIBLE.** `ResolverBase.DeleteRecord` read
32
+ * `LatestResult?.Message` rather than `CompleteMessage`, so a registered refusal reached the API as
33
+ * the literal string "Unknown error". Measured on this instance; the one-word fix is applied locally
34
+ * in `instances/contracts-mj6/mj` and was commented onto MJ#3973. **On an unpatched MJ these messages
35
+ * are correct and invisible** — the delete is still refused, so this is a message-quality dependency,
36
+ * not a correctness one.
37
+ *
38
+ * @module @mj-biz-apps/contracts-core-entities-server
39
+ */
40
+ import { BaseEntityResult } from '@memberjunction/core';
41
+ /**
42
+ * Count each dependency and return the refusal text, or null when nothing blocks the delete.
43
+ *
44
+ * COUNTS, NOT EXISTENCE. `count_only` costs the same as `TOP 1` on an indexed FK (MJ auto-indexes
45
+ * every FK), and the number is most of the value of the message.
46
+ *
47
+ * ALL dependencies are checked, not just the first. Deleting a template usually trips both its
48
+ * provisions and its contracts, and being told about one, fixing it, and then being refused again for
49
+ * the other is the worst version of this interaction.
50
+ *
51
+ * A FAILED COUNT ABORTS rather than defaulting to zero. Reading "could not verify" as "nothing
52
+ * references it" would let this guard wave through exactly the delete it exists to stop; the FK is
53
+ * still the floor, but a guard that fails open is worse than no guard because it looks like one.
54
+ */
55
+ export async function DescribeDeleteBlockers(entity, dependencies) {
56
+ const blockers = [];
57
+ for (const dependency of dependencies) {
58
+ const result = await entity.RunViewProviderToUse.RunView({
59
+ EntityName: dependency.EntityName,
60
+ ExtraFilter: dependency.Filter,
61
+ ResultType: 'count_only',
62
+ }, entity.ContextCurrentUser);
63
+ if (!result?.Success) {
64
+ throw new Error(`Could not check whether ${dependency.EntityName} still references this record: ` +
65
+ `${result?.ErrorMessage ?? 'unknown error'}. The delete was not attempted.`);
66
+ }
67
+ const count = result.TotalRowCount ?? 0;
68
+ if (count > 0)
69
+ blockers.push(dependency.Describe(count));
70
+ }
71
+ return blockers.length > 0 ? blockers.join(' ') : null;
72
+ }
73
+ /**
74
+ * Record a delete refusal on the entity so `LatestResult` carries the reason, and return false.
75
+ *
76
+ * Registered BEFORE `super.Delete()` is ever called, so nothing else has written a result — `Message`
77
+ * is what `CompleteMessage` reads first, so the sentence survives without needing an `Errors` entry.
78
+ * There is no field to name: a delete refusal is about the record, not about one of its columns, which
79
+ * is part of why `ValidationErrorInfo` is the wrong vehicle here and MJ#3971 asks for a delete-shaped
80
+ * one.
81
+ */
82
+ export function RefuseDelete(entity, message) {
83
+ const result = new BaseEntityResult();
84
+ result.Success = false;
85
+ result.Type = 'delete';
86
+ result.Message = message;
87
+ result.EndedAt = new Date();
88
+ entity.RegisterResultHistoryEntry(result);
89
+ return false;
90
+ }
91
+ /**
92
+ * The whole guard: count the blockers, refuse with an explanation if there are any, otherwise delete.
93
+ *
94
+ * Every subclass override is one call to this, which is the point — the only thing that varies between
95
+ * the four is the dependency list and the leading sentence.
96
+ */
97
+ export async function GuardedDelete(entity, options, lead, dependencies, superDelete) {
98
+ const blockers = await DescribeDeleteBlockers(entity, dependencies);
99
+ if (blockers)
100
+ return RefuseDelete(entity, `${lead} ${blockers}`);
101
+ return superDelete(options);
102
+ }
103
+ /** `'1 contract'` / `'3 contracts'` — so a message never reads "1 contracts". */
104
+ export function plural(count, singular, pluralForm) {
105
+ return `${count} ${count === 1 ? singular : (pluralForm ?? `${singular}s`)}`;
106
+ }
107
+ //# sourceMappingURL=delete-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-guard.js","sourceRoot":"","sources":["../src/delete-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAc,gBAAgB,EAA4B,MAAM,sBAAsB,CAAC;AAiB9F;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,MAAkB,EAAE,YAAyC;IACtG,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,OAAO,CACpD;YACI,UAAU,EAAE,UAAU,CAAC,UAAU;YACjC,WAAW,EAAE,UAAU,CAAC,MAAM;YAC9B,UAAU,EAAE,YAAY;SAC3B,EACD,MAAM,CAAC,kBAAkB,CAC5B,CAAC;QACF,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACX,2BAA2B,UAAU,CAAC,UAAU,iCAAiC;gBAC7E,GAAG,MAAM,EAAE,YAAY,IAAI,eAAe,iCAAiC,CAClF,CAAC;QACN,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,GAAG,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,MAAkB,EAAE,OAAe;IAC5D,MAAM,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;IACtC,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;IACvB,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,MAAM,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;IAC5B,MAAM,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAC/B,MAAkB,EAClB,OAAwC,EACxC,IAAY,EACZ,YAAyC,EACzC,WAAgE;IAEhE,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACpE,IAAI,QAAQ;QAAE,OAAO,YAAY,CAAC,MAAM,EAAE,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC;IACjE,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAE,UAAmB;IACvE,OAAO,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC;AACjF,CAAC"}
@@ -0,0 +1,15 @@
1
+ export { ContractEntityServer, IsNewlySelected, LoadContractEntityServer } from './ContractEntityServer.js';
2
+ export { ContractTemplateModificationEntityServer, LoadContractTemplateModificationEntityServer, } from './ContractTemplateModificationEntityServer.js';
3
+ export { ContractTemplateEntityServer, LoadContractTemplateEntityServer } from './ContractTemplateEntityServer.js';
4
+ export { ContractTemplateProvisionEntityServer, LoadContractTemplateProvisionEntityServer, } from './ContractTemplateProvisionEntityServer.js';
5
+ export { ContractTemplateTypeEntityServer, ContractTypeEntityServer, LoadContractTypeEntityServers, } from './ContractTypeEntityServers.js';
6
+ export * from './delete-guard.js';
7
+ /**
8
+ * Called by the server bootstrap. The imports above are what fire @RegisterClass; these calls are
9
+ * anti-tree-shake anchors — without a live reference a production build can drop the import and the
10
+ * subclasses silently never register, so every invariant in them stops existing with no error.
11
+ */
12
+ export declare function LoadMjBizappsContractsEntitiesServer(): void;
13
+ export { SupersedeOperation, LoadSupersedeOperation } from './SupersedeOperation.js';
14
+ export type { SupersedeInput, SupersedeOutput } from './SupersedeOperation.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC5G,OAAO,EACH,wCAAwC,EACxC,4CAA4C,GAC/C,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,4BAA4B,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AACnH,OAAO,EACH,qCAAqC,EACrC,yCAAyC,GAC5C,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACH,gCAAgC,EAChC,wBAAwB,EACxB,6BAA6B,GAChC,MAAM,gCAAgC,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,IAAI,CAS3D;AAGD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACrF,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @mj-biz-apps/contracts-core-entities-server — SERVER-ONLY entity subclasses. This package must
3
+ * NEVER be imported by client code; it is a dependency of the Server package only.
4
+ *
5
+ * WHAT BELONGS HERE, AND WHAT DOES NOT (plan §6.3). A rule lives here only when it CANNOT run in the
6
+ * browser — it needs a transaction, a lock, or a cross-entity read the client has no business doing.
7
+ * Everything the browser should be able to preflight lives on the SHARED subclass in
8
+ * @mj-biz-apps/contracts-entities instead, so the user sees the error before the round trip.
9
+ *
10
+ * Dependency shape in package.json: the sibling app package (@mj-biz-apps/contracts-entities) is a
11
+ * HARD dependency pinned to the same version (all app packages version together via changesets
12
+ * `fixed`); every @memberjunction/* package is a PEER (^X.Y.Z).
13
+ *
14
+ * TWO SUBCLASSES, and that is the whole server surface. v1 had nine of these plus ContractsEngine,
15
+ * ChildCollection, BillingDraft and seven remote operations — the billing engine wholesale. v2 is a
16
+ * record-keeping app and MJ 6's graph save replaced the composition machinery, so what is left is
17
+ * the contract number (needs a lock) and two modification invariants (need cross-entity reads).
18
+ * **Zero remote operations.**
19
+ */
20
+ import { LoadContractEntityServer } from './ContractEntityServer.js';
21
+ import { LoadContractTemplateEntityServer } from './ContractTemplateEntityServer.js';
22
+ import { LoadContractTemplateProvisionEntityServer } from './ContractTemplateProvisionEntityServer.js';
23
+ import { LoadContractTypeEntityServers } from './ContractTypeEntityServers.js';
24
+ import { LoadContractTemplateModificationEntityServer } from './ContractTemplateModificationEntityServer.js';
25
+ import { LoadSupersedeOperation } from './SupersedeOperation.js';
26
+ export { ContractEntityServer, IsNewlySelected, LoadContractEntityServer } from './ContractEntityServer.js';
27
+ export { ContractTemplateModificationEntityServer, LoadContractTemplateModificationEntityServer, } from './ContractTemplateModificationEntityServer.js';
28
+ export { ContractTemplateEntityServer, LoadContractTemplateEntityServer } from './ContractTemplateEntityServer.js';
29
+ export { ContractTemplateProvisionEntityServer, LoadContractTemplateProvisionEntityServer, } from './ContractTemplateProvisionEntityServer.js';
30
+ export { ContractTemplateTypeEntityServer, ContractTypeEntityServer, LoadContractTypeEntityServers, } from './ContractTypeEntityServers.js';
31
+ export * from './delete-guard.js';
32
+ /**
33
+ * Called by the server bootstrap. The imports above are what fire @RegisterClass; these calls are
34
+ * anti-tree-shake anchors — without a live reference a production build can drop the import and the
35
+ * subclasses silently never register, so every invariant in them stops existing with no error.
36
+ */
37
+ export function LoadMjBizappsContractsEntitiesServer() {
38
+ LoadContractEntityServer();
39
+ LoadSupersedeOperation();
40
+ LoadContractTemplateModificationEntityServer();
41
+ // R-8's delete guards. These have NO Validate/Save rules at all, so a dropped registration is
42
+ // completely silent: deletes keep being refused by the FK, just with a constraint name again.
43
+ LoadContractTemplateEntityServer();
44
+ LoadContractTemplateProvisionEntityServer();
45
+ LoadContractTypeEntityServers();
46
+ }
47
+ // Re-papering: the cross-record write the browser must not do itself (see the file header, and MJ#4002).
48
+ export { SupersedeOperation, LoadSupersedeOperation } from './SupersedeOperation.js';
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,EAAE,yCAAyC,EAAE,MAAM,4CAA4C,CAAC;AACvG,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,4CAA4C,EAAE,MAAM,+CAA+C,CAAC;AAE7G,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC5G,OAAO,EACH,wCAAwC,EACxC,4CAA4C,GAC/C,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,4BAA4B,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AACnH,OAAO,EACH,qCAAqC,EACrC,yCAAyC,GAC5C,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACH,gCAAgC,EAChC,wBAAwB,EACxB,6BAA6B,GAChC,MAAM,gCAAgC,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAElC;;;;GAIG;AACH,MAAM,UAAU,oCAAoC;IAChD,wBAAwB,EAAE,CAAC;IAC3B,sBAAsB,EAAE,CAAC;IACzB,4CAA4C,EAAE,CAAC;IAC/C,8FAA8F;IAC9F,8FAA8F;IAC9F,gCAAgC,EAAE,CAAC;IACnC,yCAAyC,EAAE,CAAC;IAC5C,6BAA6B,EAAE,CAAC;AACpC,CAAC;AAED,yGAAyG;AACzG,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "mjdevManaged": "MJDEV-MANAGED: mjdev owns parts of this file — the \"packages/dev-apps/*\" workspaces glob, any dev-linked open-app dependencies (e.g. \"@mj-biz-apps/*\"), and the Angular \"overrides\" pin set. Do not hand-edit those; the tool wires and reverts them. They are left visible in git status, but the git-tree guard blocks committing them. See .mjdev-docs/MANAGED-FILES.md; revert with `mjdev git:stage-clean <slug> package.json`.",
3
+ "name": "@mj-biz-apps/contracts-core-entities-server",
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "description": "Server-side entity subclass overrides (validation, computed fields, save hooks). TODO(template): rename.",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/MemberJunction/bizapps-contracts"
12
+ },
13
+ "license": "BUSL-1.1",
14
+ "dependencies": {
15
+ "@mj-biz-apps/contracts-entities": "^0.1.0"
16
+ },
17
+ "peerDependencies": {
18
+ "@memberjunction/core": "^6.1.0-edge.3",
19
+ "@memberjunction/core-entities": "^6.1.0-edge.3",
20
+ "@memberjunction/global": "^6.1.0-edge.3",
21
+ "@memberjunction/sqlserver-dataprovider": "^6.1.0-edge.3"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.9.3"
25
+ },
26
+ "files": [
27
+ "/dist"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "build": "tsc && tsc-alias -f",
34
+ "test": "echo \"No tests configured yet\""
35
+ }
36
+ }