rampway 0.2.6 → 0.3.1

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.
Files changed (85) hide show
  1. package/README.md +41 -0
  2. package/build/src/cli.d.ts.map +1 -1
  3. package/build/src/cli.js +35 -7
  4. package/build/src/cli.js.map +1 -1
  5. package/build/src/commands/task.d.ts +9 -0
  6. package/build/src/commands/task.d.ts.map +1 -0
  7. package/build/src/commands/task.js +23 -0
  8. package/build/src/commands/task.js.map +1 -0
  9. package/build/src/config/validate-config.d.ts +5 -0
  10. package/build/src/config/validate-config.d.ts.map +1 -1
  11. package/build/src/config/validate-config.js +139 -0
  12. package/build/src/config/validate-config.js.map +1 -1
  13. package/build/src/core/deploy-runner.js +5 -5
  14. package/build/src/core/deploy-runner.js.map +1 -1
  15. package/build/src/core/locks.d.ts +2 -1
  16. package/build/src/core/locks.d.ts.map +1 -1
  17. package/build/src/core/locks.js +28 -9
  18. package/build/src/core/locks.js.map +1 -1
  19. package/build/src/core/operational-task-runner.d.ts +21 -0
  20. package/build/src/core/operational-task-runner.d.ts.map +1 -0
  21. package/build/src/core/operational-task-runner.js +66 -0
  22. package/build/src/core/operational-task-runner.js.map +1 -0
  23. package/build/src/core/redaction.d.ts +31 -3
  24. package/build/src/core/redaction.d.ts.map +1 -1
  25. package/build/src/core/redaction.js +83 -5
  26. package/build/src/core/redaction.js.map +1 -1
  27. package/build/src/core/release-manager.d.ts +11 -0
  28. package/build/src/core/release-manager.d.ts.map +1 -1
  29. package/build/src/core/release-manager.js +22 -0
  30. package/build/src/core/release-manager.js.map +1 -1
  31. package/build/src/core/task-runner.d.ts +4 -2
  32. package/build/src/core/task-runner.d.ts.map +1 -1
  33. package/build/src/core/task-runner.js +34 -8
  34. package/build/src/core/task-runner.js.map +1 -1
  35. package/build/src/transports/local-transport.d.ts +19 -2
  36. package/build/src/transports/local-transport.d.ts.map +1 -1
  37. package/build/src/transports/local-transport.js +97 -9
  38. package/build/src/transports/local-transport.js.map +1 -1
  39. package/build/src/transports/ssh-transport.d.ts +43 -4
  40. package/build/src/transports/ssh-transport.d.ts.map +1 -1
  41. package/build/src/transports/ssh-transport.js +105 -12
  42. package/build/src/transports/ssh-transport.js.map +1 -1
  43. package/build/src/types.d.ts +20 -0
  44. package/build/src/types.d.ts.map +1 -1
  45. package/build/src/velocious/auth.d.ts +17 -0
  46. package/build/src/velocious/auth.d.ts.map +1 -0
  47. package/build/src/velocious/auth.js +30 -0
  48. package/build/src/velocious/auth.js.map +1 -0
  49. package/build/src/velocious/control-plane.d.ts +155 -0
  50. package/build/src/velocious/control-plane.d.ts.map +1 -0
  51. package/build/src/velocious/control-plane.js +275 -0
  52. package/build/src/velocious/control-plane.js.map +1 -0
  53. package/build/src/velocious/controller.d.ts +39 -0
  54. package/build/src/velocious/controller.d.ts.map +1 -0
  55. package/build/src/velocious/controller.js +86 -0
  56. package/build/src/velocious/controller.js.map +1 -0
  57. package/build/src/velocious/executor.d.ts +55 -0
  58. package/build/src/velocious/executor.d.ts.map +1 -0
  59. package/build/src/velocious/executor.js +142 -0
  60. package/build/src/velocious/executor.js.map +1 -0
  61. package/build/src/velocious/index.d.ts +28 -0
  62. package/build/src/velocious/index.d.ts.map +1 -0
  63. package/build/src/velocious/index.js +141 -0
  64. package/build/src/velocious/index.js.map +1 -0
  65. package/build/src/velocious/path-matcher.d.ts +18 -0
  66. package/build/src/velocious/path-matcher.d.ts.map +1 -0
  67. package/build/src/velocious/path-matcher.js +48 -0
  68. package/build/src/velocious/path-matcher.js.map +1 -0
  69. package/build/src/velocious/registry.d.ts +19 -0
  70. package/build/src/velocious/registry.d.ts.map +1 -0
  71. package/build/src/velocious/registry.js +36 -0
  72. package/build/src/velocious/registry.js.map +1 -0
  73. package/build/src/velocious/run-store.d.ts +297 -0
  74. package/build/src/velocious/run-store.d.ts.map +1 -0
  75. package/build/src/velocious/run-store.js +482 -0
  76. package/build/src/velocious/run-store.js.map +1 -0
  77. package/build/src/velocious/sanitize.d.ts +23 -0
  78. package/build/src/velocious/sanitize.d.ts.map +1 -0
  79. package/build/src/velocious/sanitize.js +154 -0
  80. package/build/src/velocious/sanitize.js.map +1 -0
  81. package/docs/config-reference.md +42 -0
  82. package/docs/docker-development-environment.md +103 -0
  83. package/docs/migration-from-capistrano.md +25 -3
  84. package/docs/velocious-deployment-api.md +189 -0
  85. package/package.json +22 -3
@@ -0,0 +1,30 @@
1
+ // @ts-check
2
+ import crypto from "node:crypto";
3
+ /**
4
+ * Compares two token strings without content-dependent timing once their
5
+ * byte lengths match.
6
+ *
7
+ * @param {string} left - Candidate token.
8
+ * @param {string} right - Configured token.
9
+ * @returns {boolean} Whether the values match.
10
+ */
11
+ export function constantTimeEqual(left, right) {
12
+ // Compare fixed-length digests so even differently sized candidates reach
13
+ // timingSafeEqual instead of taking a length-mismatch branch.
14
+ const leftDigest = crypto.createHash("sha256").update(String(left)).digest();
15
+ const rightDigest = crypto.createHash("sha256").update(String(right)).digest();
16
+ return crypto.timingSafeEqual(leftDigest, rightDigest);
17
+ }
18
+ /**
19
+ * Extracts a bearer token from an Authorization header.
20
+ *
21
+ * @param {unknown} authorization - Header value.
22
+ * @returns {string|null} Token or null.
23
+ */
24
+ export function bearerToken(authorization) {
25
+ if (typeof authorization !== "string")
26
+ return null;
27
+ const match = authorization.match(/^Bearer\s+([^\s].*)$/i);
28
+ return match ? match[1].trim() : null;
29
+ }
30
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/velocious/auth.js"],"names":[],"mappings":"AAAA,YAAY;AAEZ,OAAO,MAAM,MAAM,aAAa,CAAA;AAEhC;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAI,EAAE,KAAK;IAC3C,0EAA0E;IAC1E,8DAA8D;IAC9D,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;IAC5E,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;IAC9E,OAAO,MAAM,CAAC,eAAe,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,aAAa;IACvC,IAAI,OAAO,aAAa,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAElD,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAA;IAE1D,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACvC,CAAC"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * @typedef {object} DeploymentTarget
3
+ * @property {string} configPath - Backend-held Rampway config path.
4
+ * @property {string} releaseBranch - Approved release branch.
5
+ */
6
+ /**
7
+ * @typedef {object} DeploymentMountOptions
8
+ * @property {string[]} accessTokens - Accepted tokens.
9
+ * @property {Record<string, any>} projects - Normalized allowlist.
10
+ * @property {number} staleRunTimeoutMs - Lease timeout.
11
+ */
12
+ /**
13
+ * Looks up only explicit allowlist properties.
14
+ *
15
+ * @param {Record<string, any>} projects - Normalized project allowlist.
16
+ * @param {string} project - Project identifier.
17
+ * @param {string} stage - Stage identifier.
18
+ * @returns {DeploymentTarget|undefined} Target configuration.
19
+ */
20
+ export function lookupTarget(projects: Record<string, any>, project: string, stage: string): DeploymentTarget | undefined;
21
+ /**
22
+ * Framework-neutral deployment API behavior used by the Velocious controller.
23
+ *
24
+ * @template {import('./run-store.js').DeploymentRunStoreContract} TStore
25
+ */
26
+ export class DeploymentControlPlane<TStore extends import("./run-store.js").DeploymentRunStoreContract> {
27
+ /**
28
+ * @param {object} args - Dependencies.
29
+ * @param {(callback: () => Promise<void>) => Promise<void>} args.detachedExecution - Starts work in a fresh connection context.
30
+ * @param {(payload: {context: string, error: unknown}) => void} args.emitFrameworkError - Reports internal failures.
31
+ * @param {import('./executor.js').RampwayDeploymentExecutor} args.executor - Rampway lifecycle executor.
32
+ * @param {() => number} [args.now] - Clock.
33
+ * @param {DeploymentMountOptions} args.options - Validated mount options.
34
+ * @param {TStore} args.store - Durable run store.
35
+ */
36
+ constructor({ detachedExecution, emitFrameworkError, executor, now, options, store }: {
37
+ detachedExecution: (callback: () => Promise<void>) => Promise<void>;
38
+ emitFrameworkError: (payload: {
39
+ context: string;
40
+ error: unknown;
41
+ }) => void;
42
+ executor: import("./executor.js").RampwayDeploymentExecutor;
43
+ now?: (() => number) | undefined;
44
+ options: DeploymentMountOptions;
45
+ store: TStore;
46
+ });
47
+ detachedExecution: (callback: () => Promise<void>) => Promise<void>;
48
+ emitFrameworkError: (payload: {
49
+ context: string;
50
+ error: unknown;
51
+ }) => void;
52
+ executor: import("./executor.js").RampwayDeploymentExecutor;
53
+ now: () => number;
54
+ options: DeploymentMountOptions;
55
+ store: TStore;
56
+ /**
57
+ * @param {unknown} authorization - Authorization header.
58
+ * @returns {boolean} Whether request is authorized.
59
+ */
60
+ _authorized(authorization: unknown): boolean;
61
+ /**
62
+ * @param {string} candidate - Value to compare with configured access tokens.
63
+ * @returns {boolean} Whether the value is an accepted access token.
64
+ */
65
+ _matchesAccessToken(candidate: string): boolean;
66
+ /**
67
+ * @param {string} candidate - Idempotency key candidate.
68
+ * @returns {boolean} Whether the key contains a configured access token.
69
+ */
70
+ _containsAccessToken(candidate: string): boolean;
71
+ /**
72
+ * Creates a deployment run.
73
+ *
74
+ * @param {{authorization: unknown, input: unknown}} args - Request data.
75
+ * @returns {Promise<{body: Record<string, any>, status: number}>} HTTP response.
76
+ */
77
+ create({ authorization, input }: {
78
+ authorization: unknown;
79
+ input: unknown;
80
+ }): Promise<{
81
+ body: Record<string, any>;
82
+ status: number;
83
+ }>;
84
+ /**
85
+ * @param {{authorization: unknown, id: string}} args - Request data.
86
+ * @returns {Promise<{body: Record<string, any>, status: number}>} HTTP response.
87
+ */
88
+ show({ authorization, id }: {
89
+ authorization: unknown;
90
+ id: string;
91
+ }): Promise<{
92
+ body: Record<string, any>;
93
+ status: number;
94
+ }>;
95
+ /**
96
+ * @param {{existing: import('./run-store.js').DeploymentRunRow, project: string, revision: string, stage: string}} args - Replay data.
97
+ * @returns {{body: Record<string, any>, status: number}} Response.
98
+ */
99
+ _existingResponse({ existing, project, revision, stage }: {
100
+ existing: import("./run-store.js").DeploymentRunRow;
101
+ project: string;
102
+ revision: string;
103
+ stage: string;
104
+ }): {
105
+ body: Record<string, any>;
106
+ status: number;
107
+ };
108
+ /**
109
+ * @param {{run: import('./run-store.js').DeploymentRunRow, target: DeploymentTarget}} args - Execution data.
110
+ * @returns {Promise<void>} Resolves after durable outcome.
111
+ */
112
+ _executeRun({ run, target }: {
113
+ run: import("./run-store.js").DeploymentRunRow;
114
+ target: DeploymentTarget;
115
+ }): Promise<void>;
116
+ /**
117
+ * @param {{event: string, payload: Record<string, unknown>, runId: string|null}} args - Event.
118
+ * @returns {Promise<void>} Resolves after persistence or reporting.
119
+ */
120
+ _audit({ event, payload, runId }: {
121
+ event: string;
122
+ payload: Record<string, unknown>;
123
+ runId: string | null;
124
+ }): Promise<void>;
125
+ /**
126
+ * @param {import('./run-store.js').DeploymentRunRow} run - Run.
127
+ * @returns {Record<string, any>} Public representation.
128
+ */
129
+ _serializeRun(run: import("./run-store.js").DeploymentRunRow): Record<string, any>;
130
+ }
131
+ export type DeploymentTarget = {
132
+ /**
133
+ * - Backend-held Rampway config path.
134
+ */
135
+ configPath: string;
136
+ /**
137
+ * - Approved release branch.
138
+ */
139
+ releaseBranch: string;
140
+ };
141
+ export type DeploymentMountOptions = {
142
+ /**
143
+ * - Accepted tokens.
144
+ */
145
+ accessTokens: string[];
146
+ /**
147
+ * - Normalized allowlist.
148
+ */
149
+ projects: Record<string, any>;
150
+ /**
151
+ * - Lease timeout.
152
+ */
153
+ staleRunTimeoutMs: number;
154
+ };
155
+ //# sourceMappingURL=control-plane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../../../src/velocious/control-plane.js"],"names":[],"mappings":"AASA;;;;GAIG;AAEH;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,uCALW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WACnB,MAAM,SACN,MAAM,GACJ,gBAAgB,GAAC,SAAS,CAOtC;AAED;;;;GAIG;AACH,oCAFmE,MAAM,SAA5D,OAAQ,gBAAgB,EAAE,0BAA2B;IAGhE;;;;;;;;OAQG;IACH,sFAPG;QAA+D,iBAAiB,EAAxE,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC;QACW,kBAAkB,EAA7E,CAAC,OAAO,EAAE;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAC,KAAK,IAAI;QACI,QAAQ,EAAhE,OAAO,eAAe,EAAE,yBAAyB;QAC7B,GAAG,UAAjB,MAAM;QACiB,OAAO,EAApC,sBAAsB;QACT,KAAK,EAAlB,MAAM;KAChB,EAQA;IANC,8BARoB,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAQf;IAC1C,8BARmB;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAC,KAAK,IAAI,CAQjB;IAC5C,4DAAwB;IACxB,WARe,MAAM,CAQP;IACd,gCAAsB;IACtB,cAAkB;IAGpB;;;OAGG;IACH,2BAHW,OAAO,GACL,OAAO,CAKnB;IAED;;;OAGG;IACH,+BAHW,MAAM,GACJ,OAAO,CASnB;IAED;;;OAGG;IACH,gCAHW,MAAM,GACJ,OAAO,CAQnB;IAED;;;;;OAKG;IACH,iCAHW;QAAC,aAAa,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAC,GACtC,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC,CAiDhE;IAED;;;OAGG;IACH,4BAHW;QAAC,aAAa,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAC,GAClC,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC,CAyBhE;IAED;;;OAGG;IACH,0DAHW;QAAC,QAAQ,EAAE,OAAO,gBAAgB,EAAE,gBAAgB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GACrG;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAOvD;IAED;;;OAGG;IACH,6BAHW;QAAC,GAAG,EAAE,OAAO,gBAAgB,EAAE,gBAAgB,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAC,GACxE,OAAO,CAAC,IAAI,CAAC,CAsDzB;IAED;;;OAGG;IACH,kCAHW;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,GAAC,IAAI,CAAA;KAAC,GACnE,OAAO,CAAC,IAAI,CAAC,CAQzB;IAED;;;OAGG;IACH,mBAHW,OAAO,gBAAgB,EAAE,gBAAgB,GACvC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAgB/B;CACF;;;;;gBA5Qa,MAAM;;;;mBACN,MAAM;;;;;;kBAKN,MAAM,EAAE;;;;cACR,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;;uBACnB,MAAM"}
@@ -0,0 +1,275 @@
1
+ // @ts-check
2
+ import { bearerToken, constantTimeEqual } from "./auth.js";
3
+ import { heartbeatIntervalMs, registerActiveDeploymentRun, unregisterActiveDeploymentRun } from "./run-store.js";
4
+ import { sanitizeAdapterValue, sanitizeErrorPayload } from "./sanitize.js";
5
+ const REVISION_PATTERN = /^[0-9a-f]{40}$/;
6
+ const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
7
+ /**
8
+ * @typedef {object} DeploymentTarget
9
+ * @property {string} configPath - Backend-held Rampway config path.
10
+ * @property {string} releaseBranch - Approved release branch.
11
+ */
12
+ /**
13
+ * @typedef {object} DeploymentMountOptions
14
+ * @property {string[]} accessTokens - Accepted tokens.
15
+ * @property {Record<string, any>} projects - Normalized allowlist.
16
+ * @property {number} staleRunTimeoutMs - Lease timeout.
17
+ */
18
+ /**
19
+ * Looks up only explicit allowlist properties.
20
+ *
21
+ * @param {Record<string, any>} projects - Normalized project allowlist.
22
+ * @param {string} project - Project identifier.
23
+ * @param {string} stage - Stage identifier.
24
+ * @returns {DeploymentTarget|undefined} Target configuration.
25
+ */
26
+ export function lookupTarget(projects, project, stage) {
27
+ if (!Object.hasOwn(projects, project))
28
+ return undefined;
29
+ const stages = projects[project].stages;
30
+ if (!Object.hasOwn(stages, stage))
31
+ return undefined;
32
+ return stages[stage];
33
+ }
34
+ /**
35
+ * Framework-neutral deployment API behavior used by the Velocious controller.
36
+ *
37
+ * @template {import('./run-store.js').DeploymentRunStoreContract} TStore
38
+ */
39
+ export class DeploymentControlPlane {
40
+ /**
41
+ * @param {object} args - Dependencies.
42
+ * @param {(callback: () => Promise<void>) => Promise<void>} args.detachedExecution - Starts work in a fresh connection context.
43
+ * @param {(payload: {context: string, error: unknown}) => void} args.emitFrameworkError - Reports internal failures.
44
+ * @param {import('./executor.js').RampwayDeploymentExecutor} args.executor - Rampway lifecycle executor.
45
+ * @param {() => number} [args.now] - Clock.
46
+ * @param {DeploymentMountOptions} args.options - Validated mount options.
47
+ * @param {TStore} args.store - Durable run store.
48
+ */
49
+ constructor({ detachedExecution, emitFrameworkError, executor, now = Date.now, options, store }) {
50
+ this.detachedExecution = detachedExecution;
51
+ this.emitFrameworkError = emitFrameworkError;
52
+ this.executor = executor;
53
+ this.now = now;
54
+ this.options = options;
55
+ this.store = store;
56
+ }
57
+ /**
58
+ * @param {unknown} authorization - Authorization header.
59
+ * @returns {boolean} Whether request is authorized.
60
+ */
61
+ _authorized(authorization) {
62
+ const token = bearerToken(authorization);
63
+ return token ? this._matchesAccessToken(token) : false;
64
+ }
65
+ /**
66
+ * @param {string} candidate - Value to compare with configured access tokens.
67
+ * @returns {boolean} Whether the value is an accepted access token.
68
+ */
69
+ _matchesAccessToken(candidate) {
70
+ let matches = false;
71
+ for (const accessToken of this.options.accessTokens) {
72
+ // Do every configured comparison instead of exposing which rotation token matched.
73
+ matches = constantTimeEqual(candidate, accessToken) || matches;
74
+ }
75
+ return matches;
76
+ }
77
+ /**
78
+ * @param {string} candidate - Idempotency key candidate.
79
+ * @returns {boolean} Whether the key contains a configured access token.
80
+ */
81
+ _containsAccessToken(candidate) {
82
+ let contains = false;
83
+ for (const accessToken of this.options.accessTokens) {
84
+ contains = candidate.includes(accessToken) || contains;
85
+ }
86
+ return contains;
87
+ }
88
+ /**
89
+ * Creates a deployment run.
90
+ *
91
+ * @param {{authorization: unknown, input: unknown}} args - Request data.
92
+ * @returns {Promise<{body: Record<string, any>, status: number}>} HTTP response.
93
+ */
94
+ async create({ authorization, input }) {
95
+ if (!this._authorized(authorization))
96
+ return { body: { error: "unauthorized" }, status: 401 };
97
+ const params = input && typeof input === "object" ? /** @type {Record<string, unknown>} */ (input) : {};
98
+ const revision = typeof params.revision === "string" ? params.revision : null;
99
+ const idempotencyKey = typeof params.idempotencyKey === "string" ? params.idempotencyKey : null;
100
+ const invalidFields = [];
101
+ if (!revision || !REVISION_PATTERN.test(revision))
102
+ invalidFields.push("revision");
103
+ if (!idempotencyKey || idempotencyKey.length > MAX_IDEMPOTENCY_KEY_LENGTH || this._containsAccessToken(idempotencyKey)) {
104
+ invalidFields.push("idempotencyKey");
105
+ }
106
+ if (invalidFields.length > 0)
107
+ return { body: { error: "invalid_params", fields: invalidFields }, status: 422 };
108
+ const validRevision = /** @type {string} */ (revision);
109
+ const validIdempotencyKey = /** @type {string} */ (idempotencyKey);
110
+ const project = typeof params.project === "string" ? params.project : "";
111
+ const stage = typeof params.stage === "string" ? params.stage : "";
112
+ const target = lookupTarget(this.options.projects, project, stage);
113
+ if (!target)
114
+ return { body: { error: "not_found" }, status: 404 };
115
+ const outcome = await this.store.createRunIfPossible({ idempotencyKey: validIdempotencyKey, project, revision: validRevision, stage });
116
+ if (outcome.outcome === "replay" || outcome.outcome === "conflict") {
117
+ if (!outcome.run)
118
+ throw new Error(`Run store returned ${outcome.outcome} without a run`);
119
+ return this._existingResponse({ existing: outcome.run, project, revision: validRevision, stage });
120
+ }
121
+ if (outcome.outcome === "in_progress") {
122
+ return { body: { error: "deployment_in_progress", runId: outcome.run?.id }, status: 409 };
123
+ }
124
+ if (outcome.outcome === "reconciliation_required") {
125
+ if (!outcome.run)
126
+ throw new Error("Run store returned reconciliation_required without a run");
127
+ return { body: { error: "deployment_reconciliation_required", runId: outcome.run.id }, status: 409 };
128
+ }
129
+ if (!outcome.run)
130
+ throw new Error("Run store returned created without a run");
131
+ const run = outcome.run;
132
+ await this._audit({ event: "run_requested", payload: { project, revision: validRevision, stage }, runId: run.id });
133
+ await this.detachedExecution(async () => {
134
+ try {
135
+ await this._executeRun({ run, target });
136
+ }
137
+ catch (error) {
138
+ this.emitFrameworkError({ context: "rampway-deployment-api-execute-run", error });
139
+ }
140
+ });
141
+ return { body: { run: this._serializeRun(run) }, status: 202 };
142
+ }
143
+ /**
144
+ * @param {{authorization: unknown, id: string}} args - Request data.
145
+ * @returns {Promise<{body: Record<string, any>, status: number}>} HTTP response.
146
+ */
147
+ async show({ authorization, id }) {
148
+ if (!this._authorized(authorization))
149
+ return { body: { error: "unauthorized" }, status: 401 };
150
+ const run = await this.store.findRunById(id);
151
+ if (!run)
152
+ return { body: { error: "not_found" }, status: 404 };
153
+ const target = lookupTarget(this.options.projects, run.project, run.stage);
154
+ if (!target)
155
+ return { body: { error: "not_found" }, status: 404 };
156
+ /** @type {Record<string, any>} */
157
+ const body = { run: this._serializeRun(run) };
158
+ if (this.executor.readStatus) {
159
+ try {
160
+ const current = await this.executor.readStatus({ project: run.project, stage: run.stage, target });
161
+ body.current = sanitizeAdapterValue(current, this.options.accessTokens) ?? null;
162
+ }
163
+ catch (error) {
164
+ const liveStatusError = sanitizeErrorPayload(error, this.options.accessTokens);
165
+ body.live_status = null;
166
+ body.live_status_error = liveStatusError;
167
+ this.emitFrameworkError({ context: "rampway-deployment-api-live-status", error: liveStatusError });
168
+ }
169
+ }
170
+ return { body, status: 200 };
171
+ }
172
+ /**
173
+ * @param {{existing: import('./run-store.js').DeploymentRunRow, project: string, revision: string, stage: string}} args - Replay data.
174
+ * @returns {{body: Record<string, any>, status: number}} Response.
175
+ */
176
+ _existingResponse({ existing, project, revision, stage }) {
177
+ const same = existing.project === project && existing.stage === stage && existing.revision === revision;
178
+ return same
179
+ ? { body: { replayed: true, run: this._serializeRun(existing) }, status: 200 }
180
+ : { body: { error: "idempotency_conflict", runId: existing.id }, status: 409 };
181
+ }
182
+ /**
183
+ * @param {{run: import('./run-store.js').DeploymentRunRow, target: DeploymentTarget}} args - Execution data.
184
+ * @returns {Promise<void>} Resolves after durable outcome.
185
+ */
186
+ async _executeRun({ run, target }) {
187
+ if (!run.ownerToken)
188
+ throw new Error(`Deployment run ${run.id} has no owner token`);
189
+ const ownerToken = run.ownerToken;
190
+ registerActiveDeploymentRun(run.id);
191
+ /** @type {ReturnType<typeof setInterval>|null} */
192
+ let heartbeat = null;
193
+ try {
194
+ await this.store.markRunning({ id: run.id, startedAtMs: this.now() });
195
+ heartbeat = setInterval(() => {
196
+ this.store.heartbeat({ heartbeatAtMs: this.now(), id: run.id }).catch(error => {
197
+ this.emitFrameworkError({ context: "rampway-deployment-api-heartbeat", error });
198
+ });
199
+ }, heartbeatIntervalMs(this.options.staleRunTimeoutMs));
200
+ heartbeat.unref();
201
+ await this._audit({ event: "run_started", payload: { project: run.project, revision: run.revision, stage: run.stage }, runId: run.id });
202
+ let report;
203
+ try {
204
+ const reachable = await this.executor.validateRevision({ project: run.project, revision: run.revision, stage: run.stage, target });
205
+ if (!reachable)
206
+ throw new Error(`Revision ${run.revision} is not reachable from approved release branch ${target.releaseBranch}`);
207
+ report = await this.executor.deploy({ project: run.project, revision: run.revision, runId: run.id, stage: run.stage, target });
208
+ }
209
+ catch (error) {
210
+ const safeError = sanitizeErrorPayload(error, this.options.accessTokens);
211
+ try {
212
+ await this.store.markFailed({ error: safeError, finishedAtMs: this.now(), id: run.id, ownerToken });
213
+ await this._audit({ event: "run_failed", payload: { message: safeError.message, project: run.project, revision: run.revision, stage: run.stage }, runId: run.id });
214
+ }
215
+ catch (storeError) {
216
+ this.emitFrameworkError({ context: "rampway-deployment-api-record-failure", error: storeError });
217
+ }
218
+ return;
219
+ }
220
+ try {
221
+ const result = sanitizeAdapterValue(report ?? {}, this.options.accessTokens) ?? {};
222
+ await this.store.markSucceeded({ finishedAtMs: this.now(), id: run.id, ownerToken, result });
223
+ await this._audit({ event: "run_succeeded", payload: { project: run.project, revision: run.revision, stage: run.stage }, runId: run.id });
224
+ }
225
+ catch (error) {
226
+ this.emitFrameworkError({ context: "rampway-deployment-api-record-success", error });
227
+ const reconciliationError = { message: "Deployment activation succeeded, but its result could not be persisted; operator reconciliation is required" };
228
+ try {
229
+ await this.store.markReconciliationRequired({ error: reconciliationError, finishedAtMs: this.now(), id: run.id, ownerToken });
230
+ await this._audit({ event: "run_reconciliation_required", payload: { ...reconciliationError, project: run.project, revision: run.revision, stage: run.stage }, runId: run.id });
231
+ }
232
+ catch (persistenceError) {
233
+ this.emitFrameworkError({ context: "rampway-deployment-api-record-reconciliation-required", error: persistenceError });
234
+ }
235
+ }
236
+ }
237
+ finally {
238
+ if (heartbeat)
239
+ clearInterval(heartbeat);
240
+ unregisterActiveDeploymentRun(run.id);
241
+ }
242
+ }
243
+ /**
244
+ * @param {{event: string, payload: Record<string, unknown>, runId: string|null}} args - Event.
245
+ * @returns {Promise<void>} Resolves after persistence or reporting.
246
+ */
247
+ async _audit({ event, payload, runId }) {
248
+ try {
249
+ await this.store.addAuditEvent({ event, payload: sanitizeAdapterValue(payload, this.options.accessTokens) ?? {}, runId });
250
+ }
251
+ catch (error) {
252
+ this.emitFrameworkError({ context: "rampway-deployment-api-audit", error });
253
+ }
254
+ }
255
+ /**
256
+ * @param {import('./run-store.js').DeploymentRunRow} run - Run.
257
+ * @returns {Record<string, any>} Public representation.
258
+ */
259
+ _serializeRun(run) {
260
+ return {
261
+ error: run.error,
262
+ finishedAtMs: run.finishedAtMs,
263
+ id: run.id,
264
+ idempotencyKey: run.idempotencyKey,
265
+ project: run.project,
266
+ requestedAtMs: run.requestedAtMs,
267
+ result: run.result,
268
+ revision: run.revision,
269
+ stage: run.stage,
270
+ startedAtMs: run.startedAtMs,
271
+ status: run.status
272
+ };
273
+ }
274
+ }
275
+ //# sourceMappingURL=control-plane.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-plane.js","sourceRoot":"","sources":["../../../src/velocious/control-plane.js"],"names":[],"mappings":"AAAA,YAAY;AAEZ,OAAO,EAAC,WAAW,EAAE,iBAAiB,EAAC,MAAM,WAAW,CAAA;AACxD,OAAO,EAAC,mBAAmB,EAAE,2BAA2B,EAAE,6BAA6B,EAAC,MAAM,gBAAgB,CAAA;AAC9G,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAC,MAAM,eAAe,CAAA;AAExE,MAAM,gBAAgB,GAAG,gBAAgB,CAAA;AACzC,MAAM,0BAA0B,GAAG,GAAG,CAAA;AAEtC;;;;GAIG;AAEH;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK;IACnD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;QAAE,OAAO,SAAS,CAAA;IACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;IACvC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IACnD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,sBAAsB;IACjC;;;;;;;;OAQG;IACH,YAAY,EAAC,iBAAiB,EAAE,kBAAkB,EAAE,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAC;QAC3F,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAA;QAC1C,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAA;QAC5C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,aAAa;QACvB,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,CAAA;QACxC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IACxD,CAAC;IAED;;;OAGG;IACH,mBAAmB,CAAC,SAAS;QAC3B,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YACpD,mFAAmF;YACnF,OAAO,GAAG,iBAAiB,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,OAAO,CAAA;QAChE,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAED;;;OAGG;IACH,oBAAoB,CAAC,SAAS;QAC5B,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YACpD,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAA;QACxD,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,EAAC,aAAa,EAAE,KAAK,EAAC;QACjC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;YAAE,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,cAAc,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QAEzF,MAAM,MAAM,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACvG,MAAM,QAAQ,GAAG,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA;QAC7E,MAAM,cAAc,GAAG,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAA;QAC/F,MAAM,aAAa,GAAG,EAAE,CAAA;QAExB,IAAI,CAAC,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACjF,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,GAAG,0BAA0B,IAAI,IAAI,CAAC,oBAAoB,CAAC,cAAc,CAAC,EAAE,CAAC;YACvH,aAAa,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QAC1G,MAAM,aAAa,GAAG,qBAAqB,CAAC,CAAC,QAAQ,CAAC,CAAA;QACtD,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,CAAC,cAAc,CAAC,CAAA;QAElE,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QACxE,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAClE,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;QAClE,IAAI,CAAC,MAAM;YAAE,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,WAAW,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QAE7D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,EAAC,cAAc,EAAE,mBAAmB,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAC,CAAC,CAAA;QACpI,IAAI,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACnE,IAAI,CAAC,OAAO,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,OAAO,CAAC,OAAO,gBAAgB,CAAC,CAAA;YACxF,OAAO,IAAI,CAAC,iBAAiB,CAAC,EAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAC,CAAC,CAAA;QACjG,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,KAAK,aAAa,EAAE,CAAC;YACtC,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QACvF,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,KAAK,yBAAyB,EAAE,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;YAC7F,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,oCAAoC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QAClG,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QAE7E,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAA;QACvB,MAAM,IAAI,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,EAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAC,CAAC,CAAA;QAE9G,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,IAAI,EAAE;YACtC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,WAAW,CAAC,EAAC,GAAG,EAAE,MAAM,EAAC,CAAC,CAAA;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,kBAAkB,CAAC,EAAC,OAAO,EAAE,oCAAoC,EAAE,KAAK,EAAC,CAAC,CAAA;YACjF,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,OAAO,EAAC,IAAI,EAAE,EAAC,GAAG,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;IAC5D,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,EAAC,aAAa,EAAE,EAAE,EAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;YAAE,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,cAAc,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QACzF,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QAC5C,IAAI,CAAC,GAAG;YAAE,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,WAAW,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QAE1D,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;QAC1E,IAAI,CAAC,MAAM;YAAE,OAAO,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,WAAW,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;QAC7D,kCAAkC;QAClC,MAAM,IAAI,GAAG,EAAC,GAAG,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAC,CAAA;QAE3C,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAC,CAAC,CAAA;gBAChG,IAAI,CAAC,OAAO,GAAG,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,IAAI,CAAA;YACjF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,eAAe,GAAG,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;gBAC9E,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;gBACvB,IAAI,CAAC,iBAAiB,GAAG,eAAe,CAAA;gBACxC,IAAI,CAAC,kBAAkB,CAAC,EAAC,OAAO,EAAE,oCAAoC,EAAE,KAAK,EAAE,eAAe,EAAC,CAAC,CAAA;YAClG,CAAC;QACH,CAAC;QAED,OAAO,EAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;IAC5B,CAAC;IAED;;;OAGG;IACH,iBAAiB,CAAC,EAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAC;QACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,KAAK,OAAO,IAAI,QAAQ,CAAC,KAAK,KAAK,KAAK,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAA;QACvG,OAAO,IAAI;YACT,CAAC,CAAC,EAAC,IAAI,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC;YAC1E,CAAC,CAAC,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAC,EAAE,MAAM,EAAE,GAAG,EAAC,CAAA;IAC9E,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,EAAC,GAAG,EAAE,MAAM,EAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,EAAE,qBAAqB,CAAC,CAAA;QACnF,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAA;QACjC,2BAA2B,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACnC,kDAAkD;QAClD,IAAI,SAAS,GAAG,IAAI,CAAA;QAEpB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAC,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,EAAC,CAAC,CAAA;YACnE,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;gBAC3B,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAC,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;oBAC1E,IAAI,CAAC,kBAAkB,CAAC,EAAC,OAAO,EAAE,kCAAkC,EAAE,KAAK,EAAC,CAAC,CAAA;gBAC/E,CAAC,CAAC,CAAA;YACJ,CAAC,EAAE,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAA;YACvD,SAAS,CAAC,KAAK,EAAE,CAAA;YAEjB,MAAM,IAAI,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,EAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAC,CAAC,CAAA;YAEnI,IAAI,MAAM,CAAA;YACV,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAC,CAAC,CAAA;gBAChI,IAAI,CAAC,SAAS;oBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,QAAQ,kDAAkD,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;gBACjI,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAC,CAAC,CAAA;YAC9H,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,SAAS,GAAG,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;gBACxE,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,UAAU,EAAC,CAAC,CAAA;oBACjG,MAAM,IAAI,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,EAAC,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAC,CAAC,CAAA;gBAChK,CAAC;gBAAC,OAAO,UAAU,EAAE,CAAC;oBACpB,IAAI,CAAC,kBAAkB,CAAC,EAAC,OAAO,EAAE,uCAAuC,EAAE,KAAK,EAAE,UAAU,EAAC,CAAC,CAAA;gBAChG,CAAC;gBACD,OAAM;YACR,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;gBAClF,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,EAAC,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAC,CAAC,CAAA;gBAC1F,MAAM,IAAI,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,EAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAC,CAAC,CAAA;YACvI,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,kBAAkB,CAAC,EAAC,OAAO,EAAE,uCAAuC,EAAE,KAAK,EAAC,CAAC,CAAA;gBAClF,MAAM,mBAAmB,GAAG,EAAC,OAAO,EAAE,6GAA6G,EAAC,CAAA;gBACpJ,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,EAAC,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,UAAU,EAAC,CAAC,CAAA;oBAC3H,MAAM,IAAI,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,6BAA6B,EAAE,OAAO,EAAE,EAAC,GAAG,mBAAmB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAC,CAAC,CAAA;gBAC7K,CAAC;gBAAC,OAAO,gBAAgB,EAAE,CAAC;oBAC1B,IAAI,CAAC,kBAAkB,CAAC,EAAC,OAAO,EAAE,uDAAuD,EAAE,KAAK,EAAE,gBAAgB,EAAC,CAAC,CAAA;gBACtH,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,SAAS;gBAAE,aAAa,CAAC,SAAS,CAAC,CAAA;YACvC,6BAA6B,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAC;QAClC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,EAAC,KAAK,EAAE,OAAO,EAAE,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,KAAK,EAAC,CAAC,CAAA;QACzH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,kBAAkB,CAAC,EAAC,OAAO,EAAE,8BAA8B,EAAE,KAAK,EAAC,CAAC,CAAA;QAC3E,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,GAAG;QACf,OAAO;YACL,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,aAAa,EAAE,GAAG,CAAC,aAAa;YAChC,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAA;IACH,CAAC;CACF"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Creates a scheduler that breaks away from the closing request scope and
3
+ * establishes a new Velocious connection context for background execution.
4
+ *
5
+ * @param {object} args - Scheduler dependencies.
6
+ * @param {any} args.configuration - Velocious configuration.
7
+ * @param {(payload: {context: string, error: unknown}) => void} args.emitFrameworkError - Failure reporter.
8
+ * @returns {(callback: () => Promise<void>) => Promise<void>} Detached scheduler.
9
+ */
10
+ export function createDetachedExecution({ configuration, emitFrameworkError }: {
11
+ configuration: any;
12
+ emitFrameworkError: (payload: {
13
+ context: string;
14
+ error: unknown;
15
+ }) => void;
16
+ }): (callback: () => Promise<void>) => Promise<void>;
17
+ /** Velocious HTTP adapter for Rampway's deployment control plane. */
18
+ export default class RampwayDeploymentApiController extends Controller {
19
+ /** @returns {Record<string, any>} Mount options. */
20
+ _mountOptions(): Record<string, any>;
21
+ /** @returns {DeploymentRunStore} Store. */
22
+ _store(): DeploymentRunStore;
23
+ deploymentRunStore: DeploymentRunStore | undefined;
24
+ /** @returns {DeploymentControlPlane<DeploymentRunStore>} Control plane. */
25
+ _controlPlane(): DeploymentControlPlane<DeploymentRunStore>;
26
+ /** @param {{context: string, error: unknown}} args - Failure. @returns {void} */
27
+ _emitFrameworkError({ context, error }: {
28
+ context: string;
29
+ error: unknown;
30
+ }): void;
31
+ /** @returns {Promise<void>} */
32
+ create(): Promise<void>;
33
+ /** @returns {Promise<void>} */
34
+ show(): Promise<void>;
35
+ }
36
+ import Controller from "velocious/build/src/controller.js";
37
+ import DeploymentRunStore from "./run-store.js";
38
+ import { DeploymentControlPlane } from "./control-plane.js";
39
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/velocious/controller.js"],"names":[],"mappings":"AAQA;;;;;;;;GAQG;AACH,+EAJG;IAAkB,aAAa,EAAvB,GAAG;IACwD,kBAAkB,EAA7E,CAAC,OAAO,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAC,KAAK,IAAI;CAC5D,GAAU,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAU5D;AAED,qEAAqE;AACrE;IACE,oDAAoD;IACpD,iBADc,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAMhC;IAED,2CAA2C;IAC3C,UADc,kBAAkB,CAY/B;IARG,mDAKE;IAKN,2EAA2E;IAC3E,iBADc,sBAAsB,CAAC,kBAAkB,CAAC,CAYvD;IAED,iFAAiF;IACjF,wCADY;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAC,GAA4B,IAAI,CAQ5E;IAED,+BAA+B;IAC/B,UADc,OAAO,CAAC,IAAI,CAAC,CAO1B;IAED,+BAA+B;IAC/B,QADc,OAAO,CAAC,IAAI,CAAC,CAO1B;CACF;uBA1FsB,mCAAmC;+BAG3B,gBAAgB;uCADV,oBAAoB"}
@@ -0,0 +1,86 @@
1
+ // @ts-check
2
+ import Controller from "velocious/build/src/controller.js";
3
+ import { DeploymentControlPlane } from "./control-plane.js";
4
+ import DeploymentRunStore from "./run-store.js";
5
+ import { getDeploymentMount } from "./registry.js";
6
+ /**
7
+ * Creates a scheduler that breaks away from the closing request scope and
8
+ * establishes a new Velocious connection context for background execution.
9
+ *
10
+ * @param {object} args - Scheduler dependencies.
11
+ * @param {any} args.configuration - Velocious configuration.
12
+ * @param {(payload: {context: string, error: unknown}) => void} args.emitFrameworkError - Failure reporter.
13
+ * @returns {(callback: () => Promise<void>) => Promise<void>} Detached scheduler.
14
+ */
15
+ export function createDetachedExecution({ configuration, emitFrameworkError }) {
16
+ return async (callback) => {
17
+ globalThis.setImmediate(() => {
18
+ configuration.withConnections({
19
+ name: "Rampway detached deployment"
20
+ }, callback).catch((/** @type {unknown} */ error) => emitFrameworkError({ context: "rampway-deployment-api-detached-context", error }));
21
+ });
22
+ };
23
+ }
24
+ /** Velocious HTTP adapter for Rampway's deployment control plane. */
25
+ export default class RampwayDeploymentApiController extends Controller {
26
+ /** @returns {Record<string, any>} Mount options. */
27
+ _mountOptions() {
28
+ const at = /** @type {string} */ (this.params().rampwayDeploymentMountAt);
29
+ const options = getDeploymentMount(this.getConfiguration(), at);
30
+ if (!options)
31
+ throw new Error(`No Rampway deployment API mount registered at ${at}`);
32
+ return options;
33
+ }
34
+ /** @returns {DeploymentRunStore} Store. */
35
+ _store() {
36
+ if (!this.deploymentRunStore) {
37
+ const options = this._mountOptions();
38
+ this.deploymentRunStore = new DeploymentRunStore({
39
+ configuration: this.getConfiguration(),
40
+ databaseIdentifier: options.databaseIdentifier,
41
+ mountIdentifier: options.mountIdentifier,
42
+ staleRunTimeoutMs: options.staleRunTimeoutMs
43
+ });
44
+ }
45
+ return this.deploymentRunStore;
46
+ }
47
+ /** @returns {DeploymentControlPlane<DeploymentRunStore>} Control plane. */
48
+ _controlPlane() {
49
+ const options = /** @type {import('./control-plane.js').DeploymentMountOptions & {databaseIdentifier: string, executor: import('./executor.js').RampwayDeploymentExecutor}} */ (this._mountOptions());
50
+ const configuration = this.getConfiguration();
51
+ const emitFrameworkError = (/** @type {{context: string, error: unknown}} */ { context, error }) => this._emitFrameworkError({ context, error });
52
+ return new DeploymentControlPlane({
53
+ detachedExecution: createDetachedExecution({ configuration, emitFrameworkError }),
54
+ emitFrameworkError,
55
+ executor: options.executor,
56
+ options,
57
+ store: this._store()
58
+ });
59
+ }
60
+ /** @param {{context: string, error: unknown}} args - Failure. @returns {void} */
61
+ _emitFrameworkError({ context, error }) {
62
+ const events = this.getConfiguration().getErrorEvents();
63
+ // Never attach the request: it retains the bearer header and would make
64
+ // unsafe framework reporters capable of serializing the credential.
65
+ const payload = { context, error };
66
+ events.emit("framework-error", payload);
67
+ events.emit("all-error", { ...payload, errorType: "framework-error" });
68
+ }
69
+ /** @returns {Promise<void>} */
70
+ async create() {
71
+ const response = await this._controlPlane().create({
72
+ authorization: this.request().header("authorization"),
73
+ input: this.params()
74
+ });
75
+ await this.render({ json: response.body, status: response.status });
76
+ }
77
+ /** @returns {Promise<void>} */
78
+ async show() {
79
+ const response = await this._controlPlane().show({
80
+ authorization: this.request().header("authorization"),
81
+ id: /** @type {string} */ (this.params().id)
82
+ });
83
+ await this.render({ json: response.body, status: response.status });
84
+ }
85
+ }
86
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/velocious/controller.js"],"names":[],"mappings":"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"}