Strata · guide

Rename a prop in every stored screen

A button’s `label` becomes `text`. One migration, and a screen stored before the change is read as if it had been written after it.

Install it
pnpm add @niscorp/strata @niscorp/nova @niscorp/prism zod
Step 1 of 5

A screen that was stored

A screen in Nisc is a document: a tree of nodes, each naming a component. This one was stored last season, when a button’s words were its label. It has two buttons, one of them inside a card.

A screen that was stored
import { NOVA_SEQUENCE } from '@niscorp/nova/migrations';
import { evaluate } from '@niscorp/prism';
import { PRISM_SEQUENCE } from '@niscorp/prism/migrations';
import { createUpgrader, defineSequence } from '@niscorp/strata';

const stored = {
  component: 'Stack',
  children: [
    {
      component: 'Card',
      children: [
        { component: 'Text', children: 'The Tempest, 19:30' },
        { component: 'Button', props: { label: 'Book seats' } },
      ],
    },
    { component: 'Button', props: { label: 'All shows' } },
  ],
};
Step 2 of 5

The change, for one node

The migration is a Prism config. It is handed one node as $.document and answers with the node to put in its place.

It will be shown every node of the screen, so it starts by asking whether this one is a button with a label. If so it renames the key inside props. Any other node is handed back as it is.

The change, for one node
const node = { $ref: '$.document' };
const isOldButton = {
  $and: [
    { $eq: [{ $get: { from: node, path: ['component'], fallback: null } }, 'Button'] },
    { $has: { from: node, path: ['props', 'label'] } },
  ],
};
const renamed = {
  $update: {
    from: node,
    path: ['props'],
    value: { $renameKeys: { from: { $var: 'current' }, map: { label: 'text' } } },
  },
};
const labelToText = { $case: { branches: [{ when: isOldButton, then: renamed }], else: node } };
Step 3 of 5

The kit’s sequence

The change belongs to the theatre’s kit, so the kit gets a sequence. documents is what says it is about documents and not tables. It is empty because the kit has no kind of its own: it rewrites Nova’s layouts, which at names.

The kit’s sequence
const kit = defineSequence({
  id: 'theatre.kit',
  documents: {},
  migrations: [
    {
      description: 'Button: label is now text',
      steps: [{ kind: 'document', at: 'nisc.nova/layout', transform: labelToText }],
    },
  ],
});
Step 4 of 5

An upgrader

An upgrader is made from every grammar a document can be written in. Nova’s is there because it is the one that says what a layout is and where layouts nest inside each other. Prism’s is there because Nova’s refers to it.

The transform is handed in: Strata does not depend on Prism, and calls whatever it is given.

An upgrader
const upgrader = await createUpgrader([NOVA_SEQUENCE, PRISM_SEQUENCE, kit], { transform: evaluate });
Step 5 of 5

Read the stored screen

The stamp is what was stored beside the screen: how far each grammar was when it was written. Here Nova’s and Prism’s are where they are today, and the kit’s is at 0, before the rename.

upgrade runs the migrations in between and answers with the screen as today’s code writes it, the refs of what it applied, and the stamp to store if you save it again.

Read the stored screen
const stamp = { ...upgrader.stamp, 'theatre.kit': 0 };
const read = upgrader.upgrade(stored, { kind: 'nisc.nova/layout', stamp });
console.log('applied:', read.applied);
console.log('stamp:', read.stamp);
console.dir(read.document, { depth: null });
Then

Run it.

The one migration that ran, the stamp the screen now carries, and the screen with text on both buttons, the one inside the card included.

Run it
npx tsx rename.mts
It prints
applied: [ 'theatre.kit/1' ]
stamp: { 'nisc.nova': 2, 'nisc.prism': 3, 'theatre.kit': 1 }
{
  component: 'Stack',
  children: [
    {
      component: 'Card',
      children: [
        { component: 'Text', children: 'The Tempest, 19:30' },
        { component: 'Button', props: { text: 'Book seats' } }
      ]
    },
    { component: 'Button', props: { text: 'All shows' } }
  ]
}