@contractspec/lib.progressive-delivery 11.0.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +208 -0
- package/dist/canary-analyzer.d.ts +9 -13
- package/dist/canary-analyzer.d.ts.map +1 -1
- package/dist/canary-controller.d.ts +15 -19
- package/dist/canary-controller.d.ts.map +1 -1
- package/dist/deployment-coordinator.d.ts +20 -24
- package/dist/deployment-coordinator.d.ts.map +1 -1
- package/dist/events.d.ts +5 -9
- package/dist/events.d.ts.map +1 -1
- package/dist/feature-flags.d.ts +14 -17
- package/dist/feature-flags.d.ts.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +208 -8
- package/dist/node/index.js +208 -0
- package/dist/rollback-manager.d.ts +8 -12
- package/dist/rollback-manager.d.ts.map +1 -1
- package/dist/traffic-shifter.d.ts +5 -9
- package/dist/traffic-shifter.d.ts.map +1 -1
- package/dist/types.d.ts +56 -59
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -15
- package/dist/canary-analyzer.js +0 -28
- package/dist/canary-analyzer.js.map +0 -1
- package/dist/canary-controller.js +0 -66
- package/dist/canary-controller.js.map +0 -1
- package/dist/deployment-coordinator.js +0 -47
- package/dist/deployment-coordinator.js.map +0 -1
- package/dist/events.js +0 -19
- package/dist/events.js.map +0 -1
- package/dist/feature-flags.js +0 -31
- package/dist/feature-flags.js.map +0 -1
- package/dist/rollback-manager.js +0 -20
- package/dist/rollback-manager.js.map +0 -1
- package/dist/traffic-shifter.js +0 -24
- package/dist/traffic-shifter.js.map +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// src/events.ts
|
|
2
|
+
class DeploymentEventBus {
|
|
3
|
+
listeners = new Set;
|
|
4
|
+
on(listener) {
|
|
5
|
+
this.listeners.add(listener);
|
|
6
|
+
return () => this.listeners.delete(listener);
|
|
7
|
+
}
|
|
8
|
+
emit(event) {
|
|
9
|
+
for (const listener of this.listeners) {
|
|
10
|
+
try {
|
|
11
|
+
listener(event);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error("[progressive-delivery] listener error", error);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// src/traffic-shifter.ts
|
|
19
|
+
class TrafficShifter {
|
|
20
|
+
mode;
|
|
21
|
+
constructor(mode) {
|
|
22
|
+
this.mode = mode;
|
|
23
|
+
}
|
|
24
|
+
computeSplit(stage) {
|
|
25
|
+
if (this.mode === "blue-green") {
|
|
26
|
+
return stage.percentage >= 100 ? { stable: 0, candidate: 1 } : { stable: 1, candidate: 0 };
|
|
27
|
+
}
|
|
28
|
+
const candidate = Math.min(Math.max(stage.percentage / 100, 0), 1);
|
|
29
|
+
return {
|
|
30
|
+
candidate,
|
|
31
|
+
stable: 1 - candidate
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// src/canary-analyzer.ts
|
|
36
|
+
class CanaryAnalyzer {
|
|
37
|
+
defaults;
|
|
38
|
+
constructor(defaults) {
|
|
39
|
+
this.defaults = defaults;
|
|
40
|
+
}
|
|
41
|
+
evaluate(stage, metrics) {
|
|
42
|
+
const thresholds = { ...this.defaults, ...stage.thresholds };
|
|
43
|
+
const reasons = [];
|
|
44
|
+
if (metrics.errorRate > thresholds.errorRate) {
|
|
45
|
+
reasons.push(`errorRate ${metrics.errorRate.toFixed(4)} > ${thresholds.errorRate}`);
|
|
46
|
+
}
|
|
47
|
+
if (typeof thresholds.latencyP50 === "number" && metrics.latencyP50 > thresholds.latencyP50) {
|
|
48
|
+
reasons.push(`latencyP50 ${metrics.latencyP50}ms > ${thresholds.latencyP50}ms`);
|
|
49
|
+
}
|
|
50
|
+
if (typeof thresholds.latencyP95 === "number" && metrics.latencyP95 > thresholds.latencyP95) {
|
|
51
|
+
reasons.push(`latencyP95 ${metrics.latencyP95}ms > ${thresholds.latencyP95}ms`);
|
|
52
|
+
}
|
|
53
|
+
if (typeof thresholds.latencyP99 === "number" && metrics.latencyP99 > thresholds.latencyP99) {
|
|
54
|
+
reasons.push(`latencyP99 ${metrics.latencyP99}ms > ${thresholds.latencyP99}ms`);
|
|
55
|
+
}
|
|
56
|
+
if (typeof thresholds.throughputDrop === "number" && metrics.throughput < thresholds.throughputDrop) {
|
|
57
|
+
reasons.push(`throughput ${metrics.throughput} < ${thresholds.throughputDrop}`);
|
|
58
|
+
}
|
|
59
|
+
if (thresholds.customEvaluator && !thresholds.customEvaluator(metrics)) {
|
|
60
|
+
reasons.push("custom evaluator reported failure");
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
status: reasons.length === 0 ? "pass" : "fail",
|
|
64
|
+
reasons,
|
|
65
|
+
metrics
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// src/canary-controller.ts
|
|
70
|
+
var DEFAULT_STAGES = [
|
|
71
|
+
{ percentage: 1, minDurationMs: 5 * 60 * 1000, label: "1%" },
|
|
72
|
+
{ percentage: 10, minDurationMs: 5 * 60 * 1000, label: "10%" },
|
|
73
|
+
{ percentage: 50, minDurationMs: 10 * 60 * 1000, label: "50%" },
|
|
74
|
+
{ percentage: 100, minDurationMs: 15 * 60 * 1000, label: "100%" }
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
class CanaryController {
|
|
78
|
+
options;
|
|
79
|
+
stages;
|
|
80
|
+
constructor(options) {
|
|
81
|
+
this.options = options;
|
|
82
|
+
this.stages = options.strategy.stages && options.strategy.stages.length > 0 ? options.strategy.stages : DEFAULT_STAGES;
|
|
83
|
+
}
|
|
84
|
+
getStageList() {
|
|
85
|
+
return [...this.stages];
|
|
86
|
+
}
|
|
87
|
+
async runStage(stage) {
|
|
88
|
+
this.options.eventBus?.emit({
|
|
89
|
+
type: "stage_started",
|
|
90
|
+
timestamp: new Date,
|
|
91
|
+
payload: { stage }
|
|
92
|
+
});
|
|
93
|
+
const metrics = await this.options.metricsProvider(stage, stage.minDurationMs);
|
|
94
|
+
const analysis = this.options.analyzer.evaluate(stage, metrics);
|
|
95
|
+
this.options.eventBus?.emit({
|
|
96
|
+
type: analysis.status === "pass" ? "stage_passed" : "stage_failed",
|
|
97
|
+
timestamp: new Date,
|
|
98
|
+
payload: { stage, metrics, analysis }
|
|
99
|
+
});
|
|
100
|
+
return analysis;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function createDefaultCanaryController(strategy, metricsProvider, eventBus) {
|
|
104
|
+
const analyzer = new CanaryAnalyzer(strategy.thresholds);
|
|
105
|
+
return new CanaryController({
|
|
106
|
+
strategy,
|
|
107
|
+
analyzer,
|
|
108
|
+
metricsProvider,
|
|
109
|
+
eventBus
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// src/rollback-manager.ts
|
|
113
|
+
class RollbackManager {
|
|
114
|
+
options;
|
|
115
|
+
constructor(options) {
|
|
116
|
+
this.options = options;
|
|
117
|
+
}
|
|
118
|
+
async execute(stage, reason) {
|
|
119
|
+
await this.options.rollback(stage, reason);
|
|
120
|
+
const action = {
|
|
121
|
+
reason,
|
|
122
|
+
stage,
|
|
123
|
+
triggeredAt: new Date
|
|
124
|
+
};
|
|
125
|
+
this.options.onRollback?.(action);
|
|
126
|
+
return action;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// src/deployment-coordinator.ts
|
|
130
|
+
class DeploymentCoordinator {
|
|
131
|
+
options;
|
|
132
|
+
constructor(options) {
|
|
133
|
+
this.options = options;
|
|
134
|
+
}
|
|
135
|
+
async run() {
|
|
136
|
+
const stages = this.options.controller.getStageList();
|
|
137
|
+
for (const stage of stages) {
|
|
138
|
+
const split = this.options.trafficShifter.computeSplit(stage);
|
|
139
|
+
await this.options.applyTrafficSplit(stage, split);
|
|
140
|
+
const analysis = await this.options.controller.runStage(stage);
|
|
141
|
+
if (analysis.status === "fail") {
|
|
142
|
+
const action = await this.options.rollbackManager.execute(stage, analysis.reasons.join(", "));
|
|
143
|
+
this.options.eventBus?.emit({
|
|
144
|
+
type: "rolled_back",
|
|
145
|
+
timestamp: action.triggeredAt,
|
|
146
|
+
payload: { stage, reasons: analysis.reasons }
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
status: "rolled_back",
|
|
150
|
+
failedStage: stage,
|
|
151
|
+
reasons: analysis.reasons
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (this.options.strategy.mode === "blue-green") {
|
|
156
|
+
this.options.eventBus?.emit({
|
|
157
|
+
type: "blue_green_swapped",
|
|
158
|
+
timestamp: new Date,
|
|
159
|
+
payload: { strategy: this.options.strategy }
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
this.options.eventBus?.emit({
|
|
163
|
+
type: "completed",
|
|
164
|
+
timestamp: new Date,
|
|
165
|
+
payload: { strategy: this.options.strategy }
|
|
166
|
+
});
|
|
167
|
+
return { status: "completed" };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// src/feature-flags.ts
|
|
171
|
+
var ContractSpecFeatureFlags = {
|
|
172
|
+
LIFECYCLE_DETECTION_ALPHA: "lifecycle_detection_alpha",
|
|
173
|
+
LIFECYCLE_ADVISOR_ALPHA: "lifecycle_advisor_alpha",
|
|
174
|
+
LIFECYCLE_MANAGED_SERVICE: "lifecycle_managed_service",
|
|
175
|
+
STUDIO_VISUAL_BUILDER: "studio_visual_builder",
|
|
176
|
+
STUDIO_AUTO_EVOLUTION: "studio_auto_evolution",
|
|
177
|
+
STUDIO_BYOK: "studio_byok",
|
|
178
|
+
STUDIO_DEDICATED_DEPLOYMENT: "studio_dedicated_deployment",
|
|
179
|
+
STUDIO_INTEGRATION_HUB: "studio_integration_hub",
|
|
180
|
+
STUDIO_KNOWLEDGE_HUB: "studio_knowledge_hub",
|
|
181
|
+
STUDIO_TEMPLATES: "studio_templates"
|
|
182
|
+
};
|
|
183
|
+
var lifecycleFlags = [
|
|
184
|
+
ContractSpecFeatureFlags.LIFECYCLE_DETECTION_ALPHA,
|
|
185
|
+
ContractSpecFeatureFlags.LIFECYCLE_ADVISOR_ALPHA,
|
|
186
|
+
ContractSpecFeatureFlags.LIFECYCLE_MANAGED_SERVICE
|
|
187
|
+
];
|
|
188
|
+
var studioFlags = [
|
|
189
|
+
ContractSpecFeatureFlags.STUDIO_VISUAL_BUILDER,
|
|
190
|
+
ContractSpecFeatureFlags.STUDIO_AUTO_EVOLUTION,
|
|
191
|
+
ContractSpecFeatureFlags.STUDIO_BYOK,
|
|
192
|
+
ContractSpecFeatureFlags.STUDIO_DEDICATED_DEPLOYMENT,
|
|
193
|
+
ContractSpecFeatureFlags.STUDIO_INTEGRATION_HUB,
|
|
194
|
+
ContractSpecFeatureFlags.STUDIO_KNOWLEDGE_HUB,
|
|
195
|
+
ContractSpecFeatureFlags.STUDIO_TEMPLATES
|
|
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,16 +1,12 @@
|
|
|
1
|
-
import { CanaryStage, DeploymentMetrics, DeploymentThresholds } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
reasons: string[];
|
|
7
|
-
metrics: DeploymentMetrics;
|
|
1
|
+
import type { CanaryStage, DeploymentMetrics, DeploymentThresholds } from './types';
|
|
2
|
+
export interface AnalysisResult {
|
|
3
|
+
status: 'pass' | 'fail';
|
|
4
|
+
reasons: string[];
|
|
5
|
+
metrics: DeploymentMetrics;
|
|
8
6
|
}
|
|
9
|
-
declare class CanaryAnalyzer {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
export declare class CanaryAnalyzer {
|
|
8
|
+
private readonly defaults;
|
|
9
|
+
constructor(defaults: DeploymentThresholds);
|
|
10
|
+
evaluate(stage: CanaryStage, metrics: DeploymentMetrics): AnalysisResult;
|
|
13
11
|
}
|
|
14
|
-
//#endregion
|
|
15
|
-
export { AnalysisResult, CanaryAnalyzer };
|
|
16
12
|
//# sourceMappingURL=canary-analyzer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canary-analyzer.d.ts","
|
|
1
|
+
{"version":3,"file":"canary-analyzer.d.ts","sourceRoot":"","sources":["../src/canary-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,oBAAoB;IAE3D,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,GAAG,cAAc;CAwDzE"}
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DeploymentEventBus } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
metricsProvider: MetricsProvider;
|
|
10
|
-
eventBus?: DeploymentEventBus;
|
|
1
|
+
import { CanaryAnalyzer, type AnalysisResult } from './canary-analyzer';
|
|
2
|
+
import { DeploymentEventBus } from './events';
|
|
3
|
+
import type { CanaryStage, DeploymentStrategy, MetricsProvider } from './types';
|
|
4
|
+
export interface CanaryControllerOptions {
|
|
5
|
+
strategy: DeploymentStrategy;
|
|
6
|
+
analyzer: CanaryAnalyzer;
|
|
7
|
+
metricsProvider: MetricsProvider;
|
|
8
|
+
eventBus?: DeploymentEventBus;
|
|
11
9
|
}
|
|
12
|
-
declare class CanaryController {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
export declare class CanaryController {
|
|
11
|
+
private readonly options;
|
|
12
|
+
private readonly stages;
|
|
13
|
+
constructor(options: CanaryControllerOptions);
|
|
14
|
+
getStageList(): CanaryStage[];
|
|
15
|
+
runStage(stage: CanaryStage): Promise<AnalysisResult>;
|
|
18
16
|
}
|
|
19
|
-
declare function createDefaultCanaryController(strategy: DeploymentStrategy, metricsProvider: MetricsProvider, eventBus?: DeploymentEventBus): CanaryController;
|
|
20
|
-
//#endregion
|
|
21
|
-
export { CanaryController, CanaryControllerOptions, createDefaultCanaryController };
|
|
17
|
+
export declare function createDefaultCanaryController(strategy: DeploymentStrategy, metricsProvider: MetricsProvider, eventBus?: DeploymentEventBus): CanaryController;
|
|
22
18
|
//# sourceMappingURL=canary-controller.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canary-controller.d.ts","
|
|
1
|
+
{"version":3,"file":"canary-controller.d.ts","sourceRoot":"","sources":["../src/canary-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAShF,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,cAAc,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAEV,OAAO,EAAE,uBAAuB;IAO7D,YAAY;IAIN,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;CAqB5D;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,kBAAkB,EAC5B,eAAe,EAAE,eAAe,EAChC,QAAQ,CAAC,EAAE,kBAAkB,oBAS9B"}
|
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DeploymentEventBus } from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
applyTrafficSplit: (stage: CanaryStage, split: TrafficSplit) => void | Promise<void>;
|
|
14
|
-
eventBus?: DeploymentEventBus;
|
|
1
|
+
import { CanaryController } from './canary-controller';
|
|
2
|
+
import { DeploymentEventBus } from './events';
|
|
3
|
+
import { RollbackManager } from './rollback-manager';
|
|
4
|
+
import { TrafficShifter } from './traffic-shifter';
|
|
5
|
+
import type { CanaryStage, DeploymentStrategy, TrafficSplit } from './types';
|
|
6
|
+
export interface DeploymentCoordinatorOptions {
|
|
7
|
+
strategy: DeploymentStrategy;
|
|
8
|
+
controller: CanaryController;
|
|
9
|
+
trafficShifter: TrafficShifter;
|
|
10
|
+
rollbackManager: RollbackManager;
|
|
11
|
+
applyTrafficSplit: (stage: CanaryStage, split: TrafficSplit) => void | Promise<void>;
|
|
12
|
+
eventBus?: DeploymentEventBus;
|
|
15
13
|
}
|
|
16
|
-
interface DeploymentRunResult {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
export interface DeploymentRunResult {
|
|
15
|
+
status: 'completed' | 'rolled_back';
|
|
16
|
+
failedStage?: CanaryStage;
|
|
17
|
+
reasons?: string[];
|
|
20
18
|
}
|
|
21
|
-
declare class DeploymentCoordinator {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
export declare class DeploymentCoordinator {
|
|
20
|
+
private readonly options;
|
|
21
|
+
constructor(options: DeploymentCoordinatorOptions);
|
|
22
|
+
run(): Promise<DeploymentRunResult>;
|
|
25
23
|
}
|
|
26
|
-
//#endregion
|
|
27
|
-
export { DeploymentCoordinator, DeploymentCoordinatorOptions, DeploymentRunResult };
|
|
28
24
|
//# sourceMappingURL=deployment-coordinator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-coordinator.d.ts","
|
|
1
|
+
{"version":3,"file":"deployment-coordinator.d.ts","sourceRoot":"","sources":["../src/deployment-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE7E,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,iBAAiB,EAAE,CACjB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,YAAY,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,WAAW,GAAG,aAAa,CAAC;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,4BAA4B;IAE5D,GAAG,IAAI,OAAO,CAAC,mBAAmB,CAAC;CA4C1C"}
|
package/dist/events.d.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { DeploymentEvent, DeploymentEventListener } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
on(listener: DeploymentEventListener): () => boolean;
|
|
7
|
-
emit(event: DeploymentEvent): void;
|
|
1
|
+
import type { DeploymentEvent, DeploymentEventListener } from './types';
|
|
2
|
+
export declare class DeploymentEventBus {
|
|
3
|
+
private listeners;
|
|
4
|
+
on(listener: DeploymentEventListener): () => boolean;
|
|
5
|
+
emit(event: DeploymentEvent): void;
|
|
8
6
|
}
|
|
9
|
-
//#endregion
|
|
10
|
-
export { DeploymentEventBus };
|
|
11
7
|
//# sourceMappingURL=events.d.ts.map
|
package/dist/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAExE,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,SAAS,CAAsC;IAEvD,EAAE,CAAC,QAAQ,EAAE,uBAAuB;IAKpC,IAAI,CAAC,KAAK,EAAE,eAAe;CAS5B"}
|
package/dist/feature-flags.d.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
readonly STUDIO_TEMPLATES: "studio_templates";
|
|
1
|
+
export declare const ContractSpecFeatureFlags: {
|
|
2
|
+
readonly LIFECYCLE_DETECTION_ALPHA: "lifecycle_detection_alpha";
|
|
3
|
+
readonly LIFECYCLE_ADVISOR_ALPHA: "lifecycle_advisor_alpha";
|
|
4
|
+
readonly LIFECYCLE_MANAGED_SERVICE: "lifecycle_managed_service";
|
|
5
|
+
readonly STUDIO_VISUAL_BUILDER: "studio_visual_builder";
|
|
6
|
+
readonly STUDIO_AUTO_EVOLUTION: "studio_auto_evolution";
|
|
7
|
+
readonly STUDIO_BYOK: "studio_byok";
|
|
8
|
+
readonly STUDIO_DEDICATED_DEPLOYMENT: "studio_dedicated_deployment";
|
|
9
|
+
readonly STUDIO_INTEGRATION_HUB: "studio_integration_hub";
|
|
10
|
+
readonly STUDIO_KNOWLEDGE_HUB: "studio_knowledge_hub";
|
|
11
|
+
readonly STUDIO_TEMPLATES: "studio_templates";
|
|
13
12
|
};
|
|
14
|
-
type ContractSpecFeatureFlag = keyof typeof ContractSpecFeatureFlags;
|
|
15
|
-
declare const lifecycleFlags: ("lifecycle_detection_alpha" | "lifecycle_advisor_alpha" | "lifecycle_managed_service")[];
|
|
16
|
-
declare const studioFlags: ("studio_visual_builder" | "studio_auto_evolution" | "studio_byok" | "studio_dedicated_deployment" | "studio_integration_hub" | "studio_knowledge_hub" | "studio_templates")[];
|
|
17
|
-
//#endregion
|
|
18
|
-
export { ContractSpecFeatureFlag, ContractSpecFeatureFlags, lifecycleFlags, studioFlags };
|
|
13
|
+
export type ContractSpecFeatureFlag = keyof typeof ContractSpecFeatureFlags;
|
|
14
|
+
export declare const lifecycleFlags: ("lifecycle_detection_alpha" | "lifecycle_advisor_alpha" | "lifecycle_managed_service")[];
|
|
15
|
+
export declare const studioFlags: ("studio_visual_builder" | "studio_auto_evolution" | "studio_byok" | "studio_dedicated_deployment" | "studio_integration_hub" | "studio_knowledge_hub" | "studio_templates")[];
|
|
19
16
|
//# sourceMappingURL=feature-flags.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-flags.d.ts","
|
|
1
|
+
{"version":3,"file":"feature-flags.d.ts","sourceRoot":"","sources":["../src/feature-flags.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB;;;;;;;;;;;CAW3B,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,wBAAwB,CAAC;AAE5E,eAAO,MAAM,cAAc,2FAI1B,CAAC;AAEF,eAAO,MAAM,WAAW,gLAQvB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './events';
|
|
3
|
+
export * from './traffic-shifter';
|
|
4
|
+
export * from './canary-analyzer';
|
|
5
|
+
export * from './canary-controller';
|
|
6
|
+
export * from './rollback-manager';
|
|
7
|
+
export * from './deployment-coordinator';
|
|
8
|
+
export * from './feature-flags';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,209 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
// @bun
|
|
2
|
+
// src/events.ts
|
|
3
|
+
class DeploymentEventBus {
|
|
4
|
+
listeners = new Set;
|
|
5
|
+
on(listener) {
|
|
6
|
+
this.listeners.add(listener);
|
|
7
|
+
return () => this.listeners.delete(listener);
|
|
8
|
+
}
|
|
9
|
+
emit(event) {
|
|
10
|
+
for (const listener of this.listeners) {
|
|
11
|
+
try {
|
|
12
|
+
listener(event);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error("[progressive-delivery] listener error", error);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// src/traffic-shifter.ts
|
|
20
|
+
class TrafficShifter {
|
|
21
|
+
mode;
|
|
22
|
+
constructor(mode) {
|
|
23
|
+
this.mode = mode;
|
|
24
|
+
}
|
|
25
|
+
computeSplit(stage) {
|
|
26
|
+
if (this.mode === "blue-green") {
|
|
27
|
+
return stage.percentage >= 100 ? { stable: 0, candidate: 1 } : { stable: 1, candidate: 0 };
|
|
28
|
+
}
|
|
29
|
+
const candidate = Math.min(Math.max(stage.percentage / 100, 0), 1);
|
|
30
|
+
return {
|
|
31
|
+
candidate,
|
|
32
|
+
stable: 1 - candidate
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// src/canary-analyzer.ts
|
|
37
|
+
class CanaryAnalyzer {
|
|
38
|
+
defaults;
|
|
39
|
+
constructor(defaults) {
|
|
40
|
+
this.defaults = defaults;
|
|
41
|
+
}
|
|
42
|
+
evaluate(stage, metrics) {
|
|
43
|
+
const thresholds = { ...this.defaults, ...stage.thresholds };
|
|
44
|
+
const reasons = [];
|
|
45
|
+
if (metrics.errorRate > thresholds.errorRate) {
|
|
46
|
+
reasons.push(`errorRate ${metrics.errorRate.toFixed(4)} > ${thresholds.errorRate}`);
|
|
47
|
+
}
|
|
48
|
+
if (typeof thresholds.latencyP50 === "number" && metrics.latencyP50 > thresholds.latencyP50) {
|
|
49
|
+
reasons.push(`latencyP50 ${metrics.latencyP50}ms > ${thresholds.latencyP50}ms`);
|
|
50
|
+
}
|
|
51
|
+
if (typeof thresholds.latencyP95 === "number" && metrics.latencyP95 > thresholds.latencyP95) {
|
|
52
|
+
reasons.push(`latencyP95 ${metrics.latencyP95}ms > ${thresholds.latencyP95}ms`);
|
|
53
|
+
}
|
|
54
|
+
if (typeof thresholds.latencyP99 === "number" && metrics.latencyP99 > thresholds.latencyP99) {
|
|
55
|
+
reasons.push(`latencyP99 ${metrics.latencyP99}ms > ${thresholds.latencyP99}ms`);
|
|
56
|
+
}
|
|
57
|
+
if (typeof thresholds.throughputDrop === "number" && metrics.throughput < thresholds.throughputDrop) {
|
|
58
|
+
reasons.push(`throughput ${metrics.throughput} < ${thresholds.throughputDrop}`);
|
|
59
|
+
}
|
|
60
|
+
if (thresholds.customEvaluator && !thresholds.customEvaluator(metrics)) {
|
|
61
|
+
reasons.push("custom evaluator reported failure");
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
status: reasons.length === 0 ? "pass" : "fail",
|
|
65
|
+
reasons,
|
|
66
|
+
metrics
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// src/canary-controller.ts
|
|
71
|
+
var DEFAULT_STAGES = [
|
|
72
|
+
{ percentage: 1, minDurationMs: 5 * 60 * 1000, label: "1%" },
|
|
73
|
+
{ percentage: 10, minDurationMs: 5 * 60 * 1000, label: "10%" },
|
|
74
|
+
{ percentage: 50, minDurationMs: 10 * 60 * 1000, label: "50%" },
|
|
75
|
+
{ percentage: 100, minDurationMs: 15 * 60 * 1000, label: "100%" }
|
|
76
|
+
];
|
|
8
77
|
|
|
9
|
-
|
|
78
|
+
class CanaryController {
|
|
79
|
+
options;
|
|
80
|
+
stages;
|
|
81
|
+
constructor(options) {
|
|
82
|
+
this.options = options;
|
|
83
|
+
this.stages = options.strategy.stages && options.strategy.stages.length > 0 ? options.strategy.stages : DEFAULT_STAGES;
|
|
84
|
+
}
|
|
85
|
+
getStageList() {
|
|
86
|
+
return [...this.stages];
|
|
87
|
+
}
|
|
88
|
+
async runStage(stage) {
|
|
89
|
+
this.options.eventBus?.emit({
|
|
90
|
+
type: "stage_started",
|
|
91
|
+
timestamp: new Date,
|
|
92
|
+
payload: { stage }
|
|
93
|
+
});
|
|
94
|
+
const metrics = await this.options.metricsProvider(stage, stage.minDurationMs);
|
|
95
|
+
const analysis = this.options.analyzer.evaluate(stage, metrics);
|
|
96
|
+
this.options.eventBus?.emit({
|
|
97
|
+
type: analysis.status === "pass" ? "stage_passed" : "stage_failed",
|
|
98
|
+
timestamp: new Date,
|
|
99
|
+
payload: { stage, metrics, analysis }
|
|
100
|
+
});
|
|
101
|
+
return analysis;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function createDefaultCanaryController(strategy, metricsProvider, eventBus) {
|
|
105
|
+
const analyzer = new CanaryAnalyzer(strategy.thresholds);
|
|
106
|
+
return new CanaryController({
|
|
107
|
+
strategy,
|
|
108
|
+
analyzer,
|
|
109
|
+
metricsProvider,
|
|
110
|
+
eventBus
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// src/rollback-manager.ts
|
|
114
|
+
class RollbackManager {
|
|
115
|
+
options;
|
|
116
|
+
constructor(options) {
|
|
117
|
+
this.options = options;
|
|
118
|
+
}
|
|
119
|
+
async execute(stage, reason) {
|
|
120
|
+
await this.options.rollback(stage, reason);
|
|
121
|
+
const action = {
|
|
122
|
+
reason,
|
|
123
|
+
stage,
|
|
124
|
+
triggeredAt: new Date
|
|
125
|
+
};
|
|
126
|
+
this.options.onRollback?.(action);
|
|
127
|
+
return action;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// src/deployment-coordinator.ts
|
|
131
|
+
class DeploymentCoordinator {
|
|
132
|
+
options;
|
|
133
|
+
constructor(options) {
|
|
134
|
+
this.options = options;
|
|
135
|
+
}
|
|
136
|
+
async run() {
|
|
137
|
+
const stages = this.options.controller.getStageList();
|
|
138
|
+
for (const stage of stages) {
|
|
139
|
+
const split = this.options.trafficShifter.computeSplit(stage);
|
|
140
|
+
await this.options.applyTrafficSplit(stage, split);
|
|
141
|
+
const analysis = await this.options.controller.runStage(stage);
|
|
142
|
+
if (analysis.status === "fail") {
|
|
143
|
+
const action = await this.options.rollbackManager.execute(stage, analysis.reasons.join(", "));
|
|
144
|
+
this.options.eventBus?.emit({
|
|
145
|
+
type: "rolled_back",
|
|
146
|
+
timestamp: action.triggeredAt,
|
|
147
|
+
payload: { stage, reasons: analysis.reasons }
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
status: "rolled_back",
|
|
151
|
+
failedStage: stage,
|
|
152
|
+
reasons: analysis.reasons
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (this.options.strategy.mode === "blue-green") {
|
|
157
|
+
this.options.eventBus?.emit({
|
|
158
|
+
type: "blue_green_swapped",
|
|
159
|
+
timestamp: new Date,
|
|
160
|
+
payload: { strategy: this.options.strategy }
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
this.options.eventBus?.emit({
|
|
164
|
+
type: "completed",
|
|
165
|
+
timestamp: new Date,
|
|
166
|
+
payload: { strategy: this.options.strategy }
|
|
167
|
+
});
|
|
168
|
+
return { status: "completed" };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// src/feature-flags.ts
|
|
172
|
+
var ContractSpecFeatureFlags = {
|
|
173
|
+
LIFECYCLE_DETECTION_ALPHA: "lifecycle_detection_alpha",
|
|
174
|
+
LIFECYCLE_ADVISOR_ALPHA: "lifecycle_advisor_alpha",
|
|
175
|
+
LIFECYCLE_MANAGED_SERVICE: "lifecycle_managed_service",
|
|
176
|
+
STUDIO_VISUAL_BUILDER: "studio_visual_builder",
|
|
177
|
+
STUDIO_AUTO_EVOLUTION: "studio_auto_evolution",
|
|
178
|
+
STUDIO_BYOK: "studio_byok",
|
|
179
|
+
STUDIO_DEDICATED_DEPLOYMENT: "studio_dedicated_deployment",
|
|
180
|
+
STUDIO_INTEGRATION_HUB: "studio_integration_hub",
|
|
181
|
+
STUDIO_KNOWLEDGE_HUB: "studio_knowledge_hub",
|
|
182
|
+
STUDIO_TEMPLATES: "studio_templates"
|
|
183
|
+
};
|
|
184
|
+
var lifecycleFlags = [
|
|
185
|
+
ContractSpecFeatureFlags.LIFECYCLE_DETECTION_ALPHA,
|
|
186
|
+
ContractSpecFeatureFlags.LIFECYCLE_ADVISOR_ALPHA,
|
|
187
|
+
ContractSpecFeatureFlags.LIFECYCLE_MANAGED_SERVICE
|
|
188
|
+
];
|
|
189
|
+
var studioFlags = [
|
|
190
|
+
ContractSpecFeatureFlags.STUDIO_VISUAL_BUILDER,
|
|
191
|
+
ContractSpecFeatureFlags.STUDIO_AUTO_EVOLUTION,
|
|
192
|
+
ContractSpecFeatureFlags.STUDIO_BYOK,
|
|
193
|
+
ContractSpecFeatureFlags.STUDIO_DEDICATED_DEPLOYMENT,
|
|
194
|
+
ContractSpecFeatureFlags.STUDIO_INTEGRATION_HUB,
|
|
195
|
+
ContractSpecFeatureFlags.STUDIO_KNOWLEDGE_HUB,
|
|
196
|
+
ContractSpecFeatureFlags.STUDIO_TEMPLATES
|
|
197
|
+
];
|
|
198
|
+
export {
|
|
199
|
+
studioFlags,
|
|
200
|
+
lifecycleFlags,
|
|
201
|
+
createDefaultCanaryController,
|
|
202
|
+
TrafficShifter,
|
|
203
|
+
RollbackManager,
|
|
204
|
+
DeploymentEventBus,
|
|
205
|
+
DeploymentCoordinator,
|
|
206
|
+
ContractSpecFeatureFlags,
|
|
207
|
+
CanaryController,
|
|
208
|
+
CanaryAnalyzer
|
|
209
|
+
};
|