@contractspec/example.personalization 3.7.6 → 3.7.10
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/.turbo/turbo-build.log +36 -30
- package/AGENTS.md +43 -19
- package/CHANGELOG.md +28 -0
- package/README.md +67 -16
- package/dist/behavior-tracking.js +3 -3
- package/dist/browser/behavior-tracking.js +3 -3
- package/dist/browser/index.js +154 -30
- package/dist/browser/overlay-customization.js +3 -3
- package/dist/browser/personalization.experiment.js +62 -0
- package/dist/browser/personalization.theme.js +64 -0
- package/dist/browser/workflow-extension.js +1 -1
- package/dist/contracts.test.d.ts +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +154 -30
- package/dist/node/behavior-tracking.js +3 -3
- package/dist/node/index.js +154 -30
- package/dist/node/overlay-customization.js +3 -3
- package/dist/node/personalization.experiment.js +62 -0
- package/dist/node/personalization.theme.js +64 -0
- package/dist/node/workflow-extension.js +1 -1
- package/dist/overlay-customization.js +3 -3
- package/dist/personalization.experiment.d.ts +2 -0
- package/dist/personalization.experiment.js +63 -0
- package/dist/personalization.theme.d.ts +2 -0
- package/dist/personalization.theme.js +65 -0
- package/dist/workflow-extension.js +1 -1
- package/package.json +38 -10
- package/src/behavior-tracking.ts +37 -37
- package/src/contracts.test.ts +20 -0
- package/src/docs/personalization.docblock.ts +21 -21
- package/src/example.ts +26 -26
- package/src/index.ts +4 -2
- package/src/overlay-customization.ts +45 -45
- package/src/personalization.experiment.ts +61 -0
- package/src/personalization.feature.ts +16 -16
- package/src/personalization.theme.ts +63 -0
- package/src/workflow-extension.ts +47 -47
- package/tsconfig.json +17 -15
- package/tsdown.config.js +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/workflow-extension.ts
|
|
2
2
|
import { StabilityEnum } from "@contractspec/lib.contracts-spec";
|
|
3
|
-
import { WorkflowComposer } from "@contractspec/lib.workflow-composer";
|
|
4
3
|
import { Logger, LogLevel } from "@contractspec/lib.logger";
|
|
4
|
+
import { WorkflowComposer } from "@contractspec/lib.workflow-composer";
|
|
5
5
|
var logger = new Logger({
|
|
6
6
|
level: LogLevel.INFO,
|
|
7
7
|
environment: "production",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/overlay-customization.ts
|
|
3
|
-
import {
|
|
4
|
-
import { signOverlay } from "@contractspec/lib.overlay-engine/signer";
|
|
3
|
+
import { Logger, LogLevel } from "@contractspec/lib.logger";
|
|
5
4
|
import {
|
|
6
5
|
OverlayEngine,
|
|
7
6
|
OverlayRegistry
|
|
8
7
|
} from "@contractspec/lib.overlay-engine";
|
|
9
|
-
import {
|
|
8
|
+
import { signOverlay } from "@contractspec/lib.overlay-engine/signer";
|
|
9
|
+
import { defineOverlay } from "@contractspec/lib.overlay-engine/spec";
|
|
10
10
|
var logger = new Logger({
|
|
11
11
|
level: LogLevel.INFO,
|
|
12
12
|
environment: "production",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/personalization.experiment.ts
|
|
3
|
+
import {
|
|
4
|
+
OwnersEnum,
|
|
5
|
+
StabilityEnum
|
|
6
|
+
} from "@contractspec/lib.contracts-spec/ownership";
|
|
7
|
+
var PersonalizationExperiment = {
|
|
8
|
+
meta: {
|
|
9
|
+
key: "personalization.experiment.overlay-copy",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
title: "Personalization Overlay Copy Experiment",
|
|
12
|
+
description: "Tests a control onboarding copy against a personalized overlay variant.",
|
|
13
|
+
domain: "personalization",
|
|
14
|
+
owners: [OwnersEnum.PlatformCore],
|
|
15
|
+
tags: ["personalization", "experiment", "overlay"],
|
|
16
|
+
stability: StabilityEnum.Experimental
|
|
17
|
+
},
|
|
18
|
+
controlVariant: "control",
|
|
19
|
+
variants: [
|
|
20
|
+
{
|
|
21
|
+
id: "control",
|
|
22
|
+
key: "control",
|
|
23
|
+
description: "Default onboarding copy and standard workflow."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "personalized-overlay",
|
|
27
|
+
key: "personalized-overlay",
|
|
28
|
+
description: "Personalized copy with a branded theme override.",
|
|
29
|
+
overrides: [
|
|
30
|
+
{
|
|
31
|
+
type: "theme",
|
|
32
|
+
target: "personalization.theme.guided-onboarding",
|
|
33
|
+
version: "1.0.0"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: "workflow",
|
|
37
|
+
target: "billing.invoiceApproval",
|
|
38
|
+
version: "1.0.0"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
allocation: {
|
|
44
|
+
type: "sticky",
|
|
45
|
+
attribute: "userId",
|
|
46
|
+
salt: "personalization-overlay-copy"
|
|
47
|
+
},
|
|
48
|
+
successMetrics: [
|
|
49
|
+
{
|
|
50
|
+
key: "checklist-completion-rate",
|
|
51
|
+
telemetryEvent: {
|
|
52
|
+
key: "personalization.assignment.completed",
|
|
53
|
+
version: "1.0.0"
|
|
54
|
+
},
|
|
55
|
+
aggregation: "count",
|
|
56
|
+
target: 1
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
tags: ["personalization", "experiment"]
|
|
60
|
+
};
|
|
61
|
+
export {
|
|
62
|
+
PersonalizationExperiment
|
|
63
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/personalization.theme.ts
|
|
3
|
+
import {
|
|
4
|
+
OwnersEnum,
|
|
5
|
+
StabilityEnum
|
|
6
|
+
} from "@contractspec/lib.contracts-spec/ownership";
|
|
7
|
+
var PersonalizationTheme = {
|
|
8
|
+
meta: {
|
|
9
|
+
key: "personalization.theme.guided-onboarding",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
title: "Guided Onboarding Theme",
|
|
12
|
+
description: "Theme tokens used when the personalized onboarding experience is active.",
|
|
13
|
+
domain: "personalization",
|
|
14
|
+
owners: [OwnersEnum.PlatformCore],
|
|
15
|
+
tags: ["personalization", "theme", "onboarding"],
|
|
16
|
+
stability: StabilityEnum.Experimental,
|
|
17
|
+
scopes: ["tenant", "user"]
|
|
18
|
+
},
|
|
19
|
+
tokens: {
|
|
20
|
+
colors: {
|
|
21
|
+
surface: { value: "#FCF6E8" },
|
|
22
|
+
accent: { value: "#C8742A" },
|
|
23
|
+
text: { value: "#2F2419" }
|
|
24
|
+
},
|
|
25
|
+
radii: {
|
|
26
|
+
card: { value: 18 }
|
|
27
|
+
},
|
|
28
|
+
space: {
|
|
29
|
+
panel: { value: 24 }
|
|
30
|
+
},
|
|
31
|
+
typography: {
|
|
32
|
+
body: { value: 16 },
|
|
33
|
+
title: { value: 28 }
|
|
34
|
+
},
|
|
35
|
+
motion: {
|
|
36
|
+
stagger: { value: "180ms" }
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
components: [
|
|
40
|
+
{
|
|
41
|
+
component: "OnboardingChecklist",
|
|
42
|
+
variants: {
|
|
43
|
+
guided: {
|
|
44
|
+
props: {
|
|
45
|
+
emphasis: "warm"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
overrides: [
|
|
52
|
+
{
|
|
53
|
+
scope: "tenant",
|
|
54
|
+
target: "tenant:acme",
|
|
55
|
+
tokens: {
|
|
56
|
+
colors: {
|
|
57
|
+
accent: { value: "#8A4B12" }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
export {
|
|
64
|
+
PersonalizationTheme
|
|
65
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/workflow-extension.ts
|
|
3
3
|
import { StabilityEnum } from "@contractspec/lib.contracts-spec";
|
|
4
|
-
import { WorkflowComposer } from "@contractspec/lib.workflow-composer";
|
|
5
4
|
import { Logger, LogLevel } from "@contractspec/lib.logger";
|
|
5
|
+
import { WorkflowComposer } from "@contractspec/lib.workflow-composer";
|
|
6
6
|
var logger = new Logger({
|
|
7
7
|
level: LogLevel.INFO,
|
|
8
8
|
environment: "production",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/example.personalization",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.10",
|
|
4
4
|
"description": "Personalization examples: behavior tracking, overlay customization, workflow extension.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -47,6 +47,13 @@
|
|
|
47
47
|
"node": "./dist/node/overlay-customization.js",
|
|
48
48
|
"default": "./dist/overlay-customization.js"
|
|
49
49
|
},
|
|
50
|
+
"./personalization.experiment": {
|
|
51
|
+
"types": "./dist/personalization.experiment.d.ts",
|
|
52
|
+
"browser": "./dist/browser/personalization.experiment.js",
|
|
53
|
+
"bun": "./dist/personalization.experiment.js",
|
|
54
|
+
"node": "./dist/node/personalization.experiment.js",
|
|
55
|
+
"default": "./dist/personalization.experiment.js"
|
|
56
|
+
},
|
|
50
57
|
"./personalization.feature": {
|
|
51
58
|
"types": "./dist/personalization.feature.d.ts",
|
|
52
59
|
"browser": "./dist/browser/personalization.feature.js",
|
|
@@ -54,6 +61,13 @@
|
|
|
54
61
|
"node": "./dist/node/personalization.feature.js",
|
|
55
62
|
"default": "./dist/personalization.feature.js"
|
|
56
63
|
},
|
|
64
|
+
"./personalization.theme": {
|
|
65
|
+
"types": "./dist/personalization.theme.d.ts",
|
|
66
|
+
"browser": "./dist/browser/personalization.theme.js",
|
|
67
|
+
"bun": "./dist/personalization.theme.js",
|
|
68
|
+
"node": "./dist/node/personalization.theme.js",
|
|
69
|
+
"default": "./dist/personalization.theme.js"
|
|
70
|
+
},
|
|
57
71
|
"./workflow-extension": {
|
|
58
72
|
"types": "./dist/workflow-extension.d.ts",
|
|
59
73
|
"browser": "./dist/browser/workflow-extension.js",
|
|
@@ -71,23 +85,23 @@
|
|
|
71
85
|
"dev": "contractspec-bun-build dev",
|
|
72
86
|
"clean": "rimraf dist .turbo",
|
|
73
87
|
"lint": "bun lint:fix",
|
|
74
|
-
"lint:fix": "
|
|
75
|
-
"lint:check": "
|
|
88
|
+
"lint:fix": "biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .",
|
|
89
|
+
"lint:check": "biome check .",
|
|
76
90
|
"test": "bun test --pass-with-no-tests",
|
|
77
91
|
"prebuild": "contractspec-bun-build prebuild",
|
|
78
92
|
"typecheck": "tsc --noEmit"
|
|
79
93
|
},
|
|
80
94
|
"dependencies": {
|
|
81
|
-
"@contractspec/lib.personalization": "6.0.
|
|
82
|
-
"@contractspec/lib.overlay-engine": "3.7.
|
|
83
|
-
"@contractspec/lib.workflow-composer": "3.7.
|
|
84
|
-
"@contractspec/lib.contracts-spec": "
|
|
85
|
-
"@contractspec/lib.logger": "3.7.
|
|
95
|
+
"@contractspec/lib.personalization": "6.0.10",
|
|
96
|
+
"@contractspec/lib.overlay-engine": "3.7.10",
|
|
97
|
+
"@contractspec/lib.workflow-composer": "3.7.10",
|
|
98
|
+
"@contractspec/lib.contracts-spec": "4.1.2",
|
|
99
|
+
"@contractspec/lib.logger": "3.7.8"
|
|
86
100
|
},
|
|
87
101
|
"devDependencies": {
|
|
88
|
-
"@contractspec/tool.typescript": "3.7.
|
|
102
|
+
"@contractspec/tool.typescript": "3.7.8",
|
|
89
103
|
"typescript": "^5.9.3",
|
|
90
|
-
"@contractspec/tool.bun": "3.7.
|
|
104
|
+
"@contractspec/tool.bun": "3.7.8"
|
|
91
105
|
},
|
|
92
106
|
"publishConfig": {
|
|
93
107
|
"access": "public",
|
|
@@ -134,6 +148,13 @@
|
|
|
134
148
|
"node": "./dist/node/overlay-customization.js",
|
|
135
149
|
"default": "./dist/overlay-customization.js"
|
|
136
150
|
},
|
|
151
|
+
"./personalization.experiment": {
|
|
152
|
+
"types": "./dist/personalization.experiment.d.ts",
|
|
153
|
+
"browser": "./dist/browser/personalization.experiment.js",
|
|
154
|
+
"bun": "./dist/personalization.experiment.js",
|
|
155
|
+
"node": "./dist/node/personalization.experiment.js",
|
|
156
|
+
"default": "./dist/personalization.experiment.js"
|
|
157
|
+
},
|
|
137
158
|
"./personalization.feature": {
|
|
138
159
|
"types": "./dist/personalization.feature.d.ts",
|
|
139
160
|
"browser": "./dist/browser/personalization.feature.js",
|
|
@@ -141,6 +162,13 @@
|
|
|
141
162
|
"node": "./dist/node/personalization.feature.js",
|
|
142
163
|
"default": "./dist/personalization.feature.js"
|
|
143
164
|
},
|
|
165
|
+
"./personalization.theme": {
|
|
166
|
+
"types": "./dist/personalization.theme.d.ts",
|
|
167
|
+
"browser": "./dist/browser/personalization.theme.js",
|
|
168
|
+
"bun": "./dist/personalization.theme.js",
|
|
169
|
+
"node": "./dist/node/personalization.theme.js",
|
|
170
|
+
"default": "./dist/personalization.theme.js"
|
|
171
|
+
},
|
|
144
172
|
"./workflow-extension": {
|
|
145
173
|
"types": "./dist/workflow-extension.d.ts",
|
|
146
174
|
"browser": "./dist/browser/workflow-extension.js",
|
package/src/behavior-tracking.ts
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { createBehaviorTracker } from '@contractspec/lib.personalization/tracker';
|
|
2
|
-
import { InMemoryBehaviorStore } from '@contractspec/lib.personalization/store';
|
|
3
|
-
import { BehaviorAnalyzer } from '@contractspec/lib.personalization/analyzer';
|
|
4
1
|
import { Logger, LogLevel } from '@contractspec/lib.logger';
|
|
2
|
+
import { BehaviorAnalyzer } from '@contractspec/lib.personalization/analyzer';
|
|
3
|
+
import { InMemoryBehaviorStore } from '@contractspec/lib.personalization/store';
|
|
4
|
+
import { createBehaviorTracker } from '@contractspec/lib.personalization/tracker';
|
|
5
5
|
|
|
6
6
|
const logger = new Logger({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
level: process.env.NODE_ENV === 'production' ? LogLevel.INFO : LogLevel.DEBUG,
|
|
8
|
+
environment:
|
|
9
|
+
(process.env.NODE_ENV as 'production' | 'development' | 'test') ||
|
|
10
|
+
'development',
|
|
11
|
+
enableColors: process.env.NODE_ENV !== 'production',
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
export async function runBehaviorTrackingExample(): Promise<void> {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const store = new InMemoryBehaviorStore();
|
|
16
|
+
const tracker = createBehaviorTracker({
|
|
17
|
+
store,
|
|
18
|
+
context: {
|
|
19
|
+
tenantId: 'acme',
|
|
20
|
+
userId: 'user-123',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
tracker.trackFieldAccess({
|
|
25
|
+
operation: 'billing.createOrder',
|
|
26
|
+
field: 'internalNotes',
|
|
27
|
+
});
|
|
28
|
+
tracker.trackFieldAccess({
|
|
29
|
+
operation: 'billing.createOrder',
|
|
30
|
+
field: 'customerReference',
|
|
31
|
+
});
|
|
32
|
+
tracker.trackFeatureUsage({ feature: 'workflow-editor', action: 'opened' });
|
|
33
|
+
tracker.trackWorkflowStep({
|
|
34
|
+
workflow: 'invoice-approval',
|
|
35
|
+
step: 'review',
|
|
36
|
+
status: 'entered',
|
|
37
|
+
});
|
|
38
|
+
await tracker.flush();
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
const analyzer = new BehaviorAnalyzer(store);
|
|
41
|
+
const insights = await analyzer.analyze({
|
|
42
|
+
tenantId: 'acme',
|
|
43
|
+
userId: 'user-123',
|
|
44
|
+
});
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
logger.info('Behavior insights computed', { insights });
|
|
47
47
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
PersonalizationExperiment,
|
|
4
|
+
PersonalizationFeature,
|
|
5
|
+
PersonalizationTheme,
|
|
6
|
+
} from './index';
|
|
7
|
+
|
|
8
|
+
describe('@contractspec/example.personalization', () => {
|
|
9
|
+
test('exports the canonical experiment and theme', () => {
|
|
10
|
+
expect(PersonalizationExperiment.meta.key).toBe(
|
|
11
|
+
'personalization.experiment.overlay-copy'
|
|
12
|
+
);
|
|
13
|
+
expect(PersonalizationExperiment.variants).toHaveLength(2);
|
|
14
|
+
expect(PersonalizationTheme.meta.key).toBe(
|
|
15
|
+
'personalization.theme.guided-onboarding'
|
|
16
|
+
);
|
|
17
|
+
expect(PersonalizationTheme.overrides).toHaveLength(1);
|
|
18
|
+
expect(PersonalizationFeature.meta.key).toBe('personalization');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -2,27 +2,27 @@ import type { DocBlock } from '@contractspec/lib.contracts-spec/docs';
|
|
|
2
2
|
import { registerDocBlocks } from '@contractspec/lib.contracts-spec/docs';
|
|
3
3
|
|
|
4
4
|
const blocks: DocBlock[] = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
5
|
+
{
|
|
6
|
+
id: 'docs.examples.personalization',
|
|
7
|
+
title: 'Personalization Patterns (example)',
|
|
8
|
+
summary:
|
|
9
|
+
'Behavior tracking, overlay-driven UI tweaks, and tenant workflow extension patterns.',
|
|
10
|
+
kind: 'reference',
|
|
11
|
+
visibility: 'public',
|
|
12
|
+
route: '/docs/examples/personalization',
|
|
13
|
+
tags: ['personalization', 'overlays', 'workflows', 'example'],
|
|
14
|
+
body: `## Includes\n- Behavior tracking + insight analysis.\n- Overlay customization (hide fields, rename labels).\n- Workflow extension (inject tenant-specific steps).\n\n## Guardrails\n- Keep tracking events structured and non-PII.\n- Keep overlays signed and scoped.\n- Keep workflow composition deterministic.`,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'docs.examples.personalization.usage',
|
|
18
|
+
title: 'Personalization — Usage',
|
|
19
|
+
summary: 'How to run the small examples and swap adapters.',
|
|
20
|
+
kind: 'usage',
|
|
21
|
+
visibility: 'public',
|
|
22
|
+
route: '/docs/examples/personalization/usage',
|
|
23
|
+
tags: ['personalization', 'usage'],
|
|
24
|
+
body: `## Usage\n- Call \`runBehaviorTrackingExample()\` for insights.\n- Call \`runOverlayCustomizationExample()\` to apply overlays.\n- Call \`composeTenantWorkflowExample()\` and \`logTenantWorkflowSteps()\` to inspect steps.\n\n## Notes\n- Replace in-memory stores with app-layer storage.\n- Keep PII out of logs and markdown outputs.`,
|
|
25
|
+
},
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
registerDocBlocks(blocks);
|
package/src/example.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import { defineExample } from '@contractspec/lib.contracts-spec';
|
|
2
2
|
|
|
3
3
|
const example = defineExample({
|
|
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
|
-
|
|
4
|
+
meta: {
|
|
5
|
+
key: 'personalization',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
title: 'Personalization Patterns',
|
|
8
|
+
description:
|
|
9
|
+
'Small examples for behavior tracking, overlay-based UI customization, and tenant workflow extension.',
|
|
10
|
+
kind: 'library',
|
|
11
|
+
visibility: 'public',
|
|
12
|
+
stability: 'experimental',
|
|
13
|
+
owners: ['@platform.core'],
|
|
14
|
+
tags: ['personalization', 'overlays', 'behavior', 'workflows'],
|
|
15
|
+
},
|
|
16
|
+
docs: {
|
|
17
|
+
rootDocId: 'docs.examples.personalization',
|
|
18
|
+
usageDocId: 'docs.examples.personalization.usage',
|
|
19
|
+
},
|
|
20
|
+
entrypoints: {
|
|
21
|
+
packageName: '@contractspec/example.personalization',
|
|
22
|
+
docs: './docs',
|
|
23
|
+
},
|
|
24
|
+
surfaces: {
|
|
25
|
+
templates: true,
|
|
26
|
+
sandbox: { enabled: true, modes: ['markdown', 'specs'] },
|
|
27
|
+
studio: { enabled: true, installable: true },
|
|
28
|
+
mcp: { enabled: true },
|
|
29
|
+
},
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
export default example;
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './behavior-tracking';
|
|
2
|
+
export { default as example } from './example';
|
|
2
3
|
export * from './overlay-customization';
|
|
3
|
-
export * from './
|
|
4
|
+
export * from './personalization.experiment';
|
|
4
5
|
export * from './personalization.feature';
|
|
5
|
-
export
|
|
6
|
+
export * from './personalization.theme';
|
|
7
|
+
export * from './workflow-extension';
|
|
6
8
|
import './docs';
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { signOverlay } from '@contractspec/lib.overlay-engine/signer';
|
|
1
|
+
import { Logger, LogLevel } from '@contractspec/lib.logger';
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
OverlayEngine,
|
|
4
|
+
OverlayRegistry,
|
|
6
5
|
} from '@contractspec/lib.overlay-engine';
|
|
7
|
-
import {
|
|
6
|
+
import { signOverlay } from '@contractspec/lib.overlay-engine/signer';
|
|
7
|
+
import { defineOverlay } from '@contractspec/lib.overlay-engine/spec';
|
|
8
8
|
|
|
9
9
|
const logger = new Logger({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
level: process.env.NODE_ENV === 'production' ? LogLevel.INFO : LogLevel.DEBUG,
|
|
11
|
+
environment:
|
|
12
|
+
(process.env.NODE_ENV as 'production' | 'development' | 'test') ||
|
|
13
|
+
'development',
|
|
14
|
+
enableColors: process.env.NODE_ENV !== 'production',
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
export async function runOverlayCustomizationExample(): Promise<void> {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const registry = new OverlayRegistry({ allowUnsigned: true });
|
|
19
|
+
const engine = new OverlayEngine({ registry });
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
21
|
+
const overlay = defineOverlay({
|
|
22
|
+
overlayId: 'demo-overlay',
|
|
23
|
+
version: '1.0.0',
|
|
24
|
+
appliesTo: {
|
|
25
|
+
capability: 'billing.createOrder',
|
|
26
|
+
tenantId: 'demo',
|
|
27
|
+
},
|
|
28
|
+
modifications: [
|
|
29
|
+
{ type: 'hideField', field: 'internalNotes' },
|
|
30
|
+
{
|
|
31
|
+
type: 'renameLabel',
|
|
32
|
+
field: 'customerReference',
|
|
33
|
+
newLabel: 'PO Number',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const signed = await signOverlay(overlay, process.env.PRIVATE_KEY_PEM ?? '');
|
|
39
|
+
registry.register(signed);
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
41
|
+
const result = engine.apply({
|
|
42
|
+
target: {
|
|
43
|
+
fields: [
|
|
44
|
+
{
|
|
45
|
+
key: 'customerReference',
|
|
46
|
+
label: 'Customer Reference',
|
|
47
|
+
visible: true,
|
|
48
|
+
},
|
|
49
|
+
{ key: 'internalNotes', label: 'Internal Notes', visible: true },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
capability: 'billing.createOrder',
|
|
53
|
+
tenantId: 'demo',
|
|
54
|
+
});
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
logger.info('Overlay applied', { fields: result.target.fields });
|
|
57
57
|
}
|