@lynxship/cli 0.1.7 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +175 -19
  2. package/dist/android-toolchain.d.ts +23 -0
  3. package/dist/android-toolchain.d.ts.map +1 -0
  4. package/dist/android-toolchain.js +319 -0
  5. package/dist/artifact-build.d.ts +17 -0
  6. package/dist/artifact-build.d.ts.map +1 -0
  7. package/dist/artifact-build.js +56 -0
  8. package/dist/artifact-name.d.ts +1 -1
  9. package/dist/artifact-name.d.ts.map +1 -1
  10. package/dist/autolink.d.ts +3 -2
  11. package/dist/autolink.d.ts.map +1 -1
  12. package/dist/autolink.js +130 -6
  13. package/dist/bundle-build.d.ts +2 -0
  14. package/dist/bundle-build.d.ts.map +1 -1
  15. package/dist/bundle-build.js +11 -3
  16. package/dist/config.d.ts +19 -0
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +1 -1
  19. package/dist/desktop-build.d.ts +22 -0
  20. package/dist/desktop-build.d.ts.map +1 -0
  21. package/dist/desktop-build.js +161 -0
  22. package/dist/desktop-signing.d.ts +14 -0
  23. package/dist/desktop-signing.d.ts.map +1 -0
  24. package/dist/desktop-signing.js +200 -0
  25. package/dist/guidance.d.ts.map +1 -1
  26. package/dist/guidance.js +113 -4
  27. package/dist/harmony-build.d.ts +22 -0
  28. package/dist/harmony-build.d.ts.map +1 -0
  29. package/dist/harmony-build.js +194 -0
  30. package/dist/index.js +333 -73
  31. package/dist/ios-toolchain.d.ts +19 -0
  32. package/dist/ios-toolchain.d.ts.map +1 -0
  33. package/dist/ios-toolchain.js +303 -0
  34. package/dist/lynx-devtool.d.ts +18 -0
  35. package/dist/lynx-devtool.d.ts.map +1 -0
  36. package/dist/lynx-devtool.js +125 -0
  37. package/dist/process-runner.d.ts +11 -0
  38. package/dist/process-runner.d.ts.map +1 -1
  39. package/dist/process-runner.js +29 -0
  40. package/dist/prompt.d.ts +1 -0
  41. package/dist/prompt.d.ts.map +1 -1
  42. package/dist/prompt.js +6 -0
  43. package/dist/remote.d.ts +3 -3
  44. package/dist/remote.d.ts.map +1 -1
  45. package/dist/target-toolchain.d.ts +17 -0
  46. package/dist/target-toolchain.d.ts.map +1 -0
  47. package/dist/target-toolchain.js +122 -0
  48. package/dist/ui/components.d.ts.map +1 -1
  49. package/dist/ui/components.js +8 -2
  50. package/dist/web-build.d.ts +16 -0
  51. package/dist/web-build.d.ts.map +1 -0
  52. package/dist/web-build.js +116 -0
  53. package/package.json +4 -4
@@ -0,0 +1,19 @@
1
+ import type { BuildProfile } from "./config.js";
2
+ export type IosToolchainStatus = "pass" | "warn" | "fail";
3
+ export interface IosToolchainCheck {
4
+ name: string;
5
+ status: IosToolchainStatus;
6
+ ok: boolean;
7
+ value: string;
8
+ fix?: string;
9
+ }
10
+ export interface IosToolchainReport {
11
+ ok: boolean;
12
+ checks: IosToolchainCheck[];
13
+ project?: string;
14
+ scheme?: string;
15
+ automaticSigning: boolean;
16
+ }
17
+ export declare function inspectIosToolchain(root: string, profile: BuildProfile): Promise<IosToolchainReport>;
18
+ export declare function formatIosToolchainFailure(report: IosToolchainReport): string;
19
+ //# sourceMappingURL=ios-toolchain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-toolchain.d.ts","sourceRoot":"","sources":["../src/ios-toolchain.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOhD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAoKD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,kBAAkB,CAAC,CA4T7B;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAQ5E"}
@@ -0,0 +1,303 @@
1
+ import { homedir, platform } from "node:os";
2
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { captureProcess, commandExists, } from "./process-runner.js";
5
+ function check(name, status, value, fix) {
6
+ return { name, status, ok: status !== "fail", value, fix };
7
+ }
8
+ function findHost(root, profile) {
9
+ const configured = profile.ios?.workspace ?? profile.ios?.project;
10
+ if (configured) {
11
+ const configuredPath = resolve(root, configured);
12
+ if (configuredPath.endsWith(".xcodeproj")) {
13
+ const workspace = configuredPath.replace(/\.xcodeproj$/, ".xcworkspace");
14
+ if (existsSync(workspace))
15
+ return workspace;
16
+ }
17
+ return configuredPath;
18
+ }
19
+ for (const directory of ["ios", "macos"]) {
20
+ try {
21
+ const candidate = readdirSync(join(root, directory)).find((name) => name.endsWith(".xcworkspace") || name.endsWith(".xcodeproj"));
22
+ if (candidate)
23
+ return join(root, directory, candidate);
24
+ }
25
+ catch {
26
+ // A native host is optional until an iOS build is requested.
27
+ }
28
+ }
29
+ return undefined;
30
+ }
31
+ function projectFlag(path) {
32
+ return path.endsWith(".xcworkspace") ? "-workspace" : "-project";
33
+ }
34
+ function commandOutput(result) {
35
+ return `${result.stdout}\n${result.stderr}`.trim();
36
+ }
37
+ function setting(output, name) {
38
+ return output.match(new RegExp(`^${name}\\s*=\\s*(.+)$`, "m"))?.[1]?.trim();
39
+ }
40
+ function provisioningProfilesDirectory() {
41
+ return join(homedir(), "Library", "MobileDevice", "Provisioning Profiles");
42
+ }
43
+ function provisioningProfileFiles() {
44
+ try {
45
+ return readdirSync(provisioningProfilesDirectory())
46
+ .filter((name) => /\.(?:mobileprovision|provisionprofile)$/.test(name))
47
+ .map((name) => join(provisioningProfilesDirectory(), name));
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ }
53
+ function plistValue(content, key) {
54
+ return content.match(new RegExp(`<key>${key}</key>\\s*<(?:string|date)>([^<]+)</`))?.[1];
55
+ }
56
+ async function validProvisioningProfile(file, root, bundleIdentifier) {
57
+ if (!commandExists("security"))
58
+ return false;
59
+ try {
60
+ const decoded = await captureProcess("security", ["cms", "-D", "-i", file], { cwd: root });
61
+ if (decoded.code !== 0)
62
+ return false;
63
+ const content = commandOutput(decoded);
64
+ const expiration = plistValue(content, "ExpirationDate");
65
+ if (!expiration || Number.isNaN(Date.parse(expiration)))
66
+ return false;
67
+ if (Date.parse(expiration) <= Date.now())
68
+ return false;
69
+ if (!bundleIdentifier)
70
+ return true;
71
+ const applicationIdentifier = plistValue(content, "application-identifier");
72
+ if (!applicationIdentifier)
73
+ return false;
74
+ const profileBundle = applicationIdentifier.split(".").slice(1).join(".");
75
+ return profileBundle === bundleIdentifier || profileBundle === "*";
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ }
81
+ async function hasValidProvisioningProfile(root, bundleIdentifier) {
82
+ const files = provisioningProfileFiles();
83
+ for (const file of files) {
84
+ if (await validProvisioningProfile(file, root, bundleIdentifier))
85
+ return true;
86
+ }
87
+ return false;
88
+ }
89
+ async function probeProject(root, context, profile) {
90
+ if (!context.path || !context.scheme || !commandExists("xcodebuild"))
91
+ return context;
92
+ const configuration = profile.ios?.configuration ?? "Release";
93
+ const settings = await captureProcess("xcodebuild", [
94
+ context.flag,
95
+ context.path,
96
+ "-scheme",
97
+ context.scheme,
98
+ "-configuration",
99
+ configuration,
100
+ "-showBuildSettings",
101
+ ], { cwd: root });
102
+ return { ...context, settings };
103
+ }
104
+ async function probeVersion(command, args, cwd) {
105
+ if (!commandExists(command))
106
+ return undefined;
107
+ try {
108
+ const result = await captureProcess(command, args, { cwd });
109
+ if (result.code !== 0)
110
+ return undefined;
111
+ return commandOutput(result).split(/\r?\n/)[0]?.trim();
112
+ }
113
+ catch {
114
+ return undefined;
115
+ }
116
+ }
117
+ async function probeCommand(command, args, cwd) {
118
+ if (!commandExists(command))
119
+ return false;
120
+ try {
121
+ return (await captureProcess(command, args, { cwd })).code === 0;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ export async function inspectIosToolchain(root, profile) {
128
+ const checks = [];
129
+ const isMac = platform() === "darwin";
130
+ const host = findHost(root, profile);
131
+ const hostExists = Boolean(host && existsSync(host));
132
+ const scheme = profile.ios?.scheme;
133
+ const exportOptions = profile.ios?.exportOptionsPlist
134
+ ? resolve(root, profile.ios.exportOptionsPlist)
135
+ : undefined;
136
+ checks.push(check("ios-platform", isMac ? "pass" : "fail", isMac ? "macOS" : `${platform()} · macOS is required for an IPA`, isMac
137
+ ? undefined
138
+ : "Run the iOS build on a macOS machine or macOS CI runner"));
139
+ if (!isMac) {
140
+ checks.push(check("ios-host", "warn", hostExists
141
+ ? "host found but not inspectable on this OS"
142
+ : "not inspected"));
143
+ return {
144
+ ok: checks.every((item) => item.status !== "fail"),
145
+ checks,
146
+ project: host,
147
+ scheme,
148
+ automaticSigning: false,
149
+ };
150
+ }
151
+ const developerDirectory = await probeVersion("xcode-select", ["-p"], root);
152
+ checks.push(check("xcode-select", developerDirectory ? "pass" : "fail", developerDirectory ?? "no active Xcode developer directory", developerDirectory
153
+ ? undefined
154
+ : "sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer"));
155
+ const xcodeVersion = await probeVersion("xcodebuild", ["-version"], root);
156
+ checks.push(check("xcode", xcodeVersion ? "pass" : "fail", xcodeVersion ?? "xcodebuild unavailable", xcodeVersion
157
+ ? undefined
158
+ : "Install Xcode, open it once, accept its license, then run xcode-select --install if needed"));
159
+ checks.push(check("xcrun", commandExists("xcrun") ? "pass" : "fail", commandExists("xcrun") ? "available" : "missing", commandExists("xcrun")
160
+ ? undefined
161
+ : "Install Xcode and select its command-line tools"));
162
+ checks.push(check("codesign", commandExists("codesign") ? "pass" : "fail", commandExists("codesign") ? "available" : "missing", commandExists("codesign")
163
+ ? undefined
164
+ : "Install Xcode command-line tools"));
165
+ checks.push(check("unzip", commandExists("unzip") ? "pass" : "fail", commandExists("unzip") ? "available" : "missing", commandExists("unzip")
166
+ ? undefined
167
+ : "Install the macOS command-line tools"));
168
+ checks.push(check("ios-host", hostExists ? "pass" : "fail", hostExists ? host : "Xcode project or workspace not found", hostExists
169
+ ? undefined
170
+ : "lynxship ios host init --bundle-identifier com.example.myapp"));
171
+ checks.push(check("ios-scheme", scheme ? "pass" : "fail", scheme ?? "missing from the production build profile", scheme ? undefined : "Set build.production.ios.scheme in lynxship.json"));
172
+ const context = await probeProject(root, {
173
+ path: hostExists ? host : undefined,
174
+ flag: host ? projectFlag(host) : "-project",
175
+ scheme,
176
+ }, profile);
177
+ const settingsOutput = context.settings
178
+ ? commandOutput(context.settings)
179
+ : "";
180
+ checks.push(check("xcode-project-settings", !context.settings
181
+ ? "warn"
182
+ : context.settings.code === 0
183
+ ? "pass"
184
+ : "fail", !context.settings
185
+ ? "not inspected until host and scheme are configured"
186
+ : context.settings.code === 0
187
+ ? "scheme and configuration load successfully"
188
+ : "xcodebuild could not load the selected scheme", context.settings?.code === 0 || !context.settings
189
+ ? undefined
190
+ : "Open the project in Xcode and verify the scheme is shared and buildable"));
191
+ const automaticSigning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Automatic";
192
+ const team = setting(settingsOutput, "DEVELOPMENT_TEAM");
193
+ checks.push(check("apple-team", context.settings?.code !== 0 || !context.settings
194
+ ? "warn"
195
+ : team
196
+ ? "pass"
197
+ : "fail", team ?? "team not resolved from Xcode build settings", team
198
+ ? undefined
199
+ : "Select the Apple Developer Team in Xcode Signing & Capabilities"));
200
+ const distribution = profile.distribution ?? profile.ios?.distribution ?? "store";
201
+ const identityResult = await (commandExists("security")
202
+ ? captureProcess("security", ["find-identity", "-v", "-p", "codesigning"], {
203
+ cwd: root,
204
+ })
205
+ : undefined);
206
+ const identityOutput = identityResult ? commandOutput(identityResult) : "";
207
+ const identityCount = Number(identityOutput.match(/(\d+) valid identities found/)?.[1] ?? 0);
208
+ const requiredIdentity = distribution === "store" ? "Apple Distribution" : "Apple Development";
209
+ checks.push(check("apple-signing-identity", identityCount === 0 || !identityOutput.includes(requiredIdentity)
210
+ ? "fail"
211
+ : "pass", identityCount === 0
212
+ ? "no valid code-signing identity found"
213
+ : identityOutput.includes(requiredIdentity)
214
+ ? `${requiredIdentity} identity available`
215
+ : `${requiredIdentity} identity not found`, identityCount > 0 && identityOutput.includes(requiredIdentity)
216
+ ? undefined
217
+ : "Install or select the Apple certificate in Xcode Settings > Accounts, then rerun doctor"));
218
+ const podfile = join(root, "ios", "Podfile");
219
+ const podRequired = existsSync(podfile);
220
+ const podVersion = podRequired
221
+ ? await probeVersion("pod", ["--version"], root)
222
+ : undefined;
223
+ checks.push(check("cocoapods", !podRequired || podVersion ? "pass" : "fail", !podRequired
224
+ ? "not required · no ios/Podfile"
225
+ : (podVersion ?? "Podfile found but pod is unavailable"), !podRequired || podVersion
226
+ ? undefined
227
+ : "gem install cocoapods, then run pod install in the project"));
228
+ let exportOptionsContent;
229
+ if (exportOptions && existsSync(exportOptions)) {
230
+ try {
231
+ exportOptionsContent = readFileSync(exportOptions, "utf8");
232
+ }
233
+ catch {
234
+ exportOptionsContent = undefined;
235
+ }
236
+ }
237
+ const exportMethod = exportOptionsContent?.match(/<key>method<\/key>\s*<string>([^<]+)<\/string>/)?.[1];
238
+ const supportedExportMethods = new Set([
239
+ "app-store",
240
+ "ad-hoc",
241
+ "development",
242
+ "enterprise",
243
+ ]);
244
+ checks.push(check("export-options", !exportOptions
245
+ ? "fail"
246
+ : !existsSync(exportOptions)
247
+ ? "fail"
248
+ : !exportMethod || !supportedExportMethods.has(exportMethod)
249
+ ? "fail"
250
+ : "pass", !exportOptions
251
+ ? "missing from the production build profile"
252
+ : !existsSync(exportOptions)
253
+ ? `${exportOptions} not found`
254
+ : exportMethod && supportedExportMethods.has(exportMethod)
255
+ ? `method: ${exportMethod}`
256
+ : "method missing or invalid for an iOS export", exportOptions &&
257
+ existsSync(exportOptions) &&
258
+ exportMethod &&
259
+ supportedExportMethods.has(exportMethod)
260
+ ? undefined
261
+ : "Set build.production.ios.exportOptionsPlist to a valid ExportOptions.plist"));
262
+ const manualProvisioning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Manual" ||
263
+ exportOptionsContent?.includes("<key>provisioningProfiles</key>") === true;
264
+ const profilesAvailable = await hasValidProvisioningProfile(root, setting(settingsOutput, "PRODUCT_BUNDLE_IDENTIFIER"));
265
+ checks.push(check("ios-provisioning", manualProvisioning && !profilesAvailable
266
+ ? "fail"
267
+ : automaticSigning && !profilesAvailable
268
+ ? "warn"
269
+ : "pass", manualProvisioning && !profilesAvailable
270
+ ? "manual signing selected but no local provisioning profile was found"
271
+ : automaticSigning && !profilesAvailable
272
+ ? "Xcode-managed provisioning will be requested during the build"
273
+ : profilesAvailable
274
+ ? "local provisioning profile available"
275
+ : "not required by the detected signing mode", manualProvisioning && !profilesAvailable
276
+ ? "Download a matching .mobileprovision/.provisionprofile from Apple Developer and install it in Xcode"
277
+ : undefined));
278
+ const simctlAvailable = await probeCommand("xcrun", ["--find", "simctl"], root);
279
+ const devicectlAvailable = await probeCommand("xcrun", ["--find", "devicectl"], root);
280
+ checks.push(check("ios-device-tools", simctlAvailable || devicectlAvailable ? "pass" : "warn", simctlAvailable && devicectlAvailable
281
+ ? "simctl and devicectl available"
282
+ : simctlAvailable
283
+ ? "simctl available · devicectl unavailable"
284
+ : devicectlAvailable
285
+ ? "devicectl available · simctl unavailable"
286
+ : "simctl and devicectl unavailable", simctlAvailable || devicectlAvailable
287
+ ? undefined
288
+ : "Install the full Xcode toolchain, then rerun lynxship doctor --platform ios"));
289
+ const failures = checks.filter((item) => item.status === "fail");
290
+ return {
291
+ ok: failures.length === 0,
292
+ checks,
293
+ project: host,
294
+ scheme,
295
+ automaticSigning,
296
+ };
297
+ }
298
+ export function formatIosToolchainFailure(report) {
299
+ return report.checks
300
+ .filter((item) => item.status === "fail")
301
+ .map((item) => `${item.name}: ${item.value}${item.fix ? ` · fix: ${item.fix}` : ""}`)
302
+ .join("; ");
303
+ }
@@ -0,0 +1,18 @@
1
+ import type { Platform } from "@lynxship/contracts";
2
+ export type DevToolCheckStatus = "pass" | "warn" | "fail";
3
+ export interface DevToolCheck {
4
+ name: string;
5
+ status: DevToolCheckStatus;
6
+ value: string;
7
+ fix?: string;
8
+ }
9
+ export interface LynxDevToolStatus {
10
+ platform: Platform;
11
+ checks: DevToolCheck[];
12
+ ok: boolean;
13
+ traceReady: boolean;
14
+ recorderReady: boolean;
15
+ }
16
+ export declare function inspectLynxDevTool(root: string, platform: Platform): Promise<LynxDevToolStatus>;
17
+ export declare function formatDevToolFailure(status: LynxDevToolStatus): string;
18
+ //# sourceMappingURL=lynx-devtool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lynx-devtool.d.ts","sourceRoot":"","sources":["../src/lynx-devtool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;CACxB;AAoDD,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,iBAAiB,CAAC,CA4K5B;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAQtE"}
@@ -0,0 +1,125 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { commandExists } from "./process-runner.js";
5
+ async function readPackageJson(root) {
6
+ try {
7
+ return JSON.parse(await readFile(join(root, "package.json"), "utf8"));
8
+ }
9
+ catch {
10
+ return {};
11
+ }
12
+ }
13
+ function dependencyVersion(packageJson, packageName) {
14
+ return (packageJson.dependencies?.[packageName] ??
15
+ packageJson.devDependencies?.[packageName]);
16
+ }
17
+ function hasDevVersion(text) {
18
+ return /(?:^|[^\w])(?:["']?\d[^\s"']*-dev|version\s*[=:]\s*["']?\d[^\s"']*-dev)/i.test(text);
19
+ }
20
+ function hasPackageMarker(text, packageName) {
21
+ return text.includes(packageName);
22
+ }
23
+ async function readIfExists(file) {
24
+ if (!existsSync(file))
25
+ return "";
26
+ return readFile(file, "utf8").catch(() => "");
27
+ }
28
+ function check(name, status, value, fix) {
29
+ return { name, status, value, ...(fix ? { fix } : {}) };
30
+ }
31
+ export async function inspectLynxDevTool(root, platform) {
32
+ const packageJson = await readPackageJson(root);
33
+ const checks = [];
34
+ const hasDevScript = Boolean(packageJson.scripts?.dev);
35
+ checks.push(check("rspeedy-dev", hasDevScript ? "pass" : "fail", hasDevScript
36
+ ? "package.json dev script found"
37
+ : "missing package.json dev script", hasDevScript
38
+ ? undefined
39
+ : "add a project dev script that runs rspeedy dev"));
40
+ const rspeedy = dependencyVersion(packageJson, "@lynx-js/rspeedy");
41
+ checks.push(check("rspeedy", rspeedy ? "pass" : "fail", rspeedy ? `configured · ${rspeedy}` : "@lynx-js/rspeedy is not declared", rspeedy
42
+ ? undefined
43
+ : "install the project’s pinned @lynx-js/rspeedy version"));
44
+ const devtoolPath = process.env.LYNXSHIP_DEVTOOL_PATH;
45
+ const desktopAvailable = Boolean(devtoolPath && existsSync(devtoolPath));
46
+ checks.push(check("lynx-devtool-desktop", desktopAvailable ? "pass" : "warn", desktopAvailable
47
+ ? `configured · ${devtoolPath}`
48
+ : "Desktop application not registered in LYNXSHIP_DEVTOOL_PATH", desktopAvailable
49
+ ? undefined
50
+ : "install Lynx DevTool Desktop, then optionally set LYNXSHIP_DEVTOOL_PATH"));
51
+ if (platform === "android") {
52
+ const settings = await readIfExists(join(root, "android", "settings.gradle"));
53
+ const settingsKts = await readIfExists(join(root, "android", "settings.gradle.kts"));
54
+ const app = await readIfExists(join(root, "android", "app", "build.gradle"));
55
+ const appKts = await readIfExists(join(root, "android", "app", "build.gradle.kts"));
56
+ const hostText = `${settings}\n${settingsKts}\n${app}\n${appKts}`;
57
+ const hostExists = Boolean(settings || settingsKts || app || appKts);
58
+ const hasTrace = hasPackageMarker(hostText, "lynx-trace");
59
+ const hasDevTool = hasPackageMarker(hostText, "lynx-devtool");
60
+ const devRuntime = hasDevVersion(hostText);
61
+ checks.push(check("android-host", hostExists ? "pass" : "warn", hostExists
62
+ ? "Gradle host detected"
63
+ : "no Android host; Lynx Explorer remains available through dev", hostExists
64
+ ? undefined
65
+ : "run lynxship android host init --application-id <id> for a native host"));
66
+ checks.push(check("android-trace-runtime", hasTrace && devRuntime ? "pass" : hasTrace ? "warn" : "fail", hasTrace
67
+ ? devRuntime
68
+ ? "lynx-trace and -dev runtime detected"
69
+ : "lynx-trace is present but no -dev Lynx dependency was detected"
70
+ : "lynx-trace dependency not detected", hasTrace && devRuntime
71
+ ? undefined
72
+ : "use the matching Lynx -dev, lynx-trace -dev and lynx-devtool dependencies"));
73
+ checks.push(check("android-devtool-runtime", hasDevTool && devRuntime ? "pass" : hasDevTool ? "warn" : "fail", hasDevTool
74
+ ? devRuntime
75
+ ? "lynx-devtool and -dev runtime detected"
76
+ : "lynx-devtool is present but no -dev Lynx dependency was detected"
77
+ : "lynx-devtool dependency not detected", hasDevTool && devRuntime
78
+ ? undefined
79
+ : "integrate the matching Lynx DevTool development dependencies"));
80
+ if (!commandExists("adb"))
81
+ checks.push(check("android-debug-transport", "warn", "adb not found; USB device discovery is unavailable", "install Android SDK Platform-Tools and connect a device"));
82
+ }
83
+ else {
84
+ const podfile = await readIfExists(join(root, "ios", "Podfile"));
85
+ const hostExists = Boolean(podfile);
86
+ const hasTrace = hasPackageMarker(podfile, "LynxTrace");
87
+ const hasDevTool = hasPackageMarker(podfile, "LynxDevtool");
88
+ const devRuntime = hasDevVersion(podfile);
89
+ checks.push(check("ios-host", hostExists ? "pass" : "warn", hostExists
90
+ ? "CocoaPods host detected"
91
+ : "no iOS host; Lynx Explorer remains available through dev", hostExists
92
+ ? undefined
93
+ : "run lynxship ios host init --bundle-identifier <id> on macOS"));
94
+ checks.push(check("ios-trace-runtime", hasTrace && devRuntime ? "pass" : hasTrace ? "warn" : "fail", hasTrace
95
+ ? devRuntime
96
+ ? "LynxTrace and -dev runtime detected"
97
+ : "LynxTrace is present but no -dev Lynx dependency was detected"
98
+ : "LynxTrace dependency not detected", hasTrace && devRuntime
99
+ ? undefined
100
+ : "use the matching Lynx -dev and LynxTrace -dev pods"));
101
+ checks.push(check("ios-devtool-runtime", hasDevTool && devRuntime ? "pass" : hasDevTool ? "warn" : "fail", hasDevTool
102
+ ? devRuntime
103
+ ? "LynxDevtool and -dev runtime detected"
104
+ : "LynxDevtool is present but no -dev Lynx dependency was detected"
105
+ : "LynxDevtool dependency not detected", hasDevTool && devRuntime
106
+ ? undefined
107
+ : "integrate the matching Lynx DevTool development pods"));
108
+ }
109
+ const traceReady = checks.every((item) => !["android-trace-runtime", "ios-trace-runtime"].includes(item.name) ||
110
+ item.status === "pass");
111
+ const recorderReady = traceReady;
112
+ return {
113
+ platform,
114
+ checks,
115
+ ok: checks.every((item) => item.status !== "fail"),
116
+ traceReady,
117
+ recorderReady,
118
+ };
119
+ }
120
+ export function formatDevToolFailure(status) {
121
+ return status.checks
122
+ .filter((item) => item.status === "fail")
123
+ .map((item) => `${item.name}: ${item.value}${item.fix ? ` · fix: ${item.fix}` : ""}`)
124
+ .join("; ");
125
+ }
@@ -4,6 +4,12 @@ export interface ProcessOptions {
4
4
  quiet?: boolean;
5
5
  onOutput?: (line: string) => void;
6
6
  }
7
+ export interface CapturedProcessResult {
8
+ code: number;
9
+ signal: NodeJS.Signals | null;
10
+ stdout: string;
11
+ stderr: string;
12
+ }
7
13
  export declare function packageManagerCommand(root: string): {
8
14
  command: string;
9
15
  prefix: string[];
@@ -13,6 +19,11 @@ export declare function packageManagerScriptCommand(root: string, script: string
13
19
  args: string[];
14
20
  };
15
21
  export declare function runProcess(command: string, args: string[], options: ProcessOptions): Promise<void>;
22
+ /**
23
+ * Run a small probe without printing it as a build event. Non-zero exit codes
24
+ * are returned to the caller because probes are expected to test availability.
25
+ */
26
+ export declare function captureProcess(command: string, args: string[], options: Pick<ProcessOptions, "cwd" | "env">): Promise<CapturedProcessResult>;
16
27
  export declare function runRspeedy(root: string, subcommand: string, args: string[], options: Omit<ProcessOptions, "cwd">): Promise<void>;
17
28
  export declare function commandExists(command: string): boolean;
18
29
  export declare function executableExists(file: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"process-runner.d.ts","sourceRoot":"","sources":["../src/process-runner.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAcA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,MAAM,EAAO,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAGrC;AAsBD,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CA4Df;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,GACnC,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA4BtD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUtD"}
1
+ {"version":3,"file":"process-runner.d.ts","sourceRoot":"","sources":["../src/process-runner.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAcA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,MAAM,EAAO,GAClB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAGrC;AAsBD,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CA4Df;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,KAAK,CAAC,GAC3C,OAAO,CAAC,qBAAqB,CAAC,CAkChC;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,GACnC,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA4BtD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUtD"}
@@ -91,6 +91,35 @@ export function runProcess(command, args, options) {
91
91
  });
92
92
  });
93
93
  }
94
+ /**
95
+ * Run a small probe without printing it as a build event. Non-zero exit codes
96
+ * are returned to the caller because probes are expected to test availability.
97
+ */
98
+ export function captureProcess(command, args, options) {
99
+ const windowsCommand = process.platform === "win32" && /\.(?:bat|cmd)$/i.test(command);
100
+ const commandLine = [command, ...args]
101
+ .map((value) => /[\s"]/.test(value) ? `"${value.replaceAll('"', '\\"')}"` : value)
102
+ .join(" ");
103
+ return new Promise((resolve, reject) => {
104
+ const child = spawn(windowsCommand ? "cmd.exe" : command, windowsCommand ? ["/d", "/s", "/c", commandLine] : args, {
105
+ cwd: options.cwd,
106
+ env: options.env ?? process.env,
107
+ shell: false,
108
+ stdio: ["ignore", "pipe", "pipe"],
109
+ windowsHide: true,
110
+ });
111
+ let stdout = "";
112
+ let stderr = "";
113
+ child.stdout?.on("data", (chunk) => {
114
+ stdout += chunk.toString();
115
+ });
116
+ child.stderr?.on("data", (chunk) => {
117
+ stderr += chunk.toString();
118
+ });
119
+ child.once("error", reject);
120
+ child.once("close", (code, signal) => resolve({ code: code ?? 1, signal, stdout, stderr }));
121
+ });
122
+ }
94
123
  export async function runRspeedy(root, subcommand, args, options) {
95
124
  const manager = packageManagerCommand(root);
96
125
  await runProcess(manager.command, [...manager.prefix, "rspeedy", subcommand, ...args], { ...options, cwd: root });
package/dist/prompt.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export declare function prompt(label: string, defaultValue?: string): Promise<string>;
2
+ export declare function confirm(label: string, defaultValue?: boolean): Promise<boolean>;
2
3
  export declare function secret(label: string): Promise<string>;
3
4
  //# sourceMappingURL=prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAGA,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2C3D"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAGA,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,wBAAsB,OAAO,CAC3B,KAAK,EAAE,MAAM,EACb,YAAY,UAAQ,GACnB,OAAO,CAAC,OAAO,CAAC,CAIlB;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2C3D"}
package/dist/prompt.js CHANGED
@@ -14,6 +14,12 @@ export async function prompt(label, defaultValue) {
14
14
  readline.close();
15
15
  }
16
16
  }
17
+ export async function confirm(label, defaultValue = false) {
18
+ const answer = await prompt(`${label} ${defaultValue ? "[Y/n]" : "[y/N]"}`);
19
+ if (!answer)
20
+ return defaultValue;
21
+ return ["y", "yes", "oui", "o"].includes(answer.toLowerCase());
22
+ }
17
23
  export async function secret(label) {
18
24
  if (!process.stdin.isTTY || !process.stdin.setRawMode)
19
25
  throw new Error("Secret input requires an interactive terminal; use environment variables in CI");
package/dist/remote.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BuildJob } from "@lynxship/contracts";
1
+ import type { BuildJob, Platform } from "@lynxship/contracts";
2
2
  import type { LynxShipConfig } from "./config.js";
3
3
  export interface RemoteCliState {
4
4
  remoteOrganizationId?: string;
@@ -8,7 +8,7 @@ export interface OtaPublishRequest {
8
8
  projectId: string;
9
9
  organizationId: string;
10
10
  channel: string;
11
- platform: "android" | "ios";
11
+ platform: Platform;
12
12
  runtimeVersion: string;
13
13
  assets: Array<{
14
14
  path: string;
@@ -23,7 +23,7 @@ export interface OtaPublishRequest {
23
23
  export interface OtaRollbackRequest {
24
24
  projectId: string;
25
25
  channel: string;
26
- platform: "android" | "ios";
26
+ platform: Platform;
27
27
  releaseId: string;
28
28
  reason: string;
29
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,GAAG,KAAK,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,GAAG,KAAK,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAsCD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAmDxD;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC,CAoDlB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ/C"}
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAsCD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAmDxD;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC,CAoDlB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ/C"}
@@ -0,0 +1,17 @@
1
+ import type { BuildProfile } from "./config.js";
2
+ export type TargetCheckStatus = "pass" | "warn" | "fail";
3
+ export interface TargetCheck {
4
+ name: string;
5
+ status: TargetCheckStatus;
6
+ ok: boolean;
7
+ value: string;
8
+ fix?: string;
9
+ }
10
+ export interface TargetToolchainReport {
11
+ ok: boolean;
12
+ checks: TargetCheck[];
13
+ }
14
+ export declare function inspectWebTarget(root: string, profile: BuildProfile): Promise<TargetToolchainReport>;
15
+ export declare function inspectHarmonyTarget(root: string, profile: BuildProfile): Promise<TargetToolchainReport>;
16
+ export declare function inspectDesktopTarget(root: string, profile?: BuildProfile): Promise<TargetToolchainReport>;
17
+ //# sourceMappingURL=target-toolchain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target-toolchain.d.ts","sourceRoot":"","sources":["../src/target-toolchain.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAoCD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,qBAAqB,CAAC,CAsChC;AA2BD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,qBAAqB,CAAC,CAmDhC;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,qBAAqB,CAAC,CAwDhC"}