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