@lynxship/cli 0.1.20 → 0.1.21

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 (191) hide show
  1. package/README.md +66 -5
  2. package/dist/android/signing.d.ts +16 -0
  3. package/dist/android/signing.d.ts.map +1 -0
  4. package/dist/android/signing.js +131 -0
  5. package/dist/android/types.d.ts +12 -0
  6. package/dist/android/types.d.ts.map +1 -0
  7. package/dist/android/types.js +1 -0
  8. package/dist/android-build.d.ts +4 -13
  9. package/dist/android-build.d.ts.map +1 -1
  10. package/dist/android-build.js +14 -133
  11. package/dist/android-toolchain/inspection.d.ts +3 -0
  12. package/dist/android-toolchain/inspection.d.ts.map +1 -0
  13. package/dist/android-toolchain/inspection.js +122 -0
  14. package/dist/android-toolchain/probes.d.ts +23 -0
  15. package/dist/android-toolchain/probes.d.ts.map +1 -0
  16. package/dist/android-toolchain/probes.js +183 -0
  17. package/dist/android-toolchain/types.d.ts +20 -0
  18. package/dist/android-toolchain/types.d.ts.map +1 -0
  19. package/dist/android-toolchain/types.js +1 -0
  20. package/dist/android-toolchain.d.ts +4 -20
  21. package/dist/android-toolchain.d.ts.map +1 -1
  22. package/dist/android-toolchain.js +4 -300
  23. package/dist/app-config.d.ts +25 -0
  24. package/dist/app-config.d.ts.map +1 -0
  25. package/dist/app-config.js +106 -0
  26. package/dist/bundle-build.d.ts.map +1 -1
  27. package/dist/bundle-build.js +11 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +66 -0
  31. package/dist/commands/build-execution.d.ts +41 -0
  32. package/dist/commands/build-execution.d.ts.map +1 -0
  33. package/dist/commands/build-execution.js +182 -0
  34. package/dist/commands/configuration.d.ts +19 -0
  35. package/dist/commands/configuration.d.ts.map +1 -0
  36. package/dist/commands/configuration.js +174 -0
  37. package/dist/commands/development.d.ts +22 -0
  38. package/dist/commands/development.d.ts.map +1 -0
  39. package/dist/commands/development.js +132 -0
  40. package/dist/commands/device.d.ts +3 -0
  41. package/dist/commands/device.d.ts.map +1 -0
  42. package/dist/commands/device.js +86 -0
  43. package/dist/commands/doctor.d.ts +18 -0
  44. package/dist/commands/doctor.d.ts.map +1 -0
  45. package/dist/commands/doctor.js +320 -0
  46. package/dist/commands/help.d.ts +2 -0
  47. package/dist/commands/help.d.ts.map +1 -0
  48. package/dist/commands/help.js +118 -0
  49. package/dist/commands/i18n.d.ts +14 -0
  50. package/dist/commands/i18n.d.ts.map +1 -0
  51. package/dist/commands/i18n.js +88 -0
  52. package/dist/commands/logs.d.ts +3 -0
  53. package/dist/commands/logs.d.ts.map +1 -0
  54. package/dist/commands/logs.js +51 -0
  55. package/dist/commands/metadata.d.ts +3 -0
  56. package/dist/commands/metadata.d.ts.map +1 -0
  57. package/dist/commands/metadata.js +45 -0
  58. package/dist/commands/ota.d.ts +28 -0
  59. package/dist/commands/ota.d.ts.map +1 -0
  60. package/dist/commands/ota.js +203 -0
  61. package/dist/commands/project.d.ts +14 -0
  62. package/dist/commands/project.d.ts.map +1 -0
  63. package/dist/commands/project.js +122 -0
  64. package/dist/commands/remote-build.d.ts +35 -0
  65. package/dist/commands/remote-build.d.ts.map +1 -0
  66. package/dist/commands/remote-build.js +73 -0
  67. package/dist/commands/submit.d.ts +27 -0
  68. package/dist/commands/submit.d.ts.map +1 -0
  69. package/dist/commands/submit.js +92 -0
  70. package/dist/ecosystem.d.ts +9 -0
  71. package/dist/ecosystem.d.ts.map +1 -0
  72. package/dist/ecosystem.js +95 -0
  73. package/dist/frameworks.d.ts.map +1 -1
  74. package/dist/frameworks.js +5 -1
  75. package/dist/guidance/catalog.d.ts +3 -0
  76. package/dist/guidance/catalog.d.ts.map +1 -0
  77. package/dist/guidance/catalog.js +504 -0
  78. package/dist/guidance/types.d.ts +10 -0
  79. package/dist/guidance/types.d.ts.map +1 -0
  80. package/dist/guidance/types.js +1 -0
  81. package/dist/guidance.d.ts +2 -9
  82. package/dist/guidance.d.ts.map +1 -1
  83. package/dist/guidance.js +1 -502
  84. package/dist/i18n/discovery.d.ts +14 -0
  85. package/dist/i18n/discovery.d.ts.map +1 -0
  86. package/dist/i18n/discovery.js +106 -0
  87. package/dist/i18n/plan.d.ts +12 -0
  88. package/dist/i18n/plan.d.ts.map +1 -0
  89. package/dist/i18n/plan.js +95 -0
  90. package/dist/i18n/setup.d.ts +11 -0
  91. package/dist/i18n/setup.d.ts.map +1 -0
  92. package/dist/i18n/setup.js +47 -0
  93. package/dist/i18n/types.d.ts +17 -0
  94. package/dist/i18n/types.d.ts.map +1 -0
  95. package/dist/i18n/types.js +1 -0
  96. package/dist/index.d.ts +1 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +4 -2069
  99. package/dist/ios/assets.d.ts +19 -0
  100. package/dist/ios/assets.d.ts.map +1 -0
  101. package/dist/ios/assets.js +128 -0
  102. package/dist/ios/production-build.d.ts +6 -0
  103. package/dist/ios/production-build.d.ts.map +1 -0
  104. package/dist/ios/production-build.js +200 -0
  105. package/dist/ios/project.d.ts +4 -0
  106. package/dist/ios/project.d.ts.map +1 -0
  107. package/dist/ios/project.js +43 -0
  108. package/dist/ios/simulator-build.d.ts +8 -0
  109. package/dist/ios/simulator-build.d.ts.map +1 -0
  110. package/dist/ios/simulator-build.js +188 -0
  111. package/dist/ios/simulator.d.ts +5 -0
  112. package/dist/ios/simulator.d.ts.map +1 -0
  113. package/dist/ios/simulator.js +60 -0
  114. package/dist/ios/types.d.ts +14 -0
  115. package/dist/ios/types.d.ts.map +1 -0
  116. package/dist/ios/types.js +1 -0
  117. package/dist/ios-build.d.ts +4 -37
  118. package/dist/ios-build.d.ts.map +1 -1
  119. package/dist/ios-build.js +3 -575
  120. package/dist/ios-host.d.ts.map +1 -1
  121. package/dist/ios-toolchain/inspection.d.ts +4 -0
  122. package/dist/ios-toolchain/inspection.d.ts.map +1 -0
  123. package/dist/ios-toolchain/inspection.js +211 -0
  124. package/dist/ios-toolchain/probes.d.ts +24 -0
  125. package/dist/ios-toolchain/probes.d.ts.map +1 -0
  126. package/dist/ios-toolchain/probes.js +127 -0
  127. package/dist/ios-toolchain/types.d.ts +17 -0
  128. package/dist/ios-toolchain/types.d.ts.map +1 -0
  129. package/dist/ios-toolchain/types.js +1 -0
  130. package/dist/ios-toolchain.d.ts +3 -18
  131. package/dist/ios-toolchain.d.ts.map +1 -1
  132. package/dist/ios-toolchain.js +1 -333
  133. package/dist/plugins/contracts.d.ts +28 -0
  134. package/dist/plugins/contracts.d.ts.map +1 -0
  135. package/dist/plugins/contracts.js +1 -0
  136. package/dist/plugins/discovery.d.ts +19 -0
  137. package/dist/plugins/discovery.d.ts.map +1 -0
  138. package/dist/plugins/discovery.js +250 -0
  139. package/dist/plugins/native-operations.d.ts +28 -0
  140. package/dist/plugins/native-operations.d.ts.map +1 -0
  141. package/dist/plugins/native-operations.js +217 -0
  142. package/dist/plugins/operations.d.ts +9 -0
  143. package/dist/plugins/operations.d.ts.map +1 -0
  144. package/dist/plugins/operations.js +76 -0
  145. package/dist/plugins/validation.d.ts +9 -0
  146. package/dist/plugins/validation.d.ts.map +1 -0
  147. package/dist/plugins/validation.js +52 -0
  148. package/dist/plugins.d.ts +3 -34
  149. package/dist/plugins.d.ts.map +1 -1
  150. package/dist/plugins.js +2 -595
  151. package/dist/process-runner.d.ts +4 -0
  152. package/dist/process-runner.d.ts.map +1 -1
  153. package/dist/process-runner.js +14 -0
  154. package/dist/remote.d.ts +22 -1
  155. package/dist/remote.d.ts.map +1 -1
  156. package/dist/remote.js +141 -2
  157. package/dist/rspeedy-app-config.d.ts +4 -0
  158. package/dist/rspeedy-app-config.d.ts.map +1 -0
  159. package/dist/rspeedy-app-config.js +32 -0
  160. package/dist/runtime/args.d.ts +3 -0
  161. package/dist/runtime/args.d.ts.map +1 -0
  162. package/dist/runtime/args.js +11 -0
  163. package/dist/runtime/build-commands.d.ts +3 -0
  164. package/dist/runtime/build-commands.d.ts.map +1 -0
  165. package/dist/runtime/build-commands.js +282 -0
  166. package/dist/runtime/context.d.ts +63 -0
  167. package/dist/runtime/context.d.ts.map +1 -0
  168. package/dist/runtime/context.js +179 -0
  169. package/dist/runtime/early-commands.d.ts +4 -0
  170. package/dist/runtime/early-commands.d.ts.map +1 -0
  171. package/dist/runtime/early-commands.js +284 -0
  172. package/dist/runtime/project.d.ts +8 -0
  173. package/dist/runtime/project.d.ts.map +1 -0
  174. package/dist/runtime/project.js +38 -0
  175. package/dist/runtime/state.d.ts +47 -0
  176. package/dist/runtime/state.d.ts.map +1 -0
  177. package/dist/runtime/state.js +33 -0
  178. package/dist/secure-store/backends.d.ts +20 -0
  179. package/dist/secure-store/backends.d.ts.map +1 -0
  180. package/dist/secure-store/backends.js +248 -0
  181. package/dist/secure-store/types.d.ts +27 -0
  182. package/dist/secure-store/types.d.ts.map +1 -0
  183. package/dist/secure-store/types.js +1 -0
  184. package/dist/secure-store.d.ts +2 -26
  185. package/dist/secure-store.d.ts.map +1 -1
  186. package/dist/secure-store.js +3 -245
  187. package/package.json +7 -4
  188. package/templates/android-host/app/build.gradle +1 -0
  189. package/templates/android-host/app/src/main/java/template/MainActivity.java +98 -3
  190. package/templates/ios-host/__IOS_TARGET_NAME__/DemoLynxProvider.swift +26 -16
  191. package/templates/ios-host/__IOS_TARGET_NAME__/ViewController.swift +107 -19
@@ -1,336 +1,4 @@
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, target) {
90
- if (!context.path || !context.scheme || !commandExists("xcodebuild"))
91
- return context;
92
- const configuration = profile.ios?.configuration ??
93
- (target === "simulator" ? "Debug" : "Release");
94
- const settings = await captureProcess("xcodebuild", [
95
- context.flag,
96
- context.path,
97
- "-scheme",
98
- context.scheme,
99
- "-configuration",
100
- configuration,
101
- "-showBuildSettings",
102
- ], { cwd: root });
103
- return { ...context, settings };
104
- }
105
- async function probeVersion(command, args, cwd) {
106
- if (!commandExists(command))
107
- return undefined;
108
- try {
109
- const result = await captureProcess(command, args, { cwd });
110
- if (result.code !== 0)
111
- return undefined;
112
- return commandOutput(result).split(/\r?\n/)[0]?.trim();
113
- }
114
- catch {
115
- return undefined;
116
- }
117
- }
118
- async function probeCommand(command, args, cwd) {
119
- if (!commandExists(command))
120
- return false;
121
- try {
122
- return (await captureProcess(command, args, { cwd })).code === 0;
123
- }
124
- catch {
125
- return false;
126
- }
127
- }
128
- export async function inspectIosToolchain(root, profile, target = "device") {
129
- const checks = [];
130
- const isMac = platform() === "darwin";
131
- const host = findHost(root, profile);
132
- const hostExists = Boolean(host && existsSync(host));
133
- const scheme = profile.ios?.scheme;
134
- const simulator = target === "simulator";
135
- const exportOptions = profile.ios?.exportOptionsPlist
136
- ? resolve(root, profile.ios.exportOptionsPlist)
137
- : undefined;
138
- checks.push(check("ios-platform", isMac ? "pass" : "fail", isMac
139
- ? "macOS"
140
- : `${platform()} · macOS is required for an iOS ${simulator ? "Simulator app" : "IPA"}`, isMac
141
- ? undefined
142
- : "Run the iOS build on a macOS machine or macOS CI runner"));
143
- if (!isMac) {
144
- checks.push(check("ios-host", "warn", hostExists
145
- ? "host found but not inspectable on this OS"
146
- : "not inspected"));
147
- return {
148
- ok: checks.every((item) => item.status !== "fail"),
149
- checks,
150
- project: host,
151
- scheme,
152
- automaticSigning: false,
153
- };
154
- }
155
- const developerDirectory = await probeVersion("xcode-select", ["-p"], root);
156
- checks.push(check("xcode-select", developerDirectory ? "pass" : "fail", developerDirectory ?? "no active Xcode developer directory", developerDirectory
157
- ? undefined
158
- : "sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer"));
159
- const xcodeVersion = await probeVersion("xcodebuild", ["-version"], root);
160
- checks.push(check("xcode", xcodeVersion ? "pass" : "fail", xcodeVersion ?? "xcodebuild unavailable", xcodeVersion
161
- ? undefined
162
- : "Install Xcode, open it once, accept its license, then run xcode-select --install if needed"));
163
- checks.push(check("xcrun", commandExists("xcrun") ? "pass" : "fail", commandExists("xcrun") ? "available" : "missing", commandExists("xcrun")
164
- ? undefined
165
- : "Install Xcode and select its command-line tools"));
166
- checks.push(check("codesign", commandExists("codesign") ? "pass" : "fail", commandExists("codesign") ? "available" : "missing", commandExists("codesign")
167
- ? undefined
168
- : "Install Xcode command-line tools"));
169
- checks.push(check("unzip", commandExists("unzip") ? "pass" : "fail", commandExists("unzip") ? "available" : "missing", commandExists("unzip")
170
- ? undefined
171
- : "Install the macOS command-line tools"));
172
- checks.push(check("ios-host", hostExists ? "pass" : "fail", hostExists ? host : "Xcode project or workspace not found", hostExists
173
- ? undefined
174
- : "lynxship ios host init --bundle-identifier com.example.myapp"));
175
- checks.push(check("ios-scheme", scheme ? "pass" : "fail", scheme ?? "missing from the selected build profile", scheme ? undefined : "Set ios.scheme in the selected build profile"));
176
- const context = await probeProject(root, {
177
- path: hostExists ? host : undefined,
178
- flag: host ? projectFlag(host) : "-project",
179
- scheme,
180
- }, profile, target);
181
- const settingsOutput = context.settings
182
- ? commandOutput(context.settings)
183
- : "";
184
- checks.push(check("xcode-project-settings", !context.settings
185
- ? "warn"
186
- : context.settings.code === 0
187
- ? "pass"
188
- : "fail", !context.settings
189
- ? "not inspected until host and scheme are configured"
190
- : context.settings.code === 0
191
- ? "scheme and configuration load successfully"
192
- : "xcodebuild could not load the selected scheme", context.settings?.code === 0 || !context.settings
193
- ? undefined
194
- : "Open the project in Xcode and verify the scheme is shared and buildable"));
195
- const automaticSigning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Automatic";
196
- const team = setting(settingsOutput, "DEVELOPMENT_TEAM");
197
- checks.push(check("apple-team", simulator
198
- ? "pass"
199
- : context.settings?.code !== 0 || !context.settings
200
- ? "warn"
201
- : team
202
- ? "pass"
203
- : "fail", simulator
204
- ? "not required for iOS Simulator"
205
- : (team ?? "team not resolved from Xcode build settings"), simulator || team
206
- ? undefined
207
- : "Select the Apple Developer Team in Xcode Signing & Capabilities"));
208
- const distribution = profile.distribution ?? profile.ios?.distribution ?? "store";
209
- const identityResult = simulator
210
- ? undefined
211
- : await (commandExists("security")
212
- ? captureProcess("security", ["find-identity", "-v", "-p", "codesigning"], {
213
- cwd: root,
214
- })
215
- : undefined);
216
- const identityOutput = identityResult ? commandOutput(identityResult) : "";
217
- const identityCount = Number(identityOutput.match(/(\d+) valid identities found/)?.[1] ?? 0);
218
- const requiredIdentity = distribution === "store" ? "Apple Distribution" : "Apple Development";
219
- checks.push(check("apple-signing-identity", simulator
220
- ? "pass"
221
- : identityCount === 0 || !identityOutput.includes(requiredIdentity)
222
- ? "fail"
223
- : "pass", simulator
224
- ? "not required for iOS Simulator"
225
- : identityCount === 0
226
- ? "no valid code-signing identity found"
227
- : identityOutput.includes(requiredIdentity)
228
- ? `${requiredIdentity} identity available`
229
- : `${requiredIdentity} identity not found`, simulator ||
230
- (identityCount > 0 && identityOutput.includes(requiredIdentity))
231
- ? undefined
232
- : "Install or select the Apple certificate in Xcode Settings > Accounts, then rerun doctor"));
233
- const podfile = join(root, "ios", "Podfile");
234
- const podRequired = existsSync(podfile);
235
- const podVersion = podRequired
236
- ? await probeVersion("pod", ["--version"], root)
237
- : undefined;
238
- checks.push(check("cocoapods", !podRequired || podVersion ? "pass" : "fail", !podRequired
239
- ? "not required · no ios/Podfile"
240
- : (podVersion ?? "Podfile found but pod is unavailable"), !podRequired || podVersion
241
- ? undefined
242
- : "gem install cocoapods, then run pod install in the project"));
243
- let exportOptionsContent;
244
- if (exportOptions && existsSync(exportOptions)) {
245
- try {
246
- exportOptionsContent = readFileSync(exportOptions, "utf8");
247
- }
248
- catch {
249
- exportOptionsContent = undefined;
250
- }
251
- }
252
- const exportMethod = exportOptionsContent?.match(/<key>method<\/key>\s*<string>([^<]+)<\/string>/)?.[1];
253
- const supportedExportMethods = new Set([
254
- "app-store",
255
- "ad-hoc",
256
- "development",
257
- "enterprise",
258
- ]);
259
- checks.push(check("export-options", simulator
260
- ? "pass"
261
- : !exportOptions
262
- ? "fail"
263
- : !existsSync(exportOptions)
264
- ? "fail"
265
- : !exportMethod || !supportedExportMethods.has(exportMethod)
266
- ? "fail"
267
- : "pass", simulator
268
- ? "not required for iOS Simulator"
269
- : !exportOptions
270
- ? "missing from the production build profile"
271
- : !existsSync(exportOptions)
272
- ? `${exportOptions} not found`
273
- : exportMethod && supportedExportMethods.has(exportMethod)
274
- ? `method: ${exportMethod}`
275
- : "method missing or invalid for an iOS export", simulator ||
276
- (exportOptions &&
277
- existsSync(exportOptions) &&
278
- exportMethod &&
279
- supportedExportMethods.has(exportMethod))
280
- ? undefined
281
- : "Set build.production.ios.exportOptionsPlist to a valid ExportOptions.plist"));
282
- const manualProvisioning = setting(settingsOutput, "CODE_SIGN_STYLE") === "Manual" ||
283
- exportOptionsContent?.includes("<key>provisioningProfiles</key>") === true;
284
- const profilesAvailable = await hasValidProvisioningProfile(root, setting(settingsOutput, "PRODUCT_BUNDLE_IDENTIFIER"));
285
- checks.push(check("ios-provisioning", simulator
286
- ? "pass"
287
- : manualProvisioning && !profilesAvailable
288
- ? "fail"
289
- : automaticSigning && !profilesAvailable
290
- ? "warn"
291
- : "pass", simulator
292
- ? "not required for iOS Simulator"
293
- : manualProvisioning && !profilesAvailable
294
- ? "manual signing selected but no local provisioning profile was found"
295
- : automaticSigning && !profilesAvailable
296
- ? "Xcode-managed provisioning will be requested during the build"
297
- : profilesAvailable
298
- ? "local provisioning profile available"
299
- : "not required by the detected signing mode", simulator || !(manualProvisioning && !profilesAvailable)
300
- ? undefined
301
- : "Download a matching .mobileprovision/.provisionprofile from Apple Developer and install it in Xcode"));
302
- const simctlAvailable = await probeCommand("xcrun", ["--find", "simctl"], root);
303
- const devicectlAvailable = await probeCommand("xcrun", ["--find", "devicectl"], root);
304
- checks.push(check("ios-device-tools", simctlAvailable || devicectlAvailable ? "pass" : "warn", simctlAvailable && devicectlAvailable
305
- ? "simctl and devicectl available"
306
- : simctlAvailable
307
- ? "simctl available · devicectl unavailable"
308
- : devicectlAvailable
309
- ? "devicectl available · simctl unavailable"
310
- : "simctl and devicectl unavailable", simctlAvailable || devicectlAvailable
311
- ? undefined
312
- : "Install the full Xcode toolchain, then rerun lynxship doctor --platform ios"));
313
- if (simulator) {
314
- const runtimeResult = await (commandExists("xcrun")
315
- ? captureProcess("xcrun", ["simctl", "list", "runtimes"], { cwd: root })
316
- : undefined);
317
- const runtimeOutput = runtimeResult ? commandOutput(runtimeResult) : "";
318
- const hasRuntime = /iOS\s+[0-9]/.test(runtimeOutput);
319
- checks.push(check("ios-simulator-runtime", hasRuntime ? "pass" : "fail", hasRuntime
320
- ? "iOS Simulator runtime available"
321
- : "no iOS Simulator runtime found", hasRuntime
322
- ? undefined
323
- : "Open Xcode > Settings > Components and install an iOS Simulator runtime"));
324
- }
325
- const failures = checks.filter((item) => item.status === "fail");
326
- return {
327
- ok: failures.length === 0,
328
- checks,
329
- project: host,
330
- scheme,
331
- automaticSigning,
332
- };
333
- }
1
+ export { inspectIosToolchain } from "./ios-toolchain/inspection.js";
334
2
  export function formatIosToolchainFailure(report) {
335
3
  return report.checks
336
4
  .filter((item) => item.status === "fail")
@@ -0,0 +1,28 @@
1
+ import type { AutolinkContribution, BuildContribution, CloudIntegrationContribution, PluginCapability, PluginPermission, PluginPlanChange, TemplateContribution } from "@lynxship/plugin-api";
2
+ import type { LynxShipConfig } from "../config.js";
3
+ export interface ProjectPluginInfo {
4
+ name: string;
5
+ version?: string;
6
+ packagePath?: string;
7
+ entry?: string;
8
+ apiVersion?: number;
9
+ capabilities: PluginCapability[];
10
+ permissions: PluginPermission[];
11
+ status: "ready" | "missing" | "invalid";
12
+ reason: string;
13
+ }
14
+ export interface PluginReport {
15
+ configured: number;
16
+ plugins: ProjectPluginInfo[];
17
+ }
18
+ export interface PluginApplication {
19
+ config: LynxShipConfig;
20
+ report: PluginReport;
21
+ applied: string[];
22
+ templates: TemplateContribution[];
23
+ cloud: CloudIntegrationContribution[];
24
+ build: BuildContribution[];
25
+ changes: PluginPlanChange[];
26
+ autolink: AutolinkContribution[];
27
+ }
28
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/plugins/contracts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,EAC5B,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,KAAK,EAAE,4BAA4B,EAAE,CAAC;IACtC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type { Platform } from "@lynxship/contracts";
2
+ import { type JsonObject, type LynxShipPluginDefinition, type PluginPlatform, type PluginReference } from "@lynxship/plugin-api";
3
+ import type { LynxShipConfig } from "../config.js";
4
+ import type { ProjectPluginInfo, PluginReport } from "./contracts.js";
5
+ export interface ResolvedPlugin {
6
+ info: ProjectPluginInfo;
7
+ definition?: LynxShipPluginDefinition;
8
+ }
9
+ export declare function pluginReference(reference: PluginReference): {
10
+ name: string;
11
+ options: JsonObject;
12
+ };
13
+ export declare function resolvePlugin(root: string, reference: PluginReference, load?: boolean): Promise<ResolvedPlugin>;
14
+ export declare function pluginReferences(config: LynxShipConfig): PluginReference[];
15
+ export declare function inspectProjectPlugins(root: string, config: LynxShipConfig): Promise<PluginReport>;
16
+ export declare function appliesToPlatform(platforms: readonly PluginPlatform[] | undefined, platform: Platform): boolean;
17
+ export declare function mergeJson(left: JsonObject, right: JsonObject): JsonObject;
18
+ export declare function safeProjectFile(root: string, file: string): string;
19
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/plugins/discovery.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,KAAK,UAAU,EACf,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAuBtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,CAAC,EAAE,wBAAwB,CAAC;CACvC;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,eAAe,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,CAAC;CACrB,CAGA;AA8FD,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,eAAe,EAC1B,IAAI,UAAO,GACV,OAAO,CAAC,cAAc,CAAC,CAgIzB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,EAAE,CAI1E;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,EAChD,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAOT;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAkBzE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAYlE"}
@@ -0,0 +1,250 @@
1
+ import { createRequire } from "node:module";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { capabilities, isKnownList, LYNXSHIP_PLUGIN_API_VERSION, KNOWN_CAPABILITIES, KNOWN_PERMISSIONS, permissions, validPluginDefinition, } from "./validation.js";
6
+ export function pluginReference(reference) {
7
+ if (typeof reference === "string")
8
+ return { name: reference, options: {} };
9
+ return { name: reference[0], options: reference[1] };
10
+ }
11
+ function packageDirectory(entry) {
12
+ let current = dirname(entry);
13
+ while (true) {
14
+ if (existsSync(join(current, "package.json")))
15
+ return current;
16
+ const parent = dirname(current);
17
+ if (parent === current)
18
+ return dirname(entry);
19
+ current = parent;
20
+ }
21
+ }
22
+ function parsePackageJson(file) {
23
+ try {
24
+ return JSON.parse(readFileSync(file, "utf8"));
25
+ }
26
+ catch {
27
+ return {};
28
+ }
29
+ }
30
+ function packageJsonFromEntry(entry) {
31
+ return join(packageDirectory(entry), "package.json");
32
+ }
33
+ function findInstalledPackageJson(root, name) {
34
+ let current = root;
35
+ while (true) {
36
+ const candidate = join(current, "node_modules", ...name.split("/"), "package.json");
37
+ if (existsSync(candidate))
38
+ return candidate;
39
+ const parent = dirname(current);
40
+ if (parent === current)
41
+ return undefined;
42
+ current = parent;
43
+ }
44
+ }
45
+ function resolvePackageEntry(root, name) {
46
+ try {
47
+ const requireFromProject = createRequire(join(root, "package.json"));
48
+ let entry;
49
+ let packagePath;
50
+ try {
51
+ entry = requireFromProject.resolve(name);
52
+ packagePath = packageJsonFromEntry(entry);
53
+ }
54
+ catch {
55
+ packagePath = findInstalledPackageJson(root, name) ?? "";
56
+ if (!packagePath)
57
+ throw new Error("Cannot find module '" + name + "'");
58
+ entry = packagePath;
59
+ }
60
+ const packageJson = parsePackageJson(packagePath);
61
+ const plugin = packageJson.lynxship?.plugin;
62
+ if (typeof plugin !== "string" || !plugin)
63
+ return {
64
+ packagePath,
65
+ reason: `${name} does not declare package.json#lynxship.plugin`,
66
+ };
67
+ const packageRoot = dirname(packagePath);
68
+ const resolved = resolve(packageRoot, plugin);
69
+ const outside = relative(packageRoot, resolved).startsWith("..");
70
+ if (outside)
71
+ return {
72
+ packagePath,
73
+ reason: `${name} points its plugin entry outside the package`,
74
+ };
75
+ if (!existsSync(resolved))
76
+ return {
77
+ packagePath,
78
+ reason: `${name} plugin entry does not exist: ${plugin}`,
79
+ };
80
+ return { packagePath, entry: resolved };
81
+ }
82
+ catch (error) {
83
+ return {
84
+ reason: error instanceof Error
85
+ ? `Cannot resolve ${name}: ${error.message}`
86
+ : `Cannot resolve ${name}`,
87
+ };
88
+ }
89
+ }
90
+ export async function resolvePlugin(root, reference, load = true) {
91
+ const requested = pluginReference(reference);
92
+ const resolved = resolvePackageEntry(root, requested.name);
93
+ if (!resolved.packagePath || !resolved.entry) {
94
+ return {
95
+ info: {
96
+ name: requested.name,
97
+ capabilities: [],
98
+ permissions: [],
99
+ status: "missing",
100
+ reason: resolved.reason ?? "Plugin package was not found",
101
+ },
102
+ };
103
+ }
104
+ const packageJson = parsePackageJson(resolved.packagePath);
105
+ const metadata = packageJson.lynxship;
106
+ const apiVersion = typeof metadata?.apiVersion === "number" ? metadata.apiVersion : undefined;
107
+ const packageCapabilities = capabilities(metadata?.capabilities);
108
+ const packagePermissions = permissions(metadata?.permissions);
109
+ if (!isKnownList(metadata?.capabilities, KNOWN_CAPABILITIES) ||
110
+ !isKnownList(metadata?.permissions, KNOWN_PERMISSIONS)) {
111
+ return {
112
+ info: {
113
+ name: requested.name,
114
+ version: typeof packageJson.version === "string"
115
+ ? packageJson.version
116
+ : undefined,
117
+ packagePath: resolved.packagePath,
118
+ entry: resolved.entry,
119
+ apiVersion,
120
+ capabilities: packageCapabilities,
121
+ permissions: packagePermissions,
122
+ status: "invalid",
123
+ reason: "Plugin package capabilities and permissions must be arrays of known values",
124
+ },
125
+ };
126
+ }
127
+ if (apiVersion !== LYNXSHIP_PLUGIN_API_VERSION) {
128
+ return {
129
+ info: {
130
+ name: requested.name,
131
+ version: typeof packageJson.version === "string"
132
+ ? packageJson.version
133
+ : undefined,
134
+ packagePath: resolved.packagePath,
135
+ entry: resolved.entry,
136
+ apiVersion,
137
+ capabilities: packageCapabilities,
138
+ permissions: packagePermissions,
139
+ status: "invalid",
140
+ reason: `Plugin API ${String(apiVersion ?? "missing")} is not supported; expected ${LYNXSHIP_PLUGIN_API_VERSION}`,
141
+ },
142
+ };
143
+ }
144
+ const info = {
145
+ name: requested.name,
146
+ version: typeof packageJson.version === "string" ? packageJson.version : undefined,
147
+ packagePath: resolved.packagePath,
148
+ entry: resolved.entry,
149
+ apiVersion,
150
+ capabilities: packageCapabilities,
151
+ permissions: packagePermissions,
152
+ status: "ready",
153
+ reason: "Plugin manifest is valid",
154
+ };
155
+ if (!load)
156
+ return { info };
157
+ try {
158
+ const module = (await import(pathToFileURL(resolved.entry).href));
159
+ const definition = module.default ?? module.plugin;
160
+ if (!validPluginDefinition(definition)) {
161
+ return {
162
+ info: {
163
+ ...info,
164
+ status: "invalid",
165
+ reason: "Plugin entry must export a LynxShipPluginDefinition",
166
+ },
167
+ };
168
+ }
169
+ if (capabilities(definition.capabilities).join(",") !==
170
+ packageCapabilities.join(",") ||
171
+ permissions(definition.permissions).join(",") !==
172
+ packagePermissions.join(",")) {
173
+ return {
174
+ info: {
175
+ ...info,
176
+ status: "invalid",
177
+ reason: "Plugin code capabilities/permissions do not match package.json",
178
+ },
179
+ };
180
+ }
181
+ if (definition.name !== requested.name &&
182
+ definition.name !== packageJson.name) {
183
+ return {
184
+ info: {
185
+ ...info,
186
+ status: "invalid",
187
+ reason: `Plugin entry name '${definition.name}' does not match its package`,
188
+ },
189
+ };
190
+ }
191
+ return { info, definition };
192
+ }
193
+ catch (error) {
194
+ return {
195
+ info: {
196
+ ...info,
197
+ status: "invalid",
198
+ reason: error instanceof Error
199
+ ? `Plugin entry failed to load: ${error.message}`
200
+ : "Plugin entry failed to load",
201
+ },
202
+ };
203
+ }
204
+ }
205
+ export function pluginReferences(config) {
206
+ const value = config.plugins;
207
+ if (!Array.isArray(value))
208
+ return [];
209
+ return value;
210
+ }
211
+ export async function inspectProjectPlugins(root, config) {
212
+ const references = pluginReferences(config);
213
+ const plugins = await Promise.all(references.map(async (reference) => (await resolvePlugin(root, reference, false)).info));
214
+ return { configured: references.length, plugins };
215
+ }
216
+ export function appliesToPlatform(platforms, platform) {
217
+ return (!platforms ||
218
+ platforms.length === 0 ||
219
+ platforms.includes("all") ||
220
+ platforms.includes(platform));
221
+ }
222
+ export function mergeJson(left, right) {
223
+ const result = { ...left };
224
+ for (const [key, value] of Object.entries(right)) {
225
+ const current = result[key];
226
+ if (current &&
227
+ typeof current === "object" &&
228
+ !Array.isArray(current) &&
229
+ value &&
230
+ typeof value === "object" &&
231
+ !Array.isArray(value)) {
232
+ result[key] = mergeJson(current, value);
233
+ }
234
+ else {
235
+ result[key] = value;
236
+ }
237
+ }
238
+ return result;
239
+ }
240
+ export function safeProjectFile(root, file) {
241
+ if (isAbsolute(file))
242
+ throw new Error(`Plugin native file must be relative: ${file}`);
243
+ const target = resolve(root, file);
244
+ const projectRelative = relative(root, target);
245
+ if (!projectRelative ||
246
+ projectRelative.startsWith("..") ||
247
+ isAbsolute(projectRelative))
248
+ throw new Error(`Plugin native file escapes the project: ${file}`);
249
+ return target;
250
+ }
@@ -0,0 +1,28 @@
1
+ import type { Platform } from "@lynxship/contracts";
2
+ import { type BuildContribution, type CloudIntegrationContribution, type JsonObject, type LynxShipPluginDefinition, type NativeFileOperation, type AutolinkContribution, type PluginPermission, type PluginPlanChange, type TemplateContribution } from "@lynxship/plugin-api";
3
+ export interface OwnedNativeOperation {
4
+ plugin: string;
5
+ operation: NativeFileOperation;
6
+ }
7
+ declare function permissionForContribution(contribution: {
8
+ config?: JsonObject;
9
+ build?: BuildContribution;
10
+ native?: readonly NativeFileOperation[];
11
+ autolink?: AutolinkContribution;
12
+ templates?: readonly TemplateContribution[];
13
+ cloud?: readonly CloudIntegrationContribution[];
14
+ }): PluginPermission[];
15
+ export declare function assertPluginContribution(plugin: LynxShipPluginDefinition, contribution: unknown): asserts contribution is {
16
+ config?: JsonObject;
17
+ build?: BuildContribution;
18
+ native?: readonly NativeFileOperation[];
19
+ autolink?: AutolinkContribution;
20
+ templates?: readonly TemplateContribution[];
21
+ cloud?: readonly CloudIntegrationContribution[];
22
+ };
23
+ export declare function assertPluginPermissions(plugin: LynxShipPluginDefinition, contribution: Parameters<typeof permissionForContribution>[0]): void;
24
+ export declare function inspectNativeOperation(root: string, owned: OwnedNativeOperation, platform: Platform): Promise<PluginPlanChange | undefined>;
25
+ export declare function assertNativeConflicts(root: string, operations: readonly OwnedNativeOperation[]): void;
26
+ export declare function applyNativeOperationsAtomically(root: string, operations: readonly OwnedNativeOperation[], platform: Platform): Promise<PluginPlanChange[]>;
27
+ export {};
28
+ //# sourceMappingURL=native-operations.d.ts.map