@akanjs/cli 3.0.0-beta.2 → 3.0.0-beta.20

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 (187) hide show
  1. package/.build-stamp +1 -1
  2. package/CodeAgent-awv258wb.js +9 -0
  3. package/CodeAgentRpcHost-p7h6n2hp.js +7 -0
  4. package/CodeAgentRpcListener-7p7z5dd7.js +97 -0
  5. package/CodeTui-1nqh35ez.js +1457 -0
  6. package/README.md +3 -0
  7. package/TunnelShare-scf4e4pd.js +9 -0
  8. package/{agent.command-d2gfxfck.js → agent.command-1s9ag7sb.js} +8 -9
  9. package/akanCode.js +102 -0
  10. package/{application.command-pj2m7584.js → application.command-49hwy6va.js} +51 -78
  11. package/{applicationBuildRunner-kxwbssgn.js → applicationBuildRunner-ybyp97pv.js} +63 -64
  12. package/{applicationReleasePackager-4yasxwv9.js → applicationReleasePackager-esnrphw8.js} +40 -60
  13. package/buildBatch.proc.js +19 -24
  14. package/{capacitorApp-36rbr858.js → capacitorApp-1synz4en.js} +3 -4
  15. package/cloud.command-d7rqtzt9.js +94 -0
  16. package/cloud.script-bkbpcr96.js +19 -0
  17. package/code.command-nn48xzej.js +154 -0
  18. package/commandManifest.json +1 -1
  19. package/consoleToStderr-h5bryj94.js +7 -0
  20. package/context.command-s3mcmvpa.js +48 -0
  21. package/context.runner-k99nb5pk.js +21 -0
  22. package/{dependencyScanner-shs82ynp.js → dependencyScanner-ye2e6pdz.js} +2 -2
  23. package/{devTui-40ggmd99.js → devTui-q7k7bakd.js} +122 -40
  24. package/fileSys-5ngz93ga.js +9 -0
  25. package/guideline.command-sct4qnt5.js +42 -0
  26. package/guidelines/agentRule/agentRule.instruction.md +206 -25
  27. package/guidelines/conventions/conventions.instruction.md +79 -24
  28. package/guidelines/fieldRule/fieldRule.instruction.md +1 -0
  29. package/guidelines/mcpRule/mcpRule.instruction.md +7 -5
  30. package/guidelines/queryRule/queryRule.instruction.md +17 -7
  31. package/guidelines/runtimeRule/runtimeRule.instruction.md +74 -0
  32. package/guidelines/ssrRule/ssrRule.instruction.md +4 -2
  33. package/guidelines/transportRule/transportRule.instruction.md +51 -0
  34. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +66 -3
  35. package/incrementalBuilder.proc.js +31 -48
  36. package/{index-h7yw2z9n.js → index-0bq14vr1.js} +8 -14
  37. package/index-13ffe9wp.js +69 -0
  38. package/index-22d57v4c.js +266 -0
  39. package/{index-fz5q3gd7.js → index-31yr441d.js} +10 -17
  40. package/index-3hx1z96c.js +87 -0
  41. package/index-3sq321ak.js +53 -0
  42. package/{index-kcxmv48c.js → index-3t7g9e4r.js} +45 -79
  43. package/{index-q0y2jq9j.js → index-5b6tavv3.js} +6 -3
  44. package/{index-47pj3aee.js → index-5e0n4hqx.js} +11 -12
  45. package/{index-259dsnz3.js → index-5pc1qrbv.js} +5 -8
  46. package/index-7md2g9vw.js +6 -0
  47. package/index-8mna0947.js +187 -0
  48. package/{index-zry5nz81.js → index-8sf7wmdr.js} +51 -30
  49. package/{index-2tv3fdx4.js → index-9d9bgcx4.js} +342 -765
  50. package/index-9dp031by.js +12 -0
  51. package/{index-ne2qy0nk.js → index-ahemdr5n.js} +12 -12
  52. package/{index-ws5e0j51.js → index-as7xmyz8.js} +40 -24
  53. package/index-axzjaqve.js +1118 -0
  54. package/{index-3d9y066c.js → index-bjwxs904.js} +9 -8
  55. package/index-c6eggbee.js +123 -0
  56. package/index-d2242ksy.js +99 -0
  57. package/{index-a64esmtb.js → index-ddxvdd1p.js} +169 -305
  58. package/{index-92zztqy1.js → index-dy0jx9ay.js} +2 -17
  59. package/{index-pnqbkf5p.js → index-dzf3r49a.js} +6 -2
  60. package/{index-w8kmx1b6.js → index-fgq75haa.js} +198 -290
  61. package/{index-by5gpp8x.js → index-hj6w4ka1.js} +2 -1
  62. package/{index-hwkygn9s.js → index-khpfpcsd.js} +302 -33
  63. package/{index-wt7qrtd4.js → index-mxav6j3c.js} +93 -232
  64. package/{index-gyd220p2.js → index-myhc3c1j.js} +554 -577
  65. package/{index-600zhnve.js → index-nv5976x3.js} +56 -197
  66. package/index-p6pbz9n5.js +25 -0
  67. package/{index-4edzf1gr.js → index-przybvy5.js} +77 -150
  68. package/index-smme4r47.js +49 -0
  69. package/{index-mak3cd28.js → index-w2zdcw3k.js} +137 -255
  70. package/index-wa5jbxj1.js +760 -0
  71. package/{index-v0qh106m.js → index-ww4z397j.js} +527 -723
  72. package/{index-j9yhg404.js → index-xqjwd384.js} +4 -7
  73. package/{index-7737gxcp.js → index-xqpzbab5.js} +107 -241
  74. package/index-xsjh6nzh.js +2729 -0
  75. package/{index-ktb5njgy.js → index-ygn77a5w.js} +47 -99
  76. package/index-ztp3e73t.js +700 -0
  77. package/index.js +23 -22
  78. package/{library.command-nbsky672.js → library.command-1gx04jgn.js} +4 -5
  79. package/{localRegistry.command-yeq57wjr.js → localRegistry.command-zndxj1q6.js} +22 -42
  80. package/{module.command-29vqzrh8.js → module.command-3waddk95.js} +8 -11
  81. package/{package.command-axv0abmn.js → package.command-zrc7br1y.js} +6 -7
  82. package/package.json +14 -5
  83. package/{page.command-vs9s3zrh.js → page.command-vpjgardh.js} +4 -5
  84. package/{primitive.command-cbxmqbtc.js → primitive.command-sz84tk4t.js} +9 -22
  85. package/prompter-ptaa3nk0.js +8 -0
  86. package/{quality.command-gmbs7nws.js → quality.command-g869hafs.js} +203 -376
  87. package/{recipeScanner-fpagtgc1.js → recipeScanner-4b69ybe0.js} +1 -1
  88. package/{repair.command-bqsjptsj.js → repair.command-chsnjvvv.js} +11 -35
  89. package/{routeSourceValidator-apyqw7wk.js → routeSourceValidator-7ycnfz0e.js} +0 -6
  90. package/{scalar.command-6k1vhe9s.js → scalar.command-2v21newx.js} +7 -10
  91. package/skills/akan-code/SKILL.md +130 -0
  92. package/skills/akan-debug/SKILL.md +42 -0
  93. package/skills/akan-deps/SKILL.md +50 -0
  94. package/skills/akan-err/SKILL.md +39 -0
  95. package/skills/akan-module/SKILL.md +45 -0
  96. package/skills/akan-scaffold/SKILL.md +76 -0
  97. package/skills/akan-scalar/SKILL.md +52 -0
  98. package/skills/akan-service/SKILL.md +34 -0
  99. package/skills/akan-store/SKILL.md +67 -0
  100. package/skills/akan-ui/SKILL.md +63 -0
  101. package/skills/akan-validate/SKILL.md +44 -0
  102. package/{subspace.command-fwjfcts7.js → subspace.command-ckcbwfp1.js} +138 -67
  103. package/templates/__scalar/__model__/__model__.abstract.md.template +8 -0
  104. package/templates/appSample/lib/__scalar/workHistory/workHistory.abstract.ts +8 -39
  105. package/templates/appSample/lib/_noti/noti.abstract.ts +6 -29
  106. package/templates/appSample/lib/task/task.abstract.ts +12 -32
  107. package/templates/appSample/lib/task/task.signal.spec.ts +15 -18
  108. package/templates/appSample/lib/task/task.signal.test.ts +15 -1
  109. package/templates/appSample/lib/task/task.signal.ts +1 -1
  110. package/templates/appSample/srvkit/{AuthGuard.ts → guards.ts} +7 -4
  111. package/templates/appSample/srvkit/{SessionInternalArg.ts → internalArgs.ts} +6 -4
  112. package/templates/client.ts +7 -30
  113. package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -1
  114. package/templates/crudPages/[__model__Id]/page.tsx +2 -1
  115. package/templates/crudPages/new/page.tsx +2 -1
  116. package/templates/crudPages/page.tsx +2 -1
  117. package/templates/crudSinglePage/page.tsx +2 -1
  118. package/templates/lib/__lib/lib.dictionary.ts +0 -2
  119. package/templates/lib/__lib/lib.document.ts +0 -2
  120. package/templates/lib/sig.ts +8 -30
  121. package/templates/localDev/docker-compose.yaml.template +3 -14
  122. package/templates/module/__model__.signal.ts +12 -4
  123. package/templates/pkgRoot/tsconfig.json.template +2 -7
  124. package/templates/server.ts +0 -2
  125. package/templates/service/__model__.abstract.md.template +8 -0
  126. package/tunnel.command-kd2yr4fx.js +129 -0
  127. package/{typeChecker-3xgrzc2v.js → typeChecker-px49mezc.js} +1 -1
  128. package/typecheck.proc.js +1 -1
  129. package/workflow.command-44252cqv.js +79 -0
  130. package/{workspace.command-8s050m0h.js → workspace.command-fqcztt5p.js} +77 -109
  131. package/abstractCompactor-mjh5ppsq.js +0 -101
  132. package/cloud.command-jshwdq22.js +0 -93
  133. package/context.command-ez8a0zyz.js +0 -80
  134. package/devTuiView-c08ccbat.js +0 -9
  135. package/getDirname-dr3jz1xg.js +0 -7
  136. package/guideline.command-xmqwx8zm.js +0 -359
  137. package/guidelines/agentRule/agentRule.generate.json +0 -31
  138. package/guidelines/componentRule/componentRule.generate.json +0 -31
  139. package/guidelines/conventions/conventions.generate.json +0 -25
  140. package/guidelines/cssRule/cssRule.generate.json +0 -31
  141. package/guidelines/docPageRule/docPageRule.generate.json +0 -31
  142. package/guidelines/docPageRule/docPageRule.instruction.md +0 -25
  143. package/guidelines/docSyncRule/docSyncRule.generate.json +0 -31
  144. package/guidelines/docSyncRule/docSyncRule.instruction.md +0 -25
  145. package/guidelines/enumConstant/enumConstant.generate.json +0 -31
  146. package/guidelines/enumConstant/enumConstant.instruction.md +0 -28
  147. package/guidelines/fieldRule/fieldRule.generate.json +0 -31
  148. package/guidelines/framework/framework.generate.json +0 -32
  149. package/guidelines/mcpRule/mcpRule.generate.json +0 -35
  150. package/guidelines/modelConstant/modelConstant.generate.json +0 -32
  151. package/guidelines/modelDictionary/modelDictionary.generate.json +0 -32
  152. package/guidelines/modelDocument/modelDocument.generate.json +0 -31
  153. package/guidelines/modelService/modelService.generate.json +0 -31
  154. package/guidelines/modelSignal/modelSignal.generate.json +0 -31
  155. package/guidelines/modelStore/modelStore.generate.json +0 -31
  156. package/guidelines/modelTemplate/modelTemplate.generate.json +0 -31
  157. package/guidelines/modelUnit/modelUnit.generate.json +0 -31
  158. package/guidelines/modelUtil/modelUtil.generate.json +0 -31
  159. package/guidelines/modelView/modelView.generate.json +0 -31
  160. package/guidelines/modelZone/modelZone.generate.json +0 -31
  161. package/guidelines/moduleCodegen/moduleCodegen.generate.json +0 -31
  162. package/guidelines/moduleCodegen/moduleCodegen.instruction.md +0 -28
  163. package/guidelines/moduleOverview/moduleOverview.generate.json +0 -31
  164. package/guidelines/queryRule/queryRule.generate.json +0 -31
  165. package/guidelines/recipeRule/recipeRule.generate.json +0 -31
  166. package/guidelines/runtimeRule/runtimeRule.generate.json +0 -25
  167. package/guidelines/scalarConstant/scalarConstant.generate.json +0 -32
  168. package/guidelines/scalarDictionary/scalarDictionary.generate.json +0 -32
  169. package/guidelines/scalarModule/scalarModule.generate.json +0 -31
  170. package/guidelines/sharedUiUsage/sharedUiUsage.generate.json +0 -31
  171. package/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +0 -25
  172. package/guidelines/ssrRule/ssrRule.generate.json +0 -31
  173. package/guidelines/transportRule/transportRule.generate.json +0 -31
  174. package/guidelines/utilUiUsage/utilUiUsage.generate.json +0 -31
  175. package/guidelines/utilUiUsage/utilUiUsage.instruction.md +0 -25
  176. package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +0 -24
  177. package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +0 -33
  178. package/index-2hq52jq3.js +0 -465
  179. package/index-6wwzs1bc.js +0 -7
  180. package/index-8pm30m73.js +0 -841
  181. package/index-kn1h7p1x.js +0 -560
  182. package/index-meh558jy.js +0 -65
  183. package/index-rwg2aa4f.js +0 -229
  184. package/templates/__scalar/__model__/__model__.abstract.md +0 -28
  185. package/templates/module/__model__.abstract.md +0 -28
  186. package/templates/service/__model__.abstract.md +0 -28
  187. package/workflow.command-nwpcdc8p.js +0 -107
@@ -1,11 +1,11 @@
1
1
  // @bun
2
2
  import {
3
3
  CommandExecutionError
4
- } from "./index-2tv3fdx4.js";
4
+ } from "./index-9d9bgcx4.js";
5
5
  import {
6
6
  resolveMobilePath,
7
7
  targetHtmlFilename
8
- } from "./index-47pj3aee.js";
8
+ } from "./index-5e0n4hqx.js";
9
9
 
10
10
  // pkgs/@akanjs/devkit/capacitorApp.ts
11
11
  import { cp, mkdir, readFile, rm, writeFile } from "fs/promises";
@@ -13,7 +13,7 @@ import os from "os";
13
13
  import path from "path";
14
14
  import { select } from "@inquirer/prompts";
15
15
  import { MobileProject } from "@trapezedev/project";
16
- import { capitalize } from "akanjs/common";
16
+ import { capitalize, isRecord } from "akanjs/common";
17
17
 
18
18
  // pkgs/@akanjs/devkit/fileEditor.ts
19
19
  class FileEditor {
@@ -32,52 +32,28 @@ class FileEditor {
32
32
  }
33
33
  }
34
34
  find(pattern) {
35
- const lines = this.#content.split(`
36
- `);
37
35
  const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
38
- for (let i = 0;i < lines.length; i++) {
39
- const line = lines[i];
40
- if (!line)
41
- continue;
42
- if (regex.test(line))
43
- return i;
44
- }
45
- return -1;
36
+ return this.#content.split(`
37
+ `).findIndex((line) => !!line && regex.test(line));
46
38
  }
47
39
  findAll(pattern) {
48
- const lines = this.#content.split(`
49
- `);
50
40
  const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
51
- const matches = [];
52
- for (let i = 0;i < lines.length; i++) {
53
- const line = lines[i];
54
- if (!line)
55
- continue;
56
- if (regex.test(line))
57
- matches.push(i);
58
- }
59
- return matches;
41
+ return this.#content.split(`
42
+ `).flatMap((line, index) => line && regex.test(line) ? [index] : []);
60
43
  }
61
44
  insertAfter(pattern, data) {
62
- const lineIndex = this.find(pattern);
63
- if (lineIndex === -1) {
64
- throw new Error(`Pattern not found: ${pattern}`);
65
- }
66
- const lines = this.#content.split(`
67
- `);
68
- lines.splice(lineIndex + 1, 0, data);
69
- this.#content = lines.join(`
70
- `);
71
- return this;
45
+ return this.#insertAt(pattern, data, 1);
72
46
  }
73
47
  insertBefore(pattern, data) {
48
+ return this.#insertAt(pattern, data, 0);
49
+ }
50
+ #insertAt(pattern, data, offset) {
74
51
  const lineIndex = this.find(pattern);
75
- if (lineIndex === -1) {
52
+ if (lineIndex === -1)
76
53
  throw new Error(`Pattern not found: ${pattern}`);
77
- }
78
54
  const lines = this.#content.split(`
79
55
  `);
80
- lines.splice(lineIndex, 0, data);
56
+ lines.splice(lineIndex + offset, 0, data);
81
57
  this.#content = lines.join(`
82
58
  `);
83
59
  return this;
@@ -115,30 +91,7 @@ ${this.#content}`;
115
91
 
116
92
  // pkgs/@akanjs/devkit/capacitorApp.ts
117
93
  var SWIFTUICORE_MIN_IOS_MAJOR = 18;
118
- var iosNativeBlockedEnvKeys = new Set([
119
- "AR",
120
- "AS",
121
- "CC",
122
- "CFLAGS",
123
- "CONDA_BUILD_SYSROOT",
124
- "CONDA_PREFIX",
125
- "CPP",
126
- "CPPFLAGS",
127
- "CPATH",
128
- "CXX",
129
- "CXXFLAGS",
130
- "LD",
131
- "LDFLAGS",
132
- "LIBRARY_PATH",
133
- "MACOSX_DEPLOYMENT_TARGET",
134
- "NM",
135
- "OBJC",
136
- "OBJCXX",
137
- "PREFIX",
138
- "RANLIB",
139
- "SDKROOT",
140
- "STRIP"
141
- ]);
94
+ var iosNativeBlockedEnvKeys = new Set("AR AS CC CFLAGS CONDA_BUILD_SYSROOT CONDA_PREFIX CPP CPPFLAGS CPATH CXX CXXFLAGS LD LDFLAGS LIBRARY_PATH".split(" ").concat("MACOSX_DEPLOYMENT_TARGET NM OBJC OBJCXX PREFIX RANLIB SDKROOT STRIP".split(" ")));
142
95
  var rootCapacitorConfigFilenames = [
143
96
  "capacitor.config.ts",
144
97
  "capacitor.config.js",
@@ -152,22 +105,7 @@ async function writeRootCapacitorConfig(appRoot, content) {
152
105
  await clearRootCapacitorConfigs(appRoot);
153
106
  await Bun.write(path.join(appRoot, "capacitor.config.json"), content);
154
107
  }
155
- var virtualInterfacePrefixes = [
156
- "bridge",
157
- "utun",
158
- "llw",
159
- "awdl",
160
- "ap",
161
- "vmnet",
162
- "vnic",
163
- "tap",
164
- "tun",
165
- "docker",
166
- "veth",
167
- "vboxnet",
168
- "gif",
169
- "stf"
170
- ];
108
+ var virtualInterfacePrefixes = "bridge utun llw awdl ap vmnet vnic tap tun docker veth vboxnet gif stf".split(" ");
171
109
  var physicalInterfacePrefixes = ["en", "eth", "wlan", "wlp", "enp", "eno", "wlo"];
172
110
  var isPrivateLanIpv4 = (address) => {
173
111
  if (address.startsWith("10.") || address.startsWith("192.168."))
@@ -203,7 +141,6 @@ var selectLocalDevHost = (interfaces, { override } = {}) => {
203
141
  const [best] = [...candidates].sort((a, b) => scoreDevHostCandidate(b.name, b.address) - scoreDevHostCandidate(a.name, a.address) || a.name.localeCompare(b.name) || a.address.localeCompare(b.address));
204
142
  return best ? { host: best.address, source: "detected", candidates } : { host: "127.0.0.1", source: "loopback", candidates };
205
143
  };
206
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
207
144
  var asString = (value) => typeof value === "string" ? value : undefined;
208
145
  var firstString = (...values) => values.find((value) => typeof value === "string");
209
146
  var scoreIosDeviceTarget = (target) => {
@@ -232,11 +169,8 @@ var formatSimctlRuntime = (key) => {
232
169
  return `${match[1]} ${match[2]}${match[3] ? `.${match[3]}` : ""}`;
233
170
  };
234
171
  var parseIosRuntimeMajor = (runtime) => {
235
- const major = runtime?.match(/(\d+)/)?.[1];
236
- if (major === undefined)
237
- return;
238
- const parsed = Number.parseInt(major, 10);
239
- return Number.isNaN(parsed) ? undefined : parsed;
172
+ const major = runtime?.match(/\d+/)?.[0];
173
+ return major === undefined ? undefined : Number.parseInt(major, 10);
240
174
  };
241
175
  var iosRunTargetRank = (target) => {
242
176
  const state = target.state?.toLowerCase() ?? "";
@@ -450,7 +384,7 @@ function classifyIosRunFailure(log) {
450
384
  return {
451
385
  kind: "unknown",
452
386
  title: "iOS native run failed.",
453
- detail: "Review the xcodebuild/devicectl output above. You can retry with --noAllowProvisioningUpdates to use the conservative path."
387
+ detail: "Review the xcodebuild/devicectl output above. You can retry with --no-allow-provisioning-updates to use the conservative path."
454
388
  };
455
389
  }
456
390
  function formatIosRunFailureMessage(input) {
@@ -528,6 +462,20 @@ function raiseGradleMinSdkVersion(content, floor = ANDROID_MIN_SDK_VERSION) {
528
462
  return null;
529
463
  return content.replace(/(minSdkVersion\s*=\s*)\d+/, `$1${floor}`);
530
464
  }
465
+ var iosUsageDescriptionKeys = {
466
+ cameraUsageDescription: ["NSCameraUsageDescription"],
467
+ photoAddUsageDescription: ["NSPhotoLibraryAddUsageDescription"],
468
+ photoUsageDescription: ["NSPhotoLibraryUsageDescription"],
469
+ contactsUsageDescription: ["NSContactsUsageDescription"],
470
+ locationAlwaysUsageDescription: ["NSLocationAlwaysAndWhenInUseUsageDescription", "NSLocationAlwaysUsageDescription"],
471
+ locationWhenInUseUsageDescription: ["NSLocationWhenInUseUsageDescription"],
472
+ microphoneUsageDescription: ["NSMicrophoneUsageDescription"],
473
+ speechRecognitionUsageDescription: ["NSSpeechRecognitionUsageDescription"]
474
+ };
475
+ var toIosInfoPlistUsageDescriptions = (descriptions) => Object.fromEntries(Object.entries(descriptions).flatMap(([key, value]) => {
476
+ const plistKeys = Object.hasOwn(iosUsageDescriptionKeys, key) ? iosUsageDescriptionKeys[key] : [`NS${capitalize(key)}`];
477
+ return plistKeys.map((plistKey) => [plistKey, value]);
478
+ }));
531
479
  var mergeAllowNavigation = (configured, localIp) => {
532
480
  const values = Array.isArray(configured) ? configured.filter((value) => typeof value === "string") : [];
533
481
  if (localIp)
@@ -552,9 +500,8 @@ function assertJsonSerializable(value, label = "capacitor.config", seen = new We
552
500
  throw new Error(`${label} must be JSON serializable. Found circular reference.`);
553
501
  seen.add(objectValue);
554
502
  if (Array.isArray(value)) {
555
- value.forEach((item, index) => {
503
+ for (const [index, item] of value.entries())
556
504
  assertJsonSerializable(item, `${label}[${index}]`, seen);
557
- });
558
505
  return;
559
506
  }
560
507
  for (const [key, item] of Object.entries(value)) {
@@ -715,7 +662,6 @@ class CapacitorApp2 {
715
662
  await this.#prepareIos({ operation: "release", env, regenerate });
716
663
  await this.#spawnMobile("npx", ["cap", "build", "ios"], { operation: "release", env }, { stdio: "inherit" });
717
664
  this.app.verbose(`build completed iOS.`);
718
- return;
719
665
  }
720
666
  async syncIos() {
721
667
  await this.#spawnMobile("npx", ["cap", "sync", "ios"], { operation: "local", env: "local" });
@@ -735,7 +681,10 @@ class CapacitorApp2 {
735
681
  await this.#runIosPhysicalDevice({ operation, env, runTarget, noAllowProvisioningUpdates });
736
682
  }
737
683
  async#selectIosRunTarget(deviceId) {
738
- const targets = sortIosRunTargets(await this.#loadIosRunTargets());
684
+ const targets = sortIosRunTargets([
685
+ ...await this.#loadPhysicalIosDevices(),
686
+ ...await this.#loadIosSimulators()
687
+ ]);
739
688
  if (deviceId) {
740
689
  const needle = deviceId.toLowerCase();
741
690
  const found = targets.find((target) => target.id === deviceId) ?? targets.find((target) => target.name.toLowerCase() === needle) ?? targets.find((target) => target.name.toLowerCase().includes(needle) || target.id.toLowerCase().includes(needle) || (target.runtime?.toLowerCase().includes(needle) ?? false));
@@ -767,11 +716,6 @@ class CapacitorApp2 {
767
716
  return;
768
717
  this.app.logger.warn(`Selected simulator runs ${target.runtime ?? "an older iOS"}. Recent SDK builds link SwiftUICore and require iOS ${SWIFTUICORE_MIN_IOS_MAJOR}+; if the app crashes at launch with a "Library not loaded: SwiftUICore" dyld error, pick an iOS ${SWIFTUICORE_MIN_IOS_MAJOR}+ simulator instead.`);
769
718
  }
770
- async#loadIosRunTargets() {
771
- const devices = await this.#loadPhysicalIosDevices();
772
- const simulators = await this.#loadIosSimulators();
773
- return [...devices, ...simulators];
774
- }
775
719
  async#loadPhysicalIosDevices() {
776
720
  try {
777
721
  return parseDevicectlDevices(await this.#spawn("xcrun", ["devicectl", "list", "devices", "--json-output", "-"]));
@@ -806,12 +750,11 @@ ${textError instanceof Error ? textError.message : ""}`);
806
750
  }) {
807
751
  const mobileEnv = sanitizeIosNativeRunEnv(await this.#commandEnv(operation, env));
808
752
  const configContent = await this.#writeCapacitorConfig({ operation, platform: "ios", iosRunTargetKind: runTarget.kind }, mobileEnv);
809
- await this.#writeRootCapacitorConfig(configContent);
810
- const scheme = this.#iosScheme();
753
+ await writeRootCapacitorConfig(this.app.cwdPath, configContent);
811
754
  const command = buildIosNativeRunCommand({
812
755
  appRoot: this.app.cwdPath,
813
756
  device: runTarget,
814
- scheme,
757
+ scheme: isRecord(this.target.ios) && typeof this.target.ios.scheme === "string" ? this.target.ios.scheme : "App",
815
758
  configuration: operation === "release" ? "Release" : "Debug"
816
759
  });
817
760
  const xcodebuildArgs = noAllowProvisioningUpdates ? command.xcodebuildArgs : [...command.xcodebuildArgs.slice(0, -1), "-allowProvisioningUpdates", ...command.xcodebuildArgs.slice(-1)];
@@ -821,12 +764,9 @@ ${textError instanceof Error ? textError.message : ""}`);
821
764
  env: mobileEnv
822
765
  });
823
766
  const devicectlId = runTarget.devicectlId ?? runTarget.id;
824
- await this.#spawn("xcrun", ["devicectl", "device", "install", "app", "--device", devicectlId, command.appPath], {
825
- env: mobileEnv
826
- });
827
- await this.#spawn("xcrun", ["devicectl", "device", "process", "launch", "--device", devicectlId, this.target.appId], {
828
- env: mobileEnv
829
- });
767
+ const devicectl = (verb, subject) => this.#spawn("xcrun", ["devicectl", "device", ...verb, "--device", devicectlId, subject], { env: mobileEnv });
768
+ await devicectl(["install", "app"], command.appPath);
769
+ await devicectl(["process", "launch"], this.target.appId);
830
770
  } catch (error) {
831
771
  throw new Error(formatIosRunFailureMessage({
832
772
  classification: classifyIosRunFailure(this.#errorOutput(error)),
@@ -835,12 +775,9 @@ ${textError instanceof Error ? textError.message : ""}`);
835
775
  teamId: await this.#getIosDevelopmentTeam()
836
776
  }));
837
777
  } finally {
838
- await this.#clearRootCapacitorConfigs();
778
+ await clearRootCapacitorConfigs(this.app.cwdPath);
839
779
  }
840
780
  }
841
- #iosScheme() {
842
- return isRecord(this.target.ios) && typeof this.target.ios.scheme === "string" ? this.target.ios.scheme : "App";
843
- }
844
781
  async#getIosDevelopmentTeam() {
845
782
  const pbxprojPath = path.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
846
783
  if (!await Bun.file(pbxprojPath).exists())
@@ -865,7 +802,7 @@ ${error.message}`;
865
802
  await this.#disableNativeKeyboardResizeInAndroid();
866
803
  await this.project.commit();
867
804
  await this.#generateAssets({ operation, env });
868
- await this.#ensureAndroidAssetsDir();
805
+ await mkdir(path.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
869
806
  await this.#ensureAndroidDebugKeystore();
870
807
  await this.#spawnMobile("npx", ["cap", "sync", "android"], { operation, env });
871
808
  await this.#setDeepLinksInAndroid(this.target.deepLinks?.schemes ?? [], this.target.deepLinks?.domains ?? []);
@@ -911,8 +848,7 @@ ${error.message}`;
911
848
  await this.#prepareAndroid({ operation: "release", env, regenerate });
912
849
  await this.#assertAndroidReleaseSigningConfig();
913
850
  await this.#updateAndroidBuildTypes();
914
- const isWindows = process.platform === "win32";
915
- const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
851
+ const gradleCommand = process.platform === "win32" ? "gradlew.bat" : "./gradlew";
916
852
  await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
917
853
  stdio: "inherit",
918
854
  cwd: path.join(this.app.cwdPath, this.androidRootPath),
@@ -939,30 +875,17 @@ ${error.message}`;
939
875
  if (changed)
940
876
  await writeFile(manifestPath, manifest);
941
877
  }
942
- async#ensureAndroidAssetsDir() {
943
- await mkdir(path.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
944
- }
945
878
  async#ensureAndroidDebugKeystore() {
946
879
  const keystorePath = path.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
947
880
  if (await Bun.file(keystorePath).exists())
948
881
  return;
882
+ const options = "-storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000";
949
883
  await this.#spawn("keytool", [
950
884
  "-genkeypair",
951
885
  "-v",
952
886
  "-keystore",
953
887
  keystorePath,
954
- "-storepass",
955
- "android",
956
- "-alias",
957
- "androiddebugkey",
958
- "-keypass",
959
- "android",
960
- "-keyalg",
961
- "RSA",
962
- "-keysize",
963
- "2048",
964
- "-validity",
965
- "10000",
888
+ ...options.split(" "),
966
889
  "-dname",
967
890
  "CN=Android Debug,O=Android,C=US"
968
891
  ]);
@@ -978,8 +901,7 @@ ${error.message}`;
978
901
  await this.#prepareAndroid({ operation, env, regenerate });
979
902
  await this.#assertAndroidAdbReady();
980
903
  this.app.logger.info(`Running Android in ${operation} mode on ${env} env`);
981
- const args = ["cap", "run", "android"];
982
- await this.#spawnMobile("npx", args, { operation, env }, { stdio: "inherit" });
904
+ await this.#spawnMobile("npx", ["cap", "run", "android"], { operation, env }, { stdio: "inherit" });
983
905
  }
984
906
  async#assertAndroidReleaseSigningConfig() {
985
907
  const gradlePropertiesPath = path.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
@@ -1086,14 +1008,12 @@ ${error.message}`;
1086
1008
  if (!this.target.assets)
1087
1009
  return;
1088
1010
  await mkdir(this.targetAssetRoot, { recursive: true });
1089
- if (this.target.assets.icon)
1090
- await cp(path.join(this.app.cwdPath, this.target.assets.icon), path.join(this.targetAssetRoot, "icon.png"), {
1091
- force: true
1092
- });
1093
- if (this.target.assets.splash)
1094
- await cp(path.join(this.app.cwdPath, this.target.assets.splash), path.join(this.targetAssetRoot, "splash.png"), {
1095
- force: true
1096
- });
1011
+ for (const [name, source] of [
1012
+ ["icon", this.target.assets.icon],
1013
+ ["splash", this.target.assets.splash]
1014
+ ])
1015
+ if (source)
1016
+ await cp(path.join(this.app.cwdPath, source), path.join(this.targetAssetRoot, `${name}.png`), { force: true });
1097
1017
  }
1098
1018
  async#prepareExternalFiles(platform) {
1099
1019
  const files = this.target.files?.[platform];
@@ -1179,20 +1099,11 @@ ${error.message}`;
1179
1099
  operation,
1180
1100
  env,
1181
1101
  setIosUsageDescriptions: (descriptions) => this.#setPermissionInIos(descriptions),
1182
- updateIosInfoPlist: async (values) => {
1183
- await Promise.all([
1184
- this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", values),
1185
- this.project.ios.updateInfoPlist(this.iosTargetName, "Release", values)
1186
- ]);
1187
- },
1102
+ updateIosInfoPlist: (values) => this.#updateIosInfoPlist(values),
1188
1103
  addIosEntitlements: (entitlements) => this.#addIosEntitlements(entitlements),
1189
1104
  editIosAppDelegate: (transform) => this.#editIosAppDelegate(transform),
1190
- addAndroidPermissions: (permissions) => {
1191
- this.#setPermissionsInAndroid(permissions);
1192
- },
1193
- addAndroidFeatures: (features) => {
1194
- this.#setFeaturesInAndroid(features);
1195
- }
1105
+ addAndroidPermissions: (permissions) => this.#setPermissionsInAndroid(permissions),
1106
+ addAndroidFeatures: (features) => this.#setFeaturesInAndroid(features)
1196
1107
  };
1197
1108
  }
1198
1109
  async#applyDeepLinks(platform, { operation, env }) {
@@ -1263,12 +1174,6 @@ ${error.message}`;
1263
1174
  params.set("akanMobileIndexPath", this.target.indexPath);
1264
1175
  return `http://${ip}:${port}/${pathname}?${params}`;
1265
1176
  }
1266
- async#clearRootCapacitorConfigs() {
1267
- await clearRootCapacitorConfigs(this.app.cwdPath);
1268
- }
1269
- async#writeRootCapacitorConfig(content) {
1270
- await writeRootCapacitorConfig(this.app.cwdPath, content);
1271
- }
1272
1177
  async#spawn(command, args = [], options = {}) {
1273
1178
  return await this.app.spawn(command, args, { cwd: this.app.cwdPath, ...options });
1274
1179
  }
@@ -1276,14 +1181,11 @@ ${error.message}`;
1276
1181
  const { iosRunTargetKind, platform, ...spawnOptions } = options;
1277
1182
  const mobileEnv = { ...await this.#commandEnv(operation, env), ...options.env };
1278
1183
  const configContent = await this.#writeCapacitorConfig({ operation, platform: platform ?? this.#inferMobilePlatform(args), iosRunTargetKind }, mobileEnv);
1279
- await this.#writeRootCapacitorConfig(configContent);
1184
+ await writeRootCapacitorConfig(this.app.cwdPath, configContent);
1280
1185
  try {
1281
- return await this.#spawn(command, args, {
1282
- ...spawnOptions,
1283
- env: mobileEnv
1284
- });
1186
+ return await this.#spawn(command, args, { ...spawnOptions, env: mobileEnv });
1285
1187
  } finally {
1286
- await this.#clearRootCapacitorConfigs();
1188
+ await clearRootCapacitorConfigs(this.app.cwdPath);
1287
1189
  }
1288
1190
  }
1289
1191
  #inferMobilePlatform(args) {
@@ -1328,22 +1230,16 @@ ${error.message}`;
1328
1230
  if (next !== content)
1329
1231
  await editor.setContent(next).save();
1330
1232
  }
1233
+ async#updateIosInfoPlist(values) {
1234
+ await Promise.all(["Debug", "Release"].map((build) => this.project.ios.updateInfoPlist(this.iosTargetName, build, values)));
1235
+ }
1331
1236
  async#setPermissionInIos(permissions) {
1332
- const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize(key)}`, value]));
1333
- await Promise.all([
1334
- this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", updateNs),
1335
- this.project.ios.updateInfoPlist(this.iosTargetName, "Release", updateNs)
1336
- ]);
1237
+ await this.#updateIosInfoPlist(toIosInfoPlistUsageDescriptions(permissions));
1337
1238
  }
1338
1239
  async#setUrlSchemesInIos(schemes) {
1339
- const urlTypes = schemes.map((scheme) => ({
1340
- CFBundleURLName: this.target.appId,
1341
- CFBundleURLSchemes: [scheme]
1342
- }));
1343
- await Promise.all([
1344
- this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", { CFBundleURLTypes: urlTypes }),
1345
- this.project.ios.updateInfoPlist(this.iosTargetName, "Release", { CFBundleURLTypes: urlTypes })
1346
- ]);
1240
+ await this.#updateIosInfoPlist({
1241
+ CFBundleURLTypes: schemes.map((scheme) => ({ CFBundleURLName: this.target.appId, CFBundleURLSchemes: [scheme] }))
1242
+ });
1347
1243
  }
1348
1244
  #serializeIosEntitlements(entitlements) {
1349
1245
  const lines = [];
@@ -1419,97 +1315,62 @@ ${error.message}`;
1419
1315
  return;
1420
1316
  await this.#setCodeSignEntitlementsInIos(entitlementsRelPath);
1421
1317
  }
1422
- async#setUrlSchemesInAndroid(schemes) {
1318
+ async#setDeepLinksInAndroid(schemes, domains) {
1423
1319
  const manifestPath = path.join(this.app.cwdPath, this.androidRootPath, "app/src/main/AndroidManifest.xml");
1424
- let manifest = await readFile(manifestPath, "utf8");
1425
- let changed = false;
1320
+ const original = await readFile(manifestPath, "utf8");
1321
+ const pathPrefix = resolveMobilePath(this.target, "/");
1322
+ let manifest = original;
1426
1323
  for (const scheme of schemes) {
1427
1324
  if (manifest.includes(`android:scheme="${scheme}"`))
1428
1325
  continue;
1429
- const filter = [
1430
- " <intent-filter>",
1431
- ' <action android:name="android.intent.action.VIEW" />',
1432
- ' <category android:name="android.intent.category.DEFAULT" />',
1433
- ' <category android:name="android.intent.category.BROWSABLE" />',
1434
- ` <data android:scheme="${scheme}" />`,
1435
- " </intent-filter>"
1436
- ].join(`
1437
- `);
1438
- manifest = manifest.replace(/(\s*<\/activity>)/, `
1439
- ${filter}$1`);
1440
- changed = true;
1326
+ manifest = CapacitorApp2.#withIntentFilter(manifest, "<intent-filter>", `<data android:scheme="${scheme}" />`);
1441
1327
  }
1442
- if (changed)
1443
- await writeFile(manifestPath, manifest);
1444
- }
1445
- async#setDeepLinksInAndroid(schemes, domains) {
1446
- await this.#setUrlSchemesInAndroid(schemes);
1447
- const manifestPath = path.join(this.app.cwdPath, this.androidRootPath, "app/src/main/AndroidManifest.xml");
1448
- let manifest = await readFile(manifestPath, "utf8");
1449
- let changed = false;
1450
- const pathPrefix = resolveMobilePath(this.target, "/");
1451
1328
  for (const domain of domains) {
1452
1329
  if (manifest.includes(`android:host="${domain}"`) && manifest.includes('android:scheme="https"'))
1453
1330
  continue;
1454
- const filter = [
1455
- ' <intent-filter android:autoVerify="true">',
1456
- ' <action android:name="android.intent.action.VIEW" />',
1457
- ' <category android:name="android.intent.category.DEFAULT" />',
1458
- ' <category android:name="android.intent.category.BROWSABLE" />',
1459
- ` <data android:scheme="https" android:host="${domain}" android:pathPrefix="${pathPrefix}" />`,
1460
- " </intent-filter>"
1461
- ].join(`
1462
- `);
1463
- manifest = manifest.replace(/(\s*<\/activity>)/, `
1464
- ${filter}$1`);
1465
- changed = true;
1331
+ manifest = CapacitorApp2.#withIntentFilter(manifest, '<intent-filter android:autoVerify="true">', `<data android:scheme="https" android:host="${domain}" android:pathPrefix="${pathPrefix}" />`);
1466
1332
  }
1467
- if (changed)
1333
+ if (manifest !== original)
1468
1334
  await writeFile(manifestPath, manifest);
1469
1335
  }
1336
+ static #withIntentFilter(manifest, open, data) {
1337
+ const filter = [
1338
+ ` ${open}`,
1339
+ ' <action android:name="android.intent.action.VIEW" />',
1340
+ ' <category android:name="android.intent.category.DEFAULT" />',
1341
+ ' <category android:name="android.intent.category.BROWSABLE" />',
1342
+ ` ${data}`,
1343
+ " </intent-filter>"
1344
+ ].join(`
1345
+ `);
1346
+ return manifest.replace(/(\s*<\/activity>)/, `
1347
+ ${filter}$1`);
1348
+ }
1470
1349
  #setFeaturesInAndroid(features) {
1471
1350
  for (const feature of features) {
1472
- if (this.#hasFeatureInAndroid(feature)) {
1351
+ if (this.#androidManifestNames("uses-feature").includes(feature)) {
1473
1352
  this.app.logger.info(`${feature} already exists in android`);
1474
- return this;
1353
+ return;
1475
1354
  }
1476
1355
  this.app.logger.info(`Adding ${feature} to android`);
1477
1356
  this.project.android.getAndroidManifest().injectFragment("manifest", `<uses-feature android:name="${feature}" />`);
1478
1357
  }
1479
- return this;
1480
- }
1481
- #getFeaturesInAndroid() {
1482
- const androidManifest = this.project.android.getAndroidManifest();
1483
- const element = androidManifest.getDocumentElement();
1484
- if (!element)
1485
- throw new Error("manifest not found");
1486
- const usesFeature = element.getElementsByTagName("uses-feature");
1487
- return Array.from(usesFeature).map((feature) => feature.getAttribute("android:name"));
1488
- }
1489
- #hasFeatureInAndroid(feature) {
1490
- return this.#getFeaturesInAndroid().includes(feature);
1491
1358
  }
1492
1359
  #setPermissionsInAndroid(permissions) {
1493
1360
  for (const permission of permissions) {
1494
- if (this.#hasPermissionInAndroid(permission)) {
1361
+ if (this.#androidManifestNames("uses-permission").includes(`android.permission.${permission}`)) {
1495
1362
  this.app.logger.info(`${permission} already exists in android`);
1496
1363
  continue;
1497
1364
  }
1498
1365
  this.app.logger.info(`Adding ${permission} to android`);
1499
1366
  this.project.android.getAndroidManifest().injectFragment("manifest", `<uses-permission android:name="android.permission.${permission}" />`);
1500
1367
  }
1501
- return this;
1502
1368
  }
1503
- #getPermissionsInAndroid() {
1504
- const androidManifest = this.project.android.getAndroidManifest();
1505
- const element = androidManifest.getDocumentElement();
1369
+ #androidManifestNames(tagName) {
1370
+ const element = this.project.android.getAndroidManifest().getDocumentElement();
1506
1371
  if (!element)
1507
1372
  throw new Error("manifest not found");
1508
- const usesPermission = element.getElementsByTagName("uses-permission");
1509
- return Array.from(usesPermission).map((permission) => permission.getAttribute("android:name"));
1510
- }
1511
- #hasPermissionInAndroid(permission) {
1512
- return this.#getPermissionsInAndroid().includes(`android.permission.${permission}`);
1373
+ return Array.from(element.getElementsByTagName(tagName)).map((node) => node.getAttribute("android:name"));
1513
1374
  }
1514
1375
  }
1515
1376