Skip to content

Commit 932219d

Browse files
committed
Looser zod schemas
1 parent 4694824 commit 932219d

13 files changed

+318
-166
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "gib",
4-
"version": "0.7.2",
4+
"version": "0.7.3",
55
"description": "A TUI application for automating the installation of BepInEx",
66
"license": "ISC",
77
"author": "Tobey Blaber",

src/cli/index.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,9 @@ export const run = async () => {
874874
buildPlist(
875875
{
876876
CFBundleIconFile,
877-
CFBundleName: `${CFBundleName ?? name} (Vanilla)`,
877+
CFBundleName: `${
878+
typeof CFBundleName === "string" ? CFBundleName : name
879+
} (Vanilla)`,
878880
CFBundleInfoDictionaryVersion: "1.0",
879881
CFBundlePackageType: "APPL",
880882
CFBundleVersion: "1.0",
@@ -913,14 +915,21 @@ export const run = async () => {
913915
* - get current shortcuts, add new shortcut
914916
*/
915917
const { CFBundleName, CFBundleIconFile } = plist;
916-
const gameName = CFBundleName ?? basename(gameAppPath);
918+
const gameName = typeof CFBundleName === "string"
919+
? CFBundleName
920+
: basename(gameAppPath);
917921
const game = code(gameName);
918922

919923
const steamInstalled = await isInstalled();
920924
const [userId, user] = steamInstalled
921925
? await getMostRecentUser()
922926
: [undefined, undefined];
923-
const username = user && code(user.PersonaName ?? user.AccountName);
927+
const username = user &&
928+
code(
929+
typeof user.PersonaName === "string"
930+
? user.PersonaName
931+
: user.AccountName,
932+
);
924933

925934
shouldAddShortcut = await isInstalled() && confirmShim(wrap([
926935
[
@@ -1229,7 +1238,11 @@ export const run = async () => {
12291238
"We also added a shortcut to Steam to launch the game with",
12301239
"BepInEx. You can find it in your Steam library named",
12311240
code(
1232-
`${plist.CFBundleName ?? basename(gameAppPath)} (BepInEx)`,
1241+
`${
1242+
typeof plist.CFBundleName === "string"
1243+
? plist.CFBundleName
1244+
: basename(gameAppPath)
1245+
} (BepInEx)`,
12331246
),
12341247
].join(" "),
12351248
null,

src/launchers/epic/app.ts

+72-42
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,42 @@ export type App = AppBase<AppManifest> & {
1818
launchId: string;
1919
};
2020

21-
export const isFullyInstalled = (app: App) =>
22-
app.manifest.bIsIncompleteInstall !== true;
21+
export const isFullyInstalled = ({ manifest: { bIsIncompleteInstall } }: App) =>
22+
bIsIncompleteInstall !== true;
2323

2424
/** Gets information about installed Epic Games Launcher apps. */
2525
export async function* getApps() {
2626
const launcherInstalled = getLauncherInstalled();
2727

28-
for await (const manifest of getManifests()) {
28+
for await (const _manifest of getManifests()) {
2929
const info = (await launcherInstalled)
30-
.find((x) => [x.artifactId, x.appName].includes(manifest.appName));
30+
.find((x) => [x.artifactId, x.appName].includes(_manifest.appName));
3131

3232
if (info) {
33-
const merged = { ...info, ...manifest };
33+
const manifest = { ...info, ..._manifest };
34+
const {
35+
displayName: name,
36+
installLocation: path,
37+
namespaceId,
38+
catalogNamespace,
39+
itemId,
40+
catalogItemId,
41+
artifactId,
42+
appName,
43+
} = manifest;
44+
const id = typeof artifactId === "string" ? artifactId : appName;
45+
const launchId = [
46+
typeof namespaceId === "string" ? namespaceId : catalogNamespace,
47+
typeof itemId === "string" ? itemId : catalogItemId,
48+
id,
49+
].filter(Boolean).join(":");
3450
yield {
3551
launcher,
36-
manifest: merged,
37-
id: merged.artifactId ?? merged.appName,
38-
name: merged.displayName,
39-
path: merged.installLocation,
40-
launchId: [
41-
merged.namespaceId ?? merged.catalogNamespace,
42-
merged.itemId ?? merged.catalogItemId,
43-
merged.artifactId ?? merged.appName,
44-
]
45-
.filter(Boolean)
46-
.join(":"),
52+
manifest,
53+
id,
54+
name,
55+
path,
56+
launchId,
4757
} satisfies App;
4858
}
4959
}
@@ -67,21 +77,31 @@ export const getAppById = async (id: string) =>
6777
]),
6878
)
6979
.returnType<App | undefined>()
70-
.with([P.not(P.nullish), P.not(P.nullish)], ([info, manifest]) => {
71-
const merged = { ...info, ...manifest };
80+
.with([P.not(P.nullish), P.not(P.nullish)], ([info, _manifest]) => {
81+
const manifest = { ...info, ..._manifest };
82+
const {
83+
displayName: name,
84+
installLocation: path,
85+
namespaceId,
86+
catalogNamespace,
87+
itemId,
88+
catalogItemId,
89+
artifactId,
90+
appName,
91+
} = manifest;
92+
const id = typeof artifactId === "string" ? artifactId : appName;
93+
const launchId = [
94+
typeof namespaceId === "string" ? namespaceId : catalogNamespace,
95+
typeof itemId === "string" ? itemId : catalogItemId,
96+
id,
97+
].filter(Boolean).join(":");
7298
return {
7399
launcher,
74-
manifest: merged,
75-
id: merged.artifactId ?? merged.appName,
76-
name: merged.displayName,
77-
path: merged.installLocation,
78-
launchId: [
79-
merged.namespaceId ?? merged.catalogNamespace,
80-
merged.itemId ?? merged.catalogItemId,
81-
merged.artifactId ?? merged.appName,
82-
]
83-
.filter(Boolean)
84-
.join(":"),
100+
manifest,
101+
id,
102+
name,
103+
path,
104+
launchId,
85105
} satisfies App;
86106
})
87107
.otherwise(() => undefined);
@@ -102,21 +122,31 @@ export const getAppByPath = async (path: string) =>
102122
]),
103123
)
104124
.returnType<App | undefined>()
105-
.with([P.not(P.nullish), P.not(P.nullish)], ([info, manifest]) => {
106-
const merged = { ...info, ...manifest };
125+
.with([P.not(P.nullish), P.not(P.nullish)], ([info, _manifest]) => {
126+
const manifest = { ...info, ..._manifest };
127+
const {
128+
displayName: name,
129+
installLocation: path,
130+
namespaceId,
131+
catalogNamespace,
132+
itemId,
133+
catalogItemId,
134+
artifactId,
135+
appName,
136+
} = manifest;
137+
const id = typeof artifactId === "string" ? artifactId : appName;
138+
const launchId = [
139+
typeof namespaceId === "string" ? namespaceId : catalogNamespace,
140+
typeof itemId === "string" ? itemId : catalogItemId,
141+
id,
142+
].filter(Boolean).join(":");
107143
return {
108144
launcher,
109-
manifest: merged,
110-
id: merged.artifactId ?? merged.appName,
111-
name: merged.displayName,
112-
path: merged.installLocation,
113-
launchId: [
114-
merged.namespaceId ?? merged.catalogNamespace,
115-
merged.itemId ?? merged.catalogItemId,
116-
merged.artifactId ?? merged.appName,
117-
]
118-
.filter(Boolean)
119-
.join(":"),
145+
manifest,
146+
id,
147+
name,
148+
path,
149+
launchId,
120150
} satisfies App;
121151
})
122152
.otherwise(() => undefined);

src/launchers/epic/launcherInstalled.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ export const launcherInstalledSchema = toCamelCaseKeys(
1313
installationList: toCamelCaseKeys(
1414
z.object({
1515
installLocation: z.string(),
16-
namespaceId: z.string().optional(),
17-
itemId: z.string().optional(),
18-
artifactId: z.string().optional(),
19-
appVersion: z.string().optional(),
16+
/** @type {string | undefined} */
17+
namespaceId: z.unknown().optional(),
18+
/** @type {string | undefined} */
19+
itemId: z.unknown().optional(),
20+
/** @type {string | undefined} */
21+
artifactId: z.unknown().optional(),
22+
/** @type {string | undefined} */
23+
appVersion: z.unknown().optional(),
2024
appName: z.string(),
2125
}).passthrough(),
2226
).array(),

src/launchers/epic/manifest.ts

+88-44
Original file line numberDiff line numberDiff line change
@@ -10,55 +10,99 @@ import type { launcherInstalledSchema } from "./launcherInstalled.ts";
1010
export const appManifestSchema = toCamelCaseKeys(
1111
z.object({
1212
formatVersion: z.number(),
13-
bIsIncompleteInstall: z.boolean().optional(),
14-
launchCommand: z.string().optional(),
15-
launchExecutable: z.string().optional(),
16-
manifestLocation: z.string().optional(),
17-
manifestHash: z.string().optional(),
18-
bIsApplication: z.boolean().optional(),
19-
bIsExecutable: z.boolean().optional(),
20-
bIsManaged: z.boolean().optional(),
21-
bNeedsValidation: z.boolean().optional(),
22-
bRequiresAuth: z.boolean().optional(),
23-
bAllowMultipleInstances: z.boolean().optional(),
24-
bCanRunOffline: z.boolean().optional(),
25-
bAllowUriCmdArgs: z.boolean().optional(),
26-
bLaunchElevated: z.boolean().optional(),
27-
baseUrLs: z.string().array().optional(),
28-
buildLabel: z.string().optional(),
29-
appCategories: z.string().array().optional(),
30-
chunkDbs: z.unknown().array().optional(),
31-
compatibleApps: z.unknown().array().optional(),
13+
/** @type {boolean | undefined} */
14+
bIsIncompleteInstall: z.unknown().optional(),
15+
/** @type {string | undefined} */
16+
launchCommand: z.unknown().optional(),
17+
/** @type {string | undefined} */
18+
launchExecutable: z.unknown().optional(),
19+
/** @type {string | undefined} */
20+
manifestLocation: z.unknown().optional(),
21+
/** @type {string | undefined} */
22+
manifestHash: z.unknown().optional(),
23+
/** @type {boolean | undefined} */
24+
bIsApplication: z.unknown().optional(),
25+
/** @type {boolean | undefined} */
26+
bIsExecutable: z.unknown().optional(),
27+
/** @type {boolean | undefined} */
28+
bIsManaged: z.unknown().optional(),
29+
/** @type {boolean | undefined} */
30+
bNeedsValidation: z.unknown().optional(),
31+
/** @type {boolean | undefined} */
32+
bRequiresAuth: z.unknown().optional(),
33+
/** @type {boolean | undefined} */
34+
bAllowMultipleInstances: z.unknown().optional(),
35+
/** @type {boolean | undefined} */
36+
bCanRunOffline: z.unknown().optional(),
37+
/** @type {boolean | undefined} */
38+
bAllowUriCmdArgs: z.unknown().optional(),
39+
/** @type {boolean | undefined} */
40+
bLaunchElevated: z.unknown().optional(),
41+
/** @type {string[] | undefined} */
42+
baseUrLs: z.unknown().optional(),
43+
/** @type {string | undefined} */
44+
buildLabel: z.unknown().optional(),
45+
/** @type {string[] | undefined} */
46+
appCategories: z.unknown().optional(),
47+
/** @type {unknown[] | undefined} */
48+
chunkDbs: z.unknown().optional(),
49+
/** @type {unknown[] | undefined} */
50+
compatibleApps: z.unknown().optional(),
3251
displayName: z.string(),
33-
installationGuid: z.string().optional(),
52+
/** @type {string | undefined} */
53+
installationGuid: z.unknown().optional(),
3454
installLocation: z.string(),
35-
installSessionId: z.string().optional(),
36-
installTags: z.unknown().array().optional(),
37-
installComponents: z.unknown().array().optional(),
38-
hostInstallationGuid: z.string().optional(),
39-
prereqIds: z.unknown().array().optional(),
40-
prereqSha1Hash: z.string().optional(),
41-
lastPrereqSucceededSha1Hash: z.string().optional(),
42-
stagingLocation: z.string().optional(),
43-
technicalType: z.string().optional(),
44-
vaultThumbnailUrl: z.string().optional(),
45-
vaultTitleText: z.string().optional(),
46-
installSize: z.number().optional(),
47-
mainWindowProcessName: z.string().optional(),
48-
processNames: z.unknown().array().optional(),
49-
backgroundProcessNames: z.unknown().array().optional(),
50-
ignoredProcessNames: z.unknown().array().optional(),
51-
dlcProcessNames: z.unknown().array().optional(),
52-
mandatoryAppFolderName: z.string().optional(),
53-
ownershipToken: z.string().optional(),
55+
/** @type {string | undefined} */
56+
installSessionId: z.unknown().optional(),
57+
/** @type {unknown[] | undefined} */
58+
installTags: z.unknown().optional(),
59+
/** @type {unknown[] | undefined} */
60+
installComponents: z.unknown().optional(),
61+
/** @type {string | undefined} */
62+
hostInstallationGuid: z.unknown().optional(),
63+
/** @type {unknown[] | undefined} */
64+
prereqIds: z.unknown().optional(),
65+
/** @type {string | undefined} */
66+
prereqSha1Hash: z.unknown().optional(),
67+
/** @type {string | undefined} */
68+
lastPrereqSucceededSha1Hash: z.unknown().optional(),
69+
/** @type {string | undefined} */
70+
stagingLocation: z.unknown().optional(),
71+
/** @type {string | undefined} */
72+
technicalType: z.unknown().optional(),
73+
/** @type {string | undefined} */
74+
vaultThumbnailUrl: z.unknown().optional(),
75+
/** @type {string | undefined} */
76+
vaultTitleText: z.unknown().optional(),
77+
/** @type {number | undefined} */
78+
installSize: z.unknown().optional(),
79+
/** @type {string | undefined} */
80+
mainWindowProcessName: z.unknown().optional(),
81+
/** @type {unknown[] | undefined} */
82+
processNames: z.unknown().optional(),
83+
/** @type {unknown[] | undefined} */
84+
backgroundProcessNames: z.unknown().optional(),
85+
/** @type {unknown[] | undefined} */
86+
ignoredProcessNames: z.unknown().optional(),
87+
/** @type {unknown[] | undefined} */
88+
dlcProcessNames: z.unknown().optional(),
89+
/** @type {string | undefined} */
90+
mandatoryAppFolderName: z.unknown().optional(),
91+
/** @type {string | undefined} */
92+
ownershipToken: z.unknown().optional(),
5493
catalogNamespace: z.string(),
5594
catalogItemId: z.string(),
5695
appName: z.string(),
57-
appVersionString: z.string().optional(),
58-
mainGameCatalogNamespace: z.string().optional(),
59-
mainGameCatalogItemId: z.string().optional(),
60-
mainGameAppName: z.string().optional(),
61-
allowedUriEnvVars: z.unknown().array().optional(),
96+
/** @type {string | undefined} */
97+
appVersionString: z.unknown().optional(),
98+
/** @type {string | undefined} */
99+
mainGameCatalogNamespace: z.unknown().optional(),
100+
/** @type {string | undefined} */
101+
mainGameCatalogItemId: z.unknown().optional(),
102+
/** @type {string | undefined} */
103+
mainGameAppName: z.unknown().optional(),
104+
/** @type {unknown[] | undefined} */
105+
allowedUriEnvVars: z.unknown().optional(),
62106
}).passthrough(),
63107
);
64108

src/launchers/steam/launchOption.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ export async function getLaunchOptions(
137137
)
138138
.returnType<string | undefined>()
139139
.with(P.number, (n) => n.toString())
140-
.otherwise((x) => x);
140+
.with(P.string, (s) => s)
141+
.otherwise(() => undefined);
141142

142143
return launchOptions && removeSlashes(launchOptions) || undefined;
143144
}

0 commit comments

Comments
 (0)