@contractspec/lib.progressive-delivery 34.0.16 → 34.0.17

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,208 +1 @@
1
- // src/canary-analyzer.ts
2
- class CanaryAnalyzer {
3
- defaults;
4
- constructor(defaults) {
5
- this.defaults = defaults;
6
- }
7
- evaluate(stage, metrics) {
8
- const thresholds = { ...this.defaults, ...stage.thresholds };
9
- const reasons = [];
10
- if (metrics.errorRate > thresholds.errorRate) {
11
- reasons.push(`errorRate ${metrics.errorRate.toFixed(4)} > ${thresholds.errorRate}`);
12
- }
13
- if (typeof thresholds.latencyP50 === "number" && metrics.latencyP50 > thresholds.latencyP50) {
14
- reasons.push(`latencyP50 ${metrics.latencyP50}ms > ${thresholds.latencyP50}ms`);
15
- }
16
- if (typeof thresholds.latencyP95 === "number" && metrics.latencyP95 > thresholds.latencyP95) {
17
- reasons.push(`latencyP95 ${metrics.latencyP95}ms > ${thresholds.latencyP95}ms`);
18
- }
19
- if (typeof thresholds.latencyP99 === "number" && metrics.latencyP99 > thresholds.latencyP99) {
20
- reasons.push(`latencyP99 ${metrics.latencyP99}ms > ${thresholds.latencyP99}ms`);
21
- }
22
- if (typeof thresholds.throughputDrop === "number" && metrics.throughput < thresholds.throughputDrop) {
23
- reasons.push(`throughput ${metrics.throughput} < ${thresholds.throughputDrop}`);
24
- }
25
- if (thresholds.customEvaluator && !thresholds.customEvaluator(metrics)) {
26
- reasons.push("custom evaluator reported failure");
27
- }
28
- return {
29
- status: reasons.length === 0 ? "pass" : "fail",
30
- reasons,
31
- metrics
32
- };
33
- }
34
- }
35
- // src/canary-controller.ts
36
- var DEFAULT_STAGES = [
37
- { percentage: 1, minDurationMs: 5 * 60 * 1000, label: "1%" },
38
- { percentage: 10, minDurationMs: 5 * 60 * 1000, label: "10%" },
39
- { percentage: 50, minDurationMs: 10 * 60 * 1000, label: "50%" },
40
- { percentage: 100, minDurationMs: 15 * 60 * 1000, label: "100%" }
41
- ];
42
-
43
- class CanaryController {
44
- options;
45
- stages;
46
- constructor(options) {
47
- this.options = options;
48
- this.stages = options.strategy.stages && options.strategy.stages.length > 0 ? options.strategy.stages : DEFAULT_STAGES;
49
- }
50
- getStageList() {
51
- return [...this.stages];
52
- }
53
- async runStage(stage) {
54
- this.options.eventBus?.emit({
55
- type: "stage_started",
56
- timestamp: new Date,
57
- payload: { stage }
58
- });
59
- const metrics = await this.options.metricsProvider(stage, stage.minDurationMs);
60
- const analysis = this.options.analyzer.evaluate(stage, metrics);
61
- this.options.eventBus?.emit({
62
- type: analysis.status === "pass" ? "stage_passed" : "stage_failed",
63
- timestamp: new Date,
64
- payload: { stage, metrics, analysis }
65
- });
66
- return analysis;
67
- }
68
- }
69
- function createDefaultCanaryController(strategy, metricsProvider, eventBus) {
70
- const analyzer = new CanaryAnalyzer(strategy.thresholds);
71
- return new CanaryController({
72
- strategy,
73
- analyzer,
74
- metricsProvider,
75
- eventBus
76
- });
77
- }
78
- // src/deployment-coordinator.ts
79
- class DeploymentCoordinator {
80
- options;
81
- constructor(options) {
82
- this.options = options;
83
- }
84
- async run() {
85
- const stages = this.options.controller.getStageList();
86
- for (const stage of stages) {
87
- const split = this.options.trafficShifter.computeSplit(stage);
88
- await this.options.applyTrafficSplit(stage, split);
89
- const analysis = await this.options.controller.runStage(stage);
90
- if (analysis.status === "fail") {
91
- const action = await this.options.rollbackManager.execute(stage, analysis.reasons.join(", "));
92
- this.options.eventBus?.emit({
93
- type: "rolled_back",
94
- timestamp: action.triggeredAt,
95
- payload: { stage, reasons: analysis.reasons }
96
- });
97
- return {
98
- status: "rolled_back",
99
- failedStage: stage,
100
- reasons: analysis.reasons
101
- };
102
- }
103
- }
104
- if (this.options.strategy.mode === "blue-green") {
105
- this.options.eventBus?.emit({
106
- type: "blue_green_swapped",
107
- timestamp: new Date,
108
- payload: { strategy: this.options.strategy }
109
- });
110
- }
111
- this.options.eventBus?.emit({
112
- type: "completed",
113
- timestamp: new Date,
114
- payload: { strategy: this.options.strategy }
115
- });
116
- return { status: "completed" };
117
- }
118
- }
119
- // src/events.ts
120
- class DeploymentEventBus {
121
- listeners = new Set;
122
- on(listener) {
123
- this.listeners.add(listener);
124
- return () => this.listeners.delete(listener);
125
- }
126
- emit(event) {
127
- for (const listener of this.listeners) {
128
- try {
129
- listener(event);
130
- } catch (error) {
131
- console.error("[progressive-delivery] listener error", error);
132
- }
133
- }
134
- }
135
- }
136
- // src/feature-flags.ts
137
- var ContractSpecFeatureFlags = {
138
- LIFECYCLE_DETECTION_ALPHA: "lifecycle_detection_alpha",
139
- LIFECYCLE_ADVISOR_ALPHA: "lifecycle_advisor_alpha",
140
- LIFECYCLE_MANAGED_SERVICE: "lifecycle_managed_service",
141
- STUDIO_VISUAL_BUILDER: "studio_visual_builder",
142
- STUDIO_AUTO_EVOLUTION: "studio_auto_evolution",
143
- STUDIO_BYOK: "studio_byok",
144
- STUDIO_DEDICATED_DEPLOYMENT: "studio_dedicated_deployment",
145
- STUDIO_INTEGRATION_HUB: "studio_integration_hub",
146
- STUDIO_KNOWLEDGE_HUB: "studio_knowledge_hub",
147
- STUDIO_TEMPLATES: "studio_templates"
148
- };
149
- var lifecycleFlags = [
150
- ContractSpecFeatureFlags.LIFECYCLE_DETECTION_ALPHA,
151
- ContractSpecFeatureFlags.LIFECYCLE_ADVISOR_ALPHA,
152
- ContractSpecFeatureFlags.LIFECYCLE_MANAGED_SERVICE
153
- ];
154
- var studioFlags = [
155
- ContractSpecFeatureFlags.STUDIO_VISUAL_BUILDER,
156
- ContractSpecFeatureFlags.STUDIO_AUTO_EVOLUTION,
157
- ContractSpecFeatureFlags.STUDIO_BYOK,
158
- ContractSpecFeatureFlags.STUDIO_DEDICATED_DEPLOYMENT,
159
- ContractSpecFeatureFlags.STUDIO_INTEGRATION_HUB,
160
- ContractSpecFeatureFlags.STUDIO_KNOWLEDGE_HUB,
161
- ContractSpecFeatureFlags.STUDIO_TEMPLATES
162
- ];
163
- // src/rollback-manager.ts
164
- class RollbackManager {
165
- options;
166
- constructor(options) {
167
- this.options = options;
168
- }
169
- async execute(stage, reason) {
170
- await this.options.rollback(stage, reason);
171
- const action = {
172
- reason,
173
- stage,
174
- triggeredAt: new Date
175
- };
176
- this.options.onRollback?.(action);
177
- return action;
178
- }
179
- }
180
- // src/traffic-shifter.ts
181
- class TrafficShifter {
182
- mode;
183
- constructor(mode) {
184
- this.mode = mode;
185
- }
186
- computeSplit(stage) {
187
- if (this.mode === "blue-green") {
188
- return stage.percentage >= 100 ? { stable: 0, candidate: 1 } : { stable: 1, candidate: 0 };
189
- }
190
- const candidate = Math.min(Math.max(stage.percentage / 100, 0), 1);
191
- return {
192
- candidate,
193
- stable: 1 - candidate
194
- };
195
- }
196
- }
197
- export {
198
- studioFlags,
199
- lifecycleFlags,
200
- createDefaultCanaryController,
201
- TrafficShifter,
202
- RollbackManager,
203
- DeploymentEventBus,
204
- DeploymentCoordinator,
205
- ContractSpecFeatureFlags,
206
- CanaryController,
207
- CanaryAnalyzer
208
- };
1
+ class n{defaults;constructor(e){this.defaults=e}evaluate(e,t){let a={...this.defaults,...e.thresholds},r=[];if(t.errorRate>a.errorRate)r.push(`errorRate ${t.errorRate.toFixed(4)} > ${a.errorRate}`);if(typeof a.latencyP50==="number"&&t.latencyP50>a.latencyP50)r.push(`latencyP50 ${t.latencyP50}ms > ${a.latencyP50}ms`);if(typeof a.latencyP95==="number"&&t.latencyP95>a.latencyP95)r.push(`latencyP95 ${t.latencyP95}ms > ${a.latencyP95}ms`);if(typeof a.latencyP99==="number"&&t.latencyP99>a.latencyP99)r.push(`latencyP99 ${t.latencyP99}ms > ${a.latencyP99}ms`);if(typeof a.throughputDrop==="number"&&t.throughput<a.throughputDrop)r.push(`throughput ${t.throughput} < ${a.throughputDrop}`);if(a.customEvaluator&&!a.customEvaluator(t))r.push("custom evaluator reported failure");return{status:r.length===0?"pass":"fail",reasons:r,metrics:t}}}var l=[{percentage:1,minDurationMs:300000,label:"1%"},{percentage:10,minDurationMs:300000,label:"10%"},{percentage:50,minDurationMs:600000,label:"50%"},{percentage:100,minDurationMs:900000,label:"100%"}];class s{options;stages;constructor(e){this.options=e;this.stages=e.strategy.stages&&e.strategy.stages.length>0?e.strategy.stages:l}getStageList(){return[...this.stages]}async runStage(e){this.options.eventBus?.emit({type:"stage_started",timestamp:new Date,payload:{stage:e}});let t=await this.options.metricsProvider(e,e.minDurationMs),a=this.options.analyzer.evaluate(e,t);return this.options.eventBus?.emit({type:a.status==="pass"?"stage_passed":"stage_failed",timestamp:new Date,payload:{stage:e,metrics:t,analysis:a}}),a}}function g(e,t,a){let r=new n(e.thresholds);return new s({strategy:e,analyzer:r,metricsProvider:t,eventBus:a})}class p{options;constructor(e){this.options=e}async run(){let e=this.options.controller.getStageList();for(let t of e){let a=this.options.trafficShifter.computeSplit(t);await this.options.applyTrafficSplit(t,a);let r=await this.options.controller.runStage(t);if(r.status==="fail"){let i=await this.options.rollbackManager.execute(t,r.reasons.join(", "));return this.options.eventBus?.emit({type:"rolled_back",timestamp:i.triggeredAt,payload:{stage:t,reasons:r.reasons}}),{status:"rolled_back",failedStage:t,reasons:r.reasons}}}if(this.options.strategy.mode==="blue-green")this.options.eventBus?.emit({type:"blue_green_swapped",timestamp:new Date,payload:{strategy:this.options.strategy}});return this.options.eventBus?.emit({type:"completed",timestamp:new Date,payload:{strategy:this.options.strategy}}),{status:"completed"}}}class y{listeners=new Set;on(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e){for(let t of this.listeners)try{t(e)}catch(a){console.error("[progressive-delivery] listener error",a)}}}var o={LIFECYCLE_DETECTION_ALPHA:"lifecycle_detection_alpha",LIFECYCLE_ADVISOR_ALPHA:"lifecycle_advisor_alpha",LIFECYCLE_MANAGED_SERVICE:"lifecycle_managed_service",STUDIO_VISUAL_BUILDER:"studio_visual_builder",STUDIO_AUTO_EVOLUTION:"studio_auto_evolution",STUDIO_BYOK:"studio_byok",STUDIO_DEDICATED_DEPLOYMENT:"studio_dedicated_deployment",STUDIO_INTEGRATION_HUB:"studio_integration_hub",STUDIO_KNOWLEDGE_HUB:"studio_knowledge_hub",STUDIO_TEMPLATES:"studio_templates"},h=[o.LIFECYCLE_DETECTION_ALPHA,o.LIFECYCLE_ADVISOR_ALPHA,o.LIFECYCLE_MANAGED_SERVICE],_=[o.STUDIO_VISUAL_BUILDER,o.STUDIO_AUTO_EVOLUTION,o.STUDIO_BYOK,o.STUDIO_DEDICATED_DEPLOYMENT,o.STUDIO_INTEGRATION_HUB,o.STUDIO_KNOWLEDGE_HUB,o.STUDIO_TEMPLATES];class c{options;constructor(e){this.options=e}async execute(e,t){await this.options.rollback(e,t);let a={reason:t,stage:e,triggeredAt:new Date};return this.options.onRollback?.(a),a}}class u{mode;constructor(e){this.mode=e}computeSplit(e){if(this.mode==="blue-green")return e.percentage>=100?{stable:0,candidate:1}:{stable:1,candidate:0};let t=Math.min(Math.max(e.percentage/100,0),1);return{candidate:t,stable:1-t}}}export{_ as studioFlags,h as lifecycleFlags,g as createDefaultCanaryController,u as TrafficShifter,c as RollbackManager,y as DeploymentEventBus,p as DeploymentCoordinator,o as ContractSpecFeatureFlags,s as CanaryController,n as CanaryAnalyzer};
package/dist/index.js CHANGED
@@ -1,209 +1,2 @@
1
1
  // @bun
2
- // src/canary-analyzer.ts
3
- class CanaryAnalyzer {
4
- defaults;
5
- constructor(defaults) {
6
- this.defaults = defaults;
7
- }
8
- evaluate(stage, metrics) {
9
- const thresholds = { ...this.defaults, ...stage.thresholds };
10
- const reasons = [];
11
- if (metrics.errorRate > thresholds.errorRate) {
12
- reasons.push(`errorRate ${metrics.errorRate.toFixed(4)} > ${thresholds.errorRate}`);
13
- }
14
- if (typeof thresholds.latencyP50 === "number" && metrics.latencyP50 > thresholds.latencyP50) {
15
- reasons.push(`latencyP50 ${metrics.latencyP50}ms > ${thresholds.latencyP50}ms`);
16
- }
17
- if (typeof thresholds.latencyP95 === "number" && metrics.latencyP95 > thresholds.latencyP95) {
18
- reasons.push(`latencyP95 ${metrics.latencyP95}ms > ${thresholds.latencyP95}ms`);
19
- }
20
- if (typeof thresholds.latencyP99 === "number" && metrics.latencyP99 > thresholds.latencyP99) {
21
- reasons.push(`latencyP99 ${metrics.latencyP99}ms > ${thresholds.latencyP99}ms`);
22
- }
23
- if (typeof thresholds.throughputDrop === "number" && metrics.throughput < thresholds.throughputDrop) {
24
- reasons.push(`throughput ${metrics.throughput} < ${thresholds.throughputDrop}`);
25
- }
26
- if (thresholds.customEvaluator && !thresholds.customEvaluator(metrics)) {
27
- reasons.push("custom evaluator reported failure");
28
- }
29
- return {
30
- status: reasons.length === 0 ? "pass" : "fail",
31
- reasons,
32
- metrics
33
- };
34
- }
35
- }
36
- // src/canary-controller.ts
37
- var DEFAULT_STAGES = [
38
- { percentage: 1, minDurationMs: 5 * 60 * 1000, label: "1%" },
39
- { percentage: 10, minDurationMs: 5 * 60 * 1000, label: "10%" },
40
- { percentage: 50, minDurationMs: 10 * 60 * 1000, label: "50%" },
41
- { percentage: 100, minDurationMs: 15 * 60 * 1000, label: "100%" }
42
- ];
43
-
44
- class CanaryController {
45
- options;
46
- stages;
47
- constructor(options) {
48
- this.options = options;
49
- this.stages = options.strategy.stages && options.strategy.stages.length > 0 ? options.strategy.stages : DEFAULT_STAGES;
50
- }
51
- getStageList() {
52
- return [...this.stages];
53
- }
54
- async runStage(stage) {
55
- this.options.eventBus?.emit({
56
- type: "stage_started",
57
- timestamp: new Date,
58
- payload: { stage }
59
- });
60
- const metrics = await this.options.metricsProvider(stage, stage.minDurationMs);
61
- const analysis = this.options.analyzer.evaluate(stage, metrics);
62
- this.options.eventBus?.emit({
63
- type: analysis.status === "pass" ? "stage_passed" : "stage_failed",
64
- timestamp: new Date,
65
- payload: { stage, metrics, analysis }
66
- });
67
- return analysis;
68
- }
69
- }
70
- function createDefaultCanaryController(strategy, metricsProvider, eventBus) {
71
- const analyzer = new CanaryAnalyzer(strategy.thresholds);
72
- return new CanaryController({
73
- strategy,
74
- analyzer,
75
- metricsProvider,
76
- eventBus
77
- });
78
- }
79
- // src/deployment-coordinator.ts
80
- class DeploymentCoordinator {
81
- options;
82
- constructor(options) {
83
- this.options = options;
84
- }
85
- async run() {
86
- const stages = this.options.controller.getStageList();
87
- for (const stage of stages) {
88
- const split = this.options.trafficShifter.computeSplit(stage);
89
- await this.options.applyTrafficSplit(stage, split);
90
- const analysis = await this.options.controller.runStage(stage);
91
- if (analysis.status === "fail") {
92
- const action = await this.options.rollbackManager.execute(stage, analysis.reasons.join(", "));
93
- this.options.eventBus?.emit({
94
- type: "rolled_back",
95
- timestamp: action.triggeredAt,
96
- payload: { stage, reasons: analysis.reasons }
97
- });
98
- return {
99
- status: "rolled_back",
100
- failedStage: stage,
101
- reasons: analysis.reasons
102
- };
103
- }
104
- }
105
- if (this.options.strategy.mode === "blue-green") {
106
- this.options.eventBus?.emit({
107
- type: "blue_green_swapped",
108
- timestamp: new Date,
109
- payload: { strategy: this.options.strategy }
110
- });
111
- }
112
- this.options.eventBus?.emit({
113
- type: "completed",
114
- timestamp: new Date,
115
- payload: { strategy: this.options.strategy }
116
- });
117
- return { status: "completed" };
118
- }
119
- }
120
- // src/events.ts
121
- class DeploymentEventBus {
122
- listeners = new Set;
123
- on(listener) {
124
- this.listeners.add(listener);
125
- return () => this.listeners.delete(listener);
126
- }
127
- emit(event) {
128
- for (const listener of this.listeners) {
129
- try {
130
- listener(event);
131
- } catch (error) {
132
- console.error("[progressive-delivery] listener error", error);
133
- }
134
- }
135
- }
136
- }
137
- // src/feature-flags.ts
138
- var ContractSpecFeatureFlags = {
139
- LIFECYCLE_DETECTION_ALPHA: "lifecycle_detection_alpha",
140
- LIFECYCLE_ADVISOR_ALPHA: "lifecycle_advisor_alpha",
141
- LIFECYCLE_MANAGED_SERVICE: "lifecycle_managed_service",
142
- STUDIO_VISUAL_BUILDER: "studio_visual_builder",
143
- STUDIO_AUTO_EVOLUTION: "studio_auto_evolution",
144
- STUDIO_BYOK: "studio_byok",
145
- STUDIO_DEDICATED_DEPLOYMENT: "studio_dedicated_deployment",
146
- STUDIO_INTEGRATION_HUB: "studio_integration_hub",
147
- STUDIO_KNOWLEDGE_HUB: "studio_knowledge_hub",
148
- STUDIO_TEMPLATES: "studio_templates"
149
- };
150
- var lifecycleFlags = [
151
- ContractSpecFeatureFlags.LIFECYCLE_DETECTION_ALPHA,
152
- ContractSpecFeatureFlags.LIFECYCLE_ADVISOR_ALPHA,
153
- ContractSpecFeatureFlags.LIFECYCLE_MANAGED_SERVICE
154
- ];
155
- var studioFlags = [
156
- ContractSpecFeatureFlags.STUDIO_VISUAL_BUILDER,
157
- ContractSpecFeatureFlags.STUDIO_AUTO_EVOLUTION,
158
- ContractSpecFeatureFlags.STUDIO_BYOK,
159
- ContractSpecFeatureFlags.STUDIO_DEDICATED_DEPLOYMENT,
160
- ContractSpecFeatureFlags.STUDIO_INTEGRATION_HUB,
161
- ContractSpecFeatureFlags.STUDIO_KNOWLEDGE_HUB,
162
- ContractSpecFeatureFlags.STUDIO_TEMPLATES
163
- ];
164
- // src/rollback-manager.ts
165
- class RollbackManager {
166
- options;
167
- constructor(options) {
168
- this.options = options;
169
- }
170
- async execute(stage, reason) {
171
- await this.options.rollback(stage, reason);
172
- const action = {
173
- reason,
174
- stage,
175
- triggeredAt: new Date
176
- };
177
- this.options.onRollback?.(action);
178
- return action;
179
- }
180
- }
181
- // src/traffic-shifter.ts
182
- class TrafficShifter {
183
- mode;
184
- constructor(mode) {
185
- this.mode = mode;
186
- }
187
- computeSplit(stage) {
188
- if (this.mode === "blue-green") {
189
- return stage.percentage >= 100 ? { stable: 0, candidate: 1 } : { stable: 1, candidate: 0 };
190
- }
191
- const candidate = Math.min(Math.max(stage.percentage / 100, 0), 1);
192
- return {
193
- candidate,
194
- stable: 1 - candidate
195
- };
196
- }
197
- }
198
- export {
199
- studioFlags,
200
- lifecycleFlags,
201
- createDefaultCanaryController,
202
- TrafficShifter,
203
- RollbackManager,
204
- DeploymentEventBus,
205
- DeploymentCoordinator,
206
- ContractSpecFeatureFlags,
207
- CanaryController,
208
- CanaryAnalyzer
209
- };
2
+ class n{defaults;constructor(e){this.defaults=e}evaluate(e,t){let a={...this.defaults,...e.thresholds},r=[];if(t.errorRate>a.errorRate)r.push(`errorRate ${t.errorRate.toFixed(4)} > ${a.errorRate}`);if(typeof a.latencyP50==="number"&&t.latencyP50>a.latencyP50)r.push(`latencyP50 ${t.latencyP50}ms > ${a.latencyP50}ms`);if(typeof a.latencyP95==="number"&&t.latencyP95>a.latencyP95)r.push(`latencyP95 ${t.latencyP95}ms > ${a.latencyP95}ms`);if(typeof a.latencyP99==="number"&&t.latencyP99>a.latencyP99)r.push(`latencyP99 ${t.latencyP99}ms > ${a.latencyP99}ms`);if(typeof a.throughputDrop==="number"&&t.throughput<a.throughputDrop)r.push(`throughput ${t.throughput} < ${a.throughputDrop}`);if(a.customEvaluator&&!a.customEvaluator(t))r.push("custom evaluator reported failure");return{status:r.length===0?"pass":"fail",reasons:r,metrics:t}}}var l=[{percentage:1,minDurationMs:300000,label:"1%"},{percentage:10,minDurationMs:300000,label:"10%"},{percentage:50,minDurationMs:600000,label:"50%"},{percentage:100,minDurationMs:900000,label:"100%"}];class s{options;stages;constructor(e){this.options=e;this.stages=e.strategy.stages&&e.strategy.stages.length>0?e.strategy.stages:l}getStageList(){return[...this.stages]}async runStage(e){this.options.eventBus?.emit({type:"stage_started",timestamp:new Date,payload:{stage:e}});let t=await this.options.metricsProvider(e,e.minDurationMs),a=this.options.analyzer.evaluate(e,t);return this.options.eventBus?.emit({type:a.status==="pass"?"stage_passed":"stage_failed",timestamp:new Date,payload:{stage:e,metrics:t,analysis:a}}),a}}function g(e,t,a){let r=new n(e.thresholds);return new s({strategy:e,analyzer:r,metricsProvider:t,eventBus:a})}class p{options;constructor(e){this.options=e}async run(){let e=this.options.controller.getStageList();for(let t of e){let a=this.options.trafficShifter.computeSplit(t);await this.options.applyTrafficSplit(t,a);let r=await this.options.controller.runStage(t);if(r.status==="fail"){let i=await this.options.rollbackManager.execute(t,r.reasons.join(", "));return this.options.eventBus?.emit({type:"rolled_back",timestamp:i.triggeredAt,payload:{stage:t,reasons:r.reasons}}),{status:"rolled_back",failedStage:t,reasons:r.reasons}}}if(this.options.strategy.mode==="blue-green")this.options.eventBus?.emit({type:"blue_green_swapped",timestamp:new Date,payload:{strategy:this.options.strategy}});return this.options.eventBus?.emit({type:"completed",timestamp:new Date,payload:{strategy:this.options.strategy}}),{status:"completed"}}}class y{listeners=new Set;on(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e){for(let t of this.listeners)try{t(e)}catch(a){console.error("[progressive-delivery] listener error",a)}}}var o={LIFECYCLE_DETECTION_ALPHA:"lifecycle_detection_alpha",LIFECYCLE_ADVISOR_ALPHA:"lifecycle_advisor_alpha",LIFECYCLE_MANAGED_SERVICE:"lifecycle_managed_service",STUDIO_VISUAL_BUILDER:"studio_visual_builder",STUDIO_AUTO_EVOLUTION:"studio_auto_evolution",STUDIO_BYOK:"studio_byok",STUDIO_DEDICATED_DEPLOYMENT:"studio_dedicated_deployment",STUDIO_INTEGRATION_HUB:"studio_integration_hub",STUDIO_KNOWLEDGE_HUB:"studio_knowledge_hub",STUDIO_TEMPLATES:"studio_templates"},h=[o.LIFECYCLE_DETECTION_ALPHA,o.LIFECYCLE_ADVISOR_ALPHA,o.LIFECYCLE_MANAGED_SERVICE],_=[o.STUDIO_VISUAL_BUILDER,o.STUDIO_AUTO_EVOLUTION,o.STUDIO_BYOK,o.STUDIO_DEDICATED_DEPLOYMENT,o.STUDIO_INTEGRATION_HUB,o.STUDIO_KNOWLEDGE_HUB,o.STUDIO_TEMPLATES];class c{options;constructor(e){this.options=e}async execute(e,t){await this.options.rollback(e,t);let a={reason:t,stage:e,triggeredAt:new Date};return this.options.onRollback?.(a),a}}class u{mode;constructor(e){this.mode=e}computeSplit(e){if(this.mode==="blue-green")return e.percentage>=100?{stable:0,candidate:1}:{stable:1,candidate:0};let t=Math.min(Math.max(e.percentage/100,0),1);return{candidate:t,stable:1-t}}}export{_ as studioFlags,h as lifecycleFlags,g as createDefaultCanaryController,u as TrafficShifter,c as RollbackManager,y as DeploymentEventBus,p as DeploymentCoordinator,o as ContractSpecFeatureFlags,s as CanaryController,n as CanaryAnalyzer};
@@ -1,208 +1 @@
1
- // src/canary-analyzer.ts
2
- class CanaryAnalyzer {
3
- defaults;
4
- constructor(defaults) {
5
- this.defaults = defaults;
6
- }
7
- evaluate(stage, metrics) {
8
- const thresholds = { ...this.defaults, ...stage.thresholds };
9
- const reasons = [];
10
- if (metrics.errorRate > thresholds.errorRate) {
11
- reasons.push(`errorRate ${metrics.errorRate.toFixed(4)} > ${thresholds.errorRate}`);
12
- }
13
- if (typeof thresholds.latencyP50 === "number" && metrics.latencyP50 > thresholds.latencyP50) {
14
- reasons.push(`latencyP50 ${metrics.latencyP50}ms > ${thresholds.latencyP50}ms`);
15
- }
16
- if (typeof thresholds.latencyP95 === "number" && metrics.latencyP95 > thresholds.latencyP95) {
17
- reasons.push(`latencyP95 ${metrics.latencyP95}ms > ${thresholds.latencyP95}ms`);
18
- }
19
- if (typeof thresholds.latencyP99 === "number" && metrics.latencyP99 > thresholds.latencyP99) {
20
- reasons.push(`latencyP99 ${metrics.latencyP99}ms > ${thresholds.latencyP99}ms`);
21
- }
22
- if (typeof thresholds.throughputDrop === "number" && metrics.throughput < thresholds.throughputDrop) {
23
- reasons.push(`throughput ${metrics.throughput} < ${thresholds.throughputDrop}`);
24
- }
25
- if (thresholds.customEvaluator && !thresholds.customEvaluator(metrics)) {
26
- reasons.push("custom evaluator reported failure");
27
- }
28
- return {
29
- status: reasons.length === 0 ? "pass" : "fail",
30
- reasons,
31
- metrics
32
- };
33
- }
34
- }
35
- // src/canary-controller.ts
36
- var DEFAULT_STAGES = [
37
- { percentage: 1, minDurationMs: 5 * 60 * 1000, label: "1%" },
38
- { percentage: 10, minDurationMs: 5 * 60 * 1000, label: "10%" },
39
- { percentage: 50, minDurationMs: 10 * 60 * 1000, label: "50%" },
40
- { percentage: 100, minDurationMs: 15 * 60 * 1000, label: "100%" }
41
- ];
42
-
43
- class CanaryController {
44
- options;
45
- stages;
46
- constructor(options) {
47
- this.options = options;
48
- this.stages = options.strategy.stages && options.strategy.stages.length > 0 ? options.strategy.stages : DEFAULT_STAGES;
49
- }
50
- getStageList() {
51
- return [...this.stages];
52
- }
53
- async runStage(stage) {
54
- this.options.eventBus?.emit({
55
- type: "stage_started",
56
- timestamp: new Date,
57
- payload: { stage }
58
- });
59
- const metrics = await this.options.metricsProvider(stage, stage.minDurationMs);
60
- const analysis = this.options.analyzer.evaluate(stage, metrics);
61
- this.options.eventBus?.emit({
62
- type: analysis.status === "pass" ? "stage_passed" : "stage_failed",
63
- timestamp: new Date,
64
- payload: { stage, metrics, analysis }
65
- });
66
- return analysis;
67
- }
68
- }
69
- function createDefaultCanaryController(strategy, metricsProvider, eventBus) {
70
- const analyzer = new CanaryAnalyzer(strategy.thresholds);
71
- return new CanaryController({
72
- strategy,
73
- analyzer,
74
- metricsProvider,
75
- eventBus
76
- });
77
- }
78
- // src/deployment-coordinator.ts
79
- class DeploymentCoordinator {
80
- options;
81
- constructor(options) {
82
- this.options = options;
83
- }
84
- async run() {
85
- const stages = this.options.controller.getStageList();
86
- for (const stage of stages) {
87
- const split = this.options.trafficShifter.computeSplit(stage);
88
- await this.options.applyTrafficSplit(stage, split);
89
- const analysis = await this.options.controller.runStage(stage);
90
- if (analysis.status === "fail") {
91
- const action = await this.options.rollbackManager.execute(stage, analysis.reasons.join(", "));
92
- this.options.eventBus?.emit({
93
- type: "rolled_back",
94
- timestamp: action.triggeredAt,
95
- payload: { stage, reasons: analysis.reasons }
96
- });
97
- return {
98
- status: "rolled_back",
99
- failedStage: stage,
100
- reasons: analysis.reasons
101
- };
102
- }
103
- }
104
- if (this.options.strategy.mode === "blue-green") {
105
- this.options.eventBus?.emit({
106
- type: "blue_green_swapped",
107
- timestamp: new Date,
108
- payload: { strategy: this.options.strategy }
109
- });
110
- }
111
- this.options.eventBus?.emit({
112
- type: "completed",
113
- timestamp: new Date,
114
- payload: { strategy: this.options.strategy }
115
- });
116
- return { status: "completed" };
117
- }
118
- }
119
- // src/events.ts
120
- class DeploymentEventBus {
121
- listeners = new Set;
122
- on(listener) {
123
- this.listeners.add(listener);
124
- return () => this.listeners.delete(listener);
125
- }
126
- emit(event) {
127
- for (const listener of this.listeners) {
128
- try {
129
- listener(event);
130
- } catch (error) {
131
- console.error("[progressive-delivery] listener error", error);
132
- }
133
- }
134
- }
135
- }
136
- // src/feature-flags.ts
137
- var ContractSpecFeatureFlags = {
138
- LIFECYCLE_DETECTION_ALPHA: "lifecycle_detection_alpha",
139
- LIFECYCLE_ADVISOR_ALPHA: "lifecycle_advisor_alpha",
140
- LIFECYCLE_MANAGED_SERVICE: "lifecycle_managed_service",
141
- STUDIO_VISUAL_BUILDER: "studio_visual_builder",
142
- STUDIO_AUTO_EVOLUTION: "studio_auto_evolution",
143
- STUDIO_BYOK: "studio_byok",
144
- STUDIO_DEDICATED_DEPLOYMENT: "studio_dedicated_deployment",
145
- STUDIO_INTEGRATION_HUB: "studio_integration_hub",
146
- STUDIO_KNOWLEDGE_HUB: "studio_knowledge_hub",
147
- STUDIO_TEMPLATES: "studio_templates"
148
- };
149
- var lifecycleFlags = [
150
- ContractSpecFeatureFlags.LIFECYCLE_DETECTION_ALPHA,
151
- ContractSpecFeatureFlags.LIFECYCLE_ADVISOR_ALPHA,
152
- ContractSpecFeatureFlags.LIFECYCLE_MANAGED_SERVICE
153
- ];
154
- var studioFlags = [
155
- ContractSpecFeatureFlags.STUDIO_VISUAL_BUILDER,
156
- ContractSpecFeatureFlags.STUDIO_AUTO_EVOLUTION,
157
- ContractSpecFeatureFlags.STUDIO_BYOK,
158
- ContractSpecFeatureFlags.STUDIO_DEDICATED_DEPLOYMENT,
159
- ContractSpecFeatureFlags.STUDIO_INTEGRATION_HUB,
160
- ContractSpecFeatureFlags.STUDIO_KNOWLEDGE_HUB,
161
- ContractSpecFeatureFlags.STUDIO_TEMPLATES
162
- ];
163
- // src/rollback-manager.ts
164
- class RollbackManager {
165
- options;
166
- constructor(options) {
167
- this.options = options;
168
- }
169
- async execute(stage, reason) {
170
- await this.options.rollback(stage, reason);
171
- const action = {
172
- reason,
173
- stage,
174
- triggeredAt: new Date
175
- };
176
- this.options.onRollback?.(action);
177
- return action;
178
- }
179
- }
180
- // src/traffic-shifter.ts
181
- class TrafficShifter {
182
- mode;
183
- constructor(mode) {
184
- this.mode = mode;
185
- }
186
- computeSplit(stage) {
187
- if (this.mode === "blue-green") {
188
- return stage.percentage >= 100 ? { stable: 0, candidate: 1 } : { stable: 1, candidate: 0 };
189
- }
190
- const candidate = Math.min(Math.max(stage.percentage / 100, 0), 1);
191
- return {
192
- candidate,
193
- stable: 1 - candidate
194
- };
195
- }
196
- }
197
- export {
198
- studioFlags,
199
- lifecycleFlags,
200
- createDefaultCanaryController,
201
- TrafficShifter,
202
- RollbackManager,
203
- DeploymentEventBus,
204
- DeploymentCoordinator,
205
- ContractSpecFeatureFlags,
206
- CanaryController,
207
- CanaryAnalyzer
208
- };
1
+ class n{defaults;constructor(e){this.defaults=e}evaluate(e,t){let a={...this.defaults,...e.thresholds},r=[];if(t.errorRate>a.errorRate)r.push(`errorRate ${t.errorRate.toFixed(4)} > ${a.errorRate}`);if(typeof a.latencyP50==="number"&&t.latencyP50>a.latencyP50)r.push(`latencyP50 ${t.latencyP50}ms > ${a.latencyP50}ms`);if(typeof a.latencyP95==="number"&&t.latencyP95>a.latencyP95)r.push(`latencyP95 ${t.latencyP95}ms > ${a.latencyP95}ms`);if(typeof a.latencyP99==="number"&&t.latencyP99>a.latencyP99)r.push(`latencyP99 ${t.latencyP99}ms > ${a.latencyP99}ms`);if(typeof a.throughputDrop==="number"&&t.throughput<a.throughputDrop)r.push(`throughput ${t.throughput} < ${a.throughputDrop}`);if(a.customEvaluator&&!a.customEvaluator(t))r.push("custom evaluator reported failure");return{status:r.length===0?"pass":"fail",reasons:r,metrics:t}}}var l=[{percentage:1,minDurationMs:300000,label:"1%"},{percentage:10,minDurationMs:300000,label:"10%"},{percentage:50,minDurationMs:600000,label:"50%"},{percentage:100,minDurationMs:900000,label:"100%"}];class s{options;stages;constructor(e){this.options=e;this.stages=e.strategy.stages&&e.strategy.stages.length>0?e.strategy.stages:l}getStageList(){return[...this.stages]}async runStage(e){this.options.eventBus?.emit({type:"stage_started",timestamp:new Date,payload:{stage:e}});let t=await this.options.metricsProvider(e,e.minDurationMs),a=this.options.analyzer.evaluate(e,t);return this.options.eventBus?.emit({type:a.status==="pass"?"stage_passed":"stage_failed",timestamp:new Date,payload:{stage:e,metrics:t,analysis:a}}),a}}function g(e,t,a){let r=new n(e.thresholds);return new s({strategy:e,analyzer:r,metricsProvider:t,eventBus:a})}class p{options;constructor(e){this.options=e}async run(){let e=this.options.controller.getStageList();for(let t of e){let a=this.options.trafficShifter.computeSplit(t);await this.options.applyTrafficSplit(t,a);let r=await this.options.controller.runStage(t);if(r.status==="fail"){let i=await this.options.rollbackManager.execute(t,r.reasons.join(", "));return this.options.eventBus?.emit({type:"rolled_back",timestamp:i.triggeredAt,payload:{stage:t,reasons:r.reasons}}),{status:"rolled_back",failedStage:t,reasons:r.reasons}}}if(this.options.strategy.mode==="blue-green")this.options.eventBus?.emit({type:"blue_green_swapped",timestamp:new Date,payload:{strategy:this.options.strategy}});return this.options.eventBus?.emit({type:"completed",timestamp:new Date,payload:{strategy:this.options.strategy}}),{status:"completed"}}}class y{listeners=new Set;on(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e){for(let t of this.listeners)try{t(e)}catch(a){console.error("[progressive-delivery] listener error",a)}}}var o={LIFECYCLE_DETECTION_ALPHA:"lifecycle_detection_alpha",LIFECYCLE_ADVISOR_ALPHA:"lifecycle_advisor_alpha",LIFECYCLE_MANAGED_SERVICE:"lifecycle_managed_service",STUDIO_VISUAL_BUILDER:"studio_visual_builder",STUDIO_AUTO_EVOLUTION:"studio_auto_evolution",STUDIO_BYOK:"studio_byok",STUDIO_DEDICATED_DEPLOYMENT:"studio_dedicated_deployment",STUDIO_INTEGRATION_HUB:"studio_integration_hub",STUDIO_KNOWLEDGE_HUB:"studio_knowledge_hub",STUDIO_TEMPLATES:"studio_templates"},h=[o.LIFECYCLE_DETECTION_ALPHA,o.LIFECYCLE_ADVISOR_ALPHA,o.LIFECYCLE_MANAGED_SERVICE],_=[o.STUDIO_VISUAL_BUILDER,o.STUDIO_AUTO_EVOLUTION,o.STUDIO_BYOK,o.STUDIO_DEDICATED_DEPLOYMENT,o.STUDIO_INTEGRATION_HUB,o.STUDIO_KNOWLEDGE_HUB,o.STUDIO_TEMPLATES];class c{options;constructor(e){this.options=e}async execute(e,t){await this.options.rollback(e,t);let a={reason:t,stage:e,triggeredAt:new Date};return this.options.onRollback?.(a),a}}class u{mode;constructor(e){this.mode=e}computeSplit(e){if(this.mode==="blue-green")return e.percentage>=100?{stable:0,candidate:1}:{stable:1,candidate:0};let t=Math.min(Math.max(e.percentage/100,0),1);return{candidate:t,stable:1-t}}}export{_ as studioFlags,h as lifecycleFlags,g as createDefaultCanaryController,u as TrafficShifter,c as RollbackManager,y as DeploymentEventBus,p as DeploymentCoordinator,o as ContractSpecFeatureFlags,s as CanaryController,n as CanaryAnalyzer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractspec/lib.progressive-delivery",
3
- "version": "34.0.16",
3
+ "version": "34.0.17",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -16,8 +16,8 @@
16
16
  "dev": "contractspec-bun-build dev",
17
17
  "clean": "rimraf dist .turbo",
18
18
  "lint": "bun lint:fix",
19
- "lint:fix": "biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .",
20
- "lint:check": "biome check .",
19
+ "lint:fix": "node ../../../scripts/biome.cjs check --write --unsafe --only=nursery/useSortedClasses . && node ../../../scripts/biome.cjs check --write .",
20
+ "lint:check": "node ../../../scripts/biome.cjs check .",
21
21
  "prebuild": "contractspec-bun-build prebuild",
22
22
  "typecheck": "tsc --noEmit"
23
23
  },
@@ -27,12 +27,12 @@
27
27
  "directory": "packages/libs/progressive-delivery"
28
28
  },
29
29
  "peerDependencies": {
30
- "@contractspec/lib.observability": "3.7.17"
30
+ "@contractspec/lib.observability": "3.7.18"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@contractspec/tool.typescript": "3.7.13",
34
34
  "typescript": "^5.9.3",
35
- "@contractspec/tool.bun": "3.7.13"
35
+ "@contractspec/tool.bun": "3.7.14"
36
36
  },
37
37
  "exports": {
38
38
  ".": {