@akanjs/devkit 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.
@@ -50,7 +50,7 @@ const loadExtAppConfig = async (tmpPrefix: string, appConfig: string, libConfig:
50
50
  };
51
51
 
52
52
  describe("AkanAppConfig", () => {
53
- test("applies defaults for route domains, i18n, image, mobile, and imports", () => {
53
+ test("applies defaults for route domains, i18n, image, native, and imports", () => {
54
54
  const config = new AkanAppConfig(app, ["shared"], packageJson, {}, baseDevEnv);
55
55
 
56
56
  expect([...config.domains].sort()).toEqual([
@@ -62,7 +62,7 @@ describe("AkanAppConfig", () => {
62
62
  expect(config.i18n.defaultLocale).toBe("en");
63
63
  expect(config.i18n.locales).toContain("en");
64
64
  expect(config.images.formats).toEqual(["image/webp"]);
65
- expect(config.mobile).toMatchObject({
65
+ expect(config.native).toMatchObject({
66
66
  appName: "portal",
67
67
  appId: "com.akanjs.portal",
68
68
  version: "0.0.1",
@@ -102,7 +102,7 @@ describe("AkanAppConfig", () => {
102
102
  },
103
103
  ],
104
104
  i18n: { locales: ["ko", "en"], defaultLocale: "ko" },
105
- mobile: {
105
+ native: {
106
106
  appName: "Portal App",
107
107
  appId: "com.portal.mobile",
108
108
  version: "1.2.3",
@@ -135,8 +135,8 @@ describe("AkanAppConfig", () => {
135
135
  expect(config.i18n.defaultLocale).toBe("ko");
136
136
  expect(config.images.qualities).toEqual([80, 90]);
137
137
  expect(config.images.dangerouslyAllowSVG).toBe(true);
138
- expect(config.mobile.buildNum).toBe(7);
139
- expect(config.mobile.targets.default).toMatchObject({
138
+ expect(config.native.buildNum).toBe(7);
139
+ expect(config.native.targets.default).toMatchObject({
140
140
  name: "default",
141
141
  appName: "Portal App",
142
142
  appId: "com.portal.mobile",
@@ -241,10 +241,10 @@ describe("AkanAppConfig", () => {
241
241
  expect(lines(new AkanAppConfig(app, [], packageJson, {}, baseDevEnv).dockerfile)).toEqual(lines(before.join("\n")));
242
242
  });
243
243
 
244
- test("refuses a csr-less build that ships a mobile app", () => {
244
+ test("refuses a csr-less build that ships a native app", () => {
245
245
  expect(
246
- () => new AkanAppConfig(app, [], packageJson, { web: { csr: false }, mobile: { appName: "portal" } }, baseDevEnv),
247
- ).toThrow("the mobile apps ship that bundle");
246
+ () => new AkanAppConfig(app, [], packageJson, { web: { csr: false }, native: { appName: "portal" } }, baseDevEnv),
247
+ ).toThrow("the native apps ship that bundle");
248
248
  });
249
249
 
250
250
  test("installs only ca-certificates and tzdata in the default image", () => {
@@ -427,14 +427,14 @@ describe("AkanAppConfig", () => {
427
427
  ]);
428
428
  });
429
429
 
430
- test("normalizes multiple mobile targets and validates base paths", () => {
430
+ test("normalizes the native targets and validates base paths", () => {
431
431
  const config = new AkanAppConfig(
432
432
  app,
433
433
  [],
434
434
  packageJson,
435
435
  {
436
436
  routes: [{ basePath: "admin", domains: {} }],
437
- mobile: {
437
+ native: {
438
438
  appName: "Portal",
439
439
  appId: "com.portal.app",
440
440
  version: "1.0.0",
@@ -447,12 +447,9 @@ describe("AkanAppConfig", () => {
447
447
  appId: "com.portal.admin",
448
448
  buildNum: 8,
449
449
  permissions: ["camera"],
450
- deepLinks: {
451
- schemes: ["portal-admin", "portal-admin"],
452
- domains: ["https://Portal.Admin/"],
453
- ios: { teamId: " TEAMID " },
454
- android: { sha256CertFingerprints: ["AA:BB", "AA:BB"] },
455
- },
450
+ deepLinks: { schemes: ["portal-admin", "portal-admin"], domains: ["https://Portal.Admin/"] },
451
+ ios: { teamId: " TEAMID " },
452
+ android: { sha256CertFingerprints: ["AA:BB", "AA:BB"] },
456
453
  },
457
454
  },
458
455
  },
@@ -460,7 +457,7 @@ describe("AkanAppConfig", () => {
460
457
  baseDevEnv,
461
458
  );
462
459
 
463
- expect(config.mobile.targets.admin).toMatchObject({
460
+ expect(config.native.targets.admin).toMatchObject({
464
461
  name: "admin",
465
462
  basePath: "admin",
466
463
  indexPath: "/admin/home",
@@ -469,46 +466,51 @@ describe("AkanAppConfig", () => {
469
466
  version: "1.0.0",
470
467
  buildNum: 8,
471
468
  permissions: ["camera"],
472
- deepLinks: {
473
- schemes: ["portal-admin"],
474
- domains: ["portal.admin"],
475
- ios: { teamId: "TEAMID" },
476
- android: { sha256CertFingerprints: ["AA:BB"] },
477
- },
469
+ deepLinks: { schemes: ["portal-admin"], domains: ["portal.admin"] },
470
+ ios: { teamId: "TEAMID" },
471
+ android: { sha256CertFingerprints: ["AA:BB"] },
478
472
  });
479
473
 
480
474
  expect(
481
475
  () =>
482
- new AkanAppConfig(
483
- app,
484
- [],
485
- packageJson,
486
- {
487
- mobile: { targets: { bad: { basePath: "missing" } } },
488
- },
489
- baseDevEnv,
490
- ),
476
+ new AkanAppConfig(app, [], packageJson, { native: { targets: { bad: { basePath: "missing" } } } }, baseDevEnv),
491
477
  ).toThrow("unknown basePath");
492
478
  });
493
479
 
494
- test("merges the mobile-wide files and native settings into each target, the target winning", () => {
480
+ test("an app without basePaths has one target, default, with no basePath and the section's settings", () => {
481
+ const config = new AkanAppConfig(
482
+ app,
483
+ [],
484
+ packageJson,
485
+ { native: { indexPath: "/explore", desktop: { server: true } } },
486
+ baseDevEnv,
487
+ );
488
+
489
+ expect(Object.keys(config.native.targets)).toEqual(["default"]);
490
+ expect(config.native.targets.default.basePath).toBeUndefined();
491
+ expect(config.native.targets.default).toMatchObject({ indexPath: "/explore", desktop: { server: true } });
492
+ });
493
+
494
+ test("a target takes the native section with its own fields over it: objects merge, lists and values replace", () => {
495
495
  const config = new AkanAppConfig(
496
496
  app,
497
497
  [],
498
498
  packageJson,
499
499
  {
500
- mobile: {
500
+ native: {
501
501
  fileName: "portal",
502
- files: { "android/res/raw/chime.mp3": "assets/chime.mp3" },
503
- native: {
504
- plugins: ["iap"],
505
- ios: { infoPlist: { ITSAppUsesNonExemptEncryption: false } },
506
- android: { manifest: ["<queries/>"], googleServices: "secrets/google-services.json" },
502
+ plugins: ["iap"],
503
+ ios: { infoPlist: { ITSAppUsesNonExemptEncryption: false }, files: { "sound.caf": "assets/sound.caf" } },
504
+ android: {
505
+ manifest: ["<queries/>"],
506
+ googleServices: "secrets/google-services.json",
507
+ files: { "res/raw/chime.mp3": "assets/chime.mp3" },
507
508
  },
508
509
  targets: {
509
510
  default: {
510
- files: { "ios/sound.caf": "assets/sound.caf" },
511
- native: { plugins: ["iap", "share"], android: { manifest: ["<uses-feature/>"] } },
511
+ plugins: ["share"],
512
+ ios: { files: { "extra.caf": "assets/extra.caf" } },
513
+ android: { manifest: ["<uses-feature/>"] },
512
514
  },
513
515
  },
514
516
  },
@@ -516,44 +518,46 @@ describe("AkanAppConfig", () => {
516
518
  baseDevEnv,
517
519
  );
518
520
 
519
- expect(config.mobile.targets.default).toMatchObject({
521
+ expect(config.native.targets.default).toMatchObject({
520
522
  fileName: "portal",
521
- files: { "android/res/raw/chime.mp3": "assets/chime.mp3", "ios/sound.caf": "assets/sound.caf" },
522
- native: {
523
- plugins: ["iap", "share"],
524
- ios: { infoPlist: { ITSAppUsesNonExemptEncryption: false } },
525
- android: { manifest: ["<queries/>", "<uses-feature/>"], googleServices: "secrets/google-services.json" },
523
+ plugins: ["share"],
524
+ ios: {
525
+ infoPlist: { ITSAppUsesNonExemptEncryption: false },
526
+ files: { "sound.caf": "assets/sound.caf", "extra.caf": "assets/extra.caf" },
527
+ },
528
+ android: {
529
+ manifest: ["<uses-feature/>"],
530
+ googleServices: "secrets/google-services.json",
531
+ files: { "res/raw/chime.mp3": "assets/chime.mp3" },
526
532
  },
527
533
  });
528
534
  });
529
535
 
530
- test("gives every target the mobile-wide updates, a target overriding a field", () => {
536
+ test("gives every target the section's updates, a target overriding a field", () => {
531
537
  const updates = { url: "https://releases.example.com/portal", publicKey: "key=" };
532
538
  const config = new AkanAppConfig(
533
539
  app,
534
540
  [],
535
541
  packageJson,
536
- {
537
- mobile: {
538
- updates,
539
- targets: { default: {}, pilot: { updates: { channel: "pilot" } } },
540
- },
541
- },
542
+ { native: { updates, targets: { default: {}, pilot: { updates: { channel: "pilot" } } } } },
542
543
  baseDevEnv,
543
544
  );
544
545
 
545
- expect(config.mobile.targets.default?.updates).toEqual(updates);
546
- expect(config.mobile.targets.pilot?.updates).toEqual({ ...updates, channel: "pilot" });
546
+ expect(config.native.targets.default.updates).toEqual(updates);
547
+ expect(config.native.targets.pilot.updates).toEqual({ ...updates, channel: "pilot" });
547
548
  expect(
548
549
  () =>
549
550
  new AkanAppConfig(
550
551
  app,
551
552
  [],
552
553
  packageJson,
553
- { mobile: { targets: { pilot: { updates: { channel: "pilot" } } } } },
554
+ { native: { targets: { pilot: { updates: { channel: "pilot" } } } } },
554
555
  baseDevEnv,
555
556
  ),
556
- ).toThrow("mobile.targets.pilot.updates in apps/portal/akan.config.ts has no url or publicKey");
557
+ ).toThrow("native.targets.pilot.updates in apps/portal/akan.config.ts has no url or publicKey");
558
+ expect(
559
+ () => new AkanAppConfig(app, [], packageJson, { native: { updates: { channel: "pilot" } } }, baseDevEnv),
560
+ ).toThrow("native.updates in apps/portal/akan.config.ts has no url or publicKey.");
557
561
  });
558
562
 
559
563
  test("merges the desktop settings field by field, the target winning", () => {
@@ -562,80 +566,93 @@ describe("AkanAppConfig", () => {
562
566
  [],
563
567
  packageJson,
564
568
  {
565
- mobile: {
566
- native: { desktop: { recovery: "reload", window: { fullscreen: true } } },
567
- targets: { default: { native: { desktop: { window: { skipTaskbar: true } } } } },
569
+ native: {
570
+ desktop: { recovery: "reload", window: { fullscreen: true } },
571
+ targets: { default: { desktop: { window: { skipTaskbar: true } } } },
568
572
  },
569
573
  },
570
574
  baseDevEnv,
571
575
  );
572
576
 
573
- expect(config.mobile.targets.default?.native?.desktop).toEqual({
577
+ expect(config.native.targets.default.desktop).toEqual({
574
578
  recovery: "reload",
575
579
  window: { fullscreen: true, skipTaskbar: true },
576
580
  });
577
581
  });
578
582
 
579
- test("merges the mobile-wide updates, push and privacy into each target field by field, the target winning", () => {
583
+ test("merges the section's push and privacy into each target field by field; an icon object is one value", () => {
580
584
  const publicKey = Buffer.alloc(32, 7).toString("base64");
581
585
  const config = new AkanAppConfig(
582
586
  app,
583
587
  [],
584
588
  packageJson,
585
589
  {
586
- mobile: {
590
+ native: {
587
591
  updates: { url: "https://updates.example.com/portal", publicKey },
588
- native: { push: { android: { color: "#ff5a5f" } }, privacy: { tracking: false } },
592
+ icon: { image: "assets/icon.png", backgroundColor: "#000000" },
593
+ android: { push: { color: "#ff5a5f" } },
594
+ ios: { privacy: { tracking: false } },
589
595
  targets: {
590
596
  default: {},
591
597
  beta: {
592
598
  updates: { channel: "beta" },
593
- native: { push: { android: { smallIcon: "assets/noti.png" } } },
599
+ icon: { image: "assets/beta.png" },
600
+ android: { push: { smallIcon: "assets/noti.png" } },
594
601
  },
595
602
  },
596
603
  },
597
- } as never,
604
+ },
598
605
  baseDevEnv,
599
606
  );
600
607
 
601
- expect(config.mobile.targets.default?.updates).toEqual({ url: "https://updates.example.com/portal", publicKey });
602
- expect(config.mobile.targets.beta).toMatchObject({
608
+ expect(config.native.targets.default.updates).toEqual({ url: "https://updates.example.com/portal", publicKey });
609
+ expect(config.native.targets.beta).toMatchObject({
603
610
  updates: { url: "https://updates.example.com/portal", publicKey, channel: "beta" },
604
- native: { push: { android: { color: "#ff5a5f", smallIcon: "assets/noti.png" } }, privacy: { tracking: false } },
611
+ android: { push: { color: "#ff5a5f", smallIcon: "assets/noti.png" } },
612
+ ios: { privacy: { tracking: false } },
605
613
  });
614
+ expect(config.native.targets.beta.icon).toEqual({ image: "assets/beta.png" });
606
615
  });
607
616
 
608
- test("refuses the Capacitor-era keys with the setting that replaces them", () => {
609
- const make = (mobile: Record<string, unknown>) => () =>
610
- new AkanAppConfig(app, [], packageJson, { mobile } as never, baseDevEnv);
617
+ test("refuses `mobile` and every setting that moved, naming where it went", () => {
618
+ const make = (config: Record<string, unknown>) => () =>
619
+ new AkanAppConfig(app, [], packageJson, config as never, baseDevEnv);
611
620
 
612
- expect(make({ plugins: { Keyboard: { resize: "none" } } })).toThrow(
613
- "mobile.plugins in apps/portal/akan.config.ts is a Capacitor setting",
621
+ expect(make({ mobile: { appName: "portal" } })).toThrow("declares `mobile`, which is now `native`");
622
+ expect(make({ native: { assets: { icon: "assets/icon.png" } } })).toThrow(
623
+ "native.assets in apps/portal/akan.config.ts has moved: icon and splash sit directly in the native section.",
624
+ );
625
+ expect(make({ native: { targets: { default: { native: { desktop: { server: true } } } } } })).toThrow(
626
+ "native.targets.default.native in apps/portal/akan.config.ts has moved",
627
+ );
628
+ expect(make({ native: { files: { "ios/sound.caf": "assets/sound.caf" } } })).toThrow(
629
+ "native.files in apps/portal/akan.config.ts has moved: ios.files",
630
+ );
631
+ expect(make({ native: { deepLinks: { ios: { teamId: "TEAMID" } } } })).toThrow(
632
+ "native.deepLinks.ios in apps/portal/akan.config.ts has moved: ios.teamId.",
614
633
  );
615
- expect(make({ targets: { default: { ios: { scheme: "App" } } } })).toThrow(
616
- "mobile.targets.default.ios in apps/portal/akan.config.ts is a Capacitor setting",
634
+ expect(make({ native: { android: { files: { "App/x.json": "x.json" } } } })).toThrow(
635
+ 'native.android.files["App/x.json"] in apps/portal/akan.config.ts must land at res/<type>/<file> or assets/<path>.',
617
636
  );
618
- expect(make({ targets: { default: { files: { android: { "app/google-services.json": "x.json" } } } } })).toThrow(
619
- "google-services.json goes to native.android.googleServices",
637
+ expect(make({ native: { targets: { kiosk: { ios: { files: { "../x.caf": "x.caf" } } } } } })).toThrow(
638
+ 'native.targets.kiosk.ios.files["../x.caf"] in apps/portal/akan.config.ts must land at <path in the app bundle>.',
620
639
  );
621
- expect(make({ files: { "App/App/Info.plist": "x.plist" } })).toThrow(
622
- "must land under ios/<path>, android/res/<type>/<file> or android/assets/<path>",
640
+ expect(make({ native: { ios: { scheme: "App" } } })).toThrow(
641
+ "native.ios.scheme in apps/portal/akan.config.ts is not a native setting",
623
642
  );
624
- expect(make({ server: { url: "http://x" } })).toThrow(
625
- "mobile.server in apps/portal/akan.config.ts is not a mobile setting",
643
+ expect(make({ native: { server: { url: "http://x" } } })).toThrow(
644
+ "native.server in apps/portal/akan.config.ts is not a native setting",
626
645
  );
627
646
  });
628
647
 
629
- test("derives a repo-scoped default appId and records explicit-mobile intent", () => {
630
- // No mobile section: appId defaults to the repo-scoped reverse-DNS id, and mobile is not explicit.
631
- const withoutMobile = new AkanAppConfig(app, [], packageJson, {}, baseDevEnv);
632
- expect(withoutMobile.mobile.appId).toBe("com.akanjs.portal");
633
- expect(withoutMobile.hasMobileConfig).toBe(false);
648
+ test("derives a repo-scoped default appId and records an explicit native section", () => {
649
+ const withoutNative = new AkanAppConfig(app, [], packageJson, {}, baseDevEnv);
650
+ expect(withoutNative.native.appId).toBe("com.akanjs.portal");
651
+ expect(withoutNative.hasNativeConfig).toBe(false);
634
652
 
635
- // Explicit mobile section without appId still uses the repo-scoped default but marks intent.
636
- const withMobile = new AkanAppConfig(app, [], packageJson, { mobile: { version: "2.0.0" } }, baseDevEnv);
637
- expect(withMobile.mobile.appId).toBe("com.akanjs.portal");
638
- expect(withMobile.hasMobileConfig).toBe(true);
653
+ const withNative = new AkanAppConfig(app, [], packageJson, { native: { version: "2.0.0" } }, baseDevEnv);
654
+ expect(withNative.native.appId).toBe("com.akanjs.portal");
655
+ expect(withNative.hasNativeConfig).toBe(true);
639
656
  });
640
657
  });
641
658