@qpjoy/electron-launcher 2.1.0 → 2.2.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.
@@ -96,6 +96,18 @@ export interface ElectronLauncherUpdateCheckResult {
96
96
  decision: ElectronLauncherReleasePolicyDecision;
97
97
  artifacts: ElectronLauncherReleaseArtifactRef[];
98
98
  reason: string;
99
+ /** Markdown release notes from the server-side decision, when provided. */
100
+ releaseNotes?: string | null;
101
+ /** Feature keys granted to this install by the matched plan. */
102
+ featureFlags?: string[];
103
+ /** Why this install did (not) receive the release; shown in the update panel. */
104
+ rollout?: {
105
+ matchedBy?: string | null;
106
+ bucket?: number | null;
107
+ percentage?: number | null;
108
+ } | null;
109
+ /** Which check path produced this result. */
110
+ checkSource?: 'release-check' | 'plans-legacy';
99
111
  }
100
112
  export interface ElectronLauncherReleaseUpdaterOptions {
101
113
  baseUrl: string;
@@ -11,6 +11,28 @@ export function createElectronLauncherReleaseUpdater(options) {
11
11
  return {
12
12
  async check(input) {
13
13
  const checkedAt = new Date().toISOString();
14
+ // Preferred path: server-side single-install decision (docs/19 §6). The
15
+ // server owns target lists and rollout bucketing; the full plans list is
16
+ // being withdrawn to admin-only. Fall back to the legacy flow against
17
+ // older servers that do not expose /release/check yet.
18
+ const installId = input.installId ?? options.reportInstallId ?? null;
19
+ if (installId) {
20
+ try {
21
+ const payload = await requestJson(fetchImpl, joinUrl(baseUrl, '/internal/v1/release/check'), 'POST', {
22
+ installId,
23
+ userId: input.userId ?? null,
24
+ channel: input.channel,
25
+ platform: input.platform ?? null,
26
+ components: { [input.componentId]: input.currentVersion }
27
+ });
28
+ if (payload && typeof payload.status === 'string') {
29
+ return mapReleaseCheckPayload(payload, input, baseUrl, checkedAt);
30
+ }
31
+ }
32
+ catch {
33
+ // Older server without /release/check; use the legacy plans flow.
34
+ }
35
+ }
14
36
  const plansPayload = await requestJson(fetchImpl, joinUrl(baseUrl, '/internal/v1/release-management/plans'), 'GET');
15
37
  const plans = Array.isArray(plansPayload.plans) ? plansPayload.plans : [];
16
38
  const plan = selectReleasePlan(plans, input);
@@ -42,7 +64,8 @@ export function createElectronLauncherReleaseUpdater(options) {
42
64
  artifacts,
43
65
  reason: status === 'blocked'
44
66
  ? plan?.test?.gate?.reason || `release gate is ${gateVerdict}`
45
- : decision.reason
67
+ : decision.reason,
68
+ checkSource: 'plans-legacy'
46
69
  };
47
70
  },
48
71
  async report(input) {
@@ -96,6 +119,53 @@ export async function downloadElectronLauncherReleaseArtifactToFile(input) {
96
119
  bytes
97
120
  };
98
121
  }
122
+ function mapReleaseCheckPayload(payload, input, baseUrl, checkedAt) {
123
+ const decision = payload.decision ?? {
124
+ componentKind: input.componentKind || 'app-managed',
125
+ componentId: input.componentId,
126
+ currentVersion: input.currentVersion,
127
+ targetVersion: input.currentVersion,
128
+ updateAvailable: false,
129
+ updateMode: 'none',
130
+ canSkip: true,
131
+ canDefer: true,
132
+ requiresGate: false,
133
+ rollbackRequired: false,
134
+ reason: payload.reason
135
+ };
136
+ const plan = payload.planId && payload.releaseId
137
+ ? {
138
+ planId: payload.planId,
139
+ releaseId: payload.releaseId,
140
+ environment: 'internal',
141
+ channel: payload.channel,
142
+ installId: input.installId ?? null,
143
+ userId: input.userId ?? null,
144
+ createdBy: 'release-check',
145
+ components: decision.componentKind === 'app-managed' ? { app: decision } : { launcher: decision },
146
+ artifacts: payload.artifacts ?? [],
147
+ rollout: {
148
+ percentage: payload.rollout?.percentage ?? undefined,
149
+ featureKeys: payload.featureFlags ?? []
150
+ },
151
+ activation: payload.activation ?? undefined,
152
+ createdAt: payload.signedAt
153
+ }
154
+ : null;
155
+ return {
156
+ checkedAt,
157
+ baseUrl,
158
+ status: payload.status,
159
+ plan,
160
+ decision,
161
+ artifacts: payload.artifacts ?? [],
162
+ reason: payload.reason,
163
+ releaseNotes: payload.releaseNotes ?? null,
164
+ featureFlags: payload.featureFlags ?? [],
165
+ rollout: payload.rollout ?? null,
166
+ checkSource: 'release-check'
167
+ };
168
+ }
99
169
  function selectReleasePlan(plans, input) {
100
170
  return plans.find((plan) => {
101
171
  if (plan.channel !== input.channel)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qpjoy/electron-launcher",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Product-facing Electron Launcher client for MX Launcher release, update, rollout, AppCenter, and network integration.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -53,10 +53,10 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
+ "@qpjoy/mx-launcher-core": "^2.2.0",
56
57
  "@qpjoy/electron-core-wireguard": "^2.1.0",
57
- "@qpjoy/mx-launcher-embed-sdk": "^2.1.0",
58
- "@qpjoy/mx-launcher-core": "^2.1.0",
59
- "@qpjoy/mx-launcher-standalone": "^2.1.0"
58
+ "@qpjoy/mx-launcher-standalone": "^2.2.0",
59
+ "@qpjoy/mx-launcher-embed-sdk": "^2.2.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^22.10.7",