@pushmesh/sdk 0.7.3-rc.4 → 0.7.3

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/dist/cjs/boot.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true}),exports.boot=boot;const auto_1=require("./inapp/auto"),permissions_1=require("./permissions"),players_1=require("./players");async function boot(config){if(true===config.inAppAutoMontar&&(0,auto_1.montarInAppAutomaticamente)(),await(0,players_1.init)(config),(0,permissions_1.attachAppStateHook)(),true===config.pedirPermissao)try{await(0,permissions_1.reativarNotificacoes)()}catch(e){console.warn("[PushMesh] pedirPermissao falhou",e)}(0,permissions_1.checkAndReport)().then(permissions_1.avisarSeMudo).catch(e=>console.warn("[PushMesh] checkAndReport falhou",e))}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true}),exports.boot=boot;const auto_1=require("./inapp/auto"),permissions_1=require("./permissions"),players_1=require("./players");async function boot(config){if(true===config.inAppAutoMontar&&(0,auto_1.montarInAppAutomaticamente)(),await(0,players_1.init)(config),(0,permissions_1.attachAppStateHook)(),true===config.pedirPermissao)try{await(0,permissions_1.reativarNotificacoes)({comAjustes:false})}catch(e){console.warn("[PushMesh] pedirPermissao falhou",e)}(0,permissions_1.checkAndReport)().then(permissions_1.avisarSeMudo).catch(e=>console.warn("[PushMesh] checkAndReport falhou",e))}
@@ -20,7 +20,7 @@
20
20
  * linha sozinho (script `version` → scripts/sync-versao.mjs); editando à mão,
21
21
  * suba as duas.
22
22
  */
23
- export declare const SDK_VERSAO = "0.7.3-rc.4";
23
+ export declare const SDK_VERSAO = "0.7.3";
24
24
  export interface PerfilDispositivo {
25
25
  device_os?: string;
26
26
  fabricante?: string;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true}),exports.SDK_VERSAO=void 0,exports.coletarPerfil=coletarPerfil;const react_native_1=require("react-native"),storage_1=require("./storage");function moduloPerfil(){const mods=react_native_1.NativeModules;return"ios"===react_native_1.Platform.OS?mods.PushMeshApns:mods.PushMeshFirebase}function timezoneId(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||void 0}catch{return}}function pais(){try{const loc=Intl.DateTimeFormat().resolvedOptions().locale,m=/[-_]([A-Za-z]{2})(?:[-_]|$)/.exec(loc);return m?m[1].toUpperCase():void 0}catch{return}}async function proximaSessao(){const atual=Number(await(0,storage_1.getItem)(storage_1.KEYS.sessoes)??"0"),proxima=Number.isFinite(atual)&&atual>=0?atual+1:1;return await(0,storage_1.setItem)(storage_1.KEYS.sessoes,String(proxima)),proxima}function semVazios(p){const out={};for(const[k,v]of Object.entries(p))null!=v&&""!==v&&(out[k]=v);return out}async function coletarPerfil(){const perfil={sdk_versao:exports.SDK_VERSAO,timezone_id:timezoneId(),pais:pais()};try{perfil.sessoes=await proximaSessao()}catch{}try{const nativo=moduloPerfil();if(nativo?.perfilDispositivo){const n=await nativo.perfilDispositivo();Object.assign(perfil,{device_os:n.device_os,fabricante:n.fabricante,modelo:n.modelo,net_type:n.net_type,carrier:n.carrier,rooted:n.rooted,standby_bucket:n.standby_bucket,bateria_irrestrita:n.bateria_irrestrita,notif_permissoes:n.notif_permissoes,instalacao_id:n.instalacao_id})}}catch{}if(!perfil.device_os){const v=react_native_1.Platform.Version;perfil.device_os="string"==typeof v?v:String(v)}if(!perfil.fabricante){const c=react_native_1.Platform.constants;perfil.fabricante="ios"===react_native_1.Platform.OS?"Apple":c?.Manufacturer}return semVazios(perfil)}exports.SDK_VERSAO="0.7.3-rc.4";
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true}),exports.SDK_VERSAO=void 0,exports.coletarPerfil=coletarPerfil;const react_native_1=require("react-native"),storage_1=require("./storage");function moduloPerfil(){const mods=react_native_1.NativeModules;return"ios"===react_native_1.Platform.OS?mods.PushMeshApns:mods.PushMeshFirebase}function timezoneId(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||void 0}catch{return}}function pais(){try{const loc=Intl.DateTimeFormat().resolvedOptions().locale,m=/[-_]([A-Za-z]{2})(?:[-_]|$)/.exec(loc);return m?m[1].toUpperCase():void 0}catch{return}}async function proximaSessao(){const atual=Number(await(0,storage_1.getItem)(storage_1.KEYS.sessoes)??"0"),proxima=Number.isFinite(atual)&&atual>=0?atual+1:1;return await(0,storage_1.setItem)(storage_1.KEYS.sessoes,String(proxima)),proxima}function semVazios(p){const out={};for(const[k,v]of Object.entries(p))null!=v&&""!==v&&(out[k]=v);return out}async function coletarPerfil(){const perfil={sdk_versao:exports.SDK_VERSAO,timezone_id:timezoneId(),pais:pais()};try{perfil.sessoes=await proximaSessao()}catch{}try{const nativo=moduloPerfil();if(nativo?.perfilDispositivo){const n=await nativo.perfilDispositivo();Object.assign(perfil,{device_os:n.device_os,fabricante:n.fabricante,modelo:n.modelo,net_type:n.net_type,carrier:n.carrier,rooted:n.rooted,standby_bucket:n.standby_bucket,bateria_irrestrita:n.bateria_irrestrita,notif_permissoes:n.notif_permissoes,instalacao_id:n.instalacao_id})}}catch{}if(!perfil.device_os){const v=react_native_1.Platform.Version;perfil.device_os="string"==typeof v?v:String(v)}if(!perfil.fabricante){const c=react_native_1.Platform.constants;perfil.fabricante="ios"===react_native_1.Platform.OS?"Apple":c?.Manufacturer}return semVazios(perfil)}exports.SDK_VERSAO="0.7.3";
@@ -35,7 +35,9 @@ export declare function openNotificationSettings(): Promise<void>;
35
35
  *
36
36
  * @returns true se, ao final, a permissão está concedida.
37
37
  */
38
- export declare function reativarNotificacoes(): Promise<boolean>;
38
+ export declare function reativarNotificacoes(opcoes?: {
39
+ comAjustes?: boolean;
40
+ }): Promise<boolean>;
39
41
  /**
40
42
  * Lê a permissão e reporta ao servidor SE mudou desde o último reporte.
41
43
  * Detecta a virada (reativação): se havia campanha Reativa aguardando e o
@@ -1 +1 @@
1
- "use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(o,m,k,k2){void 0===k2&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);desc&&!("get"in desc?!m.__esModule:desc.writable||desc.configurable)||(desc={enumerable:true,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){void 0===k2&&(k2=k),o[k2]=m[k]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(o,v){Object.defineProperty(o,"default",{enumerable:true,value:v})}:function(o,v){o.default=v}),__importStar=this&&this.__importStar||function(){var ownKeys=function(o){return ownKeys=Object.getOwnPropertyNames||function(o){var ar=[];for(var k in o)Object.prototype.hasOwnProperty.call(o,k)&&(ar[ar.length]=k);return ar},ownKeys(o)};return function(mod){if(mod&&mod.__esModule)return mod;var result={};if(null!=mod)for(var k=ownKeys(mod),i=0;i<k.length;i++)"default"!==k[i]&&__createBinding(result,mod,k[i]);return __setModuleDefault(result,mod),result}}();Object.defineProperty(exports,"__esModule",{value:true}),exports.mapAuthorizationStatus=mapAuthorizationStatus,exports.readNotificationTypes=readNotificationTypes,exports.canRequestNatively=canRequestNatively,exports.openNotificationSettings=openNotificationSettings,exports.reativarNotificacoes=reativarNotificacoes,exports.checkAndReport=checkAndReport,exports.markReativaPending=markReativaPending,exports.attachAppStateHook=attachAppStateHook,exports.detachAppStateHook=detachAppStateHook,exports.avisarSeMudo=avisarSeMudo;const react_native_1=__importStar(require("@notifee/react-native")),react_native_2=require("react-native"),queue_1=require("./queue"),state_1=require("./state"),storage_1=require("./storage"),types_1=require("./types");function mapAuthorizationStatus(status){switch(status){case react_native_1.AuthorizationStatus.AUTHORIZED:case react_native_1.AuthorizationStatus.PROVISIONAL:return types_1.RpNotificationTypes.Ativo;case react_native_1.AuthorizationStatus.DENIED:return types_1.RpNotificationTypes.Bloqueado;case react_native_1.AuthorizationStatus.NOT_DETERMINED:default:return types_1.RpNotificationTypes.NuncaPerguntou}}async function readNotificationTypes(){return mapAuthorizationStatus((await react_native_1.default.getNotificationSettings()).authorizationStatus)}async function canRequestNatively(){return(await react_native_1.default.getNotificationSettings()).authorizationStatus===react_native_1.AuthorizationStatus.NOT_DETERMINED&&("ios"===react_native_2.Platform.OS||"android"===react_native_2.Platform.OS&&Number(react_native_2.Platform.Version)>=33)}async function openNotificationSettings(){if("android"===react_native_2.Platform.OS)return void await react_native_1.default.openNotificationSettings();const nativo=react_native_2.NativeModules.PushMeshApns;if(nativo?.abrirAjustesDeNotificacao)try{if(await nativo.abrirAjustesDeNotificacao())return}catch{}await react_native_2.Linking.openSettings()}async function reativarNotificacoes(){return await canRequestNatively()?await react_native_1.default.requestPermission():await openNotificationSettings(),1===await checkAndReport()}async function checkAndReport(){const current=await readNotificationTypes(),previousRaw=await(0,storage_1.getItem)(storage_1.KEYS.notificationTypes),previous=null===previousRaw?null:Number(previousRaw);if(previous===current)return current;const playerId=state_1.state.playerId??await(0,storage_1.getItem)(storage_1.KEYS.playerId);if(playerId&&state_1.state.config){const{sendOrQueue:sendOrQueue}=await Promise.resolve().then(()=>__importStar(require("./queue")));await sendOrQueue("PUT",`/api/v1/players/${playerId}`,{app_id:(0,state_1.requireConfig)().appId,notification_types:current}),await(0,storage_1.setItem)(storage_1.KEYS.notificationTypes,String(current))}if(current===types_1.RpNotificationTypes.Ativo&&null!==previous&&previous!==types_1.RpNotificationTypes.Ativo){const reativaPending=await(0,storage_1.getItem)(storage_1.KEYS.reativaPending);if(reativaPending){const{track:track}=await Promise.resolve().then(()=>__importStar(require("./inapp")));await track("reativou",reativaPending),await(0,storage_1.setItem)(storage_1.KEYS.reativaPending,"")}}return current}async function markReativaPending(messageId){await(0,storage_1.setItem)(storage_1.KEYS.reativaPending,messageId)}function attachAppStateHook(){state_1.state.appStateSubscription||(state_1.state.appStateSubscription=react_native_2.AppState.addEventListener("change",nextState=>{if("active"===nextState){const bgDesde=state_1.state.emBackgroundDesde;state_1.state.emBackgroundDesde=null,!state_1.state.playerId&&state_1.state.token&&state_1.state.config?Promise.resolve().then(()=>__importStar(require("./players"))).then(m=>m.register()).catch(()=>{}).finally(()=>{(0,queue_1.flushQueue)().catch(()=>{})}):(0,queue_1.flushQueue)().catch(()=>{}),checkAndReport().catch(e=>console.warn("[PushMesh] checkAndReport falhou",e)),Promise.resolve().then(()=>__importStar(require("./inapp/lifecycle"))).then(m=>m.aoVoltarAoForeground(bgDesde)).catch(()=>{})}else null===state_1.state.emBackgroundDesde&&(state_1.state.emBackgroundDesde=Date.now())}))}function detachAppStateHook(){state_1.state.appStateSubscription?.remove(),state_1.state.appStateSubscription=null}function avisarSeMudo(tipos){if(1===tipos)return;const comoResolver="chame await PushMesh.permissions.reativarNotificacoes() no momento certo do seu fluxo, ou passe { pedirPermissao: true } no init para pedir logo no boot.";console.warn(0===tipos?`[PushMesh] Este aparelho está com a notificação BLOQUEADA. O push vai ser aceito pelo FCM/APNs e NÃO vai aparecer na tela — e o recibo de entrega nunca chega. Como resolver: ${comoResolver}`:`[PushMesh] A permissão de notificação ainda não foi pedida a esta pessoa (Android 13+ e iOS exigem o pedido). Enquanto isso o push é entregue e não aparece. Como resolver: ${comoResolver}`)}
1
+ "use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(o,m,k,k2){void 0===k2&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);desc&&!("get"in desc?!m.__esModule:desc.writable||desc.configurable)||(desc={enumerable:true,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){void 0===k2&&(k2=k),o[k2]=m[k]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(o,v){Object.defineProperty(o,"default",{enumerable:true,value:v})}:function(o,v){o.default=v}),__importStar=this&&this.__importStar||function(){var ownKeys=function(o){return ownKeys=Object.getOwnPropertyNames||function(o){var ar=[];for(var k in o)Object.prototype.hasOwnProperty.call(o,k)&&(ar[ar.length]=k);return ar},ownKeys(o)};return function(mod){if(mod&&mod.__esModule)return mod;var result={};if(null!=mod)for(var k=ownKeys(mod),i=0;i<k.length;i++)"default"!==k[i]&&__createBinding(result,mod,k[i]);return __setModuleDefault(result,mod),result}}();Object.defineProperty(exports,"__esModule",{value:true}),exports.mapAuthorizationStatus=mapAuthorizationStatus,exports.readNotificationTypes=readNotificationTypes,exports.canRequestNatively=canRequestNatively,exports.openNotificationSettings=openNotificationSettings,exports.reativarNotificacoes=reativarNotificacoes,exports.checkAndReport=checkAndReport,exports.markReativaPending=markReativaPending,exports.attachAppStateHook=attachAppStateHook,exports.detachAppStateHook=detachAppStateHook,exports.avisarSeMudo=avisarSeMudo;const react_native_1=__importStar(require("@notifee/react-native")),react_native_2=require("react-native"),queue_1=require("./queue"),state_1=require("./state"),storage_1=require("./storage"),types_1=require("./types");function mapAuthorizationStatus(status){switch(status){case react_native_1.AuthorizationStatus.AUTHORIZED:case react_native_1.AuthorizationStatus.PROVISIONAL:return types_1.RpNotificationTypes.Ativo;case react_native_1.AuthorizationStatus.DENIED:return types_1.RpNotificationTypes.Bloqueado;case react_native_1.AuthorizationStatus.NOT_DETERMINED:default:return types_1.RpNotificationTypes.NuncaPerguntou}}async function readNotificationTypes(){return mapAuthorizationStatus((await react_native_1.default.getNotificationSettings()).authorizationStatus)}async function canRequestNatively(){const settings=await react_native_1.default.getNotificationSettings();return"android"===react_native_2.Platform.OS?settings.authorizationStatus!==react_native_1.AuthorizationStatus.AUTHORIZED&&Number(react_native_2.Platform.Version)>=33:settings.authorizationStatus===react_native_1.AuthorizationStatus.NOT_DETERMINED}async function openNotificationSettings(){if("android"===react_native_2.Platform.OS)return void await react_native_1.default.openNotificationSettings();const nativo=react_native_2.NativeModules.PushMeshApns;if(nativo?.abrirAjustesDeNotificacao)try{if(await nativo.abrirAjustesDeNotificacao())return}catch{}await react_native_2.Linking.openSettings()}async function reativarNotificacoes(opcoes={}){const{comAjustes:comAjustes=true}=opcoes;if(await canRequestNatively()){if(await react_native_1.default.requestPermission(),1===await checkAndReport())return true;if("android"!==react_native_2.Platform.OS)return false}return!!comAjustes&&(await openNotificationSettings(),1===await checkAndReport())}async function checkAndReport(){const current=await readNotificationTypes(),previousRaw=await(0,storage_1.getItem)(storage_1.KEYS.notificationTypes),previous=null===previousRaw?null:Number(previousRaw);if(previous===current)return current;const playerId=state_1.state.playerId??await(0,storage_1.getItem)(storage_1.KEYS.playerId);if(playerId&&state_1.state.config){const{sendOrQueue:sendOrQueue}=await Promise.resolve().then(()=>__importStar(require("./queue")));await sendOrQueue("PUT",`/api/v1/players/${playerId}`,{app_id:(0,state_1.requireConfig)().appId,notification_types:current}),await(0,storage_1.setItem)(storage_1.KEYS.notificationTypes,String(current))}if(current===types_1.RpNotificationTypes.Ativo&&null!==previous&&previous!==types_1.RpNotificationTypes.Ativo){const reativaPending=await(0,storage_1.getItem)(storage_1.KEYS.reativaPending);if(reativaPending){const{track:track}=await Promise.resolve().then(()=>__importStar(require("./inapp")));await track("reativou",reativaPending),await(0,storage_1.setItem)(storage_1.KEYS.reativaPending,"")}}return current}async function markReativaPending(messageId){await(0,storage_1.setItem)(storage_1.KEYS.reativaPending,messageId)}function attachAppStateHook(){state_1.state.appStateSubscription||(state_1.state.appStateSubscription=react_native_2.AppState.addEventListener("change",nextState=>{if("active"===nextState){const bgDesde=state_1.state.emBackgroundDesde;state_1.state.emBackgroundDesde=null,!state_1.state.playerId&&state_1.state.token&&state_1.state.config?Promise.resolve().then(()=>__importStar(require("./players"))).then(m=>m.register()).catch(()=>{}).finally(()=>{(0,queue_1.flushQueue)().catch(()=>{})}):(0,queue_1.flushQueue)().catch(()=>{}),checkAndReport().catch(e=>console.warn("[PushMesh] checkAndReport falhou",e)),Promise.resolve().then(()=>__importStar(require("./inapp/lifecycle"))).then(m=>m.aoVoltarAoForeground(bgDesde)).catch(()=>{})}else null===state_1.state.emBackgroundDesde&&(state_1.state.emBackgroundDesde=Date.now())}))}function detachAppStateHook(){state_1.state.appStateSubscription?.remove(),state_1.state.appStateSubscription=null}function avisarSeMudo(tipos){if(1===tipos)return;const comoResolver="chame await PushMesh.permissions.reativarNotificacoes() no momento certo do seu fluxo, ou passe { pedirPermissao: true } no init para pedir logo no boot.";console.warn(0===tipos?`[PushMesh] Este aparelho está com a notificação BLOQUEADA. O push vai ser aceito pelo FCM/APNs e NÃO vai aparecer na tela — e o recibo de entrega nunca chega. Como resolver: ${comoResolver}`:`[PushMesh] A permissão de notificação ainda não foi pedida a esta pessoa (Android 13+ e iOS exigem o pedido). Enquanto isso o push é entregue e não aparece. Como resolver: ${comoResolver}`)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushmesh/sdk",
3
- "version": "0.7.3-rc.4",
3
+ "version": "0.7.3",
4
4
  "description": "React Native SDK for PushMesh — push notifications with real delivery receipts, permission re-activation and in-app banners. Classic API included. (Guia em português: README.pt-BR.md)",
5
5
  "license": "MIT",
6
6
  "private": false,
package/plugin/index.js CHANGED
@@ -51,6 +51,9 @@ const pkg = require('../package.json');
51
51
  * lá faz a extensão não rodar EM SILÊNCIO. */
52
52
  const ALVO_NSE = 'PushMeshNotificationService';
53
53
 
54
+ /** O pbxproj às vezes cita o valor, às vezes não — comparar cru erra metade. */
55
+ const desaspas = v => String(v ?? '').replace(/^"|"$/g, '');
56
+
54
57
  // ---------------------------------------------------------------------------
55
58
  // iOS — 1. capability Push Notifications
56
59
  // ---------------------------------------------------------------------------
@@ -126,6 +129,27 @@ const comArquivosDaExtensao = config =>
126
129
  },
127
130
  ]);
128
131
 
132
+ /**
133
+ * UUID do grupo RAIZ do projeto.
134
+ *
135
+ * Primeiro pela API (`getFirstProject`), que é o caminho certo. O laço pelo hash
136
+ * é rede de proteção para versões do node-xcode sem esse método — e é o que a
137
+ * versão anterior fazia sozinha, adivinhando "o grupo sem nome e sem caminho".
138
+ * Adivinhar era o problema: quando errava, ninguém ficava sabendo.
139
+ */
140
+ const grupoRaiz = projeto => {
141
+ if (typeof projeto.getFirstProject === 'function') {
142
+ const uuid = projeto.getFirstProject()?.firstProject?.mainGroup;
143
+ if (uuid) return uuid;
144
+ }
145
+ const projetos = projeto.hash?.project?.objects?.PBXProject ?? {};
146
+ for (const chave of Object.keys(projetos)) {
147
+ const uuid = projetos[chave]?.mainGroup;
148
+ if (uuid) return uuid;
149
+ }
150
+ return undefined;
151
+ };
152
+
129
153
  /** Cria o alvo da extensão no projeto do Xcode. */
130
154
  const comAlvoDaExtensao = config =>
131
155
  withXcodeProject(config, cfg => {
@@ -148,34 +172,114 @@ const comAlvoDaExtensao = config =>
148
172
  ALVO_NSE,
149
173
  ALVO_NSE,
150
174
  );
151
- // Pendura o grupo na raiz do projeto (o grupo sem nome é a raiz).
152
- const grupos = projeto.hash.project.objects.PBXGroup;
153
- for (const chave of Object.keys(grupos)) {
154
- if (grupos[chave].name === undefined && grupos[chave].path === undefined) {
155
- projeto.addToPbxGroup(grupo.uuid, chave);
156
- break;
157
- }
175
+ // Pendura o grupo no grupo RAIZ declarado pelo próprio projeto.
176
+ //
177
+ // Antes isto procurava "o grupo sem nome e sem caminho" e torcia para ser a
178
+ // raiz. Quando o palpite errava, o grupo ficava ÓRFÃO — e aí vem a parte
179
+ // cruel: o `pod install`, que o prebuild roda logo em seguida, abre o
180
+ // projeto com a gem `xcodeproj` e PODA objetos inalcançáveis ao salvar. O
181
+ // grupo e o NotificationService.swift sumiam, o alvo continuava lá, e a
182
+ // extensão era compilada VAZIA — .appex com Info.plist e nenhum executável.
183
+ // O sintoma aparecia só na instalação ("is missing its bundle executable"),
184
+ // longe da causa, e no aparelho a imagem do push simplesmente não baixava.
185
+ const raiz = grupoRaiz(projeto);
186
+ if (!raiz) {
187
+ // FALHAR ALTO. Sem raiz, o grupo fica órfão, o `pod install` o poda, e a
188
+ // extensão é compilada vazia — o app nem instala, com um erro que fala de
189
+ // "bundle executable" e não diz nada sobre a causa. Melhor quebrar aqui.
190
+ throw new Error(
191
+ '[@pushmesh/sdk] não encontrei o grupo raiz do projeto do Xcode para ' +
192
+ 'pendurar a extensão de notificação. Rode `npx expo prebuild --clean` ' +
193
+ 'e, se persistir, abra uma issue com a versão do @expo/config-plugins.',
194
+ );
158
195
  }
196
+ projeto.addToPbxGroup(grupo.uuid, raiz);
159
197
 
160
198
  const alvo = projeto.addTarget(ALVO_NSE, 'app_extension', ALVO_NSE, `${bundleApp}.${ALVO_NSE}`);
161
- projeto.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', alvo.uuid);
199
+ // O arquivo entra JUNTO com a fase. Criar a fase vazia e depois chamar
200
+ // addSourceFile deixava o PBXBuildFile solto, fora da fase — e o
201
+ // `pod install` seguinte podava esse objeto inalcançável. O alvo compilava
202
+ // sem nenhuma fonte e o .appex saía sem executável.
203
+ projeto.addBuildPhase(
204
+ ['NotificationService.swift'],
205
+ 'PBXSourcesBuildPhase',
206
+ 'Sources',
207
+ alvo.uuid,
208
+ );
162
209
  projeto.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', alvo.uuid);
163
210
  projeto.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', alvo.uuid);
164
- projeto.addSourceFile('NotificationService.swift', { target: alvo.uuid }, grupo.uuid);
165
211
 
166
212
  // O alvo TEM de aceitar as mesmas versões de iOS que o app: o Xcode cria a
167
213
  // extensão no iOS mais novo, e aí ela simplesmente NÃO RODA em aparelho
168
214
  // com versão anterior — sem erro, sem aviso, a imagem só não aparece.
169
215
  const minimoDoApp = cfg.ios?.deploymentTarget ?? '15.1';
216
+
217
+ // O TIME de assinatura precisa ser HERDADO do app. Sem ele a build morre
218
+ // com "Signing for PushMeshNotificationService requires a development
219
+ // team" — e o desenvolvedor, que nunca pediu esta extensão, fica olhando
220
+ // um erro sobre um alvo que ele não criou. Ordem de busca: o que o Expo
221
+ // configurou (`ios.appleTeamId`), senão o que já está no alvo do APP.
170
222
  const configs = projeto.pbxXCBuildConfigurationSection();
223
+
224
+ // As versões da extensão TÊM de ser as mesmas do app. O Info.plist dela
225
+ // referencia $(MARKETING_VERSION) e $(CURRENT_PROJECT_VERSION); sem essas
226
+ // duas no alvo, o Xcode substitui por vazio, as chaves SOMEM do plist
227
+ // gerado, e o iOS recusa instalar o APP INTEIRO com "Invalid placeholder
228
+ // attributes" — um erro que fala de uma extensão que o desenvolvedor nunca
229
+ // criou e não diz o que fazer.
230
+ let versaoMarketing;
231
+ let versaoProjeto;
232
+ {
233
+ const nomeApp = desaspas(`"${cfg.modRequest?.projectName ?? ''}"`);
234
+ for (const chave of Object.keys(configs)) {
235
+ const b = configs[chave].buildSettings;
236
+ if (!b || desaspas(b.PRODUCT_NAME) !== nomeApp) continue;
237
+ if (b.MARKETING_VERSION) versaoMarketing = b.MARKETING_VERSION;
238
+ if (b.CURRENT_PROJECT_VERSION) versaoProjeto = b.CURRENT_PROJECT_VERSION;
239
+ if (versaoMarketing && versaoProjeto) break;
240
+ }
241
+ }
242
+ versaoMarketing = versaoMarketing ?? `"${cfg.version ?? '1.0.0'}"`;
243
+ versaoProjeto = versaoProjeto ?? `"${cfg.ios?.buildNumber ?? '1'}"`;
244
+
245
+ let time = cfg.ios?.appleTeamId;
246
+ if (!time) {
247
+ const nomeApp = `"${cfg.modRequest?.projectName ?? ''}"`;
248
+ for (const chave of Object.keys(configs)) {
249
+ const b = configs[chave].buildSettings;
250
+ if (b?.DEVELOPMENT_TEAM && desaspas(b.PRODUCT_NAME) === desaspas(nomeApp)) {
251
+ time = b.DEVELOPMENT_TEAM;
252
+ break;
253
+ }
254
+ }
255
+ }
256
+ if (!time) {
257
+ // Último recurso: QUALQUER alvo que já tenha time (o app, em projeto
258
+ // gerado pelo Expo, é o único com um antes de a extensão existir).
259
+ for (const chave of Object.keys(configs)) {
260
+ const t = configs[chave].buildSettings?.DEVELOPMENT_TEAM;
261
+ if (t && t !== '""') {
262
+ time = String(t).replace(/"/g, '');
263
+ break;
264
+ }
265
+ }
266
+ }
267
+
171
268
  for (const chave of Object.keys(configs)) {
172
269
  const build = configs[chave].buildSettings;
173
- if (!build || build.PRODUCT_NAME !== `"${ALVO_NSE}"`) continue;
270
+ // O pbxproj grava PRODUCT_NAME SEM aspas quando o nome não tem espaço —
271
+ // comparar só com a forma entre aspas fazia este laço não casar NUNCA, e
272
+ // todos os ajustes abaixo (inclusive o time de assinatura) silenciosamente
273
+ // não aconteciam. A build só morria lá na frente, no CodeSign.
274
+ if (!build || desaspas(build.PRODUCT_NAME) !== ALVO_NSE) continue;
174
275
  build.IPHONEOS_DEPLOYMENT_TARGET = minimoDoApp;
175
276
  build.INFOPLIST_FILE = `"${ALVO_NSE}/Info.plist"`;
176
277
  build.SWIFT_VERSION = '5.0';
177
278
  build.TARGETED_DEVICE_FAMILY = '"1,2"';
279
+ build.MARKETING_VERSION = versaoMarketing;
280
+ build.CURRENT_PROJECT_VERSION = versaoProjeto;
178
281
  build.CODE_SIGN_STYLE = 'Automatic';
282
+ if (time) build.DEVELOPMENT_TEAM = time;
179
283
  }
180
284
  return cfg;
181
285
  });