@absolutejs/absolute 0.20.0-beta.3 → 0.20.0-beta.5
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/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/angular/index.js +341 -22
- package/dist/angular/index.js.map +8 -5
- package/dist/angular/server.js +341 -22
- package/dist/angular/server.js.map +8 -5
- package/dist/build.js +94 -22
- package/dist/build.js.map +10 -10
- package/dist/cli/index.js +1750 -655
- package/dist/dev/client/cssUtils.ts +16 -2
- package/dist/dev/client/handlers/rebuild.ts +11 -1
- package/dist/dev/client/hmrTiming.ts +14 -7
- package/dist/index.js +364 -191
- package/dist/index.js.map +18 -18
- package/dist/mobile/browser.js +14 -1
- package/dist/mobile/browser.js.map +3 -3
- package/dist/mobile/index.js +1233 -160
- package/dist/mobile/index.js.map +17 -13
- package/dist/mobile/remoteMacAgentEntry.js +29 -0
- package/dist/src/angular/pageHandler.d.ts +3 -0
- package/dist/src/cli/config/server.d.ts +1 -1
- package/dist/src/core/pageHandlers.d.ts +11 -2
- package/dist/src/mobile/androidEmulatorController.d.ts +6 -1
- package/dist/src/mobile/buildPipeline.d.ts +1 -0
- package/dist/src/mobile/capacitorBundle.d.ts +11 -1
- package/dist/src/mobile/client.d.ts +4 -0
- package/dist/src/mobile/index.d.ts +2 -0
- package/dist/src/mobile/nativeAuth.d.ts +17 -0
- package/dist/src/mobile/releaseArtifact.d.ts +2 -0
- package/dist/src/mobile/remoteMacAgent.d.ts +2 -0
- package/dist/src/mobile/remoteMacAgentEntry.d.ts +1 -0
- package/dist/src/mobile/remoteMacProtocol.d.ts +114 -0
- package/dist/src/mobile/remoteMacWire.d.ts +2 -0
- package/dist/src/mobile/shellAuth.d.ts +8 -0
- package/dist/src/mobile/shellBootstrap.d.ts +11 -1
- package/dist/src/mobile/shellSync.d.ts +2 -0
- package/dist/src/mobile/staticDocument.d.ts +5 -0
- package/dist/src/mobile/transport.d.ts +9 -1
- package/dist/src/plugins/imageOptimizer.d.ts +1 -1
- package/dist/src/svelte/pageHandler.d.ts +3 -0
- package/dist/src/vue/pageHandler.d.ts +3 -0
- package/dist/svelte/index.js +312 -23
- package/dist/svelte/index.js.map +7 -4
- package/dist/svelte/server.js +307 -18
- package/dist/svelte/server.js.map +7 -4
- package/dist/vue/index.js +312 -23
- package/dist/vue/index.js.map +7 -4
- package/dist/vue/server.js +307 -18
- package/dist/vue/server.js.map +7 -4
- package/package.json +20 -8
package/dist/mobile/index.js
CHANGED
|
@@ -159,6 +159,14 @@ var init_startupBanner = __esm(() => {
|
|
|
159
159
|
];
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
+
// src/utils/stringModifiers.ts
|
|
163
|
+
var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9\-_]+/g, "").replace(/[-_]{2,}/g, "-"), toKebab = (str) => normalizeSlug(str).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), toPascal = (str) => {
|
|
164
|
+
if (!str.includes("-") && !str.includes("_")) {
|
|
165
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
166
|
+
}
|
|
167
|
+
return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
|
|
168
|
+
};
|
|
169
|
+
|
|
162
170
|
// src/mobile/artifactStore.ts
|
|
163
171
|
import { createHash as createHash2 } from "crypto";
|
|
164
172
|
import {
|
|
@@ -249,13 +257,19 @@ var parseCompatibilityPage = (value) => {
|
|
|
249
257
|
if (!isCanonicalRecord(value) || !isPageFramework(value.framework)) {
|
|
250
258
|
throw new TypeError("Compatibility artifact contains an invalid page.");
|
|
251
259
|
}
|
|
260
|
+
const styleBundleHash = typeof value.styleBundleHash === "string" ? value.styleBundleHash : undefined;
|
|
261
|
+
const styleBundlePath = typeof value.styleBundlePath === "string" ? value.styleBundlePath : undefined;
|
|
262
|
+
if (Boolean(styleBundleHash) !== Boolean(styleBundlePath)) {
|
|
263
|
+
throw new TypeError("Compatibility page style hash and path must be provided together.");
|
|
264
|
+
}
|
|
252
265
|
return {
|
|
253
266
|
bundleHash: readString(value.bundleHash, "page.bundleHash"),
|
|
254
267
|
bundlePath: readString(value.bundlePath, "page.bundlePath"),
|
|
255
268
|
contract: readString(value.contract, "page.contract"),
|
|
256
269
|
framework: value.framework,
|
|
257
270
|
pageId: readString(value.pageId, "page.pageId"),
|
|
258
|
-
propsSchemaHash: readString(value.propsSchemaHash, "page.propsSchemaHash")
|
|
271
|
+
propsSchemaHash: readString(value.propsSchemaHash, "page.propsSchemaHash"),
|
|
272
|
+
...styleBundleHash && styleBundlePath ? { styleBundleHash, styleBundlePath } : {}
|
|
259
273
|
};
|
|
260
274
|
};
|
|
261
275
|
var parseCompatibilityRoute = (value) => {
|
|
@@ -287,14 +301,23 @@ var validateProducerModule = (module) => {
|
|
|
287
301
|
}
|
|
288
302
|
return module;
|
|
289
303
|
};
|
|
290
|
-
var normalizePage = (page) =>
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
304
|
+
var normalizePage = (page) => {
|
|
305
|
+
if (Boolean(page.styleBundleHash) !== Boolean(page.styleBundlePath)) {
|
|
306
|
+
throw new TypeError("Compatibility page style hash and path must be provided together.");
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
bundleHash: requireNonEmpty(page.bundleHash, "page.bundleHash"),
|
|
310
|
+
bundlePath: requireNonEmpty(page.bundlePath, "page.bundlePath"),
|
|
311
|
+
contract: requireNonEmpty(page.contract, "page.contract"),
|
|
312
|
+
framework: page.framework,
|
|
313
|
+
pageId: requireNonEmpty(page.pageId, "page.pageId"),
|
|
314
|
+
propsSchemaHash: requireNonEmpty(page.propsSchemaHash, "page.propsSchemaHash"),
|
|
315
|
+
...page.styleBundleHash && page.styleBundlePath ? {
|
|
316
|
+
styleBundleHash: requireNonEmpty(page.styleBundleHash, "page.styleBundleHash"),
|
|
317
|
+
styleBundlePath: requireNonEmpty(page.styleBundlePath, "page.styleBundlePath")
|
|
318
|
+
} : {}
|
|
319
|
+
};
|
|
320
|
+
};
|
|
298
321
|
var normalizeRoute = (route) => {
|
|
299
322
|
if (!route.pattern.startsWith("/")) {
|
|
300
323
|
throw new TypeError("route.pattern must start with /.");
|
|
@@ -1059,11 +1082,11 @@ var hashNativeTree = async (root, label, ignorePublicBundle) => {
|
|
|
1059
1082
|
const records = await collectNativeDirectory(resolvedRoot, label, resolvedRoot, ignorePublicBundle);
|
|
1060
1083
|
return createHash3("sha256").update(records.join("")).digest("hex");
|
|
1061
1084
|
};
|
|
1062
|
-
var fingerprintAbsoluteAndroidNativeProject = async (project) => {
|
|
1085
|
+
var fingerprintAbsoluteAndroidNativeProject = async (project, options = {}) => {
|
|
1063
1086
|
const { dependencies } = await nativeDependencySources(project.nativeDirectory);
|
|
1064
1087
|
const roots = [
|
|
1065
1088
|
{
|
|
1066
|
-
ignorePublicBundle: true,
|
|
1089
|
+
ignorePublicBundle: options.includePublicBundle !== true,
|
|
1067
1090
|
label: "android",
|
|
1068
1091
|
source: project.nativeDirectory
|
|
1069
1092
|
},
|
|
@@ -2533,14 +2556,582 @@ var createAbsoluteIosNativeWatcher = async (options) => {
|
|
|
2533
2556
|
return { close };
|
|
2534
2557
|
};
|
|
2535
2558
|
var isAbsoluteIosNativeRootInput = (path) => ROOT_NATIVE_INPUTS.has(basename(path));
|
|
2559
|
+
// src/mobile/remoteMacProtocol.ts
|
|
2560
|
+
import { createHash as createHash7, randomUUID as randomUUID3 } from "crypto";
|
|
2561
|
+
import { chmod, mkdir as mkdir6, readFile as readFile8, rename as rename7, writeFile as writeFile7 } from "fs/promises";
|
|
2562
|
+
import { homedir as homedir2 } from "os";
|
|
2563
|
+
import {
|
|
2564
|
+
dirname as dirname5,
|
|
2565
|
+
isAbsolute as isAbsolute5,
|
|
2566
|
+
join as join7,
|
|
2567
|
+
posix,
|
|
2568
|
+
relative as relative6,
|
|
2569
|
+
resolve as resolvePath2,
|
|
2570
|
+
sep as sep5
|
|
2571
|
+
} from "path";
|
|
2572
|
+
|
|
2573
|
+
// src/mobile/remoteMacWire.ts
|
|
2574
|
+
var ABSOLUTE_REMOTE_MAC_EVENT_PREFIX = "ABSOLUTE_REMOTE_MAC\t";
|
|
2575
|
+
var ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION = 1;
|
|
2576
|
+
|
|
2577
|
+
// src/mobile/remoteMacProtocol.ts
|
|
2578
|
+
var PROFILE_FORMAT = 1;
|
|
2579
|
+
var PROFILE_NAME = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/u;
|
|
2580
|
+
var SSH_DESTINATION = /^(?:[A-Za-z0-9._-]+@)?[A-Za-z0-9._:-]+$/u;
|
|
2581
|
+
var defaultProfilePath = () => join7(homedir2(), ".absolutejs", "mobile", "remote-macs.json");
|
|
2582
|
+
var emptyStore = () => ({
|
|
2583
|
+
format: PROFILE_FORMAT,
|
|
2584
|
+
profiles: {}
|
|
2585
|
+
});
|
|
2586
|
+
var loadStore = async (path = defaultProfilePath()) => {
|
|
2587
|
+
try {
|
|
2588
|
+
const parsed = JSON.parse(await readFile8(path, "utf8"));
|
|
2589
|
+
if (parsed.format !== PROFILE_FORMAT || typeof parsed.profiles !== "object" || parsed.profiles === null || Array.isArray(parsed.profiles))
|
|
2590
|
+
throw new Error("Unsupported remote Mac profile format.");
|
|
2591
|
+
for (const [key, profile] of Object.entries(parsed.profiles)) {
|
|
2592
|
+
if (typeof profile !== "object" || profile === null || validateAbsoluteRemoteMacProfileName(key) !== key || profile.name !== key || validateAbsoluteSshDestination(profile.destination) !== profile.destination || validatePort(profile.port) !== profile.port || typeof profile.createdAt !== "string" || !profile.createdAt || typeof profile.bunPath !== "string" || !profile.bunPath.startsWith("/") || /[\r\n\0]/u.test(profile.bunPath) || typeof profile.workspaceRoot !== "string" || !profile.workspaceRoot.startsWith("/") || profile.workspaceRoot === "/" || /[\r\n\0]/u.test(profile.workspaceRoot) || typeof profile.xcodeVersion !== "string" || !profile.xcodeVersion.startsWith("Xcode "))
|
|
2593
|
+
throw new Error(`Remote Mac profile ${JSON.stringify(key)} is invalid.`);
|
|
2594
|
+
}
|
|
2595
|
+
if (parsed.defaultProfile !== undefined && !parsed.profiles[parsed.defaultProfile])
|
|
2596
|
+
throw new Error("The default remote Mac profile does not exist.");
|
|
2597
|
+
return parsed;
|
|
2598
|
+
} catch (error) {
|
|
2599
|
+
if (error.code === "ENOENT")
|
|
2600
|
+
return emptyStore();
|
|
2601
|
+
throw error;
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
var saveStore = async (store, path = defaultProfilePath()) => {
|
|
2605
|
+
await mkdir6(dirname5(path), { recursive: true });
|
|
2606
|
+
const temporary = `${path}.${randomUUID3()}.tmp`;
|
|
2607
|
+
await writeFile7(temporary, `${JSON.stringify(store, null, 2)}
|
|
2608
|
+
`, {
|
|
2609
|
+
mode: 384
|
|
2610
|
+
});
|
|
2611
|
+
await rename7(temporary, path);
|
|
2612
|
+
await chmod(path, 384);
|
|
2613
|
+
};
|
|
2614
|
+
var validateAbsoluteRemoteMacProfileName = (name) => {
|
|
2615
|
+
const normalized = name.trim().toLowerCase();
|
|
2616
|
+
if (!PROFILE_NAME.test(normalized))
|
|
2617
|
+
throw new TypeError("Remote Mac profile names must use 1-64 lowercase letters, digits, dots, dashes, or underscores.");
|
|
2618
|
+
return normalized;
|
|
2619
|
+
};
|
|
2620
|
+
var validateAbsoluteSshDestination = (destination) => {
|
|
2621
|
+
const normalized = destination.trim();
|
|
2622
|
+
if (!SSH_DESTINATION.test(normalized) || normalized.startsWith("-"))
|
|
2623
|
+
throw new TypeError("Remote Mac SSH destination must be a host, SSH alias, or user@host without command-line options.");
|
|
2624
|
+
return normalized;
|
|
2625
|
+
};
|
|
2626
|
+
var validatePort = (port) => {
|
|
2627
|
+
if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535))
|
|
2628
|
+
throw new TypeError("Remote Mac SSH port must be between 1 and 65535.");
|
|
2629
|
+
return port;
|
|
2630
|
+
};
|
|
2631
|
+
var shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
2632
|
+
var absoluteRemoteMacSshBase = (profile, options = {}) => [
|
|
2633
|
+
"ssh",
|
|
2634
|
+
"-o",
|
|
2635
|
+
"BatchMode=yes",
|
|
2636
|
+
"-o",
|
|
2637
|
+
"ConnectTimeout=10",
|
|
2638
|
+
"-o",
|
|
2639
|
+
"ServerAliveInterval=15",
|
|
2640
|
+
"-o",
|
|
2641
|
+
"ServerAliveCountMax=3",
|
|
2642
|
+
"-o",
|
|
2643
|
+
`StrictHostKeyChecking=${options.acceptNew ? "accept-new" : "yes"}`,
|
|
2644
|
+
...profile.port ? ["-p", String(profile.port)] : [],
|
|
2645
|
+
profile.destination
|
|
2646
|
+
];
|
|
2647
|
+
var localCapture = async (command) => {
|
|
2648
|
+
const process2 = Bun.spawn(command, {
|
|
2649
|
+
stderr: "pipe",
|
|
2650
|
+
stdin: "ignore",
|
|
2651
|
+
stdout: "pipe"
|
|
2652
|
+
});
|
|
2653
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
2654
|
+
process2.exited,
|
|
2655
|
+
new Response(process2.stdout).text(),
|
|
2656
|
+
new Response(process2.stderr).text()
|
|
2657
|
+
]);
|
|
2658
|
+
return { exitCode, stderr, stdout };
|
|
2659
|
+
};
|
|
2660
|
+
var defaultTransport = {
|
|
2661
|
+
capture: localCapture,
|
|
2662
|
+
spawn: (command, options) => Bun.spawn(command, {
|
|
2663
|
+
signal: options.signal,
|
|
2664
|
+
stderr: "pipe",
|
|
2665
|
+
stdin: "pipe",
|
|
2666
|
+
stdout: "pipe"
|
|
2667
|
+
})
|
|
2668
|
+
};
|
|
2669
|
+
var requireRemoteSuccess = (result, label) => {
|
|
2670
|
+
if (result.exitCode !== 0)
|
|
2671
|
+
throw new Error(`${label} failed: ${(result.stderr || result.stdout).trim() || `status ${result.exitCode}`}`);
|
|
2672
|
+
return result.stdout.trim();
|
|
2673
|
+
};
|
|
2674
|
+
var getAbsoluteRemoteMacProfile = async (name, profilePath) => {
|
|
2675
|
+
const store = await loadStore(profilePath);
|
|
2676
|
+
const selected = name ?? process.env.ABSOLUTE_IOS_REMOTE ?? store.defaultProfile;
|
|
2677
|
+
if (!selected)
|
|
2678
|
+
return;
|
|
2679
|
+
const profile = store.profiles[selected];
|
|
2680
|
+
if (!profile)
|
|
2681
|
+
throw new Error(`Remote Mac profile ${JSON.stringify(selected)} was not found.`);
|
|
2682
|
+
return profile;
|
|
2683
|
+
};
|
|
2684
|
+
var inspectAbsoluteRemoteMac = async (destination, options = {}) => {
|
|
2685
|
+
const profile = {
|
|
2686
|
+
destination: validateAbsoluteSshDestination(destination),
|
|
2687
|
+
port: validatePort(options.port)
|
|
2688
|
+
};
|
|
2689
|
+
const capture = options.transport?.capture ?? defaultTransport.capture;
|
|
2690
|
+
const command = [
|
|
2691
|
+
...absoluteRemoteMacSshBase(profile, {
|
|
2692
|
+
acceptNew: options.acceptNew === true
|
|
2693
|
+
}),
|
|
2694
|
+
"/bin/sh -lc",
|
|
2695
|
+
shellQuote(`bun_path="$(command -v bun || true)"; if [ -z "$bun_path" ] && [ -x "$HOME/.bun/bin/bun" ]; then bun_path="$HOME/.bun/bin/bun"; fi; printf '%s\\n' "$(uname -s)" "$HOME" "$bun_path" "$(/usr/bin/xcodebuild -version 2>/dev/null | tr '\\n' ' ' || true)"`)
|
|
2696
|
+
];
|
|
2697
|
+
const lines = requireRemoteSuccess(await capture(command), "Remote Mac handshake").split(/\r?\n/u);
|
|
2698
|
+
const [operatingSystem, home, bunPath, xcodeVersion] = lines;
|
|
2699
|
+
if (operatingSystem !== "Darwin")
|
|
2700
|
+
throw new Error("The SSH target is not a Mac.");
|
|
2701
|
+
if (!home?.startsWith("/") || !bunPath?.startsWith("/"))
|
|
2702
|
+
throw new Error("The remote Mac must have Bun installed and available to SSH.");
|
|
2703
|
+
if (!xcodeVersion?.startsWith("Xcode "))
|
|
2704
|
+
throw new Error("The remote Mac must have full Xcode installed and selected.");
|
|
2705
|
+
return { bunPath, home, os: operatingSystem, xcodeVersion };
|
|
2706
|
+
};
|
|
2707
|
+
var listAbsoluteRemoteMacProfiles = async (profilePath) => {
|
|
2708
|
+
const store = await loadStore(profilePath);
|
|
2709
|
+
return {
|
|
2710
|
+
defaultProfile: store.defaultProfile,
|
|
2711
|
+
profiles: Object.values(store.profiles).sort((left, right) => left.name.localeCompare(right.name))
|
|
2712
|
+
};
|
|
2713
|
+
};
|
|
2714
|
+
var pairAbsoluteRemoteMac = async (options) => {
|
|
2715
|
+
const name = validateAbsoluteRemoteMacProfileName(options.name);
|
|
2716
|
+
const destination = validateAbsoluteSshDestination(options.destination);
|
|
2717
|
+
const port = validatePort(options.port);
|
|
2718
|
+
const inspection = await inspectAbsoluteRemoteMac(destination, {
|
|
2719
|
+
acceptNew: true,
|
|
2720
|
+
port,
|
|
2721
|
+
transport: options.transport
|
|
2722
|
+
});
|
|
2723
|
+
const workspaceRoot = options.workspaceRoot ? options.workspaceRoot.trim() : posix.join(inspection.home, ".absolutejs", "remote-ios");
|
|
2724
|
+
if (!workspaceRoot.startsWith("/") || workspaceRoot === "/" || /[\r\n\0]/u.test(workspaceRoot))
|
|
2725
|
+
throw new TypeError("Remote Mac workspace must be an absolute macOS path.");
|
|
2726
|
+
const profile = {
|
|
2727
|
+
bunPath: inspection.bunPath,
|
|
2728
|
+
createdAt: new Date().toISOString(),
|
|
2729
|
+
destination,
|
|
2730
|
+
name,
|
|
2731
|
+
...port ? { port } : {},
|
|
2732
|
+
workspaceRoot,
|
|
2733
|
+
xcodeVersion: inspection.xcodeVersion
|
|
2734
|
+
};
|
|
2735
|
+
const store = await loadStore(options.profilePath);
|
|
2736
|
+
store.profiles[name] = profile;
|
|
2737
|
+
store.defaultProfile = name;
|
|
2738
|
+
await saveStore(store, options.profilePath);
|
|
2739
|
+
return profile;
|
|
2740
|
+
};
|
|
2741
|
+
var removeAbsoluteRemoteMacProfile = async (name, profilePath) => {
|
|
2742
|
+
const normalized = validateAbsoluteRemoteMacProfileName(name);
|
|
2743
|
+
const store = await loadStore(profilePath);
|
|
2744
|
+
if (!store.profiles[normalized])
|
|
2745
|
+
return false;
|
|
2746
|
+
delete store.profiles[normalized];
|
|
2747
|
+
if (store.defaultProfile === normalized) {
|
|
2748
|
+
const [nextDefault] = Object.keys(store.profiles).sort();
|
|
2749
|
+
store.defaultProfile = nextDefault;
|
|
2750
|
+
}
|
|
2751
|
+
await saveStore(store, profilePath);
|
|
2752
|
+
return true;
|
|
2753
|
+
};
|
|
2754
|
+
var projectIdentity = (projectRoot, appId) => createHash7("sha256").update(`${resolvePath2(projectRoot)}\x00${appId}`).digest("hex").slice(0, 20);
|
|
2755
|
+
var createAbsoluteRemoteIosDevProject = (config, projectRoot, profile) => ({
|
|
2756
|
+
cap: join7(resolvePath2(projectRoot), "node_modules", ".bin", "cap"),
|
|
2757
|
+
config,
|
|
2758
|
+
nativeDirectory: join7(config.nativeProjectDirectory, "ios"),
|
|
2759
|
+
profile,
|
|
2760
|
+
projectRoot: resolvePath2(projectRoot),
|
|
2761
|
+
remote: true,
|
|
2762
|
+
remoteProjectRoot: posix.join(profile.workspaceRoot, "projects", projectIdentity(projectRoot, config.appId), "current"),
|
|
2763
|
+
xcodebuild: "remote:xcodebuild",
|
|
2764
|
+
xcrun: "remote:xcrun"
|
|
2765
|
+
});
|
|
2766
|
+
var installAbsoluteRemoteMacAgent = async (project) => {
|
|
2767
|
+
const artifact = await materializeAbsoluteRemoteMacAgent(project.projectRoot);
|
|
2768
|
+
const directory = posix.join(project.profile.workspaceRoot, "agents", `protocol-${ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION}`, artifact.sha256);
|
|
2769
|
+
const remotePath = posix.join(directory, "agent.js");
|
|
2770
|
+
const verifyScript = `test -f ${shellQuote(remotePath)} && ` + `test "$(shasum -a 256 ${shellQuote(remotePath)} | awk '{print $1}')" = ${shellQuote(artifact.sha256)}`;
|
|
2771
|
+
const verified = await defaultTransport.capture([
|
|
2772
|
+
...absoluteRemoteMacSshBase(project.profile),
|
|
2773
|
+
"/bin/sh -lc",
|
|
2774
|
+
shellQuote(verifyScript)
|
|
2775
|
+
]);
|
|
2776
|
+
if (verified.exitCode === 0)
|
|
2777
|
+
return { ...artifact, remotePath, uploaded: false };
|
|
2778
|
+
const temporary = posix.join(directory, `.agent-${randomUUID3()}.tmp`);
|
|
2779
|
+
const installScript = [
|
|
2780
|
+
"set -eu",
|
|
2781
|
+
"umask 077",
|
|
2782
|
+
`mkdir -p ${shellQuote(directory)}`,
|
|
2783
|
+
`cat > ${shellQuote(temporary)}`,
|
|
2784
|
+
`test "$(shasum -a 256 ${shellQuote(temporary)} | awk '{print $1}')" = ${shellQuote(artifact.sha256)}`,
|
|
2785
|
+
`chmod 600 ${shellQuote(temporary)}`,
|
|
2786
|
+
`mv ${shellQuote(temporary)} ${shellQuote(remotePath)}`
|
|
2787
|
+
].join("; ");
|
|
2788
|
+
const upload = Bun.spawn([
|
|
2789
|
+
...absoluteRemoteMacSshBase(project.profile),
|
|
2790
|
+
"/bin/sh -lc",
|
|
2791
|
+
shellQuote(installScript)
|
|
2792
|
+
], {
|
|
2793
|
+
stderr: "pipe",
|
|
2794
|
+
stdin: Bun.file(artifact.path),
|
|
2795
|
+
stdout: "pipe"
|
|
2796
|
+
});
|
|
2797
|
+
const [exitCode, stderr] = await Promise.all([
|
|
2798
|
+
upload.exited,
|
|
2799
|
+
new Response(upload.stderr).text()
|
|
2800
|
+
]);
|
|
2801
|
+
if (exitCode !== 0)
|
|
2802
|
+
throw new Error(`Remote Mac agent installation failed: ${stderr.trim() || `status ${exitCode}`}`);
|
|
2803
|
+
return { ...artifact, remotePath, uploaded: true };
|
|
2804
|
+
};
|
|
2805
|
+
var materializeAbsoluteRemoteMacAgent = async (projectRoot) => {
|
|
2806
|
+
const shippedCandidates = [
|
|
2807
|
+
join7(import.meta.dir, "remoteMacAgentEntry.js"),
|
|
2808
|
+
join7(import.meta.dir, "..", "mobile", "remoteMacAgentEntry.js")
|
|
2809
|
+
];
|
|
2810
|
+
let path;
|
|
2811
|
+
for (const candidate of shippedCandidates) {
|
|
2812
|
+
if (await Bun.file(candidate).exists()) {
|
|
2813
|
+
path = candidate;
|
|
2814
|
+
break;
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
if (!path) {
|
|
2818
|
+
const sourceCandidates = [
|
|
2819
|
+
join7(import.meta.dir, "remoteMacAgentEntry.ts"),
|
|
2820
|
+
join7(import.meta.dir, "..", "..", "src", "mobile", "remoteMacAgentEntry.ts")
|
|
2821
|
+
];
|
|
2822
|
+
const source = await sourceCandidates.reduce(async (found, candidate) => await found ?? (await Bun.file(candidate).exists() ? candidate : undefined), Promise.resolve(undefined));
|
|
2823
|
+
if (!source)
|
|
2824
|
+
throw new Error("The AbsoluteJS installation does not contain its remote Mac agent artifact.");
|
|
2825
|
+
const outdir = join7(resolvePath2(projectRoot), ".absolutejs", "mobile", "remote-agent");
|
|
2826
|
+
await mkdir6(outdir, { recursive: true });
|
|
2827
|
+
const result = await Bun.build({
|
|
2828
|
+
entrypoints: [source],
|
|
2829
|
+
minify: true,
|
|
2830
|
+
outdir,
|
|
2831
|
+
target: "bun"
|
|
2832
|
+
});
|
|
2833
|
+
if (!result.success)
|
|
2834
|
+
throw new AggregateError(result.logs, "Failed to build the AbsoluteJS remote Mac agent.");
|
|
2835
|
+
path = join7(outdir, "remoteMacAgentEntry.js");
|
|
2836
|
+
}
|
|
2837
|
+
const bytes = await Bun.file(path).arrayBuffer();
|
|
2838
|
+
const sha256 = createHash7("sha256").update(new Uint8Array(bytes)).digest("hex");
|
|
2839
|
+
return { bytes: bytes.byteLength, path, sha256 };
|
|
2840
|
+
};
|
|
2841
|
+
var portableRelativePath = (root, path) => relative6(root, path).split(sep5).join(posix.sep);
|
|
2842
|
+
var portableMobileConfig = (project) => ({
|
|
2843
|
+
appId: project.config.appId,
|
|
2844
|
+
appName: project.config.appName,
|
|
2845
|
+
bundleDirectory: portableRelativePath(project.projectRoot, project.config.bundleDirectory),
|
|
2846
|
+
...project.config.deepLinkScheme || project.config.deepLinkHosts.length > 1 || project.config.appleAppIdPrefix ? {
|
|
2847
|
+
deepLinks: {
|
|
2848
|
+
...project.config.deepLinkScheme ? { scheme: project.config.deepLinkScheme } : {},
|
|
2849
|
+
hosts: project.config.deepLinkHosts,
|
|
2850
|
+
...project.config.appleAppIdPrefix ? {
|
|
2851
|
+
apple: {
|
|
2852
|
+
appIdPrefix: project.config.appleAppIdPrefix
|
|
2853
|
+
}
|
|
2854
|
+
} : {}
|
|
2855
|
+
}
|
|
2856
|
+
} : {},
|
|
2857
|
+
entry: project.config.entry,
|
|
2858
|
+
...project.config.iosVersion ? { ios: { version: project.config.iosVersion } } : {},
|
|
2859
|
+
nativeProject: {
|
|
2860
|
+
directory: portableRelativePath(project.projectRoot, project.config.nativeProjectDirectory),
|
|
2861
|
+
mode: "source"
|
|
2862
|
+
},
|
|
2863
|
+
platforms: ["ios"],
|
|
2864
|
+
server: { productionOrigin: project.config.productionOrigin }
|
|
2865
|
+
});
|
|
2866
|
+
var absoluteRemoteProjectSyncCommands = (project) => {
|
|
2867
|
+
const current = project.remoteProjectRoot;
|
|
2868
|
+
const parent = posix.dirname(current);
|
|
2869
|
+
const staging = posix.join(parent, `.incoming-${randomUUID3()}`);
|
|
2870
|
+
const previous = posix.join(parent, ".previous");
|
|
2871
|
+
const script = [
|
|
2872
|
+
"set -eu",
|
|
2873
|
+
`mkdir -p ${shellQuote(staging)}`,
|
|
2874
|
+
`tar -xf - -C ${shellQuote(staging)}`,
|
|
2875
|
+
`if [ -d ${shellQuote(posix.join(current, "node_modules"))} ]; then mv ${shellQuote(posix.join(current, "node_modules"))} ${shellQuote(posix.join(staging, "node_modules"))}; fi`,
|
|
2876
|
+
`if [ -d ${shellQuote(posix.join(current, ".absolutejs"))} ]; then mv ${shellQuote(posix.join(current, ".absolutejs"))} ${shellQuote(posix.join(staging, ".absolutejs"))}; fi`,
|
|
2877
|
+
`rm -rf ${shellQuote(previous)}`,
|
|
2878
|
+
`if [ -d ${shellQuote(current)} ]; then mv ${shellQuote(current)} ${shellQuote(previous)}; fi`,
|
|
2879
|
+
`mv ${shellQuote(staging)} ${shellQuote(current)}`,
|
|
2880
|
+
`rm -rf ${shellQuote(previous)}`
|
|
2881
|
+
].join("; ");
|
|
2882
|
+
return {
|
|
2883
|
+
remote: [
|
|
2884
|
+
...absoluteRemoteMacSshBase(project.profile),
|
|
2885
|
+
"/bin/sh -lc",
|
|
2886
|
+
shellQuote(script)
|
|
2887
|
+
],
|
|
2888
|
+
tar: [
|
|
2889
|
+
"tar",
|
|
2890
|
+
"--exclude=.git",
|
|
2891
|
+
"--exclude=node_modules",
|
|
2892
|
+
"--exclude=build",
|
|
2893
|
+
"--exclude=.absolutejs",
|
|
2894
|
+
"-cf",
|
|
2895
|
+
"-",
|
|
2896
|
+
"-C",
|
|
2897
|
+
project.projectRoot,
|
|
2898
|
+
"."
|
|
2899
|
+
]
|
|
2900
|
+
};
|
|
2901
|
+
};
|
|
2902
|
+
var syncAbsoluteRemoteMacProject = async (project) => {
|
|
2903
|
+
const commands = absoluteRemoteProjectSyncCommands(project);
|
|
2904
|
+
const archive = Bun.spawn(commands.tar, {
|
|
2905
|
+
stderr: "pipe",
|
|
2906
|
+
stdout: "pipe"
|
|
2907
|
+
});
|
|
2908
|
+
const upload = Bun.spawn(commands.remote, {
|
|
2909
|
+
stderr: "pipe",
|
|
2910
|
+
stdin: archive.stdout,
|
|
2911
|
+
stdout: "pipe"
|
|
2912
|
+
});
|
|
2913
|
+
const [archiveExit, uploadExit, archiveError, uploadError] = await Promise.all([
|
|
2914
|
+
archive.exited,
|
|
2915
|
+
upload.exited,
|
|
2916
|
+
new Response(archive.stderr).text(),
|
|
2917
|
+
new Response(upload.stderr).text()
|
|
2918
|
+
]);
|
|
2919
|
+
if (archiveExit !== 0 || uploadExit !== 0)
|
|
2920
|
+
throw new Error(`Remote Mac project synchronization failed: ${(archiveError || uploadError).trim()}`);
|
|
2921
|
+
const install = await defaultTransport.capture([
|
|
2922
|
+
...absoluteRemoteMacSshBase(project.profile),
|
|
2923
|
+
"/bin/sh -lc",
|
|
2924
|
+
shellQuote(`cd ${shellQuote(project.remoteProjectRoot)} && ${shellQuote(project.profile.bunPath)} install --frozen-lockfile`)
|
|
2925
|
+
]);
|
|
2926
|
+
requireRemoteSuccess(install, "Remote Mac dependency installation");
|
|
2927
|
+
};
|
|
2928
|
+
var consumeLines2 = async (stream, onLine) => {
|
|
2929
|
+
const reader = stream.getReader();
|
|
2930
|
+
const decoder = new TextDecoder;
|
|
2931
|
+
let buffered = "";
|
|
2932
|
+
try {
|
|
2933
|
+
while (true) {
|
|
2934
|
+
const { done, value } = await reader.read();
|
|
2935
|
+
if (done)
|
|
2936
|
+
break;
|
|
2937
|
+
buffered += decoder.decode(value, { stream: true });
|
|
2938
|
+
const lines = buffered.split(/\r?\n/u);
|
|
2939
|
+
buffered = lines.pop() ?? "";
|
|
2940
|
+
lines.forEach(onLine);
|
|
2941
|
+
}
|
|
2942
|
+
buffered += decoder.decode();
|
|
2943
|
+
if (buffered)
|
|
2944
|
+
onLine(buffered);
|
|
2945
|
+
} finally {
|
|
2946
|
+
reader.releaseLock();
|
|
2947
|
+
}
|
|
2948
|
+
};
|
|
2949
|
+
var startAbsoluteRemoteIosDevSession = async (options) => {
|
|
2950
|
+
const startedAt = performance.now();
|
|
2951
|
+
const transport = options.transport ?? defaultTransport;
|
|
2952
|
+
const installAgent = options.installAgent ?? installAbsoluteRemoteMacAgent;
|
|
2953
|
+
const syncProject = options.syncProject ?? syncAbsoluteRemoteMacProject;
|
|
2954
|
+
const agentStartedAt = performance.now();
|
|
2955
|
+
const agent = await installAgent(options.project);
|
|
2956
|
+
const agentDuration = performance.now() - agentStartedAt;
|
|
2957
|
+
const syncStartedAt = performance.now();
|
|
2958
|
+
await syncProject(options.project);
|
|
2959
|
+
const syncDuration = performance.now() - syncStartedAt;
|
|
2960
|
+
const encodedConfig = Buffer.from(JSON.stringify(portableMobileConfig(options.project))).toString("base64url");
|
|
2961
|
+
const remoteCommand = [
|
|
2962
|
+
`cd ${shellQuote(options.project.remoteProjectRoot)}`,
|
|
2963
|
+
"&&",
|
|
2964
|
+
"exec",
|
|
2965
|
+
shellQuote(options.project.profile.bunPath),
|
|
2966
|
+
shellQuote(agent.remotePath),
|
|
2967
|
+
"--port",
|
|
2968
|
+
String(options.port),
|
|
2969
|
+
"--mobile-config",
|
|
2970
|
+
shellQuote(encodedConfig),
|
|
2971
|
+
...options.https ? ["--https"] : []
|
|
2972
|
+
].join(" ");
|
|
2973
|
+
const command = [
|
|
2974
|
+
...absoluteRemoteMacSshBase(options.project.profile),
|
|
2975
|
+
"-o",
|
|
2976
|
+
"ExitOnForwardFailure=yes",
|
|
2977
|
+
"-R",
|
|
2978
|
+
`${options.port}:127.0.0.1:${options.port}`,
|
|
2979
|
+
"/bin/sh -lc",
|
|
2980
|
+
shellQuote(remoteCommand)
|
|
2981
|
+
];
|
|
2982
|
+
const connectStartedAt = performance.now();
|
|
2983
|
+
const process2 = transport.spawn(command, { signal: options.signal });
|
|
2984
|
+
let state = "syncing";
|
|
2985
|
+
let ready;
|
|
2986
|
+
let fatal;
|
|
2987
|
+
const pending = new Map;
|
|
2988
|
+
let resolveReady;
|
|
2989
|
+
let rejectReady;
|
|
2990
|
+
const readyPromise = new Promise((resolve6, reject) => {
|
|
2991
|
+
resolveReady = resolve6;
|
|
2992
|
+
rejectReady = reject;
|
|
2993
|
+
});
|
|
2994
|
+
const handleEvent = (event) => {
|
|
2995
|
+
if (event.v !== ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION) {
|
|
2996
|
+
rejectReady(new Error("Remote Mac protocol version mismatch."));
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
if (event.type === "log")
|
|
3000
|
+
options.log?.(event.message);
|
|
3001
|
+
if (event.type === "native-log")
|
|
3002
|
+
options.nativeLog?.(event.entry);
|
|
3003
|
+
if (event.type === "state") {
|
|
3004
|
+
({ state } = event);
|
|
3005
|
+
options.onStateChange?.(state);
|
|
3006
|
+
}
|
|
3007
|
+
if (event.type === "timing")
|
|
3008
|
+
options.onPhaseTiming?.(event);
|
|
3009
|
+
if (event.type === "ready") {
|
|
3010
|
+
ready = event;
|
|
3011
|
+
resolveReady();
|
|
3012
|
+
}
|
|
3013
|
+
if (event.type === "fatal") {
|
|
3014
|
+
fatal = new Error(event.error);
|
|
3015
|
+
rejectReady(fatal);
|
|
3016
|
+
}
|
|
3017
|
+
if (event.type === "response") {
|
|
3018
|
+
const request2 = pending.get(event.id);
|
|
3019
|
+
if (!request2)
|
|
3020
|
+
return;
|
|
3021
|
+
pending.delete(event.id);
|
|
3022
|
+
if (event.ok)
|
|
3023
|
+
request2.resolve(event.result);
|
|
3024
|
+
else
|
|
3025
|
+
request2.reject(new Error(event.error ?? "Remote command failed."));
|
|
3026
|
+
}
|
|
3027
|
+
};
|
|
3028
|
+
const stdoutDone = consumeLines2(process2.stdout, (line) => {
|
|
3029
|
+
if (!line.startsWith(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX))
|
|
3030
|
+
return;
|
|
3031
|
+
try {
|
|
3032
|
+
handleEvent(JSON.parse(line.slice(ABSOLUTE_REMOTE_MAC_EVENT_PREFIX.length)));
|
|
3033
|
+
} catch {
|
|
3034
|
+
options.log?.(`Remote Mac emitted an invalid protocol event.`);
|
|
3035
|
+
}
|
|
3036
|
+
}).catch((error) => {
|
|
3037
|
+
fatal = error instanceof Error ? error : new Error("Failed to read the remote Mac protocol stream.");
|
|
3038
|
+
rejectReady(fatal);
|
|
3039
|
+
});
|
|
3040
|
+
const stderrDone = consumeLines2(process2.stderr, (line) => options.log?.(`[remote] ${line}`)).catch((error) => options.log?.(`[remote] ${error instanceof Error ? error.message : "Failed to read SSH stderr."}`));
|
|
3041
|
+
process2.exited.then(async (exitCode) => {
|
|
3042
|
+
await Promise.all([stdoutDone, stderrDone]);
|
|
3043
|
+
const error = fatal ?? new Error(`Remote Mac connection closed with status ${exitCode}.`);
|
|
3044
|
+
if (!ready)
|
|
3045
|
+
rejectReady(error);
|
|
3046
|
+
pending.forEach(({ reject }) => reject(error));
|
|
3047
|
+
pending.clear();
|
|
3048
|
+
return;
|
|
3049
|
+
});
|
|
3050
|
+
await readyPromise;
|
|
3051
|
+
if (!ready)
|
|
3052
|
+
throw fatal ?? new Error("Remote Mac did not become ready.");
|
|
3053
|
+
const totalDuration = performance.now() - startedAt;
|
|
3054
|
+
let currentReady = {
|
|
3055
|
+
...ready,
|
|
3056
|
+
timings: {
|
|
3057
|
+
...ready.timings,
|
|
3058
|
+
"remote-agent": agentDuration,
|
|
3059
|
+
"remote-connect": performance.now() - connectStartedAt,
|
|
3060
|
+
"remote-sync": syncDuration,
|
|
3061
|
+
total: totalDuration
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
3064
|
+
options.log?.(`Remote Mac connected (${options.project.profile.name}); agent ${agent.uploaded ? "uploaded" : "cache hit"}, project synced, and iOS ready in ${totalDuration.toFixed(2)}ms.`);
|
|
3065
|
+
const request = (commandName) => {
|
|
3066
|
+
const id = randomUUID3();
|
|
3067
|
+
const response = new Promise((resolve6, reject) => pending.set(id, { reject, resolve: resolve6 }));
|
|
3068
|
+
process2.stdin.write(`${JSON.stringify({ command: commandName, id, v: 1 })}
|
|
3069
|
+
`);
|
|
3070
|
+
process2.stdin.flush();
|
|
3071
|
+
return response;
|
|
3072
|
+
};
|
|
3073
|
+
let closed = false;
|
|
3074
|
+
const close = async () => {
|
|
3075
|
+
if (closed)
|
|
3076
|
+
return;
|
|
3077
|
+
closed = true;
|
|
3078
|
+
await request("close").catch(() => {
|
|
3079
|
+
return;
|
|
3080
|
+
});
|
|
3081
|
+
process2.stdin.end();
|
|
3082
|
+
await process2.exited.catch(() => {
|
|
3083
|
+
return;
|
|
3084
|
+
});
|
|
3085
|
+
};
|
|
3086
|
+
const makeSession = () => ({
|
|
3087
|
+
close,
|
|
3088
|
+
nativeCacheHit: currentReady.nativeCacheHit,
|
|
3089
|
+
startedSimulator: currentReady.startedSimulator,
|
|
3090
|
+
timings: currentReady.timings,
|
|
3091
|
+
udid: currentReady.udid,
|
|
3092
|
+
rebuild: async () => {
|
|
3093
|
+
const rebuildStartedAt = performance.now();
|
|
3094
|
+
const rebuildSyncStartedAt = performance.now();
|
|
3095
|
+
await syncProject(options.project);
|
|
3096
|
+
const rebuildSyncDuration = performance.now() - rebuildSyncStartedAt;
|
|
3097
|
+
const result = await request("rebuild");
|
|
3098
|
+
currentReady = {
|
|
3099
|
+
...result,
|
|
3100
|
+
timings: {
|
|
3101
|
+
...result.timings,
|
|
3102
|
+
"remote-sync": rebuildSyncDuration,
|
|
3103
|
+
total: performance.now() - rebuildStartedAt
|
|
3104
|
+
}
|
|
3105
|
+
};
|
|
3106
|
+
return makeSession();
|
|
3107
|
+
},
|
|
3108
|
+
relaunch: async () => {
|
|
3109
|
+
await request("relaunch");
|
|
3110
|
+
},
|
|
3111
|
+
screenshot: async (destination) => {
|
|
3112
|
+
const result = await request("screenshot");
|
|
3113
|
+
const target = resolvePath2(options.project.projectRoot, destination);
|
|
3114
|
+
const targetRelative = relative6(options.project.projectRoot, target);
|
|
3115
|
+
if (targetRelative.startsWith("..") || isAbsolute5(targetRelative))
|
|
3116
|
+
throw new Error("iOS screenshot must remain inside the project.");
|
|
3117
|
+
await mkdir6(dirname5(target), { recursive: true });
|
|
3118
|
+
await writeFile7(target, Buffer.from(result.data, "base64"));
|
|
3119
|
+
return target;
|
|
3120
|
+
},
|
|
3121
|
+
get state() {
|
|
3122
|
+
return state;
|
|
3123
|
+
}
|
|
3124
|
+
});
|
|
3125
|
+
return makeSession();
|
|
3126
|
+
};
|
|
2536
3127
|
// src/mobile/associationFiles.ts
|
|
2537
3128
|
import {
|
|
2538
3129
|
access as access7,
|
|
2539
|
-
mkdir as
|
|
2540
|
-
readFile as
|
|
2541
|
-
rename as
|
|
3130
|
+
mkdir as mkdir7,
|
|
3131
|
+
readFile as readFile9,
|
|
3132
|
+
rename as rename8,
|
|
2542
3133
|
rm as rm6,
|
|
2543
|
-
writeFile as
|
|
3134
|
+
writeFile as writeFile8
|
|
2544
3135
|
} from "fs/promises";
|
|
2545
3136
|
import { resolve as resolve7 } from "path";
|
|
2546
3137
|
import { Elysia } from "elysia";
|
|
@@ -2575,8 +3166,9 @@ var normalizeEntry = (entry) => {
|
|
|
2575
3166
|
};
|
|
2576
3167
|
var normalizeProductionOrigin = (value) => {
|
|
2577
3168
|
const parsed = new URL(requireText(value, "mobile.server.productionOrigin"));
|
|
2578
|
-
|
|
2579
|
-
|
|
3169
|
+
const isLoopbackHttp = parsed.protocol === "http:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]");
|
|
3170
|
+
if (parsed.protocol !== "https:" && !isLoopbackHttp) {
|
|
3171
|
+
throw new TypeError("mobile.server.productionOrigin must use HTTPS, except for a loopback development origin.");
|
|
2580
3172
|
}
|
|
2581
3173
|
if (parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
|
|
2582
3174
|
throw new TypeError("mobile.server.productionOrigin must be an origin without credentials, path, query, or hash.");
|
|
@@ -2600,9 +3192,8 @@ var normalizeHosts = (hosts, productionOrigin) => {
|
|
|
2600
3192
|
}
|
|
2601
3193
|
return value;
|
|
2602
3194
|
};
|
|
2603
|
-
const
|
|
2604
|
-
|
|
2605
|
-
]);
|
|
3195
|
+
const productionHostname = new URL(productionOrigin).hostname;
|
|
3196
|
+
const normalized = new Set(productionHostname === "[::1]" ? [] : [normalizeHostname(productionHostname)]);
|
|
2606
3197
|
for (const host of hosts ?? []) {
|
|
2607
3198
|
normalized.add(normalizeHostname(host));
|
|
2608
3199
|
}
|
|
@@ -2640,7 +3231,7 @@ var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
|
|
|
2640
3231
|
throw new TypeError("mobile.appId must use reverse-domain notation, for example com.example.app.");
|
|
2641
3232
|
}
|
|
2642
3233
|
const productionOrigin = normalizeProductionOrigin(config.server.productionOrigin);
|
|
2643
|
-
const deepLinkScheme = config.deepLinks?.scheme
|
|
3234
|
+
const deepLinkScheme = (config.deepLinks?.scheme ?? appId).trim().toLowerCase();
|
|
2644
3235
|
if (deepLinkScheme && !SCHEME_PATTERN.test(deepLinkScheme)) {
|
|
2645
3236
|
throw new TypeError("mobile.deepLinks.scheme is not a valid URL scheme.");
|
|
2646
3237
|
}
|
|
@@ -2742,7 +3333,7 @@ var createAbsoluteMobileAssociationPlugin = (mobile, projectRoot, options = {})
|
|
|
2742
3333
|
var writeAtomic = async (path, source) => {
|
|
2743
3334
|
let current;
|
|
2744
3335
|
try {
|
|
2745
|
-
current = await
|
|
3336
|
+
current = await readFile9(path, "utf8");
|
|
2746
3337
|
} catch (error) {
|
|
2747
3338
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
2748
3339
|
throw error;
|
|
@@ -2751,8 +3342,8 @@ var writeAtomic = async (path, source) => {
|
|
|
2751
3342
|
if (current === source)
|
|
2752
3343
|
return false;
|
|
2753
3344
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
2754
|
-
await
|
|
2755
|
-
await
|
|
3345
|
+
await writeFile8(temporary, source, { flag: "wx" });
|
|
3346
|
+
await rename8(temporary, path);
|
|
2756
3347
|
return true;
|
|
2757
3348
|
};
|
|
2758
3349
|
var exists2 = async (path) => {
|
|
@@ -2767,7 +3358,7 @@ var assertOwnedOutput = async (root) => {
|
|
|
2767
3358
|
const path = resolve7(root, OWNERSHIP_FILE);
|
|
2768
3359
|
let ownership;
|
|
2769
3360
|
try {
|
|
2770
|
-
ownership = JSON.parse(await
|
|
3361
|
+
ownership = JSON.parse(await readFile9(path, "utf8"));
|
|
2771
3362
|
} catch {
|
|
2772
3363
|
throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
|
|
2773
3364
|
}
|
|
@@ -2781,12 +3372,12 @@ var publishGeneratedDirectory = async (temporary, root) => {
|
|
|
2781
3372
|
await assertOwnedOutput(root);
|
|
2782
3373
|
const backup = `${root}.${crypto.randomUUID()}.previous`;
|
|
2783
3374
|
if (hasCurrent)
|
|
2784
|
-
await
|
|
3375
|
+
await rename8(root, backup);
|
|
2785
3376
|
try {
|
|
2786
|
-
await
|
|
3377
|
+
await rename8(temporary, root);
|
|
2787
3378
|
} catch (error) {
|
|
2788
3379
|
if (hasCurrent)
|
|
2789
|
-
await
|
|
3380
|
+
await rename8(backup, root);
|
|
2790
3381
|
throw error;
|
|
2791
3382
|
}
|
|
2792
3383
|
if (hasCurrent)
|
|
@@ -2794,7 +3385,7 @@ var publishGeneratedDirectory = async (temporary, root) => {
|
|
|
2794
3385
|
};
|
|
2795
3386
|
var materializeHost = async (root, host, files) => {
|
|
2796
3387
|
const directory = resolve7(root, host, ".well-known");
|
|
2797
|
-
await
|
|
3388
|
+
await mkdir7(directory, { recursive: true });
|
|
2798
3389
|
return Promise.all(files.map(async ([name, document]) => {
|
|
2799
3390
|
const path = resolve7(directory, name);
|
|
2800
3391
|
await writeAtomic(path, `${JSON.stringify(document, null, 2)}
|
|
@@ -2832,7 +3423,7 @@ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory)
|
|
|
2832
3423
|
if (documents.apple) {
|
|
2833
3424
|
files.push(["apple-app-site-association", documents.apple]);
|
|
2834
3425
|
}
|
|
2835
|
-
await
|
|
3426
|
+
await mkdir7(temporary, { recursive: true });
|
|
2836
3427
|
try {
|
|
2837
3428
|
const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
|
|
2838
3429
|
await writeAtomic(resolve7(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
|
|
@@ -2903,15 +3494,23 @@ var parseAbsoluteMobileBuildPageMetadata = (value) => {
|
|
|
2903
3494
|
};
|
|
2904
3495
|
};
|
|
2905
3496
|
// src/mobile/buildPipeline.ts
|
|
2906
|
-
import { readFile as
|
|
2907
|
-
import { join as
|
|
3497
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
3498
|
+
import { join as join12, resolve as resolve10 } from "path";
|
|
2908
3499
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2909
3500
|
|
|
2910
3501
|
// src/mobile/buildRelease.ts
|
|
2911
|
-
import { createHash as
|
|
2912
|
-
import { readFile as
|
|
2913
|
-
import { join as
|
|
2914
|
-
var sha256 = (bytes) =>
|
|
3502
|
+
import { createHash as createHash8 } from "crypto";
|
|
3503
|
+
import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile9 } from "fs/promises";
|
|
3504
|
+
import { basename as basename2, dirname as dirname6, extname, join as join8, relative as relative7, resolve as resolve8 } from "path";
|
|
3505
|
+
var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex");
|
|
3506
|
+
var STATIC_SCRIPT_PATTERN = /(<script\b[^>]*?\bsrc\s*=\s*["'])(\/[^"']+\.(?:js|ts))(["'][^>]*>)/giu;
|
|
3507
|
+
var rewriteStaticScriptPaths = (source, manifest) => source.replace(STATIC_SCRIPT_PATTERN, (match, prefix, path, suffix) => {
|
|
3508
|
+
if (path.endsWith("/htmx.min.js"))
|
|
3509
|
+
return match;
|
|
3510
|
+
const key = toPascal(basename2(path, extname(path)));
|
|
3511
|
+
const builtPath = manifest[key];
|
|
3512
|
+
return builtPath ? `${prefix}${builtPath}${suffix}` : match;
|
|
3513
|
+
});
|
|
2915
3514
|
var readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]);
|
|
2916
3515
|
var resolveAssetPath = (buildDirectory, assetPath) => {
|
|
2917
3516
|
const resolvedBuildDirectory = resolve8(buildDirectory);
|
|
@@ -2919,29 +3518,51 @@ var resolveAssetPath = (buildDirectory, assetPath) => {
|
|
|
2919
3518
|
if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
|
|
2920
3519
|
return resolvedAsset;
|
|
2921
3520
|
}
|
|
2922
|
-
return
|
|
3521
|
+
return join8(buildDirectory, assetPath.replace(/^\/+/, ""));
|
|
2923
3522
|
};
|
|
2924
3523
|
var pageFor = async (metadata, manifest, buildDirectory) => {
|
|
2925
3524
|
const assetPath = manifest[metadata.bundleKey];
|
|
2926
3525
|
if (!assetPath) {
|
|
2927
3526
|
throw new TypeError(`Mobile page ${metadata.pageId} references missing manifest asset ${metadata.bundleKey}.`);
|
|
2928
3527
|
}
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
3528
|
+
let resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
|
|
3529
|
+
if (metadata.framework === "html" || metadata.framework === "htmx") {
|
|
3530
|
+
const source = await readFile10(resolvedAssetPath, "utf8");
|
|
3531
|
+
const rewritten = rewriteStaticScriptPaths(source, manifest);
|
|
3532
|
+
const documentHash = sha256(new TextEncoder().encode(rewritten));
|
|
3533
|
+
resolvedAssetPath = join8(buildDirectory, ".absolutejs", "mobile-pages", `${documentHash}.html`);
|
|
3534
|
+
await mkdir8(dirname6(resolvedAssetPath), { recursive: true });
|
|
3535
|
+
await writeFile9(resolvedAssetPath, rewritten);
|
|
3536
|
+
}
|
|
3537
|
+
const pageAssetKey = metadata.bundleKey.replace(/Index$/u, "");
|
|
3538
|
+
const styleAssetPath = [
|
|
3539
|
+
`${pageAssetKey}BundledCSS`,
|
|
3540
|
+
`${pageAssetKey}CompiledCSS`
|
|
3541
|
+
].map((key) => manifest[key]).find((path) => typeof path === "string");
|
|
3542
|
+
const resolvedStylePath = styleAssetPath ? resolveAssetPath(buildDirectory, styleAssetPath) : undefined;
|
|
3543
|
+
const [bytes, styleBytes] = await Promise.all([
|
|
3544
|
+
readFile10(resolvedAssetPath),
|
|
3545
|
+
resolvedStylePath ? readFile10(resolvedStylePath) : undefined
|
|
3546
|
+
]);
|
|
3547
|
+
const bundlePath = `/${relative7(resolve8(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
|
|
3548
|
+
const styleBundlePath = resolvedStylePath ? `/${relative7(resolve8(buildDirectory), resolvedStylePath).replaceAll("\\", "/")}` : undefined;
|
|
2932
3549
|
return {
|
|
2933
3550
|
bundleHash: sha256(bytes),
|
|
2934
3551
|
bundlePath,
|
|
2935
3552
|
contract: metadata.contract,
|
|
2936
3553
|
framework: metadata.framework,
|
|
2937
3554
|
pageId: metadata.pageId,
|
|
2938
|
-
propsSchemaHash: metadata.propsSchemaHash
|
|
3555
|
+
propsSchemaHash: metadata.propsSchemaHash,
|
|
3556
|
+
...styleBytes && styleBundlePath ? {
|
|
3557
|
+
styleBundleHash: sha256(styleBytes),
|
|
3558
|
+
styleBundlePath
|
|
3559
|
+
} : {}
|
|
2939
3560
|
};
|
|
2940
3561
|
};
|
|
2941
3562
|
var buildAbsoluteMobileCompatibilityRelease = async (options) => {
|
|
2942
3563
|
const [captured, producerBytes] = await Promise.all([
|
|
2943
3564
|
captureAbsoluteMobileRouteGraph(options.app),
|
|
2944
|
-
|
|
3565
|
+
readFile10(options.producerPath)
|
|
2945
3566
|
]);
|
|
2946
3567
|
if (captured.length === 0) {
|
|
2947
3568
|
throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
|
|
@@ -2957,11 +3578,19 @@ var buildAbsoluteMobileCompatibilityRelease = async (options) => {
|
|
|
2957
3578
|
const pages = await Promise.all([...metadataByPage.values()].map((metadata) => pageFor(metadata, options.manifest, options.buildDirectory)));
|
|
2958
3579
|
const producerHash = sha256(producerBytes);
|
|
2959
3580
|
const appBuild = `ambuild_${sha256(new TextEncoder().encode(JSON.stringify({
|
|
2960
|
-
pages: pages.map(({
|
|
3581
|
+
pages: pages.map(({
|
|
3582
|
+
bundleHash,
|
|
3583
|
+
bundlePath,
|
|
3584
|
+
contract,
|
|
3585
|
+
pageId,
|
|
3586
|
+
styleBundleHash,
|
|
3587
|
+
styleBundlePath
|
|
3588
|
+
}) => ({
|
|
2961
3589
|
bundleHash,
|
|
2962
3590
|
bundlePath,
|
|
2963
3591
|
contract,
|
|
2964
|
-
pageId
|
|
3592
|
+
pageId,
|
|
3593
|
+
...styleBundleHash && styleBundlePath ? { styleBundleHash, styleBundlePath } : {}
|
|
2965
3594
|
})),
|
|
2966
3595
|
producerHash,
|
|
2967
3596
|
runtime: options.runtime
|
|
@@ -3021,16 +3650,17 @@ var captureAbsoluteMobileRouteGraph = async (app) => {
|
|
|
3021
3650
|
|
|
3022
3651
|
// src/mobile/capacitorBundle.ts
|
|
3023
3652
|
import {
|
|
3653
|
+
cp,
|
|
3024
3654
|
copyFile as copyFile5,
|
|
3025
|
-
mkdir as
|
|
3655
|
+
mkdir as mkdir9,
|
|
3026
3656
|
mkdtemp as mkdtemp4,
|
|
3027
|
-
readFile as
|
|
3028
|
-
rename as
|
|
3657
|
+
readFile as readFile11,
|
|
3658
|
+
rename as rename9,
|
|
3029
3659
|
rm as rm7,
|
|
3030
|
-
writeFile as
|
|
3660
|
+
writeFile as writeFile10
|
|
3031
3661
|
} from "fs/promises";
|
|
3032
3662
|
import { existsSync as existsSync2 } from "fs";
|
|
3033
|
-
import { basename as
|
|
3663
|
+
import { basename as basename3, dirname as dirname7, extname as extname2, join as join9, relative as relative8, resolve as resolve9 } from "path";
|
|
3034
3664
|
|
|
3035
3665
|
// src/mobile/routeMatcher.ts
|
|
3036
3666
|
var REGEXP_SPECIAL_CHARACTERS = /[.*+?^${}()|[\]\\]/g;
|
|
@@ -3303,6 +3933,13 @@ class AbsoluteMobilePageProtocolError extends Error {
|
|
|
3303
3933
|
this.code = code;
|
|
3304
3934
|
}
|
|
3305
3935
|
}
|
|
3936
|
+
var disposeAbsoluteMobilePage = async (target = window) => {
|
|
3937
|
+
const dispose = target.__ABSOLUTE_PAGE_DISPOSE__;
|
|
3938
|
+
target.__ABSOLUTE_PAGE_DISPOSE__ = undefined;
|
|
3939
|
+
target.__ABSOLUTE_PAGE_READY__ = undefined;
|
|
3940
|
+
if (dispose)
|
|
3941
|
+
await dispose();
|
|
3942
|
+
};
|
|
3306
3943
|
var frameworks4 = new Set([
|
|
3307
3944
|
"angular",
|
|
3308
3945
|
"ember",
|
|
@@ -3358,12 +3995,17 @@ var activateAbsoluteMobilePage = async (value, options) => {
|
|
|
3358
3995
|
throw new AbsoluteMobilePageProtocolError("invalid-envelope", "Expected a renderable mobile page response.");
|
|
3359
3996
|
}
|
|
3360
3997
|
const target = options.target ?? window;
|
|
3998
|
+
await disposeAbsoluteMobilePage(target);
|
|
3361
3999
|
target.__INITIAL_PROPS__ = envelope.response.props;
|
|
4000
|
+
target.__ABS_ANGULAR_REQUEST_CONTEXT__ = envelope.response.props;
|
|
3362
4001
|
target.__ABSOLUTE_PAGE_RENDER_MODE__ = "client";
|
|
3363
4002
|
await options.loadPage({
|
|
3364
4003
|
contract: envelope.response.contract,
|
|
3365
4004
|
pageId: envelope.response.pageId
|
|
3366
4005
|
});
|
|
4006
|
+
if (target.__ABSOLUTE_PAGE_READY__) {
|
|
4007
|
+
await target.__ABSOLUTE_PAGE_READY__;
|
|
4008
|
+
}
|
|
3367
4009
|
return {
|
|
3368
4010
|
contract: envelope.response.contract,
|
|
3369
4011
|
kind: "rendered",
|
|
@@ -3456,19 +4098,51 @@ var resolveAbsoluteMobileDeepLink = (manifest, value) => {
|
|
|
3456
4098
|
}
|
|
3457
4099
|
return `${url.pathname || "/"}${url.search}${url.hash}`;
|
|
3458
4100
|
};
|
|
4101
|
+
var resolveAbsoluteMobileNavigation = (manifest, value, localOrigin) => {
|
|
4102
|
+
const url = new URL(value, `${localOrigin}/`);
|
|
4103
|
+
const local = new URL(localOrigin);
|
|
4104
|
+
const production = new URL(manifest.productionOrigin);
|
|
4105
|
+
const matches = (candidate, allowed) => candidate.protocol === allowed.protocol && candidate.host === allowed.host;
|
|
4106
|
+
if (!matches(url, local) && !matches(url, production)) {
|
|
4107
|
+
return;
|
|
4108
|
+
}
|
|
4109
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
4110
|
+
};
|
|
3459
4111
|
|
|
3460
4112
|
// src/mobile/capacitorBundle.ts
|
|
3461
4113
|
var MANIFEST_FILE = "absolute-mobile-manifest.json";
|
|
3462
4114
|
var BOOTSTRAP_FILE = "absolute-mobile-bootstrap.js";
|
|
3463
4115
|
var INDEX_FILE = "index.html";
|
|
3464
|
-
var
|
|
4116
|
+
var CLIENT_CSS_DEPENDENCY_PATTERN = /(?:@import\s+(?:url\(\s*)?|url\(\s*)["']?((?:\/|\.\.\/|\.\/)[^"')\s]+)["']?\s*\)?/gu;
|
|
4117
|
+
var CLIENT_MARKUP_DEPENDENCY_PATTERN = /<(?:script\b[^>]*\bsrc|link\b[^>]*\bhref|img\b[^>]*\bsrc|source\b[^>]*\bsrcset)\s*=\s*["']((?:\/|\.\.\/|\.\/)[^"',\s]+)/giu;
|
|
4118
|
+
var CAPACITOR_CLIENT_FRAMEWORKS = new Set([
|
|
4119
|
+
"angular",
|
|
4120
|
+
"html",
|
|
4121
|
+
"htmx",
|
|
4122
|
+
"react",
|
|
4123
|
+
"svelte",
|
|
4124
|
+
"vue"
|
|
4125
|
+
]);
|
|
4126
|
+
var CLIENT_ASSET_DIRECTORIES = ["assets", "html", "htmx", "indexes"];
|
|
3465
4127
|
var errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
|
|
3466
4128
|
var shellBootstrapModule = () => {
|
|
3467
|
-
const candidate = ["js", "ts"].map((extension) =>
|
|
4129
|
+
const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
|
|
3468
4130
|
if (candidate)
|
|
3469
4131
|
return candidate;
|
|
3470
4132
|
throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
|
|
3471
4133
|
};
|
|
4134
|
+
var shellAuthModule = () => {
|
|
4135
|
+
const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellAuth.${extension}`)).find(existsSync2);
|
|
4136
|
+
if (candidate)
|
|
4137
|
+
return candidate;
|
|
4138
|
+
throw new TypeError("AbsoluteJS mobile auth shell module is missing.");
|
|
4139
|
+
};
|
|
4140
|
+
var shellSyncModule = () => {
|
|
4141
|
+
const candidate = ["js", "ts"].map((extension) => join9(import.meta.dir, `shellSync.${extension}`)).find(existsSync2);
|
|
4142
|
+
if (candidate)
|
|
4143
|
+
return candidate;
|
|
4144
|
+
throw new TypeError("AbsoluteJS mobile Sync shell module is missing.");
|
|
4145
|
+
};
|
|
3472
4146
|
var escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
3473
4147
|
var indexHtml = (appName) => `<!doctype html>
|
|
3474
4148
|
<html>
|
|
@@ -3492,11 +4166,16 @@ var sourceAssetPath = (buildDirectory, bundlePath) => {
|
|
|
3492
4166
|
}
|
|
3493
4167
|
return asset;
|
|
3494
4168
|
};
|
|
3495
|
-
var buildShellBootstrap = async (staging) => {
|
|
4169
|
+
var buildShellBootstrap = async (staging, auth, sync) => {
|
|
3496
4170
|
const modulePath = shellBootstrapModule();
|
|
3497
|
-
const
|
|
3498
|
-
|
|
3499
|
-
|
|
4171
|
+
const authImport = auth ? `import { createAbsoluteMobileShellAuth } from ${JSON.stringify(shellAuthModule())};
|
|
4172
|
+
` : "";
|
|
4173
|
+
const options = auth ? `{ createAuth: createAbsoluteMobileShellAuth${sync ? ", installSync: installAbsoluteMobileShellSync" : ""} }` : "";
|
|
4174
|
+
const syncImport = sync ? `import { installAbsoluteMobileShellSync } from ${JSON.stringify(shellSyncModule())};
|
|
4175
|
+
` : "";
|
|
4176
|
+
const entryPath = join9(staging, ".absolute-mobile-entry.ts");
|
|
4177
|
+
await writeFile10(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
|
|
4178
|
+
${authImport}${syncImport}void startAbsoluteMobileShell(${options});
|
|
3500
4179
|
`);
|
|
3501
4180
|
const build = await Bun.build({
|
|
3502
4181
|
entrypoints: [entryPath],
|
|
@@ -3507,7 +4186,7 @@ void startAbsoluteMobileShell();
|
|
|
3507
4186
|
if (!build.success || build.outputs.length !== 1) {
|
|
3508
4187
|
throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
|
|
3509
4188
|
}
|
|
3510
|
-
await
|
|
4189
|
+
await rename9(build.outputs[0]?.path ?? "", join9(staging, BOOTSTRAP_FILE));
|
|
3511
4190
|
await rm7(entryPath, { force: true });
|
|
3512
4191
|
};
|
|
3513
4192
|
var removePreviousBundle = async (backup, moved) => {
|
|
@@ -3518,20 +4197,20 @@ var removePreviousBundle = async (backup, moved) => {
|
|
|
3518
4197
|
var restorePreviousBundle = async (backup, destination, moved) => {
|
|
3519
4198
|
if (!moved)
|
|
3520
4199
|
return;
|
|
3521
|
-
await
|
|
4200
|
+
await rename9(backup, destination);
|
|
3522
4201
|
};
|
|
3523
4202
|
var installBundle = async (staging, destination) => {
|
|
3524
4203
|
const backup = `${destination}.previous-${crypto.randomUUID()}`;
|
|
3525
4204
|
let movedPrevious = false;
|
|
3526
4205
|
try {
|
|
3527
|
-
await
|
|
4206
|
+
await rename9(destination, backup);
|
|
3528
4207
|
movedPrevious = true;
|
|
3529
4208
|
} catch (error) {
|
|
3530
4209
|
if (!errorHasCode2(error, "ENOENT"))
|
|
3531
4210
|
throw error;
|
|
3532
4211
|
}
|
|
3533
4212
|
try {
|
|
3534
|
-
await
|
|
4213
|
+
await rename9(staging, destination);
|
|
3535
4214
|
await removePreviousBundle(backup, movedPrevious);
|
|
3536
4215
|
} catch (error) {
|
|
3537
4216
|
await restorePreviousBundle(backup, destination, movedPrevious);
|
|
@@ -3539,21 +4218,62 @@ var installBundle = async (staging, destination) => {
|
|
|
3539
4218
|
}
|
|
3540
4219
|
};
|
|
3541
4220
|
var copyClientPage = async (page, buildDirectory, staging, copiedDependencies) => {
|
|
3542
|
-
if (page.framework
|
|
3543
|
-
throw new TypeError(`Capacitor
|
|
4221
|
+
if (!CAPACITOR_CLIENT_FRAMEWORKS.has(page.framework)) {
|
|
4222
|
+
throw new TypeError(`Capacitor client rendering does not yet support ${page.framework} page ${page.pageId}.`);
|
|
3544
4223
|
}
|
|
3545
|
-
const extension =
|
|
4224
|
+
const extension = extname2(page.bundlePath) || ".js";
|
|
3546
4225
|
const localBundlePath = `./pages/${page.bundleHash}${extension}`;
|
|
3547
4226
|
const source = sourceAssetPath(buildDirectory, page.bundlePath);
|
|
3548
|
-
await copyFile5(source,
|
|
4227
|
+
await copyFile5(source, join9(staging, localBundlePath));
|
|
3549
4228
|
await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
|
|
3550
|
-
|
|
4229
|
+
let localStylePath;
|
|
4230
|
+
if (page.styleBundlePath && page.styleBundleHash) {
|
|
4231
|
+
const styleExtension = extname2(page.styleBundlePath) || ".css";
|
|
4232
|
+
localStylePath = `./styles/${page.styleBundleHash}${styleExtension}`;
|
|
4233
|
+
const styleSource = sourceAssetPath(buildDirectory, page.styleBundlePath);
|
|
4234
|
+
await mkdir9(dirname7(join9(staging, localStylePath)), {
|
|
4235
|
+
recursive: true
|
|
4236
|
+
});
|
|
4237
|
+
await copyFile5(styleSource, join9(staging, localStylePath));
|
|
4238
|
+
await copyAbsoluteClientDependencies(styleSource, buildDirectory, staging, copiedDependencies);
|
|
4239
|
+
}
|
|
4240
|
+
return {
|
|
4241
|
+
...page,
|
|
4242
|
+
localBundlePath,
|
|
4243
|
+
...localStylePath ? { localStylePath } : {}
|
|
4244
|
+
};
|
|
3551
4245
|
};
|
|
3552
|
-
var absoluteClientImports = async (sourcePath) => {
|
|
3553
|
-
const source = await
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
4246
|
+
var absoluteClientImports = async (sourcePath, buildDirectory) => {
|
|
4247
|
+
const source = await readFile11(sourcePath, "utf8");
|
|
4248
|
+
const extension = extname2(sourcePath).toLowerCase();
|
|
4249
|
+
let scriptLoader;
|
|
4250
|
+
if (extension === ".tsx")
|
|
4251
|
+
scriptLoader = "tsx";
|
|
4252
|
+
else if (extension === ".ts")
|
|
4253
|
+
scriptLoader = "ts";
|
|
4254
|
+
else if (extension === ".jsx")
|
|
4255
|
+
scriptLoader = "jsx";
|
|
4256
|
+
else if ([".js", ".mjs", ".cjs"].includes(extension))
|
|
4257
|
+
scriptLoader = "js";
|
|
4258
|
+
const scriptImports = scriptLoader ? new Bun.Transpiler({ loader: scriptLoader }).scanImports(source).map(({ path }) => path) : [];
|
|
4259
|
+
const cssImports = extension === ".css" ? [...source.matchAll(CLIENT_CSS_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
|
|
4260
|
+
const markupImports = extension === ".html" ? [...source.matchAll(CLIENT_MARKUP_DEPENDENCY_PATTERN)].flatMap((match) => match[1] ?? []) : [];
|
|
4261
|
+
return [...scriptImports, ...cssImports, ...markupImports].flatMap((specifier) => {
|
|
4262
|
+
if (!specifier)
|
|
4263
|
+
return [];
|
|
4264
|
+
if (!specifier.startsWith("/") && !specifier.startsWith("./") && !specifier.startsWith("../")) {
|
|
4265
|
+
return [];
|
|
4266
|
+
}
|
|
4267
|
+
const clean = specifier.split(/[?#]/u, 1)[0] ?? specifier;
|
|
4268
|
+
if (clean.startsWith("/"))
|
|
4269
|
+
return [clean];
|
|
4270
|
+
const resolved = resolve9(dirname7(sourcePath), clean);
|
|
4271
|
+
const root = resolve9(buildDirectory);
|
|
4272
|
+
const relativePath = relative8(root, resolved).replaceAll("\\", "/");
|
|
4273
|
+
if (relativePath === ".." || relativePath.startsWith("../")) {
|
|
4274
|
+
throw new TypeError(`Mobile client dependency escaped the build directory: ${specifier}`);
|
|
4275
|
+
}
|
|
4276
|
+
return [`/${relativePath}`];
|
|
3557
4277
|
});
|
|
3558
4278
|
};
|
|
3559
4279
|
var copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, copied) => {
|
|
@@ -3561,13 +4281,13 @@ var copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, co
|
|
|
3561
4281
|
return;
|
|
3562
4282
|
copied.add(specifier);
|
|
3563
4283
|
const source = sourceAssetPath(buildDirectory, specifier);
|
|
3564
|
-
const destination =
|
|
3565
|
-
await
|
|
4284
|
+
const destination = join9(staging, specifier.replace(/^\/+/, ""));
|
|
4285
|
+
await mkdir9(dirname7(destination), { recursive: true });
|
|
3566
4286
|
await copyFile5(source, destination);
|
|
3567
4287
|
await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
|
|
3568
4288
|
};
|
|
3569
4289
|
var copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
|
|
3570
|
-
const dependencies = await absoluteClientImports(sourcePath);
|
|
4290
|
+
const dependencies = await absoluteClientImports(sourcePath, buildDirectory);
|
|
3571
4291
|
await Promise.all(dependencies.map((specifier) => copyAbsoluteClientDependency(specifier, buildDirectory, staging, copied)));
|
|
3572
4292
|
};
|
|
3573
4293
|
var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
@@ -3575,15 +4295,20 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
3575
4295
|
throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
|
|
3576
4296
|
}
|
|
3577
4297
|
const destination = options.config.bundleDirectory;
|
|
3578
|
-
await
|
|
3579
|
-
const staging = await mkdtemp4(
|
|
4298
|
+
await mkdir9(dirname7(destination), { recursive: true });
|
|
4299
|
+
const staging = await mkdtemp4(join9(dirname7(destination), `.${basename3(destination)}.stage-`));
|
|
3580
4300
|
try {
|
|
3581
|
-
const pageDirectory =
|
|
3582
|
-
await
|
|
4301
|
+
const pageDirectory = join9(staging, "pages");
|
|
4302
|
+
await mkdir9(pageDirectory, { recursive: true });
|
|
4303
|
+
await Promise.all(CLIENT_ASSET_DIRECTORIES.map((directory) => ({
|
|
4304
|
+
destination: join9(staging, directory),
|
|
4305
|
+
source: join9(options.buildDirectory, directory)
|
|
4306
|
+
})).filter(({ source }) => existsSync2(source)).map(({ destination: assetDestination, source }) => cp(source, assetDestination, { recursive: true })));
|
|
3583
4307
|
const copiedDependencies = new Set;
|
|
3584
4308
|
const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
|
|
3585
4309
|
const manifest = {
|
|
3586
4310
|
appBuild: options.artifact.appBuild,
|
|
4311
|
+
...options.auth ? { auth: options.auth } : {},
|
|
3587
4312
|
appId: options.config.appId,
|
|
3588
4313
|
appName: options.config.appName,
|
|
3589
4314
|
deepLinkHosts: options.config.deepLinkHosts,
|
|
@@ -3593,13 +4318,14 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
3593
4318
|
pages,
|
|
3594
4319
|
productionOrigin: options.config.productionOrigin,
|
|
3595
4320
|
routes: options.artifact.routes,
|
|
3596
|
-
runtime: options.artifact.runtime
|
|
4321
|
+
runtime: options.artifact.runtime,
|
|
4322
|
+
...options.sync ? { sync: { socketTickets: true } } : {}
|
|
3597
4323
|
};
|
|
3598
4324
|
await Promise.all([
|
|
3599
|
-
|
|
4325
|
+
writeFile10(join9(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
3600
4326
|
`),
|
|
3601
|
-
|
|
3602
|
-
buildShellBootstrap(staging)
|
|
4327
|
+
writeFile10(join9(staging, INDEX_FILE), indexHtml(options.config.appName)),
|
|
4328
|
+
buildShellBootstrap(staging, options.auth !== undefined, options.auth !== undefined && options.sync === true)
|
|
3603
4329
|
]);
|
|
3604
4330
|
await installBundle(staging, destination);
|
|
3605
4331
|
return manifest;
|
|
@@ -3610,17 +4336,17 @@ var materializeAbsoluteCapacitorWebBundle = async (options) => {
|
|
|
3610
4336
|
};
|
|
3611
4337
|
|
|
3612
4338
|
// src/mobile/materializedBundle.ts
|
|
3613
|
-
import { createHash as
|
|
4339
|
+
import { createHash as createHash9 } from "crypto";
|
|
3614
4340
|
import {
|
|
3615
4341
|
access as access8,
|
|
3616
|
-
mkdir as
|
|
4342
|
+
mkdir as mkdir10,
|
|
3617
4343
|
mkdtemp as mkdtemp5,
|
|
3618
|
-
readFile as
|
|
3619
|
-
rename as
|
|
4344
|
+
readFile as readFile12,
|
|
4345
|
+
rename as rename10,
|
|
3620
4346
|
rm as rm8,
|
|
3621
|
-
writeFile as
|
|
4347
|
+
writeFile as writeFile11
|
|
3622
4348
|
} from "fs/promises";
|
|
3623
|
-
import { dirname as
|
|
4349
|
+
import { dirname as dirname8, join as join10, resolve as resolvePath3 } from "path";
|
|
3624
4350
|
import { pathToFileURL } from "url";
|
|
3625
4351
|
var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1;
|
|
3626
4352
|
var CURRENT_BUNDLE_FILE = "current.json";
|
|
@@ -3634,7 +4360,7 @@ var bundleIdFor = (currentReleaseId, releases) => {
|
|
|
3634
4360
|
currentReleaseId,
|
|
3635
4361
|
releases: releases.map(({ releaseId }) => releaseId)
|
|
3636
4362
|
});
|
|
3637
|
-
return `amb_${
|
|
4363
|
+
return `amb_${createHash9("sha256").update(identity).digest("hex")}`;
|
|
3638
4364
|
};
|
|
3639
4365
|
var parseBundleIndex = (value) => {
|
|
3640
4366
|
if (!isRecord7(value) || value.format !== ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT || typeof value.bundleId !== "string" || !BUNDLE_ID_PATTERN.test(value.bundleId) || typeof value.currentReleaseId !== "string" || !Array.isArray(value.releases)) {
|
|
@@ -3660,17 +4386,17 @@ var parseBundleIndex = (value) => {
|
|
|
3660
4386
|
};
|
|
3661
4387
|
};
|
|
3662
4388
|
var writeRelease = async (root, release) => {
|
|
3663
|
-
const directory =
|
|
3664
|
-
const producerPath =
|
|
3665
|
-
await
|
|
4389
|
+
const directory = join10(root, release.artifact.releaseId);
|
|
4390
|
+
const producerPath = join10(directory, release.artifact.producer.module);
|
|
4391
|
+
await mkdir10(dirname8(producerPath), { recursive: true });
|
|
3666
4392
|
await Promise.all([
|
|
3667
|
-
|
|
4393
|
+
writeFile11(join10(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
|
|
3668
4394
|
`),
|
|
3669
|
-
|
|
4395
|
+
writeFile11(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
|
|
3670
4396
|
]);
|
|
3671
4397
|
};
|
|
3672
4398
|
var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
|
|
3673
|
-
const destination =
|
|
4399
|
+
const destination = join10(bundlesRoot, bundleId);
|
|
3674
4400
|
try {
|
|
3675
4401
|
await access8(destination);
|
|
3676
4402
|
return destination;
|
|
@@ -3678,10 +4404,10 @@ var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
|
|
|
3678
4404
|
if (!errorHasCode3(error, "ENOENT"))
|
|
3679
4405
|
throw error;
|
|
3680
4406
|
}
|
|
3681
|
-
const staging = await mkdtemp5(
|
|
4407
|
+
const staging = await mkdtemp5(join10(bundlesRoot, ".stage-"));
|
|
3682
4408
|
try {
|
|
3683
4409
|
await Promise.all(releases.map((release) => writeRelease(staging, release)));
|
|
3684
|
-
await
|
|
4410
|
+
await rename10(staging, destination);
|
|
3685
4411
|
} catch (error) {
|
|
3686
4412
|
await rm8(staging, { force: true, recursive: true });
|
|
3687
4413
|
if (errorHasCode3(error, "EEXIST") || errorHasCode3(error, "ENOTEMPTY")) {
|
|
@@ -3711,16 +4437,16 @@ var resolveProducerHandler = (loaded, exportName) => {
|
|
|
3711
4437
|
};
|
|
3712
4438
|
};
|
|
3713
4439
|
var loadAbsoluteMobileMaterializedBundle = async (root) => {
|
|
3714
|
-
const resolvedRoot =
|
|
3715
|
-
const serialized = await
|
|
4440
|
+
const resolvedRoot = resolvePath3(root);
|
|
4441
|
+
const serialized = await readFile12(join10(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
3716
4442
|
const parsed = JSON.parse(serialized);
|
|
3717
4443
|
const index = parseBundleIndex(parsed);
|
|
3718
|
-
const bundleRoot =
|
|
4444
|
+
const bundleRoot = join10(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
3719
4445
|
return {
|
|
3720
4446
|
artifacts: index.releases,
|
|
3721
4447
|
currentReleaseId: index.currentReleaseId,
|
|
3722
4448
|
loadProducer: async (artifact) => {
|
|
3723
|
-
const modulePath =
|
|
4449
|
+
const modulePath = join10(bundleRoot, artifact.releaseId, artifact.producer.module);
|
|
3724
4450
|
await verifyAbsoluteMobileCompatibilityProducer({
|
|
3725
4451
|
artifact,
|
|
3726
4452
|
producer: Bun.file(modulePath)
|
|
@@ -3746,9 +4472,9 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
|
|
|
3746
4472
|
}
|
|
3747
4473
|
return release;
|
|
3748
4474
|
});
|
|
3749
|
-
const root =
|
|
3750
|
-
const bundlesRoot =
|
|
3751
|
-
await
|
|
4475
|
+
const root = resolvePath3(input.root);
|
|
4476
|
+
const bundlesRoot = join10(root, BUNDLES_DIRECTORY);
|
|
4477
|
+
await mkdir10(bundlesRoot, { recursive: true });
|
|
3752
4478
|
const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
|
|
3753
4479
|
await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
|
|
3754
4480
|
const index = {
|
|
@@ -3757,22 +4483,22 @@ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
|
|
|
3757
4483
|
format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
|
|
3758
4484
|
releases: artifacts
|
|
3759
4485
|
};
|
|
3760
|
-
const pointerPath =
|
|
3761
|
-
const temporaryPointerPath =
|
|
3762
|
-
await
|
|
4486
|
+
const pointerPath = join10(root, CURRENT_BUNDLE_FILE);
|
|
4487
|
+
const temporaryPointerPath = join10(root, `.current-${crypto.randomUUID()}.json`);
|
|
4488
|
+
await writeFile11(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
|
|
3763
4489
|
`, { flag: "wx" });
|
|
3764
|
-
await
|
|
4490
|
+
await rename10(temporaryPointerPath, pointerPath);
|
|
3765
4491
|
return index;
|
|
3766
4492
|
};
|
|
3767
4493
|
var readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
3768
|
-
const resolvedRoot =
|
|
4494
|
+
const resolvedRoot = resolvePath3(root);
|
|
3769
4495
|
try {
|
|
3770
|
-
const serialized = await
|
|
4496
|
+
const serialized = await readFile12(join10(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
|
|
3771
4497
|
const parsed = JSON.parse(serialized);
|
|
3772
4498
|
const index = parseBundleIndex(parsed);
|
|
3773
|
-
const bundleRoot =
|
|
4499
|
+
const bundleRoot = join10(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
|
|
3774
4500
|
return Promise.all(index.releases.map(async (artifact) => {
|
|
3775
|
-
const producer = Bun.file(
|
|
4501
|
+
const producer = Bun.file(join10(bundleRoot, artifact.releaseId, artifact.producer.module));
|
|
3776
4502
|
await verifyAbsoluteMobileCompatibilityProducer({
|
|
3777
4503
|
artifact,
|
|
3778
4504
|
producer
|
|
@@ -3786,6 +4512,58 @@ var readAbsoluteMobileMaterializedReleases = async (root) => {
|
|
|
3786
4512
|
}
|
|
3787
4513
|
};
|
|
3788
4514
|
|
|
4515
|
+
// src/mobile/nativeAuth.ts
|
|
4516
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
4517
|
+
import { join as join11 } from "path";
|
|
4518
|
+
var ABSOLUTE_AUTH_PACKAGE = "@absolutejs/auth";
|
|
4519
|
+
var ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV = "ABSOLUTE_AUTH_NATIVE_CLIENTS";
|
|
4520
|
+
var ABSOLUTE_NATIVE_AUTH_SCOPES = ["openid", "profile"];
|
|
4521
|
+
var ABSOLUTE_SYNC_PACKAGE = "@absolutejs/sync";
|
|
4522
|
+
var readPackageManifest = (projectRoot) => {
|
|
4523
|
+
try {
|
|
4524
|
+
return JSON.parse(readFileSync2(join11(projectRoot, "package.json"), "utf8"));
|
|
4525
|
+
} catch {
|
|
4526
|
+
return;
|
|
4527
|
+
}
|
|
4528
|
+
};
|
|
4529
|
+
var packageManifestHas = (manifest, packageName) => {
|
|
4530
|
+
if (typeof manifest !== "object" || manifest === null)
|
|
4531
|
+
return false;
|
|
4532
|
+
return [
|
|
4533
|
+
Reflect.get(manifest, "dependencies"),
|
|
4534
|
+
Reflect.get(manifest, "devDependencies"),
|
|
4535
|
+
Reflect.get(manifest, "optionalDependencies"),
|
|
4536
|
+
Reflect.get(manifest, "peerDependencies")
|
|
4537
|
+
].some((dependencies) => typeof dependencies === "object" && dependencies !== null && Object.hasOwn(dependencies, packageName));
|
|
4538
|
+
};
|
|
4539
|
+
var createAbsoluteMobileAuthManifest = (config) => {
|
|
4540
|
+
const scheme = config.deepLinkScheme ?? config.appId.toLowerCase();
|
|
4541
|
+
return {
|
|
4542
|
+
clientId: `absolutejs-native:${config.appId}`,
|
|
4543
|
+
issuer: config.productionOrigin,
|
|
4544
|
+
redirectUri: `${scheme}://auth/callback`,
|
|
4545
|
+
scopes: [...ABSOLUTE_NATIVE_AUTH_SCOPES]
|
|
4546
|
+
};
|
|
4547
|
+
};
|
|
4548
|
+
var installAbsoluteMobileAuthEnvironment = (projectRoot, config) => {
|
|
4549
|
+
const auth = resolveAbsoluteMobileAuthManifest(projectRoot, config);
|
|
4550
|
+
const serialized = serializeAbsoluteMobileAuthEnvironment(config, auth);
|
|
4551
|
+
if (serialized === undefined)
|
|
4552
|
+
delete process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV];
|
|
4553
|
+
else
|
|
4554
|
+
process.env[ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV] = serialized;
|
|
4555
|
+
return auth;
|
|
4556
|
+
};
|
|
4557
|
+
var projectUsesAbsoluteAuth = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_AUTH_PACKAGE);
|
|
4558
|
+
var projectUsesAbsoluteSync = (projectRoot) => packageManifestHas(readPackageManifest(projectRoot), ABSOLUTE_SYNC_PACKAGE);
|
|
4559
|
+
var resolveAbsoluteMobileAuthManifest = (projectRoot, config) => projectUsesAbsoluteAuth(projectRoot) ? createAbsoluteMobileAuthManifest(config) : undefined;
|
|
4560
|
+
var serializeAbsoluteMobileAuthEnvironment = (config, auth) => auth === undefined ? undefined : JSON.stringify([
|
|
4561
|
+
{
|
|
4562
|
+
...auth,
|
|
4563
|
+
name: `${config.appName} native app`
|
|
4564
|
+
}
|
|
4565
|
+
]);
|
|
4566
|
+
|
|
3789
4567
|
// src/mobile/buildPipeline.ts
|
|
3790
4568
|
var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes"));
|
|
3791
4569
|
var isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
@@ -3796,12 +4574,11 @@ var serverExportName = (loaded, app) => {
|
|
|
3796
4574
|
return "app";
|
|
3797
4575
|
return "default";
|
|
3798
4576
|
};
|
|
3799
|
-
var
|
|
3800
|
-
if (previous !== undefined)
|
|
3801
|
-
process.env
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
delete process.env.ABSOLUTE_BUILD_DIR;
|
|
4577
|
+
var restoreEnvironmentVariable = (name, previous) => {
|
|
4578
|
+
if (previous !== undefined)
|
|
4579
|
+
process.env[name] = previous;
|
|
4580
|
+
else
|
|
4581
|
+
delete process.env[name];
|
|
3805
4582
|
};
|
|
3806
4583
|
var requireRelease = (releases, releaseId) => {
|
|
3807
4584
|
const release = releases.get(releaseId);
|
|
@@ -3823,9 +4600,9 @@ var loadServerApp = async (producerPath) => {
|
|
|
3823
4600
|
var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
3824
4601
|
const buildDirectory = resolve10(options.buildDirectory);
|
|
3825
4602
|
const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
|
|
3826
|
-
const root =
|
|
4603
|
+
const root = join12(buildDirectory, ".absolutejs", "mobile-compatibility");
|
|
3827
4604
|
const [manifestSource, previous] = await Promise.all([
|
|
3828
|
-
|
|
4605
|
+
readFile13(join12(buildDirectory, "manifest.json"), "utf8"),
|
|
3829
4606
|
readAbsoluteMobileMaterializedReleases(root)
|
|
3830
4607
|
]);
|
|
3831
4608
|
const manifest = JSON.parse(manifestSource);
|
|
@@ -3833,12 +4610,20 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
3833
4610
|
throw new TypeError("Invalid AbsoluteJS build manifest for mobile capture.");
|
|
3834
4611
|
}
|
|
3835
4612
|
const previousBuildDirectory = process.env.ABSOLUTE_BUILD_DIR;
|
|
4613
|
+
const previousCompiledRuntime = process.env.ABSOLUTE_COMPILED_RUNTIME;
|
|
4614
|
+
const previousConfigPath = process.env.ABSOLUTE_CONFIG;
|
|
3836
4615
|
process.env.ABSOLUTE_BUILD_DIR = buildDirectory;
|
|
4616
|
+
process.env.ABSOLUTE_COMPILED_RUNTIME = "1";
|
|
4617
|
+
if (options.configPath) {
|
|
4618
|
+
process.env.ABSOLUTE_CONFIG = resolve10(options.projectRoot, options.configPath);
|
|
4619
|
+
}
|
|
3837
4620
|
let loaded;
|
|
3838
4621
|
try {
|
|
3839
4622
|
loaded = await loadServerApp(resolve10(options.producerPath));
|
|
3840
4623
|
} finally {
|
|
3841
|
-
|
|
4624
|
+
restoreEnvironmentVariable("ABSOLUTE_BUILD_DIR", previousBuildDirectory);
|
|
4625
|
+
restoreEnvironmentVariable("ABSOLUTE_COMPILED_RUNTIME", previousCompiledRuntime);
|
|
4626
|
+
restoreEnvironmentVariable("ABSOLUTE_CONFIG", previousConfigPath);
|
|
3842
4627
|
}
|
|
3843
4628
|
const current = await buildAbsoluteMobileCompatibilityRelease({
|
|
3844
4629
|
app: loaded.app,
|
|
@@ -3850,6 +4635,11 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
3850
4635
|
producerPath: resolve10(options.producerPath),
|
|
3851
4636
|
runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
|
|
3852
4637
|
});
|
|
4638
|
+
const auth = resolveAbsoluteMobileAuthManifest(options.projectRoot, mobile);
|
|
4639
|
+
const sync = auth !== undefined && projectUsesAbsoluteSync(options.projectRoot);
|
|
4640
|
+
if (auth && !loaded.app.routes.some((route) => route.path === "/.well-known/openid-configuration")) {
|
|
4641
|
+
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.");
|
|
4642
|
+
}
|
|
3853
4643
|
const releasesById = new Map([current, ...previous].map((release) => [
|
|
3854
4644
|
release.artifact.releaseId,
|
|
3855
4645
|
release
|
|
@@ -3862,8 +4652,10 @@ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
|
|
|
3862
4652
|
});
|
|
3863
4653
|
await materializeAbsoluteCapacitorWebBundle({
|
|
3864
4654
|
artifact: current.artifact,
|
|
4655
|
+
...auth ? { auth } : {},
|
|
3865
4656
|
buildDirectory,
|
|
3866
|
-
config: mobile
|
|
4657
|
+
config: mobile,
|
|
4658
|
+
...sync ? { sync: true } : {}
|
|
3867
4659
|
});
|
|
3868
4660
|
return current.artifact;
|
|
3869
4661
|
};
|
|
@@ -3888,6 +4680,64 @@ var ensureProducerStorage = () => {
|
|
|
3888
4680
|
var runWithAbsoluteMobileProducer = (context, callback) => ensureProducerStorage().run(context, callback);
|
|
3889
4681
|
|
|
3890
4682
|
// src/mobile/compatibilityDispatcher.ts
|
|
4683
|
+
var MOBILE_WEBVIEW_ORIGINS = new Set([
|
|
4684
|
+
"capacitor://localhost",
|
|
4685
|
+
"http://localhost",
|
|
4686
|
+
"https://localhost"
|
|
4687
|
+
]);
|
|
4688
|
+
var MOBILE_REQUEST_HEADER_NAMES = Object.values(MOBILE_PAGE_REQUEST_HEADERS);
|
|
4689
|
+
var MOBILE_CORS_ALLOW_HEADERS = [
|
|
4690
|
+
"accept",
|
|
4691
|
+
"content-type",
|
|
4692
|
+
"authorization",
|
|
4693
|
+
"hx-current-url",
|
|
4694
|
+
"hx-request",
|
|
4695
|
+
"hx-target",
|
|
4696
|
+
"hx-trigger",
|
|
4697
|
+
"hx-trigger-name",
|
|
4698
|
+
...MOBILE_REQUEST_HEADER_NAMES
|
|
4699
|
+
].join(", ");
|
|
4700
|
+
var MOBILE_CORS_METHODS = new Set([
|
|
4701
|
+
"DELETE",
|
|
4702
|
+
"GET",
|
|
4703
|
+
"HEAD",
|
|
4704
|
+
"OPTIONS",
|
|
4705
|
+
"PATCH",
|
|
4706
|
+
"POST",
|
|
4707
|
+
"PUT"
|
|
4708
|
+
]);
|
|
4709
|
+
var mobileWebViewOrigin = (request) => {
|
|
4710
|
+
const origin = request.headers.get("origin");
|
|
4711
|
+
return origin && MOBILE_WEBVIEW_ORIGINS.has(origin) ? origin : undefined;
|
|
4712
|
+
};
|
|
4713
|
+
var applyMobileCorsHeaders = (response, origin) => {
|
|
4714
|
+
response.headers.set("access-control-allow-credentials", "true");
|
|
4715
|
+
response.headers.set("access-control-allow-origin", origin);
|
|
4716
|
+
response.headers.append("vary", "Origin");
|
|
4717
|
+
return response;
|
|
4718
|
+
};
|
|
4719
|
+
var mobilePreflightResponse = (request) => {
|
|
4720
|
+
if (request.method !== "OPTIONS")
|
|
4721
|
+
return;
|
|
4722
|
+
const origin = mobileWebViewOrigin(request);
|
|
4723
|
+
if (!origin)
|
|
4724
|
+
return;
|
|
4725
|
+
const requestedHeaders = request.headers.get("access-control-request-headers");
|
|
4726
|
+
const requestedMethod = request.headers.get("access-control-request-method")?.toUpperCase() ?? "";
|
|
4727
|
+
if (!MOBILE_CORS_METHODS.has(requestedMethod))
|
|
4728
|
+
return;
|
|
4729
|
+
return new Response(null, {
|
|
4730
|
+
headers: {
|
|
4731
|
+
"access-control-allow-credentials": "true",
|
|
4732
|
+
"access-control-allow-headers": requestedHeaders || MOBILE_CORS_ALLOW_HEADERS,
|
|
4733
|
+
"access-control-allow-methods": [...MOBILE_CORS_METHODS].join(", "),
|
|
4734
|
+
"access-control-allow-origin": origin,
|
|
4735
|
+
"access-control-max-age": "600",
|
|
4736
|
+
vary: "Origin, Access-Control-Request-Headers"
|
|
4737
|
+
},
|
|
4738
|
+
status: 204
|
|
4739
|
+
});
|
|
4740
|
+
};
|
|
3891
4741
|
var artifactOwnsRequest = (artifact, pageId, request) => {
|
|
3892
4742
|
const { pathname } = new URL(request.url);
|
|
3893
4743
|
return artifact.routes.some((route) => route.pageId === pageId && route.method === request.method && matchesAbsoluteMobileRoutePattern(route.pattern, pathname));
|
|
@@ -3915,6 +4765,9 @@ var createAbsoluteMobileCompatibilityDispatcher = (options) => {
|
|
|
3915
4765
|
return new Elysia2({ name: "absolutejs-mobile-compatibility-dispatcher" }).request(async ({ request }) => {
|
|
3916
4766
|
if (getCurrentAbsoluteMobileProducerContext())
|
|
3917
4767
|
return;
|
|
4768
|
+
const preflight = mobilePreflightResponse(request);
|
|
4769
|
+
if (preflight)
|
|
4770
|
+
return preflight;
|
|
3918
4771
|
const parsed = parseAbsoluteMobilePageRequest(request);
|
|
3919
4772
|
if (parsed.kind !== "mobile")
|
|
3920
4773
|
return;
|
|
@@ -3938,23 +4791,28 @@ var createAbsoluteMobileCompatibilityDispatcher = (options) => {
|
|
|
3938
4791
|
console.error(`[Mobile] Failed to load retained producer ${resolved.artifact.releaseId}:`, error);
|
|
3939
4792
|
return createAbsoluteMobilePageErrorResponse(parsed.client.pageId);
|
|
3940
4793
|
}
|
|
4794
|
+
}).afterHandle("global", ({ request, responseValue }) => {
|
|
4795
|
+
const origin = mobileWebViewOrigin(request);
|
|
4796
|
+
if (!origin || !(responseValue instanceof Response))
|
|
4797
|
+
return;
|
|
4798
|
+
applyMobileCorsHeaders(responseValue, origin);
|
|
3941
4799
|
}).as("global");
|
|
3942
4800
|
};
|
|
3943
4801
|
// src/mobile/nativeDeepLinks.ts
|
|
3944
|
-
import { readFile as
|
|
3945
|
-
import { join as
|
|
4802
|
+
import { readFile as readFile14, rename as rename11, writeFile as writeFile12 } from "fs/promises";
|
|
4803
|
+
import { join as join13 } from "path";
|
|
3946
4804
|
var START_MARKER = "<!-- absolutejs:deep-links:start -->";
|
|
3947
4805
|
var END_MARKER = "<!-- absolutejs:deep-links:end -->";
|
|
3948
4806
|
var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
|
|
3949
4807
|
var NOT_FOUND = -1;
|
|
3950
4808
|
var escapeXml = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
3951
4809
|
var writeChangedFile = async (path, source) => {
|
|
3952
|
-
const current = await
|
|
4810
|
+
const current = await readFile14(path, "utf8");
|
|
3953
4811
|
if (current === source)
|
|
3954
4812
|
return false;
|
|
3955
4813
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
3956
|
-
await
|
|
3957
|
-
await
|
|
4814
|
+
await writeFile12(temporary, source, { flag: "wx" });
|
|
4815
|
+
await rename11(temporary, path);
|
|
3958
4816
|
return true;
|
|
3959
4817
|
};
|
|
3960
4818
|
var replaceManagedRegion = (source, region, insertAt) => {
|
|
@@ -3999,8 +4857,8 @@ ${hosts}
|
|
|
3999
4857
|
`;
|
|
4000
4858
|
};
|
|
4001
4859
|
var configureAndroid = async (config) => {
|
|
4002
|
-
const path =
|
|
4003
|
-
const source = await
|
|
4860
|
+
const path = join13(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
|
|
4861
|
+
const source = await readFile14(path, "utf8");
|
|
4004
4862
|
const mainActivity = source.indexOf('android:name=".MainActivity"');
|
|
4005
4863
|
if (mainActivity === NOT_FOUND) {
|
|
4006
4864
|
throw new TypeError("Android MainActivity was not found.");
|
|
@@ -4025,8 +4883,8 @@ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
|
|
|
4025
4883
|
${END_MARKER}
|
|
4026
4884
|
`;
|
|
4027
4885
|
var configureIosInfo = async (config) => {
|
|
4028
|
-
const path =
|
|
4029
|
-
const source = await
|
|
4886
|
+
const path = join13(config.nativeProjectDirectory, "ios/App/App/Info.plist");
|
|
4887
|
+
const source = await readFile14(path, "utf8");
|
|
4030
4888
|
const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
|
|
4031
4889
|
${END_MARKER}
|
|
4032
4890
|
`;
|
|
@@ -4049,10 +4907,10 @@ ${domains}
|
|
|
4049
4907
|
`;
|
|
4050
4908
|
};
|
|
4051
4909
|
var configureIosEntitlements = async (config) => {
|
|
4052
|
-
const path =
|
|
4910
|
+
const path = join13(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
|
|
4053
4911
|
let current = "";
|
|
4054
4912
|
try {
|
|
4055
|
-
current = await
|
|
4913
|
+
current = await readFile14(path, "utf8");
|
|
4056
4914
|
} catch (error) {
|
|
4057
4915
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
4058
4916
|
throw error;
|
|
@@ -4062,13 +4920,13 @@ var configureIosEntitlements = async (config) => {
|
|
|
4062
4920
|
if (current === source)
|
|
4063
4921
|
return false;
|
|
4064
4922
|
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
4065
|
-
await
|
|
4066
|
-
await
|
|
4923
|
+
await writeFile12(temporary, source, { flag: "wx" });
|
|
4924
|
+
await rename11(temporary, path);
|
|
4067
4925
|
return true;
|
|
4068
4926
|
};
|
|
4069
4927
|
var configureIosProject = async (config) => {
|
|
4070
|
-
const path =
|
|
4071
|
-
const source = await
|
|
4928
|
+
const path = join13(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
|
|
4929
|
+
const source = await readFile14(path, "utf8");
|
|
4072
4930
|
const declarations = [
|
|
4073
4931
|
...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
|
|
4074
4932
|
].map((match) => match[1]);
|
|
@@ -4107,7 +4965,7 @@ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms)
|
|
|
4107
4965
|
};
|
|
4108
4966
|
// src/mobile/releasePublisher.ts
|
|
4109
4967
|
import { access as access9 } from "fs/promises";
|
|
4110
|
-
import { isAbsolute as
|
|
4968
|
+
import { isAbsolute as isAbsolute6, relative as relative9, resolve as resolve11, sep as sep6 } from "path";
|
|
4111
4969
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
4112
4970
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
4113
4971
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -4135,8 +4993,8 @@ var isPublisher = (value) => isRecord8(value) && typeof value.publish === "funct
|
|
|
4135
4993
|
var publisherModulePath = (projectRoot, requested) => {
|
|
4136
4994
|
const root = resolve11(projectRoot);
|
|
4137
4995
|
const path = resolve11(root, requested);
|
|
4138
|
-
const projectRelative =
|
|
4139
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
4996
|
+
const projectRelative = relative9(root, path);
|
|
4997
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep6}`) || isAbsolute6(projectRelative)) {
|
|
4140
4998
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
4141
4999
|
}
|
|
4142
5000
|
return path;
|
|
@@ -4204,14 +5062,59 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
4204
5062
|
return publication;
|
|
4205
5063
|
};
|
|
4206
5064
|
// src/mobile/routeMetadataTransform.ts
|
|
4207
|
-
import { existsSync as existsSync3, readFileSync as
|
|
4208
|
-
import { dirname as
|
|
5065
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
5066
|
+
import { dirname as dirname9, extname as extname3, relative as relative10, resolve as resolve12 } from "path";
|
|
4209
5067
|
import ts from "typescript";
|
|
4210
5068
|
var ROUTE_METHODS = new Set(["get", "head"]);
|
|
4211
5069
|
var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
|
|
4212
|
-
var
|
|
5070
|
+
var PAGE_HANDLERS = new Map([
|
|
5071
|
+
[
|
|
5072
|
+
"handleHTMLPageRequest",
|
|
5073
|
+
{ framework: "html", inputKind: "static", propsProperty: "props" }
|
|
5074
|
+
],
|
|
5075
|
+
[
|
|
5076
|
+
"handleHTMXPageRequest",
|
|
5077
|
+
{ framework: "htmx", inputKind: "static", propsProperty: "props" }
|
|
5078
|
+
],
|
|
5079
|
+
[
|
|
5080
|
+
"handleAngularPageRequest",
|
|
5081
|
+
{
|
|
5082
|
+
bundleProperty: "indexPath",
|
|
5083
|
+
framework: "angular",
|
|
5084
|
+
propsProperty: "requestContext",
|
|
5085
|
+
sourceProperty: "pagePath"
|
|
5086
|
+
}
|
|
5087
|
+
],
|
|
5088
|
+
[
|
|
5089
|
+
"handleReactPageRequest",
|
|
5090
|
+
{
|
|
5091
|
+
bundleProperty: "index",
|
|
5092
|
+
framework: "react",
|
|
5093
|
+
pageProperty: "Page",
|
|
5094
|
+
propsProperty: "props"
|
|
5095
|
+
}
|
|
5096
|
+
],
|
|
5097
|
+
[
|
|
5098
|
+
"handleSveltePageRequest",
|
|
5099
|
+
{
|
|
5100
|
+
bundleProperty: "indexPath",
|
|
5101
|
+
framework: "svelte",
|
|
5102
|
+
propsProperty: "props",
|
|
5103
|
+
sourceProperty: "pagePath"
|
|
5104
|
+
}
|
|
5105
|
+
],
|
|
5106
|
+
[
|
|
5107
|
+
"handleVuePageRequest",
|
|
5108
|
+
{
|
|
5109
|
+
bundleProperty: "indexPath",
|
|
5110
|
+
framework: "vue",
|
|
5111
|
+
propsProperty: "props",
|
|
5112
|
+
sourceProperty: "pagePath"
|
|
5113
|
+
}
|
|
5114
|
+
]
|
|
5115
|
+
]);
|
|
4213
5116
|
var posixPath = (value) => value.replace(/\\/g, "/");
|
|
4214
|
-
var findTsconfig = (entry, projectRoot) => ts.findConfigFile(
|
|
5117
|
+
var findTsconfig = (entry, projectRoot) => ts.findConfigFile(dirname9(entry), existsSync3, "tsconfig.json") ?? ts.findConfigFile(projectRoot, existsSync3, "tsconfig.json");
|
|
4215
5118
|
var createProgram = (entry, projectRoot) => {
|
|
4216
5119
|
const configPath = findTsconfig(entry, projectRoot);
|
|
4217
5120
|
if (!configPath) {
|
|
@@ -4223,7 +5126,7 @@ var createProgram = (entry, projectRoot) => {
|
|
|
4223
5126
|
target: ts.ScriptTarget.ESNext
|
|
4224
5127
|
});
|
|
4225
5128
|
}
|
|
4226
|
-
const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) =>
|
|
5129
|
+
const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync3(path, "utf8")).config, ts.sys, dirname9(configPath));
|
|
4227
5130
|
if (!parsed.fileNames.includes(entry))
|
|
4228
5131
|
parsed.fileNames.push(entry);
|
|
4229
5132
|
return ts.createProgram(parsed.fileNames, parsed.options);
|
|
@@ -4329,7 +5232,7 @@ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
|
|
|
4329
5232
|
const declaration = symbol?.declarations?.[0];
|
|
4330
5233
|
const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
|
|
4331
5234
|
const exportedName = symbol?.name ?? expression.getText(sourceFile);
|
|
4332
|
-
const source = posixPath(
|
|
5235
|
+
const source = posixPath(relative10(projectRoot, file));
|
|
4333
5236
|
return `${source}#${exportedName}`;
|
|
4334
5237
|
};
|
|
4335
5238
|
var resolveAlias = (symbol, checker) => {
|
|
@@ -4361,13 +5264,108 @@ var assetKey = (expression, checker, seen = new Set) => {
|
|
|
4361
5264
|
const [, key] = expression.arguments;
|
|
4362
5265
|
return key && ts.isStringLiteralLike(key) ? key.text : undefined;
|
|
4363
5266
|
};
|
|
5267
|
+
var staticString = (expression, bindings) => {
|
|
5268
|
+
if (ts.isStringLiteralLike(expression))
|
|
5269
|
+
return expression.text;
|
|
5270
|
+
if (ts.isIdentifier(expression))
|
|
5271
|
+
return bindings.get(expression.text);
|
|
5272
|
+
if (ts.isNoSubstitutionTemplateLiteral(expression))
|
|
5273
|
+
return expression.text;
|
|
5274
|
+
if (!ts.isTemplateExpression(expression))
|
|
5275
|
+
return;
|
|
5276
|
+
let value = expression.head.text;
|
|
5277
|
+
for (const span of expression.templateSpans) {
|
|
5278
|
+
const substitution = staticString(span.expression, bindings);
|
|
5279
|
+
if (substitution === undefined)
|
|
5280
|
+
return;
|
|
5281
|
+
value += substitution + span.literal.text;
|
|
5282
|
+
}
|
|
5283
|
+
return value;
|
|
5284
|
+
};
|
|
5285
|
+
var assetKeyWithBindings = (expression, checker, bindings = new Map) => {
|
|
5286
|
+
if (!expression)
|
|
5287
|
+
return;
|
|
5288
|
+
if (ts.isCallExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "asset") {
|
|
5289
|
+
const [, key] = expression.arguments;
|
|
5290
|
+
return key ? staticString(key, bindings) : undefined;
|
|
5291
|
+
}
|
|
5292
|
+
return assetKey(expression, checker);
|
|
5293
|
+
};
|
|
5294
|
+
var callableObject = (call, checker) => {
|
|
5295
|
+
const symbol = checker.getSymbolAtLocation(call.expression);
|
|
5296
|
+
const resolved = symbol ? resolveAlias(symbol, checker) : undefined;
|
|
5297
|
+
const declaration = resolved?.valueDeclaration ?? resolved?.declarations?.[0];
|
|
5298
|
+
let callable;
|
|
5299
|
+
if (declaration && ts.isFunctionDeclaration(declaration)) {
|
|
5300
|
+
callable = declaration;
|
|
5301
|
+
} else if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer && (ts.isArrowFunction(declaration.initializer) || ts.isFunctionExpression(declaration.initializer))) {
|
|
5302
|
+
callable = declaration.initializer;
|
|
5303
|
+
}
|
|
5304
|
+
if (!callable)
|
|
5305
|
+
return;
|
|
5306
|
+
const bindings = new Map;
|
|
5307
|
+
callable.parameters.forEach((parameter, index) => {
|
|
5308
|
+
if (!ts.isIdentifier(parameter.name))
|
|
5309
|
+
return;
|
|
5310
|
+
const argument = call.arguments[index];
|
|
5311
|
+
if (!argument)
|
|
5312
|
+
return;
|
|
5313
|
+
const value = staticString(argument, new Map);
|
|
5314
|
+
if (value !== undefined)
|
|
5315
|
+
bindings.set(parameter.name.text, value);
|
|
5316
|
+
});
|
|
5317
|
+
const { body } = callable;
|
|
5318
|
+
if (!body)
|
|
5319
|
+
return;
|
|
5320
|
+
const expressionBody = ts.isParenthesizedExpression(body) ? body.expression : body;
|
|
5321
|
+
if (ts.isObjectLiteralExpression(expressionBody)) {
|
|
5322
|
+
return { bindings, object: expressionBody };
|
|
5323
|
+
}
|
|
5324
|
+
if (ts.isBlock(body)) {
|
|
5325
|
+
const returned = body.statements.find(ts.isReturnStatement)?.expression;
|
|
5326
|
+
if (returned && ts.isObjectLiteralExpression(returned)) {
|
|
5327
|
+
return { bindings, object: returned };
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
5330
|
+
return;
|
|
5331
|
+
};
|
|
5332
|
+
var spreadObject = (expression, checker, bindings) => {
|
|
5333
|
+
if (ts.isObjectLiteralExpression(expression)) {
|
|
5334
|
+
return { bindings, object: expression };
|
|
5335
|
+
}
|
|
5336
|
+
if (!ts.isCallExpression(expression))
|
|
5337
|
+
return;
|
|
5338
|
+
return callableObject(expression, checker);
|
|
5339
|
+
};
|
|
5340
|
+
var objectAssetKey = (object, name, checker, bindings = new Map) => {
|
|
5341
|
+
for (const property of [...object.properties].reverse()) {
|
|
5342
|
+
if (propertyName(property) === name && ts.isShorthandPropertyAssignment(property)) {
|
|
5343
|
+
return assetKeyWithBindings(property.name, checker, bindings);
|
|
5344
|
+
}
|
|
5345
|
+
if (propertyName(property) === name && ts.isPropertyAssignment(property)) {
|
|
5346
|
+
return assetKeyWithBindings(property.initializer, checker, bindings);
|
|
5347
|
+
}
|
|
5348
|
+
if (!ts.isSpreadAssignment(property))
|
|
5349
|
+
continue;
|
|
5350
|
+
const nestedObject = spreadObject(property.expression, checker, bindings);
|
|
5351
|
+
if (!nestedObject)
|
|
5352
|
+
continue;
|
|
5353
|
+
const nested = objectAssetKey(nestedObject.object, name, checker, nestedObject.bindings);
|
|
5354
|
+
if (nested)
|
|
5355
|
+
return nested;
|
|
5356
|
+
}
|
|
5357
|
+
return;
|
|
5358
|
+
};
|
|
4364
5359
|
var findPageCall = (nodes) => {
|
|
4365
5360
|
let found;
|
|
4366
5361
|
const visit = (candidate) => {
|
|
4367
5362
|
if (found)
|
|
4368
5363
|
return;
|
|
4369
|
-
if (ts.isCallExpression(candidate) && ts.isIdentifier(candidate.expression) && candidate.expression.text
|
|
4370
|
-
|
|
5364
|
+
if (ts.isCallExpression(candidate) && ts.isIdentifier(candidate.expression) && PAGE_HANDLERS.has(candidate.expression.text)) {
|
|
5365
|
+
const definition = PAGE_HANDLERS.get(candidate.expression.text);
|
|
5366
|
+
if (!definition)
|
|
5367
|
+
return;
|
|
5368
|
+
found = { definition, node: candidate };
|
|
4371
5369
|
return;
|
|
4372
5370
|
}
|
|
4373
5371
|
ts.forEachChild(candidate, visit);
|
|
@@ -4386,30 +5384,67 @@ var analyzeRouteCall = (node, sourceFile, checker, projectRoot) => {
|
|
|
4386
5384
|
const [routePath] = node.arguments;
|
|
4387
5385
|
if (!routePath || !ts.isStringLiteralLike(routePath))
|
|
4388
5386
|
return;
|
|
4389
|
-
const
|
|
5387
|
+
const foundPageCall = findPageCall(node.arguments.slice(1));
|
|
5388
|
+
const pageCall = foundPageCall?.node;
|
|
5389
|
+
const definition = foundPageCall?.definition;
|
|
4390
5390
|
const [input] = pageCall?.arguments ?? [];
|
|
4391
|
-
if (!pageCall || !input
|
|
5391
|
+
if (!pageCall || !input) {
|
|
4392
5392
|
return;
|
|
4393
5393
|
}
|
|
4394
|
-
|
|
4395
|
-
if (!page)
|
|
5394
|
+
if (!definition)
|
|
4396
5395
|
return;
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
5396
|
+
if (definition.inputKind === "static") {
|
|
5397
|
+
const bundleKey2 = assetKey(input, checker);
|
|
5398
|
+
if (!bundleKey2)
|
|
5399
|
+
return;
|
|
5400
|
+
const pageId2 = `${definition.framework}:${bundleKey2}`;
|
|
5401
|
+
const propsSchemaHash2 = hashAbsoluteMobilePropsSchema({
|
|
5402
|
+
properties: {},
|
|
5403
|
+
type: "object"
|
|
5404
|
+
});
|
|
5405
|
+
return {
|
|
5406
|
+
inputKind: "static",
|
|
5407
|
+
metadata: {
|
|
5408
|
+
bundleKey: bundleKey2,
|
|
5409
|
+
contract: `${definition.framework}:${pageId2}:${propsSchemaHash2}`,
|
|
5410
|
+
framework: definition.framework,
|
|
5411
|
+
pageId: pageId2,
|
|
5412
|
+
propsSchemaHash: propsSchemaHash2
|
|
5413
|
+
},
|
|
5414
|
+
pageCallStart: pageCall.getStart(sourceFile),
|
|
5415
|
+
routeCallSpan: `${node.getStart(sourceFile)}:${node.end}`
|
|
5416
|
+
};
|
|
5417
|
+
}
|
|
5418
|
+
if (!ts.isObjectLiteralExpression(input) || !definition.bundleProperty) {
|
|
5419
|
+
return;
|
|
5420
|
+
}
|
|
5421
|
+
const page = definition.pageProperty ? objectPropertyExpression(input, definition.pageProperty) : undefined;
|
|
5422
|
+
const source = definition.sourceProperty ? objectAssetKey(input, definition.sourceProperty, checker) : undefined;
|
|
5423
|
+
if (definition.pageProperty && !page)
|
|
5424
|
+
return;
|
|
5425
|
+
if (definition.sourceProperty && !source)
|
|
5426
|
+
return;
|
|
5427
|
+
const props = objectPropertyExpression(input, definition.propsProperty);
|
|
5428
|
+
const bundleKey = objectAssetKey(input, definition.bundleProperty, checker);
|
|
4400
5429
|
if (!bundleKey)
|
|
4401
5430
|
return;
|
|
4402
|
-
const pageId = resolvePageIdentity(page, sourceFile, checker, projectRoot)
|
|
4403
|
-
|
|
5431
|
+
const pageId = page ? resolvePageIdentity(page, sourceFile, checker, projectRoot) : `${definition.framework}:${source}`;
|
|
5432
|
+
let propsType;
|
|
5433
|
+
if (page)
|
|
5434
|
+
propsType = pagePropsType(page, props, checker);
|
|
5435
|
+
else if (props)
|
|
5436
|
+
propsType = checker.getTypeAtLocation(props);
|
|
5437
|
+
const schema = propsType ? serializeType(propsType, checker) : { properties: {}, type: "object" };
|
|
4404
5438
|
const propsSchemaHash = hashAbsoluteMobilePropsSchema(schema);
|
|
4405
5439
|
const metadata = {
|
|
4406
5440
|
bundleKey,
|
|
4407
|
-
contract:
|
|
4408
|
-
framework:
|
|
5441
|
+
contract: `${definition.framework}:${pageId}:${propsSchemaHash}`,
|
|
5442
|
+
framework: definition.framework,
|
|
4409
5443
|
pageId,
|
|
4410
5444
|
propsSchemaHash
|
|
4411
5445
|
};
|
|
4412
5446
|
const result = {
|
|
5447
|
+
inputKind: "object",
|
|
4413
5448
|
metadata,
|
|
4414
5449
|
pageCallStart: pageCall.getStart(sourceFile),
|
|
4415
5450
|
routeCallSpan: `${node.getStart(sourceFile)}:${node.end}`
|
|
@@ -4466,6 +5501,16 @@ var routeOptions = (existing, metadata) => {
|
|
|
4466
5501
|
var transformPageCall = (node, page) => {
|
|
4467
5502
|
if (!page)
|
|
4468
5503
|
return;
|
|
5504
|
+
if (page.inputKind === "static") {
|
|
5505
|
+
const [pagePath, existingOptions, ...rest] = node.arguments;
|
|
5506
|
+
if (!pagePath)
|
|
5507
|
+
return;
|
|
5508
|
+
const options = ts.factory.createObjectLiteralExpression([
|
|
5509
|
+
...existingOptions ? [ts.factory.createSpreadAssignment(existingOptions)] : [],
|
|
5510
|
+
ts.factory.createPropertyAssignment("__absoluteMobile", metadataExpression(page.metadata))
|
|
5511
|
+
]);
|
|
5512
|
+
return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [pagePath, options, ...rest]);
|
|
5513
|
+
}
|
|
4469
5514
|
const [input] = node.arguments;
|
|
4470
5515
|
if (!input || !ts.isObjectLiteralExpression(input))
|
|
4471
5516
|
return;
|
|
@@ -4532,7 +5577,7 @@ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
|
|
|
4532
5577
|
const source = await Bun.file(path).text();
|
|
4533
5578
|
return {
|
|
4534
5579
|
contents: transformFile(source, path, analysis),
|
|
4535
|
-
loader:
|
|
5580
|
+
loader: extname3(path).endsWith("x") ? "tsx" : "ts"
|
|
4536
5581
|
};
|
|
4537
5582
|
});
|
|
4538
5583
|
}
|
|
@@ -4543,7 +5588,7 @@ var inspectAbsoluteMobileRouteMetadata = (options) => {
|
|
|
4543
5588
|
const entry = resolve12(options.entry);
|
|
4544
5589
|
const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
|
|
4545
5590
|
return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
|
|
4546
|
-
file: posixPath(
|
|
5591
|
+
file: posixPath(relative10(projectRoot, file)),
|
|
4547
5592
|
metadata
|
|
4548
5593
|
})));
|
|
4549
5594
|
};
|
|
@@ -4552,17 +5597,27 @@ export {
|
|
|
4552
5597
|
waitForAbsoluteIosHmrLog,
|
|
4553
5598
|
verifyAbsoluteMobileCompatibilityProducer,
|
|
4554
5599
|
verifyAbsoluteMobileAssociationFiles,
|
|
5600
|
+
validateAbsoluteSshDestination,
|
|
5601
|
+
validateAbsoluteRemoteMacProfileName,
|
|
5602
|
+
syncAbsoluteRemoteMacProject,
|
|
5603
|
+
startAbsoluteRemoteIosDevSession,
|
|
4555
5604
|
startAbsoluteIosDevSession,
|
|
5605
|
+
serializeAbsoluteMobileAuthEnvironment,
|
|
4556
5606
|
runWithAbsoluteMobileProducer,
|
|
4557
5607
|
retainAbsoluteMobileCompatibilityArtifacts,
|
|
4558
5608
|
resolveAbsoluteMobileRoute,
|
|
5609
|
+
resolveAbsoluteMobileNavigation,
|
|
4559
5610
|
resolveAbsoluteMobileDeepLink,
|
|
4560
5611
|
resolveAbsoluteMobileCompatibilityRelease,
|
|
5612
|
+
resolveAbsoluteMobileAuthManifest,
|
|
4561
5613
|
repairAbsoluteIosDevSession,
|
|
5614
|
+
removeAbsoluteRemoteMacProfile,
|
|
4562
5615
|
redactAbsoluteIosLog,
|
|
4563
5616
|
readAbsoluteMobileMaterializedReleases,
|
|
4564
5617
|
publishAbsoluteIosRelease,
|
|
4565
5618
|
publishAbsoluteAndroidRelease,
|
|
5619
|
+
projectUsesAbsoluteSync,
|
|
5620
|
+
projectUsesAbsoluteAuth,
|
|
4566
5621
|
prepareAbsoluteIosRelease,
|
|
4567
5622
|
prepareAbsoluteIosDevProject,
|
|
4568
5623
|
prepareAbsoluteAndroidRelease,
|
|
@@ -4575,23 +5630,32 @@ export {
|
|
|
4575
5630
|
parseAbsoluteMobileBuildPageMetadata,
|
|
4576
5631
|
parseAbsoluteIosLogLine,
|
|
4577
5632
|
parseAbsoluteIosHmrLog,
|
|
5633
|
+
pairAbsoluteRemoteMac,
|
|
4578
5634
|
normalizeAbsoluteMobileConfig,
|
|
4579
5635
|
navigateAbsoluteMobilePage,
|
|
5636
|
+
materializeAbsoluteRemoteMacAgent,
|
|
4580
5637
|
materializeAbsoluteMobileCompatibilityBundle,
|
|
4581
5638
|
materializeAbsoluteMobileAssociationFiles,
|
|
4582
5639
|
materializeAbsoluteCapacitorWebBundle,
|
|
4583
5640
|
matchesAbsoluteMobileRoutePattern,
|
|
4584
5641
|
loadAbsoluteNativeReleasePublisher,
|
|
4585
5642
|
loadAbsoluteMobileMaterializedBundle,
|
|
5643
|
+
listAbsoluteRemoteMacProfiles,
|
|
4586
5644
|
isAbsoluteIosNativeRootInput,
|
|
5645
|
+
installAbsoluteRemoteMacAgent,
|
|
5646
|
+
installAbsoluteMobileAuthEnvironment,
|
|
5647
|
+
inspectAbsoluteRemoteMac,
|
|
4587
5648
|
inspectAbsoluteMobileRouteMetadata,
|
|
4588
5649
|
hashAbsoluteMobilePropsSchema,
|
|
4589
5650
|
getCurrentAbsoluteMobileProducerContext,
|
|
5651
|
+
getAbsoluteRemoteMacProfile,
|
|
4590
5652
|
fingerprintAbsoluteIosNativeProject,
|
|
4591
5653
|
fingerprintAbsoluteIosDevProject,
|
|
4592
5654
|
finalizeAbsoluteMobilePage,
|
|
4593
5655
|
finalizeAbsoluteMobileCompatibilityBuild,
|
|
4594
5656
|
fetchAbsoluteMobilePage,
|
|
5657
|
+
disposeAbsoluteMobilePage,
|
|
5658
|
+
createAbsoluteRemoteIosDevProject,
|
|
4595
5659
|
createAbsoluteMobileUpgradeResponse,
|
|
4596
5660
|
createAbsoluteMobileRouteMetadataPlugin,
|
|
4597
5661
|
createAbsoluteMobilePageRequest,
|
|
@@ -4601,6 +5665,7 @@ export {
|
|
|
4601
5665
|
createAbsoluteMobileCompatibilityDispatcher,
|
|
4602
5666
|
createAbsoluteMobileCompatibilityArtifact,
|
|
4603
5667
|
createAbsoluteMobileBlobArtifactStore,
|
|
5668
|
+
createAbsoluteMobileAuthManifest,
|
|
4604
5669
|
createAbsoluteMobileAssociationPlugin,
|
|
4605
5670
|
createAbsoluteMobileAssociationDocuments,
|
|
4606
5671
|
createAbsoluteIosNativeWatcher,
|
|
@@ -4612,10 +5677,17 @@ export {
|
|
|
4612
5677
|
applyAbsoluteNativeDeepLinks,
|
|
4613
5678
|
activateAbsoluteMobilePage,
|
|
4614
5679
|
acceptsAbsoluteMobilePage,
|
|
5680
|
+
absoluteRemoteProjectSyncCommands,
|
|
5681
|
+
absoluteRemoteMacSshBase,
|
|
4615
5682
|
MOBILE_PAGE_REQUEST_HEADERS,
|
|
4616
5683
|
AbsoluteMobilePageProtocolError,
|
|
4617
5684
|
APPLE_ASSOCIATION_PATH,
|
|
4618
5685
|
ANDROID_ASSOCIATION_PATH,
|
|
5686
|
+
ABSOLUTE_SYNC_PACKAGE,
|
|
5687
|
+
ABSOLUTE_REMOTE_MAC_PROTOCOL_VERSION,
|
|
5688
|
+
ABSOLUTE_REMOTE_MAC_EVENT_PREFIX,
|
|
5689
|
+
ABSOLUTE_NATIVE_AUTH_SCOPES,
|
|
5690
|
+
ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV,
|
|
4619
5691
|
ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL,
|
|
4620
5692
|
ABSOLUTE_MOBILE_ROUTE_DETAIL,
|
|
4621
5693
|
ABSOLUTE_MOBILE_RETAINED_GENERATIONS,
|
|
@@ -4626,8 +5698,9 @@ export {
|
|
|
4626
5698
|
ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
|
|
4627
5699
|
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
4628
5700
|
ABSOLUTE_IOS_RELEASE_FORMAT,
|
|
5701
|
+
ABSOLUTE_AUTH_PACKAGE,
|
|
4629
5702
|
ABSOLUTE_ANDROID_RELEASE_FORMAT
|
|
4630
5703
|
};
|
|
4631
5704
|
|
|
4632
|
-
//# debugId=
|
|
5705
|
+
//# debugId=7AFC88B0A927AFE564756E2164756E21
|
|
4633
5706
|
//# sourceMappingURL=index.js.map
|