// Form session keys (D-02). A record form makes one key when it mounts and a // relation child modal makes its own when it opens. Uploads, file removals, // deferred relation calls and the final save carry the key in a header, so // the server can hold the work against it and commit it with the save. A key // never appears in a URL: it would end up in logs and the browser history. /** Header of the record form's key. */ export const SESSION_HEADER = 'X-Session-Key' /** Header of a relation child modal's own key. */ export const CHILD_SESSION_HEADER = 'X-Child-Session-Key' /** Random bytes per key: 256 bits, above D-02's 128-bit floor. */ const KEY_BYTES = 32 /** * A new key: 32 bytes from crypto.getRandomValues encoded as unpadded * base64url, 43 characters of A-Z a-z 0-9 _ - (the server's key pattern). */ export function newSessionKey(): string { const bytes = new Uint8Array(KEY_BYTES) globalThis.crypto.getRandomValues(bytes) let binary = '' for (const byte of bytes) { binary += String.fromCharCode(byte) } return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') }