@mj-biz-apps/common-entities 5.43.0 → 5.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/party-signals.d.ts +74 -0
- package/dist/party-signals.d.ts.map +1 -0
- package/dist/party-signals.js +94 -0
- package/dist/party-signals.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './generated/entity_subclasses.js';
|
|
2
2
|
export type { ActivityDetails, ActivitySyncConnectionSettings, ActivitySyncRuleFilter, } from './activity-json-types.js';
|
|
3
3
|
export * from './business-day.js';
|
|
4
|
+
export * from './party-signals.js';
|
|
4
5
|
export * from './business-time-zone-engine.js';
|
|
5
6
|
/**
|
|
6
7
|
* This function is used to force the generated entities to be loaded. This is necessary because of the way that tree shaking works in webpack.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA;AAC7C,YAAY,EACR,eAAe,EACf,8BAA8B,EAC9B,sBAAsB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gCAAgC,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,SACpC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA;AAC7C,YAAY,EACR,eAAe,EACf,8BAA8B,EAC9B,sBAAsB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,SACpC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './generated/entity_subclasses.js';
|
|
2
2
|
export * from './business-day.js';
|
|
3
|
+
export * from './party-signals.js';
|
|
3
4
|
export * from './business-time-zone-engine.js';
|
|
4
5
|
/**
|
|
5
6
|
* This function is used to force the generated entities to be loaded. This is necessary because of the way that tree shaking works in webpack.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA;AAO7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,gCAAgC,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB;AACrC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA;AAO7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB;AACrC,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Party Signals contract — how an app tells the shared party pickers who its customers are.
|
|
3
|
+
*
|
|
4
|
+
* Common owns Organizations and People but cannot see the orders, contracts or sales schemas, and
|
|
5
|
+
* neither party carries a customer flag or a last-activity date. Rather than teach Common about
|
|
6
|
+
* every app, each app ships ONE MJ Query in the `Party Signals` category returning:
|
|
7
|
+
*
|
|
8
|
+
* PartyKind 'organization' | 'person'
|
|
9
|
+
* PartyID the Common Organization or Person ID
|
|
10
|
+
* Count how many of the app's records name this party (orders, contracts, won deals)
|
|
11
|
+
* LastActivityAt the most recent date among them, or null
|
|
12
|
+
*
|
|
13
|
+
* and carrying `[signal: order|orders]` in its `Description` so a picker row can be labelled
|
|
14
|
+
* "4 orders" without knowing what an order is. The category IS the registry: nothing imports an
|
|
15
|
+
* app, and every client — Explorer, Skip, agents, MCP, reports — discovers the same queries
|
|
16
|
+
* through metadata and gets the same answer to "is this party a customer".
|
|
17
|
+
*
|
|
18
|
+
* This module is the contract plus the pure union of the rows. It has no MJ dependency at all, so
|
|
19
|
+
* the ranking it feeds can be tested without a database, a provider or Angular.
|
|
20
|
+
*/
|
|
21
|
+
export declare const PARTY_SIGNALS_CATEGORY = "Party Signals";
|
|
22
|
+
export type PartyKind = 'organization' | 'person';
|
|
23
|
+
/** One row as a signal query returns it. Loosely typed on purpose: it crosses a SQL boundary. */
|
|
24
|
+
export interface PartySignalRow {
|
|
25
|
+
PartyKind: string;
|
|
26
|
+
PartyID: string;
|
|
27
|
+
Count: number;
|
|
28
|
+
LastActivityAt: string | Date | null;
|
|
29
|
+
}
|
|
30
|
+
/** One query's contribution: its rows plus the nouns its description declared. */
|
|
31
|
+
export interface PartySignalSource {
|
|
32
|
+
QueryKey: string;
|
|
33
|
+
NounSingular: string;
|
|
34
|
+
NounPlural: string;
|
|
35
|
+
Rows: PartySignalRow[];
|
|
36
|
+
}
|
|
37
|
+
/** What one app says about one party. */
|
|
38
|
+
export interface PartySignal {
|
|
39
|
+
QueryKey: string;
|
|
40
|
+
NounSingular: string;
|
|
41
|
+
NounPlural: string;
|
|
42
|
+
Count: number;
|
|
43
|
+
LastActivityAt: Date | null;
|
|
44
|
+
}
|
|
45
|
+
/** What every app together says about one party. */
|
|
46
|
+
export interface PartyRosterEntry {
|
|
47
|
+
PartyID: string;
|
|
48
|
+
Kind: PartyKind;
|
|
49
|
+
Signals: PartySignal[];
|
|
50
|
+
TotalCount: number;
|
|
51
|
+
LastActivityAt: Date | null;
|
|
52
|
+
}
|
|
53
|
+
export interface SignalNouns {
|
|
54
|
+
NounSingular: string;
|
|
55
|
+
NounPlural: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Pull the chip nouns out of a query description. A query that forgets the marker still works;
|
|
59
|
+
* its rows are simply labelled "4 records", which is honest rather than wrong.
|
|
60
|
+
*/
|
|
61
|
+
export declare function ParseSignalNouns(description: string | null | undefined): SignalNouns;
|
|
62
|
+
/** "4 orders", "1 order" — the text of one picker chip. */
|
|
63
|
+
export declare function ChipText(signal: Pick<PartySignal, 'Count' | 'NounSingular' | 'NounPlural'>): string;
|
|
64
|
+
/**
|
|
65
|
+
* Union every source into one entry per party.
|
|
66
|
+
*
|
|
67
|
+
* Party IDs are matched case-insensitively because GUIDs arrive in whichever case the provider
|
|
68
|
+
* and the query happened to produce, and two spellings of one organization would otherwise read
|
|
69
|
+
* as two customers — the exact defect this whole feature exists to stop showing people. The first
|
|
70
|
+
* spelling seen is the one kept, so it still matches the record it came from. Rows whose kind is
|
|
71
|
+
* outside the contract, or whose ID is blank, are dropped rather than trusted.
|
|
72
|
+
*/
|
|
73
|
+
export declare function MergePartySignalRows(sources: ReadonlyArray<PartySignalSource>): PartyRosterEntry[];
|
|
74
|
+
//# sourceMappingURL=party-signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"party-signals.d.ts","sourceRoot":"","sources":["../src/party-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,sBAAsB,kBAAkB,CAAC;AAEtD,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,QAAQ,CAAC;AAElD,iGAAiG;AACjG,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CACxC;AAED,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;CAC/B;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACtB;AAMD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,CAMpF;AAED,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,cAAc,GAAG,YAAY,CAAC,GAAG,MAAM,CAEnG;AAmCD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,gBAAgB,EAAE,CAgBlG"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Party Signals contract — how an app tells the shared party pickers who its customers are.
|
|
3
|
+
*
|
|
4
|
+
* Common owns Organizations and People but cannot see the orders, contracts or sales schemas, and
|
|
5
|
+
* neither party carries a customer flag or a last-activity date. Rather than teach Common about
|
|
6
|
+
* every app, each app ships ONE MJ Query in the `Party Signals` category returning:
|
|
7
|
+
*
|
|
8
|
+
* PartyKind 'organization' | 'person'
|
|
9
|
+
* PartyID the Common Organization or Person ID
|
|
10
|
+
* Count how many of the app's records name this party (orders, contracts, won deals)
|
|
11
|
+
* LastActivityAt the most recent date among them, or null
|
|
12
|
+
*
|
|
13
|
+
* and carrying `[signal: order|orders]` in its `Description` so a picker row can be labelled
|
|
14
|
+
* "4 orders" without knowing what an order is. The category IS the registry: nothing imports an
|
|
15
|
+
* app, and every client — Explorer, Skip, agents, MCP, reports — discovers the same queries
|
|
16
|
+
* through metadata and gets the same answer to "is this party a customer".
|
|
17
|
+
*
|
|
18
|
+
* This module is the contract plus the pure union of the rows. It has no MJ dependency at all, so
|
|
19
|
+
* the ranking it feeds can be tested without a database, a provider or Angular.
|
|
20
|
+
*/
|
|
21
|
+
export const PARTY_SIGNALS_CATEGORY = 'Party Signals';
|
|
22
|
+
const SIGNAL_MARKER = /\[signal:\s*([^|\]]+)\|([^\]]+)\]/i;
|
|
23
|
+
const DEFAULT_NOUNS = { NounSingular: 'record', NounPlural: 'records' };
|
|
24
|
+
/**
|
|
25
|
+
* Pull the chip nouns out of a query description. A query that forgets the marker still works;
|
|
26
|
+
* its rows are simply labelled "4 records", which is honest rather than wrong.
|
|
27
|
+
*/
|
|
28
|
+
export function ParseSignalNouns(description) {
|
|
29
|
+
const match = SIGNAL_MARKER.exec(description ?? '');
|
|
30
|
+
if (!match) {
|
|
31
|
+
return { ...DEFAULT_NOUNS };
|
|
32
|
+
}
|
|
33
|
+
return { NounSingular: match[1].trim(), NounPlural: match[2].trim() };
|
|
34
|
+
}
|
|
35
|
+
/** "4 orders", "1 order" — the text of one picker chip. */
|
|
36
|
+
export function ChipText(signal) {
|
|
37
|
+
return `${signal.Count} ${signal.Count === 1 ? signal.NounSingular : signal.NounPlural}`;
|
|
38
|
+
}
|
|
39
|
+
function toDate(value) {
|
|
40
|
+
if (!value) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
44
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
45
|
+
}
|
|
46
|
+
function toKind(value) {
|
|
47
|
+
const kind = (value ?? '').trim().toLowerCase();
|
|
48
|
+
return kind === 'organization' || kind === 'person' ? kind : null;
|
|
49
|
+
}
|
|
50
|
+
function toCount(value) {
|
|
51
|
+
const count = Number(value);
|
|
52
|
+
return Number.isFinite(count) ? count : 0;
|
|
53
|
+
}
|
|
54
|
+
function addRow(entry, source, row) {
|
|
55
|
+
const at = toDate(row.LastActivityAt);
|
|
56
|
+
entry.Signals.push({
|
|
57
|
+
QueryKey: source.QueryKey,
|
|
58
|
+
NounSingular: source.NounSingular,
|
|
59
|
+
NounPlural: source.NounPlural,
|
|
60
|
+
Count: toCount(row.Count),
|
|
61
|
+
LastActivityAt: at,
|
|
62
|
+
});
|
|
63
|
+
entry.TotalCount += toCount(row.Count);
|
|
64
|
+
if (at && (!entry.LastActivityAt || at > entry.LastActivityAt)) {
|
|
65
|
+
entry.LastActivityAt = at;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Union every source into one entry per party.
|
|
70
|
+
*
|
|
71
|
+
* Party IDs are matched case-insensitively because GUIDs arrive in whichever case the provider
|
|
72
|
+
* and the query happened to produce, and two spellings of one organization would otherwise read
|
|
73
|
+
* as two customers — the exact defect this whole feature exists to stop showing people. The first
|
|
74
|
+
* spelling seen is the one kept, so it still matches the record it came from. Rows whose kind is
|
|
75
|
+
* outside the contract, or whose ID is blank, are dropped rather than trusted.
|
|
76
|
+
*/
|
|
77
|
+
export function MergePartySignalRows(sources) {
|
|
78
|
+
const byID = new Map();
|
|
79
|
+
for (const source of sources) {
|
|
80
|
+
for (const row of source.Rows) {
|
|
81
|
+
const kind = toKind(row.PartyKind);
|
|
82
|
+
const id = (row.PartyID ?? '').trim();
|
|
83
|
+
if (!kind || !id) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const key = id.toLowerCase();
|
|
87
|
+
const entry = byID.get(key) ?? { PartyID: id, Kind: kind, Signals: [], TotalCount: 0, LastActivityAt: null };
|
|
88
|
+
addRow(entry, source, row);
|
|
89
|
+
byID.set(key, entry);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [...byID.values()];
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=party-signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"party-signals.js","sourceRoot":"","sources":["../src/party-signals.ts"],"names":[],"mappings":"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"}
|