@absolutejs/absolute 0.20.0-beta.24 → 0.20.0-beta.26
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 +12 -1
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +1254 -977
- package/dist/build.js.map +5 -4
- package/dist/cli/index.js +128 -11
- package/dist/index.js +1316 -1038
- package/dist/index.js.map +6 -5
- package/dist/mobile/index.js +393 -269
- package/dist/mobile/index.js.map +8 -8
- package/dist/mobile/remoteMacAgentEntry.js +10 -10
- package/dist/mobile/shellAuth.js +2 -1
- package/dist/mobile/shellBootstrap.js +5 -1
- package/dist/src/mobile/config.d.ts +1 -0
- package/dist/src/mobile/deviceCapabilities.d.ts +5 -0
- package/dist/src/mobile/shellAuth.d.ts +3 -1
- package/dist/src/mobile/shellBootstrap.d.ts +5 -1
- package/dist/src/mobile/shellPush.d.ts +11 -0
- package/dist/types/build.d.ts +16 -1
- package/package.json +7 -5
package/dist/mobile/index.js
CHANGED
|
@@ -589,6 +589,263 @@ 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 { privacyAccessedApis, pushNotifications, usageDescriptions } = value;
|
|
638
|
+
if (pushNotifications !== undefined && pushNotifications !== true)
|
|
639
|
+
throw new TypeError(`${field}.pushNotifications must be true.`);
|
|
640
|
+
if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
|
|
641
|
+
throw new TypeError(`${field}.usageDescriptions contains an unsupported purpose.`);
|
|
642
|
+
const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field}.privacyAccessedApis`);
|
|
643
|
+
return {
|
|
644
|
+
...privacy === undefined ? {} : { privacyAccessedApis: privacy },
|
|
645
|
+
...pushNotifications === true ? { pushNotifications: true } : {},
|
|
646
|
+
...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
|
|
647
|
+
};
|
|
648
|
+
}, parseProvider = (name, value) => {
|
|
649
|
+
if (!IDENTIFIER_PATTERN.test(name))
|
|
650
|
+
throw new TypeError("Device capability names must be identifiers.");
|
|
651
|
+
if (!object2(value))
|
|
652
|
+
throw new TypeError(`Device capability ${name} must be an object.`);
|
|
653
|
+
const factory = text(value.factory, `${name}.factory`);
|
|
654
|
+
const module = text(value.module, `${name}.module`);
|
|
655
|
+
if (!IDENTIFIER_PATTERN.test(factory))
|
|
656
|
+
throw new TypeError(`${name}.factory must be a JavaScript identifier.`);
|
|
657
|
+
if (!CAPACITOR_MODULE_PATTERN.test(module))
|
|
658
|
+
throw new TypeError(`${name}.module must be an official devices-capacitor subpath.`);
|
|
659
|
+
if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" && CAPACITOR_PACKAGE_PATTERN.test(spec)))
|
|
660
|
+
throw new TypeError(`${name}.packages must contain exact official Capacitor package versions.`);
|
|
661
|
+
let native;
|
|
662
|
+
const { native: nativeMetadata } = value;
|
|
663
|
+
if (nativeMetadata !== undefined) {
|
|
664
|
+
if (!object2(nativeMetadata))
|
|
665
|
+
throw new TypeError(`${name}.native must be an object.`);
|
|
666
|
+
const { android, ios } = nativeMetadata;
|
|
667
|
+
const permissions = androidPermissions(android, `${name}.native.android`);
|
|
668
|
+
const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
|
|
669
|
+
native = {
|
|
670
|
+
...permissions === undefined ? {} : { android: { permissions } },
|
|
671
|
+
...iosRequirements === undefined ? {} : { ios: iosRequirements }
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
factory,
|
|
676
|
+
module,
|
|
677
|
+
...native === undefined ? {} : { native },
|
|
678
|
+
packages: [...value.packages]
|
|
679
|
+
};
|
|
680
|
+
}, absoluteDeviceNativeRequirements = (plan) => {
|
|
681
|
+
const privacy = plan.capabilities.reduce((requirements, name) => {
|
|
682
|
+
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
683
|
+
const reasons = plan.providers[name]?.native?.ios?.privacyAccessedApis?.[api] ?? [];
|
|
684
|
+
if (reasons.length === 0)
|
|
685
|
+
continue;
|
|
686
|
+
const current = requirements[api] ?? new Set;
|
|
687
|
+
for (const reason of reasons)
|
|
688
|
+
current.add(reason);
|
|
689
|
+
requirements[api] = current;
|
|
690
|
+
}
|
|
691
|
+
return requirements;
|
|
692
|
+
}, {});
|
|
693
|
+
return {
|
|
694
|
+
androidPermissions: [
|
|
695
|
+
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.android?.permissions ?? []))
|
|
696
|
+
].sort(),
|
|
697
|
+
iosPrivacyAccessedApis: IOS_PRIVACY_ACCESSED_APIS.flatMap((api) => {
|
|
698
|
+
const reasons = privacy[api];
|
|
699
|
+
return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
|
|
700
|
+
}),
|
|
701
|
+
iosPushNotifications: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.pushNotifications === true),
|
|
702
|
+
iosUsageDescriptions: [
|
|
703
|
+
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
|
|
704
|
+
].sort()
|
|
705
|
+
};
|
|
706
|
+
}, loadAbsoluteDeviceCapabilityProviders = (projectRoot) => {
|
|
707
|
+
const path = join13(resolve11(projectRoot), "node_modules", CAPACITOR_ADAPTER, "package.json");
|
|
708
|
+
const manifest = readJson(path);
|
|
709
|
+
const { absolutejs } = manifest;
|
|
710
|
+
const devices = object2(absolutejs) ? absolutejs.devices : undefined;
|
|
711
|
+
if (!object2(devices) || devices.format !== 1 || devices.provider !== "capacitor" || !object2(devices.capabilities))
|
|
712
|
+
throw new TypeError(`${CAPACITOR_ADAPTER} does not publish supported capability metadata.`);
|
|
713
|
+
const entries = Object.entries(devices.capabilities).map(([name, provider]) => ({
|
|
714
|
+
name,
|
|
715
|
+
provider: parseProvider(name, provider)
|
|
716
|
+
}));
|
|
717
|
+
return Object.fromEntries(entries.sort((left, right) => left.name.localeCompare(right.name)).map(({ name, provider }) => [name, provider]));
|
|
718
|
+
}, isIgnored = (path) => path.split("/").some((segment) => IGNORED_DIRECTORIES.has(segment)), importedCapabilities = (source, file) => {
|
|
719
|
+
const names = new Set;
|
|
720
|
+
const namespaces = new Set;
|
|
721
|
+
const visit = (node) => {
|
|
722
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && !node.importClause?.isTypeOnly) {
|
|
723
|
+
const bindings = node.importClause?.namedBindings;
|
|
724
|
+
if (bindings && ts.isNamedImports(bindings)) {
|
|
725
|
+
for (const element of bindings.elements)
|
|
726
|
+
if (!element.isTypeOnly)
|
|
727
|
+
names.add((element.propertyName ?? element.name).text);
|
|
728
|
+
}
|
|
729
|
+
if (bindings && ts.isNamespaceImport(bindings))
|
|
730
|
+
namespaces.add(bindings.name.text);
|
|
731
|
+
}
|
|
732
|
+
if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
733
|
+
for (const element of node.exportClause.elements)
|
|
734
|
+
if (!element.isTypeOnly)
|
|
735
|
+
names.add((element.propertyName ?? element.name).text);
|
|
736
|
+
}
|
|
737
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && namespaces.has(node.expression.text))
|
|
738
|
+
names.add(node.name.text);
|
|
739
|
+
ts.forEachChild(node, visit);
|
|
740
|
+
};
|
|
741
|
+
const extension = extname3(file).toLowerCase();
|
|
742
|
+
const sources = extension === ".svelte" || extension === ".vue" ? [...source.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script\s*>/giu)].map((match) => match[1]).filter((value) => value !== undefined) : [source];
|
|
743
|
+
for (const [index, script] of sources.entries())
|
|
744
|
+
visit(ts.createSourceFile(`${file}#script-${index}`, script, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX));
|
|
745
|
+
return names;
|
|
746
|
+
}, assertAbsoluteDeviceCapabilityPackages = (projectRoot, plan) => {
|
|
747
|
+
const missing = missingAbsoluteDeviceCapabilityPackages(plan, directAbsoluteProjectPackages(projectRoot));
|
|
748
|
+
const mismatched = plan.requiredPackages.filter((spec) => {
|
|
749
|
+
const separator = spec.lastIndexOf("@");
|
|
750
|
+
const packageName = spec.slice(0, separator);
|
|
751
|
+
if (missing.includes(spec))
|
|
752
|
+
return false;
|
|
753
|
+
try {
|
|
754
|
+
return readJson(join13(resolve11(projectRoot), "node_modules", packageName, "package.json")).version !== spec.slice(separator + 1);
|
|
755
|
+
} catch {
|
|
756
|
+
return true;
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
const unmet = [...missing, ...mismatched];
|
|
760
|
+
if (unmet.length > 0)
|
|
761
|
+
throw new TypeError(`Device capabilities ${plan.capabilities.join(", ")} require ${unmet.join(", ")}. Run absolute mobile sync and approve the detected capability plugins.`);
|
|
762
|
+
}, directAbsoluteProjectPackages = (projectRoot) => {
|
|
763
|
+
const manifest = readJson(join13(resolve11(projectRoot), "package.json"));
|
|
764
|
+
const packages = new Set;
|
|
765
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
766
|
+
const dependencies = manifest[field];
|
|
767
|
+
if (object2(dependencies))
|
|
768
|
+
for (const name of Object.keys(dependencies))
|
|
769
|
+
packages.add(name);
|
|
770
|
+
}
|
|
771
|
+
return packages;
|
|
772
|
+
}, discoverAbsoluteDeviceCapabilities = (projectRoot, providers = loadAbsoluteDeviceCapabilityProviders(projectRoot)) => {
|
|
773
|
+
const root = resolve11(projectRoot);
|
|
774
|
+
const known = new Set(Object.keys(providers));
|
|
775
|
+
const capabilities = new Set;
|
|
776
|
+
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
777
|
+
const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
|
|
778
|
+
if (isIgnored(portable))
|
|
779
|
+
continue;
|
|
780
|
+
const source = readFileSync4(resolve11(root, portable), "utf8");
|
|
781
|
+
for (const name of importedCapabilities(source, portable))
|
|
782
|
+
if (known.has(name))
|
|
783
|
+
capabilities.add(name);
|
|
784
|
+
}
|
|
785
|
+
return [...capabilities].sort();
|
|
786
|
+
}, missingAbsoluteDeviceCapabilityPackages = (plan, directPackages) => plan.requiredPackages.filter((spec) => {
|
|
787
|
+
const packageName = spec.slice(0, spec.lastIndexOf("@"));
|
|
788
|
+
return !directPackages.has(packageName);
|
|
789
|
+
}), projectImportsAbsoluteDeviceCapability = (projectRoot, capability) => {
|
|
790
|
+
const root = resolve11(projectRoot);
|
|
791
|
+
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
792
|
+
const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
|
|
793
|
+
if (isIgnored(portable))
|
|
794
|
+
continue;
|
|
795
|
+
const source = readFileSync4(resolve11(root, portable), "utf8");
|
|
796
|
+
if (importedCapabilities(source, portable).has(capability))
|
|
797
|
+
return true;
|
|
798
|
+
}
|
|
799
|
+
return false;
|
|
800
|
+
}, resolveAbsoluteDeviceCapabilityPlan = (projectRoot) => {
|
|
801
|
+
const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot);
|
|
802
|
+
const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
|
|
803
|
+
const providers = {};
|
|
804
|
+
for (const name of capabilities) {
|
|
805
|
+
const provider = allProviders[name];
|
|
806
|
+
if (provider)
|
|
807
|
+
providers[name] = provider;
|
|
808
|
+
}
|
|
809
|
+
return {
|
|
810
|
+
capabilities,
|
|
811
|
+
providers,
|
|
812
|
+
requiredPackages: [
|
|
813
|
+
...new Set(capabilities.flatMap((name) => providers[name]?.packages ?? []))
|
|
814
|
+
].sort()
|
|
815
|
+
};
|
|
816
|
+
};
|
|
817
|
+
var init_deviceCapabilities = __esm(() => {
|
|
818
|
+
SOURCE_GLOB = new Bun.Glob("**/*.{js,jsx,ts,tsx,svelte,vue}");
|
|
819
|
+
IGNORED_DIRECTORIES = new Set([
|
|
820
|
+
".absolutejs",
|
|
821
|
+
".git",
|
|
822
|
+
".test-builds",
|
|
823
|
+
".test-shards",
|
|
824
|
+
"build",
|
|
825
|
+
"dist",
|
|
826
|
+
"node_modules",
|
|
827
|
+
"test",
|
|
828
|
+
"tests"
|
|
829
|
+
]);
|
|
830
|
+
IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
|
|
831
|
+
CAPACITOR_MODULE_PATTERN = /^@absolutejs\/devices-capacitor\/[a-z][a-z0-9-]*$/u;
|
|
832
|
+
CAPACITOR_PACKAGE_PATTERN = /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u;
|
|
833
|
+
ANDROID_PERMISSION_PATTERN = /^android\.permission\.[A-Z][A-Z0-9_]*$/u;
|
|
834
|
+
IOS_USAGE_DESCRIPTIONS = new Set([
|
|
835
|
+
"camera",
|
|
836
|
+
"location-always",
|
|
837
|
+
"location-when-in-use",
|
|
838
|
+
"photo-library",
|
|
839
|
+
"photo-library-add"
|
|
840
|
+
]);
|
|
841
|
+
IOS_PRIVACY_ACCESSED_API_REASONS = {
|
|
842
|
+
NSPrivacyAccessedAPICategoryFileTimestamp: new Set(["C617.1"])
|
|
843
|
+
};
|
|
844
|
+
IOS_PRIVACY_ACCESSED_APIS = [
|
|
845
|
+
"NSPrivacyAccessedAPICategoryFileTimestamp"
|
|
846
|
+
];
|
|
847
|
+
});
|
|
848
|
+
|
|
592
849
|
// src/mobile/artifactStore.ts
|
|
593
850
|
import { createHash as createHash2 } from "crypto";
|
|
594
851
|
import {
|
|
@@ -3680,7 +3937,8 @@ var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
|
|
|
3680
3937
|
iosVersion: normalizeIosVersion(config.ios?.version),
|
|
3681
3938
|
nativeProjectDirectory: resolveProjectPath(projectRoot, config.nativeProject?.directory ?? "mobile", "mobile.nativeProject.directory"),
|
|
3682
3939
|
platforms: normalizePlatforms(config.platforms),
|
|
3683
|
-
productionOrigin
|
|
3940
|
+
productionOrigin,
|
|
3941
|
+
pushAndroidGoogleServicesFile: resolveProjectPath(projectRoot, config.pushNotifications?.android?.googleServicesFile ?? "google-services.json", "mobile.pushNotifications.android.googleServicesFile")
|
|
3684
3942
|
};
|
|
3685
3943
|
};
|
|
3686
3944
|
|
|
@@ -4575,6 +4833,12 @@ var shellSyncModule = () => {
|
|
|
4575
4833
|
return candidate;
|
|
4576
4834
|
throw new TypeError("AbsoluteJS mobile Sync shell module is missing.");
|
|
4577
4835
|
};
|
|
4836
|
+
var shellPushModule = () => {
|
|
4837
|
+
const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellPush.${extension}`)).find(existsSync2);
|
|
4838
|
+
if (candidate)
|
|
4839
|
+
return candidate;
|
|
4840
|
+
throw new TypeError("AbsoluteJS mobile push shell module is missing.");
|
|
4841
|
+
};
|
|
4578
4842
|
var escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4579
4843
|
var indexHtml = (appName) => `<!doctype html>
|
|
4580
4844
|
<html>
|
|
@@ -4627,6 +4891,10 @@ var buildShellBootstrap = async (staging, auth, sync, storagePrefix, deviceCapab
|
|
|
4627
4891
|
` : "";
|
|
4628
4892
|
const options = auth ? `{ createAuth: createAbsoluteMobileShellAuth${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : "";
|
|
4629
4893
|
const syncImport = sync ? `import { installAbsoluteMobileShellSync } from ${JSON.stringify(shellSyncModule())};
|
|
4894
|
+
` : "";
|
|
4895
|
+
const pushIndex = deviceCapabilities.capabilities.indexOf("pushNotifications");
|
|
4896
|
+
const push = pushIndex !== -1;
|
|
4897
|
+
const pushImport = push ? `import { createAbsoluteMobileShellPush } from ${JSON.stringify(shellPushModule())};
|
|
4630
4898
|
` : "";
|
|
4631
4899
|
const capabilityImports = (await Promise.all(deviceCapabilities.capabilities.map(async (name, index) => {
|
|
4632
4900
|
const provider = deviceCapabilities.providers[name];
|
|
@@ -4635,14 +4903,17 @@ var buildShellBootstrap = async (staging, auth, sync, storagePrefix, deviceCapab
|
|
|
4635
4903
|
return `import { ${provider.factory} as absoluteDeviceCapability${index} } from ${JSON.stringify(await resolveProjectImport(projectRoot, provider.module))};`;
|
|
4636
4904
|
}))).join(`
|
|
4637
4905
|
`);
|
|
4638
|
-
const
|
|
4906
|
+
const pushSetup = push ? `const absoluteMobilePush = createAbsoluteMobileShellPush();
|
|
4907
|
+
const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absoluteMobilePush.capabilityOptions);
|
|
4908
|
+
` : "";
|
|
4909
|
+
const capabilityOptions = deviceCapabilities.capabilities.map((name, index) => `${JSON.stringify(name)}: ${name === "pushNotifications" ? "absoluteMobilePushCapability" : `absoluteDeviceCapability${index}()`}`).join(", ");
|
|
4639
4910
|
const entryPath = join9(staging, ".absolute-mobile-entry.ts");
|
|
4640
4911
|
const baseAdapterModule = await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor");
|
|
4641
4912
|
await writeFile10(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
|
|
4642
4913
|
import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};
|
|
4643
|
-
${authImport}${syncImport}${capabilityImports}
|
|
4644
|
-
installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });
|
|
4645
|
-
void startAbsoluteMobileShell(${options});
|
|
4914
|
+
${authImport}${syncImport}${pushImport}${capabilityImports}
|
|
4915
|
+
${pushSetup}installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });
|
|
4916
|
+
void startAbsoluteMobileShell(${push ? `{ createAuth: (config, options) => createAbsoluteMobileShellAuth(config, options), beforeSignOut: absoluteMobilePush.beforeSignOut, connectPush: (auth) => absoluteMobilePush.connect(auth, absoluteMobilePushCapability)${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : options});
|
|
4646
4917
|
`);
|
|
4647
4918
|
const build = await Bun.build({
|
|
4648
4919
|
entrypoints: [entryPath],
|
|
@@ -5047,265 +5318,7 @@ var serializeAbsoluteMobileAuthEnvironment = (config, auth) => auth === undefine
|
|
|
5047
5318
|
|
|
5048
5319
|
// src/mobile/buildPipeline.ts
|
|
5049
5320
|
init_syncSchema();
|
|
5050
|
-
|
|
5051
|
-
// src/mobile/deviceCapabilities.ts
|
|
5052
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
5053
|
-
import { extname as extname3, join as join13, relative as relative9, resolve as resolve11 } from "path";
|
|
5054
|
-
import ts from "typescript";
|
|
5055
|
-
var DEVICES_PACKAGE = "@absolutejs/devices";
|
|
5056
|
-
var CAPACITOR_ADAPTER = "@absolutejs/devices-capacitor";
|
|
5057
|
-
var SOURCE_GLOB = new Bun.Glob("**/*.{js,jsx,ts,tsx,svelte,vue}");
|
|
5058
|
-
var IGNORED_DIRECTORIES = new Set([
|
|
5059
|
-
".absolutejs",
|
|
5060
|
-
".git",
|
|
5061
|
-
".test-builds",
|
|
5062
|
-
".test-shards",
|
|
5063
|
-
"build",
|
|
5064
|
-
"dist",
|
|
5065
|
-
"node_modules",
|
|
5066
|
-
"test",
|
|
5067
|
-
"tests"
|
|
5068
|
-
]);
|
|
5069
|
-
var IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
|
|
5070
|
-
var CAPACITOR_MODULE_PATTERN = /^@absolutejs\/devices-capacitor\/[a-z][a-z0-9-]*$/u;
|
|
5071
|
-
var CAPACITOR_PACKAGE_PATTERN = /^@capacitor\/[a-z][a-z0-9-]*@\d+\.\d+\.\d+$/u;
|
|
5072
|
-
var ANDROID_PERMISSION_PATTERN = /^android\.permission\.[A-Z][A-Z0-9_]*$/u;
|
|
5073
|
-
var IOS_USAGE_DESCRIPTIONS = new Set([
|
|
5074
|
-
"camera",
|
|
5075
|
-
"location-always",
|
|
5076
|
-
"location-when-in-use",
|
|
5077
|
-
"photo-library",
|
|
5078
|
-
"photo-library-add"
|
|
5079
|
-
]);
|
|
5080
|
-
var IOS_PRIVACY_ACCESSED_API_REASONS = {
|
|
5081
|
-
NSPrivacyAccessedAPICategoryFileTimestamp: new Set(["C617.1"])
|
|
5082
|
-
};
|
|
5083
|
-
var IOS_PRIVACY_ACCESSED_APIS = [
|
|
5084
|
-
"NSPrivacyAccessedAPICategoryFileTimestamp"
|
|
5085
|
-
];
|
|
5086
|
-
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5087
|
-
var readJson = (path) => {
|
|
5088
|
-
const value = JSON.parse(readFileSync4(path, "utf8"));
|
|
5089
|
-
if (!object2(value))
|
|
5090
|
-
throw new TypeError(`${path} must contain an object.`);
|
|
5091
|
-
return value;
|
|
5092
|
-
};
|
|
5093
|
-
var text = (value, field) => {
|
|
5094
|
-
if (typeof value !== "string" || value.length === 0)
|
|
5095
|
-
throw new TypeError(`${field} must be a non-empty string.`);
|
|
5096
|
-
return value;
|
|
5097
|
-
};
|
|
5098
|
-
var androidPermissions = (value, field) => {
|
|
5099
|
-
if (value === undefined)
|
|
5100
|
-
return;
|
|
5101
|
-
if (!object2(value))
|
|
5102
|
-
throw new TypeError(`${field} must be an object.`);
|
|
5103
|
-
const { permissions } = value;
|
|
5104
|
-
if (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string" && ANDROID_PERMISSION_PATTERN.test(permission)))
|
|
5105
|
-
throw new TypeError(`${field}.permissions must contain Android permission names.`);
|
|
5106
|
-
return [...permissions];
|
|
5107
|
-
};
|
|
5108
|
-
var iosPrivacyAccessedApis = (value, field) => {
|
|
5109
|
-
if (value === undefined)
|
|
5110
|
-
return;
|
|
5111
|
-
if (!object2(value))
|
|
5112
|
-
throw new TypeError(`${field} must be an object.`);
|
|
5113
|
-
const privacy = {};
|
|
5114
|
-
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
5115
|
-
const reasons = value[api];
|
|
5116
|
-
if (reasons === undefined)
|
|
5117
|
-
continue;
|
|
5118
|
-
const supported = IOS_PRIVACY_ACCESSED_API_REASONS[api];
|
|
5119
|
-
if (!Array.isArray(reasons) || reasons.length === 0 || !reasons.every((reason) => typeof reason === "string" && supported.has(reason)))
|
|
5120
|
-
throw new TypeError(`${field} contains an unsupported API or reason.`);
|
|
5121
|
-
privacy[api] = [...reasons];
|
|
5122
|
-
}
|
|
5123
|
-
if (Object.keys(value).some((api) => !IOS_PRIVACY_ACCESSED_APIS.some((known) => known === api)))
|
|
5124
|
-
throw new TypeError(`${field} contains an unsupported API or reason.`);
|
|
5125
|
-
return privacy;
|
|
5126
|
-
};
|
|
5127
|
-
var iosNativeRequirements = (value, field) => {
|
|
5128
|
-
if (value === undefined)
|
|
5129
|
-
return;
|
|
5130
|
-
if (!object2(value))
|
|
5131
|
-
throw new TypeError(`${field} must be an object.`);
|
|
5132
|
-
const { privacyAccessedApis, usageDescriptions } = value;
|
|
5133
|
-
if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
|
|
5134
|
-
throw new TypeError(`${field}.usageDescriptions contains an unsupported purpose.`);
|
|
5135
|
-
const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field}.privacyAccessedApis`);
|
|
5136
|
-
return {
|
|
5137
|
-
...privacy === undefined ? {} : { privacyAccessedApis: privacy },
|
|
5138
|
-
...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
|
|
5139
|
-
};
|
|
5140
|
-
};
|
|
5141
|
-
var parseProvider = (name, value) => {
|
|
5142
|
-
if (!IDENTIFIER_PATTERN.test(name))
|
|
5143
|
-
throw new TypeError("Device capability names must be identifiers.");
|
|
5144
|
-
if (!object2(value))
|
|
5145
|
-
throw new TypeError(`Device capability ${name} must be an object.`);
|
|
5146
|
-
const factory = text(value.factory, `${name}.factory`);
|
|
5147
|
-
const module = text(value.module, `${name}.module`);
|
|
5148
|
-
if (!IDENTIFIER_PATTERN.test(factory))
|
|
5149
|
-
throw new TypeError(`${name}.factory must be a JavaScript identifier.`);
|
|
5150
|
-
if (!CAPACITOR_MODULE_PATTERN.test(module))
|
|
5151
|
-
throw new TypeError(`${name}.module must be an official devices-capacitor subpath.`);
|
|
5152
|
-
if (!Array.isArray(value.packages) || !value.packages.every((spec) => typeof spec === "string" && CAPACITOR_PACKAGE_PATTERN.test(spec)))
|
|
5153
|
-
throw new TypeError(`${name}.packages must contain exact official Capacitor package versions.`);
|
|
5154
|
-
let native;
|
|
5155
|
-
const { native: nativeMetadata } = value;
|
|
5156
|
-
if (nativeMetadata !== undefined) {
|
|
5157
|
-
if (!object2(nativeMetadata))
|
|
5158
|
-
throw new TypeError(`${name}.native must be an object.`);
|
|
5159
|
-
const { android, ios } = nativeMetadata;
|
|
5160
|
-
const permissions = androidPermissions(android, `${name}.native.android`);
|
|
5161
|
-
const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
|
|
5162
|
-
native = {
|
|
5163
|
-
...permissions === undefined ? {} : { android: { permissions } },
|
|
5164
|
-
...iosRequirements === undefined ? {} : { ios: iosRequirements }
|
|
5165
|
-
};
|
|
5166
|
-
}
|
|
5167
|
-
return {
|
|
5168
|
-
factory,
|
|
5169
|
-
module,
|
|
5170
|
-
...native === undefined ? {} : { native },
|
|
5171
|
-
packages: [...value.packages]
|
|
5172
|
-
};
|
|
5173
|
-
};
|
|
5174
|
-
var absoluteDeviceNativeRequirements = (plan) => {
|
|
5175
|
-
const privacy = plan.capabilities.reduce((requirements, name) => {
|
|
5176
|
-
for (const api of IOS_PRIVACY_ACCESSED_APIS) {
|
|
5177
|
-
const reasons = plan.providers[name]?.native?.ios?.privacyAccessedApis?.[api] ?? [];
|
|
5178
|
-
if (reasons.length === 0)
|
|
5179
|
-
continue;
|
|
5180
|
-
const current = requirements[api] ?? new Set;
|
|
5181
|
-
for (const reason of reasons)
|
|
5182
|
-
current.add(reason);
|
|
5183
|
-
requirements[api] = current;
|
|
5184
|
-
}
|
|
5185
|
-
return requirements;
|
|
5186
|
-
}, {});
|
|
5187
|
-
return {
|
|
5188
|
-
androidPermissions: [
|
|
5189
|
-
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.android?.permissions ?? []))
|
|
5190
|
-
].sort(),
|
|
5191
|
-
iosPrivacyAccessedApis: IOS_PRIVACY_ACCESSED_APIS.flatMap((api) => {
|
|
5192
|
-
const reasons = privacy[api];
|
|
5193
|
-
return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
|
|
5194
|
-
}),
|
|
5195
|
-
iosUsageDescriptions: [
|
|
5196
|
-
...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
|
|
5197
|
-
].sort()
|
|
5198
|
-
};
|
|
5199
|
-
};
|
|
5200
|
-
var loadAbsoluteDeviceCapabilityProviders = (projectRoot) => {
|
|
5201
|
-
const path = join13(resolve11(projectRoot), "node_modules", CAPACITOR_ADAPTER, "package.json");
|
|
5202
|
-
const manifest = readJson(path);
|
|
5203
|
-
const { absolutejs } = manifest;
|
|
5204
|
-
const devices = object2(absolutejs) ? absolutejs.devices : undefined;
|
|
5205
|
-
if (!object2(devices) || devices.format !== 1 || devices.provider !== "capacitor" || !object2(devices.capabilities))
|
|
5206
|
-
throw new TypeError(`${CAPACITOR_ADAPTER} does not publish supported capability metadata.`);
|
|
5207
|
-
const entries = Object.entries(devices.capabilities).map(([name, provider]) => ({
|
|
5208
|
-
name,
|
|
5209
|
-
provider: parseProvider(name, provider)
|
|
5210
|
-
}));
|
|
5211
|
-
return Object.fromEntries(entries.sort((left, right) => left.name.localeCompare(right.name)).map(({ name, provider }) => [name, provider]));
|
|
5212
|
-
};
|
|
5213
|
-
var isIgnored = (path) => path.split("/").some((segment) => IGNORED_DIRECTORIES.has(segment));
|
|
5214
|
-
var importedCapabilities = (source, file) => {
|
|
5215
|
-
const names = new Set;
|
|
5216
|
-
const namespaces = new Set;
|
|
5217
|
-
const visit = (node) => {
|
|
5218
|
-
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && !node.importClause?.isTypeOnly) {
|
|
5219
|
-
const bindings = node.importClause?.namedBindings;
|
|
5220
|
-
if (bindings && ts.isNamedImports(bindings)) {
|
|
5221
|
-
for (const element of bindings.elements)
|
|
5222
|
-
if (!element.isTypeOnly)
|
|
5223
|
-
names.add((element.propertyName ?? element.name).text);
|
|
5224
|
-
}
|
|
5225
|
-
if (bindings && ts.isNamespaceImport(bindings))
|
|
5226
|
-
namespaces.add(bindings.name.text);
|
|
5227
|
-
}
|
|
5228
|
-
if (ts.isExportDeclaration(node) && !node.isTypeOnly && node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === DEVICES_PACKAGE && node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
5229
|
-
for (const element of node.exportClause.elements)
|
|
5230
|
-
if (!element.isTypeOnly)
|
|
5231
|
-
names.add((element.propertyName ?? element.name).text);
|
|
5232
|
-
}
|
|
5233
|
-
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && namespaces.has(node.expression.text))
|
|
5234
|
-
names.add(node.name.text);
|
|
5235
|
-
ts.forEachChild(node, visit);
|
|
5236
|
-
};
|
|
5237
|
-
const extension = extname3(file).toLowerCase();
|
|
5238
|
-
const sources = extension === ".svelte" || extension === ".vue" ? [...source.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script\s*>/giu)].map((match) => match[1]).filter((value) => value !== undefined) : [source];
|
|
5239
|
-
for (const [index, script] of sources.entries())
|
|
5240
|
-
visit(ts.createSourceFile(`${file}#script-${index}`, script, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX));
|
|
5241
|
-
return names;
|
|
5242
|
-
};
|
|
5243
|
-
var assertAbsoluteDeviceCapabilityPackages = (projectRoot, plan) => {
|
|
5244
|
-
const missing = missingAbsoluteDeviceCapabilityPackages(plan, directAbsoluteProjectPackages(projectRoot));
|
|
5245
|
-
const mismatched = plan.requiredPackages.filter((spec) => {
|
|
5246
|
-
const separator = spec.lastIndexOf("@");
|
|
5247
|
-
const packageName = spec.slice(0, separator);
|
|
5248
|
-
if (missing.includes(spec))
|
|
5249
|
-
return false;
|
|
5250
|
-
try {
|
|
5251
|
-
return readJson(join13(resolve11(projectRoot), "node_modules", packageName, "package.json")).version !== spec.slice(separator + 1);
|
|
5252
|
-
} catch {
|
|
5253
|
-
return true;
|
|
5254
|
-
}
|
|
5255
|
-
});
|
|
5256
|
-
const unmet = [...missing, ...mismatched];
|
|
5257
|
-
if (unmet.length > 0)
|
|
5258
|
-
throw new TypeError(`Device capabilities ${plan.capabilities.join(", ")} require ${unmet.join(", ")}. Run absolute mobile sync and approve the detected capability plugins.`);
|
|
5259
|
-
};
|
|
5260
|
-
var directAbsoluteProjectPackages = (projectRoot) => {
|
|
5261
|
-
const manifest = readJson(join13(resolve11(projectRoot), "package.json"));
|
|
5262
|
-
const packages = new Set;
|
|
5263
|
-
for (const field of ["dependencies", "devDependencies"]) {
|
|
5264
|
-
const dependencies = manifest[field];
|
|
5265
|
-
if (object2(dependencies))
|
|
5266
|
-
for (const name of Object.keys(dependencies))
|
|
5267
|
-
packages.add(name);
|
|
5268
|
-
}
|
|
5269
|
-
return packages;
|
|
5270
|
-
};
|
|
5271
|
-
var discoverAbsoluteDeviceCapabilities = (projectRoot, providers = loadAbsoluteDeviceCapabilityProviders(projectRoot)) => {
|
|
5272
|
-
const root = resolve11(projectRoot);
|
|
5273
|
-
const known = new Set(Object.keys(providers));
|
|
5274
|
-
const capabilities = new Set;
|
|
5275
|
-
for (const path of SOURCE_GLOB.scanSync({ cwd: root })) {
|
|
5276
|
-
const portable = relative9(root, resolve11(root, path)).replaceAll("\\", "/");
|
|
5277
|
-
if (isIgnored(portable))
|
|
5278
|
-
continue;
|
|
5279
|
-
const source = readFileSync4(resolve11(root, portable), "utf8");
|
|
5280
|
-
for (const name of importedCapabilities(source, portable))
|
|
5281
|
-
if (known.has(name))
|
|
5282
|
-
capabilities.add(name);
|
|
5283
|
-
}
|
|
5284
|
-
return [...capabilities].sort();
|
|
5285
|
-
};
|
|
5286
|
-
var missingAbsoluteDeviceCapabilityPackages = (plan, directPackages) => plan.requiredPackages.filter((spec) => {
|
|
5287
|
-
const packageName = spec.slice(0, spec.lastIndexOf("@"));
|
|
5288
|
-
return !directPackages.has(packageName);
|
|
5289
|
-
});
|
|
5290
|
-
var resolveAbsoluteDeviceCapabilityPlan = (projectRoot) => {
|
|
5291
|
-
const allProviders = loadAbsoluteDeviceCapabilityProviders(projectRoot);
|
|
5292
|
-
const capabilities = discoverAbsoluteDeviceCapabilities(projectRoot, allProviders);
|
|
5293
|
-
const providers = {};
|
|
5294
|
-
for (const name of capabilities) {
|
|
5295
|
-
const provider = allProviders[name];
|
|
5296
|
-
if (provider)
|
|
5297
|
-
providers[name] = provider;
|
|
5298
|
-
}
|
|
5299
|
-
return {
|
|
5300
|
-
capabilities,
|
|
5301
|
-
providers,
|
|
5302
|
-
requiredPackages: [
|
|
5303
|
-
...new Set(capabilities.flatMap((name) => providers[name]?.packages ?? []))
|
|
5304
|
-
].sort()
|
|
5305
|
-
};
|
|
5306
|
-
};
|
|
5307
|
-
|
|
5308
|
-
// src/mobile/buildPipeline.ts
|
|
5321
|
+
init_deviceCapabilities();
|
|
5309
5322
|
var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes"));
|
|
5310
5323
|
var isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
5311
5324
|
var serverExportName = (loaded, app) => {
|
|
@@ -5380,6 +5393,11 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
5380
5393
|
const sync = auth !== undefined && projectUsesAbsoluteSync(options.projectRoot);
|
|
5381
5394
|
const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
|
|
5382
5395
|
const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot);
|
|
5396
|
+
const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
|
|
5397
|
+
if (usesPush && !auth)
|
|
5398
|
+
throw new TypeError("Portable push notifications require @absolutejs/auth so provider tokens can be registered without exposing identity controls to page code.");
|
|
5399
|
+
if (usesPush && !loaded.app.routes.some((route) => route.path === "/auth/push" || route.path === "/auth/mobile/push"))
|
|
5400
|
+
throw new TypeError("@absolutejs/devices pushNotifications is used, but Auth push is not configured. Pass a trusted server-side registrar to auth({ push: ... }).");
|
|
5383
5401
|
assertAbsoluteDeviceCapabilityPackages(options.projectRoot, deviceCapabilities);
|
|
5384
5402
|
if (auth && !loaded.app.routes.some((route) => route.path === "/.well-known/openid-configuration")) {
|
|
5385
5403
|
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.");
|
|
@@ -5517,6 +5535,10 @@ var installAbsoluteMobileSyncRemediation = (bridge = {
|
|
|
5517
5535
|
registry3.installations.splice(index, 1);
|
|
5518
5536
|
};
|
|
5519
5537
|
};
|
|
5538
|
+
|
|
5539
|
+
// src/mobile/index.ts
|
|
5540
|
+
init_deviceCapabilities();
|
|
5541
|
+
|
|
5520
5542
|
// src/mobile/compatibilityDispatcher.ts
|
|
5521
5543
|
import { Elysia as Elysia2 } from "elysia";
|
|
5522
5544
|
|
|
@@ -5822,6 +5844,7 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
|
|
|
5822
5844
|
};
|
|
5823
5845
|
};
|
|
5824
5846
|
// src/mobile/nativeDeviceCapabilities.ts
|
|
5847
|
+
init_deviceCapabilities();
|
|
5825
5848
|
import { readFile as readFile15, rename as rename12, writeFile as writeFile13 } from "fs/promises";
|
|
5826
5849
|
import { join as join16 } from "path";
|
|
5827
5850
|
var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->";
|
|
@@ -5829,6 +5852,8 @@ var END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->";
|
|
|
5829
5852
|
var NOT_FOUND2 = -1;
|
|
5830
5853
|
var IOS_PRIVACY_FILE_REFERENCE = "A85D0C000000000000000001";
|
|
5831
5854
|
var IOS_PRIVACY_BUILD_FILE = "A85D0C000000000000000002";
|
|
5855
|
+
var PUSH_START_MARKER = "absolutejs:push-notifications:start";
|
|
5856
|
+
var PUSH_END_MARKER = "absolutejs:push-notifications:end";
|
|
5832
5857
|
var escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
5833
5858
|
var writeChangedFile2 = async (path, source) => {
|
|
5834
5859
|
const current = await readFile15(path, "utf8");
|
|
@@ -5839,6 +5864,16 @@ var writeChangedFile2 = async (path, source) => {
|
|
|
5839
5864
|
await rename12(temporary, path);
|
|
5840
5865
|
return true;
|
|
5841
5866
|
};
|
|
5867
|
+
var writeOptionalChangedFile = async (path, source) => {
|
|
5868
|
+
const current = await optionalSource(path);
|
|
5869
|
+
if (current === source)
|
|
5870
|
+
return false;
|
|
5871
|
+
if (current === null) {
|
|
5872
|
+
await writeFile13(path, source, { flag: "wx" });
|
|
5873
|
+
return true;
|
|
5874
|
+
}
|
|
5875
|
+
return writeChangedFile2(path, source);
|
|
5876
|
+
};
|
|
5842
5877
|
var optionalSource = async (path) => {
|
|
5843
5878
|
try {
|
|
5844
5879
|
return await readFile15(path, "utf8");
|
|
@@ -6028,11 +6063,73 @@ ${content}
|
|
|
6028
6063
|
const privacyPath = join16(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
|
|
6029
6064
|
const privacyCurrent = await optionalSource(privacyPath);
|
|
6030
6065
|
const privacySource = privacyManifestSource(privacyCurrent, requirements);
|
|
6031
|
-
const [privacyChanged, projectChanged] = await Promise.all([
|
|
6066
|
+
const [privacyChanged, projectChanged, pushChanged] = await Promise.all([
|
|
6032
6067
|
writeIosPrivacyManifest(privacyPath, privacyCurrent, privacySource),
|
|
6033
|
-
configureIosPrivacyProject(config, requirements)
|
|
6068
|
+
configureIosPrivacyProject(config, requirements),
|
|
6069
|
+
configureIosPushNotifications(config, requirements.iosPushNotifications)
|
|
6034
6070
|
]);
|
|
6035
|
-
return infoChanged || privacyChanged || projectChanged;
|
|
6071
|
+
return infoChanged || privacyChanged || projectChanged || pushChanged;
|
|
6072
|
+
};
|
|
6073
|
+
var configureIosPushNotifications = async (config, enabled) => {
|
|
6074
|
+
const entitlementsPath = join16(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
6075
|
+
const entitlements = await optionalSource(entitlementsPath);
|
|
6076
|
+
if (entitlements === null && !enabled)
|
|
6077
|
+
return false;
|
|
6078
|
+
if (entitlements === null)
|
|
6079
|
+
throw new TypeError("AbsoluteJS iOS entitlements are missing. Run native deep-link projection before device capabilities.");
|
|
6080
|
+
const entitlementRegion = enabled ? ` <!-- ${PUSH_START_MARKER} -->
|
|
6081
|
+
<key>aps-environment</key>
|
|
6082
|
+
<string>development</string>
|
|
6083
|
+
<!-- ${PUSH_END_MARKER} -->
|
|
6084
|
+
` : "";
|
|
6085
|
+
const nextEntitlements = replacePushRegion(entitlements, entitlementRegion, entitlements.lastIndexOf("</dict>"));
|
|
6086
|
+
const delegatePath = join16(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
6087
|
+
const delegate = await optionalSource(delegatePath);
|
|
6088
|
+
if (delegate === null && !enabled)
|
|
6089
|
+
return false;
|
|
6090
|
+
if (delegate === null)
|
|
6091
|
+
throw new TypeError("Capacitor AppDelegate.swift is missing for iOS push notifications.");
|
|
6092
|
+
const hasSuccess = delegate.includes("didRegisterForRemoteNotificationsWithDeviceToken");
|
|
6093
|
+
const hasFailure = delegate.includes("didFailToRegisterForRemoteNotificationsWithError");
|
|
6094
|
+
if (enabled && hasSuccess !== hasFailure && !delegate.includes(PUSH_START_MARKER))
|
|
6095
|
+
throw new TypeError("iOS AppDelegate has a partial custom remote-notification registration implementation.");
|
|
6096
|
+
const alreadyForwarded = enabled && hasSuccess && hasFailure && delegate.includes("capacitorDidRegisterForRemoteNotifications") && delegate.includes("capacitorDidFailToRegisterForRemoteNotifications");
|
|
6097
|
+
const swiftRegion = enabled && !alreadyForwarded ? `
|
|
6098
|
+
// ${PUSH_START_MARKER}
|
|
6099
|
+
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
|
6100
|
+
NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken)
|
|
6101
|
+
}
|
|
6102
|
+
|
|
6103
|
+
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
|
6104
|
+
NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
|
|
6105
|
+
}
|
|
6106
|
+
// ${PUSH_END_MARKER}
|
|
6107
|
+
` : "";
|
|
6108
|
+
const nextDelegate = alreadyForwarded ? delegate : replacePushRegion(delegate, swiftRegion, delegate.lastIndexOf("}"));
|
|
6109
|
+
const changed = await Promise.all([
|
|
6110
|
+
writeChangedFile2(entitlementsPath, nextEntitlements),
|
|
6111
|
+
writeChangedFile2(delegatePath, nextDelegate)
|
|
6112
|
+
]);
|
|
6113
|
+
return changed.some(Boolean);
|
|
6114
|
+
};
|
|
6115
|
+
var replacePushRegion = (source, region, insertion) => {
|
|
6116
|
+
const start = source.indexOf(PUSH_START_MARKER);
|
|
6117
|
+
const end = source.indexOf(PUSH_END_MARKER);
|
|
6118
|
+
if (start < 0 !== end < 0 || start >= 0 && end < start)
|
|
6119
|
+
throw new TypeError("AbsoluteJS push-notification ownership markers are malformed.");
|
|
6120
|
+
if (start >= 0) {
|
|
6121
|
+
const lineStart = source.lastIndexOf(`
|
|
6122
|
+
`, start) + 1;
|
|
6123
|
+
const nextLine = source.indexOf(`
|
|
6124
|
+
`, end + PUSH_END_MARKER.length);
|
|
6125
|
+
const lineEnd = nextLine < 0 ? source.length : nextLine + 1;
|
|
6126
|
+
return `${source.slice(0, lineStart)}${region}${source.slice(lineEnd)}`;
|
|
6127
|
+
}
|
|
6128
|
+
if (!region)
|
|
6129
|
+
return source;
|
|
6130
|
+
if (insertion < 0)
|
|
6131
|
+
throw new TypeError("Could not find a safe native project location for push notifications.");
|
|
6132
|
+
return `${source.slice(0, insertion)}${region}${source.slice(insertion)}`;
|
|
6036
6133
|
};
|
|
6037
6134
|
var configureAndroid2 = async (config, plan) => {
|
|
6038
6135
|
const path = join16(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
@@ -6047,7 +6144,33 @@ ${content}
|
|
|
6047
6144
|
const application = source.indexOf("<application");
|
|
6048
6145
|
const insertion = application === NOT_FOUND2 ? NOT_FOUND2 : source.lastIndexOf(`
|
|
6049
6146
|
`, application) + 1;
|
|
6050
|
-
|
|
6147
|
+
const nextManifest = managed(source, region, insertion);
|
|
6148
|
+
if (!plan.capabilities.includes("pushNotifications"))
|
|
6149
|
+
return writeChangedFile2(path, nextManifest);
|
|
6150
|
+
const firebaseSource = await optionalSource(config.pushAndroidGoogleServicesFile);
|
|
6151
|
+
if (firebaseSource === null)
|
|
6152
|
+
throw new TypeError(`Push notifications require Firebase config at ${config.pushAndroidGoogleServicesFile}. Set mobile.pushNotifications.android.googleServicesFile to override it.`);
|
|
6153
|
+
let firebase;
|
|
6154
|
+
try {
|
|
6155
|
+
firebase = JSON.parse(firebaseSource);
|
|
6156
|
+
} catch (error) {
|
|
6157
|
+
throw new TypeError("Android google-services.json is invalid JSON.", {
|
|
6158
|
+
cause: error
|
|
6159
|
+
});
|
|
6160
|
+
}
|
|
6161
|
+
const clients = typeof firebase === "object" && firebase !== null ? Reflect.get(firebase, "client") : undefined;
|
|
6162
|
+
const matchesApp = Array.isArray(clients) && clients.some((client) => {
|
|
6163
|
+
const info = typeof client === "object" && client !== null ? Reflect.get(client, "client_info") : undefined;
|
|
6164
|
+
const android = typeof info === "object" && info !== null ? Reflect.get(info, "android_client_info") : undefined;
|
|
6165
|
+
return typeof android === "object" && android !== null && Reflect.get(android, "package_name") === config.appId;
|
|
6166
|
+
});
|
|
6167
|
+
if (!matchesApp)
|
|
6168
|
+
throw new TypeError(`Android google-services.json does not contain package ${config.appId}.`);
|
|
6169
|
+
const [manifestChanged, firebaseChanged] = await Promise.all([
|
|
6170
|
+
writeChangedFile2(path, nextManifest),
|
|
6171
|
+
writeOptionalChangedFile(join16(config.nativeProjectDirectory, "android/app/google-services.json"), firebaseSource)
|
|
6172
|
+
]);
|
|
6173
|
+
return manifestChanged || firebaseChanged;
|
|
6051
6174
|
};
|
|
6052
6175
|
var applyAbsoluteNativeDeviceCapabilities = async (projectRoot, config, platforms = config.platforms, plan = resolveAbsoluteDeviceCapabilityPlan(projectRoot)) => {
|
|
6053
6176
|
const results = await Promise.all(platforms.map(async (platform) => ({
|
|
@@ -6714,6 +6837,7 @@ export {
|
|
|
6714
6837
|
publishAbsoluteAndroidRelease,
|
|
6715
6838
|
projectUsesAbsoluteSync,
|
|
6716
6839
|
projectUsesAbsoluteAuth,
|
|
6840
|
+
projectImportsAbsoluteDeviceCapability,
|
|
6717
6841
|
prepareAbsoluteIosRelease,
|
|
6718
6842
|
prepareAbsoluteIosDevProject,
|
|
6719
6843
|
prepareAbsoluteAndroidRelease,
|
|
@@ -6808,5 +6932,5 @@ export {
|
|
|
6808
6932
|
ABSOLUTE_ANDROID_RELEASE_FORMAT
|
|
6809
6933
|
};
|
|
6810
6934
|
|
|
6811
|
-
//# debugId=
|
|
6935
|
+
//# debugId=17F8DCA356CF789D64756E2164756E21
|
|
6812
6936
|
//# sourceMappingURL=index.js.map
|