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

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.
@@ -3680,7 +3680,8 @@ var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
3680
3680
  iosVersion: normalizeIosVersion(config.ios?.version),
3681
3681
  nativeProjectDirectory: resolveProjectPath(projectRoot, config.nativeProject?.directory ?? "mobile", "mobile.nativeProject.directory"),
3682
3682
  platforms: normalizePlatforms(config.platforms),
3683
- productionOrigin
3683
+ productionOrigin,
3684
+ pushAndroidGoogleServicesFile: resolveProjectPath(projectRoot, config.pushNotifications?.android?.googleServicesFile ?? "google-services.json", "mobile.pushNotifications.android.googleServicesFile")
3684
3685
  };
3685
3686
  };
3686
3687
 
@@ -4575,6 +4576,12 @@ var shellSyncModule = () => {
4575
4576
  return candidate;
4576
4577
  throw new TypeError("AbsoluteJS mobile Sync shell module is missing.");
4577
4578
  };
4579
+ var shellPushModule = () => {
4580
+ const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellPush.${extension}`)).find(existsSync2);
4581
+ if (candidate)
4582
+ return candidate;
4583
+ throw new TypeError("AbsoluteJS mobile push shell module is missing.");
4584
+ };
4578
4585
  var escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
4579
4586
  var indexHtml = (appName) => `<!doctype html>
4580
4587
  <html>
@@ -4627,6 +4634,10 @@ var buildShellBootstrap = async (staging, auth, sync, storagePrefix, deviceCapab
4627
4634
  ` : "";
4628
4635
  const options = auth ? `{ createAuth: createAbsoluteMobileShellAuth${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : "";
4629
4636
  const syncImport = sync ? `import { installAbsoluteMobileShellSync } from ${JSON.stringify(shellSyncModule())};
4637
+ ` : "";
4638
+ const pushIndex = deviceCapabilities.capabilities.indexOf("pushNotifications");
4639
+ const push = pushIndex !== -1;
4640
+ const pushImport = push ? `import { createAbsoluteMobileShellPush } from ${JSON.stringify(shellPushModule())};
4630
4641
  ` : "";
4631
4642
  const capabilityImports = (await Promise.all(deviceCapabilities.capabilities.map(async (name, index) => {
4632
4643
  const provider = deviceCapabilities.providers[name];
@@ -4635,14 +4646,17 @@ var buildShellBootstrap = async (staging, auth, sync, storagePrefix, deviceCapab
4635
4646
  return `import { ${provider.factory} as absoluteDeviceCapability${index} } from ${JSON.stringify(await resolveProjectImport(projectRoot, provider.module))};`;
4636
4647
  }))).join(`
4637
4648
  `);
4638
- const capabilityOptions = deviceCapabilities.capabilities.map((name, index) => `${JSON.stringify(name)}: absoluteDeviceCapability${index}()`).join(", ");
4649
+ const pushSetup = push ? `const absoluteMobilePush = createAbsoluteMobileShellPush();
4650
+ const absoluteMobilePushCapability = absoluteDeviceCapability${pushIndex}(absoluteMobilePush.capabilityOptions);
4651
+ ` : "";
4652
+ const capabilityOptions = deviceCapabilities.capabilities.map((name, index) => `${JSON.stringify(name)}: ${name === "pushNotifications" ? "absoluteMobilePushCapability" : `absoluteDeviceCapability${index}()`}`).join(", ");
4639
4653
  const entryPath = join9(staging, ".absolute-mobile-entry.ts");
4640
4654
  const baseAdapterModule = await resolveProjectImport(projectRoot, "@absolutejs/devices-capacitor");
4641
4655
  await writeFile10(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
4642
4656
  import { installCapacitorDeviceAdapterIfNative } from ${JSON.stringify(baseAdapterModule)};
4643
- ${authImport}${syncImport}${capabilityImports}
4644
- installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });
4645
- void startAbsoluteMobileShell(${options});
4657
+ ${authImport}${syncImport}${pushImport}${capabilityImports}
4658
+ ${pushSetup}installCapacitorDeviceAdapterIfNative({ storagePrefix: ${JSON.stringify(storagePrefix)}${capabilityOptions ? `, ${capabilityOptions}` : ""} });
4659
+ void startAbsoluteMobileShell(${push ? `{ createAuth: (config, options) => createAbsoluteMobileShellAuth(config, options), beforeSignOut: absoluteMobilePush.beforeSignOut, connectPush: (auth) => absoluteMobilePush.connect(auth, absoluteMobilePushCapability)${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : options});
4646
4660
  `);
4647
4661
  const build = await Bun.build({
4648
4662
  entrypoints: [entryPath],
@@ -5129,12 +5143,15 @@ var iosNativeRequirements = (value, field) => {
5129
5143
  return;
5130
5144
  if (!object2(value))
5131
5145
  throw new TypeError(`${field} must be an object.`);
5132
- const { privacyAccessedApis, usageDescriptions } = value;
5146
+ const { privacyAccessedApis, pushNotifications, usageDescriptions } = value;
5147
+ if (pushNotifications !== undefined && pushNotifications !== true)
5148
+ throw new TypeError(`${field}.pushNotifications must be true.`);
5133
5149
  if (usageDescriptions !== undefined && (!Array.isArray(usageDescriptions) || !usageDescriptions.every((purpose) => typeof purpose === "string" && IOS_USAGE_DESCRIPTIONS.has(purpose))))
5134
5150
  throw new TypeError(`${field}.usageDescriptions contains an unsupported purpose.`);
5135
5151
  const privacy = iosPrivacyAccessedApis(privacyAccessedApis, `${field}.privacyAccessedApis`);
5136
5152
  return {
5137
5153
  ...privacy === undefined ? {} : { privacyAccessedApis: privacy },
5154
+ ...pushNotifications === true ? { pushNotifications: true } : {},
5138
5155
  ...usageDescriptions === undefined ? {} : { usageDescriptions: [...usageDescriptions] }
5139
5156
  };
5140
5157
  };
@@ -5192,6 +5209,7 @@ var absoluteDeviceNativeRequirements = (plan) => {
5192
5209
  const reasons = privacy[api];
5193
5210
  return reasons ? [{ api, reasons: [...reasons].sort() }] : [];
5194
5211
  }),
5212
+ iosPushNotifications: plan.capabilities.some((name) => plan.providers[name]?.native?.ios?.pushNotifications === true),
5195
5213
  iosUsageDescriptions: [
5196
5214
  ...new Set(plan.capabilities.flatMap((name) => plan.providers[name]?.native?.ios?.usageDescriptions ?? []))
5197
5215
  ].sort()
@@ -5380,6 +5398,11 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
5380
5398
  const sync = auth !== undefined && projectUsesAbsoluteSync(options.projectRoot);
5381
5399
  const syncSchema = sync ? discoverAbsoluteSyncSchema(options.projectRoot) : undefined;
5382
5400
  const deviceCapabilities = resolveAbsoluteDeviceCapabilityPlan(options.projectRoot);
5401
+ const usesPush = deviceCapabilities.capabilities.includes("pushNotifications");
5402
+ if (usesPush && !auth)
5403
+ throw new TypeError("Portable push notifications require @absolutejs/auth so provider tokens can be registered without exposing identity controls to page code.");
5404
+ if (usesPush && !loaded.app.routes.some((route) => route.path === "/auth/mobile/push"))
5405
+ throw new TypeError("@absolutejs/devices pushNotifications is used, but Auth nativePush is not configured. Pass a server-side registrar to auth({ nativePush: ... }).");
5383
5406
  assertAbsoluteDeviceCapabilityPackages(options.projectRoot, deviceCapabilities);
5384
5407
  if (auth && !loaded.app.routes.some((route) => route.path === "/.well-known/openid-configuration")) {
5385
5408
  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.");
@@ -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("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
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
- return writeChangedFile2(path, managed(source, region, insertion));
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) => ({
@@ -6808,5 +6931,5 @@ export {
6808
6931
  ABSOLUTE_ANDROID_RELEASE_FORMAT
6809
6932
  };
6810
6933
 
6811
- //# debugId=443011A81611F30D64756E2164756E21
6934
+ //# debugId=896911BF5E0E827564756E2164756E21
6812
6935
  //# sourceMappingURL=index.js.map