peachsim 0.1.657 → 0.1.658
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 +21 -2
- package/cli/app-config.ts +25 -9
- package/cli/bridge-flow-runner.ts +10 -23
- package/cli/commands/compat.ts +234 -10
- package/cli/commands/do-chain.ts +5 -1
- package/cli/commands/inspect/actions.ts +33 -76
- package/cli/commands/inspect/resolve-target.ts +67 -2
- package/cli/commands/inspect.ts +16 -1
- package/cli/drivers/playwright-provisioning.ts +95 -0
- package/cli/ws-bridge.ts +7 -7
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +9 -1
- package/dist-lib/bridge-contract-input.mjs +9 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +3 -2
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +78 -35
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +51 -8
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +317 -70
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +8 -4
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +300 -29
- package/dist-lib/native-app-identity.cjs +58 -25
- package/dist-lib/native-app-identity.mjs +57 -25
- package/dist-lib/plist.cjs +64 -0
- package/dist-lib/plist.mjs +29 -0
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +2 -2
- package/dist-lib/sdk.cjs +36 -49
- package/dist-lib/sdk.mjs +36 -49
- package/dist-lib/skills.cjs +250 -131
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +51 -8
- package/kotlin/AppSources/android/app/UiModeManager.kt +9 -0
- package/kotlin/AppSources/android/content/Context.kt +9 -2
- package/kotlin/AppSources/android/content/res/Configuration.kt +16 -0
- package/kotlin/AppSources/android/content/res/Resources.kt +7 -0
- package/kotlin/AppSources/android/graphics/Color.kt +13 -0
- package/kotlin/AppSources/android/view/View.kt +2 -0
- package/kotlin/AppSources/android/view/Window.kt +99 -8
- package/kotlin/AppSources/android/view/WindowInsetsController.kt +12 -0
- package/kotlin/AppSources/android/view/WindowManager.kt +16 -0
- package/kotlin/AppSources/androidx/activity/EdgeToEdge.kt +71 -0
- package/kotlin/AppSources/androidx/core/view/WindowCompat.kt +32 -0
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +16 -11
- package/kotlin/AppSources/dev/peach/app/PeachWindowHost.kt +2 -1
- package/kotlin/ComposeSources/PeachComposeHost.kt +11 -1
- package/package.json +8 -1
- package/skills/peach-setup/SKILL.md +27 -0
- package/src/android-theme.ts +167 -0
- package/src/android-xml.ts +70 -0
- package/src/bridge-contract-input.ts +10 -1
- package/src/bridge-contract.ts +7 -1
- package/src/config.ts +8 -0
- package/src/generated/android-framework-themes.json +6303 -0
- package/src/host/replacement-module-handler.ts +33 -0
- package/src/index.ts +1 -0
- package/src/kotlin-app.ts +75 -61
- package/src/kotlin-host.ts +7 -3
- package/src/metro-plugin.ts +46 -3
- package/src/native-app-identity.ts +75 -26
- package/src/sim-client.ts +2 -4
package/dist-lib/metro.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.658 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -62,6 +62,7 @@ function tagRnxBundleForPlugin(bundleUrl, replacementOrigin, replacementToken, p
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// src/host/replacement-module-handler.ts
|
|
65
|
+
var import_node_fs2 = require("node:fs");
|
|
65
66
|
var import_promises = __toESM(require("node:fs/promises"), 1);
|
|
66
67
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
67
68
|
|
|
@@ -191,12 +192,15 @@ function transformsFor(filePath) {
|
|
|
191
192
|
);
|
|
192
193
|
}
|
|
193
194
|
}
|
|
195
|
+
function isLocalSpecifier(specifier) {
|
|
196
|
+
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
197
|
+
}
|
|
194
198
|
function isContrastKitSpecifier(specifier) {
|
|
195
199
|
return specifier === "contrast-kit" || specifier.startsWith("contrast-kit/");
|
|
196
200
|
}
|
|
197
201
|
function unsupportedSpecifier(specifier) {
|
|
198
202
|
return new Error(
|
|
199
|
-
`replacement modules may only import react, react-native, contrast-kit,
|
|
203
|
+
`replacement modules may only import react, react-native, contrast-kit, the packages rnx stubs for this app, and other project files by relative path; '${specifier}' is none of those. vendor the code into a project file, or add the package to peach.config so the simulator bridges it.`
|
|
200
204
|
);
|
|
201
205
|
}
|
|
202
206
|
function bridgeDeclaration(name, specifier) {
|
|
@@ -221,11 +225,9 @@ function compileReplacementModule(input) {
|
|
|
221
225
|
const transformed = (0, import_dist.transform)(input.source, {
|
|
222
226
|
transforms: transformsFor(input.filePath),
|
|
223
227
|
filePath: input.filePath,
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
jsxRuntime: "
|
|
227
|
-
jsxPragma: "React.createElement",
|
|
228
|
-
jsxFragmentPragma: "React.Fragment",
|
|
228
|
+
// the automatic runtime, as @react-native/babel-preset and babel-preset-expo
|
|
229
|
+
// compile the app's own files: jsx needs no React import.
|
|
230
|
+
jsxRuntime: "automatic",
|
|
229
231
|
production: true,
|
|
230
232
|
sourceMapOptions: { compiledFilename: input.filePath }
|
|
231
233
|
});
|
|
@@ -252,6 +254,13 @@ function compileReplacementModule(input) {
|
|
|
252
254
|
return name;
|
|
253
255
|
};
|
|
254
256
|
const edits = [];
|
|
257
|
+
const pointAtLocalModule = (specifierToken, specifier) => {
|
|
258
|
+
edits.push({
|
|
259
|
+
start: specifierToken.start,
|
|
260
|
+
end: specifierToken.end,
|
|
261
|
+
text: JSON.stringify(input.localModuleUrl(specifier))
|
|
262
|
+
});
|
|
263
|
+
};
|
|
255
264
|
for (let index = 0; index < tokens.length; index++) {
|
|
256
265
|
const token = tokens[index];
|
|
257
266
|
if (token.type === EXPORT_KEYWORD) {
|
|
@@ -271,6 +280,10 @@ function compileReplacementModule(input) {
|
|
|
271
280
|
}
|
|
272
281
|
if (fromAt > 0 && tokens[fromAt] && read(tokens[fromAt]) === "from" && tokens[fromAt + 1]?.type === import_types.TokenType.string) {
|
|
273
282
|
const specifier2 = readSpecifier(tokens[fromAt + 1]);
|
|
283
|
+
if (isLocalSpecifier(specifier2)) {
|
|
284
|
+
pointAtLocalModule(tokens[fromAt + 1], specifier2);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
274
287
|
throw new Error(
|
|
275
288
|
`a replacement module cannot re-export from another module, and this one re-exports from '${specifier2}'. import what you need and export the binding.`
|
|
276
289
|
);
|
|
@@ -323,6 +336,10 @@ function compileReplacementModule(input) {
|
|
|
323
336
|
const specifierToken = tokens[cursor];
|
|
324
337
|
if (!specifierToken) throw new Error("replacement module import is unterminated");
|
|
325
338
|
const specifier = readSpecifier(specifierToken);
|
|
339
|
+
if (isLocalSpecifier(specifier)) {
|
|
340
|
+
pointAtLocalModule(specifierToken, specifier);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
326
343
|
if (specifier !== "react" && specifier !== "react-native" && !JSX_RUNTIME_SPECIFIERS.has(specifier) && !isContrastKitSpecifier(specifier) && !input.runtimePackages.has(specifier)) {
|
|
327
344
|
throw unsupportedSpecifier(specifier);
|
|
328
345
|
}
|
|
@@ -379,6 +396,25 @@ function sendJavaScript(req, res, source) {
|
|
|
379
396
|
});
|
|
380
397
|
res.end(req.method === "HEAD" ? void 0 : source);
|
|
381
398
|
}
|
|
399
|
+
function resolveLocalImport(fromFile, specifier) {
|
|
400
|
+
const base = import_node_path.default.resolve(import_node_path.default.dirname(fromFile), specifier);
|
|
401
|
+
const candidates = [
|
|
402
|
+
...REPLACEMENT_MODULE_EXTENSIONS.some(
|
|
403
|
+
(ext) => ext === import_node_path.default.extname(base).toLowerCase()
|
|
404
|
+
) ? [base] : [],
|
|
405
|
+
...REPLACEMENT_MODULE_EXTENSIONS.map((ext) => base + ext),
|
|
406
|
+
...REPLACEMENT_MODULE_EXTENSIONS.map((ext) => import_node_path.default.join(base, `index${ext}`))
|
|
407
|
+
];
|
|
408
|
+
const found = candidates.find(
|
|
409
|
+
(candidate) => (0, import_node_fs2.statSync)(candidate, { throwIfNoEntry: false })?.isFile()
|
|
410
|
+
);
|
|
411
|
+
if (!found) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
`'${specifier}' imported by ${fromFile} matches no ${REPLACEMENT_MODULE_EXTENSIONS.join(", ")} file`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return found;
|
|
417
|
+
}
|
|
382
418
|
function isReplacementModuleRequestUrl(rawUrl) {
|
|
383
419
|
if (!rawUrl) return false;
|
|
384
420
|
try {
|
|
@@ -469,7 +505,14 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
|
|
|
469
505
|
const compiled = compileReplacementModule({
|
|
470
506
|
filePath: resolvedTargetPath,
|
|
471
507
|
source: await import_promises.default.readFile(resolvedTargetPath, "utf8"),
|
|
472
|
-
runtimePackages
|
|
508
|
+
runtimePackages,
|
|
509
|
+
// the imported file's url is this request's with only its path changed,
|
|
510
|
+
// so the token, cache key and runtime packages carry over.
|
|
511
|
+
localModuleUrl: (specifier) => {
|
|
512
|
+
const local = new URL(url.href);
|
|
513
|
+
local.searchParams.set("path", resolveLocalImport(targetPath, specifier));
|
|
514
|
+
return `${local.pathname}${local.search}`;
|
|
515
|
+
}
|
|
473
516
|
});
|
|
474
517
|
const map = Buffer.from(JSON.stringify(compiled.sourceMap), "utf8").toString("base64");
|
|
475
518
|
sendJavaScript(
|
|
@@ -528,26 +571,223 @@ function appendRNXMetroModuleIdentityFooter(bundleText, identity) {
|
|
|
528
571
|
${RNXSIM_METRO_SOURCE_MAP_COMMENT}${detached.sourceMappingUrl}`;
|
|
529
572
|
}
|
|
530
573
|
|
|
574
|
+
// src/native-app-identity.ts
|
|
575
|
+
var NATIVE_APP_IDENTITY_PREFIX = "globalThis.__sootsimNativeAppIdentity=";
|
|
576
|
+
var METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
|
|
577
|
+
function isRecord(value) {
|
|
578
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
579
|
+
}
|
|
580
|
+
function nonEmptyString(value) {
|
|
581
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
582
|
+
}
|
|
583
|
+
function createNativeAppIdentityFooter(identity) {
|
|
584
|
+
const payload = JSON.stringify(identity).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/<\//g, "<\\/");
|
|
585
|
+
return `
|
|
586
|
+
${NATIVE_APP_IDENTITY_PREFIX}${payload};`;
|
|
587
|
+
}
|
|
588
|
+
function nativeAppIdentityFooterStart(source) {
|
|
589
|
+
return source.lastIndexOf(`
|
|
590
|
+
${NATIVE_APP_IDENTITY_PREFIX}`);
|
|
591
|
+
}
|
|
592
|
+
function insertNativeAppIdentityFooter(source, identity) {
|
|
593
|
+
const existing = nativeAppIdentityFooterStart(source);
|
|
594
|
+
const existingEnd = existing < 0 ? -1 : source.includes("\n", existing + 1) ? source.indexOf("\n", existing + 1) : -1;
|
|
595
|
+
const withoutExisting = existing < 0 ? source : source.slice(0, existing) + source.slice(existingEnd < 0 ? source.length : existingEnd);
|
|
596
|
+
const moduleFooter = withoutExisting.lastIndexOf(`
|
|
597
|
+
${METRO_MODULE_PATHS_PREFIX}`);
|
|
598
|
+
const footer = createNativeAppIdentityFooter(identity);
|
|
599
|
+
if (moduleFooter < 0) return withoutExisting + footer;
|
|
600
|
+
return withoutExisting.slice(0, moduleFooter) + footer + withoutExisting.slice(moduleFooter);
|
|
601
|
+
}
|
|
602
|
+
function plistString(plist, key) {
|
|
603
|
+
const match = plist.match(new RegExp(`<key>${key}</key>\\s*<string>([^<]*)</string>`));
|
|
604
|
+
return match ? match[1].trim() : null;
|
|
605
|
+
}
|
|
606
|
+
function resolveBuildSetting(value, settings, depth = 0) {
|
|
607
|
+
if (depth > 8) return null;
|
|
608
|
+
let unresolved = false;
|
|
609
|
+
const resolved = value.replace(/\$\(([^()]+)\)/g, (_match, reference) => {
|
|
610
|
+
const separator = reference.indexOf(":");
|
|
611
|
+
if (separator >= 0) {
|
|
612
|
+
unresolved = true;
|
|
613
|
+
return "";
|
|
614
|
+
}
|
|
615
|
+
const replacement = settings[reference];
|
|
616
|
+
if (replacement === void 0) {
|
|
617
|
+
unresolved = true;
|
|
618
|
+
return "";
|
|
619
|
+
}
|
|
620
|
+
return replacement;
|
|
621
|
+
});
|
|
622
|
+
if (unresolved) return null;
|
|
623
|
+
if (resolved.includes("$(")) return resolveBuildSetting(resolved, settings, depth + 1);
|
|
624
|
+
return resolved;
|
|
625
|
+
}
|
|
626
|
+
function resolvedIdentityField(value) {
|
|
627
|
+
const trimmed = value?.trim() ?? "";
|
|
628
|
+
if (!trimmed || trimmed.includes("$(")) return null;
|
|
629
|
+
return trimmed;
|
|
630
|
+
}
|
|
631
|
+
function identityFieldFrom(settings, raw, fallbackSetting) {
|
|
632
|
+
if (raw !== null) return resolvedIdentityField(resolveBuildSetting(raw, settings));
|
|
633
|
+
return resolvedIdentityField(nonEmptyString(fallbackSetting ?? null));
|
|
634
|
+
}
|
|
635
|
+
function readBuildSettingsBlock(source, start) {
|
|
636
|
+
let depth = 0;
|
|
637
|
+
for (let index = start; index < source.length; index++) {
|
|
638
|
+
const character = source[index];
|
|
639
|
+
if (character === "{") depth++;
|
|
640
|
+
else if (character === "}") {
|
|
641
|
+
depth--;
|
|
642
|
+
if (depth === 0) return source.slice(start, index + 1);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return source.slice(start);
|
|
646
|
+
}
|
|
647
|
+
function parseBuildSettings(block) {
|
|
648
|
+
const settings = {};
|
|
649
|
+
const matchPattern = /([A-Za-z0-9_]+) = ("(?:[^"\\]|\\.)*"|[^;{}]*);/g;
|
|
650
|
+
let match;
|
|
651
|
+
while ((match = matchPattern.exec(block)) !== null) {
|
|
652
|
+
const raw = match[2].trim();
|
|
653
|
+
settings[match[1]] = raw.startsWith('"') ? raw.slice(1, -1) : raw.trim();
|
|
654
|
+
}
|
|
655
|
+
return settings;
|
|
656
|
+
}
|
|
657
|
+
function appTargetReleaseSettings(pbxproj, targetName, infoPlistRelativePath) {
|
|
658
|
+
const configurationPattern = /\/\* (Release|Debug) \*\/ = \{/g;
|
|
659
|
+
const candidates = [];
|
|
660
|
+
let match;
|
|
661
|
+
while ((match = configurationPattern.exec(pbxproj)) !== null) {
|
|
662
|
+
const blockStart = pbxproj.indexOf("{", match.index);
|
|
663
|
+
if (blockStart < 0) continue;
|
|
664
|
+
const block = readBuildSettingsBlock(pbxproj, blockStart);
|
|
665
|
+
const buildSettingsStart = block.indexOf("buildSettings = {");
|
|
666
|
+
if (buildSettingsStart < 0) continue;
|
|
667
|
+
const settings = parseBuildSettings(
|
|
668
|
+
readBuildSettingsBlock(block, block.indexOf("{", buildSettingsStart))
|
|
669
|
+
);
|
|
670
|
+
candidates.push({ name: match[1], settings });
|
|
671
|
+
}
|
|
672
|
+
const plistMatch = (settings) => {
|
|
673
|
+
const plist = settings.INFOPLIST_FILE;
|
|
674
|
+
if (!plist) return false;
|
|
675
|
+
const normalized = plist.replace(/^"|"$/g, "");
|
|
676
|
+
return normalized === infoPlistRelativePath || infoPlistRelativePath.endsWith(`/${normalized}`) || normalized.endsWith(`/${infoPlistRelativePath}`);
|
|
677
|
+
};
|
|
678
|
+
const named = (settings) => resolveBuildSetting(settings.PRODUCT_NAME ?? "", settings) === targetName;
|
|
679
|
+
return candidates.find(
|
|
680
|
+
(candidate) => candidate.name === "Release" && plistMatch(candidate.settings)
|
|
681
|
+
)?.settings ?? candidates.find(
|
|
682
|
+
(candidate) => candidate.name === "Release" && named(candidate.settings)
|
|
683
|
+
)?.settings ?? null;
|
|
684
|
+
}
|
|
685
|
+
function nativeAppIdentityFromIos(files, parseInfoPlist) {
|
|
686
|
+
const pbxprojPath = Object.keys(files).find(
|
|
687
|
+
(path6) => /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path6)
|
|
688
|
+
);
|
|
689
|
+
if (!pbxprojPath) return null;
|
|
690
|
+
const pbxproj = files[pbxprojPath];
|
|
691
|
+
const applicationTargets = new Set(
|
|
692
|
+
[
|
|
693
|
+
...pbxproj.matchAll(
|
|
694
|
+
/isa = PBXNativeTarget;[^}]*?name = "?([^";]+)"?;[^}]*?productType = "com\.apple\.product-type\.application";/g
|
|
695
|
+
)
|
|
696
|
+
].map((match) => match[1])
|
|
697
|
+
);
|
|
698
|
+
const appDelegatePattern = /(?:^|\/)ios\/([^/]+)\/AppDelegate\.(?:swift|mm|m)$/;
|
|
699
|
+
const targetName = Object.keys(files).filter((path6) => !path6.includes("/Pods/")).map((path6) => path6.match(appDelegatePattern)?.[1]).find((name) => name !== void 0 && applicationTargets.has(name)) ?? null;
|
|
700
|
+
if (!targetName) return null;
|
|
701
|
+
const plist = files[`ios/${targetName}/Info.plist`];
|
|
702
|
+
if (!plist) return null;
|
|
703
|
+
const settings = appTargetReleaseSettings(
|
|
704
|
+
pbxproj,
|
|
705
|
+
targetName,
|
|
706
|
+
`${targetName}/Info.plist`
|
|
707
|
+
);
|
|
708
|
+
if (!settings) return null;
|
|
709
|
+
const bundleIdentifier = identityFieldFrom(
|
|
710
|
+
settings,
|
|
711
|
+
plistString(plist, "CFBundleIdentifier"),
|
|
712
|
+
settings.PRODUCT_BUNDLE_IDENTIFIER
|
|
713
|
+
);
|
|
714
|
+
if (!bundleIdentifier) return null;
|
|
715
|
+
const appVersion = identityFieldFrom(
|
|
716
|
+
settings,
|
|
717
|
+
plistString(plist, "CFBundleShortVersionString"),
|
|
718
|
+
settings.MARKETING_VERSION
|
|
719
|
+
);
|
|
720
|
+
const buildVersion = identityFieldFrom(
|
|
721
|
+
settings,
|
|
722
|
+
plistString(plist, "CFBundleVersion"),
|
|
723
|
+
settings.CURRENT_PROJECT_VERSION
|
|
724
|
+
);
|
|
725
|
+
if (!appVersion || !buildVersion) return null;
|
|
726
|
+
const applicationName = identityFieldFrom(settings, plistString(plist, "CFBundleDisplayName"), void 0) ?? identityFieldFrom(
|
|
727
|
+
settings,
|
|
728
|
+
plistString(plist, "CFBundleName"),
|
|
729
|
+
settings.PRODUCT_NAME
|
|
730
|
+
) ?? resolvedIdentityField(resolveBuildSetting(settings.PRODUCT_NAME ?? "", settings)) ?? resolvedIdentityField(targetName);
|
|
731
|
+
if (!applicationName) return null;
|
|
732
|
+
const dictionary = parseInfoPlist(plist);
|
|
733
|
+
if (!isRecord(dictionary)) throw new Error("native app Info.plist is not a dictionary");
|
|
734
|
+
const permissionUsageDescriptions = {};
|
|
735
|
+
for (const [key, value] of Object.entries(dictionary)) {
|
|
736
|
+
if (!/^NS.*UsageDescription$/.test(key)) continue;
|
|
737
|
+
if (typeof value !== "string") {
|
|
738
|
+
permissionUsageDescriptions[key] = value;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
const resolved = resolveBuildSetting(value, settings);
|
|
742
|
+
if (resolved === null) throw new Error(`native app Info.plist ${key} is unresolved`);
|
|
743
|
+
permissionUsageDescriptions[key] = resolved;
|
|
744
|
+
}
|
|
745
|
+
if (settings.GENERATE_INFOPLIST_FILE === "YES") {
|
|
746
|
+
for (const [setting, value] of Object.entries(settings)) {
|
|
747
|
+
if (!/^INFOPLIST_KEY_NS.*UsageDescription$/.test(setting)) continue;
|
|
748
|
+
const key = setting.slice("INFOPLIST_KEY_".length);
|
|
749
|
+
const resolved = resolveBuildSetting(value, settings);
|
|
750
|
+
if (resolved === null)
|
|
751
|
+
throw new Error(`native app build setting ${setting} is unresolved`);
|
|
752
|
+
permissionUsageDescriptions[key] = resolved;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
applicationName,
|
|
757
|
+
appVersion,
|
|
758
|
+
buildVersion,
|
|
759
|
+
bundleIdentifier,
|
|
760
|
+
platform: "ios",
|
|
761
|
+
permissionUsageDescriptions
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
function canExtractNativeAppIdentity(platform) {
|
|
765
|
+
return platform === "ios";
|
|
766
|
+
}
|
|
767
|
+
function extractNativeAppIdentityFromFiles(files, platform, parseInfoPlist) {
|
|
768
|
+
return canExtractNativeAppIdentity(platform) ? nativeAppIdentityFromIos(files, parseInfoPlist) : null;
|
|
769
|
+
}
|
|
770
|
+
|
|
531
771
|
// src/native-firebase-config.ts
|
|
532
|
-
var
|
|
772
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
533
773
|
var import_node_module = require("node:module");
|
|
534
774
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
535
775
|
|
|
536
776
|
// src/is-record.ts
|
|
537
|
-
function
|
|
777
|
+
function isRecord2(value) {
|
|
538
778
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
539
779
|
}
|
|
540
780
|
|
|
541
781
|
// src/plist.ts
|
|
542
782
|
var import_plist = __toESM(require("@expo/plist"), 1);
|
|
543
783
|
function resolvePlistParser(value) {
|
|
544
|
-
if (!
|
|
784
|
+
if (!isRecord2(value)) return null;
|
|
545
785
|
const direct = value.parse;
|
|
546
786
|
if (typeof direct === "function") {
|
|
547
787
|
return (source) => Reflect.apply(direct, value, [source]);
|
|
548
788
|
}
|
|
549
789
|
const nested = value.default;
|
|
550
|
-
if (!
|
|
790
|
+
if (!isRecord2(nested)) return null;
|
|
551
791
|
const nestedParse = nested.parse;
|
|
552
792
|
return typeof nestedParse === "function" ? (source) => Reflect.apply(nestedParse, nested, [source]) : null;
|
|
553
793
|
}
|
|
@@ -565,28 +805,28 @@ function readNativeFirebaseConfig(projectRoot) {
|
|
|
565
805
|
"app.config.ts",
|
|
566
806
|
"app.config.cjs",
|
|
567
807
|
"app.config.mjs"
|
|
568
|
-
].some((name) =>
|
|
808
|
+
].some((name) => import_node_fs3.default.existsSync(import_node_path2.default.join(projectRoot, name))))
|
|
569
809
|
return result;
|
|
570
810
|
const require2 = (0, import_node_module.createRequire)(import_node_path2.default.join(projectRoot, "package.json"));
|
|
571
811
|
const configModule = require2("@expo/config");
|
|
572
|
-
if (!
|
|
812
|
+
if (!isRecord2(configModule) || typeof configModule.getConfig !== "function") {
|
|
573
813
|
throw new Error("Expo native configuration is unavailable.");
|
|
574
814
|
}
|
|
575
815
|
const resolved = Reflect.apply(configModule.getConfig, configModule, [
|
|
576
816
|
projectRoot,
|
|
577
817
|
{ skipSDKVersionRequirement: true }
|
|
578
818
|
]);
|
|
579
|
-
const config =
|
|
819
|
+
const config = isRecord2(resolved) && isRecord2(resolved.exp) ? resolved.exp : {};
|
|
580
820
|
for (const platform of ["ios", "android"]) {
|
|
581
821
|
const native = config[platform];
|
|
582
|
-
if (!
|
|
822
|
+
if (!isRecord2(native) || typeof native.googleServicesFile !== "string") continue;
|
|
583
823
|
try {
|
|
584
|
-
const source =
|
|
824
|
+
const source = import_node_fs3.default.readFileSync(
|
|
585
825
|
import_node_path2.default.resolve(projectRoot, native.googleServicesFile),
|
|
586
826
|
"utf8"
|
|
587
827
|
);
|
|
588
828
|
const parsed = platform === "ios" ? parsePlistSource(source) : JSON.parse(source);
|
|
589
|
-
if (!
|
|
829
|
+
if (!isRecord2(parsed)) throw new Error("Invalid Google services file.");
|
|
590
830
|
if (platform === "ios") {
|
|
591
831
|
result[platform] = {
|
|
592
832
|
apiKey: parsed.API_KEY,
|
|
@@ -601,31 +841,31 @@ function readNativeFirebaseConfig(projectRoot) {
|
|
|
601
841
|
deepLinkUrlScheme: parsed.REVERSED_CLIENT_ID
|
|
602
842
|
};
|
|
603
843
|
} else {
|
|
604
|
-
const project =
|
|
844
|
+
const project = isRecord2(parsed.project_info) ? parsed.project_info : {};
|
|
605
845
|
const clients = Array.isArray(parsed.client) ? parsed.client : [];
|
|
606
846
|
const client = clients.find((value) => {
|
|
607
|
-
if (!
|
|
847
|
+
if (!isRecord2(value) || !isRecord2(value.client_info)) return false;
|
|
608
848
|
const android = value.client_info.android_client_info;
|
|
609
|
-
return
|
|
849
|
+
return isRecord2(android) && android.package_name === native.package;
|
|
610
850
|
});
|
|
611
|
-
if (!
|
|
851
|
+
if (!isRecord2(client) || !isRecord2(client.client_info))
|
|
612
852
|
throw new Error("No Google services client matches the Android package.");
|
|
613
853
|
const apiKeys = Array.isArray(client.api_key) ? client.api_key : [];
|
|
614
854
|
const apiKey = apiKeys.find(
|
|
615
|
-
(value) =>
|
|
855
|
+
(value) => isRecord2(value) && typeof value.current_key === "string"
|
|
616
856
|
);
|
|
617
857
|
const oauthClients = Array.isArray(client.oauth_client) ? client.oauth_client : [];
|
|
618
858
|
const oauth = oauthClients.find(
|
|
619
|
-
(value) =>
|
|
859
|
+
(value) => isRecord2(value) && value.client_type === 3
|
|
620
860
|
);
|
|
621
861
|
result[platform] = {
|
|
622
|
-
apiKey:
|
|
862
|
+
apiKey: isRecord2(apiKey) ? apiKey.current_key : void 0,
|
|
623
863
|
appId: client.client_info.mobilesdk_app_id,
|
|
624
864
|
projectId: project.project_id,
|
|
625
865
|
databaseURL: project.firebase_url,
|
|
626
866
|
storageBucket: project.storage_bucket,
|
|
627
867
|
messagingSenderId: project.project_number,
|
|
628
|
-
clientId:
|
|
868
|
+
clientId: isRecord2(oauth) ? oauth.client_id : void 0
|
|
629
869
|
};
|
|
630
870
|
}
|
|
631
871
|
} catch (error) {
|
|
@@ -646,7 +886,7 @@ function isValidRuntimeVersion(value) {
|
|
|
646
886
|
}
|
|
647
887
|
|
|
648
888
|
// src/home-paths.ts
|
|
649
|
-
var
|
|
889
|
+
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
650
890
|
var import_node_os = require("node:os");
|
|
651
891
|
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
652
892
|
var PEACH_HOME_ENV = "PEACH_HOME";
|
|
@@ -667,7 +907,7 @@ function activeRuntimeFile() {
|
|
|
667
907
|
}
|
|
668
908
|
function readActiveRuntime() {
|
|
669
909
|
try {
|
|
670
|
-
const value =
|
|
910
|
+
const value = import_node_fs4.default.readFileSync(activeRuntimeFile(), "utf8").trim();
|
|
671
911
|
return value.length > 0 ? value : null;
|
|
672
912
|
} catch {
|
|
673
913
|
return null;
|
|
@@ -950,7 +1190,38 @@ function withRNX(config) {
|
|
|
950
1190
|
customSerializer: async (entryPoint, preModules, graph, serializerOptions) => {
|
|
951
1191
|
const serializer = existingSerializer || (defaultSerializer ||= createMetroDefaultSerializer(cfgProjectRoot));
|
|
952
1192
|
const result = await serializer(entryPoint, preModules, graph, serializerOptions);
|
|
953
|
-
|
|
1193
|
+
let annotated = appendRNXModulePaths(
|
|
1194
|
+
result,
|
|
1195
|
+
graph,
|
|
1196
|
+
serializerOptions,
|
|
1197
|
+
cfgProjectRoot
|
|
1198
|
+
);
|
|
1199
|
+
const iosRoot = import_path2.default.join(cfgProjectRoot, "ios");
|
|
1200
|
+
const bundlePlatform = graph.transformOptions?.platform;
|
|
1201
|
+
if (canExtractNativeAppIdentity(bundlePlatform) && import_fs2.default.existsSync(iosRoot)) {
|
|
1202
|
+
const files = {};
|
|
1203
|
+
for (const entry of import_fs2.default.readdirSync(iosRoot, { withFileTypes: true })) {
|
|
1204
|
+
if (!entry.isDirectory() || entry.name === "Pods") continue;
|
|
1205
|
+
const names = entry.name.endsWith(".xcodeproj") ? ["project.pbxproj"] : ["Info.plist", "AppDelegate.swift", "AppDelegate.mm", "AppDelegate.m"];
|
|
1206
|
+
for (const name of names) {
|
|
1207
|
+
const relative2 = `ios/${entry.name}/${name}`;
|
|
1208
|
+
const file = import_path2.default.join(cfgProjectRoot, relative2);
|
|
1209
|
+
if (import_fs2.default.existsSync(file)) files[relative2] = import_fs2.default.readFileSync(file, "utf8");
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
const identity = extractNativeAppIdentityFromFiles(
|
|
1213
|
+
files,
|
|
1214
|
+
bundlePlatform,
|
|
1215
|
+
parsePlistSource
|
|
1216
|
+
);
|
|
1217
|
+
if (identity) {
|
|
1218
|
+
annotated = typeof annotated === "string" ? insertNativeAppIdentityFooter(annotated, identity) : {
|
|
1219
|
+
...annotated,
|
|
1220
|
+
code: insertNativeAppIdentityFooter(annotated.code, identity)
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return annotated;
|
|
954
1225
|
}
|
|
955
1226
|
}
|
|
956
1227
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.658 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -23,6 +23,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
23
23
|
var native_app_identity_exports = {};
|
|
24
24
|
__export(native_app_identity_exports, {
|
|
25
25
|
applyNativeAppIdentity: () => applyNativeAppIdentity,
|
|
26
|
+
canExtractNativeAppIdentity: () => canExtractNativeAppIdentity,
|
|
26
27
|
createNativeAppIdentityFooter: () => createNativeAppIdentityFooter,
|
|
27
28
|
detachNativeAppIdentityFooter: () => detachNativeAppIdentityFooter,
|
|
28
29
|
extractNativeAppIdentityFromFiles: () => extractNativeAppIdentityFromFiles,
|
|
@@ -43,6 +44,8 @@ function nonEmptyString(value) {
|
|
|
43
44
|
}
|
|
44
45
|
function isNativeAppIdentity(value) {
|
|
45
46
|
if (!isRecord(value)) return false;
|
|
47
|
+
const descriptions = value.permissionUsageDescriptions;
|
|
48
|
+
if (descriptions !== void 0 && !isRecord(descriptions)) return false;
|
|
46
49
|
const platform = value.platform;
|
|
47
50
|
if (platform !== "ios" && platform !== "android") return false;
|
|
48
51
|
const applicationName = value.applicationName;
|
|
@@ -64,7 +67,10 @@ function readNativeAppIdentity(value) {
|
|
|
64
67
|
appVersion: value.appVersion,
|
|
65
68
|
buildVersion: value.buildVersion,
|
|
66
69
|
bundleIdentifier: value.bundleIdentifier,
|
|
67
|
-
platform: value.platform
|
|
70
|
+
platform: value.platform,
|
|
71
|
+
...value.permissionUsageDescriptions === void 0 ? {} : {
|
|
72
|
+
permissionUsageDescriptions: { ...value.permissionUsageDescriptions }
|
|
73
|
+
}
|
|
68
74
|
};
|
|
69
75
|
}
|
|
70
76
|
function applyNativeAppIdentity(identity) {
|
|
@@ -169,7 +175,7 @@ function readBuildSettingsBlock(source, start) {
|
|
|
169
175
|
}
|
|
170
176
|
function parseBuildSettings(block) {
|
|
171
177
|
const settings = {};
|
|
172
|
-
const matchPattern = /([A-
|
|
178
|
+
const matchPattern = /([A-Za-z0-9_]+) = ("(?:[^"\\]|\\.)*"|[^;{}]*);/g;
|
|
173
179
|
let match;
|
|
174
180
|
while ((match = matchPattern.exec(block)) !== null) {
|
|
175
181
|
const raw = match[2].trim();
|
|
@@ -205,27 +211,29 @@ function appTargetReleaseSettings(pbxproj, targetName, infoPlistRelativePath) {
|
|
|
205
211
|
(candidate) => candidate.name === "Release" && named(candidate.settings)
|
|
206
212
|
)?.settings ?? null;
|
|
207
213
|
}
|
|
208
|
-
function nativeAppIdentityFromIos(files) {
|
|
214
|
+
function nativeAppIdentityFromIos(files, parseInfoPlist) {
|
|
215
|
+
const pbxprojPath = Object.keys(files).find(
|
|
216
|
+
(path) => /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path)
|
|
217
|
+
);
|
|
218
|
+
if (!pbxprojPath) return null;
|
|
219
|
+
const pbxproj = files[pbxprojPath];
|
|
220
|
+
const applicationTargets = new Set(
|
|
221
|
+
[
|
|
222
|
+
...pbxproj.matchAll(
|
|
223
|
+
/isa = PBXNativeTarget;[^}]*?name = "?([^";]+)"?;[^}]*?productType = "com\.apple\.product-type\.application";/g
|
|
224
|
+
)
|
|
225
|
+
].map((match) => match[1])
|
|
226
|
+
);
|
|
209
227
|
const appDelegatePattern = /(?:^|\/)ios\/([^/]+)\/AppDelegate\.(?:swift|mm|m)$/;
|
|
210
|
-
|
|
211
|
-
for (const path of Object.keys(files)) {
|
|
212
|
-
if (path.includes("/Pods/")) continue;
|
|
213
|
-
const match = path.match(appDelegatePattern);
|
|
214
|
-
if (match) {
|
|
215
|
-
targetName = match[1];
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
228
|
+
const targetName = Object.keys(files).filter((path) => !path.includes("/Pods/")).map((path) => path.match(appDelegatePattern)?.[1]).find((name) => name !== void 0 && applicationTargets.has(name)) ?? null;
|
|
219
229
|
if (!targetName) return null;
|
|
220
|
-
const
|
|
221
|
-
(path) => path === `ios/${targetName}/Info.plist`
|
|
222
|
-
);
|
|
223
|
-
const plist = plistPath ? files[plistPath] : null;
|
|
230
|
+
const plist = files[`ios/${targetName}/Info.plist`];
|
|
224
231
|
if (!plist) return null;
|
|
225
|
-
const
|
|
226
|
-
|
|
232
|
+
const settings = appTargetReleaseSettings(
|
|
233
|
+
pbxproj,
|
|
234
|
+
targetName,
|
|
235
|
+
`${targetName}/Info.plist`
|
|
227
236
|
);
|
|
228
|
-
const settings = pbxprojPath ? appTargetReleaseSettings(files[pbxprojPath], targetName, `${targetName}/Info.plist`) : null;
|
|
229
237
|
if (!settings) return null;
|
|
230
238
|
const bundleIdentifier = identityFieldFrom(
|
|
231
239
|
settings,
|
|
@@ -250,23 +258,48 @@ function nativeAppIdentityFromIos(files) {
|
|
|
250
258
|
settings.PRODUCT_NAME
|
|
251
259
|
) ?? resolvedIdentityField(resolveBuildSetting(settings.PRODUCT_NAME ?? "", settings)) ?? resolvedIdentityField(targetName);
|
|
252
260
|
if (!applicationName) return null;
|
|
261
|
+
const dictionary = parseInfoPlist(plist);
|
|
262
|
+
if (!isRecord(dictionary)) throw new Error("native app Info.plist is not a dictionary");
|
|
263
|
+
const permissionUsageDescriptions = {};
|
|
264
|
+
for (const [key, value] of Object.entries(dictionary)) {
|
|
265
|
+
if (!/^NS.*UsageDescription$/.test(key)) continue;
|
|
266
|
+
if (typeof value !== "string") {
|
|
267
|
+
permissionUsageDescriptions[key] = value;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const resolved = resolveBuildSetting(value, settings);
|
|
271
|
+
if (resolved === null) throw new Error(`native app Info.plist ${key} is unresolved`);
|
|
272
|
+
permissionUsageDescriptions[key] = resolved;
|
|
273
|
+
}
|
|
274
|
+
if (settings.GENERATE_INFOPLIST_FILE === "YES") {
|
|
275
|
+
for (const [setting, value] of Object.entries(settings)) {
|
|
276
|
+
if (!/^INFOPLIST_KEY_NS.*UsageDescription$/.test(setting)) continue;
|
|
277
|
+
const key = setting.slice("INFOPLIST_KEY_".length);
|
|
278
|
+
const resolved = resolveBuildSetting(value, settings);
|
|
279
|
+
if (resolved === null)
|
|
280
|
+
throw new Error(`native app build setting ${setting} is unresolved`);
|
|
281
|
+
permissionUsageDescriptions[key] = resolved;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
253
284
|
return {
|
|
254
285
|
applicationName,
|
|
255
286
|
appVersion,
|
|
256
287
|
buildVersion,
|
|
257
288
|
bundleIdentifier,
|
|
258
|
-
platform: "ios"
|
|
289
|
+
platform: "ios",
|
|
290
|
+
permissionUsageDescriptions
|
|
259
291
|
};
|
|
260
292
|
}
|
|
261
|
-
function
|
|
262
|
-
return
|
|
293
|
+
function canExtractNativeAppIdentity(platform) {
|
|
294
|
+
return platform === "ios";
|
|
263
295
|
}
|
|
264
|
-
function extractNativeAppIdentityFromFiles(files, platform) {
|
|
265
|
-
return platform
|
|
296
|
+
function extractNativeAppIdentityFromFiles(files, platform, parseInfoPlist) {
|
|
297
|
+
return canExtractNativeAppIdentity(platform) ? nativeAppIdentityFromIos(files, parseInfoPlist) : null;
|
|
266
298
|
}
|
|
267
299
|
// Annotate the CommonJS export names for ESM import in node:
|
|
268
300
|
0 && (module.exports = {
|
|
269
301
|
applyNativeAppIdentity,
|
|
302
|
+
canExtractNativeAppIdentity,
|
|
270
303
|
createNativeAppIdentityFooter,
|
|
271
304
|
detachNativeAppIdentityFooter,
|
|
272
305
|
extractNativeAppIdentityFromFiles,
|