@cardinali-studio/adminkit 2.2.8 → 2.2.9

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.
@@ -75,6 +75,7 @@ export declare class D1DeploymentAttemptRepository {
75
75
  fail(input: {
76
76
  attemptId: string;
77
77
  releaseId: string;
78
+ intentKind: DeploymentIntent;
78
79
  tokenChecksum: string;
79
80
  phase: DeploymentPhase;
80
81
  now: number;
@@ -142,12 +142,22 @@ export class D1DeploymentAttemptRepository {
142
142
  async fail(input) {
143
143
  if (isTerminalDeploymentPhase(input.phase))
144
144
  throw new Error("invalid_attempt_transition");
145
- const result = await this.db.prepare(`UPDATE production_deployment_attempts
146
- SET phase = 'failed', active_slot = NULL, error_code = ?, error_stage = ?, safe_message = ?, updated_at = ?, finished_at = ?
147
- WHERE attempt_id = ? AND release_id = ? AND phase = ? AND ci_claim_token_checksum = ? AND ci_claim_expires_at > ?`)
148
- .bind(input.code, input.stage, input.message, input.now, input.now, input.attemptId, input.releaseId, input.phase, input.tokenChecksum, input.now).run();
149
- if (result.meta?.changes !== 1)
145
+ const guard = `EXISTS (SELECT 1 FROM production_deployment_attempts WHERE attempt_id = ? AND release_id = ?
146
+ AND intent_kind = ? AND phase = ? AND ci_claim_token_checksum = ? AND ci_claim_expires_at > ?)`;
147
+ const results = await this.db.batch([
148
+ this.db.prepare(`UPDATE content_releases SET status = 'failed', active_slot = NULL, finished_at = ?
149
+ WHERE release_id = ? AND status = 'publishing' AND ? = 'content' AND ${guard}`)
150
+ .bind(input.now, input.releaseId, input.intentKind, input.attemptId, input.releaseId, input.intentKind, input.phase, input.tokenChecksum, input.now),
151
+ this.db.prepare(`UPDATE production_deployment_attempts
152
+ SET phase = 'failed', active_slot = NULL, error_code = ?, error_stage = ?, safe_message = ?, updated_at = ?, finished_at = ?
153
+ WHERE attempt_id = ? AND release_id = ? AND intent_kind = ? AND phase = ?
154
+ AND ci_claim_token_checksum = ? AND ci_claim_expires_at > ?`)
155
+ .bind(input.code, input.stage, input.message, input.now, input.now, input.attemptId, input.releaseId, input.intentKind, input.phase, input.tokenChecksum, input.now),
156
+ ]);
157
+ const expectedReleaseChanges = input.intentKind === "content" ? 1 : 0;
158
+ if (results[0]?.meta?.changes !== expectedReleaseChanges || results[1]?.meta?.changes !== 1) {
150
159
  throw new Error("attempt_unavailable");
160
+ }
151
161
  }
152
162
  /** Promotes the release and succeeds the exact verified attempt in one D1
153
163
  * batch. Every mutating statement repeats the capability guard, so a stale
@@ -130,7 +130,7 @@ export async function handleInternalContentCi({ request, env }) {
130
130
  if (path === "/internal/deployment/fail" && request.method === "POST") {
131
131
  const detail = failure[input.code];
132
132
  if (!detail) throw new Error("invalid_request");
133
- await attempts.fail({ ...identity, tokenChecksum: checksum, phase: attempt.phase, now, code: input.code, ...detail });
133
+ await attempts.fail({ ...identity, intentKind: attempt.intentKind, tokenChecksum: checksum, phase: attempt.phase, now, code: input.code, ...detail });
134
134
  return response({ ok: true, attempt: { ...identity, phase: "failed" } });
135
135
  }
136
136
  return safeError("invalid_request", "routing", 405);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardinali-studio/adminkit",
3
- "version": "2.2.8",
3
+ "version": "2.2.9",
4
4
  "type": "module",
5
5
  "description": "Motore riusabile di un pannello di amministrazione per siti Hugo su Cloudflare Pages: codec del front matter guidato dallo schema, form React, libreria media su R2. NIENTE di specifico di un sito qui dentro.",
6
6
  "license": "UNLICENSED",