APOLLO VISION LABS

Migrer vers 0.2.0

Ce qui change pour une application en 0.1.x, dans l'ordre, et ce qui ne demande aucune attention.

La 0.2.0 ajoute la checklist et élargit la persistance pour lui faire place. Si vous ne passez pas de prop storage, la mise à jour se résume à un changement de version. Si vous fournissiez votre propre GuideStorage, une signature change et une clé de stockage se déplace.

Ce qu’on peut ignorer

Rien n’a changé du côté des visites. Tour, Step, GuideProvider, GuideTour, useTour, useGuideStep, les politiques de cible absente, la comparaison de routes, la navigation déléguée, translate et les labels de GuideTour sont tels quels. Les événements de visite sont inchangés, et les trois événements de checklist sont des ajouts à l’union GuideEvent, pas des remplacements.

La checklist est facultative. Ne pas monter ChecklistProvider laisse votre application exactement où elle était.

pnpm up @apollovisionlabs/guide-core @apollovisionlabs/guide-mui

Sans version figée, parce que cette page traite de la sortie de la 0.1.x et non d’une version cible précise. @apollovisionlabs/guide-mui a dépassé la 0.2.0 depuis : la 0.3.0 ajoute une prop labels à Checklist et ChecklistLauncher pour que leurs propres textes se traduisent. C’est un ajout de prop, sans rupture, donc cela ne change rien à cette migration ; voir Checklist.

Rupture 1 : GuideStorage devient générique

La 0.1.x figeait l’interface sur une forme :

// 0.1.x
interface GuideStorage {
  read(tourId: string): Promise<TourProgress | null>
  write(tourId: string, progress: TourProgress): Promise<void>
}

La 0.2.0 rend les deux méthodes génériques sur la valeur stockée, et la clé devient une simple chaîne que l’appelant a déjà préfixée :

// 0.2.0
interface GuideStorage {
  read<T>(key: string): Promise<T | null>
  write<T>(key: string, value: T): Promise<void>
}

Une implémentation écrite contre l’ancienne signature ne compile plus. La correction est mécanique : rendre les deux méthodes génériques et cesser de supposer que la clé est un identifiant de visite.

Avant :

import type { GuideStorage, TourProgress } from '@apollovisionlabs/guide-core'

export function createApiStorage(baseUrl: string): GuideStorage {
  return {
    async read(tourId: string): Promise<TourProgress | null> {
      const response = await fetch(`${baseUrl}/tours/${tourId}/progress`)
      if (response.status === 404) return null
      return (await response.json()) as TourProgress
    },
    async write(tourId: string, progress: TourProgress): Promise<void> {
      await fetch(`${baseUrl}/tours/${tourId}/progress`, {
        method: 'PUT',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(progress),
      })
    },
  }
}

Après :

import type { GuideStorage } from '@apollovisionlabs/guide-core'

export function createApiStorage(baseUrl: string): GuideStorage {
  return {
    async read<T>(key: string): Promise<T | null> {
      const response = await fetch(`${baseUrl}/guide/${encodeURIComponent(key)}`)
      if (response.status === 404) return null
      return (await response.json()) as T
    },
    async write<T>(key: string, value: T): Promise<void> {
      await fetch(`${baseUrl}/guide/${encodeURIComponent(key)}`, {
        method: 'PUT',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(value),
      })
    },
  }
}

Trois choses ont bougé. Le paramètre est une clé, pas un identifiant de visite : une route construite comme /tours/<id> ne décrit plus ce qui arrive. La valeur est opaque, donc une colonne nommée d’après stepIndex n’a plus la bonne forme : stockez du JSON indexé par (utilisateur, clé). Et la même instance recevra des clés checklist:<id> dès que vous monterez un ChecklistProvider avec elle, ce qui est tout l’intérêt du changement : une implémentation, deux fonctionnalités.

Rupture 2 : la clé de stockage a changé

La 0.1.x stockait la progression d’une visite sous l’identifiant de visite nu. La 0.2.0 la stocke sous tour:<id>.

La progression écrite par la 0.1.x se retrouve donc à une clé que plus personne ne lit. Rien ne casse : lire tour:product dans un stockage qui ne contient que product renvoie null, c’est-à-dire exactement le cas de la première visite, que le provider gère déjà. Un utilisateur en plein milieu d’une visite au moment de la mise à jour recommence cette visite une fois, depuis sa première étape. Un utilisateur qui avait terminé une visite la reverra si votre application la lui propose.

Vous pouvez laisser les anciennes entrées où elles sont et accepter ce redémarrage unique, ce que la bibliothèque suppose. Si ce redémarrage n’est pas acceptable chez vous, migrez les enregistrements de votre côté en recopiant chaque <id> vers tour:<id> avant que la nouvelle version n’atteigne les utilisateurs. Il n’y a pas d’aide de migration dans la bibliothèque, et le cas localStorage tient en une courte boucle sur votre propre espace de nommage.

Changement de comportement : les valeurs stockées sont validées

Celui-ci ne change aucune signature : rien ne vous en avertira à la compilation.

La 0.1.x passait tel quel à l’état de la visite ce que le stockage renvoyait, en vérifiant seulement que status valait in-progress. La 0.2.0 vérifie d’abord la forme, avec isTourProgress, et rejette tout ce qui échoue. La progression de checklist reçoit le même traitement via isChecklistProgress.

La conséquence : une entrée corrompue, tronquée, modifiée à la main ou écrite par une version de votre propre code qui stockait autre chose est désormais ignorée, et la visite repart de sa première étape au lieu de reprendre sur une valeur qu’il ne fallait pas croire. Si vous observiez en 0.1.x des reprises occasionnelles sur un index d’étape impossible, c’est la cause, et elle est corrigée.

Les deux gardes sont exportées, pour que votre propre code de stockage puisse poser la même question que les providers :

import { isTourProgress } from '@apollovisionlabs/guide-core'

const stored = await storage.read<unknown>('tour:product')
if (!isTourProgress(stored)) {
  // rien d'exploitable à cette clé
}

Dans l’ordre

  1. Passez les deux paquets à leur version courante.
  2. Si vous fournissez un GuideStorage maison, élargissez read et write à la signature générique et traitez le paramètre comme une clé opaque. Lancez le typecheck : rien d’autre n’a changé dans le cœur, donc un typecheck propre signifie que la signature est réglée.
  3. Tranchez sur les clés de visite orphelines : accepter un redémarrage par utilisateur en cours de visite, ou recopier <id> vers tour:<id> dans votre stockage avant la mise en production.
  4. Vérifiez que rien chez vous ne dépendait d’une entrée corrompue reprise telle quelle. En pratique, il s’agit de confirmer qu’une visite repartant du début est acceptable quand une valeur stockée est illisible.
  5. Éventuellement, ajoutez la checklist. C’est un nouveau provider et deux nouveaux composants, et elle partage l’instance de stockage que vous avez déjà.

Pour aller plus loin