@voltro/plugin-releases 0.83.1 → 0.85.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 CHANGED
@@ -1,12 +1,13 @@
1
1
  import { ReleaseNotFound as e, ReleaseRefused as t } from "./errors.js";
2
2
  import { a as n, i as r, n as i, o as a, r as o, s, t as c } from "./rollout-CGRKMTfv.js";
3
- import { _ as l, a as u, c as d, d as ee, f as te, g as f, h as p, l as ne, m as re, o as ie, p as ae, r as m, s as oe, t as h, u as se, v as g, y as _ } from "./rpc-U7ytx6SC.js";
4
- import { Effect as v } from "effect";
3
+ import { _ as l, a as u, c as ee, d as te, f as ne, g as d, h as f, l as re, m as ie, o as ae, p as oe, r as p, s as se, t as m, u as ce, v as h, y as g } from "./rpc-DrfZqJIA.js";
4
+ import { Effect as _, Result as v, Schema as le } from "effect";
5
5
  import { randomBytes as y } from "node:crypto";
6
- import { definePlugin as ce } from "@voltro/protocol";
7
- import { boolean as b, eq as x, id as S, integer as C, json as w, queryFor as T, registerRetention as le, retentionTtlMsFromEnv as ue, table as E, text as D, timestamp as O } from "@voltro/database";
8
- import { newTraceId as de } from "@voltro/runtime";
9
- var fe = (e, t, n) => {
6
+ import { AsyncLocalStorage as b } from "node:async_hooks";
7
+ import { definePlugin as ue } from "@voltro/protocol";
8
+ import { and as de, boolean as x, eq as S, id as C, inSet as fe, integer as w, json as T, queryFor as E, registerRetention as pe, retentionTtlMsFromEnv as me, table as D, text as O, timestamp as k } from "@voltro/database";
9
+ import { newTraceId as he } from "@voltro/runtime";
10
+ var ge = (e, t, n) => {
10
11
  let r = /* @__PURE__ */ new Set();
11
12
  for (let e of t) e.currentReleaseId !== null && r.add(e.currentReleaseId);
12
13
  let i = [];
@@ -15,92 +16,93 @@ var fe = (e, t, n) => {
15
16
  for (let e of s.slice(o)) r.has(e.id) || i.push(e);
16
17
  }
17
18
  return i;
18
- }, pe = [
19
+ }, A = [
19
20
  "web",
20
21
  "ios",
21
22
  "android",
22
23
  "macos",
23
24
  "windows",
24
25
  "linux"
25
- ], me = [
26
+ ], j = [
26
27
  "pwa",
27
28
  "expo",
28
29
  "tauri",
29
30
  "electron"
30
- ], k = E(f, {
31
- id: S({ prefix: "relapp" }),
32
- app: D(),
33
- platform: D(),
34
- shell: D(),
35
- bundleId: D().nullable(),
36
- updaterPublicKey: D().nullable(),
37
- runtimeVersion: D().nullable(),
38
- createdAt: O().default("now")
31
+ ], M = D(d, {
32
+ id: C({ prefix: "relapp" }),
33
+ app: O(),
34
+ platform: O(),
35
+ shell: O(),
36
+ revision: w().default(0),
37
+ bundleId: O().nullable(),
38
+ updaterPublicKey: O().nullable(),
39
+ runtimeVersion: O().nullable(),
40
+ createdAt: k().default("now")
39
41
  }).index("byReleaseAppTarget", [
40
42
  "app",
41
43
  "platform",
42
44
  "shell"
43
- ]), A = E(g, {
44
- id: S({ prefix: "relch" }),
45
- appId: D(),
46
- name: D(),
47
- currentReleaseId: D().nullable(),
48
- minSupportedVersion: D().nullable(),
49
- killSwitches: w().nullable(),
50
- public: b().default(!1),
51
- allowUnsigned: b().default(!1),
52
- updatedAt: O().default("now").onUpdate("now")
53
- }).index("byReleaseChannelApp", ["appId", "name"]), j = E(p, {
54
- id: S({ prefix: "rel" }),
55
- appId: D(),
56
- channelId: D(),
57
- version: D(),
58
- buildId: D().nullable(),
59
- notes: D().nullable(),
60
- state: D().default("draft"),
61
- rolloutPercent: C().default(0),
62
- rolloutSalt: D(),
63
- cohorts: w().nullable(),
64
- createdBy: D().nullable(),
65
- createdAt: O().default("now"),
66
- updatedAt: O().default("now").onUpdate("now")
67
- }).index("byReleaseAppChannel", ["appId", "channelId"]).index("byReleaseVersion", ["appId", "version"]), M = E(l, {
68
- id: S({ prefix: "relart" }),
69
- releaseId: D(),
70
- kind: D(),
71
- target: D().nullable(),
72
- url: D(),
73
- sha256: D(),
74
- size: C(),
75
- signature: D().nullable(),
76
- notarized: b().default(!1),
77
- assetKey: D().nullable(),
78
- contentType: D().nullable(),
79
- createdAt: O().default("now")
80
- }).index("byReleaseArtifactRelease", ["releaseId"]), N = E(_, {
81
- id: S({ prefix: "relev" }),
82
- appId: D(),
83
- channelId: D().nullable(),
84
- releaseId: D().nullable(),
85
- kind: D(),
86
- reason: D().nullable(),
87
- actorId: D().nullable(),
88
- detail: w().nullable(),
89
- at: O().default("now")
90
- }).index("byReleaseEventApp", ["appId", "at"]), P = [
91
- k,
92
- A,
93
- j,
45
+ ]), N = D(h, {
46
+ id: C({ prefix: "relch" }),
47
+ appId: O(),
48
+ name: O(),
49
+ currentReleaseId: O().nullable(),
50
+ minSupportedVersion: O().nullable(),
51
+ killSwitches: T().nullable(),
52
+ public: x().default(!1),
53
+ allowUnsigned: x().default(!1),
54
+ updatedAt: k().default("now").onUpdate("now")
55
+ }).index("byReleaseChannelApp", ["appId", "name"]), P = D(f, {
56
+ id: C({ prefix: "rel" }),
57
+ appId: O(),
58
+ channelId: O(),
59
+ version: O(),
60
+ buildId: O().nullable(),
61
+ notes: O().nullable(),
62
+ state: O().default("draft"),
63
+ rolloutPercent: w().default(0),
64
+ rolloutSalt: O(),
65
+ cohorts: T().nullable(),
66
+ createdBy: O().nullable(),
67
+ createdAt: k().default("now"),
68
+ updatedAt: k().default("now").onUpdate("now")
69
+ }).index("byReleaseAppChannel", ["appId", "channelId"]).index("byReleaseVersion", ["appId", "version"]), F = D(l, {
70
+ id: C({ prefix: "relart" }),
71
+ releaseId: O(),
72
+ kind: O(),
73
+ target: O().nullable(),
74
+ url: O(),
75
+ sha256: O(),
76
+ size: w(),
77
+ signature: O().nullable(),
78
+ notarized: x().default(!1),
79
+ assetKey: O().nullable(),
80
+ contentType: O().nullable(),
81
+ createdAt: k().default("now")
82
+ }).index("byReleaseArtifactRelease", ["releaseId"]), I = D(g, {
83
+ id: C({ prefix: "relev" }),
84
+ appId: O(),
85
+ channelId: O().nullable(),
86
+ releaseId: O().nullable(),
87
+ kind: O(),
88
+ reason: O().nullable(),
89
+ actorId: O().nullable(),
90
+ detail: T().nullable(),
91
+ at: k().default("now")
92
+ }).index("byReleaseEventApp", ["appId", "at"]), L = [
94
93
  M,
95
- N
96
- ], F = (e, t) => t.currentVersion !== null && c(e.version, t.currentVersion) <= 0 ? !1 : o({
94
+ N,
95
+ P,
96
+ F,
97
+ I
98
+ ], R = (e, t) => t.currentVersion !== null && c(e.version, t.currentVersion) <= 0 ? !1 : o({
97
99
  installId: t.installId,
98
100
  salt: e.rolloutSalt,
99
101
  percent: e.rolloutPercent,
100
102
  ...t.cohorts === void 0 ? {} : { installCohorts: t.cohorts },
101
103
  releaseCohorts: e.cohorts
102
- }), I = (e, t) => {
103
- if (!F(e, t)) return null;
104
+ }), z = (e, t) => {
105
+ if (!R(e, t)) return null;
104
106
  let n = e.artifacts.find((e) => e.target === t.target && e.signature !== null);
105
107
  return !n || n.signature === null ? null : {
106
108
  version: e.version,
@@ -111,7 +113,7 @@ var fe = (e, t, n) => {
111
113
  url: n.url
112
114
  } }
113
115
  };
114
- }, L = (e, t, n = {}) => {
116
+ }, B = (e, t, n = {}) => {
115
117
  let r = {
116
118
  win: [
117
119
  "nsis",
@@ -136,14 +138,14 @@ var fe = (e, t, n) => {
136
138
  a.push(`path: ${o.url}`);
137
139
  let s = n[o.sha256];
138
140
  return a.push(s === void 0 ? `sha256: ${o.sha256}` : `sha512: ${s}`), a.push(`releaseDate: '${e.createdAt.toISOString()}'`), e.notes && a.push(`releaseNotes: ${JSON.stringify(e.notes)}`), a.join("\n") + "\n";
139
- }, R = (e, t, n) => {
141
+ }, _e = (e, t, n) => {
140
142
  if (n.rolledBackTo !== null) return {
141
143
  kind: "rollback",
142
144
  commitTime: n.rolledBackTo.commitTime.toISOString()
143
145
  };
144
146
  if (e === null || n.runtimeVersion !== null && n.runtimeVersion !== t.runtimeVersion) return { kind: "no-update" };
145
147
  let r = e.buildId ?? e.id;
146
- if (t.currentUpdateId === r || !F(e, {
148
+ if (t.currentUpdateId === r || !R(e, {
147
149
  installId: t.installId,
148
150
  currentVersion: null,
149
151
  ...t.cohorts === void 0 ? {} : { cohorts: t.cohorts }
@@ -176,7 +178,7 @@ var fe = (e, t, n) => {
176
178
  }
177
179
  }
178
180
  };
179
- }, z = {
181
+ }, ve = {
180
182
  macos: ["dmg", "zip"],
181
183
  windows: [
182
184
  "nsis",
@@ -190,21 +192,21 @@ var fe = (e, t, n) => {
190
192
  ],
191
193
  ios: ["ipa"],
192
194
  android: ["apk", "aab"]
193
- }, B = (e, t, n) => {
194
- let r = z[t] ?? [], i = e.artifacts.filter((e) => r.includes(e.kind));
195
+ }, V = (e, t, n) => {
196
+ let r = ve[t] ?? [], i = e.artifacts.filter((e) => r.includes(e.kind));
195
197
  if (i.length === 0) return null;
196
198
  if (n !== void 0) {
197
199
  let e = i.find((e) => e.target !== null && e.target.endsWith(n));
198
200
  if (e) return e;
199
201
  }
200
202
  return i[0];
201
- }, V = (e) => /Windows/i.test(e) ? {
203
+ }, H = (e) => /Windows/i.test(e) ? {
202
204
  os: "windows",
203
205
  arch: /ARM64|aarch64/i.test(e) ? "aarch64" : "x86_64"
204
206
  } : /Macintosh|Mac OS X/i.test(e) ? { os: "macos" } : /Android/i.test(e) ? { os: "android" } : /iPhone|iPad|iPod/i.test(e) ? { os: "ios" } : /Linux/i.test(e) ? {
205
207
  os: "linux",
206
208
  arch: /aarch64|arm64/i.test(e) ? "aarch64" : "x86_64"
207
- } : null, H = (e, t, n = {}) => ({
209
+ } : null, U = (e, t, n = {}) => ({
208
210
  status: e,
209
211
  body: JSON.stringify(t),
210
212
  contentType: "application/json; charset=utf-8",
@@ -212,38 +214,41 @@ var fe = (e, t, n) => {
212
214
  "cache-control": "no-store",
213
215
  ...n
214
216
  }
215
- }), he = (e, t, n) => ({
217
+ }), ye = (e, t, n) => ({
216
218
  status: e,
217
219
  body: t,
218
220
  contentType: n,
219
221
  headers: { "cache-control": "no-store" }
220
- }), U = (e) => ({
222
+ }), W = (e) => ({
221
223
  status: e,
222
224
  body: "",
223
225
  headers: { "cache-control": "no-store" }
224
- }), W = (e) => `${e}_${y(9).toString("base64url")}`, G = (n) => {
225
- let r = (n.prefix ?? "/releases").replace(/\/+$/, ""), a = n.now ?? Date.now, o, s = () => {
226
+ }), G = (e) => `${e}_${y(9).toString("base64url")}`, be = (n) => {
227
+ let r = (n.prefix ?? "/releases").replace(/\/+$/, ""), a = n.now ?? Date.now, o, s = new b(), p = new b(), y = () => {
228
+ let e = s.getStore();
229
+ if (e) return e;
226
230
  if (!o) throw new t({ reason: "releases: the app store is not bound yet" });
227
231
  return o;
228
- }, m = null, y = async (e) => {
229
- let t = (await s().query(T(k).where(x("app", e.app)).descriptor)).find((t) => t.platform === e.platform && t.shell === e.shell), n = !t;
230
- t ||= await s().insert(f, {
231
- id: W("relapp"),
232
+ }, x = null, C = async (e) => {
233
+ let t = (await y().query(E(M).where(S("app", e.app)).descriptor)).find((t) => t.platform === e.platform && t.shell === e.shell), n = !t;
234
+ t ||= await y().insert(d, {
235
+ id: G("relapp"),
232
236
  app: e.app,
233
237
  platform: e.platform,
234
238
  shell: e.shell,
239
+ revision: 0,
235
240
  bundleId: e.bundleId ?? null,
236
241
  updaterPublicKey: e.updaterPublicKey ?? null,
237
242
  runtimeVersion: e.runtimeVersion ?? null,
238
243
  createdAt: new Date(a())
239
244
  });
240
- let r = await s().query(T(A).where(x("appId", t.id)).descriptor);
245
+ let r = await y().query(E(N).where(S("appId", t.id)).descriptor);
241
246
  for (let n of e.channels ?? [
242
247
  "stable",
243
248
  "beta",
244
249
  "internal"
245
- ]) r.some((e) => e.name === n) || await s().insert(g, {
246
- id: W("relch"),
250
+ ]) r.some((e) => e.name === n) || await y().insert(h, {
251
+ id: G("relch"),
247
252
  appId: t.id,
248
253
  name: n,
249
254
  currentReleaseId: null,
@@ -257,19 +262,19 @@ var fe = (e, t, n) => {
257
262
  row: t,
258
263
  created: n
259
264
  };
260
- }, b = async () => {
261
- for (let e of n.apps) await y(e);
262
- }, S = () => m ?? Promise.resolve(), C = (t, n, r) => v.tryPromise({
265
+ }, w = async () => {
266
+ for (let e of n.apps) await C(e);
267
+ }, T = () => x ?? Promise.resolve(), D = (t, n, r) => _.tryPromise({
263
268
  try: async () => {
264
- await S();
265
- let i = (await s().query(T(k).where(x("app", t)).descriptor)).find((e) => (n === void 0 || e.platform === n) && (r === void 0 || e.shell === r));
266
- if (!i) throw new e({ app: t });
267
- return i;
269
+ await T();
270
+ let i = await y().query(E(M).where(S("app", t)).descriptor), a = p.getStore(), o = i.find((e) => (a === void 0 || e.id === a) && (n === void 0 || e.platform === n) && (r === void 0 || e.shell === r));
271
+ if (!o) throw new e({ app: t });
272
+ return o;
268
273
  },
269
274
  catch: (n) => n instanceof e ? n : new e({ app: t })
270
- }), w = (t, n, r) => v.tryPromise({
275
+ }), O = (t, n, r) => _.tryPromise({
271
276
  try: async () => {
272
- let i = (await s().query(T(A).where(x("appId", t)).descriptor)).find((e) => e.name === n);
277
+ let i = (await y().query(E(N).where(S("appId", t)).descriptor)).find((e) => e.name === n);
273
278
  if (!i) throw new e({
274
279
  app: r,
275
280
  channel: n
@@ -280,11 +285,11 @@ var fe = (e, t, n) => {
280
285
  app: r,
281
286
  channel: n
282
287
  })
283
- }), E = (e) => s().query(T(j).where(x("id", e)).descriptor).then((e) => e[0] ?? null), D = (e) => s().query(T(j).where(x("appId", e)).descriptor), O = (e) => s().query(T(M).where(x("releaseId", e)).descriptor), pe = (e) => s().query(T(A).where(x("appId", e)).descriptor), me = (e) => n.retention?.perChannel?.[e] ?? n.retention?.keep ?? 10, F = async (e, t) => {
284
- let n = await pe(e), r = fe(await D(e), n, me);
288
+ }), k = (e) => y().query(E(P).where(S("id", e)).descriptor).then((e) => e[0] ?? null), A = (e) => y().query(E(P).where(S("appId", e)).descriptor), j = (e) => y().query(E(F).where(S("releaseId", e)).descriptor), R = (e) => y().query(E(N).where(S("appId", e)).descriptor), ve = (e) => n.retention?.perChannel?.[e] ?? n.retention?.keep ?? 10, be = async (e, t) => {
289
+ let n = await R(e), r = ge(await A(e), n, ve);
285
290
  if (r.length === 0) return [];
286
- for (let e of r) await s().deleteMany(l, { where: x("releaseId", e.id) }), await s().delete(p, e.id);
287
- return await K({
291
+ for (let e of r) await y().deleteMany(l, { where: S("releaseId", e.id) }), await y().delete(f, e.id);
292
+ return await q({
288
293
  appId: e,
289
294
  kind: "prune",
290
295
  actorId: t,
@@ -293,8 +298,8 @@ var fe = (e, t, n) => {
293
298
  channel: n.find((t) => t.id === e.channelId)?.name ?? null
294
299
  })) }
295
300
  }), r;
296
- }, z = async (e) => {
297
- let t = await O(e.id);
301
+ }, Ce = async (e) => {
302
+ let t = await j(e.id);
298
303
  return {
299
304
  id: e.id,
300
305
  version: e.version,
@@ -315,8 +320,8 @@ var fe = (e, t, n) => {
315
320
  contentType: e.contentType
316
321
  }))
317
322
  };
318
- }, G = async (e, t) => {
319
- let n = await v.runPromise(C(e)), r = await v.runPromise(w(n.id, t, e)), i = r.currentReleaseId ? await E(r.currentReleaseId) : null;
323
+ }, K = async (e, t) => {
324
+ let n = await _.runPromise(D(e)), r = await _.runPromise(O(n.id, t, e)), i = r.currentReleaseId ? await k(r.currentReleaseId) : null;
320
325
  return {
321
326
  app: e,
322
327
  channel: t,
@@ -327,8 +332,8 @@ var fe = (e, t, n) => {
327
332
  rolloutSalt: i?.rolloutSalt ?? null,
328
333
  cohorts: i?.cohorts ?? null
329
334
  };
330
- }, K = (e) => s().insert(_, {
331
- id: W("relev"),
335
+ }, q = (e) => y().insert(g, {
336
+ id: G("relev"),
332
337
  appId: e.appId,
333
338
  channelId: e.channelId ?? null,
334
339
  releaseId: e.releaseId ?? null,
@@ -337,41 +342,54 @@ var fe = (e, t, n) => {
337
342
  actorId: e.actorId ?? null,
338
343
  detail: e.detail ?? null,
339
344
  at: new Date(a())
340
- }), q = (e) => e.request.subject.type === "user" ? e.request.subject.id : null, ve = {
341
- ...oe,
345
+ }), J = (e) => e.request.subject.type === "user" ? e.request.subject.id : null, we = {
346
+ ...se,
342
347
  description: "The reactive release policy of a channel: latest version, minimum supported version, kill switches, rollout.",
343
- execute: (e) => v.tryPromise({
344
- try: () => G(e.app, e.channel),
348
+ execute: (e) => _.tryPromise({
349
+ try: () => K(e.app, e.channel),
345
350
  catch: (e) => e
346
351
  })
347
- }, ye = {
348
- ...ie,
352
+ }, Te = {
353
+ ...ae,
349
354
  description: "Every release of an app target, newest first.",
350
- execute: (e) => v.gen(function* () {
351
- let t = yield* C(e.app), n = yield* v.promise(() => D(t.id)), r = yield* v.promise(() => s().query(T(A).where(x("appId", t.id)).descriptor)), i = [];
355
+ execute: (e) => _.gen(function* () {
356
+ let t = yield* D(e.app), n = yield* _.promise(() => A(t.id)), r = yield* _.promise(() => y().query(E(N).where(S("appId", t.id)).descriptor)), i = /* @__PURE__ */ new Map();
357
+ for (let e of r) i.has(e.id) || i.set(e.id, e.name);
358
+ let a = /* @__PURE__ */ new Map(), o = [...new Set(n.map((e) => e.id))];
359
+ for (let e = 0; e < o.length; e += 500) {
360
+ let t = yield* _.promise(() => y().query(E(F).where(fe("releaseId", o.slice(e, e + 500))).descriptor));
361
+ for (let e of t) {
362
+ let t = a.get(e.releaseId) ?? {
363
+ count: 0,
364
+ signed: !0
365
+ };
366
+ t.count++, t.signed &&= e.signature !== null, a.set(e.releaseId, t);
367
+ }
368
+ }
369
+ let s = [];
352
370
  for (let e of [...n].sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime() || c(t.version, e.version))) {
353
- let t = yield* v.promise(() => O(e.id));
354
- i.push({
371
+ let t = a.get(e.id);
372
+ s.push({
355
373
  id: e.id,
356
374
  version: e.version,
357
375
  buildId: e.buildId,
358
376
  state: e.state,
359
377
  rolloutPercent: e.rolloutPercent,
360
- channel: r.find((t) => t.id === e.channelId)?.name ?? "?",
378
+ channel: i.get(e.channelId) ?? "?",
361
379
  createdAt: e.createdAt.toISOString(),
362
- artifacts: t.length,
363
- signed: t.length > 0 && t.every((e) => e.signature !== null)
380
+ artifacts: t?.count ?? 0,
381
+ signed: t !== void 0 && t.count > 0 && t.signed
364
382
  });
365
383
  }
366
- return i;
384
+ return s;
367
385
  })
368
- }, be = {
369
- ...ne,
386
+ }, Ee = {
387
+ ...re,
370
388
  description: "Register an app target at runtime, with its channels; idempotent.",
371
- execute: (e) => v.tryPromise({
389
+ execute: (e) => _.tryPromise({
372
390
  try: async () => {
373
- await S();
374
- let { row: t, created: n } = await y({
391
+ await T();
392
+ let { row: t, created: n } = await C({
375
393
  app: e.app,
376
394
  platform: e.platform,
377
395
  shell: e.shell,
@@ -387,14 +405,14 @@ var fe = (e, t, n) => {
387
405
  },
388
406
  catch: (n) => new t({ reason: `releases: ${e.app} could not be registered: ${n instanceof Error ? n.message : String(n)}` })
389
407
  })
390
- }, xe = {
408
+ }, De = {
391
409
  ...u,
392
410
  description: "Create a draft release of a version on a channel.",
393
- execute: (e, n) => v.gen(function* () {
394
- let r = yield* C(e.app), i = yield* w(r.id, e.channel, e.app);
395
- if ((yield* v.promise(() => D(r.id))).some((t) => t.version === e.version)) return yield* v.fail(new t({ reason: `version ${e.version} already exists for ${e.app}` }));
396
- let o = W("rel");
397
- return yield* v.promise(() => s().insert(p, {
411
+ execute: (e, n) => _.gen(function* () {
412
+ let r = yield* D(e.app), i = yield* O(r.id, e.channel, e.app);
413
+ if ((yield* _.promise(() => A(r.id))).some((t) => t.version === e.version)) return yield* _.fail(new t({ reason: `version ${e.version} already exists for ${e.app}` }));
414
+ let o = G("rel");
415
+ return yield* _.promise(() => y().insert(f, {
398
416
  id: o,
399
417
  appId: r.id,
400
418
  channelId: i.id,
@@ -403,32 +421,32 @@ var fe = (e, t, n) => {
403
421
  notes: e.notes ?? null,
404
422
  state: "draft",
405
423
  rolloutPercent: 0,
406
- rolloutSalt: W("salt"),
424
+ rolloutSalt: G("salt"),
407
425
  cohorts: e.cohorts ?? null,
408
- createdBy: q(n),
426
+ createdBy: J(n),
409
427
  createdAt: new Date(a()),
410
428
  updatedAt: new Date(a())
411
- })), yield* v.promise(() => K({
429
+ })), yield* _.promise(() => q({
412
430
  appId: r.id,
413
431
  channelId: i.id,
414
432
  releaseId: o,
415
433
  kind: "create",
416
- actorId: q(n),
434
+ actorId: J(n),
417
435
  detail: { version: e.version }
418
- })), yield* v.promise(() => F(r.id, q(n))), { id: o };
436
+ })), yield* _.promise(() => be(r.id, J(n))), { id: o };
419
437
  })
420
- }, Se = {
421
- ...re,
438
+ }, Oe = {
439
+ ...ie,
422
440
  description: "Attach an artefact (a file the client downloads) to a release.",
423
- execute: (n, r) => v.gen(function* () {
424
- let i = yield* v.promise(() => E(n.releaseId));
425
- if (!i) return yield* v.fail(new e({
441
+ execute: (n, r) => _.gen(function* () {
442
+ let i = yield* _.promise(() => k(n.releaseId));
443
+ if (!i) return yield* _.fail(new e({
426
444
  app: "?",
427
445
  version: n.releaseId
428
446
  }));
429
- if (!/^[a-f0-9]{64}$/i.test(n.sha256)) return yield* v.fail(new t({ reason: "sha256 must be 64 hex characters" }));
430
- let o = W("relart");
431
- return yield* v.promise(() => s().insert(l, {
447
+ if (!/^[a-f0-9]{64}$/i.test(n.sha256)) return yield* _.fail(new t({ reason: "sha256 must be 64 hex characters" }));
448
+ let o = G("relart");
449
+ return yield* _.promise(() => y().insert(l, {
432
450
  id: o,
433
451
  releaseId: i.id,
434
452
  kind: n.kind,
@@ -441,158 +459,158 @@ var fe = (e, t, n) => {
441
459
  assetKey: n.assetKey ?? null,
442
460
  contentType: n.contentType ?? null,
443
461
  createdAt: new Date(a())
444
- })), yield* v.promise(() => K({
462
+ })), yield* _.promise(() => q({
445
463
  appId: i.appId,
446
464
  releaseId: i.id,
447
465
  kind: "upload",
448
- actorId: q(r),
466
+ actorId: J(r),
449
467
  detail: {
450
468
  kind: n.kind,
451
469
  target: n.target ?? null
452
470
  }
453
471
  })), { id: o };
454
472
  })
455
- }, Ce = {
456
- ...d,
473
+ }, ke = {
474
+ ...ee,
457
475
  description: "Point a channel at a release (no rebuild) and start its rollout.",
458
- execute: (n, r) => v.gen(function* () {
459
- let i = yield* C(n.app), o = yield* w(i.id, n.to, n.app), c = yield* v.promise(() => D(i.id)), l = c.find((e) => e.version === n.version);
460
- if (!l) return yield* v.fail(new e({
476
+ execute: (n, r) => _.gen(function* () {
477
+ let i = yield* D(n.app), o = yield* O(i.id, n.to, n.app), s = yield* _.promise(() => A(i.id)), c = s.find((e) => e.version === n.version);
478
+ if (!c) return yield* _.fail(new e({
461
479
  app: n.app,
462
480
  version: n.version
463
481
  }));
464
- let u = yield* v.promise(() => O(l.id));
465
- if (!o.allowUnsigned && u.some((e) => e.signature === null)) return yield* v.fail(new t({ reason: `release ${n.version} has unsigned artefacts; channel ${n.to} refuses them` }));
466
- let d = Math.max(0, Math.min(100, n.rolloutPercent ?? 100));
467
- return yield* v.promise(() => s().update(p, l.id, {
482
+ let l = yield* _.promise(() => j(c.id));
483
+ if (!o.allowUnsigned && l.some((e) => e.signature === null)) return yield* _.fail(new t({ reason: `release ${n.version} has unsigned artefacts; channel ${n.to} refuses them` }));
484
+ let u = Math.max(0, Math.min(100, n.rolloutPercent ?? 100));
485
+ return yield* _.promise(() => y().update(f, c.id, {
468
486
  channelId: o.id,
469
- state: d >= 100 ? "complete" : "rolling",
470
- rolloutPercent: d,
487
+ state: u >= 100 ? "complete" : "rolling",
488
+ rolloutPercent: u,
471
489
  updatedAt: new Date(a())
472
- })), yield* v.promise(() => s().update(g, o.id, {
473
- currentReleaseId: l.id,
490
+ })), yield* _.promise(() => y().update(h, o.id, {
491
+ currentReleaseId: c.id,
474
492
  updatedAt: new Date(a())
475
- })), yield* v.promise(() => K({
493
+ })), yield* _.promise(() => q({
476
494
  appId: i.id,
477
495
  channelId: o.id,
478
- releaseId: l.id,
496
+ releaseId: c.id,
479
497
  kind: "promote",
480
- actorId: q(r),
498
+ actorId: J(r),
481
499
  detail: {
482
- from: c.find((e) => e.id === o.currentReleaseId)?.version ?? null,
500
+ from: s.find((e) => e.id === o.currentReleaseId)?.version ?? null,
483
501
  to: n.version,
484
- rolloutPercent: d
502
+ rolloutPercent: u
485
503
  }
486
504
  })), {
487
505
  channel: n.to,
488
506
  version: n.version,
489
- rolloutPercent: d
507
+ rolloutPercent: u
490
508
  };
491
509
  })
492
- }, we = {
493
- ...te,
510
+ }, Ae = {
511
+ ...ne,
494
512
  description: "Change the rollout percentage of the channel's current release.",
495
- execute: (n, r) => v.gen(function* () {
496
- let i = yield* C(n.app), o = yield* w(i.id, n.channel, n.app);
497
- if (!o.currentReleaseId) return yield* v.fail(new t({ reason: `channel ${n.channel} has no current release` }));
498
- let c = yield* v.promise(() => E(o.currentReleaseId));
499
- if (!c) return yield* v.fail(new e({
513
+ execute: (n, r) => _.gen(function* () {
514
+ let i = yield* D(n.app), o = yield* O(i.id, n.channel, n.app);
515
+ if (!o.currentReleaseId) return yield* _.fail(new t({ reason: `channel ${n.channel} has no current release` }));
516
+ let s = yield* _.promise(() => k(o.currentReleaseId));
517
+ if (!s) return yield* _.fail(new e({
500
518
  app: n.app,
501
519
  channel: n.channel
502
520
  }));
503
- let l = Math.max(0, Math.min(100, n.percent));
504
- return yield* v.promise(() => s().update(p, c.id, {
505
- rolloutPercent: l,
506
- state: l >= 100 ? "complete" : "rolling",
521
+ let c = Math.max(0, Math.min(100, n.percent));
522
+ return yield* _.promise(() => y().update(f, s.id, {
523
+ rolloutPercent: c,
524
+ state: c >= 100 ? "complete" : "rolling",
507
525
  updatedAt: new Date(a())
508
- })), yield* v.promise(() => K({
526
+ })), yield* _.promise(() => q({
509
527
  appId: i.id,
510
528
  channelId: o.id,
511
- releaseId: c.id,
529
+ releaseId: s.id,
512
530
  kind: "rollout",
513
- actorId: q(r),
514
- detail: { percent: l }
531
+ actorId: J(r),
532
+ detail: { percent: c }
515
533
  })), {
516
- version: c.version,
517
- rolloutPercent: l
534
+ version: s.version,
535
+ rolloutPercent: c
518
536
  };
519
537
  })
520
- }, Te = {
521
- ...ee,
538
+ }, je = {
539
+ ...te,
522
540
  description: "Point the channel back at the previous release; the current one is marked rolled back.",
523
- execute: (e, n) => v.gen(function* () {
524
- let r = yield* C(e.app), i = yield* w(r.id, e.channel, e.app);
525
- if (!i.currentReleaseId) return yield* v.fail(new t({ reason: `channel ${e.channel} has no current release` }));
526
- let o = yield* v.promise(() => D(r.id)), l = o.find((e) => e.id === i.currentReleaseId), u = o.filter((e) => e.channelId === i.id && e.id !== i.currentReleaseId && e.state !== "rolledBack").sort((e, t) => c(t.version, e.version))[0];
527
- return l && (yield* v.promise(() => s().update(p, l.id, {
541
+ execute: (e, n) => _.gen(function* () {
542
+ let r = yield* D(e.app), i = yield* O(r.id, e.channel, e.app);
543
+ if (!i.currentReleaseId) return yield* _.fail(new t({ reason: `channel ${e.channel} has no current release` }));
544
+ let o = yield* _.promise(() => A(r.id)), s = o.find((e) => e.id === i.currentReleaseId), l = o.filter((e) => e.channelId === i.id && e.id !== i.currentReleaseId && e.state !== "rolledBack").sort((e, t) => c(t.version, e.version))[0];
545
+ return s && (yield* _.promise(() => y().update(f, s.id, {
528
546
  state: "rolledBack",
529
547
  updatedAt: new Date(a())
530
- }))), yield* v.promise(() => s().update(g, i.id, {
531
- currentReleaseId: u?.id ?? null,
548
+ }))), yield* _.promise(() => y().update(h, i.id, {
549
+ currentReleaseId: l?.id ?? null,
532
550
  updatedAt: new Date(a())
533
- })), yield* v.promise(() => K({
551
+ })), yield* _.promise(() => q({
534
552
  appId: r.id,
535
553
  channelId: i.id,
536
- releaseId: l?.id ?? null,
554
+ releaseId: s?.id ?? null,
537
555
  kind: "rollback",
538
556
  reason: e.reason,
539
- actorId: q(n),
557
+ actorId: J(n),
540
558
  detail: {
541
- from: l?.version ?? null,
542
- to: u?.version ?? null
559
+ from: s?.version ?? null,
560
+ to: l?.version ?? null
543
561
  }
544
- })), { version: u?.version ?? null };
562
+ })), { version: l?.version ?? null };
545
563
  })
546
- }, J = {
547
- ...ae,
564
+ }, Me = {
565
+ ...oe,
548
566
  description: "Set the minimum supported version and kill switches of a channel.",
549
- execute: (e, t) => v.gen(function* () {
550
- let n = yield* C(e.app), r = yield* w(n.id, e.channel, e.app);
551
- return yield* v.promise(() => s().update(g, r.id, {
567
+ execute: (e, t) => _.gen(function* () {
568
+ let n = yield* D(e.app), r = yield* O(n.id, e.channel, e.app);
569
+ return yield* _.promise(() => y().update(h, r.id, {
552
570
  ...e.minSupportedVersion === void 0 ? {} : { minSupportedVersion: e.minSupportedVersion },
553
571
  ...e.killSwitches === void 0 ? {} : { killSwitches: e.killSwitches },
554
572
  updatedAt: new Date(a())
555
- })), yield* v.promise(() => K({
573
+ })), yield* _.promise(() => q({
556
574
  appId: n.id,
557
575
  channelId: r.id,
558
576
  kind: "policy",
559
577
  reason: e.reason,
560
- actorId: q(t),
578
+ actorId: J(t),
561
579
  detail: {
562
580
  minSupportedVersion: e.minSupportedVersion ?? null,
563
581
  killSwitches: e.killSwitches ?? null
564
582
  }
565
- })), yield* v.promise(() => G(e.app, e.channel));
583
+ })), yield* _.promise(() => K(e.app, e.channel));
566
584
  })
567
585
  }, Y = async (e, t) => {
568
- let n = await v.runPromise(C(e)), r = await v.runPromise(w(n.id, t, e)), i = r.currentReleaseId ? await E(r.currentReleaseId) : null;
586
+ let n = await _.runPromise(D(e)), r = await _.runPromise(O(n.id, t, e)), i = r.currentReleaseId ? await k(r.currentReleaseId) : null;
569
587
  return {
570
588
  app: n,
571
589
  channel: r,
572
- release: i ? await z(i) : null
590
+ release: i ? await Ce(i) : null
573
591
  };
574
- }, X = (e, t) => t.get("install") ?? e.headers["x-voltro-install-id"] ?? e.headers["expo-embedded-update-id"] ?? "anonymous", Ee = {
592
+ }, X = (e, t) => t.get("install") ?? e.headers["x-voltro-install-id"] ?? e.headers["expo-embedded-update-id"] ?? "anonymous", Ne = {
575
593
  method: "GET",
576
594
  path: r,
577
595
  handle: async (t) => {
578
- if (t.method !== "GET" && t.method !== "HEAD") return H(405, { error: "method_not_allowed" });
596
+ if (t.method !== "GET" && t.method !== "HEAD") return U(405, { error: "method_not_allowed" });
579
597
  let n = t.path.slice(r.length).split("/").filter((e) => e.length > 0).map(decodeURIComponent), a = new URLSearchParams(t.query), [o, s] = n;
580
- if (!o || !s) return H(404, { error: "not_found" });
598
+ if (!o || !s) return U(404, { error: "not_found" });
581
599
  try {
582
600
  if (s === "tauri") {
583
601
  let [, , e, r, i] = n;
584
- if (!e || !r || !i) return H(400, { error: "expected /tauri/:channel/:target/:currentVersion" });
585
- let { release: s } = await Y(o, e), c = s ? I(s, {
602
+ if (!e || !r || !i) return U(400, { error: "expected /tauri/:channel/:target/:currentVersion" });
603
+ let { release: s } = await Y(o, e), c = s ? z(s, {
586
604
  installId: X(t, a),
587
605
  currentVersion: i,
588
606
  target: r
589
607
  }) : null;
590
- return c ? H(200, c) : U(204);
608
+ return c ? U(200, c) : W(204);
591
609
  }
592
610
  if (s === "expo" && n[2] === "manifest") {
593
611
  let e = t.headers["expo-channel-name"] ?? a.get("channel") ?? "stable", n = t.headers["expo-platform"] ?? a.get("platform"), r = t.headers["expo-runtime-version"] ?? a.get("runtimeVersion") ?? "";
594
- if (n !== "ios" && n !== "android") return H(400, { error: "expo-platform must be ios or android" });
595
- let { app: i, release: s } = await Y(o, e), c = R(s, {
612
+ if (n !== "ios" && n !== "android") return U(400, { error: "expo-platform must be ios or android" });
613
+ let { app: i, release: s } = await Y(o, e), c = _e(s, {
596
614
  installId: X(t, a),
597
615
  currentVersion: null,
598
616
  platform: n,
@@ -602,16 +620,16 @@ var fe = (e, t, n) => {
602
620
  runtimeVersion: i.runtimeVersion,
603
621
  rolledBackTo: null
604
622
  });
605
- return c.kind === "no-update" ? H(200, { type: "noUpdateAvailable" }, {
623
+ return c.kind === "no-update" ? U(200, { type: "noUpdateAvailable" }, {
606
624
  "expo-protocol-version": "1",
607
625
  "expo-sfv-version": "0"
608
- }) : c.kind === "rollback" ? H(200, {
626
+ }) : c.kind === "rollback" ? U(200, {
609
627
  type: "rollBackToEmbedded",
610
628
  parameters: { commitTime: c.commitTime }
611
629
  }, {
612
630
  "expo-protocol-version": "1",
613
631
  "expo-sfv-version": "0"
614
- }) : H(200, c.manifest, {
632
+ }) : U(200, c.manifest, {
615
633
  "expo-protocol-version": "1",
616
634
  "expo-sfv-version": "0",
617
635
  "expo-manifest-filters": ""
@@ -619,21 +637,21 @@ var fe = (e, t, n) => {
619
637
  }
620
638
  if (s === "electron") {
621
639
  let [, , e, t] = n;
622
- if (!e || !t) return H(400, { error: "expected /electron/:channel/latest.yml" });
640
+ if (!e || !t) return U(400, { error: "expected /electron/:channel/latest.yml" });
623
641
  let r = t === "latest-mac.yml" ? "mac" : t === "latest-linux.yml" ? "linux" : t === "latest.yml" ? "win" : null;
624
- if (r === null) return H(404, { error: "not_found" });
625
- let { release: i } = await Y(o, e), a = i ? L(i, r) : null;
626
- return a ? he(200, a, "text/yaml; charset=utf-8") : U(204);
642
+ if (r === null) return U(404, { error: "not_found" });
643
+ let { release: i } = await Y(o, e), a = i ? B(i, r) : null;
644
+ return a ? ye(200, a, "text/yaml; charset=utf-8") : W(204);
627
645
  }
628
646
  if (s === "download") {
629
647
  let [, , e] = n;
630
- if (!e) return H(400, { error: "expected /download/:channel" });
648
+ if (!e) return U(400, { error: "expected /download/:channel" });
631
649
  let { channel: r, release: i } = await Y(o, e);
632
- if (!r.public) return H(403, { error: "channel_not_public" });
633
- if (!i) return U(204);
634
- let s = a.get("os") ?? V(t.headers["user-agent"] ?? "")?.os, c = a.get("arch") ?? V(t.headers["user-agent"] ?? "")?.arch;
635
- if (!s) return H(400, { error: "os is required (?os=macos|windows|linux|ios|android)" });
636
- let l = B(i, s, c);
650
+ if (!r.public) return U(403, { error: "channel_not_public" });
651
+ if (!i) return W(204);
652
+ let s = a.get("os") ?? H(t.headers["user-agent"] ?? "")?.os, c = a.get("arch") ?? H(t.headers["user-agent"] ?? "")?.arch;
653
+ if (!s) return U(400, { error: "os is required (?os=macos|windows|linux|ios|android)" });
654
+ let l = V(i, s, c);
637
655
  return l ? {
638
656
  status: 302,
639
657
  body: "",
@@ -641,23 +659,23 @@ var fe = (e, t, n) => {
641
659
  location: l.url,
642
660
  "cache-control": "no-store"
643
661
  }
644
- } : H(404, { error: "no_artifact_for_os" });
662
+ } : U(404, { error: "no_artifact_for_os" });
645
663
  }
646
664
  if (s === "policy") {
647
665
  let [, , e] = n;
648
- if (!e) return H(400, { error: "expected /policy/:channel" });
649
- let r = await G(o, e), s = a.get("version"), c = s ? i(r, {
666
+ if (!e) return U(400, { error: "expected /policy/:channel" });
667
+ let r = await K(o, e), s = a.get("version"), c = s ? i(r, {
650
668
  version: s,
651
669
  installId: X(t, a)
652
670
  }) : null;
653
- return H(200, {
671
+ return U(200, {
654
672
  policy: r,
655
673
  ...c ? { verdict: c } : {}
656
674
  });
657
675
  }
658
- return H(404, { error: "not_found" });
676
+ return U(404, { error: "not_found" });
659
677
  } catch (t) {
660
- if (t instanceof e) return H(404, {
678
+ if (t instanceof e) return U(404, {
661
679
  error: "not_found",
662
680
  app: t.app
663
681
  });
@@ -667,20 +685,27 @@ var fe = (e, t, n) => {
667
685
  }, Z = (e) => ({
668
686
  kind: "json",
669
687
  data: e
670
- }), De = [{
688
+ }), Pe = [{
671
689
  method: "GET",
672
690
  path: "/overview",
673
691
  scope: "shared-store",
674
692
  description: "Every app target with its channels, current versions and rollout state.",
675
- handler: () => v.promise(async () => {
676
- let e = await s().query(T(k).descriptor), t = await s().query(T(A).descriptor), n = await s().query(T(j).descriptor);
693
+ handler: () => _.promise(async () => {
694
+ let e = await y().query(E(M).descriptor), t = await y().query(E(N).descriptor), n = await y().query(E(P).descriptor), r = /* @__PURE__ */ new Map();
695
+ for (let e of t) {
696
+ let t = r.get(e.appId);
697
+ t === void 0 ? r.set(e.appId, [e]) : t.push(e);
698
+ }
699
+ let i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
700
+ for (let e of n) i.has(e.id) || i.set(e.id, e), a.set(e.appId, (a.get(e.appId) ?? 0) + 1);
677
701
  return Z({ apps: e.map((e) => ({
678
702
  id: e.id,
679
703
  app: e.app,
680
704
  platform: e.platform,
681
705
  shell: e.shell,
682
- channels: t.filter((t) => t.appId === e.id).map((e) => {
683
- let t = n.find((t) => t.id === e.currentReleaseId);
706
+ revision: e.revision,
707
+ channels: (r.get(e.id) ?? []).map((e) => {
708
+ let t = e.currentReleaseId === null ? void 0 : i.get(e.currentReleaseId);
684
709
  return {
685
710
  name: e.name,
686
711
  current: t?.version ?? null,
@@ -690,7 +715,7 @@ var fe = (e, t, n) => {
690
715
  public: e.public
691
716
  };
692
717
  }),
693
- releases: n.filter((t) => t.appId === e.id).length
718
+ releases: a.get(e.id) ?? 0
694
719
  })) });
695
720
  })
696
721
  }, {
@@ -698,20 +723,48 @@ var fe = (e, t, n) => {
698
723
  path: "/events",
699
724
  scope: "shared-store",
700
725
  description: "The last 100 release events (promote, rollback, rollout, policy).",
701
- handler: () => v.promise(async () => {
702
- let e = await s().query(T(N).descriptor);
726
+ handler: () => _.promise(async () => {
727
+ let e = await y().query(E(I).descriptor), t = await y().query(E(N).descriptor), n = new Map(t.map((e) => [e.id, e.name]));
703
728
  return Z({ events: [...e].sort((e, t) => t.at.getTime() - e.at.getTime()).slice(0, 100).map((e) => ({
704
729
  ...e,
730
+ channelName: e.channelId === null ? null : n.get(e.channelId) ?? null,
705
731
  at: e.at.toISOString()
706
732
  })) });
707
733
  })
708
- }], Oe = () => ({ request: {
734
+ }], Fe = async (n, r) => {
735
+ let i = n.releaseId === void 0 ? null : await k(n.releaseId);
736
+ if (n.releaseId !== void 0 && !i) throw new e({ app: n.releaseId });
737
+ let a = (await y().query(E(M).descriptor)).filter((e) => (n.app === void 0 || e.app === n.app) && (n.platform === void 0 || e.platform === n.platform) && (n.shell === void 0 || e.shell === n.shell) && (i === null || e.id === i.appId) && (n.expectedTarget === void 0 || e.id === n.expectedTarget.id));
738
+ if (a.length === 0 && r) return;
739
+ if (a.length === 0) throw new e({ app: n.app ?? n.releaseId ?? "" });
740
+ if (a.length !== 1) throw new t({ reason: "Multiple release targets match this app. Select an exact target." });
741
+ let o = a[0], s = n.expectedTarget?.revision ?? o.revision;
742
+ if (!Number.isSafeInteger(s) || s < 0 || s >= 2 ** 53 - 1 || o.revision !== s || await y().updateMany("_voltro_release_apps", { revision: s + 1 }, { where: de(S("id", o.id), S("revision", s)) }) !== 1) throw new t({ reason: "Release target changed. Refresh and review the action again." });
743
+ return o.id;
744
+ }, Ie = (e) => e.kind === "mutation" ? {
745
+ ...e,
746
+ execute: (t, n) => _.gen(function* () {
747
+ let r = yield* _.context();
748
+ return yield* _.tryPromise({
749
+ try: async (i) => {
750
+ await T();
751
+ let a = (a) => s.run(a, async () => {
752
+ let a = await Fe(t, e.name === "releases.registerApp"), o = () => _.runPromiseWith(r)(e.execute(t, n).pipe(_.result), { signal: i }), s = a === void 0 ? await o() : await p.run(a, o);
753
+ if (v.isFailure(s)) throw s.failure;
754
+ return s.success;
755
+ });
756
+ return n.mutationStore ? a(n.mutationStore) : y().transactional(a);
757
+ },
758
+ catch: (e) => e
759
+ });
760
+ })
761
+ } : e, Le = () => ({ request: {
709
762
  subject: {
710
763
  type: "anonymous",
711
764
  id: null,
712
765
  tenantId: null
713
766
  },
714
- traceId: de()
767
+ traceId: he()
715
768
  } }), Q = (e, t) => ({
716
769
  kind: "json",
717
770
  status: e,
@@ -720,7 +773,7 @@ var fe = (e, t, n) => {
720
773
  method: "POST",
721
774
  path: n,
722
775
  description: i,
723
- handler: (n) => v.gen(function* () {
776
+ handler: (n) => _.gen(function* () {
724
777
  let i;
725
778
  try {
726
779
  i = JSON.parse(n.body || "{}");
@@ -728,30 +781,32 @@ var fe = (e, t, n) => {
728
781
  return Q(400, "invalid JSON body");
729
782
  }
730
783
  if (typeof i != "object" || !i || Array.isArray(i)) return Q(400, "expected a JSON object body");
731
- let a = yield* r.execute(i, Oe()).pipe(v.either);
732
- if (a._tag === "Left") {
733
- let n = a.left;
734
- return n instanceof e ? Q(404, `not found: ${n.app}${n.version ? ` ${n.version}` : ""}${n.channel ? ` (${n.channel})` : ""}`) : n instanceof t ? Q(409, n.reason) : Q(400, n instanceof Error ? n.message : String(n));
784
+ let a = yield* le.decodeUnknownEffect(r.input)(i).pipe(_.result);
785
+ if (v.isFailure(a)) return Q(400, "invalid action input");
786
+ let o = yield* Ie(r).execute(a.success, Le()).pipe(_.result);
787
+ if (v.isFailure(o)) {
788
+ let n = o.failure;
789
+ return n instanceof e ? Q(404, `not found: ${n.app}${n.version ? ` ${n.version}` : ""}${n.channel ? ` (${n.channel})` : ""}`) : n instanceof t ? Q(409, n.reason) : Q(500, "release action failed");
735
790
  }
736
- return Z({ result: a.right });
791
+ return Z({ result: o.success });
737
792
  })
738
- }), ke = [
739
- $("/apps", be, "Register an app target at runtime: { app, platform, shell, channels? }."),
740
- $("/create", xe, "Create a draft release: { app, channel, version, buildId?, notes? }."),
741
- $("/upload", Se, "Attach an artefact: { releaseId, kind, target?, url, sha256, size, signature? }."),
742
- $("/promote", Ce, "Promote a version onto a channel: { app, version, to, rolloutPercent? }."),
743
- $("/rollout", we, "Set the rollout percentage: { app, channel, percent }."),
744
- $("/rollback", Te, "Roll a channel back: { app, channel, reason }."),
745
- $("/policy", J, "Set the policy: { app, channel, minSupportedVersion?, killSwitches?, reason }.")
793
+ }), Re = [
794
+ $("/apps", Ee, "Register an app target at runtime: { app, platform, shell, channels? }."),
795
+ $("/create", De, "Create a draft release: { app, channel, version, buildId?, notes? }."),
796
+ $("/upload", Oe, "Attach an artefact: { releaseId, kind, target?, url, sha256, size, signature? }."),
797
+ $("/promote", ke, "Promote a version onto a channel: { app, version, to, rolloutPercent? }."),
798
+ $("/rollout", Ae, "Set the rollout percentage: { app, channel, percent }."),
799
+ $("/rollback", je, "Roll a channel back: { app, channel, reason }."),
800
+ $("/policy", Me, "Set the policy: { app, channel, minSupportedVersion?, killSwitches?, reason }.")
746
801
  ];
747
- return le({
802
+ return pe({
748
803
  source: "plugin",
749
- table: _,
804
+ table: g,
750
805
  timeColumn: "at",
751
- ttlMs: ue(process.env.VOLTRO_RELEASE_EVENTS_TTL_HOURS, 8760),
806
+ ttlMs: me(process.env.VOLTRO_RELEASE_EVENTS_TTL_HOURS, 8760),
752
807
  envVar: "VOLTRO_RELEASE_EVENTS_TTL_HOURS"
753
- }), ce({
754
- name: n.name ? `${h}#${n.name}` : h,
808
+ }), ue({
809
+ name: n.name ? `${m}#${n.name}` : m,
755
810
  description: "Release channels, artefacts and rollouts — the self-hosted update server behind Tauri, Expo OTA, electron-updater and PWA feeds, with a reactive release policy.",
756
811
  permissions: [
757
812
  "inspect:read",
@@ -759,40 +814,40 @@ var fe = (e, t, n) => {
759
814
  "store:write"
760
815
  ],
761
816
  routes: [
762
- ve,
763
- ye,
764
- be,
765
- xe,
766
- Se,
767
- Ce,
768
817
  we,
769
818
  Te,
770
- J
771
- ],
772
- rpcClientDescriptors: se,
773
- httpRoutes: [Ee],
774
- inspectEndpoints: [...De, ...ke],
775
- extendSchema: { tables: [...P] },
776
- declaredEnv: _e,
777
- privacy: ge,
819
+ Ee,
820
+ De,
821
+ Oe,
822
+ ke,
823
+ Ae,
824
+ je,
825
+ Me
826
+ ].map(Ie),
827
+ rpcClientDescriptors: ce,
828
+ httpRoutes: [Ne],
829
+ inspectEndpoints: [...Pe, ...Re],
830
+ extendSchema: { tables: [...L] },
831
+ declaredEnv: Se,
832
+ privacy: xe,
778
833
  bindDataStore: (e) => {
779
- o = e, m = b(), m.catch(() => {});
834
+ o = e, x = w(), x.catch(() => {});
780
835
  },
781
- onActivate: () => v.tryPromise({
836
+ onActivate: () => _.tryPromise({
782
837
  catch: (e) => /* @__PURE__ */ Error(`@voltro/plugin-releases could not seed its targets and channels: ${e instanceof Error ? e.message : String(e)}`),
783
- try: () => S()
838
+ try: () => T()
784
839
  })
785
840
  });
786
- }, ge = { collects: [{
841
+ }, xe = { collects: [{
787
842
  category: "device-id",
788
843
  linked: !1,
789
844
  tracking: !1,
790
845
  purposes: ["app-functionality"]
791
- }] }, _e = [{
846
+ }] }, Se = [{
792
847
  name: "VOLTRO_RELEASE_EVENTS_TTL_HOURS",
793
848
  required: !1,
794
849
  secret: !1,
795
850
  description: "Hours a release event (create, upload, promote, rollout, rollback, policy, prune) is kept before the retention sweep removes it. Default 8760 (one year)."
796
851
  }];
797
852
  //#endregion
798
- export { h as BASE_NAME, m as RELEASES_MANAGE_SCOPE, p as RELEASES_TABLE, f as RELEASE_APPS_TABLE, l as RELEASE_ARTIFACTS_TABLE, g as RELEASE_CHANNELS_TABLE, _ as RELEASE_EVENTS_TABLE, pe as RELEASE_PLATFORMS, me as RELEASE_SHELLS, e as ReleaseNotFound, t as ReleaseRefused, c as compareVersions, i as evaluatePolicy, o as inRollout, r as isNewerVersion, F as offeredTo, V as osFromUserAgent, n as parseVersion, B as pickDownload, k as releaseAppsTable, M as releaseArtifactsTable, A as releaseChannelsTable, N as releaseEventsTable, P as releaseTables, G as releasesPlugin, j as releasesTable, L as renderElectronLatestYml, R as renderExpoFeed, I as renderTauriFeed, a as rolloutBucket, s as sortVersionsDesc };
853
+ export { m as BASE_NAME, p as RELEASES_MANAGE_SCOPE, f as RELEASES_TABLE, d as RELEASE_APPS_TABLE, l as RELEASE_ARTIFACTS_TABLE, h as RELEASE_CHANNELS_TABLE, g as RELEASE_EVENTS_TABLE, A as RELEASE_PLATFORMS, j as RELEASE_SHELLS, e as ReleaseNotFound, t as ReleaseRefused, c as compareVersions, i as evaluatePolicy, o as inRollout, r as isNewerVersion, R as offeredTo, H as osFromUserAgent, n as parseVersion, V as pickDownload, M as releaseAppsTable, F as releaseArtifactsTable, N as releaseChannelsTable, I as releaseEventsTable, L as releaseTables, be as releasesPlugin, P as releasesTable, B as renderElectronLatestYml, _e as renderExpoFeed, z as renderTauriFeed, a as rolloutBucket, s as sortVersionsDesc };