@dszp/netsapiens-lib 0.7.0 → 0.9.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/src/resolver.ts CHANGED
@@ -24,6 +24,7 @@
24
24
  */
25
25
 
26
26
  import type { EdgeKind, FlowGraph, FlowNode, NodeKind, Rec, Snapshot } from './model.js';
27
+ import { DEFAULT_DEVICE_SUFFIXES } from './inventory.js';
27
28
 
28
29
  export interface EntityRef {
29
30
  kind: 'did' | 'user' | 'queue' | 'attendant';
@@ -42,27 +43,49 @@ const GREET_MAX = 90;
42
43
  const trim = (v: string, max = GREET_MAX): string => (v.length > max ? `${v.slice(0, max - 1).trimEnd()}…` : v);
43
44
 
44
45
  /**
45
- * Endpoint type by the agent-id / extension letter suffix (heuristic):
46
- * wp SNAPmobile Web (browser phone) · t Microsoft Teams · m SNAPmobile (mobile) ·
47
- * r mobile/desktop app · b / other lower letters usually a desk phone.
46
+ * A diagram needs a glyph, and a suffix legend carries none — so the icons are the resolver's own,
47
+ * keyed by the same suffixes. `r` is here without being in the legend on purpose: it is the conventional
48
+ * NetSapiens+Ringotel app suffix, common enough to draw with a phone rather than a desk handset, but not
49
+ * something NetSapiens ships, so it has no place in a table this library states as fact.
50
+ *
51
+ * **Both tables are prototype-free.** The key is a device-name suffix off a snapshot, so `constructor`,
52
+ * `toString` and `hasOwnProperty` are all reachable keys; on a plain object literal each of them answers
53
+ * with something inherited and truthy, and `?? 'desk phone'` then never fires. A `Map` would do as well;
54
+ * `Object.create(null)` keeps the lookup a subscript.
55
+ */
56
+ const SUFFIX_ICONS: Record<string, string> = Object.assign(Object.create(null) as Record<string, string>,
57
+ { wp: '🌐', t: '💻', m: '📱', r: '📱' });
58
+ /** Kinds for suffixes {@link DEFAULT_DEVICE_SUFFIXES} does not name. `r` for the reason above; anything
59
+ * else lower-cased (`b`, and the rest) is a desk phone, which is what an unadorned device usually is.
60
+ * Prototype-free for the reason above. */
61
+ const SUFFIX_FALLBACK_KINDS: Record<string, string> = Object.assign(Object.create(null) as Record<string, string>,
62
+ { r: 'app' });
63
+
64
+ /**
65
+ * Endpoint type by the agent-id / extension letter suffix (heuristic). The KIND comes from
66
+ * {@link DEFAULT_DEVICE_SUFFIXES} — the same legend `listDomainInventory` labels a device with, so a
67
+ * suffix means one thing across this library and the two cannot drift — with the icons above supplying
68
+ * what the legend does not.
69
+ *
48
70
  * Exact device info (model, MAC, transport) IS available via the device API but isn't pulled yet —
49
- * see ARCHITECTURE.md → NetSapiens routing model; this suffix guess is the cheap approximation.
71
+ * see ARCHITECTURE.md → NetSapiens routing model; this suffix guess is the cheap approximation. Note the
72
+ * resolver reads the DEFAULT legend and takes no options: a call flow is drawn from a snapshot alone, and
73
+ * a deployment's own suffixes reach the inventory through `InventoryOptions.deviceSuffixes` instead.
74
+ *
75
+ * Exported for its own test. Not on the package surface (`index.ts`) — it is an internal heuristic, and
76
+ * a consumer wanting the legend should read `DEFAULT_DEVICE_SUFFIXES`, which is the thing being stated.
50
77
  */
51
- function deviceKindBySuffix(suffix: string): { icon: string; kind: string } {
52
- switch (suffix.toLowerCase()) {
53
- case 'wp':
54
- return { icon: '🌐', kind: 'web app' };
55
- case 't':
56
- return { icon: '💻', kind: 'Teams' };
57
- case 'm':
58
- return { icon: '📱', kind: 'mobile app' };
59
- case 'r':
60
- return { icon: '📱', kind: 'app' };
61
- case '':
62
- return { icon: '', kind: '' };
63
- default:
64
- return { icon: '📞', kind: 'desk phone' }; // b and other letters
65
- }
78
+ export function deviceKindBySuffix(suffix: string): { icon: string; kind: string } {
79
+ const k = suffix.toLowerCase();
80
+ if (!k) return { icon: '', kind: '' };
81
+ // `Object.hasOwn` rather than a truthiness test: `DEFAULT_DEVICE_SUFFIXES` is a plain object, so
82
+ // `[k]` on an inherited name answers a function whose `.label` is undefined — which happens to fall
83
+ // through correctly, and would stop doing so the day an entry gained an optional field read here.
84
+ const entry = Object.hasOwn(DEFAULT_DEVICE_SUFFIXES, k) ? DEFAULT_DEVICE_SUFFIXES[k] : undefined;
85
+ return {
86
+ icon: SUFFIX_ICONS[k] ?? '📞',
87
+ kind: entry?.label ?? SUFFIX_FALLBACK_KINDS[k] ?? 'desk phone',
88
+ };
66
89
  }
67
90
 
68
91
  /** Compact agent queue-priority badge: "P" + a keycap digit (e.g. P2️⃣). Priority is a cross-queue