@contractspec/lib.personalization 6.0.17 → 6.0.18
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/README.md +167 -69
- package/dist/adapter.js +1 -46
- package/dist/analyzer.js +1 -72
- package/dist/browser/adapter.js +1 -46
- package/dist/browser/analyzer.js +1 -72
- package/dist/browser/docs/behavior-tracking.docblock.js +2 -19
- package/dist/browser/docs/index.js +4 -50
- package/dist/browser/docs/overlay-engine.docblock.js +2 -19
- package/dist/browser/docs/workflow-composition.docblock.js +2 -19
- package/dist/browser/index.js +4 -334
- package/dist/browser/store.js +1 -75
- package/dist/browser/tracker.js +1 -94
- package/dist/docs/behavior-tracking.docblock.js +2 -19
- package/dist/docs/index.js +4 -50
- package/dist/docs/overlay-engine.docblock.js +2 -19
- package/dist/docs/workflow-composition.docblock.js +2 -19
- package/dist/index.js +4 -334
- package/dist/node/adapter.js +1 -46
- package/dist/node/analyzer.js +1 -72
- package/dist/node/docs/behavior-tracking.docblock.js +2 -19
- package/dist/node/docs/index.js +4 -50
- package/dist/node/docs/overlay-engine.docblock.js +2 -19
- package/dist/node/docs/workflow-composition.docblock.js +2 -19
- package/dist/node/index.js +4 -334
- package/dist/node/store.js +1 -75
- package/dist/node/tracker.js +1 -94
- package/dist/store.js +1 -75
- package/dist/tracker.js +1 -94
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,79 +1,177 @@
|
|
|
1
1
|
# @contractspec/lib.personalization
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
**Behavior tracking, analysis, and adaptation helpers for ContractSpec personalization.**
|
|
3
|
+
`@contractspec/lib.personalization` tracks behavior events, summarizes them into actionable insights, and converts those insights into personalization outputs such as overlay suggestions. It also defines the shared preference-dimensions model consumed by runtime layers.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **Layer**: lib.
|
|
10
|
-
- **Consumers**: bundles, example apps.
|
|
11
|
-
- `src/docs/` contains docblocks and documentation-facing exports.
|
|
12
|
-
- Related ContractSpec packages include `@contractspec/lib.bus`, `@contractspec/lib.contracts-spec`, `@contractspec/lib.knowledge`, `@contractspec/lib.overlay-engine`, `@contractspec/lib.schema`, `@contractspec/lib.surface-runtime`, ...
|
|
13
|
-
- `src/docs/` contains docblocks and documentation-facing exports.
|
|
5
|
+
Website: https://contractspec.io
|
|
14
6
|
|
|
15
7
|
## Installation
|
|
16
8
|
|
|
17
|
-
`
|
|
9
|
+
`bun add @contractspec/lib.personalization`
|
|
18
10
|
|
|
19
11
|
or
|
|
20
12
|
|
|
21
|
-
`
|
|
13
|
+
`npm install @contractspec/lib.personalization`
|
|
22
14
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
15
|
+
## What belongs here
|
|
16
|
+
|
|
17
|
+
This package currently has two jobs:
|
|
18
|
+
|
|
19
|
+
- Behavior telemetry and analysis: `tracker`, `store`, `analyzer`, `adapter`, and `types` handle event capture, storage, summarization, and conversion into adaptation hints.
|
|
20
|
+
- Shared preference contracts: `preference-dimensions` defines the 7-dimension personalization model and the adapter types used by runtime consumers.
|
|
21
|
+
|
|
22
|
+
Use this package when you need a thin personalization layer inside ContractSpec. Do not use it as a general analytics platform or as the full overlay runtime.
|
|
23
|
+
|
|
24
|
+
## Core workflow
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import {
|
|
28
|
+
BehaviorAnalyzer,
|
|
29
|
+
createBehaviorTracker,
|
|
30
|
+
insightsToOverlaySuggestion,
|
|
31
|
+
InMemoryBehaviorStore,
|
|
32
|
+
} from "@contractspec/lib.personalization";
|
|
33
|
+
|
|
34
|
+
const store = new InMemoryBehaviorStore();
|
|
35
|
+
|
|
36
|
+
const tracker = createBehaviorTracker({
|
|
37
|
+
store,
|
|
38
|
+
context: {
|
|
39
|
+
tenantId: "acme",
|
|
40
|
+
userId: "user-123",
|
|
41
|
+
role: "manager",
|
|
42
|
+
},
|
|
43
|
+
autoFlushIntervalMs: 5000,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
tracker.trackFieldAccess({
|
|
47
|
+
operation: "billing.createOrder",
|
|
48
|
+
field: "internalNotes",
|
|
49
|
+
});
|
|
50
|
+
tracker.trackFieldAccess({
|
|
51
|
+
operation: "billing.createOrder",
|
|
52
|
+
field: "customerReference",
|
|
53
|
+
});
|
|
54
|
+
tracker.trackFeatureUsage({
|
|
55
|
+
feature: "workflow-editor",
|
|
56
|
+
action: "opened",
|
|
57
|
+
});
|
|
58
|
+
tracker.trackWorkflowStep({
|
|
59
|
+
workflow: "invoice-approval",
|
|
60
|
+
step: "review",
|
|
61
|
+
status: "entered",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await tracker.flush();
|
|
65
|
+
await tracker.dispose();
|
|
66
|
+
|
|
67
|
+
const analyzer = new BehaviorAnalyzer(store, {
|
|
68
|
+
fieldInactivityThreshold: 2,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const insights = await analyzer.analyze({
|
|
72
|
+
tenantId: "acme",
|
|
73
|
+
userId: "user-123",
|
|
74
|
+
windowMs: 7 * 24 * 60 * 60 * 1000,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const overlay = insightsToOverlaySuggestion(insights, {
|
|
78
|
+
overlayId: "acme-order-form",
|
|
79
|
+
tenantId: "acme",
|
|
80
|
+
capability: "billing.createOrder",
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Typical flow:
|
|
85
|
+
|
|
86
|
+
1. Record behavior events through `BehaviorTracker`.
|
|
87
|
+
2. Persist and summarize those events through a `BehaviorStore`.
|
|
88
|
+
3. Analyze the summary with `BehaviorAnalyzer`.
|
|
89
|
+
4. Convert insights into an `OverlaySpec` suggestion or workflow adaptation hints.
|
|
90
|
+
|
|
91
|
+
## API map
|
|
92
|
+
|
|
93
|
+
### Main runtime APIs
|
|
94
|
+
|
|
95
|
+
- `BehaviorStore`: persistence boundary for recording, querying, and summarizing `BehaviorEvent` data.
|
|
96
|
+
- `InMemoryBehaviorStore`: simple in-memory implementation for tests, demos, and local composition.
|
|
97
|
+
- `BehaviorTracker` and `createBehaviorTracker`: buffered event capture with tenant/user context and OpenTelemetry instrumentation.
|
|
98
|
+
- `BehaviorAnalyzer`: converts `BehaviorSummary` data into `BehaviorInsights`.
|
|
99
|
+
- `insightsToOverlaySuggestion`: turns analysis output into an overlay-engine `OverlaySpec`.
|
|
100
|
+
- `insightsToWorkflowAdaptations`: turns workflow bottlenecks into lightweight adaptation notes.
|
|
101
|
+
|
|
102
|
+
### Core data contracts
|
|
103
|
+
|
|
104
|
+
- `BehaviorEvent`: discriminated union with three event kinds: `field_access`, `feature_usage`, and `workflow_step`.
|
|
105
|
+
- `BehaviorQuery`: filter shape used by `BehaviorStore.query()` and `BehaviorStore.summarize()`.
|
|
106
|
+
- `BehaviorSummary`: aggregated counts returned by store summarization.
|
|
107
|
+
- `BehaviorInsights`: analyzer output including hidden-field candidates, bottlenecks, and layout preference hints.
|
|
108
|
+
- `BehaviorAnalyzerOptions`: tuning knobs for inactivity threshold and minimum workflow sample size.
|
|
109
|
+
- `OverlaySuggestionOptions`: metadata required to build an overlay suggestion.
|
|
110
|
+
- `WorkflowAdaptation`: workflow, step, and note triple derived from bottlenecks.
|
|
111
|
+
|
|
112
|
+
### Preference model contracts
|
|
113
|
+
|
|
114
|
+
- `PreferenceDimensions`: the shared 7-dimension personalization model.
|
|
115
|
+
- `PreferenceScope`: source scope used when a preference value is resolved.
|
|
116
|
+
- `ResolvedPreferenceProfile`: canonical resolved preferences plus source attribution and constraint notes.
|
|
117
|
+
- `PreferenceResolutionContext`: minimal runtime context required to resolve a preference profile.
|
|
118
|
+
- `BundlePreferenceAdapter`: contract for resolving and persisting preference patches in runtime consumers.
|
|
119
|
+
|
|
120
|
+
## Public entrypoints
|
|
121
|
+
|
|
122
|
+
The root barrel at `src/index.ts` re-exports public symbols from:
|
|
123
|
+
|
|
124
|
+
- `adapter`
|
|
125
|
+
- `analyzer`
|
|
126
|
+
- `preference-dimensions`
|
|
127
|
+
- `store`
|
|
128
|
+
- `tracker`
|
|
129
|
+
- `types`
|
|
130
|
+
|
|
131
|
+
Published subpaths from `package.json`:
|
|
132
|
+
|
|
133
|
+
- `.`
|
|
134
|
+
- `./adapter`
|
|
135
|
+
- `./analyzer`
|
|
136
|
+
- `./docs`
|
|
137
|
+
- `./docs/behavior-tracking.docblock`
|
|
138
|
+
- `./docs/overlay-engine.docblock`
|
|
139
|
+
- `./docs/workflow-composition.docblock`
|
|
140
|
+
- `./preference-dimensions`
|
|
141
|
+
- `./store`
|
|
142
|
+
- `./tracker`
|
|
143
|
+
- `./types`
|
|
144
|
+
|
|
145
|
+
For application code, prefer `.` or the focused subpaths above. The `./docs*` subpaths exist for docblock registration and documentation surfaces.
|
|
146
|
+
|
|
147
|
+
## Operational semantics and gotchas
|
|
148
|
+
|
|
149
|
+
- `BehaviorTracker` buffers events in memory and flushes when the buffer reaches the configured size or when `autoFlushIntervalMs` is enabled.
|
|
150
|
+
- `flush()` persists the current buffer with `BehaviorStore.bulkRecord()`.
|
|
151
|
+
- `dispose()` clears the interval timer, then flushes any remaining buffered events.
|
|
152
|
+
- Each enqueue also emits OpenTelemetry metrics and tracing through `@opentelemetry/api`.
|
|
153
|
+
- `BehaviorAnalyzer` uses deterministic threshold heuristics. It does not do ranking, learning, or probabilistic inference.
|
|
154
|
+
- `insightsToOverlaySuggestion()` currently emits only `hideField` and `reorderFields` modifications.
|
|
155
|
+
- `layoutPreference` is inferred from field-count thresholds, not from UI render telemetry.
|
|
156
|
+
- `PreferenceDimensions` and related types are contracts. Durable persistence and runtime resolution live elsewhere.
|
|
157
|
+
|
|
158
|
+
## When not to use this package
|
|
159
|
+
|
|
160
|
+
- Do not use it as a full analytics warehouse or reporting system.
|
|
161
|
+
- Do not use it as the durable preference persistence layer.
|
|
162
|
+
- Do not use it as the overlay runtime or overlay registry implementation.
|
|
163
|
+
- Do not use it when you need workflow composition itself; this package only emits adaptation hints.
|
|
164
|
+
|
|
165
|
+
## Related packages
|
|
166
|
+
|
|
167
|
+
- `@contractspec/lib.overlay-engine`: runtime for registering, validating, and applying overlays.
|
|
168
|
+
- `@contractspec/lib.surface-runtime`: runtime consumer of preference resolution and adaptive surface behavior.
|
|
169
|
+
- `@contractspec/lib.workflow-composer`: workflow extension and composition runtime.
|
|
170
|
+
- `@contractspec/lib.contracts-spec`: shared spec and docblock contracts used across ContractSpec.
|
|
171
|
+
|
|
172
|
+
## Local commands
|
|
173
|
+
|
|
174
|
+
- `bun run lint:check`
|
|
175
|
+
- `bun run typecheck`
|
|
176
|
+
- `bun run test`
|
|
177
|
+
- `bun run build`
|
package/dist/adapter.js
CHANGED
|
@@ -1,47 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
function insightsToOverlaySuggestion(insights, options) {
|
|
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
|
-
};
|
|
36
|
-
}
|
|
37
|
-
function insightsToWorkflowAdaptations(insights) {
|
|
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
|
-
}));
|
|
43
|
-
}
|
|
44
|
-
export {
|
|
45
|
-
insightsToWorkflowAdaptations,
|
|
46
|
-
insightsToOverlaySuggestion
|
|
47
|
-
};
|
|
2
|
+
function o(t,e){let r=[];if(t.suggestedHiddenFields.forEach((i)=>{r.push({type:"hideField",field:i,reason:"Automatically hidden because usage is near zero"})}),t.frequentlyUsedFields.length)r.push({type:"reorderFields",fields:t.frequentlyUsedFields});if(!r.length)return null;return{overlayId:e.overlayId,version:e.version??"1.0.0",appliesTo:{tenantId:e.tenantId,capability:e.capability,userId:e.userId,role:e.role},modifications:r,metadata:{generatedAt:new Date().toISOString(),automated:!0}}}function n(t){return t.workflowBottlenecks.map((e)=>({workflow:e.workflow,step:e.step,note:`High drop rate (${Math.round(e.dropRate*100)}%) detected`}))}export{n as insightsToWorkflowAdaptations,o as insightsToOverlaySuggestion};
|
package/dist/analyzer.js
CHANGED
|
@@ -1,73 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
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
|
-
}
|
|
25
|
-
function buildInsights(summary, options) {
|
|
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
|
-
};
|
|
57
|
-
}
|
|
58
|
-
function detectLayout(summary) {
|
|
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";
|
|
70
|
-
}
|
|
71
|
-
export {
|
|
72
|
-
BehaviorAnalyzer
|
|
73
|
-
};
|
|
2
|
+
class h{store;options;constructor(e,t={}){this.store=e;this.options=t}async analyze(e){let t={tenantId:e.tenantId,userId:e.userId,role:e.role};if(e.windowMs)t.since=Date.now()-e.windowMs;let i=await this.store.summarize(t);return y(i,this.options)}}function y(e,t){let i=t.fieldInactivityThreshold??3,d=t.minSamples??10,a=[],u=[];for(let[o,r]of Object.entries(e.fieldCounts)){if(r<=i)a.push(o);if(r>=i*4)u.push(o)}let c=Object.entries(e.workflowStepCounts).flatMap(([o,r])=>{let s=Object.values(r).reduce((n,l)=>n+l,0);if(!s||s<d)return[];return Object.entries(r).filter(([,n])=>n/s<0.4).map(([n,l])=>({workflow:o,step:n,dropRate:1-l/s}))}),f=m(e);return{unusedFields:a,suggestedHiddenFields:a.slice(0,5),frequentlyUsedFields:u.slice(0,10),workflowBottlenecks:c,layoutPreference:f}}function m(e){let t=Object.keys(e.fieldCounts).length;if(!t)return;if(t>=15)return"table";if(t>=8)return"grid";return"form"}export{h as BehaviorAnalyzer};
|
package/dist/browser/adapter.js
CHANGED
|
@@ -1,46 +1 @@
|
|
|
1
|
-
|
|
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
|
-
};
|
|
1
|
+
function o(t,e){let r=[];if(t.suggestedHiddenFields.forEach((i)=>{r.push({type:"hideField",field:i,reason:"Automatically hidden because usage is near zero"})}),t.frequentlyUsedFields.length)r.push({type:"reorderFields",fields:t.frequentlyUsedFields});if(!r.length)return null;return{overlayId:e.overlayId,version:e.version??"1.0.0",appliesTo:{tenantId:e.tenantId,capability:e.capability,userId:e.userId,role:e.role},modifications:r,metadata:{generatedAt:new Date().toISOString(),automated:!0}}}function n(t){return t.workflowBottlenecks.map((e)=>({workflow:e.workflow,step:e.step,note:`High drop rate (${Math.round(e.dropRate*100)}%) detected`}))}export{n as insightsToWorkflowAdaptations,o as insightsToOverlaySuggestion};
|
package/dist/browser/analyzer.js
CHANGED
|
@@ -1,72 +1 @@
|
|
|
1
|
-
|
|
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
|
-
};
|
|
1
|
+
class h{store;options;constructor(e,t={}){this.store=e;this.options=t}async analyze(e){let t={tenantId:e.tenantId,userId:e.userId,role:e.role};if(e.windowMs)t.since=Date.now()-e.windowMs;let i=await this.store.summarize(t);return y(i,this.options)}}function y(e,t){let i=t.fieldInactivityThreshold??3,d=t.minSamples??10,a=[],u=[];for(let[o,r]of Object.entries(e.fieldCounts)){if(r<=i)a.push(o);if(r>=i*4)u.push(o)}let c=Object.entries(e.workflowStepCounts).flatMap(([o,r])=>{let s=Object.values(r).reduce((n,l)=>n+l,0);if(!s||s<d)return[];return Object.entries(r).filter(([,n])=>n/s<0.4).map(([n,l])=>({workflow:o,step:n,dropRate:1-l/s}))}),f=m(e);return{unusedFields:a,suggestedHiddenFields:a.slice(0,5),frequentlyUsedFields:u.slice(0,10),workflowBottlenecks:c,layoutPreference:f}}function m(e){let t=Object.keys(e.fieldCounts).length;if(!t)return;if(t>=15)return"table";if(t>=8)return"grid";return"form"}export{h as BehaviorAnalyzer};
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { registerDocBlocks } from "@contractspec/lib.contracts-spec/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
|
|
1
|
+
import{registerDocBlocks as d}from"@contractspec/lib.contracts-spec/docs";var f=[{id:"docs.personalization.behavior-tracking",title:"Behavior Tracking",summary:"`@contractspec/lib.personalization` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.",kind:"reference",visibility:"public",route:"/docs/personalization/behavior-tracking",tags:["personalization","behavior-tracking"],body:`# Behavior Tracking
|
|
13
2
|
|
|
14
3
|
\`@contractspec/lib.personalization\` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.
|
|
15
4
|
|
|
@@ -88,10 +77,4 @@ When the adapter returns an overlay spec, pass it to the overlay engine to regis
|
|
|
88
77
|
|
|
89
78
|
|
|
90
79
|
|
|
91
|
-
`
|
|
92
|
-
}
|
|
93
|
-
];
|
|
94
|
-
registerDocBlocks(personalization_behavior_tracking_DocBlocks);
|
|
95
|
-
export {
|
|
96
|
-
personalization_behavior_tracking_DocBlocks
|
|
97
|
-
};
|
|
80
|
+
`}];d(f);export{f as personalization_behavior_tracking_DocBlocks};
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { registerDocBlocks } from "@contractspec/lib.contracts-spec/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
|
|
1
|
+
import{registerDocBlocks as m}from"@contractspec/lib.contracts-spec/docs";var d=[{id:"docs.personalization.behavior-tracking",title:"Behavior Tracking",summary:"`@contractspec/lib.personalization` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.",kind:"reference",visibility:"public",route:"/docs/personalization/behavior-tracking",tags:["personalization","behavior-tracking"],body:`# Behavior Tracking
|
|
13
2
|
|
|
14
3
|
\`@contractspec/lib.personalization\` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.
|
|
15
4
|
|
|
@@ -88,23 +77,7 @@ When the adapter returns an overlay spec, pass it to the overlay engine to regis
|
|
|
88
77
|
|
|
89
78
|
|
|
90
79
|
|
|
91
|
-
`
|
|
92
|
-
}
|
|
93
|
-
];
|
|
94
|
-
registerDocBlocks(personalization_behavior_tracking_DocBlocks);
|
|
95
|
-
|
|
96
|
-
// src/docs/overlay-engine.docblock.ts
|
|
97
|
-
import { registerDocBlocks as registerDocBlocks2 } from "@contractspec/lib.contracts-spec/docs";
|
|
98
|
-
var personalization_overlay_engine_DocBlocks = [
|
|
99
|
-
{
|
|
100
|
-
id: "docs.personalization.overlay-engine",
|
|
101
|
-
title: "Overlay Engine",
|
|
102
|
-
summary: "`@contractspec/lib.overlay-engine` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.",
|
|
103
|
-
kind: "reference",
|
|
104
|
-
visibility: "public",
|
|
105
|
-
route: "/docs/personalization/overlay-engine",
|
|
106
|
-
tags: ["personalization", "overlay-engine"],
|
|
107
|
-
body: `# Overlay Engine
|
|
80
|
+
`}];m(d);import{registerDocBlocks as j}from"@contractspec/lib.contracts-spec/docs";var q=[{id:"docs.personalization.overlay-engine",title:"Overlay Engine",summary:"`@contractspec/lib.overlay-engine` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.",kind:"reference",visibility:"public",route:"/docs/personalization/overlay-engine",tags:["personalization","overlay-engine"],body:`# Overlay Engine
|
|
108
81
|
|
|
109
82
|
\`@contractspec/lib.overlay-engine\` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.
|
|
110
83
|
|
|
@@ -184,23 +157,7 @@ const result = engine.apply({
|
|
|
184
157
|
|
|
185
158
|
|
|
186
159
|
|
|
187
|
-
`
|
|
188
|
-
}
|
|
189
|
-
];
|
|
190
|
-
registerDocBlocks2(personalization_overlay_engine_DocBlocks);
|
|
191
|
-
|
|
192
|
-
// src/docs/workflow-composition.docblock.ts
|
|
193
|
-
import { registerDocBlocks as registerDocBlocks3 } from "@contractspec/lib.contracts-spec/docs";
|
|
194
|
-
var personalization_workflow_composition_DocBlocks = [
|
|
195
|
-
{
|
|
196
|
-
id: "docs.personalization.workflow-composition",
|
|
197
|
-
title: "Workflow Composition",
|
|
198
|
-
summary: "`@contractspec/lib.workflow-composer` composes base WorkflowSpecs with tenant/role/device-specific extensions, strict validation, deterministic merge ordering, metadata/annotation overlays, and orphan-graph protection for hidden-step rewrites.",
|
|
199
|
-
kind: "reference",
|
|
200
|
-
visibility: "public",
|
|
201
|
-
route: "/docs/personalization/workflow-composition",
|
|
202
|
-
tags: ["personalization", "workflow-composition"],
|
|
203
|
-
body: `# Workflow Composition
|
|
160
|
+
`}];j(q);import{registerDocBlocks as u}from"@contractspec/lib.contracts-spec/docs";var x=[{id:"docs.personalization.workflow-composition",title:"Workflow Composition",summary:"`@contractspec/lib.workflow-composer` composes base WorkflowSpecs with tenant/role/device-specific extensions, strict validation, deterministic merge ordering, metadata/annotation overlays, and orphan-graph protection for hidden-step rewrites.",kind:"reference",visibility:"public",route:"/docs/personalization/workflow-composition",tags:["personalization","workflow-composition"],body:`# Workflow Composition
|
|
204
161
|
|
|
205
162
|
\`@contractspec/lib.workflow-composer\` composes base WorkflowSpecs with tenant/role/device-specific extensions.
|
|
206
163
|
|
|
@@ -252,7 +209,4 @@ workflowRunner.execute(runtimeSpec, ctx);
|
|
|
252
209
|
- \`metadata\` and \`annotations\` overlays are merged into the composed runtime workflow for downstream observability and rollout tracing.
|
|
253
210
|
|
|
254
211
|
This keeps tenant overlays additive, auditable, and replay-safe.
|
|
255
|
-
`
|
|
256
|
-
}
|
|
257
|
-
];
|
|
258
|
-
registerDocBlocks3(personalization_workflow_composition_DocBlocks);
|
|
212
|
+
`}];u(x);
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { registerDocBlocks } from "@contractspec/lib.contracts-spec/docs";
|
|
3
|
-
var personalization_overlay_engine_DocBlocks = [
|
|
4
|
-
{
|
|
5
|
-
id: "docs.personalization.overlay-engine",
|
|
6
|
-
title: "Overlay Engine",
|
|
7
|
-
summary: "`@contractspec/lib.overlay-engine` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.",
|
|
8
|
-
kind: "reference",
|
|
9
|
-
visibility: "public",
|
|
10
|
-
route: "/docs/personalization/overlay-engine",
|
|
11
|
-
tags: ["personalization", "overlay-engine"],
|
|
12
|
-
body: `# Overlay Engine
|
|
1
|
+
import{registerDocBlocks as b}from"@contractspec/lib.contracts-spec/docs";var d=[{id:"docs.personalization.overlay-engine",title:"Overlay Engine",summary:"`@contractspec/lib.overlay-engine` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.",kind:"reference",visibility:"public",route:"/docs/personalization/overlay-engine",tags:["personalization","overlay-engine"],body:`# Overlay Engine
|
|
13
2
|
|
|
14
3
|
\`@contractspec/lib.overlay-engine\` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.
|
|
15
4
|
|
|
@@ -89,10 +78,4 @@ const result = engine.apply({
|
|
|
89
78
|
|
|
90
79
|
|
|
91
80
|
|
|
92
|
-
`
|
|
93
|
-
}
|
|
94
|
-
];
|
|
95
|
-
registerDocBlocks(personalization_overlay_engine_DocBlocks);
|
|
96
|
-
export {
|
|
97
|
-
personalization_overlay_engine_DocBlocks
|
|
98
|
-
};
|
|
81
|
+
`}];b(d);export{d as personalization_overlay_engine_DocBlocks};
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { registerDocBlocks } from "@contractspec/lib.contracts-spec/docs";
|
|
3
|
-
var personalization_workflow_composition_DocBlocks = [
|
|
4
|
-
{
|
|
5
|
-
id: "docs.personalization.workflow-composition",
|
|
6
|
-
title: "Workflow Composition",
|
|
7
|
-
summary: "`@contractspec/lib.workflow-composer` composes base WorkflowSpecs with tenant/role/device-specific extensions, strict validation, deterministic merge ordering, metadata/annotation overlays, and orphan-graph protection for hidden-step rewrites.",
|
|
8
|
-
kind: "reference",
|
|
9
|
-
visibility: "public",
|
|
10
|
-
route: "/docs/personalization/workflow-composition",
|
|
11
|
-
tags: ["personalization", "workflow-composition"],
|
|
12
|
-
body: `# Workflow Composition
|
|
1
|
+
import{registerDocBlocks as b}from"@contractspec/lib.contracts-spec/docs";var d=[{id:"docs.personalization.workflow-composition",title:"Workflow Composition",summary:"`@contractspec/lib.workflow-composer` composes base WorkflowSpecs with tenant/role/device-specific extensions, strict validation, deterministic merge ordering, metadata/annotation overlays, and orphan-graph protection for hidden-step rewrites.",kind:"reference",visibility:"public",route:"/docs/personalization/workflow-composition",tags:["personalization","workflow-composition"],body:`# Workflow Composition
|
|
13
2
|
|
|
14
3
|
\`@contractspec/lib.workflow-composer\` composes base WorkflowSpecs with tenant/role/device-specific extensions.
|
|
15
4
|
|
|
@@ -61,10 +50,4 @@ workflowRunner.execute(runtimeSpec, ctx);
|
|
|
61
50
|
- \`metadata\` and \`annotations\` overlays are merged into the composed runtime workflow for downstream observability and rollout tracing.
|
|
62
51
|
|
|
63
52
|
This keeps tenant overlays additive, auditable, and replay-safe.
|
|
64
|
-
`
|
|
65
|
-
}
|
|
66
|
-
];
|
|
67
|
-
registerDocBlocks(personalization_workflow_composition_DocBlocks);
|
|
68
|
-
export {
|
|
69
|
-
personalization_workflow_composition_DocBlocks
|
|
70
|
-
};
|
|
53
|
+
`}];b(d);export{d as personalization_workflow_composition_DocBlocks};
|