Upgrading to 1.1
Install, schema, and behavior changes when upgrading an existing 1.0.x install to 1.1.
Upgrading to 1.1
1.1 changes how the package is installed and adds database schema. Existing docs, docs sets, groups, Access records and assets are kept; no content has to be re-synced.
1. Install the peer packages
@valkyrianlabs/payload-markdown and @payloadcms/plugin-seo are now peer
dependencies instead of bundled dependencies, so the app installs them itself:
1pnpm add @valkyrianlabs/payload-markdown-docs@^1.1.0 \2 @valkyrianlabs/payload-markdown@^1.6.0 \3 @payloadcms/plugin-seo
Then regenerate the import map, because the plugin registers admin components from both packages (including the payload-markdown 1.6 block params field):
1pnpm payload generate:importmap
2. Migrate the database
What changes
| Table | Change |
|---|---|
docs_sets | New allow_tag_refs column (default true; existing rows get true) |
docs_sets_repositories | New table: the docs set Allowed repositories list |
docs_access_rels | New table: the Access record Allowed docs sets relationship |
docs | New sync_fields_hash_at_last_sync column |
_docs_v, _docs_sets_v | The same columns for versions, when drafts are enabled |
docs_sync_nonces | New unique index on (key_id, nonce) |
Payload 3.90 itself also adds users.reset_password_requested_at, so that
column can appear in the same migration.
Every change is additive except the unique nonce index: creating it fails if the nonces table holds two rows with the same key id and nonce. Earlier versions did not prevent that, so clean the table up first. Expired nonces are no longer needed for replay protection and are removed as well.
The table names above use the default collection slugs; they follow your slugs if you changed them.
With migrations (recommended)
-
Upgrade the packages (step 1).
-
Create the migration:
1pnpm payload migrate:create docs_1_1 -
Call
prepareDocsSyncMigrationat the top of the generatedup, before the generated SQL:1import { MigrateDownArgs, MigrateUpArgs, sql } from '@payloadcms/db-postgres'2import { prepareDocsSyncMigration } from '@valkyrianlabs/payload-markdown-docs/migrations'3 4export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {5 await prepareDocsSyncMigration({ payload, req })6 7 await db.execute(sql`8 -- generated SQL, unchanged9 `)10}Pass
noncesCollectionSlugif you changedcollections.nonces.slug. The helper uses the migration'sreq, so it runs in the same transaction: if the migration fails, nothing is deleted. It returns how many expired and duplicate nonces it removed, keeps the oldest row of each duplicate pair, and can run more than once. -
Run it:
1pnpm payload migrate
Without the helper, migrate fails with
could not create unique index "keyId_nonce_idx" when duplicates exist, and
the migration is rolled back.
With dev schema push
Starting the app in development (push mode) applies the schema directly. If
it stops with could not create unique index "keyId_nonce_idx", remove the
expired and duplicate nonces, then start it again:
1DELETE FROM docs_sync_nonces WHERE expires_at < now();2 3DELETE FROM docs_sync_nonces a4USING docs_sync_nonces b5WHERE a.key_id = b.key_id6 AND a.nonce = b.nonce7 AND a.id > b.id;
Push mode applies the other changes before it reaches the index, so the second start finishes the upgrade.
MongoDB
There is no SQL migration. Mongoose builds the unique index in the background
when the app starts; while duplicates exist that build fails with a logged
error and the app starts anyway. Sync still rejects replays without the index,
but concurrent requests are only handled atomically once it exists. Remove the
duplicates once with payload run:
1// scripts/prepare-docs-1-1.ts2import config from '@payload-config'3import { getPayload } from 'payload'4import { prepareDocsSyncMigration } from '@valkyrianlabs/payload-markdown-docs/migrations'5 6const payload = await getPayload({ config })7console.log(await prepareDocsSyncMigration({ payload }))8process.exit(0)
1pnpm payload run scripts/prepare-docs-1-1.ts
Then restart the app so the index is built. Apps that set autoIndex: false
in connectOptions create indexes their own way and need this index created
too.
3. Review changed defaults
- Collection access. Plugin collections are admin-only by default: only
users of the admin user collection (
config.admin.user) can manage docs records, and sync runs and nonces are read-only. If other users edited docs records, addaccessoverrides. See Collection Access. - Draft syncs and assets. With drafts enabled, a sync without
--publishno longer applies asset creates and updates; the next--publishsync does. Setsync.applyAssetsOnDraftSync: truefor the old behavior. See Sync Config. - Forwarded headers. Public URLs are no longer built from
X-Forwarded-Hostunlessendpoint.trustForwardedHeaders: true. SetserverURLin production. - Tag refs. Existing docs sets keep accepting GitHub OIDC tokens from tag
refs (
Allow tag refsis on). Turn it off per docs set to publish only from the branch. See GitHub OIDC. - Ed25519 key scope. Keys without
Allowed docs setsstill work for every docs set and log a warning. Scope them to the docs sets they publish.
4. Update the CLI
Install the matching pmdocs. The docs walk no longer drops files silently:
- Hidden files and directories are published, each with a warning. Pass
--skip-hiddento leave them out. build,distand.nextare skipped only directly below the docs root; deeper directories with those names are published as ordinary sections.
Run pmdocs validate before the first push to see what is included. See
CLI.