@px-lsp/protocol 0.2.0 → 0.2.1
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/calendar.d.ts +15 -13
- package/dist/calendar.js +26 -27
- package/dist/calendarFile.d.ts +26 -0
- package/dist/calendarFile.js +109 -0
- package/dist/calendarLoc.js +7 -11
- package/dist/configDir.d.ts +17 -0
- package/dist/configDir.js +79 -0
- package/dist/protocol.d.ts +481 -0
- package/dist/protocol.js +123 -1
- package/dist/workshopMeta.d.ts +3 -5
- package/dist/workshopMeta.js +6 -11
- package/package.json +1 -1
- package/src/calendar.ts +34 -35
- package/src/calendarFile.ts +81 -0
- package/src/calendarLoc.ts +7 -14
- package/src/configDir.ts +47 -0
- package/src/protocol.ts +487 -0
- package/src/workshopMeta.ts +6 -11
package/dist/workshopMeta.d.ts
CHANGED
|
@@ -12,16 +12,14 @@ export interface WorkshopMeta {
|
|
|
12
12
|
/** Keyed by Steam API language code (`german`, `schinese`, ...), never the default language. */
|
|
13
13
|
translations?: Record<string, WorkshopTranslation>;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
export declare function
|
|
17
|
-
/** The parsed `<dir>/<configDir>/workshop.json`, or null when absent/unreadable. */
|
|
18
|
-
export declare function readWorkshopMeta(dir: string, configDirName: string): WorkshopMeta | null;
|
|
15
|
+
/** The parsed `<configDir>/workshop.json`, or null when absent/unreadable. */
|
|
16
|
+
export declare function readWorkshopMeta(configDir: string): WorkshopMeta | null;
|
|
19
17
|
/**
|
|
20
18
|
* Merge `patch` into the record and write it back. Unknown keys of the file
|
|
21
19
|
* survive; a patch key set to `undefined` is left as it was. `translations`
|
|
22
20
|
* replaces as a whole (the caller edits the full map).
|
|
23
21
|
*/
|
|
24
|
-
export declare function upsertWorkshopMeta(
|
|
22
|
+
export declare function upsertWorkshopMeta(configDir: string, patch: WorkshopMeta): void;
|
|
25
23
|
/**
|
|
26
24
|
* The languages the Workshop accepts item text in: Steam's API language codes
|
|
27
25
|
* with their English names, in Steam's documented order.
|
package/dist/workshopMeta.js
CHANGED
|
@@ -34,7 +34,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.STEAM_LANGUAGES = void 0;
|
|
37
|
-
exports.workshopMetaRelPath = workshopMetaRelPath;
|
|
38
37
|
exports.readWorkshopMeta = readWorkshopMeta;
|
|
39
38
|
exports.upsertWorkshopMeta = upsertWorkshopMeta;
|
|
40
39
|
exports.steamLanguageLabel = steamLanguageLabel;
|
|
@@ -55,14 +54,10 @@ exports.steamLanguageForLoc = steamLanguageForLoc;
|
|
|
55
54
|
*/
|
|
56
55
|
const fs = __importStar(require("fs"));
|
|
57
56
|
const path = __importStar(require("path"));
|
|
58
|
-
/**
|
|
59
|
-
function
|
|
60
|
-
return `${configDirName}/workshop.json`;
|
|
61
|
-
}
|
|
62
|
-
/** The parsed `<dir>/<configDir>/workshop.json`, or null when absent/unreadable. */
|
|
63
|
-
function readWorkshopMeta(dir, configDirName) {
|
|
57
|
+
/** The parsed `<configDir>/workshop.json`, or null when absent/unreadable. */
|
|
58
|
+
function readWorkshopMeta(configDir) {
|
|
64
59
|
try {
|
|
65
|
-
const raw = JSON.parse(fs.readFileSync(path.join(
|
|
60
|
+
const raw = JSON.parse(fs.readFileSync(path.join(configDir, "workshop.json"), "utf8"));
|
|
66
61
|
if (typeof raw !== "object" || raw === null)
|
|
67
62
|
return null;
|
|
68
63
|
return raw;
|
|
@@ -76,9 +71,9 @@ function readWorkshopMeta(dir, configDirName) {
|
|
|
76
71
|
* survive; a patch key set to `undefined` is left as it was. `translations`
|
|
77
72
|
* replaces as a whole (the caller edits the full map).
|
|
78
73
|
*/
|
|
79
|
-
function upsertWorkshopMeta(
|
|
80
|
-
const file = path.join(
|
|
81
|
-
const current = (readWorkshopMeta(
|
|
74
|
+
function upsertWorkshopMeta(configDir, patch) {
|
|
75
|
+
const file = path.join(configDir, "workshop.json");
|
|
76
|
+
const current = (readWorkshopMeta(configDir) ?? {});
|
|
82
77
|
for (const [key, value] of Object.entries(patch)) {
|
|
83
78
|
if (value !== undefined)
|
|
84
79
|
current[key] = value;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@px-lsp/protocol",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "GPL-3.0-or-later",
|
|
5
5
|
"description": "Wire contract (custom LSP requests/notifications, settings types) and shared helpers for the px-lsp language server and its clients.",
|
|
6
6
|
"keywords": [
|
package/src/calendar.ts
CHANGED
|
@@ -11,13 +11,6 @@
|
|
|
11
11
|
* Node.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
export interface CalendarMonth {
|
|
15
|
-
/** Display name ("March", "Narvinye"). */
|
|
16
|
-
name: string;
|
|
17
|
-
/** Day count; the engine has no leap years, so one number per month. */
|
|
18
|
-
days: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
14
|
export interface CalendarSetting {
|
|
22
15
|
/** Script year displayed as year 1 of the `after` era (no year zero). */
|
|
23
16
|
epoch: number;
|
|
@@ -26,13 +19,20 @@ export interface CalendarSetting {
|
|
|
26
19
|
/** Era label for script years < epoch ("BC"). Omitted = single-era
|
|
27
20
|
* calendar: years before the epoch get no display form. */
|
|
28
21
|
before?: string;
|
|
29
|
-
/**
|
|
30
|
-
*
|
|
31
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The engine's twelve months under the mod's own names, first month first.
|
|
24
|
+
* Omitted = January to December. Only the NAMES are the mod's: the game has
|
|
25
|
+
* twelve months of fixed length (31 28 31 30 31 30 31 31 30 31 30 31, no
|
|
26
|
+
* leap years) and no script can change that, so a date's month and day are
|
|
27
|
+
* always the engine's and only read differently.
|
|
28
|
+
*/
|
|
29
|
+
months?: string[];
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
export const
|
|
32
|
+
/** Days per engine month; what a script date's day is bounded by. */
|
|
33
|
+
export const ENGINE_MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
34
|
+
|
|
35
|
+
export const GREGORIAN_MONTHS: string[] = [
|
|
36
36
|
"January",
|
|
37
37
|
"February",
|
|
38
38
|
"March",
|
|
@@ -45,9 +45,10 @@ export const GREGORIAN_MONTHS: CalendarMonth[] = [
|
|
|
45
45
|
"October",
|
|
46
46
|
"November",
|
|
47
47
|
"December",
|
|
48
|
-
]
|
|
48
|
+
];
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
/** The month names a date reads with: the mod's twelve, or the engine's. */
|
|
51
|
+
export function monthNames(cal: CalendarSetting): string[] {
|
|
51
52
|
return cal.months ?? GREGORIAN_MONTHS;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -69,17 +70,19 @@ export function sanitizeCalendar(raw: unknown): CalendarSetting | undefined {
|
|
|
69
70
|
// collision would resolve silently to the wrong one: not a usable calendar.
|
|
70
71
|
if (cal.before && cal.before.toLowerCase() === cal.after.toLowerCase()) return undefined;
|
|
71
72
|
if (Array.isArray(o.months) && o.months.length > 0) {
|
|
72
|
-
|
|
73
|
+
// Exactly the engine's twelve. A name may still arrive as the older
|
|
74
|
+
// `{ name, days }` object; its day count never meant anything to the
|
|
75
|
+
// game and is dropped.
|
|
76
|
+
if (o.months.length !== GREGORIAN_MONTHS.length) return undefined;
|
|
77
|
+
const months: string[] = [];
|
|
73
78
|
const seen = new Set<string>();
|
|
74
79
|
for (const m of o.months) {
|
|
75
|
-
|
|
76
|
-
const { name, days } = m as Record<string, unknown>;
|
|
80
|
+
const name = typeof m === "string" ? m : ((m as Record<string, unknown> | null)?.name ?? null);
|
|
77
81
|
if (typeof name !== "string" || name.trim() === "") return undefined;
|
|
78
|
-
if (typeof days !== "number" || !Number.isInteger(days) || days < 1 || days > 999) return undefined;
|
|
79
82
|
const key = name.trim().toLowerCase();
|
|
80
83
|
if (seen.has(key)) return undefined;
|
|
81
84
|
seen.add(key);
|
|
82
|
-
months.push(
|
|
85
|
+
months.push(name.trim());
|
|
83
86
|
}
|
|
84
87
|
cal.months = months;
|
|
85
88
|
}
|
|
@@ -93,10 +96,9 @@ export function parseScriptDate(text: string): { y: number; m: number; d: number
|
|
|
93
96
|
return { y: Number(match[1]), m: Number(match[2]), d: Number(match[3]) };
|
|
94
97
|
}
|
|
95
98
|
|
|
96
|
-
/**
|
|
97
|
-
export function isValidScriptDate(
|
|
98
|
-
|
|
99
|
-
return y >= 1 && m >= 1 && m <= months.length && d >= 1 && d <= months[m - 1].days;
|
|
99
|
+
/** A date the engine reads: a positive year, one of its twelve months, a day that month has. */
|
|
100
|
+
export function isValidScriptDate(y: number, m: number, d: number): boolean {
|
|
101
|
+
return y >= 1 && m >= 1 && m <= ENGINE_MONTH_DAYS.length && d >= 1 && d <= ENGINE_MONTH_DAYS[m - 1];
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
/** Era-mapped year: "1000 BC". Null for pre-epoch years of a single-era calendar. */
|
|
@@ -111,11 +113,11 @@ export function displayYear(cal: CalendarSetting, y: number): string | null {
|
|
|
111
113
|
* date does not fit the calendar.
|
|
112
114
|
*/
|
|
113
115
|
export function displayDate(cal: CalendarSetting, y: number, m: number, d: number): string | null {
|
|
114
|
-
if (!isValidScriptDate(
|
|
116
|
+
if (!isValidScriptDate(y, m, d)) return null;
|
|
115
117
|
const year = displayYear(cal, y);
|
|
116
118
|
if (!year) return null;
|
|
117
119
|
if (m === 1 && d === 1) return year;
|
|
118
|
-
return `${d} ${
|
|
120
|
+
return `${d} ${monthNames(cal)[m - 1]} ${year}`;
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
export type ConvertResult = { ok: true; script: string; display: string } | { ok: false; error: string };
|
|
@@ -123,12 +125,10 @@ export type ConvertResult = { ok: true; script: string; display: string } | { ok
|
|
|
123
125
|
/** Case-insensitive month lookup: exact name, else unique prefix. */
|
|
124
126
|
function monthByName(cal: CalendarSetting, text: string): number | null {
|
|
125
127
|
const needle = text.toLowerCase();
|
|
126
|
-
const months =
|
|
127
|
-
const exact = months.findIndex((m) => m.
|
|
128
|
+
const months = monthNames(cal);
|
|
129
|
+
const exact = months.findIndex((m) => m.toLowerCase() === needle);
|
|
128
130
|
if (exact >= 0) return exact + 1;
|
|
129
|
-
const prefixed = months
|
|
130
|
-
.map((m, i) => ({ m, i }))
|
|
131
|
-
.filter(({ m }) => m.name.toLowerCase().startsWith(needle));
|
|
131
|
+
const prefixed = months.map((m, i) => ({ m, i })).filter(({ m }) => m.toLowerCase().startsWith(needle));
|
|
132
132
|
return prefixed.length === 1 ? prefixed[0].i + 1 : null;
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -174,11 +174,10 @@ export function convertDisplayInput(cal: CalendarSetting, input: string): Conver
|
|
|
174
174
|
if (year < 1) return { ok: false, error: "years start at 1 (no year zero)" };
|
|
175
175
|
const y = era === cal.after ? year + cal.epoch - 1 : cal.epoch - year;
|
|
176
176
|
if (y < 1) return { ok: false, error: `${year} ${era} is before script year 1 (epoch ${cal.epoch})` };
|
|
177
|
-
if (!isValidScriptDate(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
: { ok: false, error: `this calendar has ${months.length} months` };
|
|
177
|
+
if (!isValidScriptDate(y, m, d)) {
|
|
178
|
+
return m >= 1 && m <= ENGINE_MONTH_DAYS.length
|
|
179
|
+
? { ok: false, error: `${monthNames(cal)[m - 1]} has ${ENGINE_MONTH_DAYS[m - 1]} days` }
|
|
180
|
+
: { ok: false, error: `the game has ${ENGINE_MONTH_DAYS.length} months` };
|
|
182
181
|
}
|
|
183
182
|
return { ok: true, script: `${y}.${m}.${d}`, display: displayDate(cal, y, m, d)! };
|
|
184
183
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-mod calendar declaration: `<mod>/.px-toolkit/calendar.json`, the
|
|
3
|
+
* JSON form of calendar.ts `CalendarSetting`. A display calendar is a fact
|
|
4
|
+
* about the mod, so it travels with the mod (committed, one per mod, read by
|
|
5
|
+
* every client and by the server itself) instead of living in one editor's
|
|
6
|
+
* window-scoped `px.calendar` setting. The setting stays as the fallback for
|
|
7
|
+
* a mod without the file.
|
|
8
|
+
*
|
|
9
|
+
* No `vscode` imports: unit-tested in plain Node.
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import { sanitizeCalendar, type CalendarSetting } from "./calendar";
|
|
14
|
+
import { migrateConfigDir, resolveConfigDir, type ConfigDirNames } from "./configDir";
|
|
15
|
+
|
|
16
|
+
export const CALENDAR_FILE = "calendar.json";
|
|
17
|
+
|
|
18
|
+
export interface CalendarFile {
|
|
19
|
+
/** Where the declaration was read from (or would be written to). */
|
|
20
|
+
file: string;
|
|
21
|
+
/** The declared calendar, when the file parses and sanitizes. */
|
|
22
|
+
calendar?: CalendarSetting;
|
|
23
|
+
/** Why an existing file yields no calendar: unparsable JSON or an unusable shape. */
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The path the file is read from: the mod's config dir (legacy name included). */
|
|
28
|
+
export function calendarFilePath(modRoot: string, names: ConfigDirNames): string {
|
|
29
|
+
return path.join(resolveConfigDir(modRoot, names), CALENDAR_FILE);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read `<mod>/.px-toolkit/calendar.json`. Null when the file does not exist;
|
|
34
|
+
* a `CalendarFile` without `calendar` when it exists but is not usable, so a
|
|
35
|
+
* client can say so instead of silently showing no dates.
|
|
36
|
+
*/
|
|
37
|
+
export function readCalendarFile(modRoot: string, names: ConfigDirNames): CalendarFile | null {
|
|
38
|
+
const file = calendarFilePath(modRoot, names);
|
|
39
|
+
let text: string;
|
|
40
|
+
try {
|
|
41
|
+
text = fs.readFileSync(file, "utf8");
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
let raw: unknown;
|
|
46
|
+
try {
|
|
47
|
+
raw = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return { file, error: `not valid JSON (${(err as Error).message})` };
|
|
50
|
+
}
|
|
51
|
+
const calendar = sanitizeCalendar(raw);
|
|
52
|
+
if (!calendar) {
|
|
53
|
+
return {
|
|
54
|
+
file,
|
|
55
|
+
error:
|
|
56
|
+
'not a usable calendar: needs a whole-number "epoch" (1 or more), a non-empty "after" era label, ' +
|
|
57
|
+
'a "before" label different from "after" when present, and, when "months" is given, exactly twelve distinct names',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return { file, calendar };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Write the declaration into the mod (renaming a legacy config dir first,
|
|
65
|
+
* like every other config-dir write). Returns the file path.
|
|
66
|
+
*/
|
|
67
|
+
export function writeCalendarFile(modRoot: string, names: ConfigDirNames, cal: CalendarSetting): string {
|
|
68
|
+
const dir = migrateConfigDir(modRoot, names);
|
|
69
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
70
|
+
const file = path.join(dir, CALENDAR_FILE);
|
|
71
|
+
fs.writeFileSync(file, JSON.stringify(cal, null, 2) + "\n", "utf8");
|
|
72
|
+
return file;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** True when `fsPath` is a calendar declaration file (any config dir name). */
|
|
76
|
+
export function isCalendarFile(fsPath: string, names: ConfigDirNames): boolean {
|
|
77
|
+
const parts = fsPath.split(/[\\/]/);
|
|
78
|
+
if (parts.length < 2 || parts[parts.length - 1].toLowerCase() !== CALENDAR_FILE) return false;
|
|
79
|
+
const dir = parts[parts.length - 2].toLowerCase();
|
|
80
|
+
return dir === names.configDirName.toLowerCase() || dir === names.legacyConfigDirName?.toLowerCase();
|
|
81
|
+
}
|
package/src/calendarLoc.ts
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* from the GameProfile, so no game knowledge lives here.
|
|
11
11
|
*/
|
|
12
12
|
import type { CalendarSetting } from "./calendar";
|
|
13
|
-
import { monthsOf } from "./calendar";
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* The loc keys a game formats dates through, verified against the game's
|
|
@@ -128,24 +127,18 @@ export function generateCalendarLoc(
|
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
if (cal.months) {
|
|
131
|
-
const months =
|
|
130
|
+
const months = cal.months;
|
|
132
131
|
if (!spec.monthKeys) {
|
|
133
132
|
notes.push("This game's month-name keys are not mapped yet; custom month names were not generated.");
|
|
134
|
-
} else if (months.length !== spec.monthKeys.length) {
|
|
135
|
-
notes.push(
|
|
136
|
-
`The engine has exactly ${spec.monthKeys.length} months; your calendar declares ${months.length}, ` +
|
|
137
|
-
"so month names were not generated (the month count itself cannot be modded)."
|
|
138
|
-
);
|
|
139
133
|
} else {
|
|
134
|
+
// The calendar carries the engine's twelve names (sanitizeCalendar); the
|
|
135
|
+
// spec lists the keys of the months it knows, first month first.
|
|
140
136
|
overrideLines.push(" # Engine month names (long and abbreviated forms both get the custom name).");
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
overrideLines.push(` ${long}:0 "${
|
|
144
|
-
if (short !== long) overrideLines.push(` ${short}:0 "${
|
|
137
|
+
spec.monthKeys.forEach(([long, short], i) => {
|
|
138
|
+
const name = months[i].replace(/"/g, '\\"');
|
|
139
|
+
overrideLines.push(` ${long}:0 "${name}"`);
|
|
140
|
+
if (short !== long) overrideLines.push(` ${short}:0 "${name}"`);
|
|
145
141
|
});
|
|
146
|
-
notes.push(
|
|
147
|
-
"Month day counts are engine-fixed (31/28/31...); custom day counts only affect the editor's display."
|
|
148
|
-
);
|
|
149
142
|
}
|
|
150
143
|
}
|
|
151
144
|
|
package/src/configDir.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The toolkit's per-mod config dir: `<mod>/.px-toolkit/`, holding
|
|
3
|
+
* `workshop.json`, `schema.json`, `playset.json`, the tiger baseline, the GUI
|
|
4
|
+
* preview values and the Workshop listing folder. Mods created before 0.4.0
|
|
5
|
+
* have a per-game name instead (each GameMeta's `legacyConfigDirName`);
|
|
6
|
+
* reads keep finding it, and the first write renames it.
|
|
7
|
+
*
|
|
8
|
+
* No `vscode` imports: unit-tested in plain Node.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
|
|
13
|
+
export const PX_CONFIG_DIR = ".px-toolkit";
|
|
14
|
+
|
|
15
|
+
export interface ConfigDirNames {
|
|
16
|
+
configDirName: string;
|
|
17
|
+
/** The pre-0.4.0 per-game name, still read as a fallback. */
|
|
18
|
+
legacyConfigDirName?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The config dir to READ from: the current name when it exists, else the
|
|
23
|
+
* legacy one when that exists, else the current name. Never touches disk.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveConfigDir(root: string, names: ConfigDirNames): string {
|
|
26
|
+
const current = path.join(root, names.configDirName);
|
|
27
|
+
if (!names.legacyConfigDirName || fs.existsSync(current)) return current;
|
|
28
|
+
const legacy = path.join(root, names.legacyConfigDirName);
|
|
29
|
+
return fs.existsSync(legacy) ? legacy : current;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The config dir to WRITE to. Renames a legacy dir to the current name first;
|
|
34
|
+
* if the rename fails (locked file, read-only parent) the legacy dir stays in
|
|
35
|
+
* use so the write still lands where reads look.
|
|
36
|
+
*/
|
|
37
|
+
export function migrateConfigDir(root: string, names: ConfigDirNames): string {
|
|
38
|
+
const current = path.join(root, names.configDirName);
|
|
39
|
+
const resolved = resolveConfigDir(root, names);
|
|
40
|
+
if (resolved === current) return current;
|
|
41
|
+
try {
|
|
42
|
+
fs.renameSync(resolved, current);
|
|
43
|
+
return current;
|
|
44
|
+
} catch {
|
|
45
|
+
return resolved;
|
|
46
|
+
}
|
|
47
|
+
}
|