From 03e1a822e4b97c5a1d59f810058a5d5d1f385c83 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 14:07:46 -0700 Subject: [PATCH 01/15] * --- ...eps-callable.js => patchsteps-callable.ts} | 18 ++++++--- ...{patchsteps-diff.js => patchsteps-diff.ts} | 28 ++++++++++--- ...atchsteps-patch.js => patchsteps-patch.ts} | 39 +++++++++++++------ ...atchsteps-utils.js => patchsteps-utils.ts} | 6 +-- .../src/{patchsteps.js => patchsteps.ts} | 0 5 files changed, 67 insertions(+), 24 deletions(-) rename common/vendor-libs/patch-steps-lib/src/{patchsteps-callable.js => patchsteps-callable.ts} (79%) rename common/vendor-libs/patch-steps-lib/src/{patchsteps-diff.js => patchsteps-diff.ts} (92%) rename common/vendor-libs/patch-steps-lib/src/{patchsteps-patch.js => patchsteps-patch.ts} (96%) rename common/vendor-libs/patch-steps-lib/src/{patchsteps-utils.js => patchsteps-utils.ts} (90%) rename common/vendor-libs/patch-steps-lib/src/{patchsteps.js => patchsteps.ts} (100%) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.js b/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts similarity index 79% rename from common/vendor-libs/patch-steps-lib/src/patchsteps-callable.js rename to common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts index 32747f0..07ef011 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.js +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts @@ -7,17 +7,25 @@ import {appliers, DebugState} from "./patchsteps-patch.js"; * @property {(fromGame: boolean| string, path: string) => Promise} * @property {DebugState} debugState * @property {boolean} debug - * / + */ +export interface State { + currentValue: unknown, + stack: unknown[], + debugState: DebugState, + debug: boolean +} /** * A user defined step that is distinguishable from builtin PatchSteps. * Errors that occur in callables are not handled by the PatchSteps interpreter. * - * @async + * @async * @callback Callable * @param {State} state is the internal PatchStep state. * @param {unknown} args is the user supplied arguments. */ +export type Callable = (state: State, args: unknown) => Promise; + /* @type {Map} */ const callables = new Map; @@ -26,7 +34,7 @@ const callables = new Map; * @param {string} id * @param {Callable} callable */ -export function register(id, callable) { +export function register(id: string, callable: Callable) { if (typeof id !== "string") { throw Error('Id must be a string'); } @@ -44,7 +52,7 @@ export function register(id, callable) { callables.set(id, callable); } -appliers["CALL"] = async function(state) { +appliers["CALL"] = async function(state: State) { const id = this["id"]; const args = this["args"]; @@ -66,7 +74,7 @@ appliers["CALL"] = async function(state) { if (e !== state.debugState) { // So they know what happened console.error(e); - state.debugState.throwError('ValueError', `Callable ${i} did not properly throw an error.`); + state.debugState.throwError('ValueError', `Callable ${id} did not properly throw an error.`); } // They properly threw the error throw e; diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.js b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts similarity index 92% rename from common/vendor-libs/patch-steps-lib/src/patchsteps-diff.js rename to common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index ccd7a1d..8099437 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.js +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -14,6 +14,24 @@ import {photocopy, photomerge} from "./patchsteps-utils.js"; +// https://github.com/dmitmel/ultimate-crosscode-typedefs/blob/master/patch-steps-lib.d.ts +export type DiffCore = (a: unknown, b: unknown, settings: DiffSettings) => AnyPatchStep[] | null; + +export interface DiffSettings { + arrayTrulyDifferentThreshold: number; + trulyDifferentThreshold: number; + arrayLookahead: number; + diffAddNewKey: number; + diffAddDelKey: number; + diffMulSameKey: number; + + diffCore: DiffCore; + comment?: string; + commentValue?: string; + path: Index[]; + optimize: boolean; +} + /** * A difference heuristic. * @param {any} a The first value to check. @@ -21,7 +39,7 @@ import {photocopy, photomerge} from "./patchsteps-utils.js"; * @param {any} settings The involved control settings. * @returns {number} A difference value from 0 (same) to 1 (different). */ -function diffHeuristic(a, b, settings) { +function diffHeuristic(a: unknown, b: unknown, settings: Partial) { if ((a === null) && (b === null)) return 0; if ((a === null) || (b === null)) @@ -91,7 +109,7 @@ function diffHeuristic(a, b, settings) { * The actual implementation is different to this description, but follows the same rules. * Stack A and the output are the same. */ -function diffArrayHeuristic(a, b, settings) { +function diffArrayHeuristic(a: unknown, b: unknown, settings: Partial) { const lookahead = settings.arrayLookahead; let sublog = []; let ia = 0; @@ -132,13 +150,13 @@ function diffArrayHeuristic(a, b, settings) { /** * Diffs two objects. This is actually an outer wrapper, which provides default settings along with optimization. - * + * * @param {any} a The original value * @param {any} b The target value * @param {object} [settings] Optional bunch of settings. May include "comment". * @return {object[]|null} Null if unpatchable (this'll never occur for two Objects or two Arrays), Array of JSON-ready Patch Steps otherwise */ -export function diff(a, b, settings) { +export function diff(a: unknown, b: unknown, settings: Partial) { let trueSettings = photocopy(defaultSettings); if (settings !== void 0) photomerge(trueSettings, settings); @@ -213,7 +231,7 @@ export function diffEnterLevel(a, b, index, settings) { } // This is the default diffCore. -function diffInterior(a, b, settings) { +function diffInterior(a: unknown, b: unknown, settings) { if ((a === null) && (b === null)) return []; if ((a === null) || (b === null)) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.js b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts similarity index 96% rename from common/vendor-libs/patch-steps-lib/src/patchsteps-patch.js rename to common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index 204715a..a56b633 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.js +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -36,10 +36,27 @@ import {photocopy, photomerge} from "./patchsteps-utils.js"; * errorMessage: string; * }; */ +export type ParsedPath = null | [fromGame: true | false | string, url: string]; +export interface StackEntryError { + type: "Error"; + errorType: string; + errorMessage: string; +} +export interface StackEntryStep { + type: "Step" +} +export type StackEntry = StackEntryStep | StackEntryError; +export interface FileInfo { + path: string; + stack: StackEntry[]; +} // Error handling for appliers. // You are expected to subclass this class if you want additional functionality. export class DebugState { + fileStack: FileInfo[]; + currentFile: FileInfo | null; + // The constructor. The default state of a DebugState is invalid; a file must be added (even if null) to make it valid. constructor() { // FileInfo[] @@ -52,7 +69,7 @@ export class DebugState { * Translates a ParsedPath into a string. * Overridable. */ - translateParsedPath(parsedPath) { + translateParsedPath(parsedPath: ParsedPath) { if (parsedPath === null) return "(unknown file)"; // By default, we know nothing. @@ -70,7 +87,7 @@ export class DebugState { * Enters a file by parsedPath. Do not override. * @final */ - addFile(parsedPath) { + addFile(parsedPath: ParsedPath) { const path = this.translateParsedPath(parsedPath); const fileInfo = { path, @@ -84,12 +101,12 @@ export class DebugState { * Removes a pushed file. * @final */ - removeLastFile() { + removeLastFile(): FileInfo { const lastFile = this.fileStack.pop(); this.currentFile = this.fileStack[this.fileStack.length - 1]; return lastFile; } - + /** * Enters a step. Note that calls to this *surround* applyStep as the index is not available to it. * @final @@ -118,7 +135,7 @@ export class DebugState { } return currentStep; } - + /** * Gets the last (i.e. current) step. * @final @@ -134,7 +151,7 @@ export class DebugState { } return currentStep; } - + /** * Throws this instance as an error. * @final @@ -175,7 +192,7 @@ export class DebugState { } console.log(message); } - + /** * Prints information about the whole stack. * @final @@ -191,7 +208,7 @@ export class DebugState { * Overridable. */ async beforeStep() { - + } /** @@ -199,7 +216,7 @@ export class DebugState { * Overridable. */ async afterStep() { - + } } @@ -381,7 +398,7 @@ appliers["PASTE"] = async function(state) { type: "ADD_ARRAY_ELEMENT", content: value }; - + if (!isNaN(this["index"])) { obj.index = this["index"]; } @@ -419,7 +436,7 @@ appliers["ENTER"] = async function (state) { const subArr = path.slice(0, i + 1); state.debugState.throwError('Error', `index sequence ${subArr.join(",")} leads to an undefined state.`); } - + state.currentValue = state.currentValue[idx]; } }; diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.js b/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts similarity index 90% rename from common/vendor-libs/patch-steps-lib/src/patchsteps-utils.js rename to common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts index 5a03aec..70d8798 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.js +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts @@ -14,7 +14,7 @@ * @param {any} b The value to merge from. * @returns {any} a */ -export function photomerge(a, b) { +export function photomerge(a: A, b: B): A & B { if (b.constructor === Object) { for (let k in b) a[photocopy(k)] = photocopy(b[k]); @@ -29,10 +29,10 @@ export function photomerge(a, b) { /** * A generic copy function. - * @param {any} a The value to copy. + * @param {any} o The value to copy. * @returns {any} copied value */ -export function photocopy(o) { +export function photocopy(o: O): O { if (o) { if (o.constructor === Array) return photomerge([], o); diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps.js b/common/vendor-libs/patch-steps-lib/src/patchsteps.ts similarity index 100% rename from common/vendor-libs/patch-steps-lib/src/patchsteps.js rename to common/vendor-libs/patch-steps-lib/src/patchsteps.ts From e812aa2c88896843bd23338b0524139c03de1ec7 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 14:19:13 -0700 Subject: [PATCH 02/15] * --- common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts | 3 ++- .../vendor-libs/patch-steps-lib/src/patchsteps-patch.ts | 8 ++++++-- common/vendor-libs/patch-steps-lib/src/types.ts | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 common/vendor-libs/patch-steps-lib/src/types.ts diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index 8099437..58c3fd2 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -13,6 +13,7 @@ */ import {photocopy, photomerge} from "./patchsteps-utils.js"; +import { Index } from './types.js' // https://github.com/dmitmel/ultimate-crosscode-typedefs/blob/master/patch-steps-lib.d.ts export type DiffCore = (a: unknown, b: unknown, settings: DiffSettings) => AnyPatchStep[] | null; @@ -221,7 +222,7 @@ export function diffApplyComment(step, settings) { * @param {object} settings Settings. * @return {object[]|null} See diff for more details */ -export function diffEnterLevel(a, b, index, settings) { +export function diffEnterLevel(a: unknown, b: unknown, index: Index, settings: DiffSettings): AnyPatchStep[] | null { settings.path.push(index); if (settings.comment !== void 0) settings.commentValue = settings.comment + "." + settings.path.join("."); diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index a56b633..28f6dd7 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -13,6 +13,7 @@ */ import {photocopy, photomerge} from "./patchsteps-utils.js"; +import {Index} from './types.js' // The following are definitions used for reference in DebugState. /* @@ -43,7 +44,9 @@ export interface StackEntryError { errorMessage: string; } export interface StackEntryStep { - type: "Step" + type: "Step", + index: Index; + name: string; } export type StackEntry = StackEntryStep | StackEntryError; export interface FileInfo { @@ -220,10 +223,11 @@ export class DebugState { } } +export type Applier = (this: StackEntryStep, state: ApplierState) => Promise; // Custom extensions are registered here. // Their 'this' is the Step, they are passed the state, and they are expected to return a Promise. // In practice this is done with async old-style functions. -export const appliers = {}; +export const appliers: Record = {}; /* * @param {any} a The object to modify diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts new file mode 100644 index 0000000..cd5c79c --- /dev/null +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -0,0 +1 @@ +export type Index = string | number; From 35b33841f0c5ab7fe64437998362c3bacd29c86a Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 14:28:14 -0700 Subject: [PATCH 03/15] * --- .../patch-steps-lib/src/patchsteps-diff.ts | 2 +- .../patch-steps-lib/src/patchsteps-patch.ts | 29 +++++++++++++------ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index 58c3fd2..29c8d1d 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -232,7 +232,7 @@ export function diffEnterLevel(a: unknown, b: unknown, index: Index, settings: D } // This is the default diffCore. -function diffInterior(a: unknown, b: unknown, settings) { +function diffInterior(a: unknown, b: unknown, settings: DiffSettings) { if ((a === null) && (b === null)) return []; if ((a === null) || (b === null)) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index 28f6dd7..ee4da6f 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -58,7 +58,7 @@ export interface FileInfo { // You are expected to subclass this class if you want additional functionality. export class DebugState { fileStack: FileInfo[]; - currentFile: FileInfo | null; + currentFile: FileInfo; // The constructor. The default state of a DebugState is invalid; a file must be added (even if null) to make it valid. constructor() { @@ -107,14 +107,14 @@ export class DebugState { removeLastFile(): FileInfo { const lastFile = this.fileStack.pop(); this.currentFile = this.fileStack[this.fileStack.length - 1]; - return lastFile; + return lastFile!; } /** * Enters a step. Note that calls to this *surround* applyStep as the index is not available to it. * @final */ - addStep(index, name = "") { + addStep(index: Index, name = "") { this.currentFile.stack.push({ type: "Step", index, @@ -143,7 +143,7 @@ export class DebugState { * Gets the last (i.e. current) step. * @final */ - getLastStep() { + getLastStep(): StackEntryStep { const stack = this.currentFile.stack; let currentStep = null; for(let index = stack.length - 1; index >= 0; index--) { @@ -152,14 +152,14 @@ export class DebugState { index = -1; } } - return currentStep; + return currentStep as StackEntryStep; } /** * Throws this instance as an error. * @final */ - throwError(type, message) { + throwError(type: string, message: string) { this.currentFile.stack.push({ type: "Error", errorType: type, @@ -172,7 +172,7 @@ export class DebugState { * Prints information about a specific file on the stack. * Overridable. */ - printFileInfo(file) { + printFileInfo(file: FileInfo) { console.log(`File %c${file.path}`, 'red'); let message = ''; const stack = file.stack; @@ -223,6 +223,17 @@ export class DebugState { } } +type Loader = (fromGame: boolean | string, path: string) => Promise; + +export interface ApplierState { + currentValue: unknown; + stack: StackEntry[]; + cloneMap: Map; + loader: Loader; + debugState: DebugState; + debug: boolean; +} + export type Applier = (this: StackEntryStep, state: ApplierState) => Promise; // Custom extensions are registered here. // Their 'this' is the Step, they are passed the state, and they are expected to return a Promise. @@ -243,7 +254,7 @@ export const appliers: Record = {}; * If not given, will be created. You need to pass your own instance of this to have proper filename tracking. * @return {Promise} A Promise */ -export async function patch(a, steps, loader, debugState) { +export async function patch(a: unknown, steps, loader: Loader, debugState?: DebugState) { if (!debugState) { debugState = new DebugState(); debugState.addFile(null); @@ -287,7 +298,7 @@ export async function patch(a, steps, loader, debugState) { } } -async function applyStep(step, state) { +async function applyStep(step, state: ApplierState) { await state.debugState.beforeStep(); state.debugState.getLastStep().name = step["type"]; if (!appliers[step["type"]]) { From 78de02b6300571c381d7c3b761480fceaebddc05 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 14:55:53 -0700 Subject: [PATCH 04/15] * --- .../patch-steps-lib/src/patchsteps-diff.ts | 20 +-- .../patch-steps-lib/src/patchsteps-patch.ts | 50 ++---- .../patch-steps-lib/src/patchsteps-utils.ts | 14 +- .../vendor-libs/patch-steps-lib/src/types.ts | 159 ++++++++++++++++++ 4 files changed, 183 insertions(+), 60 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index 29c8d1d..0f61649 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -13,25 +13,7 @@ */ import {photocopy, photomerge} from "./patchsteps-utils.js"; -import { Index } from './types.js' - -// https://github.com/dmitmel/ultimate-crosscode-typedefs/blob/master/patch-steps-lib.d.ts -export type DiffCore = (a: unknown, b: unknown, settings: DiffSettings) => AnyPatchStep[] | null; - -export interface DiffSettings { - arrayTrulyDifferentThreshold: number; - trulyDifferentThreshold: number; - arrayLookahead: number; - diffAddNewKey: number; - diffAddDelKey: number; - diffMulSameKey: number; - - diffCore: DiffCore; - comment?: string; - commentValue?: string; - path: Index[]; - optimize: boolean; -} +import {AnyPatchStep, DiffSettings, Index} from './types.js' /** * A difference heuristic. diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index ee4da6f..f7e5cfc 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -13,7 +13,17 @@ */ import {photocopy, photomerge} from "./patchsteps-utils.js"; -import {Index} from './types.js' +import { + AnyPatchStep, + Applier, + Appliers, + ApplierState, + FileInfo, + Index, + ParsedPath, + PatchFile, + StackEntryStep +} from './types.js' // The following are definitions used for reference in DebugState. /* @@ -37,22 +47,6 @@ import {Index} from './types.js' * errorMessage: string; * }; */ -export type ParsedPath = null | [fromGame: true | false | string, url: string]; -export interface StackEntryError { - type: "Error"; - errorType: string; - errorMessage: string; -} -export interface StackEntryStep { - type: "Step", - index: Index; - name: string; -} -export type StackEntry = StackEntryStep | StackEntryError; -export interface FileInfo { - path: string; - stack: StackEntry[]; -} // Error handling for appliers. // You are expected to subclass this class if you want additional functionality. @@ -223,22 +217,10 @@ export class DebugState { } } -type Loader = (fromGame: boolean | string, path: string) => Promise; - -export interface ApplierState { - currentValue: unknown; - stack: StackEntry[]; - cloneMap: Map; - loader: Loader; - debugState: DebugState; - debug: boolean; -} - -export type Applier = (this: StackEntryStep, state: ApplierState) => Promise; // Custom extensions are registered here. // Their 'this' is the Step, they are passed the state, and they are expected to return a Promise. // In practice this is done with async old-style functions. -export const appliers: Record = {}; +export const appliers: Appliers = {}; /* * @param {any} a The object to modify @@ -254,7 +236,7 @@ export const appliers: Record = {}; * If not given, will be created. You need to pass your own instance of this to have proper filename tracking. * @return {Promise} A Promise */ -export async function patch(a: unknown, steps, loader: Loader, debugState?: DebugState) { +export async function patch(a: unknown, steps: PatchFile, loader: Loader, debugState?: DebugState) { if (!debugState) { debugState = new DebugState(); debugState.addFile(null); @@ -298,7 +280,7 @@ export async function patch(a: unknown, steps, loader: Loader, debugState?: Debu } } -async function applyStep(step, state: ApplierState) { +async function applyStep(step: AnyPatchStep, state: ApplierState) { await state.debugState.beforeStep(); state.debugState.getLastStep().name = step["type"]; if (!appliers[step["type"]]) { @@ -309,7 +291,7 @@ async function applyStep(step, state: ApplierState) { await state.debugState.afterStep(); } -function replaceObjectProperty(object, key, keyword, value) { +function replaceObjectProperty(object: Record, key: string, keyword: string, value) { let oldValue = object[key]; // It's more complex than we thought. if (!Array.isArray(keyword) && typeof keyword === "object") { @@ -493,7 +475,7 @@ appliers["ADD_ARRAY_ELEMENT"] = async function (state) { }; // Reintroduced but simplified version of Emileyah's resolveUrl -function parsePath(url, fromGame) { +function parsePath(url: string, fromGame) { try { const decomposedUrl = new URL(url); const protocol = decomposedUrl.protocol; diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts index 70d8798..a6c4bab 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts @@ -15,16 +15,16 @@ * @returns {any} a */ export function photomerge(a: A, b: B): A & B { - if (b.constructor === Object) { - for (let k in b) - a[photocopy(k)] = photocopy(b[k]); - } else if (b.constructor == Array) { + if (Array.isArray(b)) { for (let i = 0; i < b.length; i++) - a.push(photocopy(b[i])); - } else { + (a as any[]).push(photocopy(b[i])); + } else if (b instanceof Object) { + for (let k in b) + (a as Record)[photocopy(k)] = photocopy((b as Record)[k]); + } else { throw new Error("We can't do that! ...Who'd clean up the mess?"); } - return a; + return a as A & B; } /** diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index cd5c79c..179106d 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -1 +1,160 @@ export type Index = string | number; + +export interface BasePatchStep { + comment?: string; +} + +export namespace PatchStep { + export interface ENTER extends BasePatchStep { + type: 'ENTER'; + index: Index | Index[]; + } + + export interface EXIT extends BasePatchStep { + type: 'EXIT'; + count?: number; + } + + export interface SET_KEY extends BasePatchStep { + type: 'SET_KEY'; + index: Index; + content?: unknown; + } + + export interface INIT_KEY extends BasePatchStep { + type: 'INIT_KEY'; + index: Index; + content: unknown; + } + + export interface REMOVE_ARRAY_ELEMENT extends BasePatchStep { + type: 'REMOVE_ARRAY_ELEMENT'; + index: number; + } + + export interface ADD_ARRAY_ELEMENT extends BasePatchStep { + type: 'ADD_ARRAY_ELEMENT'; + index?: number; + content: unknown; + } + + export interface IMPORT extends BasePatchStep { + type: 'IMPORT'; + src: string; + path?: Index[]; + index?: Index; + } + + export interface INCLUDE extends BasePatchStep { + type: 'INCLUDE'; + src: string; + } + + export interface FOR_IN extends BasePatchStep { + type: 'FOR_IN'; + values: unknown[] | Array>; + keyword: string | Record; + body: AnyPatchStep[]; + } + + export interface COPY extends BasePatchStep { + type: 'COPY'; + alias: string; + } + + export interface PASTE extends BasePatchStep { + type: 'PASTE'; + alias: string; + index?: Index; + } + + export interface COMMENT extends BasePatchStep { + type: 'COMMENT'; + value: unknown; + } + + export interface DEBUG extends BasePatchStep { + type: 'DEBUG'; + value: boolean; + } + + export interface MERGE_CONTENT extends BasePatchStep { + type: 'MERGE_CONTENT'; + content: unknown; + } + + export interface CALL extends BasePatchStep { + type: 'CALL'; + id: string; + args: unknown; + } +} + +export interface PatchStepsRegistry { + ENTER: PatchStep.ENTER; + EXIT: PatchStep.EXIT; + SET_KEY: PatchStep.SET_KEY; + INIT_KEY: PatchStep.INIT_KEY; + REMOVE_ARRAY_ELEMENT: PatchStep.REMOVE_ARRAY_ELEMENT; + ADD_ARRAY_ELEMENT: PatchStep.ADD_ARRAY_ELEMENT; + IMPORT: PatchStep.IMPORT; + INCLUDE: PatchStep.INCLUDE; + FOR_IN: PatchStep.FOR_IN; + COPY: PatchStep.COPY; + PASTE: PatchStep.PASTE; + COMMENT: PatchStep.COMMENT; + DEBUG: PatchStep.DEBUG; +} + +export type AnyPatchStep = Extract; +export type PatchFile = AnyPatchStep[] | Record; + +export type ParsedPath = null | [fromGame: true | false | string, url: string]; +export interface StackEntryError { + type: "Error"; + errorType: string; + errorMessage: string; +} +export interface StackEntryStep { + type: "Step", + index: Index; + name: string; +} +export type StackEntry = StackEntryStep | StackEntryError; +export interface FileInfo { + path: string; + stack: StackEntry[]; +} + +type Loader = (fromGame: boolean | string, path: string) => Promise; + +export interface ApplierState { + currentValue: unknown; + stack: StackEntry[]; + cloneMap: Map; + loader: Loader; + debugState: DebugState; + debug: boolean; +} + +export type Applier = (this: StackEntryStep & T, state: ApplierState) => Promise; +export type Appliers = { + [K in keyof PatchStepsRegistry]: Applier +} + +export type DiffCore = (a: unknown, b: unknown, settings: DiffSettings) => AnyPatchStep[] | null; + +export interface DiffSettings { + arrayTrulyDifferentThreshold: number; + trulyDifferentThreshold: number; + arrayLookahead: number; + diffAddNewKey: number; + diffAddDelKey: number; + diffMulSameKey: number; + + diffCore: DiffCore; + comment?: string; + commentValue?: string; + path: Index[]; + optimize: boolean; +} \ No newline at end of file From cd855b4634fe6eaae11a1850d704727b52c3d91a Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 15:17:18 -0700 Subject: [PATCH 05/15] * --- .../patch-steps-lib/src/patchsteps-diff.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index 0f61649..3d9c6a5 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -13,7 +13,7 @@ */ import {photocopy, photomerge} from "./patchsteps-utils.js"; -import {AnyPatchStep, DiffSettings, Index} from './types.js' +import {AnyPatchStep, BasePatchStep, DiffSettings, Index, PatchStep} from './types.js' /** * A difference heuristic. @@ -22,15 +22,15 @@ import {AnyPatchStep, DiffSettings, Index} from './types.js' * @param {any} settings The involved control settings. * @returns {number} A difference value from 0 (same) to 1 (different). */ -function diffHeuristic(a: unknown, b: unknown, settings: Partial) { - if ((a === null) && (b === null)) +function diffHeuristic(a: unknown, b: unknown, settings: DiffSettings): number { + if ((a === null) && (b === null) || (a === undefined) && (b === undefined)) return 0; - if ((a === null) || (b === null)) - return null; + if ((a === null) || (b === null) || (a === undefined) || (b === undefined)) + return 1; if (a.constructor !== b.constructor) return 1; - if (a.constructor === Array) { + if (Array.isArray(a) && Array.isArray(b)) { let array = diffArrayHeuristic(a, b, settings); if (array.length == 0) return 0; @@ -51,7 +51,7 @@ function diffHeuristic(a: unknown, b: unknown, settings: Partial) } } return changes / array.length; - } else if (a.constructor === Object) { + } else if (a.constructor === Object && b.constructor === Object) { let total = []; for (let k in a) total.push(k); @@ -65,7 +65,7 @@ function diffHeuristic(a: unknown, b: unknown, settings: Partial) } else if ((total[i] in b) && !(total[i] in a)) { change += settings.diffAddDelKey; } else { - change += diffHeuristic(a[total[i]], b[total[i]], settings) * settings.diffMulSameKey; + change += diffHeuristic((a as Record)[total[i]], (b as Record)[total[i]], settings) * settings.diffMulSameKey; } } if (total.length != 0) @@ -92,7 +92,7 @@ function diffHeuristic(a: unknown, b: unknown, settings: Partial) * The actual implementation is different to this description, but follows the same rules. * Stack A and the output are the same. */ -function diffArrayHeuristic(a: unknown, b: unknown, settings: Partial) { +function diffArrayHeuristic(a: unknown[], b: unknown[], settings: DiffSettings) { const lookahead = settings.arrayLookahead; let sublog = []; let ia = 0; @@ -190,7 +190,7 @@ export function diff(a: unknown, b: unknown, settings: Partial) { * @param {object} step The step to add to. * @param {object} settings The settings. */ -export function diffApplyComment(step, settings) { +export function diffApplyComment(step: T, settings: DiffSettings) { if (settings.commentValue !== void 0) step.comment = settings.commentValue; return step; @@ -215,15 +215,15 @@ export function diffEnterLevel(a: unknown, b: unknown, index: Index, settings: D // This is the default diffCore. function diffInterior(a: unknown, b: unknown, settings: DiffSettings) { - if ((a === null) && (b === null)) + if ((a === null) && (b === null) || (a === undefined) && (b === undefined)) return []; - if ((a === null) || (b === null)) + if ((a === null) || (b === null) || (a === undefined) || (b === undefined)) return null; if (a.constructor !== b.constructor) return null; - let log = []; + let log: AnyPatchStep[] = []; - if (a.constructor === Array) { + if (Array.isArray(a) && Array.isArray(b)) { let array = diffArrayHeuristic(a, b, settings); let ai = 0; let bi = 0; @@ -233,10 +233,10 @@ function diffInterior(a: unknown, b: unknown, settings: DiffSettings) { // At patch time, a[ai + x] for arbitrary 'x' is in the live array at [bi + x] for (let i = 0; i < array.length; i++) { if (array[i] == "POPA") { - log.push(diffApplyComment({"type": "REMOVE_ARRAY_ELEMENT", "index": bi}, settings)); + log.push(diffApplyComment({"type": "REMOVE_ARRAY_ELEMENT", "index": bi} as PatchStep.REMOVE_ARRAY_ELEMENT, settings)); ai++; } else if (array[i] == "INSERT") { - let insertion = diffApplyComment({"type": "ADD_ARRAY_ELEMENT", "index": bi, "content": photocopy(b[bi])}, settings); + let insertion = diffApplyComment({"type": "ADD_ARRAY_ELEMENT", "index": bi, "content": photocopy(b[bi])} as PatchStep.ADD_ARRAY_ELEMENT, settings); // Is this a set of elements being inserted at the end? let j; for (j = i + 1; j < array.length; j++) @@ -256,16 +256,16 @@ function diffInterior(a: unknown, b: unknown, settings: DiffSettings) { log.push({"type": "EXIT"}); } } else { - log.push(diffApplyComment({"type": "SET_KEY", "index": bi, "content": photocopy(b[bi])}, settings)); + log.push(diffApplyComment({"type": "SET_KEY", "index": bi, "content": photocopy(b[bi])} as PatchStep.SET_KEY, settings)); } ai++; bi++; } } - } else if (a.constructor === Object) { + } else if (a.constructor === Object && b.constructor === Object) { for (let k in a) { if (k in b) { - if (diffHeuristic(a[k], b[k], settings) >= settings.trulyDifferentThreshold) { + if (diffHeuristic((a as Record)[k], (b as Record)[k], settings) >= settings.trulyDifferentThreshold) { log.push(diffApplyComment({"type": "SET_KEY", "index": k, "content": photocopy(b[k])}, settings)); } else { let xd = diffEnterLevel(a[k], b[k], k, settings); From f624d4d4b155516632c7a324f876a05c0d52b4e0 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 15:27:54 -0700 Subject: [PATCH 06/15] * --- .../patch-steps-lib/src/patchsteps-diff.ts | 25 +++++++++++-------- .../vendor-libs/patch-steps-lib/src/types.ts | 4 +++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index 3d9c6a5..116b36d 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -13,7 +13,7 @@ */ import {photocopy, photomerge} from "./patchsteps-utils.js"; -import {AnyPatchStep, BasePatchStep, DiffSettings, Index, PatchStep} from './types.js' +import {AnyPatchStep, BasePatchStep, DiffSettings, Index, PatchFile, PatchStep, unsafeAssert} from './types.js' /** * A difference heuristic. @@ -147,10 +147,11 @@ export function diff(a: unknown, b: unknown, settings: Partial) { trueSettings.commentValue = trueSettings.comment; let result = trueSettings.diffCore(a, b, trueSettings); + if (!result) return null; if (trueSettings.optimize) { for (let i = 1; i < result.length; i++) { let here = result[i]; - let prev = result[i - 1]; + let prev: AnyPatchStep = result[i - 1]; let optimizedOut = false; if (here["type"] == "EXIT") { if (prev["type"] == "EXIT") { @@ -159,7 +160,8 @@ export function diff(a: unknown, b: unknown, settings: Partial) { here["count"] = 1; if (!("count" in prev)) prev["count"] = 1; - prev["count"] += here["count"]; + unsafeAssert(prev); + prev["count"] += here["count"]!; // Copy comments backwards to try and preserve the unoptimized autocommenter semantics if ("comment" in here) prev["comment"] = here["comment"]; @@ -168,9 +170,9 @@ export function diff(a: unknown, b: unknown, settings: Partial) { } else if (here["type"] == "ENTER") { if (prev["type"] == "ENTER") { // Crush ENTERs - if (prev["index"].constructor !== Array) + if (!Array.isArray(prev["index"])) prev["index"] = [prev["index"]]; - if (here["index"].constructor !== Array) + if (!Array.isArray(here["index"])) here["index"] = [here["index"]]; prev["index"] = prev["index"].concat(here["index"]); optimizedOut = true; @@ -265,8 +267,11 @@ function diffInterior(a: unknown, b: unknown, settings: DiffSettings) { } else if (a.constructor === Object && b.constructor === Object) { for (let k in a) { if (k in b) { - if (diffHeuristic((a as Record)[k], (b as Record)[k], settings) >= settings.trulyDifferentThreshold) { - log.push(diffApplyComment({"type": "SET_KEY", "index": k, "content": photocopy(b[k])}, settings)); + unsafeAssert>(a); + unsafeAssert>(b); + + if (diffHeuristic(a[k], b[k], settings) >= settings.trulyDifferentThreshold) { + log.push(diffApplyComment({"type": "SET_KEY", "index": k, "content": photocopy(b[k])} as PatchStep.SET_KEY, settings)); } else { let xd = diffEnterLevel(a[k], b[k], k, settings); if (xd != null) { @@ -277,16 +282,16 @@ function diffInterior(a: unknown, b: unknown, settings: DiffSettings) { } } else { // should it happen? probably not. will it happen? maybe - log.push(diffApplyComment({"type": "SET_KEY", "index": k, "content": photocopy(b[k])}, settings)); + log.push(diffApplyComment({"type": "SET_KEY", "index": k, "content": photocopy(b[k])} as PatchStep.SET_KEY, settings)); } } } else { - log.push(diffApplyComment({"type": "SET_KEY", "index": k}, settings)); + log.push(diffApplyComment({"type": "SET_KEY", "index": k} as PatchStep.SET_KEY, settings)); } } for (let k in b) if (!(k in a)) - log.push(diffApplyComment({"type": "SET_KEY", "index": k, "content": photocopy(b[k])}, settings)); + log.push(diffApplyComment({"type": "SET_KEY", "index": k, "content": photocopy((b as Record)[k])} as PatchStep.SET_KEY, settings)); } else if (a != b) { return null; } diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index 179106d..5ad0922 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -157,4 +157,8 @@ export interface DiffSettings { commentValue?: string; path: Index[]; optimize: boolean; +} + +export function unsafeAssert(val: any): asserts val is T { + return true; } \ No newline at end of file From 042a1f6eb32b09028223664e4a82de0eac9e78b1 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 15:50:44 -0700 Subject: [PATCH 07/15] * --- .../patch-steps-lib/src/patchsteps-patch.ts | 24 ++++++++++--------- .../vendor-libs/patch-steps-lib/src/types.ts | 6 ++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index f7e5cfc..e8042db 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -20,9 +20,10 @@ import { ApplierState, FileInfo, Index, + Loader, ParsedPath, PatchFile, - StackEntryStep + StackEntryStep, unsafeAssert } from './types.js' // The following are definitions used for reference in DebugState. @@ -220,7 +221,7 @@ export class DebugState { // Custom extensions are registered here. // Their 'this' is the Step, they are passed the state, and they are expected to return a Promise. // In practice this is done with async old-style functions. -export const appliers: Appliers = {}; +export const appliers: Appliers = {} as any; // TODO(lleyton): no. /* * @param {any} a The object to modify @@ -242,6 +243,9 @@ export async function patch(a: unknown, steps: PatchFile, loader: Loader, debugS debugState.addFile(null); } if (steps.constructor === Object) { + unsafeAssert>>(steps); + unsafeAssert>(a); + // Standardized Mods specification for (let k in steps) { // Switched back to a literal translation in 1.0.2 to make it make sense with spec, it's more awkward but simpler. @@ -257,6 +261,7 @@ export async function patch(a: unknown, steps: PatchFile, loader: Loader, debugS } return; } + unsafeAssert(steps); const state = { currentValue: a, stack: [], @@ -268,7 +273,7 @@ export async function patch(a: unknown, steps: PatchFile, loader: Loader, debugS for (let index = 0; index < steps.length; index++) { try { debugState.addStep(index); - await applyStep(steps[index], state, debugState); + await applyStep(steps[index], state); debugState.removeLastStep(); } catch(e) { debugState.print(); @@ -291,7 +296,7 @@ async function applyStep(step: AnyPatchStep, state: ApplierState) { await state.debugState.afterStep(); } -function replaceObjectProperty(object: Record, key: string, keyword: string, value) { +function replaceObjectProperty(object: O, key: keyof O, keyword: string | Record, value: string) { let oldValue = object[key]; // It's more complex than we thought. if (!Array.isArray(keyword) && typeof keyword === "object") { @@ -313,7 +318,7 @@ function replaceObjectProperty(object: Record, key: string, keyword * @param {String| {[replacementId]: string | number}} value The value the replace the match * @returns {void} * */ -function valueInsertion(obj, keyword, value) { +function valueInsertion(obj: Record, keyword: RegExp | {[replacementId: string]: RegExp}, value: string | {[replacementId: string]: string | number}) { if (Array.isArray(obj)) { for (let index = 0; index < obj.length; index++) { const child = obj[index]; @@ -393,12 +398,9 @@ appliers["PASTE"] = async function(state) { if (Array.isArray(state.currentValue)) { const obj = { type: "ADD_ARRAY_ELEMENT", - content: value + content: value, + ...(!isNaN(this["index"]) ? {index: this["index"]} : {}) }; - - if (!isNaN(this["index"])) { - obj.index = this["index"]; - } await applyStep(obj, state); } else if (typeof state.currentValue === "object") { await applyStep({ @@ -424,7 +426,7 @@ appliers["ENTER"] = async function (state) { } let path = [this["index"]]; - if (this["index"].constructor == Array) + if (Array.isArray(this["index"])) path = this["index"]; for (let i = 0; i < path.length;i++) { const idx = path[i]; diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index 5ad0922..6b8394c 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -126,7 +126,7 @@ export interface FileInfo { stack: StackEntry[]; } -type Loader = (fromGame: boolean | string, path: string) => Promise; +export type Loader = (fromGame: boolean | string, path: string) => Promise; export interface ApplierState { currentValue: unknown; @@ -159,6 +159,4 @@ export interface DiffSettings { optimize: boolean; } -export function unsafeAssert(val: any): asserts val is T { - return true; -} \ No newline at end of file +export function unsafeAssert(val: any): asserts val is T {} \ No newline at end of file From 187e047bebf4960653c23d8ae3538f4340a64acb Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 15:54:48 -0700 Subject: [PATCH 08/15] * --- .../patch-steps-lib/src/patchsteps-patch.ts | 11 ++++++++--- common/vendor-libs/patch-steps-lib/src/types.ts | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index e8042db..ab1b2f2 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -22,7 +22,7 @@ import { Index, Loader, ParsedPath, - PatchFile, + PatchFile, PatchStep, StackEntryStep, unsafeAssert } from './types.js' @@ -401,7 +401,7 @@ appliers["PASTE"] = async function(state) { content: value, ...(!isNaN(this["index"]) ? {index: this["index"]} : {}) }; - await applyStep(obj, state); + await applyStep(obj as PatchStep.ADD_ARRAY_ELEMENT, state); } else if (typeof state.currentValue === "object") { await applyStep({ type: "SET_KEY", @@ -457,6 +457,8 @@ appliers["SET_KEY"] = async function (state) { state.debugState.throwError('Error', 'index must be set.'); } + unsafeAssert>(state.currentValue); + if ("content" in this) { state.currentValue[this["index"]] = photocopy(this["content"]); } else { @@ -465,10 +467,13 @@ appliers["SET_KEY"] = async function (state) { }; appliers["REMOVE_ARRAY_ELEMENT"] = async function (state) { + unsafeAssert(state.currentValue); state.currentValue.splice(this["index"], 1); }; appliers["ADD_ARRAY_ELEMENT"] = async function (state) { + unsafeAssert(state.currentValue); + if ("index" in this) { state.currentValue.splice(this["index"], 0, photocopy(this["content"])); } else { @@ -477,7 +482,7 @@ appliers["ADD_ARRAY_ELEMENT"] = async function (state) { }; // Reintroduced but simplified version of Emileyah's resolveUrl -function parsePath(url: string, fromGame) { +function parsePath(url: string, fromGame: boolean) { try { const decomposedUrl = new URL(url); const protocol = decomposedUrl.protocol; diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index 6b8394c..a86daaa 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -104,6 +104,8 @@ export interface PatchStepsRegistry { PASTE: PatchStep.PASTE; COMMENT: PatchStep.COMMENT; DEBUG: PatchStep.DEBUG; + MERGE_CONTENT: PatchStep.MERGE_CONTENT; + CALL: PatchStep.CALL; } export type AnyPatchStep = Extract; From e4d7cf13ac4a972f8134e0e5d50dd46939a1eb44 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 16:03:05 -0700 Subject: [PATCH 09/15] * --- .../vendor-libs/patch-steps-lib/src/patchsteps-patch.ts | 9 ++++++--- common/vendor-libs/patch-steps-lib/src/types.ts | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index ab1b2f2..ebe23ea 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -518,10 +518,11 @@ appliers["IMPORT"] = async function (state) { let obj = await state.loader.apply(state, srcPath); if ("path" in this) { - if (!Array.isArray(this["path"])) { + const path = this["path"]; + if (!Array.isArray(path)) { state.debugState.throwError('ValueError', 'path must be an array.'); } - for (let i = 0; i < this["path"].length; i++) + for (let i = 0; i < path.length; i++) obj = obj[this["path"][i]]; } @@ -550,7 +551,9 @@ appliers["INIT_KEY"] = async function (state) { state.debugState.throwError('ValueError', 'index must be set.'); } - if (!(this["index"] in state.currentValue)) + unsafeAssert>(state.currentValue); + + if (!(this["index"] in (state.currentValue))) state.currentValue[this["index"]] = photocopy(this["content"]); }; diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index a86daaa..eea882e 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -1,3 +1,5 @@ +import {DebugState} from "./patchsteps-patch.js"; + export type Index = string | number; export interface BasePatchStep { From 60e3d9dba8cfaad2132681b1ba331cd74d88e84b Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 16:24:05 -0700 Subject: [PATCH 10/15] * --- .../patch-steps-lib/src/patchsteps-patch.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index ebe23ea..0e6a7cd 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -22,7 +22,7 @@ import { Index, Loader, ParsedPath, - PatchFile, PatchStep, + PatchFile, PatchStep, StackEntry, StackEntryStep, unsafeAssert } from './types.js' @@ -296,7 +296,7 @@ async function applyStep(step: AnyPatchStep, state: ApplierState) { await state.debugState.afterStep(); } -function replaceObjectProperty(object: O, key: keyof O, keyword: string | Record, value: string) { +function replaceObjectProperty>(object: O, key: keyof O, keyword: string | Record, value: string) { let oldValue = object[key]; // It's more complex than we thought. if (!Array.isArray(keyword) && typeof keyword === "object") { @@ -308,7 +308,7 @@ function replaceObjectProperty(object: O, key: keyof O, keyword: string | Rec } } } else { - object[key] = oldValue.replace(new RegExp(keyword, "g"), value); + object[key] = oldValue.replace(new RegExp(keyword as string, "g"), value); } } @@ -360,8 +360,11 @@ appliers["FOR_IN"] = async function (state) { state.debugState.throwError('ValueError', 'keyword must be set.'); } + unsafeAssert>>(values); + for(let i = 0; i < values.length; i++) { const cloneBody = photocopy(body); + unsafeAssert(cloneBody); const value = values[i]; valueInsertion(cloneBody, keyword, value); state.debugState.addStep(i, 'VALUE_INDEX'); @@ -430,6 +433,7 @@ appliers["ENTER"] = async function (state) { path = this["index"]; for (let i = 0; i < path.length;i++) { const idx = path[i]; + unsafeAssert(state.currentValue); state.stack.push(state.currentValue); if (state.currentValue[idx] === undefined) { const subArr = path.slice(0, i + 1); @@ -482,7 +486,7 @@ appliers["ADD_ARRAY_ELEMENT"] = async function (state) { }; // Reintroduced but simplified version of Emileyah's resolveUrl -function parsePath(url: string, fromGame: boolean) { +function parsePath(url: string, fromGame: boolean): [(boolean | string), string] { try { const decomposedUrl = new URL(url); const protocol = decomposedUrl.protocol; @@ -542,7 +546,7 @@ appliers["INCLUDE"] = async function (state) { const data = await state.loader.apply(state, srcPath); state.debugState.addFile(srcPath); - await patch(state.currentValue, data, state.loader, state.debugState); + await patch(state.currentValue, data as PatchFile, state.loader, state.debugState); state.debugState.removeLastFile(); }; From 52c26d81b8fe225450962d72b2d1860feb2056cd Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 17:40:50 -0700 Subject: [PATCH 11/15] * --- .../src/patchsteps-callable.ts | 17 ----- .../patch-steps-lib/src/patchsteps-patch.ts | 64 +++++++++---------- .../vendor-libs/patch-steps-lib/src/types.ts | 4 +- 3 files changed, 32 insertions(+), 53 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts index 07ef011..eede523 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts @@ -1,13 +1,5 @@ import {appliers, DebugState} from "./patchsteps-patch.js"; -/** - * @typedef State - * @property {unknown} currentValue - * @property {unknown[]} stack - * @property {(fromGame: boolean| string, path: string) => Promise} - * @property {DebugState} debugState - * @property {boolean} debug - */ export interface State { currentValue: unknown, stack: unknown[], @@ -18,11 +10,6 @@ export interface State { /** * A user defined step that is distinguishable from builtin PatchSteps. * Errors that occur in callables are not handled by the PatchSteps interpreter. - * - * @async - * @callback Callable - * @param {State} state is the internal PatchStep state. - * @param {unknown} args is the user supplied arguments. */ export type Callable = (state: State, args: unknown) => Promise; @@ -30,10 +17,6 @@ export type Callable = (state: State, args: unknown) => Promise; /* @type {Map} */ const callables = new Map; -/** - * @param {string} id - * @param {Callable} callable - */ export function register(id: string, callable: Callable) { if (typeof id !== "string") { throw Error('Id must be a string'); diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index 0e6a7cd..e109431 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -121,17 +121,18 @@ export class DebugState { * Leaves a step. * @final */ - removeLastStep() { + removeLastStep(): StackEntryStep { const stack = this.currentFile.stack; - let currentStep = null; + let currentStep: StackEntryStep | null = null; for(let index = stack.length - 1; index >= 0; index--) { - if (stack[index].type === "Step") { - currentStep = stack[index]; + const entry = stack[index]; + if (entry.type === "Step") { + currentStep = entry; stack.splice(index,1); index = -1; } } - return currentStep; + return currentStep!; } /** @@ -140,14 +141,15 @@ export class DebugState { */ getLastStep(): StackEntryStep { const stack = this.currentFile.stack; - let currentStep = null; + let currentStep: StackEntryStep | null = null; for(let index = stack.length - 1; index >= 0; index--) { - if (stack[index].type === "Step") { - currentStep = stack[index]; + const entry = stack[index]; + if (entry.type === "Step") { + currentStep = entry; index = -1; } } - return currentStep as StackEntryStep; + return currentStep!; } /** @@ -296,19 +298,19 @@ async function applyStep(step: AnyPatchStep, state: ApplierState) { await state.debugState.afterStep(); } -function replaceObjectProperty>(object: O, key: keyof O, keyword: string | Record, value: string) { +function replaceObjectProperty>(object: O, key: keyof O, keyword: string | RegExp | Record, value: string | {[replacementId: string]: string | number}) { let oldValue = object[key]; // It's more complex than we thought. if (!Array.isArray(keyword) && typeof keyword === "object") { // go through each and check if it matches anywhere. for(const property in keyword) { if (keyword[property]) { - object[key] = oldValue.replace(new RegExp(keyword[property], "g"), value[property] || ""); + object[key] = oldValue.replace(new RegExp(keyword[property], "g"), value[property] || "") as O[keyof O]; oldValue = object[key]; } } } else { - object[key] = oldValue.replace(new RegExp(keyword as string, "g"), value); + object[key] = oldValue.replace(new RegExp(keyword, "g"), value) as O[keyof O]; } } @@ -318,12 +320,12 @@ function replaceObjectProperty>(object: O, key: * @param {String| {[replacementId]: string | number}} value The value the replace the match * @returns {void} * */ -function valueInsertion(obj: Record, keyword: RegExp | {[replacementId: string]: RegExp}, value: string | {[replacementId: string]: string | number}) { +function valueInsertion(obj: Record | unknown[], keyword: string | RegExp | Record, value: string | {[replacementId: string]: string | number}) { if (Array.isArray(obj)) { for (let index = 0; index < obj.length; index++) { const child = obj[index]; - if (typeof child === "string") { - replaceObjectProperty(obj, index, keyword, value); + if (typeof child === "string") { + replaceObjectProperty(obj as Record, index, keyword, value); } else if (typeof child === "object") { valueInsertion(child, keyword, value); } @@ -333,9 +335,9 @@ function valueInsertion(obj: Record, keyword: RegExp | {[replac if (!obj[key]) continue; if (typeof obj[key] === "string") { - replaceObjectProperty(obj, key, keyword, value); + replaceObjectProperty(obj as Record, key, keyword, value); } else { - valueInsertion(obj[key], keyword, value); + valueInsertion(obj[key] as Record, keyword, value); } } } @@ -360,11 +362,8 @@ appliers["FOR_IN"] = async function (state) { state.debugState.throwError('ValueError', 'keyword must be set.'); } - unsafeAssert>>(values); - for(let i = 0; i < values.length; i++) { const cloneBody = photocopy(body); - unsafeAssert(cloneBody); const value = values[i]; valueInsertion(cloneBody, keyword, value); state.debugState.addStep(i, 'VALUE_INDEX'); @@ -402,13 +401,13 @@ appliers["PASTE"] = async function(state) { const obj = { type: "ADD_ARRAY_ELEMENT", content: value, - ...(!isNaN(this["index"]) ? {index: this["index"]} : {}) + ...(typeof this["index"] === 'number' && !isNaN(this["index"]) ? {index: this["index"]} : {}) }; await applyStep(obj as PatchStep.ADD_ARRAY_ELEMENT, state); } else if (typeof state.currentValue === "object") { await applyStep({ type: "SET_KEY", - index: this["index"], + index: this["index"]!, content: value }, state); } else { @@ -428,9 +427,7 @@ appliers["ENTER"] = async function (state) { state.debugState.throwError('Error', 'index must be set.'); } - let path = [this["index"]]; - if (Array.isArray(this["index"])) - path = this["index"]; + const path = Array.isArray(this["index"]) ? this["index"] : [this["index"]]; for (let i = 0; i < path.length;i++) { const idx = path[i]; unsafeAssert(state.currentValue); @@ -446,7 +443,7 @@ appliers["ENTER"] = async function (state) { appliers["EXIT"] = async function (state) { let count = 1; - if ("count" in this) + if (this["count"] !== undefined) count = this["count"]; for (let i = 0; i < count; i++) { if (state.stack.length === 0) { @@ -478,7 +475,7 @@ appliers["REMOVE_ARRAY_ELEMENT"] = async function (state) { appliers["ADD_ARRAY_ELEMENT"] = async function (state) { unsafeAssert(state.currentValue); - if ("index" in this) { + if (this["index"] !== undefined) { state.currentValue.splice(this["index"], 0, photocopy(this["content"])); } else { state.currentValue.push(photocopy(this["content"])); @@ -521,17 +518,16 @@ appliers["IMPORT"] = async function (state) { const srcPath = parsePath(this["src"], true); let obj = await state.loader.apply(state, srcPath); - if ("path" in this) { - const path = this["path"]; - if (!Array.isArray(path)) { + if (this["path"] !== undefined) { + if (!Array.isArray(this["path"])) { state.debugState.throwError('ValueError', 'path must be an array.'); } - for (let i = 0; i < path.length; i++) + for (let i = 0; i < this["path"].length; i++) obj = obj[this["path"][i]]; } - if ("index" in this) { - state.currentValue[this["index"]] = photocopy(obj); + if (this["index"] !== undefined) { + state.currentValue![this["index"]] = photocopy(obj); } else { photomerge(state.currentValue, obj); } @@ -572,4 +568,4 @@ appliers["MERGE_CONTENT"] = async function (state) { } photomerge(state.currentValue, this["content"]); -} +}; diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index eea882e..1ed8f02 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -141,7 +141,7 @@ export interface ApplierState { debug: boolean; } -export type Applier = (this: StackEntryStep & T, state: ApplierState) => Promise; +export type Applier = (this: T, state: ApplierState) => Promise; export type Appliers = { [K in keyof PatchStepsRegistry]: Applier } @@ -163,4 +163,4 @@ export interface DiffSettings { optimize: boolean; } -export function unsafeAssert(val: any): asserts val is T {} \ No newline at end of file +export function unsafeAssert(val: any): asserts val is T {} From d765d5628453eea006ea03705c6494dc99bf5cbc Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 18:12:24 -0700 Subject: [PATCH 12/15] * --- .../patch-steps-lib/src/patchsteps-patch.ts | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index e109431..7d37615 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -53,21 +53,19 @@ import { // You are expected to subclass this class if you want additional functionality. export class DebugState { fileStack: FileInfo[]; - currentFile: FileInfo; + currentFile?: FileInfo; // The constructor. The default state of a DebugState is invalid; a file must be added (even if null) to make it valid. constructor() { // FileInfo[] this.fileStack = []; - // FileInfo - this.currentFile = null; } /** * Translates a ParsedPath into a string. * Overridable. */ - translateParsedPath(parsedPath: ParsedPath) { + translateParsedPath(parsedPath: ParsedPath | null): string { if (parsedPath === null) return "(unknown file)"; // By default, we know nothing. @@ -85,7 +83,7 @@ export class DebugState { * Enters a file by parsedPath. Do not override. * @final */ - addFile(parsedPath: ParsedPath) { + addFile(parsedPath: ParsedPath): void { const path = this.translateParsedPath(parsedPath); const fileInfo = { path, @@ -109,8 +107,8 @@ export class DebugState { * Enters a step. Note that calls to this *surround* applyStep as the index is not available to it. * @final */ - addStep(index: Index, name = "") { - this.currentFile.stack.push({ + addStep(index: Index, name = ""): void { + this.currentFile!.stack.push({ type: "Step", index, name @@ -122,7 +120,7 @@ export class DebugState { * @final */ removeLastStep(): StackEntryStep { - const stack = this.currentFile.stack; + const stack = this.currentFile!.stack; let currentStep: StackEntryStep | null = null; for(let index = stack.length - 1; index >= 0; index--) { const entry = stack[index]; @@ -140,7 +138,7 @@ export class DebugState { * @final */ getLastStep(): StackEntryStep { - const stack = this.currentFile.stack; + const stack = this.currentFile!.stack; let currentStep: StackEntryStep | null = null; for(let index = stack.length - 1; index >= 0; index--) { const entry = stack[index]; @@ -156,8 +154,8 @@ export class DebugState { * Throws this instance as an error. * @final */ - throwError(type: string, message: string) { - this.currentFile.stack.push({ + throwError(type: string, message: string): void { + this.currentFile!.stack.push({ type: "Error", errorType: type, errorMessage: message @@ -169,7 +167,7 @@ export class DebugState { * Prints information about a specific file on the stack. * Overridable. */ - printFileInfo(file: FileInfo) { + printFileInfo(file: FileInfo): void { console.log(`File %c${file.path}`, 'red'); let message = ''; const stack = file.stack; @@ -197,7 +195,7 @@ export class DebugState { * Prints information about the whole stack. * @final */ - print() { + print(): void { for(let fileIndex = 0; fileIndex < this.fileStack.length; fileIndex++) { this.printFileInfo(this.fileStack[fileIndex]); } @@ -207,7 +205,7 @@ export class DebugState { * Run at the start of applyStep; after the step has been entered formally, but before executing it. * Overridable. */ - async beforeStep() { + async beforeStep(): Promise { } @@ -215,7 +213,7 @@ export class DebugState { * Run at the end of applyStep; after executing the step, but before leaving it formally. * Overridable. */ - async afterStep() { + async afterStep(): Promise { } } @@ -223,7 +221,7 @@ export class DebugState { // Custom extensions are registered here. // Their 'this' is the Step, they are passed the state, and they are expected to return a Promise. // In practice this is done with async old-style functions. -export const appliers: Appliers = {} as any; // TODO(lleyton): no. +export const appliers = {} as Appliers; /* * @param {any} a The object to modify @@ -298,19 +296,19 @@ async function applyStep(step: AnyPatchStep, state: ApplierState) { await state.debugState.afterStep(); } -function replaceObjectProperty>(object: O, key: keyof O, keyword: string | RegExp | Record, value: string | {[replacementId: string]: string | number}) { - let oldValue = object[key]; +function replaceObjectProperty(object: O, key: keyof O, keyword: string | Record, value: string | {[replacementId: string]: string | number}) { + let oldValue = object[key] as string; // It's more complex than we thought. if (!Array.isArray(keyword) && typeof keyword === "object") { // go through each and check if it matches anywhere. for(const property in keyword) { if (keyword[property]) { object[key] = oldValue.replace(new RegExp(keyword[property], "g"), value[property] || "") as O[keyof O]; - oldValue = object[key]; + oldValue = object[key] as string; } } } else { - object[key] = oldValue.replace(new RegExp(keyword, "g"), value) as O[keyof O]; + object[key] = oldValue.replace(new RegExp(keyword as string, "g"), value as string) as O[keyof O]; } } @@ -320,12 +318,12 @@ function replaceObjectProperty>(object * @param {String| {[replacementId]: string | number}} value The value the replace the match * @returns {void} * */ -function valueInsertion(obj: Record | unknown[], keyword: string | RegExp | Record, value: string | {[replacementId: string]: string | number}) { +function valueInsertion(obj: unknown, keyword: string | Record, value: string | {[replacementId: string]: string | number}) { if (Array.isArray(obj)) { for (let index = 0; index < obj.length; index++) { const child = obj[index]; if (typeof child === "string") { - replaceObjectProperty(obj as Record, index, keyword, value); + replaceObjectProperty(obj, index, keyword, value); } else if (typeof child === "object") { valueInsertion(child, keyword, value); } @@ -337,7 +335,7 @@ function valueInsertion(obj: Record | unknown[], keyword: strin if (typeof obj[key] === "string") { replaceObjectProperty(obj as Record, key, keyword, value); } else { - valueInsertion(obj[key] as Record, keyword, value); + valueInsertion(obj[key], keyword, value); } } } @@ -365,7 +363,7 @@ appliers["FOR_IN"] = async function (state) { for(let i = 0; i < values.length; i++) { const cloneBody = photocopy(body); const value = values[i]; - valueInsertion(cloneBody, keyword, value); + valueInsertion(cloneBody, keyword, value as { [replacementId: string]: string | number; }); state.debugState.addStep(i, 'VALUE_INDEX'); for (let index = 0; index < cloneBody.length; index++) { const statement = cloneBody[index]; From df1c28d506b0525e356a55db520868d67ac9f1de Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 18:25:53 -0700 Subject: [PATCH 13/15] * --- .../patch-steps-lib/src/patchsteps-diff.ts | 10 +- .../vendor-libs/patch-steps-lib/src/types.ts | 252 +++++++++--------- 2 files changed, 133 insertions(+), 129 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index 116b36d..78af663 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -52,7 +52,9 @@ function diffHeuristic(a: unknown, b: unknown, settings: DiffSettings): number { } return changes / array.length; } else if (a.constructor === Object && b.constructor === Object) { - let total = []; + unsafeAssert>(a); + unsafeAssert>(b); + let total: string[] = []; for (let k in a) total.push(k); for (let k in b) @@ -94,11 +96,11 @@ function diffHeuristic(a: unknown, b: unknown, settings: DiffSettings): number { */ function diffArrayHeuristic(a: unknown[], b: unknown[], settings: DiffSettings) { const lookahead = settings.arrayLookahead; - let sublog = []; + let sublog: string[] = []; let ia = 0; for (let i = 0; i < b.length; i++) { let validDif = 2; - let validSrc = null; + let validSrc: number | null = null; for (let j = ia; j < Math.min(ia + lookahead, a.length); j++) { let dif = diffHeuristic(a[j], b[i], settings); if (dif < validDif) { @@ -265,6 +267,8 @@ function diffInterior(a: unknown, b: unknown, settings: DiffSettings) { } } } else if (a.constructor === Object && b.constructor === Object) { + unsafeAssert>(a); + unsafeAssert>(b); for (let k in a) { if (k in b) { unsafeAssert>(a); diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index 1ed8f02..a01d304 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -3,111 +3,111 @@ import {DebugState} from "./patchsteps-patch.js"; export type Index = string | number; export interface BasePatchStep { - comment?: string; + comment?: string; } export namespace PatchStep { - export interface ENTER extends BasePatchStep { - type: 'ENTER'; - index: Index | Index[]; - } - - export interface EXIT extends BasePatchStep { - type: 'EXIT'; - count?: number; - } - - export interface SET_KEY extends BasePatchStep { - type: 'SET_KEY'; - index: Index; - content?: unknown; - } - - export interface INIT_KEY extends BasePatchStep { - type: 'INIT_KEY'; - index: Index; - content: unknown; - } - - export interface REMOVE_ARRAY_ELEMENT extends BasePatchStep { - type: 'REMOVE_ARRAY_ELEMENT'; - index: number; - } - - export interface ADD_ARRAY_ELEMENT extends BasePatchStep { - type: 'ADD_ARRAY_ELEMENT'; - index?: number; - content: unknown; - } - - export interface IMPORT extends BasePatchStep { - type: 'IMPORT'; - src: string; - path?: Index[]; - index?: Index; - } - - export interface INCLUDE extends BasePatchStep { - type: 'INCLUDE'; - src: string; - } - - export interface FOR_IN extends BasePatchStep { - type: 'FOR_IN'; - values: unknown[] | Array>; - keyword: string | Record; - body: AnyPatchStep[]; - } - - export interface COPY extends BasePatchStep { - type: 'COPY'; - alias: string; - } - - export interface PASTE extends BasePatchStep { - type: 'PASTE'; - alias: string; - index?: Index; - } - - export interface COMMENT extends BasePatchStep { - type: 'COMMENT'; - value: unknown; - } - - export interface DEBUG extends BasePatchStep { - type: 'DEBUG'; - value: boolean; - } - - export interface MERGE_CONTENT extends BasePatchStep { - type: 'MERGE_CONTENT'; - content: unknown; - } - - export interface CALL extends BasePatchStep { - type: 'CALL'; - id: string; - args: unknown; - } + export interface ENTER extends BasePatchStep { + type: 'ENTER'; + index: Index | Index[]; + } + + export interface EXIT extends BasePatchStep { + type: 'EXIT'; + count?: number; + } + + export interface SET_KEY extends BasePatchStep { + type: 'SET_KEY'; + index: Index; + content?: unknown; + } + + export interface INIT_KEY extends BasePatchStep { + type: 'INIT_KEY'; + index: Index; + content: unknown; + } + + export interface REMOVE_ARRAY_ELEMENT extends BasePatchStep { + type: 'REMOVE_ARRAY_ELEMENT'; + index: number; + } + + export interface ADD_ARRAY_ELEMENT extends BasePatchStep { + type: 'ADD_ARRAY_ELEMENT'; + index?: number; + content: unknown; + } + + export interface IMPORT extends BasePatchStep { + type: 'IMPORT'; + src: string; + path?: Index[]; + index?: Index; + } + + export interface INCLUDE extends BasePatchStep { + type: 'INCLUDE'; + src: string; + } + + export interface FOR_IN extends BasePatchStep { + type: 'FOR_IN'; + values: unknown[] | Array>; + keyword: string | Record; + body: AnyPatchStep[]; + } + + export interface COPY extends BasePatchStep { + type: 'COPY'; + alias: string; + } + + export interface PASTE extends BasePatchStep { + type: 'PASTE'; + alias: string; + index?: Index; + } + + export interface COMMENT extends BasePatchStep { + type: 'COMMENT'; + value: unknown; + } + + export interface DEBUG extends BasePatchStep { + type: 'DEBUG'; + value: boolean; + } + + export interface MERGE_CONTENT extends BasePatchStep { + type: 'MERGE_CONTENT'; + content: unknown; + } + + export interface CALL extends BasePatchStep { + type: 'CALL'; + id: string; + args: unknown; + } } export interface PatchStepsRegistry { - ENTER: PatchStep.ENTER; - EXIT: PatchStep.EXIT; - SET_KEY: PatchStep.SET_KEY; - INIT_KEY: PatchStep.INIT_KEY; - REMOVE_ARRAY_ELEMENT: PatchStep.REMOVE_ARRAY_ELEMENT; - ADD_ARRAY_ELEMENT: PatchStep.ADD_ARRAY_ELEMENT; - IMPORT: PatchStep.IMPORT; - INCLUDE: PatchStep.INCLUDE; - FOR_IN: PatchStep.FOR_IN; - COPY: PatchStep.COPY; - PASTE: PatchStep.PASTE; - COMMENT: PatchStep.COMMENT; - DEBUG: PatchStep.DEBUG; - MERGE_CONTENT: PatchStep.MERGE_CONTENT; - CALL: PatchStep.CALL; + ENTER: PatchStep.ENTER; + EXIT: PatchStep.EXIT; + SET_KEY: PatchStep.SET_KEY; + INIT_KEY: PatchStep.INIT_KEY; + REMOVE_ARRAY_ELEMENT: PatchStep.REMOVE_ARRAY_ELEMENT; + ADD_ARRAY_ELEMENT: PatchStep.ADD_ARRAY_ELEMENT; + IMPORT: PatchStep.IMPORT; + INCLUDE: PatchStep.INCLUDE; + FOR_IN: PatchStep.FOR_IN; + COPY: PatchStep.COPY; + PASTE: PatchStep.PASTE; + COMMENT: PatchStep.COMMENT; + DEBUG: PatchStep.DEBUG; + MERGE_CONTENT: PatchStep.MERGE_CONTENT; + CALL: PatchStep.CALL; } export type AnyPatchStep = Extract; @@ -115,52 +115,52 @@ export type PatchFile = AnyPatchStep[] | Record; export type ParsedPath = null | [fromGame: true | false | string, url: string]; export interface StackEntryError { - type: "Error"; - errorType: string; - errorMessage: string; + type: "Error"; + errorType: string; + errorMessage: string; } export interface StackEntryStep { - type: "Step", - index: Index; - name: string; + type: "Step", + index: Index; + name: string; } export type StackEntry = StackEntryStep | StackEntryError; export interface FileInfo { - path: string; - stack: StackEntry[]; + path: string; + stack: StackEntry[]; } export type Loader = (fromGame: boolean | string, path: string) => Promise; export interface ApplierState { - currentValue: unknown; - stack: StackEntry[]; - cloneMap: Map; - loader: Loader; - debugState: DebugState; - debug: boolean; + currentValue: unknown; + stack: StackEntry[]; + cloneMap: Map; + loader: Loader; + debugState: DebugState; + debug: boolean; } export type Applier = (this: T, state: ApplierState) => Promise; export type Appliers = { - [K in keyof PatchStepsRegistry]: Applier + [K in keyof PatchStepsRegistry]: Applier } export type DiffCore = (a: unknown, b: unknown, settings: DiffSettings) => AnyPatchStep[] | null; export interface DiffSettings { - arrayTrulyDifferentThreshold: number; - trulyDifferentThreshold: number; - arrayLookahead: number; - diffAddNewKey: number; - diffAddDelKey: number; - diffMulSameKey: number; - - diffCore: DiffCore; - comment?: string; - commentValue?: string; - path: Index[]; - optimize: boolean; + arrayTrulyDifferentThreshold: number; + trulyDifferentThreshold: number; + arrayLookahead: number; + diffAddNewKey: number; + diffAddDelKey: number; + diffMulSameKey: number; + + diffCore: DiffCore; + comment?: string; + commentValue?: string; + path: Index[]; + optimize: boolean; } export function unsafeAssert(val: any): asserts val is T {} From a8328588fa155d8361598653575859523bcc5ad4 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 18:34:20 -0700 Subject: [PATCH 14/15] * --- .../src/patchsteps-callable.ts | 6 +-- .../patch-steps-lib/src/patchsteps-diff.ts | 30 ++++++------- .../patch-steps-lib/src/patchsteps-patch.ts | 44 +++++-------------- .../patch-steps-lib/src/patchsteps-utils.ts | 10 ++--- .../vendor-libs/patch-steps-lib/src/types.ts | 16 +++++-- 5 files changed, 44 insertions(+), 62 deletions(-) diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts index eede523..9385906 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-callable.ts @@ -13,9 +13,7 @@ export interface State { */ export type Callable = (state: State, args: unknown) => Promise; - -/* @type {Map} */ -const callables = new Map; +const callables = new Map(); export function register(id: string, callable: Callable) { if (typeof id !== "string") { @@ -52,7 +50,7 @@ appliers["CALL"] = async function(state: State) { const callable = callables.get(id); try { - await callable(state, args); + await callable!(state, args); } catch (e) { if (e !== state.debugState) { // So they know what happened diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts index 78af663..de372b4 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-diff.ts @@ -17,10 +17,10 @@ import {AnyPatchStep, BasePatchStep, DiffSettings, Index, PatchFile, PatchStep, /** * A difference heuristic. - * @param {any} a The first value to check. - * @param {any} b The second value to check. - * @param {any} settings The involved control settings. - * @returns {number} A difference value from 0 (same) to 1 (different). + * @param a The first value to check. + * @param b The second value to check. + * @param settings The involved control settings. + * @returns A difference value from 0 (same) to 1 (different). */ function diffHeuristic(a: unknown, b: unknown, settings: DiffSettings): number { if ((a === null) && (b === null) || (a === undefined) && (b === undefined)) @@ -136,10 +136,10 @@ function diffArrayHeuristic(a: unknown[], b: unknown[], settings: DiffSettings) /** * Diffs two objects. This is actually an outer wrapper, which provides default settings along with optimization. * - * @param {any} a The original value - * @param {any} b The target value - * @param {object} [settings] Optional bunch of settings. May include "comment". - * @return {object[]|null} Null if unpatchable (this'll never occur for two Objects or two Arrays), Array of JSON-ready Patch Steps otherwise + * @param a The original value + * @param b The target value + * @param settings Optional bunch of settings. May include "comment". + * @return Null if unpatchable (this'll never occur for two Objects or two Arrays), Array of JSON-ready Patch Steps otherwise */ export function diff(a: unknown, b: unknown, settings: Partial) { let trueSettings = photocopy(defaultSettings); @@ -191,8 +191,8 @@ export function diff(a: unknown, b: unknown, settings: Partial) { /** * Adds a comment to the step if there is a comment in settings.commentValue. - * @param {object} step The step to add to. - * @param {object} settings The settings. + * @param step The step to add to. + * @param settings The settings. */ export function diffApplyComment(step: T, settings: DiffSettings) { if (settings.commentValue !== void 0) @@ -202,11 +202,11 @@ export function diffApplyComment(step: T, settings: Dif /** * Handles the bookkeeping in settings necessary when entering a level of the diff. - * @param {any} a The original value - * @param {any} b The target value - * @param {string | number} index The index. - * @param {object} settings Settings. - * @return {object[]|null} See diff for more details + * @param a The original value + * @param b The target value + * @param index The index. + * @param settings Settings. + * @return See diff for more details */ export function diffEnterLevel(a: unknown, b: unknown, index: Index, settings: DiffSettings): AnyPatchStep[] | null { settings.path.push(index); diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index 7d37615..c3c1e6b 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -26,39 +26,16 @@ import { StackEntryStep, unsafeAssert } from './types.js' -// The following are definitions used for reference in DebugState. -/* - * ParsedPath is actually any type that translateParsedPath can understand. - * And translateParsedPath can be overridden by the user. - * But the types declared here are those that will be received no matter what. - * declare type ParsedPath = null | [fromGame: true | false | string, url: string]; - * - * declare type FileInfo = { - * path: string; - * stack: StackEntry[]; - * }; - * - * declare type StackEntry = StackEntryStep | StackEntryError; - * declare type StackEntryStep = { - * type: "Step"; - * }; - * declare type StackEntryError = { - * type: "Error"; - * errorType: string; - * errorMessage: string; - * }; - */ - // Error handling for appliers. // You are expected to subclass this class if you want additional functionality. export class DebugState { fileStack: FileInfo[]; - currentFile?: FileInfo; + currentFile: FileInfo | null; // The constructor. The default state of a DebugState is invalid; a file must be added (even if null) to make it valid. constructor() { - // FileInfo[] this.fileStack = []; + this.currentFile = null; } /** @@ -224,18 +201,18 @@ export class DebugState { export const appliers = {} as Appliers; /* - * @param {any} a The object to modify - * @param {object|object[]} steps The patch, fresh from the JSON. Can be in legacy or Patch Steps format. - * @param {(fromGame: boolean | string, path: string) => Promise} loader The loading function. + * @param a The object to modify + * @param steps The patch, fresh from the JSON. Can be in legacy or Patch Steps format. + * @param loader The loading function. * NOTE! IF CHANGING THIS, KEEP IN MIND DEBUGSTATE translatePath GETS ARGUMENTS ARRAY OF THIS. * ALSO KEEP IN MIND THE parsePath FUNCTION! * For fromGame: false this gets a file straight from the mod, such as "package.json". * For fromGame: true this gets a file from the game, which is patched by the host if relevant. * If the PatchSteps file passes a protocol that is not understood, then, and only then, will a string be passed (without the ":" at the end) * In this case, fromGame is set to that string, instead. - * @param [debugState] debugState The DebugState stack tracer. + * @param debugState The DebugState stack tracer. * If not given, will be created. You need to pass your own instance of this to have proper filename tracking. - * @return {Promise} A Promise + * @return A Promise */ export async function patch(a: unknown, steps: PatchFile, loader: Loader, debugState?: DebugState) { if (!debugState) { @@ -313,10 +290,9 @@ function replaceObjectProperty(object: O, key: keyof O, keywor } /** - * @param {object} obj The object to search and replace the values of - * @param {RegExp| {[replacementId: string]: RegExp}} keyword The expression to match against - * @param {String| {[replacementId]: string | number}} value The value the replace the match - * @returns {void} + * @param obj The object to search and replace the values of + * @param keyword The expression to match against + * @param value The value the replace the match * */ function valueInsertion(obj: unknown, keyword: string | Record, value: string | {[replacementId: string]: string | number}) { if (Array.isArray(obj)) { diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts index a6c4bab..fcbfd50 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-utils.ts @@ -10,9 +10,9 @@ /** * A generic merge function. * NOTE: This should match Patch Steps specification, specifically how IMPORT merging works. - * @param {any} a The value to merge into. - * @param {any} b The value to merge from. - * @returns {any} a + * @param a The value to merge into. + * @param b The value to merge from. + * @returns a */ export function photomerge(a: A, b: B): A & B { if (Array.isArray(b)) { @@ -29,8 +29,8 @@ export function photomerge(a: A, b: B): A & B { /** * A generic copy function. - * @param {any} o The value to copy. - * @returns {any} copied value + * @param o The value to copy. + * @returns copied value */ export function photocopy(o: O): O { if (o) { diff --git a/common/vendor-libs/patch-steps-lib/src/types.ts b/common/vendor-libs/patch-steps-lib/src/types.ts index a01d304..77fd8e2 100644 --- a/common/vendor-libs/patch-steps-lib/src/types.ts +++ b/common/vendor-libs/patch-steps-lib/src/types.ts @@ -114,6 +114,18 @@ export type AnyPatchStep = Extract; export type ParsedPath = null | [fromGame: true | false | string, url: string]; + +// The following are definitions used for reference in DebugState. +/* + * ParsedPath is actually any type that translateParsedPath can understand. + * And translateParsedPath can be overridden by the user. + * But the types declared here are those that will be received no matter what. + */ +export interface FileInfo { + path: string; + stack: StackEntry[]; +} + export interface StackEntryError { type: "Error"; errorType: string; @@ -125,10 +137,6 @@ export interface StackEntryStep { name: string; } export type StackEntry = StackEntryStep | StackEntryError; -export interface FileInfo { - path: string; - stack: StackEntry[]; -} export type Loader = (fromGame: boolean | string, path: string) => Promise; From 35960e2fc0d639173f9654b7eeb31f0fad1b8c7e Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Mon, 27 Mar 2023 18:48:15 -0700 Subject: [PATCH 15/15] * --- common/vendor-libs/patch-steps-lib.d.ts | 1 - .../{patch-steps-lib.js => patch-steps-lib.ts} | 0 .../patch-steps-lib/src/patchsteps-patch.ts | 12 +++++++----- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 common/vendor-libs/patch-steps-lib.d.ts rename common/vendor-libs/{patch-steps-lib.js => patch-steps-lib.ts} (100%) diff --git a/common/vendor-libs/patch-steps-lib.d.ts b/common/vendor-libs/patch-steps-lib.d.ts deleted file mode 100644 index 5519c27..0000000 --- a/common/vendor-libs/patch-steps-lib.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from 'ultimate-crosscode-typedefs/patch-steps-lib'; diff --git a/common/vendor-libs/patch-steps-lib.js b/common/vendor-libs/patch-steps-lib.ts similarity index 100% rename from common/vendor-libs/patch-steps-lib.js rename to common/vendor-libs/patch-steps-lib.ts diff --git a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts index c3c1e6b..accc51c 100644 --- a/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts +++ b/common/vendor-libs/patch-steps-lib/src/patchsteps-patch.ts @@ -269,7 +269,7 @@ async function applyStep(step: AnyPatchStep, state: ApplierState) { state.debugState.getLastStep().name = ''; state.debugState.throwError('TypeError',`${step['type']} is not a valid type.`); } - await appliers[step["type"]].call(step, state); + await (appliers[step["type"]] as Applier).call(step, state); await state.debugState.afterStep(); } @@ -280,7 +280,7 @@ function replaceObjectProperty(object: O, key: keyof O, keywor // go through each and check if it matches anywhere. for(const property in keyword) { if (keyword[property]) { - object[key] = oldValue.replace(new RegExp(keyword[property], "g"), value[property] || "") as O[keyof O]; + object[key] = oldValue.replace(new RegExp(keyword[property], "g"), (value as {[replacementId: string]: string | number})[property] as string || "") as O[keyof O]; oldValue = object[key] as string; } } @@ -305,6 +305,7 @@ function valueInsertion(obj: unknown, keyword: string | Record, } } } else if (typeof obj === "object") { + unsafeAssert>(obj); for(let key in obj) { if (!obj[key]) continue; @@ -406,12 +407,12 @@ appliers["ENTER"] = async function (state) { const idx = path[i]; unsafeAssert(state.currentValue); state.stack.push(state.currentValue); - if (state.currentValue[idx] === undefined) { + if (state.currentValue[idx as keyof StackEntry] === undefined) { const subArr = path.slice(0, i + 1); state.debugState.throwError('Error', `index sequence ${subArr.join(",")} leads to an undefined state.`); } - state.currentValue = state.currentValue[idx]; + state.currentValue = state.currentValue[idx as keyof StackEntry]; } }; @@ -497,10 +498,11 @@ appliers["IMPORT"] = async function (state) { state.debugState.throwError('ValueError', 'path must be an array.'); } for (let i = 0; i < this["path"].length; i++) - obj = obj[this["path"][i]]; + obj = (obj as Record)[this["path"][i]]; } if (this["index"] !== undefined) { + unsafeAssert>(state.currentValue); state.currentValue![this["index"]] = photocopy(obj); } else { photomerge(state.currentValue, obj);