@akanjs/cli 3.0.0-beta.29 → 3.0.0-beta.30

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 (61) hide show
  1. package/.build-stamp +1 -1
  2. package/{CodeAgent-86q8d13j.js → CodeAgent-xnfyyz8h.js} +2 -2
  3. package/{CodeTui-r8tgxsyb.js → CodeTui-h66xb1fh.js} +1 -1
  4. package/README.ko.md +72 -0
  5. package/README.md +88 -0
  6. package/{agent.command-q9bmqrag.js → agent.command-c4v7kjff.js} +5 -5
  7. package/akanCode.js +2 -2
  8. package/{application.command-712jd2z8.js → application.command-yzsswrz6.js} +9 -9
  9. package/{applicationBuildRunner-ez37p161.js → applicationBuildRunner-qkt6spd5.js} +3 -3
  10. package/buildBatch.proc.js +3 -3
  11. package/{cloud.command-8eegwcsc.js → cloud.command-v8h688jc.js} +9 -9
  12. package/cloud.script-yct1sk4k.js +20 -0
  13. package/{code.command-n0c2tyzx.js → code.command-jff3tw7y.js} +6 -6
  14. package/{context.command-vxf6d7q2.js → context.command-y5sevk3c.js} +14 -14
  15. package/context.runner-90hsmacr.js +21 -0
  16. package/{guideline.command-ene55fkr.js → guideline.command-11cndccb.js} +2 -2
  17. package/guidelines/conventions/conventions.instruction.md +27 -14
  18. package/guidelines/runtimeRule/runtimeRule.instruction.md +90 -17
  19. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +20 -0
  20. package/incrementalBuilder.proc.js +2 -2
  21. package/{index-2z30v4k5.js → index-0ncc0mbk.js} +3 -3
  22. package/{index-2yvy9ztj.js → index-167c77aq.js} +4 -4
  23. package/{index-nbg9bfnr.js → index-35mmm9ca.js} +1 -1
  24. package/{index-fy42hyb9.js → index-376gpdds.js} +5 -5
  25. package/{index-n9gxx7bp.js → index-3rh7thcx.js} +13 -13
  26. package/{index-rrt9pnb4.js → index-5rp0h6na.js} +2 -2
  27. package/{index-ttn00pr3.js → index-6x9p8dpb.js} +1 -1
  28. package/{index-8ej6r02m.js → index-8tv7356t.js} +1 -1
  29. package/{index-k7809wmg.js → index-a4s954kq.js} +2 -2
  30. package/{index-rbeahw6j.js → index-bxmejydw.js} +3 -3
  31. package/{index-qtz88kh0.js → index-gxamdf18.js} +2 -2
  32. package/{index-20r2g1t3.js → index-hf6f4xnb.js} +4 -4
  33. package/{index-2s2e5qnd.js → index-hmdq5pqm.js} +1 -1
  34. package/{index-16k7m3cs.js → index-hqd0h1pb.js} +10 -10
  35. package/{index-1hs6jrys.js → index-kb16nzyt.js} +36 -33
  36. package/{index-krwz9ek9.js → index-mk3b9rk1.js} +3 -3
  37. package/{index-r8gp8zk3.js → index-n3jk2501.js} +155 -152
  38. package/{index-g44da4r2.js → index-qcejqgts.js} +5 -5
  39. package/{index-h9ed99ax.js → index-rtwhw4aq.js} +7 -7
  40. package/{index-ss828r32.js → index-se643zvp.js} +1 -1
  41. package/{index-2ctqegdm.js → index-tsb0f8ya.js} +2 -2
  42. package/{index-0pqzrbpr.js → index-tzc1gtrg.js} +2 -2
  43. package/{index-genegfnt.js → index-wfwgcnyc.js} +4 -4
  44. package/{index-b68zfsvz.js → index-xb7qhxa8.js} +1 -1
  45. package/index.js +21 -21
  46. package/{library.command-tz7vnwea.js → library.command-n9kp7yms.js} +3 -3
  47. package/{localRegistry.command-7dd85czz.js → localRegistry.command-jx6kxt2x.js} +8 -8
  48. package/{module.command-qhc9ns0s.js → module.command-q2xjf6cx.js} +5 -5
  49. package/{package.command-z3epnvas.js → package.command-q5a3nsw5.js} +3 -3
  50. package/package.json +3 -3
  51. package/{page.command-exd2vakz.js → page.command-ppenrxjn.js} +3 -3
  52. package/{primitive.command-0w6cha4j.js → primitive.command-dvgg8nem.js} +6 -6
  53. package/{quality.command-je4kc9yv.js → quality.command-pz7kg3pc.js} +2 -2
  54. package/{repair.command-knvwfgtg.js → repair.command-a6py9xvz.js} +5 -5
  55. package/{scalar.command-60nxfchc.js → scalar.command-7f3xfpen.js} +4 -4
  56. package/{subspace.command-683qxcw9.js → subspace.command-z7nqs508.js} +4 -4
  57. package/{tunnel.command-7m2kxtat.js → tunnel.command-zy4wbcgt.js} +2 -2
  58. package/{workflow.command-j5tk13ds.js → workflow.command-f142t17s.js} +10 -10
  59. package/{workspace.command-jxdm0wrb.js → workspace.command-gpk1149x.js} +20 -20
  60. package/cloud.script-55s1cmz9.js +0 -20
  61. package/context.runner-70r2fmty.js +0 -21
@@ -2,18 +2,18 @@
2
2
  import {
3
3
  binPlatforms,
4
4
  Executor
5
- } from "./index-r8gp8zk3.js";
5
+ } from "./index-n3jk2501.js";
6
6
 
7
7
  // pkgs/@akanjs/devkit/mobile/mobileTarget.ts
8
8
  var trimSlashes = (value) => value.replace(/^\/+|\/+$/g, "");
9
9
  var getMobileTargets = async (app) => {
10
10
  const config = await app.getConfig();
11
- return Object.entries(config.mobile.targets).map(([name, target]) => ({ name, config: target }));
11
+ return Object.entries(config.native.targets).map(([name, target]) => ({ name, config: target }));
12
12
  };
13
- var isTemplateOnly = ({ basePaths, mobile }) => basePaths.size > 0 && Object.values(mobile.targets).every((target) => !target.basePath);
13
+ var isTemplateOnly = ({ basePaths, native }) => basePaths.size > 0 && Object.values(native.targets).every((target) => !target.basePath);
14
14
  var getMobileTargetChoices = async (app) => {
15
15
  const config = await app.getConfig();
16
- const targetNames = Object.keys(config.mobile.targets);
16
+ const targetNames = Object.keys(config.native.targets);
17
17
  if (targetNames.length > 0 && !isTemplateOnly(config))
18
18
  return targetNames;
19
19
  return [...config.basePaths];
@@ -35,17 +35,17 @@ var resolveMobileTargets = async (app, selection) => {
35
35
  const config = await app.getConfig();
36
36
  const targets = await getMobileTargets(app);
37
37
  if (targets.length === 0)
38
- throw new Error(`No mobile targets configured for ${app.name}`);
38
+ throw new Error(`No native targets configured for ${app.name}`);
39
39
  if (!selection && targets.length === 1 && !isTemplateOnly(config))
40
40
  return targets;
41
41
  if (!selection) {
42
42
  const choices = await getMobileTargetChoices(app);
43
43
  if (choices.length === 1)
44
44
  return resolveMobileTargets(app, choices[0]);
45
- throw new Error(`Multiple mobile targets found for ${app.name}. Pass --target <${choices.join("|")}|all>.`);
45
+ throw new Error(`Multiple native targets found for ${app.name}. Pass --target <${choices.join("|")}|all>.`);
46
46
  }
47
47
  if (selection === "all") {
48
- if (Object.keys(config.mobile.targets).length > 1)
48
+ if (Object.keys(config.native.targets).length > 1)
49
49
  return targets;
50
50
  const basePaths = [...config.basePaths];
51
51
  if (basePaths.length > 1 || isTemplateOnly(config)) {
@@ -63,14 +63,14 @@ var resolveMobileTargets = async (app, selection) => {
63
63
  if (basePathTarget && config.basePaths.has(trimSlashes(selection)))
64
64
  return [basePathTarget];
65
65
  const choices = await getMobileTargetChoices(app);
66
- throw new Error(`Mobile target '${selection}' was not found. Available: ${choices.join(", ")}`);
66
+ throw new Error(`Native target '${selection}' was not found. Available: ${choices.join(", ")}`);
67
67
  };
68
68
  var resolveAppId = (appId, platform) => {
69
69
  if (typeof appId === "string")
70
70
  return appId;
71
71
  const id = appId[platform] ?? appId.default;
72
72
  if (!id)
73
- throw new Error(`mobile.appId names no id for ${platform}; give it ${platform} or default.`);
73
+ throw new Error(`native.appId names no id for ${platform}; give it ${platform} or default.`);
74
74
  return id;
75
75
  };
76
76
  var appIdsOf = (appId) => typeof appId === "string" ? [appId] : Object.values(appId).filter((id) => !!id);
@@ -378,7 +378,7 @@ class NativeConfig {
378
378
  const updates = NativeConfig.#updates(target, env);
379
379
  const folders = new Set(nativePlugins.map((plugin) => path3.resolve(plugin.dir)));
380
380
  const folderIds = new Set(nativePlugins.map((plugin) => plugin.id));
381
- const listed = (target.native?.plugins ?? []).filter((spec) => NativeConfig.#isId(spec) ? !folderIds.has(spec) : !folders.has(path3.resolve(appPath, spec)));
381
+ const listed = (target.plugins ?? []).filter((spec) => NativeConfig.#isId(spec) ? !folderIds.has(spec) : !folders.has(path3.resolve(appPath, spec)));
382
382
  const plugins = [
383
383
  ...new Set([
384
384
  ...NativeConfig.basePlugins,
@@ -393,27 +393,30 @@ class NativeConfig {
393
393
  const pluginIds = plugins.map((spec) => nativePlugins.find((plugin) => plugin.dir === spec)?.id ?? NativeConfig.#pluginId(appPath, spec));
394
394
  const usageDescriptions = Object.fromEntries(Object.entries(toIosInfoPlistUsageDescriptions(Object.assign({}, ...applied.map((contribution) => contribution.usageDescriptions ?? {})))).map(([key, text]) => [key, text.replaceAll("$(PRODUCT_NAME)", target.appName)]));
395
395
  const ios = NativeConfig.#compact({
396
- infoPlist: NativeConfig.#merged(applied.map((c) => c.infoPlist), target.native?.ios?.infoPlist),
397
- entitlements: NativeConfig.#merged(applied.map((c) => c.entitlements), target.native?.ios?.entitlements)
396
+ infoPlist: NativeConfig.#merged(applied.map((c) => c.infoPlist), target.ios?.infoPlist),
397
+ entitlements: NativeConfig.#merged(applied.map((c) => c.entitlements), target.ios?.entitlements)
398
398
  });
399
399
  const manifest = [
400
400
  ...[...new Set(applied.flatMap((c) => c.androidPermissions ?? []))].map((name) => `<uses-permission android:name="android.permission.${name}" />`),
401
401
  ...[...new Set(applied.flatMap((c) => c.androidFeatures ?? []))].map((name) => `<uses-feature android:name="${name}" android:required="false" />`),
402
- ...target.native?.android?.manifest ?? []
402
+ ...target.android?.manifest ?? []
403
403
  ];
404
404
  const android = NativeConfig.#compact({
405
405
  manifest,
406
- application: target.native?.android?.application ?? [],
407
- activity: target.native?.android?.activity ?? []
406
+ application: target.android?.application ?? [],
407
+ activity: target.android?.activity ?? []
408
408
  });
409
- const resources = Object.entries(target.files ?? {}).map(([to, from]) => ({ from: abs(from), to }));
409
+ const resources = [
410
+ ...Object.entries(target.ios?.files ?? {}).map(([to, from]) => ({ from: abs(from), to: `ios/${to}` })),
411
+ ...Object.entries(target.android?.files ?? {}).map(([to, from]) => ({ from: abs(from), to: `android/${to}` }))
412
+ ];
410
413
  const native = NativeConfig.#compact({ ios, android, resources });
411
- const googleServices = target.native?.android?.googleServices;
412
- const pushAndroid = target.native?.push?.android;
414
+ const googleServices = target.android?.googleServices;
415
+ const pushAndroid = target.android?.push;
413
416
  const desktop = NativeConfig.#compact({
414
- recovery: target.native?.desktop?.recovery,
415
- screenCapture: target.native?.desktop?.screenCapture,
416
- window: NativeConfig.#compact({ ...target.native?.desktop?.window }),
417
+ recovery: target.desktop?.recovery,
418
+ screenCapture: target.desktop?.screenCapture,
419
+ window: NativeConfig.#compact({ ...target.desktop?.window }),
417
420
  server: desktopServer,
418
421
  bin: desktopBin
419
422
  });
@@ -443,16 +446,16 @@ class NativeConfig {
443
446
  android: { ...pushAndroid, ...pushAndroid.smallIcon ? { smallIcon: abs(pushAndroid.smallIcon) } : {} }
444
447
  }
445
448
  } : {},
446
- ...target.native?.privacy ? { privacy: target.native.privacy } : {},
449
+ ...target.ios?.privacy ? { privacy: target.ios.privacy } : {},
447
450
  android: {
448
451
  debugAppIdSuffix: ".debug",
449
452
  ...googleServices ? { googleServices: abs(googleServices) } : {},
450
- ...target.native?.android?.autoplay ? { autoplay: true } : {}
453
+ ...target.android?.autoplay ? { autoplay: true } : {}
451
454
  },
452
455
  keyboard: { resize: "none" },
453
456
  ...desktop ? { desktop } : {},
454
- ...target.assets?.icon ? { icon: NativeConfig.#icon(target.assets.icon, abs) } : {},
455
- ...target.assets?.splash ? { splash: NativeConfig.#splash(target.assets.splash, abs) } : {}
457
+ ...target.icon ? { icon: NativeConfig.#icon(target.icon, abs) } : {},
458
+ ...target.splash ? { splash: NativeConfig.#splash(target.splash, abs) } : {}
456
459
  };
457
460
  return { config, warnings };
458
461
  }
@@ -578,7 +581,7 @@ ${script}${html.slice(at)}`;
578
581
  }
579
582
  async assemble(target, { html, publicDir, fontsDir }) {
580
583
  if (!await Bun.file(html).exists())
581
- throw new Error(`CSR html for mobile target '${target.name}' not found: ${html}`);
584
+ throw new Error(`CSR html for native target '${target.name}' not found: ${html}`);
582
585
  await this.clear();
583
586
  if (publicDir && await NativeWebDir.#isDir(publicDir))
584
587
  await cp(publicDir, this.dir, { recursive: true });
@@ -611,11 +614,11 @@ class NativeApp {
611
614
  this.app = app;
612
615
  this.target = target;
613
616
  this.env = env;
614
- this.targetRoot = path6.join(app.cwdPath, ".akan", "mobile", target.name);
617
+ this.targetRoot = path6.join(app.cwdPath, ".akan", "native", target.name);
615
618
  this.web = new NativeWebDir(path6.join(this.targetRoot, "web"));
616
619
  }
617
620
  outDir(platform) {
618
- return path6.join(this.targetRoot, "native", platform);
621
+ return path6.join(this.targetRoot, "build", platform);
619
622
  }
620
623
  devOutDir(platform) {
621
624
  return path6.join(this.targetRoot, "dev", platform);
@@ -669,7 +672,7 @@ class NativeApp {
669
672
  #assertValid(api, config) {
670
673
  const problems = api.validateConfig(config, { appDir: this.app.cwdPath });
671
674
  if (problems.length)
672
- throw new Error(`Mobile target '${this.target.name}' makes an invalid native config:
675
+ throw new Error(`Native target '${this.target.name}' makes an invalid native config:
673
676
  - ${problems.join(`
674
677
  - `)}`);
675
678
  }
@@ -807,14 +810,14 @@ class NativeApp {
807
810
  ...channel ? { channel } : {}
808
811
  });
809
812
  } catch (error) {
810
- throw new Error(`Mobile target '${this.target.name}': ${error instanceof Error ? error.message : String(error)}`, {
813
+ throw new Error(`Native target '${this.target.name}': ${error instanceof Error ? error.message : String(error)}`, {
811
814
  cause: error
812
815
  });
813
816
  }
814
817
  }
815
818
  #assertUpdates() {
816
819
  if (!this.target.config.updates)
817
- throw new Error(`Mobile target '${this.target.name}' has no updates: { url, publicKey } in akan.config.ts; \`akan update-keygen ${this.app.name}\` prints the key.`);
820
+ throw new Error(`Native target '${this.target.name}' has no updates: add native.updates: { url, publicKey } to akan.config.ts; \`akan update-keygen ${this.app.name}\` prints the key.`);
818
821
  }
819
822
  async updateKeygen(platform) {
820
823
  const [api, { config }] = await Promise.all([
@@ -833,7 +836,7 @@ class NativeApp {
833
836
  await this.assembleWeb();
834
837
  const { api, config } = await this.prepare(platform);
835
838
  if (!config.updates)
836
- throw new Error(`Mobile target '${this.target.name}' takes no updates: add mobile.updates: { url, publicKey } to akan.config.ts first.`);
839
+ throw new Error(`Native target '${this.target.name}' takes no updates: add native.updates: { url, publicKey } to akan.config.ts first.`);
837
840
  const packed = await api.packUpdate({
838
841
  ...this.#task(platform, config),
839
842
  platform,
@@ -929,7 +932,7 @@ class DesktopServerStage {
929
932
  }
930
933
  static assertCarriable(config) {
931
934
  if (!config.database.modes.includes("single"))
932
- throw new Error(`native.desktop.server puts the server in the app, where only database mode single runs (no Redis or Postgres); apps/${config.app.name}/akan.config.ts declares ${config.database.modes.join(", ")}.`);
935
+ throw new Error(`desktop.server in native puts the server in the app, where only database mode single runs (no Redis or Postgres); apps/${config.app.name}/akan.config.ts declares ${config.database.modes.join(", ")}.`);
933
936
  }
934
937
  static env(config, environment) {
935
938
  const { PORT: _port, AKAN_LOG_TO_FILE: _fileLog, ...image } = config.getProductionEnv(environment);
@@ -4,7 +4,7 @@ import {
4
4
  akanCodePaths2,
5
5
  McpServerConfig,
6
6
  McpSignIn
7
- } from "./index-8ej6r02m.js";
7
+ } from "./index-8tv7356t.js";
8
8
  import {
9
9
  stripAnsi
10
10
  } from "./index-7md2g9vw.js";
@@ -511,7 +511,7 @@ class AkanToolPack2 {
511
511
  return { name: "akan-tools", factory: async (pi) => await this.#register(pi) };
512
512
  }
513
513
  async names() {
514
- const { ContextRunner } = await import("./context.runner-70r2fmty.js");
514
+ const { ContextRunner } = await import("./context.runner-90hsmacr.js");
515
515
  return [
516
516
  ...new ContextRunner().listMcpTools(this.#options.mode).map((tool) => tool.name),
517
517
  AkanToolPack2.verifyToolName
@@ -519,7 +519,7 @@ class AkanToolPack2 {
519
519
  }
520
520
  static verifyToolName = "akan_verify";
521
521
  async#register(pi) {
522
- const { ContextRunner } = await import("./context.runner-70r2fmty.js");
522
+ const { ContextRunner } = await import("./context.runner-90hsmacr.js");
523
523
  const runner = new ContextRunner;
524
524
  const guidelineNames = await AkanToolPack2.#guidelineNames();
525
525
  for (const tool of runner.listMcpTools(this.#options.mode, { guidelineNames })) {
@@ -799,20 +799,11 @@ var normalizeDeepLinkDomain = (domain) => {
799
799
  }
800
800
  };
801
801
  var normalizeDeepLinks = (deepLinks) => {
802
- if (!deepLinks)
802
+ const schemes = normalizeStringList(deepLinks?.schemes);
803
+ const domains = normalizeStringList(deepLinks?.domains?.map(normalizeDeepLinkDomain));
804
+ if (!schemes && !domains)
803
805
  return;
804
- const schemes = normalizeStringList(deepLinks.schemes);
805
- const domains = normalizeStringList(deepLinks.domains?.map(normalizeDeepLinkDomain));
806
- const teamId = deepLinks.ios?.teamId?.trim();
807
- const sha256CertFingerprints = normalizeStringList(deepLinks.android?.sha256CertFingerprints);
808
- if (!schemes && !domains && !teamId && !sha256CertFingerprints)
809
- return;
810
- return {
811
- ...schemes ? { schemes } : {},
812
- ...domains ? { domains } : {},
813
- ...teamId ? { ios: { teamId } } : {},
814
- ...sha256CertFingerprints ? { android: { sha256CertFingerprints } } : {}
815
- };
806
+ return { ...schemes ? { schemes } : {}, ...domains ? { domains } : {} };
816
807
  };
817
808
  var emptyLibContributions = () => ({
818
809
  externalLibs: [],
@@ -855,8 +846,8 @@ class AkanAppConfig {
855
846
  i18n;
856
847
  api;
857
848
  publicEnv;
858
- mobile;
859
- hasMobileConfig;
849
+ native;
850
+ hasNativeConfig;
860
851
  secrets;
861
852
  assets;
862
853
  syncPageLibs;
@@ -912,155 +903,167 @@ class AkanAppConfig {
912
903
  ]
913
904
  };
914
905
  this.syncPageLibs = config?.syncPageLibs ?? false;
915
- this.hasMobileConfig = Boolean(config.mobile);
916
- this.mobile = this.#resolveMobileConfig(config.mobile);
906
+ if (config.mobile !== undefined)
907
+ throw new Error(`apps/${this.app.name}/akan.config.ts declares \`mobile\`, which is now \`native\`: the platform settings sit in native.ios, native.android and native.desktop, and a target overrides them under native.targets.<name>.`);
908
+ this.hasNativeConfig = Boolean(config.native);
909
+ this.native = this.#resolveNativeConfig(config.native);
917
910
  this.web = this.#resolveWebConfig(config.web);
918
911
  this.docker = AkanAppConfig.#resolveDocker(config.docker, libContributions.docker);
919
912
  this.dockerfile = this.#makeDockerfile();
920
913
  }
921
914
  #resolveWebConfig(web) {
922
915
  const resolved = typeof web === "object" ? { ssr: true, csr: web.csr } : { ssr: web ?? true, csr: web ?? true };
923
- if (!resolved.csr && this.hasMobileConfig)
924
- throw new Error(`apps/${this.app.name}/akan.config.ts turns the CSR bundle off but declares mobile targets; the mobile apps ship that bundle. Drop the mobile section or leave CSR on.`);
916
+ if (!resolved.csr && this.hasNativeConfig)
917
+ throw new Error(`apps/${this.app.name}/akan.config.ts turns the CSR bundle off but declares a native section; the native apps ship that bundle. Drop the native section or leave CSR on.`);
925
918
  return resolved;
926
919
  }
927
- #resolveMobileConfig(mobile) {
928
- const {
929
- targets: rawTargets,
930
- indexPath: _indexPath,
931
- ...rawMobile
932
- } = mobile ?? {};
920
+ #resolveNativeConfig(native) {
933
921
  const configPath = `apps/${this.app.name}/akan.config.ts`;
934
- AkanAppConfig.#assertMobileKeys(rawMobile, AkanAppConfig.#mobileKeys, "mobile", configPath);
935
- const appName = rawMobile.appName ?? this.app.name;
936
- const appId = rawMobile.appId ?? deriveDefaultAppId(this.baseDevEnv.repoName, this.app.name);
937
- const version = rawMobile.version ?? "0.0.1";
938
- const buildNum = rawMobile.buildNum ?? 1;
939
- const files = AkanAppConfig.#resolveMobileFiles(rawMobile.files, "mobile", configPath);
940
- const native = rawMobile.native;
941
- const updates = rawMobile.updates;
942
- const defaultTargetName = this.#defaultMobileTargetName(rawTargets);
943
- const targetEntries = Object.entries(rawTargets ?? {
944
- [defaultTargetName]: {}
945
- });
946
- const targets = Object.fromEntries(targetEntries.map(([name, rawTarget]) => {
947
- const target = rawTarget ?? {};
948
- const where = `mobile.targets.${name}`;
949
- AkanAppConfig.#assertMobileKeys(target, AkanAppConfig.#targetKeys, where, configPath);
950
- const fallbackBasePath = !rawTargets && this.basePaths.has(name) ? name : undefined;
951
- const basePath = (target.basePath ?? fallbackBasePath)?.replace(/^\/+|\/+$/g, "") || undefined;
952
- const indexPath = normalizeIndexPath(target.indexPath);
953
- const deepLinks = normalizeDeepLinks(target.deepLinks);
954
- if (basePath && !this.basePaths.has(basePath)) {
955
- throw new Error(`Mobile target '${name}' uses unknown basePath '${basePath}' in ${configPath}`);
956
- }
957
- const targetFiles = AkanAppConfig.#resolveMobileFiles(target.files, where, configPath);
958
- const fileName = target.fileName ?? rawMobile.fileName;
959
- const targetUpdates = updates || target.updates ? { ...updates, ...target.updates } : undefined;
960
- if (targetUpdates && (!targetUpdates.url || !targetUpdates.publicKey))
961
- throw new Error(`${where}.updates in ${configPath} has no url or publicKey; give them in mobile.updates or the target's own.`);
962
- const resolved = {
922
+ const { targets: rawTargets, ...root } = native ?? {};
923
+ AkanAppConfig.#assertNativeKeys(root, "native", configPath);
924
+ AkanAppConfig.#assertFiles(root, "native", configPath);
925
+ const appName = root.appName ?? this.app.name;
926
+ const appId = root.appId ?? deriveDefaultAppId(this.baseDevEnv.repoName, this.app.name);
927
+ const version = root.version ?? "0.0.1";
928
+ const buildNum = root.buildNum ?? 1;
929
+ const named = rawTargets !== undefined && Object.keys(rawTargets).length > 0;
930
+ const entries = named ? Object.entries(rawTargets) : [[this.basePaths.has(this.app.name) ? this.app.name : "default", {}]];
931
+ const targets = Object.fromEntries(entries.map(([name, own]) => {
932
+ const where = named ? `native.targets.${name}` : "native";
933
+ AkanAppConfig.#assertNativeKeys(own ?? {}, where, configPath);
934
+ const merged = AkanAppConfig.#overlay(root, own ?? {});
935
+ const fallbackBasePath = !named && this.basePaths.has(name) ? name : undefined;
936
+ const basePath = (merged.basePath ?? fallbackBasePath)?.replace(/^\/+|\/+$/g, "") || undefined;
937
+ if (basePath && !this.basePaths.has(basePath))
938
+ throw new Error(`Native target '${name}' uses unknown basePath '${basePath}' in ${configPath}`);
939
+ const { updates } = merged;
940
+ if (updates && (!updates.url || !updates.publicKey))
941
+ throw new Error(`${where}.updates in ${configPath} has no url or publicKey${named ? "; give them in native.updates or the target's own" : ""}.`);
942
+ AkanAppConfig.#assertFiles(own ?? {}, where, configPath);
943
+ const {
944
+ basePath: _basePath,
945
+ indexPath: rawIndexPath,
946
+ deepLinks: rawDeepLinks,
947
+ updates: _updates,
948
+ ...rest
949
+ } = merged;
950
+ const indexPath = normalizeIndexPath(rawIndexPath);
951
+ const deepLinks = normalizeDeepLinks(rawDeepLinks);
952
+ const { teamId: rawTeamId, ...iosRest } = merged.ios ?? {};
953
+ const teamId = rawTeamId?.trim();
954
+ const { sha256CertFingerprints: rawFingerprints, ...androidRest } = merged.android ?? {};
955
+ const fingerprints = normalizeStringList(rawFingerprints);
956
+ const target = {
957
+ ...rest,
963
958
  name,
964
- basePath,
965
- indexPath,
966
- deepLinks,
967
- appName: target.appName ?? appName,
968
- appId: target.appId ?? appId,
969
- ...fileName ? { fileName } : {},
970
- version: target.version ?? version,
971
- buildNum: target.buildNum ?? buildNum,
972
- ...target.assets ? { assets: target.assets } : {},
973
- ...target.permissions ? { permissions: target.permissions } : {},
974
- ...files || targetFiles ? { files: { ...files, ...targetFiles } } : {},
975
- ...native || target.native ? { native: AkanAppConfig.#mergeNative(native, target.native) } : {},
976
- ...targetUpdates ? { updates: targetUpdates } : {}
959
+ ...basePath ? { basePath } : {},
960
+ ...indexPath ? { indexPath } : {},
961
+ ...deepLinks ? { deepLinks } : {},
962
+ ...merged.ios ? { ios: { ...iosRest, ...teamId ? { teamId } : {} } } : {},
963
+ ...merged.android ? { android: { ...androidRest, ...fingerprints ? { sha256CertFingerprints: fingerprints } : {} } } : {},
964
+ appName: merged.appName ?? this.app.name,
965
+ appId: merged.appId ?? appId,
966
+ version: merged.version ?? version,
967
+ buildNum: merged.buildNum ?? buildNum,
968
+ ...updates ? { updates } : {}
977
969
  };
978
- return [name, resolved];
970
+ return [name, target];
979
971
  }));
980
- return {
981
- appName,
982
- appId,
983
- ...rawMobile.fileName ? { fileName: rawMobile.fileName } : {},
984
- version,
985
- buildNum,
986
- ...files ? { files } : {},
987
- ...native ? { native } : {},
988
- ...updates ? { updates } : {},
989
- targets
990
- };
991
- }
992
- static #mobileKeys = ["appName", "appId", "fileName", "version", "buildNum", "files", "native", "updates"];
993
- static #targetKeys = [
994
- "basePath",
995
- "indexPath",
996
- "appName",
997
- "appId",
998
- "fileName",
999
- "version",
1000
- "buildNum",
1001
- "assets",
1002
- "permissions",
1003
- "deepLinks",
1004
- "files",
1005
- "native",
1006
- "updates"
1007
- ];
1008
- static #assertMobileKeys(section, known, where, configPath) {
1009
- for (const key of Object.keys(section)) {
1010
- if (known.includes(key))
1011
- continue;
1012
- if (key === "plugins" || key === "ios" || key === "android")
1013
- throw new Error(`${where}.${key} in ${configPath} is a Capacitor setting. The native runtime reads native.plugins, native.ios.{infoPlist,entitlements} and native.android.{manifest,application,activity,googleServices} instead.`);
1014
- throw new Error(`${where}.${key} in ${configPath} is not a mobile setting. Known: ${known.join(", ")}.`);
1015
- }
1016
- }
1017
- static #fileTargetPattern = /^(?:ios\/.+|android\/res\/[^/]+\/[^/]+|android\/assets\/.+)$/;
1018
- static #resolveMobileFiles(files, where, configPath) {
1019
- if (!files)
1020
- return;
1021
- for (const [to, from] of Object.entries(files)) {
1022
- if (typeof from !== "string")
1023
- throw new Error(`${where}.files in ${configPath} maps where a file lands to its source now, e.g. { "android/res/raw/chime.mp3": "assets/chime.mp3" }. google-services.json goes to native.android.googleServices, and GoogleService-Info.plist is no longer needed.`);
1024
- if (!AkanAppConfig.#fileTargetPattern.test(to))
1025
- throw new Error(`${where}.files["${to}"] in ${configPath} must land under ios/<path>, android/res/<type>/<file> or android/assets/<path>.`);
1026
- }
1027
- return files;
1028
- }
1029
- static #mergeNative(base, override) {
1030
- const plugins = [...new Set([...base?.plugins ?? [], ...override?.plugins ?? []])];
1031
- const ios = { ...base?.ios, ...override?.ios };
1032
- const android = {
1033
- ...base?.android,
1034
- ...override?.android
972
+ return { appName, appId, ...root.fileName ? { fileName: root.fileName } : {}, version, buildNum, targets };
973
+ }
974
+ static #overlay(base, own) {
975
+ const merge = (kept, value, top, key) => {
976
+ if (!AkanAppConfig.#isPlainObject(kept) || !AkanAppConfig.#isPlainObject(value))
977
+ return value;
978
+ if (top && (key === "icon" || key === "splash"))
979
+ return value;
980
+ const out = { ...kept };
981
+ for (const [inner, next] of Object.entries(value))
982
+ if (next !== undefined)
983
+ out[inner] = merge(out[inner], next, false, inner);
984
+ return out;
1035
985
  };
1036
- for (const key of ["manifest", "application", "activity"]) {
1037
- const merged = [...base?.android?.[key] ?? [], ...override?.android?.[key] ?? []];
1038
- if (merged.length)
1039
- android[key] = merged;
1040
- else
1041
- delete android[key];
1042
- }
1043
- const pushAndroid = { ...base?.push?.android, ...override?.push?.android };
1044
- const privacy = { ...base?.privacy, ...override?.privacy };
1045
- const window = { ...base?.desktop?.window, ...override?.desktop?.window };
1046
- const desktop = {
1047
- ...base?.desktop,
1048
- ...override?.desktop,
1049
- ...Object.keys(window).length ? { window } : {}
986
+ const out = { ...base };
987
+ for (const [key, value] of Object.entries(own))
988
+ if (value !== undefined)
989
+ out[key] = merge(out[key], value, true, key);
990
+ return out;
991
+ }
992
+ static #isPlainObject(value) {
993
+ return typeof value === "object" && value !== null && !Array.isArray(value);
994
+ }
995
+ static #nativeKeys = {
996
+ native: [
997
+ "basePath",
998
+ "indexPath",
999
+ "appName",
1000
+ "appId",
1001
+ "fileName",
1002
+ "version",
1003
+ "buildNum",
1004
+ "icon",
1005
+ "splash",
1006
+ "permissions",
1007
+ "plugins",
1008
+ "deepLinks",
1009
+ "updates",
1010
+ "ios",
1011
+ "android",
1012
+ "desktop"
1013
+ ],
1014
+ ios: ["teamId", "infoPlist", "entitlements", "privacy", "files"],
1015
+ android: [
1016
+ "sha256CertFingerprints",
1017
+ "googleServices",
1018
+ "push",
1019
+ "autoplay",
1020
+ "manifest",
1021
+ "application",
1022
+ "activity",
1023
+ "files"
1024
+ ],
1025
+ desktop: ["server", "recovery", "window", "screenCapture"],
1026
+ deepLinks: ["schemes", "domains"]
1027
+ };
1028
+ static #movedKeys = {
1029
+ native: "the platform sections and plugins sit directly in the native section: ios, android, desktop, plugins",
1030
+ assets: "icon and splash sit directly in the native section",
1031
+ files: "ios.files (keyed by the path in the app bundle) or android.files (keyed res/<type>/<file> or assets/<path>)",
1032
+ push: "android.push",
1033
+ privacy: "ios.privacy",
1034
+ "deepLinks.ios": "ios.teamId",
1035
+ "deepLinks.android": "android.sha256CertFingerprints"
1036
+ };
1037
+ static #assertNativeKeys(section, where, configPath) {
1038
+ const check = (value, known, at, kind) => {
1039
+ if (!AkanAppConfig.#isPlainObject(value))
1040
+ return;
1041
+ for (const key of Object.keys(value)) {
1042
+ if (known.includes(key))
1043
+ continue;
1044
+ const moved = AkanAppConfig.#movedKeys[kind === "native" ? key : `${kind}.${key}`];
1045
+ throw new Error(moved ? `${at}.${key} in ${configPath} has moved: ${moved}.` : `${at}.${key} in ${configPath} is not a native setting. Known: ${known.join(", ")}.`);
1046
+ }
1050
1047
  };
1051
- return {
1052
- ...plugins.length ? { plugins } : {},
1053
- ...Object.keys(ios).length ? { ios } : {},
1054
- ...Object.keys(android).length ? { android } : {},
1055
- ...Object.keys(pushAndroid).length ? { push: { android: pushAndroid } } : {},
1056
- ...Object.keys(privacy).length ? { privacy } : {},
1057
- ...Object.keys(desktop).length ? { desktop } : {}
1048
+ check(section, AkanAppConfig.#nativeKeys.native, where, "native");
1049
+ const settings = section;
1050
+ check(settings.ios, AkanAppConfig.#nativeKeys.ios, `${where}.ios`, "ios");
1051
+ check(settings.android, AkanAppConfig.#nativeKeys.android, `${where}.android`, "android");
1052
+ check(settings.desktop, AkanAppConfig.#nativeKeys.desktop, `${where}.desktop`, "desktop");
1053
+ check(settings.deepLinks, AkanAppConfig.#nativeKeys.deepLinks, `${where}.deepLinks`, "deepLinks");
1054
+ }
1055
+ static #androidFilePattern = /^(?:res\/[^/]+\/[^/]+|assets\/.+)$/;
1056
+ static #assertFiles(settings, where, configPath) {
1057
+ const check = (files, at, valid, shape) => {
1058
+ for (const [to, from] of Object.entries(files ?? {})) {
1059
+ if (typeof from !== "string")
1060
+ throw new Error(`${at}["${to}"] in ${configPath} must name its source file, relative to the app folder, e.g. { "${shape}": "assets/chime.mp3" }.`);
1061
+ if (!valid(to))
1062
+ throw new Error(`${at}["${to}"] in ${configPath} must land at ${shape}.`);
1063
+ }
1058
1064
  };
1059
- }
1060
- #defaultMobileTargetName(rawTargets) {
1061
- if (rawTargets && Object.keys(rawTargets).length > 0)
1062
- return Object.keys(rawTargets)[0];
1063
- return this.basePaths.has(this.app.name) ? this.app.name : "default";
1065
+ check(settings.ios?.files, `${where}.ios.files`, (to) => to.length > 0 && !to.startsWith("/") && !to.split("/").includes(".."), "<path in the app bundle>");
1066
+ check(settings.android?.files, `${where}.android.files`, (to) => AkanAppConfig.#androidFilePattern.test(to), "res/<type>/<file> or assets/<path>");
1064
1067
  }
1065
1068
  #applyRoutes(routes = []) {
1066
1069
  for (const route of routes) {
@@ -1318,7 +1321,7 @@ class AkanLibConfig {
1318
1321
  }
1319
1322
  //! need to refactor
1320
1323
  var shiftBuildNum = async (app, delta) => {
1321
- const { buildNum } = (await AkanAppConfig.from(app)).mobile;
1324
+ const { buildNum } = (await AkanAppConfig.from(app)).native;
1322
1325
  const akanConfigPath = path5.join(app.cwdPath, "akan.config.ts");
1323
1326
  const akanConfig = fs2.readFileSync(akanConfigPath, "utf8");
1324
1327
  fs2.writeFileSync(akanConfigPath, akanConfig.replace(`buildNum: ${buildNum}`, `buildNum: ${buildNum + delta}`));
@@ -1759,7 +1762,7 @@ var retiredAppRootEntries = new Set([
1759
1762
  "capacitor.config.js",
1760
1763
  "capacitor.config.json"
1761
1764
  ]);
1762
- var rootEntryHintOf = (type, name) => type === "app" && retiredAppRootEntries.has(name) ? "a Capacitor-era entry; the native runtime generates its projects under .akan/mobile/<target>, so delete it" : null;
1765
+ var rootEntryHintOf = (type, name) => type === "app" && retiredAppRootEntries.has(name) ? "a Capacitor-era entry; the native runtime generates its projects under .akan/native/<target>, so delete it" : null;
1763
1766
  var isScannedRootEntry = (type, name) => !name.startsWith(".") || rootAllowedDirs[type].has(name);
1764
1767
 
1765
1768
  // pkgs/@akanjs/devkit/scanInfo.ts
@@ -3027,7 +3030,7 @@ class AppExecutor extends SysExecutor {
3027
3030
  static from(executor, name) {
3028
3031
  return new AppExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
3029
3032
  }
3030
- static #keptOnStart = [path9.join(".akan", "mobile"), path9.join(".akan", "cache", "bin")];
3033
+ static #keptOnStart = [path9.join(".akan", "native"), path9.join(".akan", "cache", "bin")];
3031
3034
  async#removeDevOutput(dir = ".akan") {
3032
3035
  const entries = await readDirEntries(this.getPath(dir)).catch(() => []);
3033
3036
  await Promise.all(entries.map(async (name) => {
@@ -5,19 +5,19 @@ import {
5
5
  } from "./index-94s080qm.js";
6
6
  import {
7
7
  script
8
- } from "./index-b68zfsvz.js";
8
+ } from "./index-xb7qhxa8.js";
9
9
  import {
10
10
  PkgExecutor
11
- } from "./index-r8gp8zk3.js";
11
+ } from "./index-n3jk2501.js";
12
12
  import {
13
13
  ApplicationScript
14
- } from "./index-n9gxx7bp.js";
14
+ } from "./index-3rh7thcx.js";
15
15
  import {
16
16
  PackageScript
17
- } from "./index-k7809wmg.js";
17
+ } from "./index-a4s954kq.js";
18
18
  import {
19
19
  CloudRunner
20
- } from "./index-rrt9pnb4.js";
20
+ } from "./index-5rp0h6na.js";
21
21
 
22
22
  // pkgs/@akanjs/cli/cloud/cloud.script.ts
23
23
  import { Logger } from "akanjs/common";