@molecule/api-deploy-rollout-default 0.0.0-stage → 1.1.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/index.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Default deploy-rollout provider for molecule.dev.
3
+ *
4
+ * Implements the two real deployment topologies over any instance provider:
5
+ * grow-then-shrink (create the new set, gate it ready, join it, then retire the old
6
+ * one at a time) and in-place (change the same instances one at a time, with a
7
+ * reverse restore on failure). No I/O of its own, no clock other than an injected
8
+ * one, no knowledge of where the instances live.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { setProvider, runRollout } from '@molecule/api-deploy-rollout'
13
+ * import { provider } from '@molecule/api-deploy-rollout-default'
14
+ *
15
+ * setProvider(provider)
16
+ *
17
+ * const outcome = await runRollout({
18
+ * provider: myInstanceProvider,
19
+ * current: oldInstances,
20
+ * policy: { replicas: 2, soakMs: 30_000 },
21
+ * })
22
+ * ```
23
+ *
24
+ * @remarks
25
+ * - **Capacity floor first.** `policy.minHealthy` (default 1) is checked before
26
+ * every retirement; a step that would go below it aborts instead of running.
27
+ * - **Soak is continuous.** With `soakMs > 0` the instance must keep answering
28
+ * `isServing`; a gap restarts the window (a fresh process can pass one probe while
29
+ * its event loop is still warming).
30
+ * - **Rollback is reverse.** Instances this run changed are restored newest first; a
31
+ * restore failure does not stop the remaining restores.
32
+ * - **Resume is a no-op when complete.** A re-run reconciles against the provider's
33
+ * own `list()` where it has one, so an interrupted rollout does not create a
34
+ * duplicate set.
35
+ *
36
+ * @module
37
+ */
38
+ export * from './browser-guard.js';
39
+ export * from './provider.js';
@@ -0,0 +1,151 @@
1
+ /**
2
+ * The default deploy-rollout provider: the state machine itself.
3
+ *
4
+ * Provider-agnostic on purpose. The deploy path wires it to Fly Machines or to a
5
+ * sandbox provider; the tests wire it to a fake that records every call. What it
6
+ * guarantees is the part that is hard to get right:
7
+ *
8
+ * 1. **A new instance joins only after it is READY.** `waitReady` is the provider's
9
+ * readiness gate (`/health/ready` when the app has the lifecycle bond,
10
+ * `/health` otherwise — the caller decides).
11
+ * 2. **Healthy capacity never drops below the policy floor.** Before any instance
12
+ * leaves the routable set, the run counts what is still serving and refuses a
13
+ * step that would go below `minHealthy`.
14
+ * 3. **One at a time, with a soak.** The next step starts only after the previous
15
+ * instance passed its gate and kept answering for the soak window.
16
+ * 4. **A failure aborts and rolls back in reverse.** Grow-then-shrink destroys every
17
+ * instance it created and leaves the old set untouched (nothing was cut over);
18
+ * in-place restores every instance it changed, newest first.
19
+ * 5. **A retirement failure never strands the set.** The step is reported and the
20
+ * rollout continues; an unreachable instance is the orphan sweep's to reclaim.
21
+ *
22
+ * @module
23
+ */
24
+ import type { DeployRolloutProvider, InPlaceRolloutOptions, InPlaceRolloutOutcome, InstanceProvider, RolloutFailure, RolloutInstance, RolloutOptions, RolloutOutcome, RolloutPolicy, RolloutStep } from '@molecule/api-deploy-rollout';
25
+ /** The default bounded drain wait — matches the lifecycle bond's own budget. */
26
+ export declare const DEFAULT_ROLLOUT_DRAIN_MS = 25000;
27
+ /** The default readiness budget handed to a provider. */
28
+ export declare const DEFAULT_ROLLOUT_READINESS_TIMEOUT_MS = 120000;
29
+ /** The default cadence of the soak probe. */
30
+ export declare const DEFAULT_SOAK_PROBE_MS = 2000;
31
+ /** The default floor of healthy instances: never blank the deployment. */
32
+ export declare const DEFAULT_MIN_HEALTHY = 1;
33
+ /** A cancellation probe: `true` means stop. */
34
+ export type RolloutCancelProbe = () => Promise<boolean> | boolean;
35
+ /** The resolved policy the state machine runs with. */
36
+ export interface ResolvedRolloutPolicy {
37
+ /** Target size of the new set (grow-then-shrink only). */
38
+ replicas: number;
39
+ /** Floor of healthy instances. */
40
+ minHealthy: number;
41
+ /** Continuous healthy time required after a change. */
42
+ soakMs: number;
43
+ /** Probe cadence during the soak. */
44
+ soakProbeMs: number;
45
+ /** Give-up bound for the soak. */
46
+ soakGraceMs: number;
47
+ /** Bounded wait for a retiring instance. */
48
+ drainMs: number;
49
+ /** Readiness budget handed to the provider. */
50
+ readinessTimeoutMs: number;
51
+ /** What an abort does to instances already changed. */
52
+ rollback: 'reverse' | 'none';
53
+ /** Whether to reconcile against the provider's current instances. */
54
+ resume: boolean;
55
+ }
56
+ /**
57
+ * A rollout that could not finish, carrying what it did so the caller can report
58
+ * it (and, where the topology allows, undo the rest itself).
59
+ *
60
+ * The same class serves both topologies: grow-then-shrink fills `role`, `created`
61
+ * and `outcome`; in-place fills `failed`, `rolledBack` and `rollbackFailures`.
62
+ */
63
+ export declare class RolloutAbortedError extends Error {
64
+ /** The API layer's key for this abort. */
65
+ readonly errorKey = "deploy-rollout.error.aborted";
66
+ /** The step that failed. */
67
+ readonly role: RolloutStep | null;
68
+ /** The instance whose step failed, when one did. */
69
+ readonly failed: string | null;
70
+ /** Instances restored to the old build, newest first. */
71
+ readonly rolledBack: string[];
72
+ /** Restores that themselves failed. */
73
+ readonly rollbackFailures: RolloutFailure[];
74
+ /** The grow-then-shrink outcome, when the abort happened there. */
75
+ readonly outcome: RolloutOutcome | null;
76
+ /**
77
+ * Build the abort with what the caller must report.
78
+ *
79
+ * @param message - What happened, in plain words.
80
+ * @param details - The role, the failed instance, what was undone and the outcome.
81
+ * @param cause - The underlying error, when there was one.
82
+ */
83
+ constructor(message: string, details?: {
84
+ role?: RolloutStep | null;
85
+ failed?: string | null;
86
+ rolledBack?: string[];
87
+ rollbackFailures?: RolloutFailure[];
88
+ outcome?: RolloutOutcome | null;
89
+ }, cause?: unknown);
90
+ }
91
+ /**
92
+ * Fill the policy defaults.
93
+ *
94
+ * `maxUnavailable` derives `minHealthy` only when it is set; without it the floor
95
+ * is one healthy instance, which is exactly the rule every existing rolling path
96
+ * has today.
97
+ *
98
+ * @param policy - The caller's policy, if any.
99
+ * @param instanceCount - How many instances the topology has, for `maxUnavailable`.
100
+ * @returns The resolved policy.
101
+ */
102
+ export declare const resolveRolloutPolicy: (policy: RolloutPolicy | undefined, instanceCount: number) => ResolvedRolloutPolicy;
103
+ /**
104
+ * Run a grow-then-shrink replacement.
105
+ *
106
+ * The caller has already created, gated and joined the PRIMARY instance of the new
107
+ * version when `primaryServing` is set; `replicas` is the size of the new set
108
+ * including that one, and this function creates the remaining `replicas - 1`.
109
+ *
110
+ * @param options - The set to build, the old set, and the provider.
111
+ * @returns The new set, the retired instances and non-fatal failures.
112
+ * @throws {RolloutAbortedError} When an instance cannot be created, become ready or
113
+ * join — after undoing everything this run created and leaving the old set serving.
114
+ */
115
+ export declare const runGrowThenShrink: <I extends RolloutInstance>(options: RolloutOptions<I>) => Promise<RolloutOutcome<I>>;
116
+ /**
117
+ * Run an in-place replacement: the same instances changed one at a time.
118
+ *
119
+ * The instance list comes from `machines`, or from the provider's own `list()`.
120
+ * The operations come from `apply`/`rollback`/`probeServing`, or from the provider's
121
+ * `replace`/`restore`/`isServing`. Before an instance is taken out of rotation,
122
+ * every OTHER instance must answer its probe, and the resulting healthy count must
123
+ * stay at or above the policy floor.
124
+ *
125
+ * @param options - The instances to roll and the provider or callbacks.
126
+ * @returns The instances changed, in roll order.
127
+ * @throws {RolloutAbortedError} On the first failed step, after the reverse rollback.
128
+ */
129
+ export declare const runInPlace: <I extends RolloutInstance>(options: InPlaceRolloutOptions<I>) => Promise<InPlaceRolloutOutcome<I>>;
130
+ /**
131
+ * The provider implementation registered under `deploy-rollout`.
132
+ */
133
+ export declare const provider: DeployRolloutProvider;
134
+ /**
135
+ * Create a fresh provider object (the same algorithm; a distinct identity for
136
+ * tests and for apps that register more than one).
137
+ *
138
+ * @returns A deploy-rollout provider.
139
+ */
140
+ export declare const createProvider: () => DeployRolloutProvider;
141
+ /**
142
+ * One plain sentence about what a finished rollout did.
143
+ *
144
+ * @param outcome - What the rollout returned.
145
+ * @param replicas - The set size, for the sentence.
146
+ * @returns The line, or `null` when there was nothing to retire or the set is one.
147
+ */
148
+ export declare const describeRolloutResult: <I extends RolloutInstance>(outcome: Pick<RolloutOutcome<I>, "retired">, replicas: number) => string | null;
149
+ /** Re-exported so a caller can type its own instance provider without a second import. */
150
+ export type { InstanceProvider };
151
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}