@contractspec/lib.personalization 1.57.0 → 1.59.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/adapter.d.ts +15 -19
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +43 -38
- package/dist/analyzer.d.ts +15 -19
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +69 -51
- package/dist/browser/adapter.js +46 -0
- package/dist/browser/analyzer.js +72 -0
- package/dist/browser/docs/behavior-tracking.docblock.js +97 -0
- package/dist/browser/docs/index.js +271 -0
- package/dist/browser/docs/overlay-engine.docblock.js +98 -0
- package/dist/browser/docs/workflow-composition.docblock.js +83 -0
- package/dist/browser/index.js +555 -0
- package/dist/browser/store.js +75 -0
- package/dist/browser/tracker.js +94 -0
- package/dist/browser/types.js +0 -0
- package/dist/docs/behavior-tracking.docblock.d.ts +2 -6
- package/dist/docs/behavior-tracking.docblock.d.ts.map +1 -1
- package/dist/docs/behavior-tracking.docblock.js +95 -15
- package/dist/docs/index.d.ts +4 -3
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +272 -3
- package/dist/docs/overlay-engine.docblock.d.ts +2 -6
- package/dist/docs/overlay-engine.docblock.d.ts.map +1 -1
- package/dist/docs/overlay-engine.docblock.js +96 -15
- package/dist/docs/workflow-composition.docblock.d.ts +2 -6
- package/dist/docs/workflow-composition.docblock.d.ts.map +1 -1
- package/dist/docs/workflow-composition.docblock.js +81 -15
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +555 -6
- package/dist/node/adapter.js +46 -0
- package/dist/node/analyzer.js +72 -0
- package/dist/node/docs/behavior-tracking.docblock.js +97 -0
- package/dist/node/docs/index.js +271 -0
- package/dist/node/docs/overlay-engine.docblock.js +98 -0
- package/dist/node/docs/workflow-composition.docblock.js +83 -0
- package/dist/node/index.js +555 -0
- package/dist/node/store.js +75 -0
- package/dist/node/tracker.js +94 -0
- package/dist/node/types.js +0 -0
- package/dist/store.d.ts +14 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +74 -57
- package/dist/tracker.d.ts +42 -46
- package/dist/tracker.d.ts.map +1 -1
- package/dist/tracker.js +93 -91
- package/dist/types.d.ts +48 -51
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/package.json +104 -37
- package/dist/adapter.js.map +0 -1
- package/dist/analyzer.js.map +0 -1
- package/dist/docs/behavior-tracking.docblock.js.map +0 -1
- package/dist/docs/overlay-engine.docblock.js.map +0 -1
- package/dist/docs/workflow-composition.docblock.js.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/tracker.js.map +0 -1
package/dist/adapter.d.ts
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
role?: string;
|
|
11
|
-
version?: string;
|
|
1
|
+
import type { OverlaySpec } from '@contractspec/lib.overlay-engine';
|
|
2
|
+
import type { BehaviorInsights } from './types';
|
|
3
|
+
export interface OverlaySuggestionOptions {
|
|
4
|
+
overlayId: string;
|
|
5
|
+
tenantId: string;
|
|
6
|
+
capability: string;
|
|
7
|
+
userId?: string;
|
|
8
|
+
role?: string;
|
|
9
|
+
version?: string;
|
|
12
10
|
}
|
|
13
|
-
interface WorkflowAdaptation {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
export interface WorkflowAdaptation {
|
|
12
|
+
workflow: string;
|
|
13
|
+
step: string;
|
|
14
|
+
note: string;
|
|
17
15
|
}
|
|
18
|
-
declare function insightsToOverlaySuggestion(insights: BehaviorInsights, options: OverlaySuggestionOptions): OverlaySpec | null;
|
|
19
|
-
declare function insightsToWorkflowAdaptations(insights: BehaviorInsights): WorkflowAdaptation[];
|
|
20
|
-
//#endregion
|
|
21
|
-
export { OverlaySuggestionOptions, WorkflowAdaptation, insightsToOverlaySuggestion, insightsToWorkflowAdaptations };
|
|
16
|
+
export declare function insightsToOverlaySuggestion(insights: BehaviorInsights, options: OverlaySuggestionOptions): OverlaySpec | null;
|
|
17
|
+
export declare function insightsToWorkflowAdaptations(insights: BehaviorInsights): WorkflowAdaptation[];
|
|
22
18
|
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EACZ,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,wBAAwB,GAChC,WAAW,GAAG,IAAI,CAqCpB;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,gBAAgB,GACzB,kBAAkB,EAAE,CAMtB"}
|
package/dist/adapter.js
CHANGED
|
@@ -1,42 +1,47 @@
|
|
|
1
|
-
|
|
1
|
+
// @bun
|
|
2
|
+
// src/adapter.ts
|
|
2
3
|
function insightsToOverlaySuggestion(insights, options) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
4
|
+
const modifications = [];
|
|
5
|
+
insights.suggestedHiddenFields.forEach((field) => {
|
|
6
|
+
modifications.push({
|
|
7
|
+
type: "hideField",
|
|
8
|
+
field,
|
|
9
|
+
reason: "Automatically hidden because usage is near zero"
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
if (insights.frequentlyUsedFields.length) {
|
|
13
|
+
modifications.push({
|
|
14
|
+
type: "reorderFields",
|
|
15
|
+
fields: insights.frequentlyUsedFields
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (!modifications.length) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
overlayId: options.overlayId,
|
|
23
|
+
version: options.version ?? "1.0.0",
|
|
24
|
+
appliesTo: {
|
|
25
|
+
tenantId: options.tenantId,
|
|
26
|
+
capability: options.capability,
|
|
27
|
+
userId: options.userId,
|
|
28
|
+
role: options.role
|
|
29
|
+
},
|
|
30
|
+
modifications,
|
|
31
|
+
metadata: {
|
|
32
|
+
generatedAt: new Date().toISOString(),
|
|
33
|
+
automated: true
|
|
34
|
+
}
|
|
35
|
+
};
|
|
31
36
|
}
|
|
32
37
|
function insightsToWorkflowAdaptations(insights) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
return insights.workflowBottlenecks.map((bottleneck) => ({
|
|
39
|
+
workflow: bottleneck.workflow,
|
|
40
|
+
step: bottleneck.step,
|
|
41
|
+
note: `High drop rate (${Math.round(bottleneck.dropRate * 100)}%) detected`
|
|
42
|
+
}));
|
|
38
43
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
export {
|
|
45
|
+
insightsToWorkflowAdaptations,
|
|
46
|
+
insightsToOverlaySuggestion
|
|
47
|
+
};
|
package/dist/analyzer.d.ts
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
fieldInactivityThreshold?: number;
|
|
7
|
-
minSamples?: number;
|
|
1
|
+
import type { BehaviorStore } from './store';
|
|
2
|
+
import type { BehaviorInsights } from './types';
|
|
3
|
+
export interface BehaviorAnalyzerOptions {
|
|
4
|
+
fieldInactivityThreshold?: number;
|
|
5
|
+
minSamples?: number;
|
|
8
6
|
}
|
|
9
|
-
interface AnalyzeParams {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export interface AnalyzeParams {
|
|
8
|
+
tenantId: string;
|
|
9
|
+
userId?: string;
|
|
10
|
+
role?: string;
|
|
11
|
+
windowMs?: number;
|
|
14
12
|
}
|
|
15
|
-
declare class BehaviorAnalyzer {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
export declare class BehaviorAnalyzer {
|
|
14
|
+
private readonly store;
|
|
15
|
+
private readonly options;
|
|
16
|
+
constructor(store: BehaviorStore, options?: BehaviorAnalyzerOptions);
|
|
17
|
+
analyze(params: AnalyzeParams): Promise<BehaviorInsights>;
|
|
20
18
|
}
|
|
21
|
-
//#endregion
|
|
22
|
-
export { AnalyzeParams, BehaviorAnalyzer, BehaviorAnalyzerOptions };
|
|
23
19
|
//# sourceMappingURL=analyzer.d.ts.map
|
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAkC,MAAM,SAAS,CAAC;AAEhF,MAAM,WAAW,uBAAuB;IACtC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,KAAK,EAAE,aAAa,EACpB,OAAO,GAAE,uBAA4B;IAGlD,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAchE"}
|
package/dist/analyzer.js
CHANGED
|
@@ -1,55 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
1
|
+
// @bun
|
|
2
|
+
// src/analyzer.ts
|
|
3
|
+
var DEFAULT_THRESHOLD = 3;
|
|
4
|
+
|
|
5
|
+
class BehaviorAnalyzer {
|
|
6
|
+
store;
|
|
7
|
+
options;
|
|
8
|
+
constructor(store, options = {}) {
|
|
9
|
+
this.store = store;
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
async analyze(params) {
|
|
13
|
+
const query = {
|
|
14
|
+
tenantId: params.tenantId,
|
|
15
|
+
userId: params.userId,
|
|
16
|
+
role: params.role
|
|
17
|
+
};
|
|
18
|
+
if (params.windowMs) {
|
|
19
|
+
query.since = Date.now() - params.windowMs;
|
|
20
|
+
}
|
|
21
|
+
const summary = await this.store.summarize(query);
|
|
22
|
+
return buildInsights(summary, this.options);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
18
25
|
function buildInsights(summary, options) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
const threshold = options.fieldInactivityThreshold ?? DEFAULT_THRESHOLD;
|
|
27
|
+
const minSamples = options.minSamples ?? 10;
|
|
28
|
+
const ignoredFields = [];
|
|
29
|
+
const frequentlyUsedFields = [];
|
|
30
|
+
for (const [field, count] of Object.entries(summary.fieldCounts)) {
|
|
31
|
+
if (count <= threshold) {
|
|
32
|
+
ignoredFields.push(field);
|
|
33
|
+
}
|
|
34
|
+
if (count >= threshold * 4) {
|
|
35
|
+
frequentlyUsedFields.push(field);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const workflowBottlenecks = Object.entries(summary.workflowStepCounts).flatMap(([workflow, steps]) => {
|
|
39
|
+
const total = Object.values(steps).reduce((acc, value) => acc + value, 0);
|
|
40
|
+
if (!total || total < minSamples) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return Object.entries(steps).filter(([, count]) => count / total < 0.4).map(([step, count]) => ({
|
|
44
|
+
workflow,
|
|
45
|
+
step,
|
|
46
|
+
dropRate: 1 - count / total
|
|
47
|
+
}));
|
|
48
|
+
});
|
|
49
|
+
const layoutPreference = detectLayout(summary);
|
|
50
|
+
return {
|
|
51
|
+
unusedFields: ignoredFields,
|
|
52
|
+
suggestedHiddenFields: ignoredFields.slice(0, 5),
|
|
53
|
+
frequentlyUsedFields: frequentlyUsedFields.slice(0, 10),
|
|
54
|
+
workflowBottlenecks,
|
|
55
|
+
layoutPreference
|
|
56
|
+
};
|
|
44
57
|
}
|
|
45
58
|
function detectLayout(summary) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
const fieldCount = Object.keys(summary.fieldCounts).length;
|
|
60
|
+
if (!fieldCount) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (fieldCount >= 15) {
|
|
64
|
+
return "table";
|
|
65
|
+
}
|
|
66
|
+
if (fieldCount >= 8) {
|
|
67
|
+
return "grid";
|
|
68
|
+
}
|
|
69
|
+
return "form";
|
|
51
70
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//# sourceMappingURL=analyzer.js.map
|
|
71
|
+
export {
|
|
72
|
+
BehaviorAnalyzer
|
|
73
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/adapter.ts
|
|
2
|
+
function insightsToOverlaySuggestion(insights, options) {
|
|
3
|
+
const modifications = [];
|
|
4
|
+
insights.suggestedHiddenFields.forEach((field) => {
|
|
5
|
+
modifications.push({
|
|
6
|
+
type: "hideField",
|
|
7
|
+
field,
|
|
8
|
+
reason: "Automatically hidden because usage is near zero"
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
if (insights.frequentlyUsedFields.length) {
|
|
12
|
+
modifications.push({
|
|
13
|
+
type: "reorderFields",
|
|
14
|
+
fields: insights.frequentlyUsedFields
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (!modifications.length) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
overlayId: options.overlayId,
|
|
22
|
+
version: options.version ?? "1.0.0",
|
|
23
|
+
appliesTo: {
|
|
24
|
+
tenantId: options.tenantId,
|
|
25
|
+
capability: options.capability,
|
|
26
|
+
userId: options.userId,
|
|
27
|
+
role: options.role
|
|
28
|
+
},
|
|
29
|
+
modifications,
|
|
30
|
+
metadata: {
|
|
31
|
+
generatedAt: new Date().toISOString(),
|
|
32
|
+
automated: true
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function insightsToWorkflowAdaptations(insights) {
|
|
37
|
+
return insights.workflowBottlenecks.map((bottleneck) => ({
|
|
38
|
+
workflow: bottleneck.workflow,
|
|
39
|
+
step: bottleneck.step,
|
|
40
|
+
note: `High drop rate (${Math.round(bottleneck.dropRate * 100)}%) detected`
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
insightsToWorkflowAdaptations,
|
|
45
|
+
insightsToOverlaySuggestion
|
|
46
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/analyzer.ts
|
|
2
|
+
var DEFAULT_THRESHOLD = 3;
|
|
3
|
+
|
|
4
|
+
class BehaviorAnalyzer {
|
|
5
|
+
store;
|
|
6
|
+
options;
|
|
7
|
+
constructor(store, options = {}) {
|
|
8
|
+
this.store = store;
|
|
9
|
+
this.options = options;
|
|
10
|
+
}
|
|
11
|
+
async analyze(params) {
|
|
12
|
+
const query = {
|
|
13
|
+
tenantId: params.tenantId,
|
|
14
|
+
userId: params.userId,
|
|
15
|
+
role: params.role
|
|
16
|
+
};
|
|
17
|
+
if (params.windowMs) {
|
|
18
|
+
query.since = Date.now() - params.windowMs;
|
|
19
|
+
}
|
|
20
|
+
const summary = await this.store.summarize(query);
|
|
21
|
+
return buildInsights(summary, this.options);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function buildInsights(summary, options) {
|
|
25
|
+
const threshold = options.fieldInactivityThreshold ?? DEFAULT_THRESHOLD;
|
|
26
|
+
const minSamples = options.minSamples ?? 10;
|
|
27
|
+
const ignoredFields = [];
|
|
28
|
+
const frequentlyUsedFields = [];
|
|
29
|
+
for (const [field, count] of Object.entries(summary.fieldCounts)) {
|
|
30
|
+
if (count <= threshold) {
|
|
31
|
+
ignoredFields.push(field);
|
|
32
|
+
}
|
|
33
|
+
if (count >= threshold * 4) {
|
|
34
|
+
frequentlyUsedFields.push(field);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const workflowBottlenecks = Object.entries(summary.workflowStepCounts).flatMap(([workflow, steps]) => {
|
|
38
|
+
const total = Object.values(steps).reduce((acc, value) => acc + value, 0);
|
|
39
|
+
if (!total || total < minSamples) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return Object.entries(steps).filter(([, count]) => count / total < 0.4).map(([step, count]) => ({
|
|
43
|
+
workflow,
|
|
44
|
+
step,
|
|
45
|
+
dropRate: 1 - count / total
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
const layoutPreference = detectLayout(summary);
|
|
49
|
+
return {
|
|
50
|
+
unusedFields: ignoredFields,
|
|
51
|
+
suggestedHiddenFields: ignoredFields.slice(0, 5),
|
|
52
|
+
frequentlyUsedFields: frequentlyUsedFields.slice(0, 10),
|
|
53
|
+
workflowBottlenecks,
|
|
54
|
+
layoutPreference
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function detectLayout(summary) {
|
|
58
|
+
const fieldCount = Object.keys(summary.fieldCounts).length;
|
|
59
|
+
if (!fieldCount) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (fieldCount >= 15) {
|
|
63
|
+
return "table";
|
|
64
|
+
}
|
|
65
|
+
if (fieldCount >= 8) {
|
|
66
|
+
return "grid";
|
|
67
|
+
}
|
|
68
|
+
return "form";
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
BehaviorAnalyzer
|
|
72
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/docs/behavior-tracking.docblock.ts
|
|
2
|
+
import { registerDocBlocks } from "@contractspec/lib.contracts/docs";
|
|
3
|
+
var personalization_behavior_tracking_DocBlocks = [
|
|
4
|
+
{
|
|
5
|
+
id: "docs.personalization.behavior-tracking",
|
|
6
|
+
title: "Behavior Tracking",
|
|
7
|
+
summary: "`@contractspec/lib.personalization` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.",
|
|
8
|
+
kind: "reference",
|
|
9
|
+
visibility: "public",
|
|
10
|
+
route: "/docs/personalization/behavior-tracking",
|
|
11
|
+
tags: ["personalization", "behavior-tracking"],
|
|
12
|
+
body: `# Behavior Tracking
|
|
13
|
+
|
|
14
|
+
\`@contractspec/lib.personalization\` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.
|
|
15
|
+
|
|
16
|
+
## Tracker
|
|
17
|
+
|
|
18
|
+
\`\`\`ts
|
|
19
|
+
import { createBehaviorTracker } from '@contractspec/lib.personalization';
|
|
20
|
+
import { InMemoryBehaviorStore } from '@contractspec/lib.personalization/store';
|
|
21
|
+
|
|
22
|
+
const tracker = createBehaviorTracker({
|
|
23
|
+
store: new InMemoryBehaviorStore(),
|
|
24
|
+
context: { tenantId: ctx.tenant.id, userId: ctx.identity.userId },
|
|
25
|
+
autoFlushIntervalMs: 5000,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
tracker.trackFieldAccess({ operation: 'billing.createOrder', field: 'internalNotes' });
|
|
29
|
+
tracker.trackFeatureUsage({ feature: 'workflow-editor', action: 'opened' });
|
|
30
|
+
tracker.trackWorkflowStep({ workflow: 'invoice-approval', step: 'review', status: 'entered' });
|
|
31
|
+
\`\`\`
|
|
32
|
+
|
|
33
|
+
All events are buffered and flushed either when the buffer hits 25 entries or when \`autoFlushIntervalMs\` elapses. Tracked metrics flow to OpenTelemetry via the meter/counter built into the tracker.
|
|
34
|
+
|
|
35
|
+
## Analyzer
|
|
36
|
+
|
|
37
|
+
\`\`\`ts
|
|
38
|
+
import { BehaviorAnalyzer } from '@contractspec/lib.personalization/analyzer';
|
|
39
|
+
|
|
40
|
+
const analyzer = new BehaviorAnalyzer(store, { fieldInactivityThreshold: 2 });
|
|
41
|
+
const insights = await analyzer.analyze({ tenantId: 'acme', userId: 'manager-42', windowMs: 7 * 24 * 60 * 60 * 1000 });
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
{
|
|
45
|
+
unusedFields: ['internalNotes'],
|
|
46
|
+
suggestedHiddenFields: ['internalNotes'],
|
|
47
|
+
frequentlyUsedFields: ['customerReference', 'items'],
|
|
48
|
+
workflowBottlenecks: [{ workflow: 'invoice-approval', step: 'finance-review', dropRate: 0.6 }],
|
|
49
|
+
layoutPreference: 'table'
|
|
50
|
+
}
|
|
51
|
+
*/
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
Use the analyzer output with the overlay adapter to generate suggestions automatically.
|
|
55
|
+
|
|
56
|
+
## Adapter
|
|
57
|
+
|
|
58
|
+
\`\`\`ts
|
|
59
|
+
import { insightsToOverlaySuggestion } from '@contractspec/lib.personalization/adapter';
|
|
60
|
+
|
|
61
|
+
const overlay = insightsToOverlaySuggestion(insights, {
|
|
62
|
+
overlayId: 'acme-order-form',
|
|
63
|
+
tenantId: 'acme',
|
|
64
|
+
capability: 'billing.createOrder',
|
|
65
|
+
});
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
When the adapter returns an overlay spec, pass it to the overlay engine to register or sign it.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
`
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
registerDocBlocks(personalization_behavior_tracking_DocBlocks);
|
|
95
|
+
export {
|
|
96
|
+
personalization_behavior_tracking_DocBlocks
|
|
97
|
+
};
|