@absolutejs/absolute 0.20.0-beta.25 → 0.20.0-beta.27

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.
@@ -589,6 +589,272 @@ var init_syncSchema = __esm(() => {
589
589
  init_client();
590
590
  });
591
591
 
592
+ // src/mobile/deviceCapabilities.ts
593
+ import { readFileSync as readFileSync4 } from "fs";
594
+ import { extname as extname3, join as join13, relative as relative9, resolve as resolve11 } from "path";
595
+ import ts from "typescript";
596
+ var DEVICES_PACKAGE = "@absolutejs/devices", CAPACITOR_ADAPTER = "@absolutejs/devices-capacitor", SOURCE_GLOB, IGNORED_DIRECTORIES, IDENTIFIER_PATTERN, CAPACITOR_MODULE_PATTERN, CAPACITOR_PACKAGE_PATTERN, ANDROID_PERMISSION_PATTERN, IOS_USAGE_DESCRIPTIONS, IOS_PRIVACY_ACCESSED_API_REASONS, IOS_PRIVACY_ACCESSED_APIS, object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
597
+ const value = JSON.parse(readFileSync4(path, "utf8"));
598
+ if (!object2(value))
599
+ throw new TypeError(`${path} must contain an object.`);
600
+ return value;
601
+ }, text = (value, field) => {
602
+ if (typeof value !== "string" || value.length === 0)
603
+ throw new TypeError(`${field} must be a non-empty string.`);
604
+ return value;
605
+ }, androidPermissions = (value, field) => {
606
+ if (value === undefined)
607
+ return;
608
+ if (!object2(value))
609
+ throw new TypeError(`${field} must be an object.`);
610
+ const { permissions } = value;
611
+ if (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string" && ANDROID_PERMISSION_PATTERN.test(permission)))
612
+ throw new TypeError(`${field}.permissions must contain Android permission names.`);
613
+ return [...permissions];
614
+ }, iosPrivacyAccessedApis = (value, field) => {
615
+ if (value === undefined)
616
+ return;
617
+ if (!object2(value))
618
+ throw new TypeError(`${field} must be an object.`);
619
+ const privacy = {};
620
+ for (const api of IOS_PRIVACY_ACCESSED_APIS) {
621
+ const reasons = value[api];
622
+ if (reasons === undefined)
623
+ continue;
624
+ const supported = IOS_PRIVACY_ACCESSED_API_REASONS[api];
625
+ if (!Array.isArray(reasons) || reasons.length === 0 || !reasons.every((reason) => typeof reason === "string" && supported.has(reason)))
626
+ throw new TypeError(`${field} contains an unsupported API or reason.`);
627
+ privacy[api] = [...reasons];
628
+ }
629
+ if (Object.keys(value).some((api) => !IOS_PRIVACY_ACCESSED_APIS.some((known) => known === api)))
630
+ throw new TypeError(`${field} contains an unsupported API or reason.`);
631
+ return privacy;
632
+ }, iosNativeRequirements = (value, field) => {
633
+ if (value === undefined)
634
+ return;
635
+ if (!object2(value))
636
+ throw new TypeError(`${field} must be an object.`);
637
+ const {
638
+ privacyAccessedApis,
639
+ pushNotifications,
640
+ systemBars,
641
+ usageDescriptions
642
+ } = value;
643
+ if (pushNotifications !== undefined && pushNotifications !== true)
644
+ throw new TypeError(`${field}.pushNotifications must be true.`);
645
+ if (systemBars !== undefined && systemBars !== true)
646
+ throw new TypeError(`${field}.systemBars must be true.`);
647
+ if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
648
+ throw new TypeError(`${field}.usageDescriptions contains an unsupported purpose.`);
649
+ const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field}.privacyAccessedApis`);
650
+ return {
651
+ ...privacy === undefined ? {} : { privacyAccessedApis: privacy },
652
+ ...pushNotifications === true ? { pushNotifications: true } : {},
653
+ ...systemBars === true ? { systemBars: true } : {},
654
+ ...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
655
+ };
656
+ }, parseProvider = (name, value) => {
657
+ if (!IDENTIFIER_PATTERN.test(name))
658
+ throw new TypeError("Device capability names must be identifiers.");
659
+ if (!object2(value))
660
+ throw new TypeError(`Device capability ${name} must be an object.`);
661
+ const factory = text(value.factory, `${name}.factory`);
662
+ const module = text(value.module, `${name}.module`);
663
+ if (!IDENTIFIER_PATTERN.test(factory))
664
+ throw new TypeError(`${name}.factory must be a JavaScript identifier.`);
665
+ if (!CAPACITOR_MODULE_PATTERN.test(module))
666
+ throw new TypeError(`${name}.module must be an official devices-capacitor subpath.`);
667
+ if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" && CAPACITOR_PACKAGE_PATTERN.test(spec)))
668
+ throw new TypeError(`${name}.packages must contain exact official Capacitor package versions.`);
669
+ let native;
670
+ const { native: nativeMetadata } = value;
671
+ if (nativeMetadata !== undefined) {
672
+ if (!object2(nativeMetadata))
673
+ throw new TypeError(`${name}.native must be an object.`);
674
+ const { android, ios } = nativeMetadata;
675
+ const permissions = androidPermissions(android, `${name}.native.android`);
676
+ const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
677
+ native = {
678
+ ...permissions === undefined ? {} : { android: { permissions } },
679
+ ...iosRequirements === undefined ? {} : { ios: iosRequirements }
680
+ };
681
+ }
682
+ return {
683
+ factory,
684
+ module,
685
+ ...native === undefined ? {} : { native },
686
+ packages: [...value.packages]
687
+ };
688
+ }, absoluteDeviceNativeRequirements = (plan) => {
689
+ const privacy = plan.capabilities.reduce((requirements, name) => {
690
+ for (const api of IOS_PRIVACY_ACCESSED_APIS) {
691
+ const reasons = plan.providers[name]?.native?.ios?.privacyAccessedApis?.[api] ?? [];
692
+ if (reasons.length === 0)
693
+ continue;
694
+ const current = requirements[api] ?? new Set;
695
+ for (const reason of reasons)
696
+ current.add(reason);
697
+ requirements[api] = current;
698
+ }
699
+ return requirements;
700
+ }, {});
701
+ return {
702
+ androidPermissions: [
703
+ ...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.android?.permissions ?? []))
704
+ ].sort(),
705
+ iosPrivacyAccessedApis: IOS_PRIVACY_ACCESSED_APIS.flatMap((api) => {
706
+ const reasons = privacy[api];
707
+ return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
708
+ }),
709
+ iosPushNotifications: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.pushNotifications === true),
710
+ iosSystemBars: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.systemBars === true),
711
+ iosUsageDescriptions: [
712
+ ...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
713
+ ].sort()
714
+ };
715
+ }, loadAbsoluteDeviceCapabilityProviders = (projectRoot) => {
716
+ const path = join13(resolve11(projectRoot), "node_modules", CAPACITOR_ADAPTER, "package.json");
717
+ const manifest = readJson(path);
718
+ const { absolutejs } = manifest;
719
+ const devices = object2(absolutejs) ? absolutejs.devices : undefined;
720
+ if (!object2(devices) || devices.format !== 1 || devices.provider !== "capacitor" || !object2(devices.capabilities))
721
+ throw new TypeError(`${CAPACITOR_ADAPTER} does not publish supported capability metadata.`);
722
+ const entries = Object.entries(devices.capabilities).map(([name, provider]) => ({
723
+ name,
724
+ provider: parseProvider(name, provider)
725
+ }));
726
+ return Object.fromEntries(entries.sort((left, right) => left.name.localeCompare(right.name)).map(({ name, provider }) => [name, provider]));
727
+ }, isIgnored = (path) => path.split("/").some((segment) => IGNORED_DIRECTORIES.has(segment)), importedCapabilities = (source, file) => {
728
+ const names = new Set;
729
+ const namespaces = new Set;
730
+ const visit = (node) => {
731
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && !node.importClause?.isTypeOnly) {
732
+ const bindings = node.importClause?.namedBindings;
733
+ if (bindings && ts.isNamedImports(bindings)) {
734
+ for (const element of bindings.elements)
735
+ if (!element.isTypeOnly)
736
+ names.add((element.propertyName ?? element.name).text);
737
+ }
738
+ if (bindings && ts.isNamespaceImport(bindings))
739
+ namespaces.add(bindings.name.text);
740
+ }
741
+ if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && node.exportClause && ts.isNamedExports(node.exportClause)) {
742
+ for (const element of node.exportClause.elements)
743
+ if (!element.isTypeOnly)
744
+ names.add((element.propertyName ?? element.name).text);
745
+ }
746
+ if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && namespaces.has(node.expression.text))
747
+ names.add(node.name.text);
748
+ ts.forEachChild(node, visit);
749
+ };
750
+ const extension = extname3(file).toLowerCase();
751
+ const sources = extension === ".svelte" || extension === ".vue" ? [...source.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script\s*>/giu)].map((match) => match[1]).filter((value) => value !== undefined) : [source];
752
+ for (const [index, script] of sources.entries())
753
+ visit(ts.createSourceFile(`${file}#script-${index}`, script, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX));
754
+ return names;
755
+ }, assertAbsoluteDeviceCapabilityPackages = (projectRoot, plan) => {
756
+ const missing = missingAbsoluteDeviceCapabilityPackages(plan, directAbsoluteProjectPackages(projectRoot));
757
+ const mismatched = plan.requiredPackages.filter((spec) => {
758
+ const separator = spec.lastIndexOf("@");
759
+ const packageName = spec.slice(0, separator);
760
+ if (missing.includes(spec))
761
+ return false;
762
+ try {
763
+ return readJson(join13(resolve11(projectRoot), "node_modules", packageName, "package.json")).version !== spec.slice(separator + 1);
764
+ } catch {
765
+ return true;
766
+ }
767
+ });
768
+ const unmet = [...missing, ...mismatched];
769
+ if (unmet.length > 0)
770
+ throw new TypeError(`Device capabilities ${plan.capabilities.join(", ")} require ${unmet.join(", ")}. Run absolute mobile sync and approve the detected capability plugins.`);
771
+ }, directAbsoluteProjectPackages = (projectRoot) => {
772
+ const manifest = readJson(join13(resolve11(projectRoot), "package.json"));
773
+ const packages = new Set;
774
+ for (const field of ["dependencies", "devDependencies"]) {
775
+ const dependencies = manifest[field];
776
+ if (object2(dependencies))
777
+ for (const name of Object.keys(dependencies))
778
+ packages.add(name);
779
+ }
780
+ return packages;
781
+ }, discoverAbsoluteDeviceCapabilities = (projectRoot, providers = loadAbsoluteDeviceCapabilityProviders(projectRoot)) => {
782
+ const root = resolve11(projectRoot);
783
+ const known = new Set(Object.keys(providers));
784
+ const capabilities = new Set;
785
+ for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
786
+ const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
787
+ if (isIgnored(portable))
788
+ continue;
789
+ const source = readFileSync4(resolve11(root, portable), "utf8");
790
+ for (const name of importedCapabilities(source, portable))
791
+ if (known.has(name))
792
+ capabilities.add(name);
793
+ }
794
+ return [...capabilities].sort();
795
+ }, missingAbsoluteDeviceCapabilityPackages = (plan, directPackages) => plan.requiredPackages.filter((spec) => {
796
+ const packageName = spec.slice(0, spec.lastIndexOf("@"));
797
+ return !directPackages.has(packageName);
798
+ }), projectImportsAbsoluteDeviceCapability = (projectRoot, capability) => {
799
+ const root = resolve11(projectRoot);
800
+ for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
801
+ const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
802
+ if (isIgnored(portable))
803
+ continue;
804
+ const source = readFileSync4(resolve11(root, portable), "utf8");
805
+ if (importedCapabilities(source, portable).has(capability))
806
+ return true;
807
+ }
808
+ return false;
809
+ }, resolveAbsoluteDeviceCapabilityPlan = (projectRoot) => {
810
+ const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot);
811
+ const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
812
+ const providers = {};
813
+ for (const name of capabilities) {
814
+ const provider = allProviders[name];
815
+ if (provider)
816
+ providers[name] = provider;
817
+ }
818
+ return {
819
+ capabilities,
820
+ providers,
821
+ requiredPackages: [
822
+ ...new Set(capabilities.flatMap((name) => providers[name]?.packages ?? []))
823
+ ].sort()
824
+ };
825
+ };
826
+ var init_deviceCapabilities = __esm(() => {
827
+ SOURCE_GLOB = new Bun.Glob("**/*.{js,jsx,ts,tsx,svelte,vue}");
828
+ IGNORED_DIRECTORIES = new Set([
829
+ ".absolutejs",
830
+ ".git",
831
+ ".test-builds",
832
+ ".test-shards",
833
+ "build",
834
+ "dist",
835
+ "node_modules",
836
+ "test",
837
+ "tests"
838
+ ]);
839
+ IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
840
+ CAPACITOR_MODULE_PATTERN = /^@absolutejs\/devices-capacitor\/[a-z][a-z0-9-]*$/u;
841
+ CAPACITOR_PACKAGE_PATTERN = /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u;
842
+ ANDROID_PERMISSION_PATTERN = /^android\.permission\.[A-Z][A-Z0-9_]*$/u;
843
+ IOS_USAGE_DESCRIPTIONS = new Set([
844
+ "camera",
845
+ "location-always",
846
+ "location-when-in-use",
847
+ "photo-library",
848
+ "photo-library-add"
849
+ ]);
850
+ IOS_PRIVACY_ACCESSED_API_REASONS = {
851
+ NSPrivacyAccessedAPICategoryFileTimestamp: new Set(["C617.1"])
852
+ };
853
+ IOS_PRIVACY_ACCESSED_APIS = [
854
+ "NSPrivacyAccessedAPICategoryFileTimestamp"
855
+ ];
856
+ });
857
+
592
858
  // src/mobile/artifactStore.ts
593
859
  import { createHash as createHash2 } from "crypto";
594
860
  import {
@@ -5061,269 +5327,7 @@ var serializeAbsoluteMobileAuthEnvironment = (config, auth) => auth === undefine
5061
5327
 
5062
5328
  // src/mobile/buildPipeline.ts
5063
5329
  init_syncSchema();
5064
-
5065
- // src/mobile/deviceCapabilities.ts
5066
- import { readFileSync as readFileSync4 } from "fs";
5067
- import { extname as extname3, join as join13, relative as relative9, resolve as resolve11 } from "path";
5068
- import ts from "typescript";
5069
- var DEVICES_PACKAGE = "@absolutejs/devices";
5070
- var CAPACITOR_ADAPTER = "@absolutejs/devices-capacitor";
5071
- var SOURCE_GLOB = new Bun.Glob("**/*.{js,jsx,ts,tsx,svelte,vue}");
5072
- var IGNORED_DIRECTORIES = new Set([
5073
- ".absolutejs",
5074
- ".git",
5075
- ".test-builds",
5076
- ".test-shards",
5077
- "build",
5078
- "dist",
5079
- "node_modules",
5080
- "test",
5081
- "tests"
5082
- ]);
5083
- var IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
5084
- var CAPACITOR_MODULE_PATTERN = /^@absolutejs\/devices-capacitor\/[a-z][a-z0-9-]*$/u;
5085
- var CAPACITOR_PACKAGE_PATTERN = /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u;
5086
- var ANDROID_PERMISSION_PATTERN = /^android\.permission\.[A-Z][A-Z0-9_]*$/u;
5087
- var IOS_USAGE_DESCRIPTIONS = new Set([
5088
- "camera",
5089
- "location-always",
5090
- "location-when-in-use",
5091
- "photo-library",
5092
- "photo-library-add"
5093
- ]);
5094
- var IOS_PRIVACY_ACCESSED_API_REASONS = {
5095
- NSPrivacyAccessedAPICategoryFileTimestamp: new Set(["C617.1"])
5096
- };
5097
- var IOS_PRIVACY_ACCESSED_APIS = [
5098
- "NSPrivacyAccessedAPICategoryFileTimestamp"
5099
- ];
5100
- var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
5101
- var readJson = (path) => {
5102
- const value = JSON.parse(readFileSync4(path, "utf8"));
5103
- if (!object2(value))
5104
- throw new TypeError(`${path} must contain an object.`);
5105
- return value;
5106
- };
5107
- var text = (value, field) => {
5108
- if (typeof value !== "string" || value.length === 0)
5109
- throw new TypeError(`${field} must be a non-empty string.`);
5110
- return value;
5111
- };
5112
- var androidPermissions = (value, field) => {
5113
- if (value === undefined)
5114
- return;
5115
- if (!object2(value))
5116
- throw new TypeError(`${field} must be an object.`);
5117
- const { permissions } = value;
5118
- if (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string" && ANDROID_PERMISSION_PATTERN.test(permission)))
5119
- throw new TypeError(`${field}.permissions must contain Android permission names.`);
5120
- return [...permissions];
5121
- };
5122
- var iosPrivacyAccessedApis = (value, field) => {
5123
- if (value === undefined)
5124
- return;
5125
- if (!object2(value))
5126
- throw new TypeError(`${field} must be an object.`);
5127
- const privacy = {};
5128
- for (const api of IOS_PRIVACY_ACCESSED_APIS) {
5129
- const reasons = value[api];
5130
- if (reasons === undefined)
5131
- continue;
5132
- const supported = IOS_PRIVACY_ACCESSED_API_REASONS[api];
5133
- if (!Array.isArray(reasons) || reasons.length === 0 || !reasons.every((reason) => typeof reason === "string" && supported.has(reason)))
5134
- throw new TypeError(`${field} contains an unsupported API or reason.`);
5135
- privacy[api] = [...reasons];
5136
- }
5137
- if (Object.keys(value).some((api) => !IOS_PRIVACY_ACCESSED_APIS.some((known) => known === api)))
5138
- throw new TypeError(`${field} contains an unsupported API or reason.`);
5139
- return privacy;
5140
- };
5141
- var iosNativeRequirements = (value, field) => {
5142
- if (value === undefined)
5143
- return;
5144
- if (!object2(value))
5145
- throw new TypeError(`${field} must be an object.`);
5146
- const { privacyAccessedApis, pushNotifications, usageDescriptions } = value;
5147
- if (pushNotifications !== undefined && pushNotifications !== true)
5148
- throw new TypeError(`${field}.pushNotifications must be true.`);
5149
- if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
5150
- throw new TypeError(`${field}.usageDescriptions contains an unsupported purpose.`);
5151
- const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field}.privacyAccessedApis`);
5152
- return {
5153
- ...privacy === undefined ? {} : { privacyAccessedApis: privacy },
5154
- ...pushNotifications === true ? { pushNotifications: true } : {},
5155
- ...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
5156
- };
5157
- };
5158
- var parseProvider = (name, value) => {
5159
- if (!IDENTIFIER_PATTERN.test(name))
5160
- throw new TypeError("Device capability names must be identifiers.");
5161
- if (!object2(value))
5162
- throw new TypeError(`Device capability ${name} must be an object.`);
5163
- const factory = text(value.factory, `${name}.factory`);
5164
- const module = text(value.module, `${name}.module`);
5165
- if (!IDENTIFIER_PATTERN.test(factory))
5166
- throw new TypeError(`${name}.factory must be a JavaScript identifier.`);
5167
- if (!CAPACITOR_MODULE_PATTERN.test(module))
5168
- throw new TypeError(`${name}.module must be an official devices-capacitor subpath.`);
5169
- if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" && CAPACITOR_PACKAGE_PATTERN.test(spec)))
5170
- throw new TypeError(`${name}.packages must contain exact official Capacitor package versions.`);
5171
- let native;
5172
- const { native: nativeMetadata } = value;
5173
- if (nativeMetadata !== undefined) {
5174
- if (!object2(nativeMetadata))
5175
- throw new TypeError(`${name}.native must be an object.`);
5176
- const { android, ios } = nativeMetadata;
5177
- const permissions = androidPermissions(android, `${name}.native.android`);
5178
- const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
5179
- native = {
5180
- ...permissions === undefined ? {} : { android: { permissions } },
5181
- ...iosRequirements === undefined ? {} : { ios: iosRequirements }
5182
- };
5183
- }
5184
- return {
5185
- factory,
5186
- module,
5187
- ...native === undefined ? {} : { native },
5188
- packages: [...value.packages]
5189
- };
5190
- };
5191
- var absoluteDeviceNativeRequirements = (plan) => {
5192
- const privacy = plan.capabilities.reduce((requirements, name) => {
5193
- for (const api of IOS_PRIVACY_ACCESSED_APIS) {
5194
- const reasons = plan.providers[name]?.native?.ios?.privacyAccessedApis?.[api] ?? [];
5195
- if (reasons.length === 0)
5196
- continue;
5197
- const current = requirements[api] ?? new Set;
5198
- for (const reason of reasons)
5199
- current.add(reason);
5200
- requirements[api] = current;
5201
- }
5202
- return requirements;
5203
- }, {});
5204
- return {
5205
- androidPermissions: [
5206
- ...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.android?.permissions ?? []))
5207
- ].sort(),
5208
- iosPrivacyAccessedApis: IOS_PRIVACY_ACCESSED_APIS.flatMap((api) => {
5209
- const reasons = privacy[api];
5210
- return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
5211
- }),
5212
- iosPushNotifications: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.pushNotifications === true),
5213
- iosUsageDescriptions: [
5214
- ...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
5215
- ].sort()
5216
- };
5217
- };
5218
- var loadAbsoluteDeviceCapabilityProviders = (projectRoot) => {
5219
- const path = join13(resolve11(projectRoot), "node_modules", CAPACITOR_ADAPTER, "package.json");
5220
- const manifest = readJson(path);
5221
- const { absolutejs } = manifest;
5222
- const devices = object2(absolutejs) ? absolutejs.devices : undefined;
5223
- if (!object2(devices) || devices.format !== 1 || devices.provider !== "capacitor" || !object2(devices.capabilities))
5224
- throw new TypeError(`${CAPACITOR_ADAPTER} does not publish supported capability metadata.`);
5225
- const entries = Object.entries(devices.capabilities).map(([name, provider]) => ({
5226
- name,
5227
- provider: parseProvider(name, provider)
5228
- }));
5229
- return Object.fromEntries(entries.sort((left, right) => left.name.localeCompare(right.name)).map(({ name, provider }) => [name, provider]));
5230
- };
5231
- var isIgnored = (path) => path.split("/").some((segment) => IGNORED_DIRECTORIES.has(segment));
5232
- var importedCapabilities = (source, file) => {
5233
- const names = new Set;
5234
- const namespaces = new Set;
5235
- const visit = (node) => {
5236
- if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && !node.importClause?.isTypeOnly) {
5237
- const bindings = node.importClause?.namedBindings;
5238
- if (bindings && ts.isNamedImports(bindings)) {
5239
- for (const element of bindings.elements)
5240
- if (!element.isTypeOnly)
5241
- names.add((element.propertyName ?? element.name).text);
5242
- }
5243
- if (bindings && ts.isNamespaceImport(bindings))
5244
- namespaces.add(bindings.name.text);
5245
- }
5246
- if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && node.exportClause && ts.isNamedExports(node.exportClause)) {
5247
- for (const element of node.exportClause.elements)
5248
- if (!element.isTypeOnly)
5249
- names.add((element.propertyName ?? element.name).text);
5250
- }
5251
- if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && namespaces.has(node.expression.text))
5252
- names.add(node.name.text);
5253
- ts.forEachChild(node, visit);
5254
- };
5255
- const extension = extname3(file).toLowerCase();
5256
- const sources = extension === ".svelte" || extension === ".vue" ? [...source.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script\s*>/giu)].map((match) => match[1]).filter((value) => value !== undefined) : [source];
5257
- for (const [index, script] of sources.entries())
5258
- visit(ts.createSourceFile(`${file}#script-${index}`, script, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX));
5259
- return names;
5260
- };
5261
- var assertAbsoluteDeviceCapabilityPackages = (projectRoot, plan) => {
5262
- const missing = missingAbsoluteDeviceCapabilityPackages(plan, directAbsoluteProjectPackages(projectRoot));
5263
- const mismatched = plan.requiredPackages.filter((spec) => {
5264
- const separator = spec.lastIndexOf("@");
5265
- const packageName = spec.slice(0, separator);
5266
- if (missing.includes(spec))
5267
- return false;
5268
- try {
5269
- return readJson(join13(resolve11(projectRoot), "node_modules", packageName, "package.json")).version !== spec.slice(separator + 1);
5270
- } catch {
5271
- return true;
5272
- }
5273
- });
5274
- const unmet = [...missing, ...mismatched];
5275
- if (unmet.length > 0)
5276
- throw new TypeError(`Device capabilities ${plan.capabilities.join(", ")} require ${unmet.join(", ")}. Run absolute mobile sync and approve the detected capability plugins.`);
5277
- };
5278
- var directAbsoluteProjectPackages = (projectRoot) => {
5279
- const manifest = readJson(join13(resolve11(projectRoot), "package.json"));
5280
- const packages = new Set;
5281
- for (const field of ["dependencies", "devDependencies"]) {
5282
- const dependencies = manifest[field];
5283
- if (object2(dependencies))
5284
- for (const name of Object.keys(dependencies))
5285
- packages.add(name);
5286
- }
5287
- return packages;
5288
- };
5289
- var discoverAbsoluteDeviceCapabilities = (projectRoot, providers = loadAbsoluteDeviceCapabilityProviders(projectRoot)) => {
5290
- const root = resolve11(projectRoot);
5291
- const known = new Set(Object.keys(providers));
5292
- const capabilities = new Set;
5293
- for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
5294
- const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
5295
- if (isIgnored(portable))
5296
- continue;
5297
- const source = readFileSync4(resolve11(root, portable), "utf8");
5298
- for (const name of importedCapabilities(source, portable))
5299
- if (known.has(name))
5300
- capabilities.add(name);
5301
- }
5302
- return [...capabilities].sort();
5303
- };
5304
- var missingAbsoluteDeviceCapabilityPackages = (plan, directPackages) => plan.requiredPackages.filter((spec) => {
5305
- const packageName = spec.slice(0, spec.lastIndexOf("@"));
5306
- return !directPackages.has(packageName);
5307
- });
5308
- var resolveAbsoluteDeviceCapabilityPlan = (projectRoot) => {
5309
- const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot);
5310
- const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
5311
- const providers = {};
5312
- for (const name of capabilities) {
5313
- const provider = allProviders[name];
5314
- if (provider)
5315
- providers[name] = provider;
5316
- }
5317
- return {
5318
- capabilities,
5319
- providers,
5320
- requiredPackages: [
5321
- ...new Set(capabilities.flatMap((name) => providers[name]?.packages ?? []))
5322
- ].sort()
5323
- };
5324
- };
5325
-
5326
- // src/mobile/buildPipeline.ts
5330
+ init_deviceCapabilities();
5327
5331
  var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes"));
5328
5332
  var isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
5329
5333
  var serverExportName = (loaded, app) => {
@@ -5401,8 +5405,8 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
5401
5405
  const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
5402
5406
  if (usesPush && !auth)
5403
5407
  throw new TypeError("Portable push notifications require @absolutejs/auth so provider tokens can be registered without exposing identity controls to page code.");
5404
- if (usesPush && !loaded.app.routes.some((route) => route.path === "/auth/mobile/push"))
5405
- throw new TypeError("@absolutejs/devices pushNotifications is used, but Auth nativePush is not configured. Pass a server-side registrar to auth({ nativePush: ... }).");
5408
+ if (usesPush && !loaded.app.routes.some((route) => route.path === "/auth/push" || route.path === "/auth/mobile/push"))
5409
+ throw new TypeError("@absolutejs/devices pushNotifications is used, but Auth push is not configured. Pass a trusted server-side registrar to auth({ push: ... }).");
5406
5410
  assertAbsoluteDeviceCapabilityPackages(options.projectRoot, deviceCapabilities);
5407
5411
  if (auth && !loaded.app.routes.some((route) => route.path === "/.well-known/openid-configuration")) {
5408
5412
  throw new TypeError("@absolutejs/auth is installed, but its OIDC provider is not mounted. Native authentication requires the auth oidc configuration so AbsoluteJS can provision a public PKCE client.");
@@ -5540,6 +5544,10 @@ var installAbsoluteMobileSyncRemediation = (bridge = {
5540
5544
  registry3.installations.splice(index, 1);
5541
5545
  };
5542
5546
  };
5547
+
5548
+ // src/mobile/index.ts
5549
+ init_deviceCapabilities();
5550
+
5543
5551
  // src/mobile/compatibilityDispatcher.ts
5544
5552
  import { Elysia as Elysia2 } from "elysia";
5545
5553
 
@@ -5845,6 +5853,7 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
5845
5853
  };
5846
5854
  };
5847
5855
  // src/mobile/nativeDeviceCapabilities.ts
5856
+ init_deviceCapabilities();
5848
5857
  import { readFile as readFile15, rename as rename12, writeFile as writeFile13 } from "fs/promises";
5849
5858
  import { join as join16 } from "path";
5850
5859
  var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->";
@@ -6052,8 +6061,18 @@ var configureIos2 = async (config, plan) => {
6052
6061
  const path = join16(config.nativeProjectDirectory, "ios/App/App/Info.plist");
6053
6062
  const source = await readFile15(path, "utf8");
6054
6063
  const requirements = absoluteDeviceNativeRequirements(plan);
6055
- const content = requirements.iosUsageDescriptions.map((purpose) => ` <key>${IOS_KEYS[purpose]}</key>
6064
+ const existingSystemBars = source.match(/<key>UIViewControllerBasedStatusBarAppearance<\/key>\s*<(true|false)\s*\/>/u);
6065
+ const ownedStart = source.indexOf(START_MARKER2);
6066
+ const ownedEnd = source.indexOf(END_MARKER2);
6067
+ const ownsSystemBars = ownedStart >= 0 && ownedEnd > ownedStart && source.slice(ownedStart, ownedEnd).includes("UIViewControllerBasedStatusBarAppearance");
6068
+ if (requirements.iosSystemBars && existingSystemBars?.[1] === "false" && !ownsSystemBars)
6069
+ throw new TypeError("iOS system bars require UIViewControllerBasedStatusBarAppearance to be true.");
6070
+ const usageContent = requirements.iosUsageDescriptions.map((purpose) => ` <key>${IOS_KEYS[purpose]}</key>
6056
6071
  <string>${escapeXml2(iosDescription(config.appName, purpose))}</string>`).join(`
6072
+ `);
6073
+ const systemBarsContent = requirements.iosSystemBars && (existingSystemBars === null || ownsSystemBars) ? ` <key>UIViewControllerBasedStatusBarAppearance</key>
6074
+ <true/>` : "";
6075
+ const content = [usageContent, systemBarsContent].filter(Boolean).join(`
6057
6076
  `);
6058
6077
  const region = content ? ` ${START_MARKER2}
6059
6078
  ${content}
@@ -6837,6 +6856,7 @@ export {
6837
6856
  publishAbsoluteAndroidRelease,
6838
6857
  projectUsesAbsoluteSync,
6839
6858
  projectUsesAbsoluteAuth,
6859
+ projectImportsAbsoluteDeviceCapability,
6840
6860
  prepareAbsoluteIosRelease,
6841
6861
  prepareAbsoluteIosDevProject,
6842
6862
  prepareAbsoluteAndroidRelease,
@@ -6931,5 +6951,5 @@ export {
6931
6951
  ABSOLUTE_ANDROID_RELEASE_FORMAT
6932
6952
  };
6933
6953
 
6934
- //# debugId=896911BF5E0E827564756E2164756E21
6954
+ //# debugId=70D55943FDDD4B5264756E2164756E21
6935
6955
  //# sourceMappingURL=index.js.map