@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.
- package/README.md +34 -2
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +1263 -977
- package/dist/build.js.map +5 -4
- package/dist/cli/index.js +35 -4
- package/dist/index.js +1323 -1037
- package/dist/index.js.map +5 -4
- package/dist/mobile/index.js +287 -267
- package/dist/mobile/index.js.map +6 -6
- package/dist/src/mobile/deviceCapabilities.d.ts +5 -0
- package/dist/src/mobile/shellPush.d.ts +1 -1
- package/dist/types/build.d.ts +9 -1
- package/package.json +8 -7
package/dist/mobile/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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=
|
|
6954
|
+
//# debugId=70D55943FDDD4B5264756E2164756E21
|
|
6935
6955
|
//# sourceMappingURL=index.js.map
|