@absolutejs/absolute 0.20.0-beta.22 → 0.20.0-beta.24

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.
@@ -5077,6 +5077,12 @@ var IOS_USAGE_DESCRIPTIONS = new Set([
5077
5077
  "photo-library",
5078
5078
  "photo-library-add"
5079
5079
  ]);
5080
+ var IOS_PRIVACY_ACCESSED_API_REASONS = {
5081
+ NSPrivacyAccessedAPICategoryFileTimestamp: new Set(["C617.1"])
5082
+ };
5083
+ var IOS_PRIVACY_ACCESSED_APIS = [
5084
+ "NSPrivacyAccessedAPICategoryFileTimestamp"
5085
+ ];
5080
5086
  var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
5081
5087
  var readJson = (path) => {
5082
5088
  const value = JSON.parse(readFileSync4(path, "utf8"));
@@ -5099,15 +5105,38 @@ var androidPermissions = (value, field) => {
5099
5105
  throw new TypeError(`${field}.permissions must contain Android permission names.`);
5100
5106
  return [...permissions];
5101
5107
  };
5102
- var iosUsageDescriptions = (value, field) => {
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) => {
5103
5128
  if (value === undefined)
5104
5129
  return;
5105
5130
  if (!object2(value))
5106
5131
  throw new TypeError(`${field} must be an object.`);
5107
- const { usageDescriptions } = value;
5108
- if (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose)))
5132
+ const { privacyAccessedApis, usageDescriptions } = value;
5133
+ if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
5109
5134
  throw new TypeError(`${field}.usageDescriptions contains an unsupported purpose.`);
5110
- return [...usageDescriptions];
5135
+ const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field}.privacyAccessedApis`);
5136
+ return {
5137
+ ...privacy === undefined ? {} : { privacyAccessedApis: privacy },
5138
+ ...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
5139
+ };
5111
5140
  };
5112
5141
  var parseProvider = (name, value) => {
5113
5142
  if (!IDENTIFIER_PATTERN.test(name))
@@ -5129,10 +5158,10 @@ var parseProvider = (name, value) => {
5129
5158
  throw new TypeError(`${name}.native must be an object.`);
5130
5159
  const { android, ios } = nativeMetadata;
5131
5160
  const permissions = androidPermissions(android, `${name}.native.android`);
5132
- const usageDescriptions = iosUsageDescriptions(ios, `${name}.native.ios`);
5161
+ const iosRequirements = iosNativeRequirements(ios, `${name}.native.ios`);
5133
5162
  native = {
5134
5163
  ...permissions === undefined ? {} : { android: { permissions } },
5135
- ...usageDescriptions === undefined ? {} : { ios: { usageDescriptions } }
5164
+ ...iosRequirements === undefined ? {} : { ios: iosRequirements }
5136
5165
  };
5137
5166
  }
5138
5167
  return {
@@ -5142,14 +5171,32 @@ var parseProvider = (name, value) => {
5142
5171
  packages: [...value.packages]
5143
5172
  };
5144
5173
  };
5145
- var absoluteDeviceNativeRequirements = (plan) => ({
5146
- androidPermissions: [
5147
- ...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.android?.permissions ?? []))
5148
- ].sort(),
5149
- iosUsageDescriptions: [
5150
- ...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
5151
- ].sort()
5152
- });
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
+ };
5153
5200
  var loadAbsoluteDeviceCapabilityProviders = (projectRoot) => {
5154
5201
  const path = join13(resolve11(projectRoot), "node_modules", CAPACITOR_ADAPTER, "package.json");
5155
5202
  const manifest = readJson(path);
@@ -5780,6 +5827,8 @@ import { join as join16 } from "path";
5780
5827
  var START_MARKER2 = "<!-- absolutejs:device-capabilities:start -->";
5781
5828
  var END_MARKER2 = "<!-- absolutejs:device-capabilities:end -->";
5782
5829
  var NOT_FOUND2 = -1;
5830
+ var IOS_PRIVACY_FILE_REFERENCE = "A85D0C000000000000000001";
5831
+ var IOS_PRIVACY_BUILD_FILE = "A85D0C000000000000000002";
5783
5832
  var escapeXml2 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
5784
5833
  var writeChangedFile2 = async (path, source) => {
5785
5834
  const current = await readFile15(path, "utf8");
@@ -5790,6 +5839,15 @@ var writeChangedFile2 = async (path, source) => {
5790
5839
  await rename12(temporary, path);
5791
5840
  return true;
5792
5841
  };
5842
+ var optionalSource = async (path) => {
5843
+ try {
5844
+ return await readFile15(path, "utf8");
5845
+ } catch (error) {
5846
+ if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT")
5847
+ return null;
5848
+ throw error;
5849
+ }
5850
+ };
5793
5851
  var managed = (source, region, insertion) => {
5794
5852
  const start = source.indexOf(START_MARKER2);
5795
5853
  const end = source.indexOf(END_MARKER2);
@@ -5827,6 +5885,134 @@ var iosDescription = (appName, purpose) => {
5827
5885
  return `${appName} does not track location in the background; this description supports the foreground location provider required by the native runtime.`;
5828
5886
  return `${appName} adds to your photo library only for photo actions you choose.`;
5829
5887
  };
5888
+ var privacyEntries = (requirements) => requirements.iosPrivacyAccessedApis.map(({ api, reasons }) => ` <dict>
5889
+ <key>NSPrivacyAccessedAPIType</key>
5890
+ <string>${escapeXml2(api)}</string>
5891
+ <key>NSPrivacyAccessedAPITypeReasons</key>
5892
+ <array>
5893
+ ${reasons.map((reason) => ` <string>${escapeXml2(reason)}</string>`).join(`
5894
+ `)}
5895
+ </array>
5896
+ </dict>`).join(`
5897
+ `);
5898
+ var privacyManifestSource = (source, requirements) => {
5899
+ const entries = privacyEntries(requirements);
5900
+ const wholeRegion = entries ? ` ${START_MARKER2}
5901
+ <key>NSPrivacyAccessedAPITypes</key>
5902
+ <array>
5903
+ ${entries}
5904
+ </array>
5905
+ ${END_MARKER2}
5906
+ ` : "";
5907
+ if (source === null)
5908
+ return entries ? `<?xml version="1.0" encoding="UTF-8"?>
5909
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
5910
+ <plist version="1.0">
5911
+ <dict>
5912
+ ${wholeRegion} <key>NSPrivacyCollectedDataTypes</key>
5913
+ <array/>
5914
+ <key>NSPrivacyTracking</key>
5915
+ <false/>
5916
+ </dict>
5917
+ </plist>
5918
+ ` : null;
5919
+ const start = source.indexOf(START_MARKER2);
5920
+ if (start !== NOT_FOUND2) {
5921
+ const end = source.indexOf(END_MARKER2);
5922
+ if (end === NOT_FOUND2)
5923
+ throw new TypeError("AbsoluteJS device-capability ownership markers are malformed.");
5924
+ const owned = source.slice(start, end);
5925
+ const ownsWholeKey = owned.includes("NSPrivacyAccessedAPITypes");
5926
+ let region = "";
5927
+ if (ownsWholeKey)
5928
+ region = wholeRegion;
5929
+ else if (entries)
5930
+ region = ` ${START_MARKER2}
5931
+ ${entries}
5932
+ ${END_MARKER2}
5933
+ `;
5934
+ return managed(source, region, source.lastIndexOf("</dict>"));
5935
+ }
5936
+ const key = source.indexOf("<key>NSPrivacyAccessedAPITypes</key>");
5937
+ if (key === NOT_FOUND2)
5938
+ return managed(source, wholeRegion, source.lastIndexOf("</dict>"));
5939
+ if (!entries)
5940
+ return source;
5941
+ const array = source.indexOf("<array>", key);
5942
+ if (array === NOT_FOUND2)
5943
+ throw new TypeError("iOS PrivacyInfo.xcprivacy has a malformed NSPrivacyAccessedAPITypes value.");
5944
+ const insertion = source.indexOf(`
5945
+ `, array);
5946
+ if (insertion === NOT_FOUND2)
5947
+ throw new TypeError("iOS PrivacyInfo.xcprivacy array is malformed.");
5948
+ return managed(source, ` ${START_MARKER2}
5949
+ ${entries}
5950
+ ${END_MARKER2}
5951
+ `, insertion + 1);
5952
+ };
5953
+ var writeIosPrivacyManifest = async (path, current, source) => {
5954
+ if (source === null)
5955
+ return false;
5956
+ if (current !== null)
5957
+ return writeChangedFile2(path, source);
5958
+ await writeFile13(path, source, { flag: "wx" });
5959
+ return true;
5960
+ };
5961
+ var configureIosPrivacyProject = async (config, requirements) => {
5962
+ if (requirements.iosPrivacyAccessedApis.length === 0)
5963
+ return false;
5964
+ const projectPath = join16(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
5965
+ const project = await readFile15(projectPath, "utf8");
5966
+ return writeChangedFile2(projectPath, addIosPrivacyProjectReference(project));
5967
+ };
5968
+ var addIosPrivacyProjectReference = (source) => {
5969
+ const fileMatch = source.match(/([A-F0-9]{24}) \/\* PrivacyInfo\.xcprivacy \*\/ = \{isa = PBXFileReference;/u);
5970
+ const fileReference = fileMatch?.[1] ?? IOS_PRIVACY_FILE_REFERENCE;
5971
+ const buildMatch = source.match(/([A-F0-9]{24}) \/\* PrivacyInfo\.xcprivacy in Resources \*\/ = \{isa = PBXBuildFile;/u);
5972
+ const buildFile = buildMatch?.[1] ?? IOS_PRIVACY_BUILD_FILE;
5973
+ if (!fileMatch && source.includes(fileReference) || !buildMatch && source.includes(buildFile))
5974
+ throw new TypeError("AbsoluteJS iOS privacy-manifest identifiers collide.");
5975
+ let next = source;
5976
+ if (!buildMatch) {
5977
+ const marker = "/* End PBXBuildFile section */";
5978
+ const index = next.indexOf(marker);
5979
+ if (index === NOT_FOUND2)
5980
+ throw new TypeError("Could not find the iOS PBXBuildFile section.");
5981
+ next = `${next.slice(0, index)} ${buildFile} /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = ${fileReference} /* PrivacyInfo.xcprivacy */; };
5982
+ ${next.slice(index)}`;
5983
+ }
5984
+ if (!fileMatch) {
5985
+ const marker = "/* End PBXFileReference section */";
5986
+ const index = next.indexOf(marker);
5987
+ if (index === NOT_FOUND2)
5988
+ throw new TypeError("Could not find the iOS PBXFileReference section.");
5989
+ next = `${next.slice(0, index)} ${fileReference} /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
5990
+ ${next.slice(index)}`;
5991
+ }
5992
+ const groupsStart = next.indexOf("/* Begin PBXGroup section */");
5993
+ const groupsEnd = next.indexOf("/* End PBXGroup section */");
5994
+ const groups = next.slice(groupsStart, groupsEnd);
5995
+ if (!groups.includes(`${fileReference} /* PrivacyInfo.xcprivacy */`)) {
5996
+ const appGroup = groups.match(/[A-F0-9]{24} \/\* App \*\/ = \{\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = \(\n/u);
5997
+ if (!appGroup || appGroup.index === undefined)
5998
+ throw new TypeError("Could not find the iOS App PBXGroup.");
5999
+ const index = groupsStart + appGroup.index + appGroup[0].length;
6000
+ next = `${next.slice(0, index)} ${fileReference} /* PrivacyInfo.xcprivacy */,
6001
+ ${next.slice(index)}`;
6002
+ }
6003
+ const resourcesStart = next.indexOf("/* Begin PBXResourcesBuildPhase section */");
6004
+ const resourcesEnd = next.indexOf("/* End PBXResourcesBuildPhase section */");
6005
+ const resources = next.slice(resourcesStart, resourcesEnd);
6006
+ if (!resources.includes(`${buildFile} /* PrivacyInfo.xcprivacy in Resources */`)) {
6007
+ const files = resources.match(/isa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = \d+;\n\t\t\tfiles = \(\n/u);
6008
+ if (!files || files.index === undefined)
6009
+ throw new TypeError("Could not find the iOS Resources build phase.");
6010
+ const index = resourcesStart + files.index + files[0].length;
6011
+ next = `${next.slice(0, index)} ${buildFile} /* PrivacyInfo.xcprivacy in Resources */,
6012
+ ${next.slice(index)}`;
6013
+ }
6014
+ return next;
6015
+ };
5830
6016
  var configureIos2 = async (config, plan) => {
5831
6017
  const path = join16(config.nativeProjectDirectory, "ios/App/App/Info.plist");
5832
6018
  const source = await readFile15(path, "utf8");
@@ -5838,7 +6024,15 @@ var configureIos2 = async (config, plan) => {
5838
6024
  ${content}
5839
6025
  ${END_MARKER2}
5840
6026
  ` : "";
5841
- return writeChangedFile2(path, managed(source, region, source.lastIndexOf("</dict>")));
6027
+ const infoChanged = await writeChangedFile2(path, managed(source, region, source.lastIndexOf("</dict>")));
6028
+ const privacyPath = join16(config.nativeProjectDirectory, "ios/App/App/PrivacyInfo.xcprivacy");
6029
+ const privacyCurrent = await optionalSource(privacyPath);
6030
+ const privacySource = privacyManifestSource(privacyCurrent, requirements);
6031
+ const [privacyChanged, projectChanged] = await Promise.all([
6032
+ writeIosPrivacyManifest(privacyPath, privacyCurrent, privacySource),
6033
+ configureIosPrivacyProject(config, requirements)
6034
+ ]);
6035
+ return infoChanged || privacyChanged || projectChanged;
5842
6036
  };
5843
6037
  var configureAndroid2 = async (config, plan) => {
5844
6038
  const path = join16(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
@@ -6614,5 +6808,5 @@ export {
6614
6808
  ABSOLUTE_ANDROID_RELEASE_FORMAT
6615
6809
  };
6616
6810
 
6617
- //# debugId=1579CD5E4A79E32964756E2164756E21
6811
+ //# debugId=443011A81611F30D64756E2164756E21
6618
6812
  //# sourceMappingURL=index.js.map