bleam 0.0.9 → 0.0.10
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/cli.cjs +246 -18
- package/dist/cli.d.cts +30 -2
- package/dist/cli.d.ts +30 -2
- package/dist/cli.js +247 -19
- package/dist/platform.d.cts +2 -0
- package/dist/platform.d.ts +2 -0
- package/dist/{ui-1WepaMS4.d.cts → ui-Dd7SXdbg.d.cts} +7 -7
- package/dist/ui.d.cts +1 -1
- package/dist/window.d.cts +1 -1
- package/package.json +2 -1
- package/templates/native/ios/Bleam/AppDelegate.swift +129 -20
- package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +19 -18
- package/templates/native/ios/PlatformHelper/main.swift +457 -66
- package/templates/native/modules/bleam-runtime/ios/PlatformModule.swift +21 -5
- package/templates/updates/README.md +272 -0
- package/templates/updates/src/index.ts +385 -0
- package/templates/updates/src/schema.ts +385 -0
- package/templates/updates/tsconfig.json +12 -0
- package/templates/updates/wrangler.jsonc +19 -0
- package/templates/native/ios/PlatformHelper/Info.plist +0 -29
package/dist/cli.cjs
CHANGED
|
@@ -24,6 +24,7 @@ let yoctocolors = require("yoctocolors");
|
|
|
24
24
|
yoctocolors = require_chunk.__toESM(yoctocolors);
|
|
25
25
|
|
|
26
26
|
//#region src/cli.ts
|
|
27
|
+
const localUpdateServiceUrl = "http://127.0.0.1:8787";
|
|
27
28
|
const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
28
29
|
const packageRoot = node_path.default.dirname((0, node_url.fileURLToPath)(new URL("../package.json", require("url").pathToFileURL(__filename).href)));
|
|
29
30
|
const packageJson = require$1("../package.json");
|
|
@@ -96,7 +97,7 @@ Commands:
|
|
|
96
97
|
Build and install a standalone macOS app
|
|
97
98
|
publish ota [dir] [--service-url <url>]
|
|
98
99
|
Publish an OTA update to the local update service
|
|
99
|
-
publish platform [dir] [--service-url <url>]
|
|
100
|
+
publish platform [dir] [--service-url <url>] [--redeploy]
|
|
100
101
|
Build and publish a platform update locally
|
|
101
102
|
doctor [dir] Check the local Bleam environment
|
|
102
103
|
typecheck [dir] Typecheck project files
|
|
@@ -108,7 +109,7 @@ function parsePublishOtaOptions(argv) {
|
|
|
108
109
|
const root = invocationRoot();
|
|
109
110
|
let projectRoot = root;
|
|
110
111
|
let hasProjectRootArg = false;
|
|
111
|
-
let serviceUrl = process.env.BLEAM_UPDATE_SERVICE_URL ??
|
|
112
|
+
let serviceUrl = process.env.BLEAM_UPDATE_SERVICE_URL ?? localUpdateServiceUrl;
|
|
112
113
|
for (let index = 0; index < argv.length; index += 1) {
|
|
113
114
|
const arg = argv[index];
|
|
114
115
|
if (arg === "--service-url") {
|
|
@@ -132,6 +133,13 @@ function parsePublishOtaOptions(argv) {
|
|
|
132
133
|
serviceUrl: url.origin
|
|
133
134
|
};
|
|
134
135
|
}
|
|
136
|
+
function parsePublishPlatformOptions(argv) {
|
|
137
|
+
const redeploy = argv.includes("--redeploy");
|
|
138
|
+
return {
|
|
139
|
+
...parsePublishOtaOptions(argv.filter((argument) => argument !== "--redeploy")),
|
|
140
|
+
redeploy
|
|
141
|
+
};
|
|
142
|
+
}
|
|
135
143
|
function invocationRoot() {
|
|
136
144
|
const pwd = process.env.PWD;
|
|
137
145
|
return pwd && node_path.default.isAbsolute(pwd) ? pwd : process.cwd();
|
|
@@ -479,7 +487,8 @@ function readProjectConfig(options) {
|
|
|
479
487
|
teamId: config.teamId,
|
|
480
488
|
appearance: config.appearance ?? normalizeAppearance(void 0),
|
|
481
489
|
runtimeVersion,
|
|
482
|
-
sdkVersion: runtimeVersion
|
|
490
|
+
sdkVersion: runtimeVersion,
|
|
491
|
+
iconPath: (0, node_fs.existsSync)(node_path.default.join(options.projectRoot, "icon.png")) ? node_path.default.join(options.projectRoot, "icon.png") : void 0
|
|
483
492
|
};
|
|
484
493
|
}
|
|
485
494
|
function slugFromName(name) {
|
|
@@ -639,6 +648,18 @@ function nativeRuntimeTemplateRoot(root = packageRoot) {
|
|
|
639
648
|
const bundledTemplate = node_path.default.join(root, "templates", "native");
|
|
640
649
|
return (0, node_fs.existsSync)(bundledTemplate) ? bundledTemplate : node_path.default.resolve(root, "..", "..", "app", "bleam");
|
|
641
650
|
}
|
|
651
|
+
function localUpdateServiceRoot(root = packageRoot) {
|
|
652
|
+
const bundledService = node_path.default.join(root, "templates", "updates");
|
|
653
|
+
return (0, node_fs.existsSync)(bundledService) ? bundledService : node_path.default.resolve(root, "..", "..", "services", "updates");
|
|
654
|
+
}
|
|
655
|
+
function localUpdateServicePaths(home = node_os.default.homedir()) {
|
|
656
|
+
const root = node_path.default.join(home, "Library", "Caches", "bleam", "updates");
|
|
657
|
+
return {
|
|
658
|
+
root,
|
|
659
|
+
state: node_path.default.join(root, "state"),
|
|
660
|
+
log: node_path.default.join(root, "worker.log")
|
|
661
|
+
};
|
|
662
|
+
}
|
|
642
663
|
function safeArtifactName(name) {
|
|
643
664
|
return name.trim().replace(/[/:\\]/g, "-").replace(/\s+/g, " ") || "App";
|
|
644
665
|
}
|
|
@@ -673,11 +694,13 @@ function replaceOrInsertPlistStringArray(plist, key, values) {
|
|
|
673
694
|
return `${plist.slice(0, closingDictionary)}\t<key>${key}</key>\n\t<array>\n${plistStringArray(values)}\n\t</array>\n${plist.slice(closingDictionary)}`;
|
|
674
695
|
}
|
|
675
696
|
function writeGeneratedAppJson(nativeRoot, config) {
|
|
697
|
+
const icon = config.iconPath ? "./icon.png" : void 0;
|
|
676
698
|
(0, node_fs.writeFileSync)(node_path.default.join(nativeRoot, "app.json"), `${JSON.stringify({ expo: {
|
|
677
699
|
name: config.name,
|
|
678
700
|
slug: slugFromName(config.name),
|
|
679
701
|
version: config.version ?? "1.0.0",
|
|
680
702
|
platforms: ["ios"],
|
|
703
|
+
icon,
|
|
681
704
|
ios: {
|
|
682
705
|
appleTeamId: config.teamId,
|
|
683
706
|
buildNumber: config.buildNumber,
|
|
@@ -685,24 +708,65 @@ function writeGeneratedAppJson(nativeRoot, config) {
|
|
|
685
708
|
}
|
|
686
709
|
} }, null, 2)}\n`);
|
|
687
710
|
}
|
|
711
|
+
function prepareNativeIcon(nativeRoot, iconPath) {
|
|
712
|
+
const generatedIcon = node_path.default.join(nativeRoot, "icon.png");
|
|
713
|
+
const appIconRoot = node_path.default.join(nativeRoot, "ios", "Bleam", "Images.xcassets", "AppIcon.appiconset");
|
|
714
|
+
(0, node_fs.rmSync)(generatedIcon, { force: true });
|
|
715
|
+
(0, node_fs.rmSync)(node_path.default.join(appIconRoot, "AppIcon.png"), { force: true });
|
|
716
|
+
if (!iconPath) return;
|
|
717
|
+
(0, node_fs.cpSync)(iconPath, generatedIcon);
|
|
718
|
+
(0, node_fs.cpSync)(iconPath, node_path.default.join(appIconRoot, "AppIcon.png"));
|
|
719
|
+
(0, node_fs.writeFileSync)(node_path.default.join(appIconRoot, "Contents.json"), `${JSON.stringify({
|
|
720
|
+
images: [{
|
|
721
|
+
filename: "AppIcon.png",
|
|
722
|
+
idiom: "universal",
|
|
723
|
+
platform: "ios",
|
|
724
|
+
size: "1024x1024"
|
|
725
|
+
}],
|
|
726
|
+
info: {
|
|
727
|
+
version: 1,
|
|
728
|
+
author: "bleam"
|
|
729
|
+
}
|
|
730
|
+
}, null, 2)}\n`);
|
|
731
|
+
}
|
|
732
|
+
function platformInputFingerprint(config) {
|
|
733
|
+
const iconSha256 = config.iconPath ? sha256(config.iconPath) : null;
|
|
734
|
+
return (0, node_crypto.createHash)("sha256").update(JSON.stringify({
|
|
735
|
+
schemaVersion: 1,
|
|
736
|
+
bleamPlatformVersion: config.runtimeVersion,
|
|
737
|
+
name: config.name,
|
|
738
|
+
appVersion: config.version ?? "1.0.0",
|
|
739
|
+
buildNumber: config.buildNumber,
|
|
740
|
+
bundleIdentifier: config.bundleIdentifier,
|
|
741
|
+
teamIdentifier: config.teamId,
|
|
742
|
+
appGroups: config.appGroups,
|
|
743
|
+
appearance: config.appearance,
|
|
744
|
+
iconSha256,
|
|
745
|
+
updateServiceUrl: process.env.BLEAM_UPDATE_SERVICE_URL ?? localUpdateServiceUrl,
|
|
746
|
+
target: {
|
|
747
|
+
os: "macos",
|
|
748
|
+
architecture: "arm64",
|
|
749
|
+
minimumMacOSVersion: "14.0"
|
|
750
|
+
}
|
|
751
|
+
})).digest("hex");
|
|
752
|
+
}
|
|
688
753
|
function patchNativeProject(nativeRoot, config) {
|
|
689
754
|
const projectPath = node_path.default.join(nativeRoot, "ios", "Bleam.xcodeproj", "project.pbxproj");
|
|
690
755
|
const infoPlistPath = node_path.default.join(nativeRoot, "ios", "Bleam", "Info.plist");
|
|
691
756
|
const generationInfoPlistPath = node_path.default.join(nativeRoot, "ios", "GenerationService", "Info.plist");
|
|
692
|
-
const platformInfoPlistPath = node_path.default.join(nativeRoot, "ios", "PlatformHelper", "Info.plist");
|
|
693
757
|
const appEntitlementsPath = node_path.default.join(nativeRoot, "ios", "Bleam", "bleam.entitlements");
|
|
694
758
|
const generationEntitlementsPath = node_path.default.join(nativeRoot, "ios", "GenerationService", "GenerationService.entitlements");
|
|
695
759
|
const productName = xcodeSettingValue(config.name);
|
|
696
760
|
const bundleIdentifier = xcodeSettingValue(config.bundleIdentifier ?? "");
|
|
697
761
|
const generationBundleIdentifier = xcodeSettingValue(`${config.bundleIdentifier}.GenerationService`);
|
|
698
|
-
const platformBundleIdentifier = xcodeSettingValue(`${config.bundleIdentifier}.PlatformHelper`);
|
|
699
762
|
const teamId = xcodeSettingValue(config.teamId ?? "");
|
|
700
763
|
const version = xcodeSettingValue(config.version ?? "1.0.0");
|
|
701
764
|
const buildNumber = xcodeSettingValue(config.buildNumber);
|
|
702
765
|
const primaryAppGroup = config.appGroups[0];
|
|
766
|
+
const fingerprint = platformInputFingerprint(config);
|
|
703
767
|
let project = (0, node_fs.readFileSync)(projectPath, "utf8");
|
|
704
768
|
project = project.replace(/PRODUCT_BUNDLE_IDENTIFIER = ([^;]+);/g, (_, current) => {
|
|
705
|
-
return `PRODUCT_BUNDLE_IDENTIFIER = ${current.endsWith(".GenerationService") ? generationBundleIdentifier :
|
|
769
|
+
return `PRODUCT_BUNDLE_IDENTIFIER = ${current.endsWith(".GenerationService") ? generationBundleIdentifier : bundleIdentifier};`;
|
|
706
770
|
}).replace(/DEVELOPMENT_TEAM = [^;]+;/g, () => {
|
|
707
771
|
return `DEVELOPMENT_TEAM = ${teamId};`;
|
|
708
772
|
}).replace(/MARKETING_VERSION = [^;]+;/g, () => {
|
|
@@ -717,9 +781,10 @@ function patchNativeProject(nativeRoot, config) {
|
|
|
717
781
|
infoPlist = replacePlistString(infoPlist, "CFBundleDisplayName", config.name);
|
|
718
782
|
infoPlist = replacePlistString(infoPlist, "CFBundleShortVersionString", config.version ?? "1.0.0");
|
|
719
783
|
infoPlist = replaceOrInsertPlistString(infoPlist, "BleamRuntimeVersion", config.runtimeVersion);
|
|
784
|
+
infoPlist = replaceOrInsertPlistString(infoPlist, "BleamPlatformFingerprint", fingerprint);
|
|
720
785
|
infoPlist = replaceOrInsertPlistString(infoPlist, "BleamTeamIdentifier", config.teamId ?? "");
|
|
721
|
-
const updateServiceUrl = process.env.BLEAM_UPDATE_SERVICE_URL;
|
|
722
|
-
|
|
786
|
+
const updateServiceUrl = process.env.BLEAM_UPDATE_SERVICE_URL ?? localUpdateServiceUrl;
|
|
787
|
+
infoPlist = replaceOrInsertPlistString(infoPlist, "BleamUpdateServiceURL", updateServiceUrl);
|
|
723
788
|
infoPlist = replacePlistString(infoPlist, "CFBundleVersion", config.buildNumber);
|
|
724
789
|
if (primaryAppGroup) infoPlist = replaceOrInsertPlistString(infoPlist, "AppGroupIdentifier", primaryAppGroup);
|
|
725
790
|
(0, node_fs.writeFileSync)(infoPlistPath, infoPlist);
|
|
@@ -727,7 +792,6 @@ function patchNativeProject(nativeRoot, config) {
|
|
|
727
792
|
let generationInfoPlist = (0, node_fs.readFileSync)(generationInfoPlistPath, "utf8");
|
|
728
793
|
generationInfoPlist = replaceOrInsertPlistString(generationInfoPlist, "AppGroupIdentifier", primaryAppGroup);
|
|
729
794
|
(0, node_fs.writeFileSync)(generationInfoPlistPath, generationInfoPlist);
|
|
730
|
-
(0, node_fs.writeFileSync)(platformInfoPlistPath, replaceOrInsertPlistString((0, node_fs.readFileSync)(platformInfoPlistPath, "utf8"), "BleamPlatformVersion", config.runtimeVersion));
|
|
731
795
|
for (const entitlementsPath of [appEntitlementsPath, generationEntitlementsPath]) (0, node_fs.writeFileSync)(entitlementsPath, replaceOrInsertPlistStringArray((0, node_fs.readFileSync)(entitlementsPath, "utf8"), "com.apple.security.application-groups", config.appGroups));
|
|
732
796
|
}
|
|
733
797
|
}
|
|
@@ -760,6 +824,7 @@ function createBuildNativeWorkspace(options, config) {
|
|
|
760
824
|
(0, node_fs.writeFileSync)(node_path.default.join(nativeRoot, "index.ts"), `import { registerApp } from 'bleam/app'\nimport App from ${JSON.stringify(node_path.default.join(options.projectRoot, "app"))}\n\nregisterApp(App)\n`);
|
|
761
825
|
(0, node_fs.writeFileSync)(node_path.default.join(nativeRoot, "metro.config.js"), `const { getDefaultConfig } = require(${JSON.stringify(node_path.default.join(packageRoot, "metro-config.cjs"))})\n\nmodule.exports = getDefaultConfig(__dirname)\n`);
|
|
762
826
|
(0, node_fs.writeFileSync)(node_path.default.join(nativeRoot, "ios", ".xcode.env"), `export NODE_BINARY=$(command -v node)\nexport BLEAM_PROJECT_ROOT=${singleQuotedString(options.projectRoot)}\nexport BLEAM_DEV_PLATFORM=macos\n`);
|
|
827
|
+
prepareNativeIcon(nativeRoot, config.iconPath);
|
|
763
828
|
patchNativeProject(nativeRoot, config);
|
|
764
829
|
return nativeRoot;
|
|
765
830
|
}
|
|
@@ -784,14 +849,13 @@ function runInheritedCommand(command, args, cwd) {
|
|
|
784
849
|
}
|
|
785
850
|
function installPods(nativeRoot) {
|
|
786
851
|
const iosRoot = node_path.default.join(nativeRoot, "ios");
|
|
787
|
-
|
|
788
|
-
if (hasCurrentPodInstall(iosRoot, expectedStamp)) {
|
|
852
|
+
if (hasCurrentPodInstall(iosRoot, podInstallInputStamp(iosRoot))) {
|
|
789
853
|
console.log("Using cached native dependencies");
|
|
790
854
|
return;
|
|
791
855
|
}
|
|
792
856
|
console.log("Installing native dependencies...");
|
|
793
857
|
runInheritedCommand("pod", ["install"], iosRoot);
|
|
794
|
-
(0, node_fs.writeFileSync)(podInstallStampPath(iosRoot), `${
|
|
858
|
+
(0, node_fs.writeFileSync)(podInstallStampPath(iosRoot), `${podInstallInputStamp(iosRoot)}\n`);
|
|
795
859
|
}
|
|
796
860
|
function ensureNativeFrameworks(nativeRoot) {
|
|
797
861
|
const frameworksRoot = node_path.default.join(nativeRoot, "ios", "Vendor", "MLXSwift", "0.31.6-macos");
|
|
@@ -824,7 +888,9 @@ function podInstallStampPath(iosRoot) {
|
|
|
824
888
|
}
|
|
825
889
|
function hasCurrentPodInstall(iosRoot, expectedStamp) {
|
|
826
890
|
const stampPath = podInstallStampPath(iosRoot);
|
|
827
|
-
|
|
891
|
+
const lockPath = node_path.default.join(iosRoot, "Podfile.lock");
|
|
892
|
+
const manifestPath = node_path.default.join(iosRoot, "Pods", "Manifest.lock");
|
|
893
|
+
return (0, node_fs.existsSync)(node_path.default.join(iosRoot, "Bleam.xcworkspace")) && (0, node_fs.existsSync)(lockPath) && (0, node_fs.existsSync)(manifestPath) && (0, node_fs.readFileSync)(lockPath, "utf8") === (0, node_fs.readFileSync)(manifestPath, "utf8") && (0, node_fs.existsSync)(stampPath) && (0, node_fs.readFileSync)(stampPath, "utf8").trim() === expectedStamp;
|
|
828
894
|
}
|
|
829
895
|
function nativeArchiveArgs(options) {
|
|
830
896
|
return [
|
|
@@ -999,10 +1065,76 @@ async function publishOta(options) {
|
|
|
999
1065
|
console.log(`Published OTA update ${releaseId}`);
|
|
1000
1066
|
console.log(`${options.serviceUrl}/v1/apps/${encodeURIComponent(config.bundleIdentifier)}/ota/${runtimeVersion}`);
|
|
1001
1067
|
}
|
|
1068
|
+
function compareVersions(left, right) {
|
|
1069
|
+
const parse = (value) => {
|
|
1070
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?(?:\+.*)?$/.exec(value);
|
|
1071
|
+
if (!match) throw new Error(`Invalid Bleam platform version: ${value}`);
|
|
1072
|
+
return {
|
|
1073
|
+
core: match.slice(1, 4).map(Number),
|
|
1074
|
+
prerelease: match[4]?.split(".")
|
|
1075
|
+
};
|
|
1076
|
+
};
|
|
1077
|
+
const a = parse(left);
|
|
1078
|
+
const b = parse(right);
|
|
1079
|
+
for (let index = 0; index < a.core.length; index += 1) if (a.core[index] !== b.core[index]) return (a.core[index] ?? 0) < (b.core[index] ?? 0) ? -1 : 1;
|
|
1080
|
+
if (!a.prerelease && !b.prerelease) return 0;
|
|
1081
|
+
if (!a.prerelease) return 1;
|
|
1082
|
+
if (!b.prerelease) return -1;
|
|
1083
|
+
const length = Math.max(a.prerelease.length, b.prerelease.length);
|
|
1084
|
+
for (let index = 0; index < length; index += 1) {
|
|
1085
|
+
const aPart = a.prerelease[index];
|
|
1086
|
+
const bPart = b.prerelease[index];
|
|
1087
|
+
if (aPart === void 0) return -1;
|
|
1088
|
+
if (bPart === void 0) return 1;
|
|
1089
|
+
if (aPart === bPart) continue;
|
|
1090
|
+
const aNumber = /^\d+$/.test(aPart) ? Number(aPart) : void 0;
|
|
1091
|
+
const bNumber = /^\d+$/.test(bPart) ? Number(bPart) : void 0;
|
|
1092
|
+
if (aNumber !== void 0 && bNumber !== void 0) return aNumber < bNumber ? -1 : 1;
|
|
1093
|
+
if (aNumber !== void 0) return -1;
|
|
1094
|
+
if (bNumber !== void 0) return 1;
|
|
1095
|
+
return aPart < bPart ? -1 : 1;
|
|
1096
|
+
}
|
|
1097
|
+
return 0;
|
|
1098
|
+
}
|
|
1099
|
+
function platformPublicationReason(active, target, redeploy) {
|
|
1100
|
+
if (active && compareVersions(target.bleamPlatformVersion, active.bleamPlatformVersion) < 0) throw new Error(`Cannot publish older Bleam platform ${target.bleamPlatformVersion} over ${active.bleamPlatformVersion}. Rollback requires a separate release operation.`);
|
|
1101
|
+
const unchanged = active?.platformFingerprint === target.platformFingerprint;
|
|
1102
|
+
if (unchanged && !redeploy) throw new Error("Platform inputs are unchanged. Developer code and ordinary assets must be published with bleam publish ota. Use --redeploy only to intentionally republish identical platform inputs.");
|
|
1103
|
+
if (!unchanged && redeploy) throw new Error("--redeploy requires an existing release with the same platform fingerprint; publish normally because platform inputs changed.");
|
|
1104
|
+
if (unchanged) return "redeploy";
|
|
1105
|
+
if (!active) return "first-release";
|
|
1106
|
+
if (!active.platformFingerprint) return "legacy-migration";
|
|
1107
|
+
if (active.bleamPlatformVersion !== target.bleamPlatformVersion) return "runtime-upgrade";
|
|
1108
|
+
return "signed-input-change";
|
|
1109
|
+
}
|
|
1110
|
+
async function activePlatformRelease(serviceUrl, config) {
|
|
1111
|
+
const response = await fetch(`${serviceUrl}/v1/apps/${encodeURIComponent(config.bundleIdentifier ?? "")}/platform/macos-arm64`, { cache: "no-store" });
|
|
1112
|
+
if (response.status === 404) return void 0;
|
|
1113
|
+
if (!response.ok) throw new Error(`Could not resolve the active platform release: ${response.status} ${await response.text()}`);
|
|
1114
|
+
const release = await response.json();
|
|
1115
|
+
if (release.kind !== "platform" || release.bundleIdentifier !== config.bundleIdentifier || release.teamIdentifier !== config.teamId || typeof release.bleamPlatformVersion !== "string" || release.platformFingerprint !== void 0 && (typeof release.platformFingerprint !== "string" || !/^[a-f0-9]{64}$/.test(release.platformFingerprint))) throw new Error("The active platform release is invalid for this app");
|
|
1116
|
+
return {
|
|
1117
|
+
bleamPlatformVersion: release.bleamPlatformVersion,
|
|
1118
|
+
platformFingerprint: release.platformFingerprint
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1002
1121
|
async function publishPlatform(options) {
|
|
1003
1122
|
const config = readProjectConfig(options);
|
|
1004
1123
|
if (!config.bundleIdentifier) throw missingBuildFieldError("bundleIdentifier");
|
|
1005
1124
|
if (!config.teamId) throw missingBuildFieldError("teamId");
|
|
1125
|
+
const fingerprint = platformInputFingerprint(config);
|
|
1126
|
+
const active = await activePlatformRelease(options.serviceUrl, config);
|
|
1127
|
+
console.log(`Bleam platform: ${active?.bleamPlatformVersion ?? "none"} -> ${config.runtimeVersion}`);
|
|
1128
|
+
console.log(`Platform fingerprint: ${active?.platformFingerprint ?? "none"} -> ${fingerprint}`);
|
|
1129
|
+
const reason = platformPublicationReason(active, {
|
|
1130
|
+
bleamPlatformVersion: config.runtimeVersion,
|
|
1131
|
+
platformFingerprint: fingerprint
|
|
1132
|
+
}, options.redeploy);
|
|
1133
|
+
if (reason === "redeploy") console.log("Redeploying unchanged platform inputs by explicit request");
|
|
1134
|
+
else if (reason === "first-release") console.log("Publishing the first platform release for this app");
|
|
1135
|
+
else if (reason === "legacy-migration") console.log("Migrating the active legacy release to platform fingerprints");
|
|
1136
|
+
else if (reason === "runtime-upgrade") console.log("Publishing a Bleam platform runtime upgrade");
|
|
1137
|
+
else console.log("Publishing changed signed app metadata or resources");
|
|
1006
1138
|
const archivePath = await buildNativeArchives({
|
|
1007
1139
|
projectRoot: options.projectRoot,
|
|
1008
1140
|
output: node_path.default.join(options.projectRoot, "dist"),
|
|
@@ -1031,7 +1163,7 @@ async function publishPlatform(options) {
|
|
|
1031
1163
|
const sizeBytes = (0, node_fs.readFileSync)(artifactPath).byteLength;
|
|
1032
1164
|
const digest = sha256(artifactPath);
|
|
1033
1165
|
const releaseId = `platform_${Date.now().toString(36)}_${digest.slice(0, 12)}`;
|
|
1034
|
-
const release = platformRelease(config, releaseId, sizeBytes, digest);
|
|
1166
|
+
const release = platformRelease(config, releaseId, sizeBytes, digest, options.redeploy);
|
|
1035
1167
|
const response = await fetch(`${options.serviceUrl}/v1/local/releases/platform`, {
|
|
1036
1168
|
method: "POST",
|
|
1037
1169
|
headers: { "content-type": "application/json" },
|
|
@@ -1044,7 +1176,7 @@ async function publishPlatform(options) {
|
|
|
1044
1176
|
console.log(`Published platform update ${releaseId}`);
|
|
1045
1177
|
console.log(`${options.serviceUrl}/v1/apps/${encodeURIComponent(config.bundleIdentifier)}/platform/macos-arm64`);
|
|
1046
1178
|
}
|
|
1047
|
-
function platformRelease(config, releaseId, sizeBytes, digest) {
|
|
1179
|
+
function platformRelease(config, releaseId, sizeBytes, digest, redeploy = false) {
|
|
1048
1180
|
if (!config.bundleIdentifier) throw missingBuildFieldError("bundleIdentifier");
|
|
1049
1181
|
if (!config.teamId) throw missingBuildFieldError("teamId");
|
|
1050
1182
|
return {
|
|
@@ -1056,7 +1188,9 @@ function platformRelease(config, releaseId, sizeBytes, digest) {
|
|
|
1056
1188
|
releaseId,
|
|
1057
1189
|
appVersion: config.version ?? "1.0.0",
|
|
1058
1190
|
buildNumber: config.buildNumber,
|
|
1059
|
-
bleamPlatformVersion: runtimeVersion,
|
|
1191
|
+
bleamPlatformVersion: config.runtimeVersion,
|
|
1192
|
+
platformFingerprint: platformInputFingerprint(config),
|
|
1193
|
+
redeploy,
|
|
1060
1194
|
target: {
|
|
1061
1195
|
os: "macos",
|
|
1062
1196
|
architecture: "arm64",
|
|
@@ -1405,6 +1539,92 @@ async function launchRuntimeClient(appPath, projectConfigPath, manifest) {
|
|
|
1405
1539
|
quitRuntimeClient();
|
|
1406
1540
|
openRuntimeClient(appPath, projectConfigPath, manifest);
|
|
1407
1541
|
}
|
|
1542
|
+
async function updateServiceHealthy(serviceUrl) {
|
|
1543
|
+
try {
|
|
1544
|
+
const response = await fetch(`${serviceUrl}/v1/health`, {
|
|
1545
|
+
cache: "no-store",
|
|
1546
|
+
signal: AbortSignal.timeout(1e3)
|
|
1547
|
+
});
|
|
1548
|
+
if (!response.ok) return false;
|
|
1549
|
+
return (await response.json()).healthy === true;
|
|
1550
|
+
} catch {
|
|
1551
|
+
return false;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
function updateServiceLogTail(logPath) {
|
|
1555
|
+
if (!(0, node_fs.existsSync)(logPath)) return "";
|
|
1556
|
+
return (0, node_fs.readFileSync)(logPath, "utf8").slice(-4e3).trim();
|
|
1557
|
+
}
|
|
1558
|
+
async function waitForUpdateService(child, serviceUrl, logPath, timeoutMs = 3e4) {
|
|
1559
|
+
const startedAt = Date.now();
|
|
1560
|
+
let startupError;
|
|
1561
|
+
child.once("error", (error) => {
|
|
1562
|
+
startupError = error;
|
|
1563
|
+
});
|
|
1564
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
1565
|
+
if (startupError || child.exitCode !== null) break;
|
|
1566
|
+
if (await updateServiceHealthy(serviceUrl)) return;
|
|
1567
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
1568
|
+
}
|
|
1569
|
+
const log = updateServiceLogTail(logPath);
|
|
1570
|
+
const detail = log ? `\n\n${log}` : "";
|
|
1571
|
+
throw new Error(`Local update Worker failed to start.\nLog: ${logPath}${detail}`, startupError ? { cause: startupError } : void 0);
|
|
1572
|
+
}
|
|
1573
|
+
async function startLocalUpdateService() {
|
|
1574
|
+
const configuredUrl = process.env.BLEAM_UPDATE_SERVICE_URL ?? localUpdateServiceUrl;
|
|
1575
|
+
const serviceUrl = new URL(configuredUrl).origin;
|
|
1576
|
+
if (await updateServiceHealthy(serviceUrl)) {
|
|
1577
|
+
console.log(`Using local update Worker at ${serviceUrl}`);
|
|
1578
|
+
return {
|
|
1579
|
+
child: void 0,
|
|
1580
|
+
serviceUrl
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
if (serviceUrl !== localUpdateServiceUrl) throw new Error(`BLEAM_UPDATE_SERVICE_URL is not healthy: ${serviceUrl}/v1/health`);
|
|
1584
|
+
const serviceRoot = localUpdateServiceRoot();
|
|
1585
|
+
const configPath = node_path.default.join(serviceRoot, "wrangler.jsonc");
|
|
1586
|
+
if (!(0, node_fs.existsSync)(configPath)) throw new Error(`Missing local update Worker: ${serviceRoot}`);
|
|
1587
|
+
const wrangler = node_path.default.join(resolvedDependencyRoot(), ".bin", "wrangler");
|
|
1588
|
+
if (!(0, node_fs.existsSync)(wrangler)) throw new Error("Missing Wrangler dependency for the local update Worker");
|
|
1589
|
+
const paths = localUpdateServicePaths();
|
|
1590
|
+
(0, node_fs.mkdirSync)(paths.state, { recursive: true });
|
|
1591
|
+
const log = (0, node_fs.openSync)(paths.log, "a");
|
|
1592
|
+
const child = (0, node_child_process.spawn)(wrangler, [
|
|
1593
|
+
"dev",
|
|
1594
|
+
"--config",
|
|
1595
|
+
configPath,
|
|
1596
|
+
"--local",
|
|
1597
|
+
"--persist-to",
|
|
1598
|
+
paths.state,
|
|
1599
|
+
"--ip",
|
|
1600
|
+
"127.0.0.1",
|
|
1601
|
+
"--port",
|
|
1602
|
+
"8787"
|
|
1603
|
+
], {
|
|
1604
|
+
cwd: serviceRoot,
|
|
1605
|
+
stdio: [
|
|
1606
|
+
"ignore",
|
|
1607
|
+
log,
|
|
1608
|
+
log
|
|
1609
|
+
],
|
|
1610
|
+
env: {
|
|
1611
|
+
...process.env,
|
|
1612
|
+
NO_COLOR: "1"
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
(0, node_fs.closeSync)(log);
|
|
1616
|
+
try {
|
|
1617
|
+
await waitForUpdateService(child, serviceUrl, paths.log);
|
|
1618
|
+
} catch (error) {
|
|
1619
|
+
child.kill("SIGTERM");
|
|
1620
|
+
throw error;
|
|
1621
|
+
}
|
|
1622
|
+
console.log(`Local update Worker running at ${serviceUrl}`);
|
|
1623
|
+
return {
|
|
1624
|
+
child,
|
|
1625
|
+
serviceUrl
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1408
1628
|
function focusRuntimeClient() {
|
|
1409
1629
|
return (0, node_child_process.spawnSync)("osascript", ["-e", "tell application id \"dev.bleam.app\" to activate"], { stdio: "ignore" }).status === 0;
|
|
1410
1630
|
}
|
|
@@ -1739,6 +1959,7 @@ async function startDevServer(options) {
|
|
|
1739
1959
|
const workspaceRoot = createDevWorkspace(options.projectRoot, config);
|
|
1740
1960
|
const origin = `http://localhost:${options.port}`;
|
|
1741
1961
|
const projectConfigPath = writeProjectRuntimeConfig(options.projectRoot, config, origin);
|
|
1962
|
+
const updateService = await startLocalUpdateService();
|
|
1742
1963
|
const child = (0, node_child_process.spawn)(process.execPath, [
|
|
1743
1964
|
(0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href),
|
|
1744
1965
|
"internal:metro",
|
|
@@ -1759,6 +1980,7 @@ async function startDevServer(options) {
|
|
|
1759
1980
|
BLEAM_DEV_PLATFORM: "macos",
|
|
1760
1981
|
BLEAM_DEV_RUNTIME_VERSION: config.runtimeVersion,
|
|
1761
1982
|
BLEAM_DEV_SDK_VERSION: config.sdkVersion,
|
|
1983
|
+
BLEAM_UPDATE_SERVICE_URL: updateService.serviceUrl,
|
|
1762
1984
|
NODE_PATH: [
|
|
1763
1985
|
node_path.default.join(options.projectRoot, "node_modules"),
|
|
1764
1986
|
resolvedDependencyRoot(),
|
|
@@ -1778,6 +2000,7 @@ async function startDevServer(options) {
|
|
|
1778
2000
|
didCleanupDevSession = true;
|
|
1779
2001
|
cleanupInteractiveControls();
|
|
1780
2002
|
if (didLaunchRuntimeClient) quitRuntimeClient();
|
|
2003
|
+
if (updateService.child?.exitCode === null) updateService.child.kill("SIGTERM");
|
|
1781
2004
|
};
|
|
1782
2005
|
const stop = (signal) => {
|
|
1783
2006
|
if (stopping) return;
|
|
@@ -1860,10 +2083,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1860
2083
|
return;
|
|
1861
2084
|
}
|
|
1862
2085
|
if (kind === "platform") {
|
|
1863
|
-
await publishPlatform(
|
|
2086
|
+
await publishPlatform(parsePublishPlatformOptions(publishArgs));
|
|
1864
2087
|
return;
|
|
1865
2088
|
}
|
|
1866
|
-
throw new Error("Usage: bleam publish <ota|platform> [dir] [--service-url <url>]");
|
|
2089
|
+
throw new Error("Usage: bleam publish <ota|platform> [dir] [--service-url <url>] [--redeploy]");
|
|
1867
2090
|
}
|
|
1868
2091
|
if (command === "doctor") {
|
|
1869
2092
|
runDoctor(parseDoctorOptions(args));
|
|
@@ -1901,11 +2124,16 @@ const __test = {
|
|
|
1901
2124
|
hasCurrentPodInstall,
|
|
1902
2125
|
installedAppPath,
|
|
1903
2126
|
installArchivedApp,
|
|
2127
|
+
localUpdateServicePaths,
|
|
2128
|
+
localUpdateServiceRoot,
|
|
1904
2129
|
nativeRuntimeTemplateRoot,
|
|
1905
2130
|
nativeArchiveArgs,
|
|
1906
2131
|
parseBuildOptions,
|
|
1907
2132
|
parseNewOptions,
|
|
1908
2133
|
parsePublishOtaOptions,
|
|
2134
|
+
parsePublishPlatformOptions,
|
|
2135
|
+
platformInputFingerprint,
|
|
2136
|
+
platformPublicationReason,
|
|
1909
2137
|
platformRelease,
|
|
1910
2138
|
podInstallInputStamp,
|
|
1911
2139
|
replaceOrInsertPlistString
|
package/dist/cli.d.cts
CHANGED
|
@@ -13,6 +13,9 @@ interface PublishOtaOptions {
|
|
|
13
13
|
projectRoot: string;
|
|
14
14
|
serviceUrl: string;
|
|
15
15
|
}
|
|
16
|
+
interface PublishPlatformOptions extends PublishOtaOptions {
|
|
17
|
+
redeploy: boolean;
|
|
18
|
+
}
|
|
16
19
|
interface ProjectConfig {
|
|
17
20
|
name: string;
|
|
18
21
|
version?: string;
|
|
@@ -23,11 +26,13 @@ interface ProjectConfig {
|
|
|
23
26
|
appearance: BleamAppearance;
|
|
24
27
|
runtimeVersion: string;
|
|
25
28
|
sdkVersion: string;
|
|
29
|
+
iconPath?: string;
|
|
26
30
|
}
|
|
27
31
|
declare function parsePublishOtaOptions(argv: string[]): PublishOtaOptions;
|
|
32
|
+
declare function parsePublishPlatformOptions(argv: string[]): PublishPlatformOptions;
|
|
28
33
|
declare function parseNewOptions(argv: string[]): {
|
|
29
34
|
projectRoot: string;
|
|
30
|
-
template: "
|
|
35
|
+
template: "state" | "basic" | "foundation-models" | "image-generation";
|
|
31
36
|
};
|
|
32
37
|
declare const projectTemplates: readonly ["basic", "state", "foundation-models", "image-generation"];
|
|
33
38
|
type ProjectTemplate = (typeof projectTemplates)[number];
|
|
@@ -37,7 +42,14 @@ declare function createProject(options: {
|
|
|
37
42
|
template?: ProjectTemplate;
|
|
38
43
|
}): void;
|
|
39
44
|
declare function nativeRuntimeTemplateRoot(root?: string): string;
|
|
45
|
+
declare function localUpdateServiceRoot(root?: string): string;
|
|
46
|
+
declare function localUpdateServicePaths(home?: string): {
|
|
47
|
+
root: string;
|
|
48
|
+
state: string;
|
|
49
|
+
log: string;
|
|
50
|
+
};
|
|
40
51
|
declare function replaceOrInsertPlistString(plist: string, key: string, value: string): string;
|
|
52
|
+
declare function platformInputFingerprint(config: ProjectConfig): string;
|
|
41
53
|
declare function createBuildNativeWorkspace(options: BuildOptions, config: ProjectConfig): string;
|
|
42
54
|
declare function podInstallInputStamp(iosRoot: string): string;
|
|
43
55
|
declare function hasCurrentPodInstall(iosRoot: string, expectedStamp: string): boolean;
|
|
@@ -48,7 +60,16 @@ declare function nativeArchiveArgs(options: {
|
|
|
48
60
|
}): string[];
|
|
49
61
|
declare function installArchivedApp(archivePath: string, applications?: string): string;
|
|
50
62
|
declare function installedAppPath(appName: string): string;
|
|
51
|
-
|
|
63
|
+
interface ActivePlatformRelease {
|
|
64
|
+
bleamPlatformVersion: string;
|
|
65
|
+
platformFingerprint?: string;
|
|
66
|
+
}
|
|
67
|
+
type PlatformPublicationReason = 'first-release' | 'legacy-migration' | 'runtime-upgrade' | 'signed-input-change' | 'redeploy';
|
|
68
|
+
declare function platformPublicationReason(active: ActivePlatformRelease | undefined, target: {
|
|
69
|
+
bleamPlatformVersion: string;
|
|
70
|
+
platformFingerprint: string;
|
|
71
|
+
}, redeploy: boolean): PlatformPublicationReason;
|
|
72
|
+
declare function platformRelease(config: ProjectConfig, releaseId: string, sizeBytes: number, digest: string, redeploy?: boolean): {
|
|
52
73
|
schemaVersion: number;
|
|
53
74
|
kind: string;
|
|
54
75
|
bundleIdentifier: string;
|
|
@@ -58,6 +79,8 @@ declare function platformRelease(config: ProjectConfig, releaseId: string, sizeB
|
|
|
58
79
|
appVersion: string;
|
|
59
80
|
buildNumber: string;
|
|
60
81
|
bleamPlatformVersion: string;
|
|
82
|
+
platformFingerprint: string;
|
|
83
|
+
redeploy: boolean;
|
|
61
84
|
target: {
|
|
62
85
|
os: string;
|
|
63
86
|
architecture: string;
|
|
@@ -78,11 +101,16 @@ declare const __test: {
|
|
|
78
101
|
hasCurrentPodInstall: typeof hasCurrentPodInstall;
|
|
79
102
|
installedAppPath: typeof installedAppPath;
|
|
80
103
|
installArchivedApp: typeof installArchivedApp;
|
|
104
|
+
localUpdateServicePaths: typeof localUpdateServicePaths;
|
|
105
|
+
localUpdateServiceRoot: typeof localUpdateServiceRoot;
|
|
81
106
|
nativeRuntimeTemplateRoot: typeof nativeRuntimeTemplateRoot;
|
|
82
107
|
nativeArchiveArgs: typeof nativeArchiveArgs;
|
|
83
108
|
parseBuildOptions: typeof parseBuildOptions;
|
|
84
109
|
parseNewOptions: typeof parseNewOptions;
|
|
85
110
|
parsePublishOtaOptions: typeof parsePublishOtaOptions;
|
|
111
|
+
parsePublishPlatformOptions: typeof parsePublishPlatformOptions;
|
|
112
|
+
platformInputFingerprint: typeof platformInputFingerprint;
|
|
113
|
+
platformPublicationReason: typeof platformPublicationReason;
|
|
86
114
|
platformRelease: typeof platformRelease;
|
|
87
115
|
podInstallInputStamp: typeof podInstallInputStamp;
|
|
88
116
|
replaceOrInsertPlistString: typeof replaceOrInsertPlistString;
|
package/dist/cli.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ interface PublishOtaOptions {
|
|
|
13
13
|
projectRoot: string;
|
|
14
14
|
serviceUrl: string;
|
|
15
15
|
}
|
|
16
|
+
interface PublishPlatformOptions extends PublishOtaOptions {
|
|
17
|
+
redeploy: boolean;
|
|
18
|
+
}
|
|
16
19
|
interface ProjectConfig {
|
|
17
20
|
name: string;
|
|
18
21
|
version?: string;
|
|
@@ -23,11 +26,13 @@ interface ProjectConfig {
|
|
|
23
26
|
appearance: BleamAppearance;
|
|
24
27
|
runtimeVersion: string;
|
|
25
28
|
sdkVersion: string;
|
|
29
|
+
iconPath?: string;
|
|
26
30
|
}
|
|
27
31
|
declare function parsePublishOtaOptions(argv: string[]): PublishOtaOptions;
|
|
32
|
+
declare function parsePublishPlatformOptions(argv: string[]): PublishPlatformOptions;
|
|
28
33
|
declare function parseNewOptions(argv: string[]): {
|
|
29
34
|
projectRoot: string;
|
|
30
|
-
template: "
|
|
35
|
+
template: "state" | "basic" | "foundation-models" | "image-generation";
|
|
31
36
|
};
|
|
32
37
|
declare const projectTemplates: readonly ["basic", "state", "foundation-models", "image-generation"];
|
|
33
38
|
type ProjectTemplate = (typeof projectTemplates)[number];
|
|
@@ -37,7 +42,14 @@ declare function createProject(options: {
|
|
|
37
42
|
template?: ProjectTemplate;
|
|
38
43
|
}): void;
|
|
39
44
|
declare function nativeRuntimeTemplateRoot(root?: string): string;
|
|
45
|
+
declare function localUpdateServiceRoot(root?: string): string;
|
|
46
|
+
declare function localUpdateServicePaths(home?: string): {
|
|
47
|
+
root: string;
|
|
48
|
+
state: string;
|
|
49
|
+
log: string;
|
|
50
|
+
};
|
|
40
51
|
declare function replaceOrInsertPlistString(plist: string, key: string, value: string): string;
|
|
52
|
+
declare function platformInputFingerprint(config: ProjectConfig): string;
|
|
41
53
|
declare function createBuildNativeWorkspace(options: BuildOptions, config: ProjectConfig): string;
|
|
42
54
|
declare function podInstallInputStamp(iosRoot: string): string;
|
|
43
55
|
declare function hasCurrentPodInstall(iosRoot: string, expectedStamp: string): boolean;
|
|
@@ -48,7 +60,16 @@ declare function nativeArchiveArgs(options: {
|
|
|
48
60
|
}): string[];
|
|
49
61
|
declare function installArchivedApp(archivePath: string, applications?: string): string;
|
|
50
62
|
declare function installedAppPath(appName: string): string;
|
|
51
|
-
|
|
63
|
+
interface ActivePlatformRelease {
|
|
64
|
+
bleamPlatformVersion: string;
|
|
65
|
+
platformFingerprint?: string;
|
|
66
|
+
}
|
|
67
|
+
type PlatformPublicationReason = 'first-release' | 'legacy-migration' | 'runtime-upgrade' | 'signed-input-change' | 'redeploy';
|
|
68
|
+
declare function platformPublicationReason(active: ActivePlatformRelease | undefined, target: {
|
|
69
|
+
bleamPlatformVersion: string;
|
|
70
|
+
platformFingerprint: string;
|
|
71
|
+
}, redeploy: boolean): PlatformPublicationReason;
|
|
72
|
+
declare function platformRelease(config: ProjectConfig, releaseId: string, sizeBytes: number, digest: string, redeploy?: boolean): {
|
|
52
73
|
schemaVersion: number;
|
|
53
74
|
kind: string;
|
|
54
75
|
bundleIdentifier: string;
|
|
@@ -58,6 +79,8 @@ declare function platformRelease(config: ProjectConfig, releaseId: string, sizeB
|
|
|
58
79
|
appVersion: string;
|
|
59
80
|
buildNumber: string;
|
|
60
81
|
bleamPlatformVersion: string;
|
|
82
|
+
platformFingerprint: string;
|
|
83
|
+
redeploy: boolean;
|
|
61
84
|
target: {
|
|
62
85
|
os: string;
|
|
63
86
|
architecture: string;
|
|
@@ -78,11 +101,16 @@ declare const __test: {
|
|
|
78
101
|
hasCurrentPodInstall: typeof hasCurrentPodInstall;
|
|
79
102
|
installedAppPath: typeof installedAppPath;
|
|
80
103
|
installArchivedApp: typeof installArchivedApp;
|
|
104
|
+
localUpdateServicePaths: typeof localUpdateServicePaths;
|
|
105
|
+
localUpdateServiceRoot: typeof localUpdateServiceRoot;
|
|
81
106
|
nativeRuntimeTemplateRoot: typeof nativeRuntimeTemplateRoot;
|
|
82
107
|
nativeArchiveArgs: typeof nativeArchiveArgs;
|
|
83
108
|
parseBuildOptions: typeof parseBuildOptions;
|
|
84
109
|
parseNewOptions: typeof parseNewOptions;
|
|
85
110
|
parsePublishOtaOptions: typeof parsePublishOtaOptions;
|
|
111
|
+
parsePublishPlatformOptions: typeof parsePublishPlatformOptions;
|
|
112
|
+
platformInputFingerprint: typeof platformInputFingerprint;
|
|
113
|
+
platformPublicationReason: typeof platformPublicationReason;
|
|
86
114
|
platformRelease: typeof platformRelease;
|
|
87
115
|
podInstallInputStamp: typeof podInstallInputStamp;
|
|
88
116
|
replaceOrInsertPlistString: typeof replaceOrInsertPlistString;
|