Files
InvoiceShelf/resources/scripts/utils/generate-client-id.ts
Darko Gjorgjijoski 9a5731106e fix(ui): stop depending on secure-context APIs over plain HTTP (#697)
crypto.randomUUID() and navigator.clipboard are both [SecureContext]-
gated, so neither exists on a plain-HTTP origin that isn't localhost.
That covers the dev host (http://invoiceshelf.test) and any self-hosted
install reached over a hostname or LAN IP — a large share of them.

generateClientId() called crypto.randomUUID() unguarded. It runs during
Pinia store construction via the invoice, estimate and recurring-invoice
stub factories, so on those origins it threw a TypeError before the
store existed and took the document screens down with it. The value is
only a placeholder identity for a row that has no server id yet — the
server assigns the real one on save, which is why DocumentItem and
DocumentTax type it `number | string`. It never needed randomness, so
it is now a session counter: no crypto, no fallback branch, works
everywhere.

PaymentDropdown.copyPdfUrl() had a textarea fallback attached with
.catch(), which cannot fire — on a non-secure origin navigator.clipboard
is undefined, so `.writeText` throws on property access before any
promise exists. Test up front instead, matching the guard the invoice
and estimate dropdowns already use.
2026-07-29 10:31:36 +02:00

18 lines
820 B
TypeScript

let counter = 0
/**
* Local identity for a row that does not exist server-side yet — a new invoice
* item, a new tax line. The server assigns the real numeric id on save (hence
* `id: number | string` on DocumentItem/DocumentTax), so this only has to stay
* unique within the page session, which a counter guarantees outright.
*
* Deliberately not `crypto.randomUUID()`: that is `[SecureContext]`-gated, so it
* is undefined on any plain-HTTP origin that isn't localhost. That covers the
* dev host (`http://invoiceshelf.test`) and self-hosted installs reached over a
* hostname or LAN IP — where calling it threw during Pinia store construction
* and took the document screens down with it. Nothing here needs randomness.
*/
export function generateClientId(): string {
return `client-${++counter}`
}