@contractspec/lib.progressive-delivery 10.0.1 → 12.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,15 +1,11 @@
|
|
|
1
|
-
import { CanaryStage, RollbackAction } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
rollback(stage: CanaryStage, reason: string): Promise<void> | void;
|
|
6
|
-
onRollback?: (action: RollbackAction) => void;
|
|
1
|
+
import type { CanaryStage, RollbackAction } from './types';
|
|
2
|
+
export interface RollbackManagerOptions {
|
|
3
|
+
rollback(stage: CanaryStage, reason: string): Promise<void> | void;
|
|
4
|
+
onRollback?: (action: RollbackAction) => void;
|
|
7
5
|
}
|
|
8
|
-
declare class RollbackManager {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export declare class RollbackManager {
|
|
7
|
+
private readonly options;
|
|
8
|
+
constructor(options: RollbackManagerOptions);
|
|
9
|
+
execute(stage: CanaryStage, reason: string): Promise<RollbackAction>;
|
|
12
10
|
}
|
|
13
|
-
//#endregion
|
|
14
|
-
export { RollbackManager, RollbackManagerOptions };
|
|
15
11
|
//# sourceMappingURL=rollback-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rollback-manager.d.ts","
|
|
1
|
+
{"version":3,"file":"rollback-manager.d.ts","sourceRoot":"","sources":["../src/rollback-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACnE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CAC/C;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,sBAAsB;IAEtD,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM;CAUjD"}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { CanaryStage, DeploymentMode, TrafficSplit } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
constructor(mode: DeploymentMode);
|
|
7
|
-
computeSplit(stage: CanaryStage): TrafficSplit;
|
|
1
|
+
import type { CanaryStage, DeploymentMode, TrafficSplit } from './types';
|
|
2
|
+
export declare class TrafficShifter {
|
|
3
|
+
private readonly mode;
|
|
4
|
+
constructor(mode: DeploymentMode);
|
|
5
|
+
computeSplit(stage: CanaryStage): TrafficSplit;
|
|
8
6
|
}
|
|
9
|
-
//#endregion
|
|
10
|
-
export { TrafficShifter };
|
|
11
7
|
//# sourceMappingURL=traffic-shifter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traffic-shifter.d.ts","
|
|
1
|
+
{"version":3,"file":"traffic-shifter.d.ts","sourceRoot":"","sources":["../src/traffic-shifter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEzE,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,cAAc;IAEjD,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY;CAa/C"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,60 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
type DeploymentEventListener = (event: DeploymentEvent) => void;
|
|
58
|
-
//#endregion
|
|
59
|
-
export { CanaryStage, DeploymentEvent, DeploymentEventListener, DeploymentMetrics, DeploymentMode, DeploymentStrategy, DeploymentThresholds, MetricsProvider, OperationTarget, RollbackAction, TrafficSplit };
|
|
1
|
+
export type DeploymentMode = 'canary' | 'blue-green';
|
|
2
|
+
export interface OperationTarget {
|
|
3
|
+
name: string;
|
|
4
|
+
version: number;
|
|
5
|
+
namespace?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DeploymentThresholds {
|
|
9
|
+
/** e.g. 0.01 = 1% */
|
|
10
|
+
errorRate: number;
|
|
11
|
+
latencyP50?: number;
|
|
12
|
+
latencyP95?: number;
|
|
13
|
+
latencyP99?: number;
|
|
14
|
+
throughputDrop?: number;
|
|
15
|
+
customEvaluator?: (metrics: DeploymentMetrics) => boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface CanaryStage {
|
|
18
|
+
percentage: number;
|
|
19
|
+
minDurationMs: number;
|
|
20
|
+
holdAfterMs?: number;
|
|
21
|
+
/** optional override thresholds */
|
|
22
|
+
thresholds?: Partial<DeploymentThresholds>;
|
|
23
|
+
label?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface DeploymentStrategy {
|
|
26
|
+
target: OperationTarget;
|
|
27
|
+
mode: DeploymentMode;
|
|
28
|
+
stages?: CanaryStage[];
|
|
29
|
+
thresholds: DeploymentThresholds;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
export interface DeploymentMetrics {
|
|
33
|
+
errorRate: number;
|
|
34
|
+
latencyP50: number;
|
|
35
|
+
latencyP95: number;
|
|
36
|
+
latencyP99: number;
|
|
37
|
+
throughput: number;
|
|
38
|
+
sampleSize?: number;
|
|
39
|
+
timestamp?: Date;
|
|
40
|
+
}
|
|
41
|
+
export type MetricsProvider = (stage: CanaryStage, windowMs: number) => Promise<DeploymentMetrics>;
|
|
42
|
+
export interface TrafficSplit {
|
|
43
|
+
stable: number;
|
|
44
|
+
candidate: number;
|
|
45
|
+
}
|
|
46
|
+
export interface RollbackAction {
|
|
47
|
+
reason: string;
|
|
48
|
+
stage: CanaryStage;
|
|
49
|
+
triggeredAt: Date;
|
|
50
|
+
}
|
|
51
|
+
export interface DeploymentEvent<TPayload = Record<string, unknown>> {
|
|
52
|
+
type: 'stage_started' | 'stage_passed' | 'stage_failed' | 'rolled_back' | 'completed' | 'blue_green_swapped';
|
|
53
|
+
payload?: TPayload;
|
|
54
|
+
timestamp: Date;
|
|
55
|
+
}
|
|
56
|
+
export type DeploymentEventListener = (event: DeploymentEvent) => void;
|
|
60
57
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,YAAY,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,oBAAoB,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,CAC5B,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEhC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,eAAe,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACjE,IAAI,EACA,eAAe,GACf,cAAc,GACd,cAAc,GACd,aAAa,GACb,WAAW,GACX,oBAAoB,CAAC;IACzB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/lib.progressive-delivery",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -10,34 +10,39 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
12
12
|
"publish:pkg:canary": "bun publish:pkg --tag canary",
|
|
13
|
-
"build": "bun build:
|
|
14
|
-
"build:bundle": "
|
|
15
|
-
"build:types": "
|
|
16
|
-
"dev": "bun
|
|
13
|
+
"build": "bun run prebuild && bun run build:bundle && bun run build:types",
|
|
14
|
+
"build:bundle": "contractspec-bun-build transpile",
|
|
15
|
+
"build:types": "contractspec-bun-build types",
|
|
16
|
+
"dev": "contractspec-bun-build dev",
|
|
17
17
|
"clean": "rimraf dist .turbo",
|
|
18
18
|
"lint": "bun lint:fix",
|
|
19
19
|
"lint:fix": "eslint src --fix",
|
|
20
|
-
"lint:check": "eslint src"
|
|
20
|
+
"lint:check": "eslint src",
|
|
21
|
+
"prebuild": "contractspec-bun-build prebuild",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
21
23
|
},
|
|
22
24
|
"peerDependencies": {
|
|
23
|
-
"@contractspec/lib.observability": "1.
|
|
25
|
+
"@contractspec/lib.observability": "1.58.0"
|
|
24
26
|
},
|
|
25
27
|
"devDependencies": {
|
|
26
|
-
"@contractspec/tool.
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"typescript": "^5.9.3"
|
|
28
|
+
"@contractspec/tool.typescript": "1.58.0",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"@contractspec/tool.bun": "1.57.0"
|
|
30
31
|
},
|
|
31
32
|
"exports": {
|
|
32
|
-
".": "./
|
|
33
|
-
"./*": "./*"
|
|
33
|
+
".": "./src/index.ts"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"registry": "https://registry.npmjs.org/",
|
|
37
37
|
"access": "public",
|
|
38
38
|
"exports": {
|
|
39
|
-
".":
|
|
40
|
-
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"bun": "./dist/index.js",
|
|
42
|
+
"node": "./dist/node/index.mjs",
|
|
43
|
+
"browser": "./dist/browser/index.js",
|
|
44
|
+
"default": "./dist/index.js"
|
|
45
|
+
}
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
}
|
package/dist/canary-analyzer.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
//#region src/canary-analyzer.ts
|
|
2
|
-
var CanaryAnalyzer = class {
|
|
3
|
-
constructor(defaults) {
|
|
4
|
-
this.defaults = defaults;
|
|
5
|
-
}
|
|
6
|
-
evaluate(stage, metrics) {
|
|
7
|
-
const thresholds = {
|
|
8
|
-
...this.defaults,
|
|
9
|
-
...stage.thresholds
|
|
10
|
-
};
|
|
11
|
-
const reasons = [];
|
|
12
|
-
if (metrics.errorRate > thresholds.errorRate) reasons.push(`errorRate ${metrics.errorRate.toFixed(4)} > ${thresholds.errorRate}`);
|
|
13
|
-
if (typeof thresholds.latencyP50 === "number" && metrics.latencyP50 > thresholds.latencyP50) reasons.push(`latencyP50 ${metrics.latencyP50}ms > ${thresholds.latencyP50}ms`);
|
|
14
|
-
if (typeof thresholds.latencyP95 === "number" && metrics.latencyP95 > thresholds.latencyP95) reasons.push(`latencyP95 ${metrics.latencyP95}ms > ${thresholds.latencyP95}ms`);
|
|
15
|
-
if (typeof thresholds.latencyP99 === "number" && metrics.latencyP99 > thresholds.latencyP99) reasons.push(`latencyP99 ${metrics.latencyP99}ms > ${thresholds.latencyP99}ms`);
|
|
16
|
-
if (typeof thresholds.throughputDrop === "number" && metrics.throughput < thresholds.throughputDrop) reasons.push(`throughput ${metrics.throughput} < ${thresholds.throughputDrop}`);
|
|
17
|
-
if (thresholds.customEvaluator && !thresholds.customEvaluator(metrics)) reasons.push("custom evaluator reported failure");
|
|
18
|
-
return {
|
|
19
|
-
status: reasons.length === 0 ? "pass" : "fail",
|
|
20
|
-
reasons,
|
|
21
|
-
metrics
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
//#endregion
|
|
27
|
-
export { CanaryAnalyzer };
|
|
28
|
-
//# sourceMappingURL=canary-analyzer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"canary-analyzer.js","names":[],"sources":["../src/canary-analyzer.ts"],"sourcesContent":["import type {\n CanaryStage,\n DeploymentMetrics,\n DeploymentThresholds,\n} from './types';\n\nexport interface AnalysisResult {\n status: 'pass' | 'fail';\n reasons: string[];\n metrics: DeploymentMetrics;\n}\n\nexport class CanaryAnalyzer {\n constructor(private readonly defaults: DeploymentThresholds) {}\n\n evaluate(stage: CanaryStage, metrics: DeploymentMetrics): AnalysisResult {\n const thresholds = { ...this.defaults, ...stage.thresholds };\n const reasons: string[] = [];\n\n if (metrics.errorRate > thresholds.errorRate) {\n reasons.push(\n `errorRate ${metrics.errorRate.toFixed(4)} > ${thresholds.errorRate}`\n );\n }\n\n if (\n typeof thresholds.latencyP50 === 'number' &&\n metrics.latencyP50 > thresholds.latencyP50\n ) {\n reasons.push(\n `latencyP50 ${metrics.latencyP50}ms > ${thresholds.latencyP50}ms`\n );\n }\n\n if (\n typeof thresholds.latencyP95 === 'number' &&\n metrics.latencyP95 > thresholds.latencyP95\n ) {\n reasons.push(\n `latencyP95 ${metrics.latencyP95}ms > ${thresholds.latencyP95}ms`\n );\n }\n\n if (\n typeof thresholds.latencyP99 === 'number' &&\n metrics.latencyP99 > thresholds.latencyP99\n ) {\n reasons.push(\n `latencyP99 ${metrics.latencyP99}ms > ${thresholds.latencyP99}ms`\n );\n }\n\n if (\n typeof thresholds.throughputDrop === 'number' &&\n metrics.throughput < thresholds.throughputDrop\n ) {\n reasons.push(\n `throughput ${metrics.throughput} < ${thresholds.throughputDrop}`\n );\n }\n\n if (thresholds.customEvaluator && !thresholds.customEvaluator(metrics)) {\n reasons.push('custom evaluator reported failure');\n }\n\n return {\n status: reasons.length === 0 ? 'pass' : 'fail',\n reasons,\n metrics,\n };\n }\n}\n"],"mappings":";AAYA,IAAa,iBAAb,MAA4B;CAC1B,YAAY,AAAiB,UAAgC;EAAhC;;CAE7B,SAAS,OAAoB,SAA4C;EACvE,MAAM,aAAa;GAAE,GAAG,KAAK;GAAU,GAAG,MAAM;GAAY;EAC5D,MAAM,UAAoB,EAAE;AAE5B,MAAI,QAAQ,YAAY,WAAW,UACjC,SAAQ,KACN,aAAa,QAAQ,UAAU,QAAQ,EAAE,CAAC,KAAK,WAAW,YAC3D;AAGH,MACE,OAAO,WAAW,eAAe,YACjC,QAAQ,aAAa,WAAW,WAEhC,SAAQ,KACN,cAAc,QAAQ,WAAW,OAAO,WAAW,WAAW,IAC/D;AAGH,MACE,OAAO,WAAW,eAAe,YACjC,QAAQ,aAAa,WAAW,WAEhC,SAAQ,KACN,cAAc,QAAQ,WAAW,OAAO,WAAW,WAAW,IAC/D;AAGH,MACE,OAAO,WAAW,eAAe,YACjC,QAAQ,aAAa,WAAW,WAEhC,SAAQ,KACN,cAAc,QAAQ,WAAW,OAAO,WAAW,WAAW,IAC/D;AAGH,MACE,OAAO,WAAW,mBAAmB,YACrC,QAAQ,aAAa,WAAW,eAEhC,SAAQ,KACN,cAAc,QAAQ,WAAW,KAAK,WAAW,iBAClD;AAGH,MAAI,WAAW,mBAAmB,CAAC,WAAW,gBAAgB,QAAQ,CACpE,SAAQ,KAAK,oCAAoC;AAGnD,SAAO;GACL,QAAQ,QAAQ,WAAW,IAAI,SAAS;GACxC;GACA;GACD"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { CanaryAnalyzer } from "./canary-analyzer.js";
|
|
2
|
-
|
|
3
|
-
//#region src/canary-controller.ts
|
|
4
|
-
const DEFAULT_STAGES = [
|
|
5
|
-
{
|
|
6
|
-
percentage: 1,
|
|
7
|
-
minDurationMs: 300 * 1e3,
|
|
8
|
-
label: "1%"
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
percentage: 10,
|
|
12
|
-
minDurationMs: 300 * 1e3,
|
|
13
|
-
label: "10%"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
percentage: 50,
|
|
17
|
-
minDurationMs: 600 * 1e3,
|
|
18
|
-
label: "50%"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
percentage: 100,
|
|
22
|
-
minDurationMs: 900 * 1e3,
|
|
23
|
-
label: "100%"
|
|
24
|
-
}
|
|
25
|
-
];
|
|
26
|
-
var CanaryController = class {
|
|
27
|
-
stages;
|
|
28
|
-
constructor(options) {
|
|
29
|
-
this.options = options;
|
|
30
|
-
this.stages = options.strategy.stages && options.strategy.stages.length > 0 ? options.strategy.stages : DEFAULT_STAGES;
|
|
31
|
-
}
|
|
32
|
-
getStageList() {
|
|
33
|
-
return [...this.stages];
|
|
34
|
-
}
|
|
35
|
-
async runStage(stage) {
|
|
36
|
-
this.options.eventBus?.emit({
|
|
37
|
-
type: "stage_started",
|
|
38
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
39
|
-
payload: { stage }
|
|
40
|
-
});
|
|
41
|
-
const metrics = await this.options.metricsProvider(stage, stage.minDurationMs);
|
|
42
|
-
const analysis = this.options.analyzer.evaluate(stage, metrics);
|
|
43
|
-
this.options.eventBus?.emit({
|
|
44
|
-
type: analysis.status === "pass" ? "stage_passed" : "stage_failed",
|
|
45
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
46
|
-
payload: {
|
|
47
|
-
stage,
|
|
48
|
-
metrics,
|
|
49
|
-
analysis
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
return analysis;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
function createDefaultCanaryController(strategy, metricsProvider, eventBus) {
|
|
56
|
-
return new CanaryController({
|
|
57
|
-
strategy,
|
|
58
|
-
analyzer: new CanaryAnalyzer(strategy.thresholds),
|
|
59
|
-
metricsProvider,
|
|
60
|
-
eventBus
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
//#endregion
|
|
65
|
-
export { CanaryController, createDefaultCanaryController };
|
|
66
|
-
//# sourceMappingURL=canary-controller.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"canary-controller.js","names":[],"sources":["../src/canary-controller.ts"],"sourcesContent":["import { CanaryAnalyzer, type AnalysisResult } from './canary-analyzer';\nimport { DeploymentEventBus } from './events';\nimport type { CanaryStage, DeploymentStrategy, MetricsProvider } from './types';\n\nconst DEFAULT_STAGES: CanaryStage[] = [\n { percentage: 1, minDurationMs: 5 * 60 * 1000, label: '1%' },\n { percentage: 10, minDurationMs: 5 * 60 * 1000, label: '10%' },\n { percentage: 50, minDurationMs: 10 * 60 * 1000, label: '50%' },\n { percentage: 100, minDurationMs: 15 * 60 * 1000, label: '100%' },\n];\n\nexport interface CanaryControllerOptions {\n strategy: DeploymentStrategy;\n analyzer: CanaryAnalyzer;\n metricsProvider: MetricsProvider;\n eventBus?: DeploymentEventBus;\n}\n\nexport class CanaryController {\n private readonly stages: CanaryStage[];\n\n constructor(private readonly options: CanaryControllerOptions) {\n this.stages =\n options.strategy.stages && options.strategy.stages.length > 0\n ? options.strategy.stages\n : DEFAULT_STAGES;\n }\n\n getStageList() {\n return [...this.stages];\n }\n\n async runStage(stage: CanaryStage): Promise<AnalysisResult> {\n this.options.eventBus?.emit({\n type: 'stage_started',\n timestamp: new Date(),\n payload: { stage },\n });\n\n const metrics = await this.options.metricsProvider(\n stage,\n stage.minDurationMs\n );\n const analysis = this.options.analyzer.evaluate(stage, metrics);\n\n this.options.eventBus?.emit({\n type: analysis.status === 'pass' ? 'stage_passed' : 'stage_failed',\n timestamp: new Date(),\n payload: { stage, metrics, analysis },\n });\n\n return analysis;\n }\n}\n\nexport function createDefaultCanaryController(\n strategy: DeploymentStrategy,\n metricsProvider: MetricsProvider,\n eventBus?: DeploymentEventBus\n) {\n const analyzer = new CanaryAnalyzer(strategy.thresholds);\n return new CanaryController({\n strategy,\n analyzer,\n metricsProvider,\n eventBus,\n });\n}\n"],"mappings":";;;AAIA,MAAM,iBAAgC;CACpC;EAAE,YAAY;EAAG,eAAe,MAAS;EAAM,OAAO;EAAM;CAC5D;EAAE,YAAY;EAAI,eAAe,MAAS;EAAM,OAAO;EAAO;CAC9D;EAAE,YAAY;EAAI,eAAe,MAAU;EAAM,OAAO;EAAO;CAC/D;EAAE,YAAY;EAAK,eAAe,MAAU;EAAM,OAAO;EAAQ;CAClE;AASD,IAAa,mBAAb,MAA8B;CAC5B,AAAiB;CAEjB,YAAY,AAAiB,SAAkC;EAAlC;AAC3B,OAAK,SACH,QAAQ,SAAS,UAAU,QAAQ,SAAS,OAAO,SAAS,IACxD,QAAQ,SAAS,SACjB;;CAGR,eAAe;AACb,SAAO,CAAC,GAAG,KAAK,OAAO;;CAGzB,MAAM,SAAS,OAA6C;AAC1D,OAAK,QAAQ,UAAU,KAAK;GAC1B,MAAM;GACN,2BAAW,IAAI,MAAM;GACrB,SAAS,EAAE,OAAO;GACnB,CAAC;EAEF,MAAM,UAAU,MAAM,KAAK,QAAQ,gBACjC,OACA,MAAM,cACP;EACD,MAAM,WAAW,KAAK,QAAQ,SAAS,SAAS,OAAO,QAAQ;AAE/D,OAAK,QAAQ,UAAU,KAAK;GAC1B,MAAM,SAAS,WAAW,SAAS,iBAAiB;GACpD,2BAAW,IAAI,MAAM;GACrB,SAAS;IAAE;IAAO;IAAS;IAAU;GACtC,CAAC;AAEF,SAAO;;;AAIX,SAAgB,8BACd,UACA,iBACA,UACA;AAEA,QAAO,IAAI,iBAAiB;EAC1B;EACA,UAHe,IAAI,eAAe,SAAS,WAAW;EAItD;EACA;EACD,CAAC"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import "./canary-controller.js";
|
|
2
|
-
|
|
3
|
-
//#region src/deployment-coordinator.ts
|
|
4
|
-
var DeploymentCoordinator = class {
|
|
5
|
-
constructor(options) {
|
|
6
|
-
this.options = options;
|
|
7
|
-
}
|
|
8
|
-
async run() {
|
|
9
|
-
const stages = this.options.controller.getStageList();
|
|
10
|
-
for (const stage of stages) {
|
|
11
|
-
const split = this.options.trafficShifter.computeSplit(stage);
|
|
12
|
-
await this.options.applyTrafficSplit(stage, split);
|
|
13
|
-
const analysis = await this.options.controller.runStage(stage);
|
|
14
|
-
if (analysis.status === "fail") {
|
|
15
|
-
const action = await this.options.rollbackManager.execute(stage, analysis.reasons.join(", "));
|
|
16
|
-
this.options.eventBus?.emit({
|
|
17
|
-
type: "rolled_back",
|
|
18
|
-
timestamp: action.triggeredAt,
|
|
19
|
-
payload: {
|
|
20
|
-
stage,
|
|
21
|
-
reasons: analysis.reasons
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return {
|
|
25
|
-
status: "rolled_back",
|
|
26
|
-
failedStage: stage,
|
|
27
|
-
reasons: analysis.reasons
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (this.options.strategy.mode === "blue-green") this.options.eventBus?.emit({
|
|
32
|
-
type: "blue_green_swapped",
|
|
33
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
34
|
-
payload: { strategy: this.options.strategy }
|
|
35
|
-
});
|
|
36
|
-
this.options.eventBus?.emit({
|
|
37
|
-
type: "completed",
|
|
38
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
39
|
-
payload: { strategy: this.options.strategy }
|
|
40
|
-
});
|
|
41
|
-
return { status: "completed" };
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
//#endregion
|
|
46
|
-
export { DeploymentCoordinator };
|
|
47
|
-
//# sourceMappingURL=deployment-coordinator.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-coordinator.js","names":[],"sources":["../src/deployment-coordinator.ts"],"sourcesContent":["import { CanaryController } from './canary-controller';\nimport { DeploymentEventBus } from './events';\nimport { RollbackManager } from './rollback-manager';\nimport { TrafficShifter } from './traffic-shifter';\nimport type { CanaryStage, DeploymentStrategy, TrafficSplit } from './types';\n\nexport interface DeploymentCoordinatorOptions {\n strategy: DeploymentStrategy;\n controller: CanaryController;\n trafficShifter: TrafficShifter;\n rollbackManager: RollbackManager;\n applyTrafficSplit: (\n stage: CanaryStage,\n split: TrafficSplit\n ) => void | Promise<void>;\n eventBus?: DeploymentEventBus;\n}\n\nexport interface DeploymentRunResult {\n status: 'completed' | 'rolled_back';\n failedStage?: CanaryStage;\n reasons?: string[];\n}\n\nexport class DeploymentCoordinator {\n constructor(private readonly options: DeploymentCoordinatorOptions) {}\n\n async run(): Promise<DeploymentRunResult> {\n const stages = this.options.controller.getStageList();\n\n for (const stage of stages) {\n const split = this.options.trafficShifter.computeSplit(stage);\n await this.options.applyTrafficSplit(stage, split);\n\n const analysis = await this.options.controller.runStage(stage);\n if (analysis.status === 'fail') {\n const action = await this.options.rollbackManager.execute(\n stage,\n analysis.reasons.join(', ')\n );\n\n this.options.eventBus?.emit({\n type: 'rolled_back',\n timestamp: action.triggeredAt,\n payload: { stage, reasons: analysis.reasons },\n });\n\n return {\n status: 'rolled_back',\n failedStage: stage,\n reasons: analysis.reasons,\n };\n }\n }\n\n if (this.options.strategy.mode === 'blue-green') {\n this.options.eventBus?.emit({\n type: 'blue_green_swapped',\n timestamp: new Date(),\n payload: { strategy: this.options.strategy },\n });\n }\n\n this.options.eventBus?.emit({\n type: 'completed',\n timestamp: new Date(),\n payload: { strategy: this.options.strategy },\n });\n\n return { status: 'completed' };\n }\n}\n"],"mappings":";;;AAwBA,IAAa,wBAAb,MAAmC;CACjC,YAAY,AAAiB,SAAuC;EAAvC;;CAE7B,MAAM,MAAoC;EACxC,MAAM,SAAS,KAAK,QAAQ,WAAW,cAAc;AAErD,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,QAAQ,KAAK,QAAQ,eAAe,aAAa,MAAM;AAC7D,SAAM,KAAK,QAAQ,kBAAkB,OAAO,MAAM;GAElD,MAAM,WAAW,MAAM,KAAK,QAAQ,WAAW,SAAS,MAAM;AAC9D,OAAI,SAAS,WAAW,QAAQ;IAC9B,MAAM,SAAS,MAAM,KAAK,QAAQ,gBAAgB,QAChD,OACA,SAAS,QAAQ,KAAK,KAAK,CAC5B;AAED,SAAK,QAAQ,UAAU,KAAK;KAC1B,MAAM;KACN,WAAW,OAAO;KAClB,SAAS;MAAE;MAAO,SAAS,SAAS;MAAS;KAC9C,CAAC;AAEF,WAAO;KACL,QAAQ;KACR,aAAa;KACb,SAAS,SAAS;KACnB;;;AAIL,MAAI,KAAK,QAAQ,SAAS,SAAS,aACjC,MAAK,QAAQ,UAAU,KAAK;GAC1B,MAAM;GACN,2BAAW,IAAI,MAAM;GACrB,SAAS,EAAE,UAAU,KAAK,QAAQ,UAAU;GAC7C,CAAC;AAGJ,OAAK,QAAQ,UAAU,KAAK;GAC1B,MAAM;GACN,2BAAW,IAAI,MAAM;GACrB,SAAS,EAAE,UAAU,KAAK,QAAQ,UAAU;GAC7C,CAAC;AAEF,SAAO,EAAE,QAAQ,aAAa"}
|
package/dist/events.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
//#region src/events.ts
|
|
2
|
-
var DeploymentEventBus = class {
|
|
3
|
-
listeners = /* @__PURE__ */ 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) try {
|
|
10
|
-
listener(event);
|
|
11
|
-
} catch (error) {
|
|
12
|
-
console.error("[progressive-delivery] listener error", error);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
|
-
export { DeploymentEventBus };
|
|
19
|
-
//# sourceMappingURL=events.js.map
|
package/dist/events.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","names":[],"sources":["../src/events.ts"],"sourcesContent":["import type { DeploymentEvent, DeploymentEventListener } from './types';\n\nexport class DeploymentEventBus {\n private listeners = new Set<DeploymentEventListener>();\n\n on(listener: DeploymentEventListener) {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n emit(event: DeploymentEvent) {\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (error) {\n console.error('[progressive-delivery] listener error', error);\n }\n }\n }\n}\n"],"mappings":";AAEA,IAAa,qBAAb,MAAgC;CAC9B,AAAQ,4BAAY,IAAI,KAA8B;CAEtD,GAAG,UAAmC;AACpC,OAAK,UAAU,IAAI,SAAS;AAC5B,eAAa,KAAK,UAAU,OAAO,SAAS;;CAG9C,KAAK,OAAwB;AAC3B,OAAK,MAAM,YAAY,KAAK,UAC1B,KAAI;AACF,YAAS,MAAM;WACR,OAAO;AACd,WAAQ,MAAM,yCAAyC,MAAM"}
|