diff --git a/src/lib/extension/BulkEntitiesTransporter.ts b/src/lib/extension/BulkEntitiesTransporter.ts index 7594b25..b83fa49 100644 --- a/src/lib/extension/BulkEntitiesTransporter.ts +++ b/src/lib/extension/BulkEntitiesTransporter.ts @@ -1,7 +1,7 @@ import type StorageEntity from "$lib/extension/base/StorageEntity"; import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from "lz-string"; import type { ImportableElementsList, ImportableEntityObject } from "$lib/extension/transporting/importables"; -import EntitiesTransporter from "$lib/extension/EntitiesTransporter"; +import EntitiesTransporter, { type SameSiteStatus } from "$lib/extension/EntitiesTransporter"; import MaintenanceProfile from "$entities/MaintenanceProfile"; import TagGroup from "$entities/TagGroup"; @@ -10,9 +10,17 @@ type TransportersMapping = { } export default class BulkEntitiesTransporter { + #lastSameSiteStatus: SameSiteStatus = null; + + get lastImportSameSiteStatus() { + return this.#lastSameSiteStatus; + } + parseAndImportFromJSON(jsonString: string): StorageEntity[] { let parsedObject: any; + this.#lastSameSiteStatus = null; + try { parsedObject = JSON.parse(jsonString); } catch (e) { @@ -23,7 +31,11 @@ export default class BulkEntitiesTransporter { throw new TypeError('Invalid or unsupported object!'); } - return parsedObject.elements + this.#lastSameSiteStatus = EntitiesTransporter.checkIsSameSiteImportedObject(parsedObject); + + let hasDifferentStatuses = false; + + const resultEntities = parsedObject.elements .map(importableObject => { if (!(importableObject.$type in BulkEntitiesTransporter.#transporters)) { console.warn('Attempting to import unsupported entity: ' + importableObject.$type); @@ -31,9 +43,21 @@ export default class BulkEntitiesTransporter { } const transporter = BulkEntitiesTransporter.#transporters[importableObject.$type as keyof App.EntityNamesMap]; - return transporter.importFromObject(importableObject); + const resultEntity = transporter.importFromObject(importableObject); + + if (transporter.lastImportSameSiteStatus !== this.#lastSameSiteStatus) { + hasDifferentStatuses = true; + } + + return resultEntity; }) .filter(maybeEntity => !!maybeEntity); + + if (hasDifferentStatuses) { + this.#lastSameSiteStatus = 'unknown'; + } + + return resultEntities; } parseAndImportFromCompressedJSON(compressedJsonString: string): StorageEntity[] { @@ -78,4 +102,19 @@ export default class BulkEntitiesTransporter { profiles: new EntitiesTransporter(MaintenanceProfile), groups: new EntitiesTransporter(TagGroup), } + + /** + * Check if the imported object is created for the same site extension or not. + * @param importedObject Object to check. + * @private + */ + static #checkIsSameSiteImportedObject(importedObject: Record): SameSiteStatus { + if (!('$site' in importedObject)) { + return "unknown"; + } + + return importedObject.$site === __CURRENT_SITE__ + ? "same" + : "different"; + } } diff --git a/src/lib/extension/EntitiesTransporter.ts b/src/lib/extension/EntitiesTransporter.ts index ac29569..0b90cec 100644 --- a/src/lib/extension/EntitiesTransporter.ts +++ b/src/lib/extension/EntitiesTransporter.ts @@ -2,10 +2,33 @@ import { validateImportedEntity } from "$lib/extension/transporting/validators"; import { exportEntityToObject } from "$lib/extension/transporting/exporters"; import StorageEntity from "$lib/extension/base/StorageEntity"; import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from "lz-string"; +import type { ImportableElement } from "$lib/extension/transporting/importables"; + +/** + * Status of the last import. + * + * - `NULL` - no import was done yet or was unsuccessful. + * - `"unknown"` — imported object was created before v0.5, when extension started to be built for multiple sites. + * - `"same"` — imported object is marked as generated by the same type of extension. + * - `"different"` — imported object is marked as generated by some other type of extension. + */ +export type SameSiteStatus = null | "unknown" | "same" | "different"; export default class EntitiesTransporter { readonly #targetEntityConstructor: new (...any: any[]) => EntityType; + #lastSameSiteStatus: SameSiteStatus = null; + + /** + * Read the status of the last successful import. This flag could be used to determine if it was for the same site as + * the current extension or when it's generated before site identity was passed to the importable object. + * + * @see {SameSiteStatus} For the list of possible statuses. + */ + get lastImportSameSiteStatus() { + return this.#lastSameSiteStatus; + } + /** * Name of the entity, exported directly from the constructor. * @private @@ -37,6 +60,8 @@ export default class EntitiesTransporter { } importFromObject(importedObject: Record): EntityType { + this.#lastSameSiteStatus = null; + // TODO: There should be an auto-upgrader somewhere before the validation. So if even the older version of schema // was used, we still will will be able to pass the validation. For now we only have non-breaking changes. validateImportedEntity( @@ -44,6 +69,8 @@ export default class EntitiesTransporter { importedObject, ); + this.#lastSameSiteStatus = EntitiesTransporter.checkIsSameSiteImportedObject(importedObject); + return new this.#targetEntityConstructor( importedObject.id, importedObject @@ -54,6 +81,7 @@ export default class EntitiesTransporter { const importedObject = this.#tryParsingAsJSON(jsonString); if (!importedObject) { + this.#lastSameSiteStatus = null; throw new Error('Invalid JSON!'); } @@ -108,4 +136,18 @@ export default class EntitiesTransporter { return jsonObject } + + /** + * Check if the imported object is created for the same site extension or not. + * @param importedObject Object to check. + */ + static checkIsSameSiteImportedObject(importedObject: Record): SameSiteStatus { + if (!('$site' in importedObject)) { + return "unknown"; + } + + return importedObject.$site === __CURRENT_SITE__ + ? "same" + : "different"; + } }