@contractspec/lib.personalization 6.0.17 → 6.0.19

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.
@@ -1,16 +1,5 @@
1
1
  // @bun
2
- // src/docs/workflow-composition.docblock.ts
3
- import { registerDocBlocks } from "@contractspec/lib.contracts-spec/docs";
4
- var personalization_workflow_composition_DocBlocks = [
5
- {
6
- id: "docs.personalization.workflow-composition",
7
- title: "Workflow Composition",
8
- 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.",
9
- kind: "reference",
10
- visibility: "public",
11
- route: "/docs/personalization/workflow-composition",
12
- tags: ["personalization", "workflow-composition"],
13
- body: `# Workflow Composition
2
+ 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
14
3
 
15
4
  \`@contractspec/lib.workflow-composer\` composes base WorkflowSpecs with tenant/role/device-specific extensions.
16
5
 
@@ -62,10 +51,4 @@ workflowRunner.execute(runtimeSpec, ctx);
62
51
  - \`metadata\` and \`annotations\` overlays are merged into the composed runtime workflow for downstream observability and rollout tracing.
63
52
 
64
53
  This keeps tenant overlays additive, auditable, and replay-safe.
65
- `
66
- }
67
- ];
68
- registerDocBlocks(personalization_workflow_composition_DocBlocks);
69
- export {
70
- personalization_workflow_composition_DocBlocks
71
- };
54
+ `}];b(d);export{d as personalization_workflow_composition_DocBlocks};
package/dist/index.js CHANGED
@@ -1,129 +1,5 @@
1
1
  // @bun
2
- // src/adapter.ts
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
-
45
- // src/analyzer.ts
46
- var DEFAULT_THRESHOLD = 3;
47
-
48
- class BehaviorAnalyzer {
49
- store;
50
- options;
51
- constructor(store, options = {}) {
52
- this.store = store;
53
- this.options = options;
54
- }
55
- async analyze(params) {
56
- const query = {
57
- tenantId: params.tenantId,
58
- userId: params.userId,
59
- role: params.role
60
- };
61
- if (params.windowMs) {
62
- query.since = Date.now() - params.windowMs;
63
- }
64
- const summary = await this.store.summarize(query);
65
- return buildInsights(summary, this.options);
66
- }
67
- }
68
- function buildInsights(summary, options) {
69
- const threshold = options.fieldInactivityThreshold ?? DEFAULT_THRESHOLD;
70
- const minSamples = options.minSamples ?? 10;
71
- const ignoredFields = [];
72
- const frequentlyUsedFields = [];
73
- for (const [field, count] of Object.entries(summary.fieldCounts)) {
74
- if (count <= threshold) {
75
- ignoredFields.push(field);
76
- }
77
- if (count >= threshold * 4) {
78
- frequentlyUsedFields.push(field);
79
- }
80
- }
81
- const workflowBottlenecks = Object.entries(summary.workflowStepCounts).flatMap(([workflow, steps]) => {
82
- const total = Object.values(steps).reduce((acc, value) => acc + value, 0);
83
- if (!total || total < minSamples) {
84
- return [];
85
- }
86
- return Object.entries(steps).filter(([, count]) => count / total < 0.4).map(([step, count]) => ({
87
- workflow,
88
- step,
89
- dropRate: 1 - count / total
90
- }));
91
- });
92
- const layoutPreference = detectLayout(summary);
93
- return {
94
- unusedFields: ignoredFields,
95
- suggestedHiddenFields: ignoredFields.slice(0, 5),
96
- frequentlyUsedFields: frequentlyUsedFields.slice(0, 10),
97
- workflowBottlenecks,
98
- layoutPreference
99
- };
100
- }
101
- function detectLayout(summary) {
102
- const fieldCount = Object.keys(summary.fieldCounts).length;
103
- if (!fieldCount) {
104
- return;
105
- }
106
- if (fieldCount >= 15) {
107
- return "table";
108
- }
109
- if (fieldCount >= 8) {
110
- return "grid";
111
- }
112
- return "form";
113
- }
114
-
115
- // src/docs/behavior-tracking.docblock.ts
116
- import { registerDocBlocks } from "@contractspec/lib.contracts-spec/docs";
117
- var personalization_behavior_tracking_DocBlocks = [
118
- {
119
- id: "docs.personalization.behavior-tracking",
120
- title: "Behavior Tracking",
121
- summary: "`@contractspec/lib.personalization` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.",
122
- kind: "reference",
123
- visibility: "public",
124
- route: "/docs/personalization/behavior-tracking",
125
- tags: ["personalization", "behavior-tracking"],
126
- body: `# Behavior Tracking
2
+ function T(e,t){let r=[];if(e.suggestedHiddenFields.forEach((o)=>{r.push({type:"hideField",field:o,reason:"Automatically hidden because usage is near zero"})}),e.frequentlyUsedFields.length)r.push({type:"reorderFields",fields:e.frequentlyUsedFields});if(!r.length)return null;return{overlayId:t.overlayId,version:t.version??"1.0.0",appliesTo:{tenantId:t.tenantId,capability:t.capability,userId:t.userId,role:t.role},modifications:r,metadata:{generatedAt:new Date().toISOString(),automated:!0}}}function O(e){return e.workflowBottlenecks.map((t)=>({workflow:t.workflow,step:t.step,note:`High drop rate (${Math.round(t.dropRate*100)}%) detected`}))}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 r=await this.store.summarize(t);return v(r,this.options)}}function v(e,t){let r=t.fieldInactivityThreshold??3,o=t.minSamples??10,i=[],l=[];for(let[n,s]of Object.entries(e.fieldCounts)){if(s<=r)i.push(n);if(s>=r*4)l.push(n)}let c=Object.entries(e.workflowStepCounts).flatMap(([n,s])=>{let u=Object.values(s).reduce((a,d)=>a+d,0);if(!u||u<o)return[];return Object.entries(s).filter(([,a])=>a/u<0.4).map(([a,d])=>({workflow:n,step:a,dropRate:1-d/u}))}),p=m(e);return{unusedFields:i,suggestedHiddenFields:i.slice(0,5),frequentlyUsedFields:l.slice(0,10),workflowBottlenecks:c,layoutPreference:p}}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"}import{registerDocBlocks as g}from"@contractspec/lib.contracts-spec/docs";var y=[{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
127
3
 
128
4
  \`@contractspec/lib.personalization\` provides primitives to observe how tenants/users interact with specs and turn that telemetry into personalization insights.
129
5
 
@@ -202,23 +78,7 @@ When the adapter returns an overlay spec, pass it to the overlay engine to regis
202
78
 
203
79
 
204
80
 
205
- `
206
- }
207
- ];
208
- registerDocBlocks(personalization_behavior_tracking_DocBlocks);
209
-
210
- // src/docs/overlay-engine.docblock.ts
211
- import { registerDocBlocks as registerDocBlocks2 } from "@contractspec/lib.contracts-spec/docs";
212
- var personalization_overlay_engine_DocBlocks = [
213
- {
214
- id: "docs.personalization.overlay-engine",
215
- title: "Overlay Engine",
216
- summary: "`@contractspec/lib.overlay-engine` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.",
217
- kind: "reference",
218
- visibility: "public",
219
- route: "/docs/personalization/overlay-engine",
220
- tags: ["personalization", "overlay-engine"],
221
- body: `# Overlay Engine
81
+ `}];g(y);import{registerDocBlocks as w}from"@contractspec/lib.contracts-spec/docs";var B=[{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
222
82
 
223
83
  \`@contractspec/lib.overlay-engine\` is the canonical runtime for OverlaySpecs. It validates specs, tracks scope precedence, and exposes hooks for React renderers.
224
84
 
@@ -298,23 +158,7 @@ const result = engine.apply({
298
158
 
299
159
 
300
160
 
301
- `
302
- }
303
- ];
304
- registerDocBlocks2(personalization_overlay_engine_DocBlocks);
305
-
306
- // src/docs/workflow-composition.docblock.ts
307
- import { registerDocBlocks as registerDocBlocks3 } from "@contractspec/lib.contracts-spec/docs";
308
- var personalization_workflow_composition_DocBlocks = [
309
- {
310
- id: "docs.personalization.workflow-composition",
311
- title: "Workflow Composition",
312
- 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.",
313
- kind: "reference",
314
- visibility: "public",
315
- route: "/docs/personalization/workflow-composition",
316
- tags: ["personalization", "workflow-composition"],
317
- body: `# Workflow Composition
161
+ `}];w(B);import{registerDocBlocks as k}from"@contractspec/lib.contracts-spec/docs";var I=[{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
318
162
 
319
163
  \`@contractspec/lib.workflow-composer\` composes base WorkflowSpecs with tenant/role/device-specific extensions.
320
164
 
@@ -366,178 +210,4 @@ workflowRunner.execute(runtimeSpec, ctx);
366
210
  - \`metadata\` and \`annotations\` overlays are merged into the composed runtime workflow for downstream observability and rollout tracing.
367
211
 
368
212
  This keeps tenant overlays additive, auditable, and replay-safe.
369
- `
370
- }
371
- ];
372
- registerDocBlocks3(personalization_workflow_composition_DocBlocks);
373
- // src/store.ts
374
- class InMemoryBehaviorStore {
375
- events = [];
376
- async record(event) {
377
- this.events.push(event);
378
- }
379
- async bulkRecord(events) {
380
- this.events.push(...events);
381
- }
382
- async query(query) {
383
- return filterEvents(this.events, query);
384
- }
385
- async summarize(query) {
386
- const events = await this.query(query);
387
- const summary = {
388
- fieldCounts: {},
389
- featureCounts: {},
390
- workflowStepCounts: {},
391
- totalEvents: events.length
392
- };
393
- events.forEach((event) => {
394
- switch (event.type) {
395
- case "field_access":
396
- summary.fieldCounts[event.field] = (summary.fieldCounts[event.field] ?? 0) + 1;
397
- break;
398
- case "feature_usage":
399
- summary.featureCounts[event.feature] = (summary.featureCounts[event.feature] ?? 0) + 1;
400
- break;
401
- case "workflow_step": {
402
- const workflow = summary.workflowStepCounts[event.workflow] ??= {};
403
- workflow[event.step] = (workflow[event.step] ?? 0) + 1;
404
- break;
405
- }
406
- default:
407
- break;
408
- }
409
- });
410
- return summary;
411
- }
412
- async clear() {
413
- this.events = [];
414
- }
415
- }
416
- function filterEvents(events, query) {
417
- return events.filter((event) => {
418
- if (query.tenantId && event.tenantId !== query.tenantId) {
419
- return false;
420
- }
421
- if (query.userId && event.userId !== query.userId) {
422
- return false;
423
- }
424
- if (query.role && event.role !== query.role) {
425
- return false;
426
- }
427
- if (query.since && event.timestamp < query.since) {
428
- return false;
429
- }
430
- if (query.until && event.timestamp > query.until) {
431
- return false;
432
- }
433
- if (query.operation && event.type === "field_access" && event.operation !== query.operation) {
434
- return false;
435
- }
436
- if (query.feature && event.type === "feature_usage" && event.feature !== query.feature) {
437
- return false;
438
- }
439
- if (query.workflow && event.type === "workflow_step" && event.workflow !== query.workflow) {
440
- return false;
441
- }
442
- return true;
443
- });
444
- }
445
-
446
- // src/tracker.ts
447
- import { metrics, trace } from "@opentelemetry/api";
448
- var DEFAULT_BUFFER_SIZE = 25;
449
-
450
- class BehaviorTracker {
451
- store;
452
- context;
453
- tracer = trace.getTracer("lssm.personalization", "1.0.0");
454
- counter = metrics.getMeter("lssm.personalization", "1.0.0").createCounter("lssm.personalization.events", {
455
- description: "Behavior events tracked for personalization"
456
- });
457
- buffer = [];
458
- bufferSize;
459
- flushTimer;
460
- constructor(options) {
461
- this.store = options.store;
462
- this.context = options.context;
463
- this.bufferSize = options.bufferSize ?? DEFAULT_BUFFER_SIZE;
464
- if (options.autoFlushIntervalMs) {
465
- this.flushTimer = setInterval(() => {
466
- this.flush();
467
- }, options.autoFlushIntervalMs);
468
- }
469
- }
470
- trackFieldAccess(input) {
471
- const event = {
472
- type: "field_access",
473
- operation: input.operation,
474
- field: input.field,
475
- timestamp: Date.now(),
476
- ...this.context,
477
- metadata: { ...this.context.metadata, ...input.metadata }
478
- };
479
- this.enqueue(event);
480
- }
481
- trackFeatureUsage(input) {
482
- const event = {
483
- type: "feature_usage",
484
- feature: input.feature,
485
- action: input.action,
486
- timestamp: Date.now(),
487
- ...this.context,
488
- metadata: { ...this.context.metadata, ...input.metadata }
489
- };
490
- this.enqueue(event);
491
- }
492
- trackWorkflowStep(input) {
493
- const event = {
494
- type: "workflow_step",
495
- workflow: input.workflow,
496
- step: input.step,
497
- status: input.status,
498
- timestamp: Date.now(),
499
- ...this.context,
500
- metadata: { ...this.context.metadata, ...input.metadata }
501
- };
502
- this.enqueue(event);
503
- }
504
- async flush() {
505
- if (!this.buffer.length)
506
- return;
507
- const events = this.buffer;
508
- this.buffer = [];
509
- await this.store.bulkRecord(events);
510
- }
511
- async dispose() {
512
- if (this.flushTimer) {
513
- clearInterval(this.flushTimer);
514
- }
515
- await this.flush();
516
- }
517
- enqueue(event) {
518
- this.buffer.push(event);
519
- this.counter.add(1, {
520
- tenantId: this.context.tenantId,
521
- type: event.type
522
- });
523
- this.tracer.startActiveSpan(`personalization.${event.type}`, (span) => {
524
- span.setAttribute("tenant.id", this.context.tenantId);
525
- if (this.context.userId)
526
- span.setAttribute("user.id", this.context.userId);
527
- span.setAttribute("personalization.event_type", event.type);
528
- span.end();
529
- });
530
- if (this.buffer.length >= this.bufferSize) {
531
- this.flush();
532
- }
533
- }
534
- }
535
- var createBehaviorTracker = (options) => new BehaviorTracker(options);
536
- export {
537
- insightsToWorkflowAdaptations,
538
- insightsToOverlaySuggestion,
539
- createBehaviorTracker,
540
- InMemoryBehaviorStore,
541
- BehaviorTracker,
542
- BehaviorAnalyzer
543
- };
213
+ `}];k(I);class x{events=[];async record(e){this.events.push(e)}async bulkRecord(e){this.events.push(...e)}async query(e){return b(this.events,e)}async summarize(e){let t=await this.query(e),r={fieldCounts:{},featureCounts:{},workflowStepCounts:{},totalEvents:t.length};return t.forEach((o)=>{switch(o.type){case"field_access":r.fieldCounts[o.field]=(r.fieldCounts[o.field]??0)+1;break;case"feature_usage":r.featureCounts[o.feature]=(r.featureCounts[o.feature]??0)+1;break;case"workflow_step":{let i=r.workflowStepCounts[o.workflow]??={};i[o.step]=(i[o.step]??0)+1;break}default:break}}),r}async clear(){this.events=[]}}function b(e,t){return e.filter((r)=>{if(t.tenantId&&r.tenantId!==t.tenantId)return!1;if(t.userId&&r.userId!==t.userId)return!1;if(t.role&&r.role!==t.role)return!1;if(t.since&&r.timestamp<t.since)return!1;if(t.until&&r.timestamp>t.until)return!1;if(t.operation&&r.type==="field_access"&&r.operation!==t.operation)return!1;if(t.feature&&r.type==="feature_usage"&&r.feature!==t.feature)return!1;if(t.workflow&&r.type==="workflow_step"&&r.workflow!==t.workflow)return!1;return!0})}import{metrics as S,trace as E}from"@opentelemetry/api";var F=25;class f{store;context;tracer=E.getTracer("lssm.personalization","1.0.0");counter=S.getMeter("lssm.personalization","1.0.0").createCounter("lssm.personalization.events",{description:"Behavior events tracked for personalization"});buffer=[];bufferSize;flushTimer;constructor(e){if(this.store=e.store,this.context=e.context,this.bufferSize=e.bufferSize??F,e.autoFlushIntervalMs)this.flushTimer=setInterval(()=>{this.flush()},e.autoFlushIntervalMs)}trackFieldAccess(e){let t={type:"field_access",operation:e.operation,field:e.field,timestamp:Date.now(),...this.context,metadata:{...this.context.metadata,...e.metadata}};this.enqueue(t)}trackFeatureUsage(e){let t={type:"feature_usage",feature:e.feature,action:e.action,timestamp:Date.now(),...this.context,metadata:{...this.context.metadata,...e.metadata}};this.enqueue(t)}trackWorkflowStep(e){let t={type:"workflow_step",workflow:e.workflow,step:e.step,status:e.status,timestamp:Date.now(),...this.context,metadata:{...this.context.metadata,...e.metadata}};this.enqueue(t)}async flush(){if(!this.buffer.length)return;let e=this.buffer;this.buffer=[],await this.store.bulkRecord(e)}async dispose(){if(this.flushTimer)clearInterval(this.flushTimer);await this.flush()}enqueue(e){if(this.buffer.push(e),this.counter.add(1,{tenantId:this.context.tenantId,type:e.type}),this.tracer.startActiveSpan(`personalization.${e.type}`,(t)=>{if(t.setAttribute("tenant.id",this.context.tenantId),this.context.userId)t.setAttribute("user.id",this.context.userId);t.setAttribute("personalization.event_type",e.type),t.end()}),this.buffer.length>=this.bufferSize)this.flush()}}var D=(e)=>new f(e);export{O as insightsToWorkflowAdaptations,T as insightsToOverlaySuggestion,D as createBehaviorTracker,x as InMemoryBehaviorStore,f as BehaviorTracker,h as BehaviorAnalyzer};
@@ -1,46 +1 @@
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
- };
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};
@@ -1,72 +1 @@
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
- };
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
- // src/docs/behavior-tracking.docblock.ts
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
- // src/docs/behavior-tracking.docblock.ts
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
- // src/docs/overlay-engine.docblock.ts
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};