Sari la conținut

Changelog

Changelog

Repere selectate din ultimele versiuni. Pentru versiunile complete de API și politica privind modificările incompatibile (breaking changes), consultă Versiunea API și politica LTS.

Modificări detaliate pentru fiecare endpoint în parte: Specificația OpenAPI și Referința API interactivă.


2026-10 · Culori în Dashboard

  • Nou: pagina de detalii a unui cod are cardul Culori: prim-plan și fundal ca valoare hex, fundal transparent, previzualizare prin ruta reală a imaginii. Verificarea este cea de la PATCH /v1/codes/{id}: perechile critice necesită o confirmare, perechile sub 1,5:1 nu pot fi salvate.
  • Resetarea la negru pe alb livrează din nou în toate cele patru formate aceiași octeți ca fără culori. Codurile fără culori nu se schimbă.
  • Codurile transparente apar pe o tablă de șah în toate previzualizările din Dashboard. Detalii: Culori în Dashboard.

2026-10 · Culori în PDF și EPS

  • Nou: qr.pdf și qr.eps desenează fg și bg, ca parametri și ca culori salvate. Negrul și orice nuanță de gri sunt scrise ca tonuri de gri (doar placa de negru), orice altă culoare ca CMYK în procente întregi, de exemplu 1F4E79 ca C74 M36 Y0 K53.
  • Fundal ca la SVG: De îndată ce este selectată o culoare, o suprafață opacă se află în spatele codului și a zonei sale de liniște, albă sau bg. bg=transparent nu desenează niciuna. Titlul rămâne negru, placa logo-ului albă.
  • Nicio modificare fără culori: Fără fg/bg și fără culori salvate, ambele formate oferă aceiași octeți ca înainte. PDF-ul unui pașaport de produs rămâne necolorat.
  • Limite: fără profil ICC, fără PDF/X. Detalii: Culori la tipărire.

2026-09 · Coduri statice fără cache de redirecționare, ștergere simultană

  • Modificat: Codurile statice nu mai sunt stocate în cache-ul de redirecționare. O accesare a link-ului lor scurt (redirect_url) citește întotdeauna starea curentă. După DELETE /v1/account, un cod static nu mai redirecționează la următoarea accesare, în timp ce anterior acest lucru putea dura până la 24 de ore. De asemenea, o schimbare de tarif se aplică imediat pentru codurile statice. Codurile dinamice sunt eliminate la ștergerea contului, ca și până acum. Codurile statice tipărite conțin direct destinația lor și nu sunt afectate.
  • Remediat: Două cereri simultane DELETE /v1/codes/:id pentru același cod returnează o dată 200 și o dată 404. Webhook-ul qr.deleted este trimis exact o singură dată, în loc de două ori ca înainte.
  • Nou: POST și DELETE /v1/codes/:id/logo răspund cu 409 (errors/conflict) dacă o altă cerere a modificat simultan logo-ul aceluiași cod, caz în care nu se modifică nimic. Anterior, un logo încărcat putea rămâne salvat fără a fi utilizat. Două cereri simultane DELETE /v1/codes/:id/logo returnează ambele 200. Detalii: Logo.

2026-09 · TypeScript-SDK 1.2.0

  • Nou în @qr3/sdk 1.2.0: client.codes.update() acceptă appearance și returnează rezultatele verificării contrastului ca issues în rezultat; dacă nu există constatări, câmpul lipsește. client.codes.imageUrl() acceptă fg, bg și ecc. Fiecare cod returnat de API (get, list, create, update, batchCreate) conține title și appearance.
  • Actualizare: npm install @qr3/sdk@latest. Urmează Python, Go și PHP. Detalii: SDK-uri & CLI și Salvarea culorilor în cod.

2026-09 · Timpul de expirare modificat se aplică în decurs de aproximativ un minut

  • Remediat: Un PATCH pe expires_at golește acum cache-ul de redirecționare al codului. Noul timp de expirare se aplică astfel în decurs de aproximativ un minut, în loc de până la 24 de ore mai târziu. Anterior, o expirare eliminată sau amânată continua să returneze 410 timp de până la 24 de ore, iar o expirare setată pentru momentul actual lăsa redirecționarea să funcționeze în continuare timp de până la 24 de ore.
  • Remediat: Un cod creat cu expires_at expiră la timp chiar și atunci când expirarea are loc în primele 24 de ore.
  • Remediat: Dacă un cod este șters în timp ce logo-ul său este încărcat sau eliminat, POST și DELETE /v1/codes/:id/logo răspund cu 404 și nu modifică codul șters, la fel ca PATCH.
  • Remediat: De asemenea, un cod static al cărui link scurt (redirect_url) a fost accesat se află în cache-ul de redirecționare. Modificarea, întreruperea și ștergerea îl golesc acum de asemenea, nu doar în cazul codurilor dinamice.

2026-09 · Salvarea culorilor pe cod

  • Nou: PATCH /v1/codes/:id acceptă appearance cu foreground_color și background_color (#RRGGBB, fundalul de asemenea transparent). qr.svg și qr.png desenează culorile stocate fără niciun parametru; un parametru de interogare precum ?fg=000000 are în continuare prioritate. PDF și EPS vor desena culorile doar într-o versiune ulterioară.
  • Verificarea contrastului: O pereche sub 1,5:1 returnează 422. Perechile cu contrast mai slab sunt salvate și raportate în meta.issues ca warning sau critical; un fundal transparent este întotdeauna critical, niciodată blocat.
  • Îmbinare: Câmpurile omise își păstrează valoarea salvată, null resetează valoarea. Modificările concurente pe același cod nu se mai suprascriu reciproc.
  • În fiecare răspuns de cod: appearance este prezent în fiecare răspuns de cod și în webhook-urile qr.created și qr.updated. POST, procesarea în lot (batch) și importul îl resping cu 422.
  • Nicio modificare pentru codurile existente: Fără culori salvate, fiecare cod returnează aceiași octeți ca înainte. Detalii: Salvarea culorilor pe cod.

2026-09 · Culori și corectarea erorilor ca parametri ai rutelor de imagini

  • Nou: Toate cele patru rute de imagini acceptă fg (culoarea de prim-plan), bg (culoarea de fundal sau transparent) și ecc (corectarea erorilor L, M, Q, H). SVG și PNG randează culorile; PDF și EPS le acceptă, dar le vor randa doar într-o versiune ulterioară. ecc funcționează în toate cele patru formate, un logo impune în continuare H.
  • Transparență: bg=transparent returnează un SVG fără fundal și un PNG cu canal alfa real. Fundalul trebuie să fie deschis la culoare și să lase liberă zona de liniște.
  • Niciodată o eroare: Valorile nevalide sunt ignorate; imaginea este returnată exact ca în cazul în care nu ar exista parametri.
  • Memorie cache: Fiecare parametru aplicat returnează Cache-Control: public, max-age=300 în loc de cele 24 de ore ale imaginii standard.
  • Nicio modificare de comportament fără parametri: Orice cod existent returnează aceiași octeți ca înainte în toate cele patru formate.
  • Disponibil în orice tarif. Detalii: Culori și corectarea erorilor.

2026-09 · PDF și EPS încorporează acum și logo-ul

  • Extensie: qr.pdf și qr.eps încorporează acum un logo setat exact la fel ca qr.svg și qr.png (vezi mai jos) — ca o imagine încorporată (PDF printr-un Image XObject, EPS printr-un dicționar de imagini în PostScript, acolo doar cu %%LanguageLevel: 3), centrat pe aceeași suprafață, cu aceeași creștere a corecției erorilor la H. Ambele formate nu livrau logo-ul până acum; acest lucru este acum remediat.
  • Memorie cache: Cu logo-ul setat, acum și qr.pdf și qr.eps returnează Cache-Control: public, max-age=300 în loc de cele 24 de ore obișnuite — exact ca SVG/PNG.
  • Soluție de rezervă: Dacă imaginea logo-ului în sine nu poate fi procesată (de exemplu, un obiect salvat deteriorat), corecția erorilor rămâne H, dar suprafața rezervată rămâne goală în loc de o imagine — niciodată o eroare de server.
  • Nicio modificare de comportament fără logo: Codurile fără logo returnează în continuare qr.pdf/qr.eps identice la nivel de octet, cu memoria cache de 24 de ore neschimbată.
  • Detalii și instrucțiuni de tipărire: Logo în codul QR.

2026-09 · Logo în codul QR — Dashboard și API

  • Nou: Un cod poate avea acum un logo în centru. În Dashboard, pagina de detalii a unui cod creează un card dedicat pentru logo: Selectează logo, la prima adăugare sau la eliminare confirmă un avertisment cu o casetă de selectare obligatorie (ambele acțiuni modifică modelul de puncte), apoi Încarcă logo/Elimină logo. O înlocuire nu necesită confirmare — se schimbă doar imaginea, nu și modelul. Rolul viewer vede starea și previzualizarea, dar niciuna dintre cele trei acțiuni.
  • API: POST /v1/codes/{id}/logo (câmpul multipart file; PNG, JPEG sau WebP, maximum 1 MB, SVG este respins) creează sau înlocuiește un logo; DELETE /v1/codes/{id}/logo îl elimină din nou și este idempotent. O imagine prea mare returnează 413, un format neacceptat 422, iar rolul viewer 403.
  • Randare: qr.svg și qr.png încorporează logo-ul ca pixeli reali (512 × 512, normalizat) și măresc nivelul de corectare a erorilor la H. Adăugarea și eliminarea modifică astfel modelul de puncte (M ↔ H), în timp ce înlocuirea nu îl modifică. qr.pdf și qr.eps nu încorporează logo-ul și rămân neschimbate. Un cod deja tipărit continuă să funcționeze în orice caz, deoarece destinația codificată nu depinde de logo — trebuie să retipărească doar cei care doresc să vadă (noua) imagine a logo-ului pe material, fără a amesteca fișierele de tipărire vechi și noi.
  • Memorie cache: Cu logo-ul setat, qr.svg și qr.png returnează Cache-Control: public, max-age=300 în loc de cele 24 de ore obișnuite. Detalii, limite și instrucțiuni de tipărire: Logo în codul QR.

2026-08 · operationId pentru toate cele 75 de operațiuni și un ghid „când să folosești” pentru agenți

  • Adăugare: Toate cele 75 de operațiuni din specificația OpenAPI au acum o operationId — listCodes, createCode, getCodeStats, archiveWorkspace și așa mai departe. Până acum, acest câmp lipsea cu desăvârșire, astfel încât fiecare generator trebuia să deducă numele metodei din metoda HTTP și cale (postV1Codes). Astfel de nume depind de cale și se schimbă la fiecare modificare a acesteia. Formatul numelui este același în toate cele 75 de locuri: list/get/create/update/replace/delete, altfel verbul acțiunii de business (validateDpp, registerGs1Identifier, pingWebhook). Verbul urmează logica de business, nu metoda HTTP — archiveWorkspace este un DELETE, importDpps este un POST.
  • Impact asupra clienților generați automat: Cei care își generează SDK-ul din specificație vor avea parte de metode redenumite (postV1Codes → createCode) la următoarea rulare. Acesta este scopul modificării, dar reprezintă o redenumire în cod extern — de aceea este menționată explicit aici. Căile, parametrii, formatele de răspuns și codurile de stare rămân neschimbate; SDK-urile oficiale, CLI și serverul MCP nu sunt afectate.
  • Introducere pentru agenți: https://qr3.app/llms.txt are o secțiune ## When to use qr3.app — șase sarcini în loc de șase funcții, plus fraza care explică pentru ce qr3.app nu este instrumentul potrivit. Serverul MCP oferă acum aceeași informație la faza de handshake: initialize către https://mcp.qr3.app/mcp răspunde cu un câmp instructions (înainte: unsprezece instrumente fără nicio clasificare). Este inclus, de asemenea, care apeluri necesită o cheie API — initialize și tools/list nu, dar fiecare tools/call da.
  • Corecturi în llms.txt: Patru informații au fost verificate în raport cu mediul de producție și nu s-au confirmat: (1) SDK-ul Python qr3app nu este publicat pe PyPI — linia pip install a fost eliminată definitiv; (2) Accept: text/markdown se aplică pentru pagina de pornire și blog, nu pentru fiecare pagină randată pe server; (3) adresa de contact este [email protected], inclusiv în JSON-LD-ul paginii de pornire; (4) /de/security/ este o redirecționare către o ancoră, nu o pagină proprie. Link nou: docs.qr3.app/de/skills/.
  • Asigurare: Trei aserțiuni în openapi-spec.test.ts — completitudine, unicitate, lowerCamelCase —, fiecare verificată prin mutație. Alte nouă teste mențin cele patru informații corectate, pentru a preveni reapariția lor.
  • Limitare cunoscută: Două dintre cele 75 de operațiuni sunt descrise în specificație, dar răspund cu 404 (GET /v1/codes/{id}/stats și GET /v1/account). Acestea au primit un nume aici și îl vor pierde din nou de îndată ce se va decide dacă vor fi implementate sau eliminate.

2026-08 · Downgrade-urile de tarif și anulările afectează acum și limitele

  • Remediere: Webhook-ul Stripe scria doar numele planului în cazul schimbării planului și al anulării, nu și cele patru coloane de limite ale organizației (max_workspaces, max_members, max_dynamic_codes, max_scans_per_month). Deoarece aplicarea limitelor utilizează valoarea maximă dintre valoarea salvată și baseline-ul planului, o organizație care a trecut la un plan inferior sau care și-a anulat abonamentul își păstra vechile limite mai mari — downgrade-ul nu avea efect. Acum, webhook-ul setează aceste coloane la baseline-ul noului tarif la fiecare schimbare efectivă de plan, iar în caz de anulare, la baseline-ul Free — exact așa cum face deja administrarea admin.
  • Comportament: Actualizările de rutină ale abonamentului (prelungire, mijloc de plată, proratare) nu afectează în continuare limitele; limitele mai mari acordate individual rămân neschimbate. Doar o schimbare efectivă de plan le resetează la baseline-ul noului tarif.
  • Impact: Nicio modificare a API-ului sau a formatului de răspuns. Organizațiile al căror downgrade a avut loc înainte de această remediere își păstrează vechile valori până când intră în vigoare următoarea schimbare de plan sau până când serviciul de suport le ajustează.

2026-08 · Cursorul keyset acum pe toate endpoint-urile de listă

  • Fix: Remedierea cursorului pentru GET /v1/codes și GET /v1/dpp (vezi mai jos) este acum implementată pentru toate celelalte liste paginate prin cursor: GET /v1/qr-codes/:id/comments, /v1/workspaces, /v1/gs1/identifiers, /v1/members, /v1/audit-logs, /v1/admin/orgs, /v1/admin/users și /v1/webhooks/:id/deliveries. Toate realizau paginarea doar după created_at; rândurile cu marcaj temporal identic (înregistrări de audit ale unei operațiuni batch, reîncercări de webhook, importuri de membri) puteau fi pierdute pe pagina următoare. Cheia de sortare este acum peste tot tuplul (created_at, id).
  • Modificare API: Pe aceste liste, meta.pagination.next_cursor este de acum înainte, de asemenea, o valoare opacă (base64url) în loc de un simplu marcaj temporal; cursorii ilizibili returnează 400, iar cursorii vechi de tip marcaj temporal vor fi acceptați în continuare ca măsură de tranziție. Excepție GET /v1/webhooks/:id/deliveries: acolo next_cursor rămâne ID-ul ultimei livrări (ID necunoscut → prima pagină). Clienții care returnează next_cursor nemodificat — Dashboard, CLI, SDK-urile, MCP — nu trebuie să schimbe nimic.
  • Impact: Nu este necesară nicio migrare. Dacă ai observat lipsa unor înregistrări la paginarea în una dintre aceste liste (de exemplu, jurnalul de audit sau jurnalul de livrări din Dashboard): acestea nu au dispărut niciodată — listele le afișează complet începând de acum.

2026-08 · Marcajele de timp după editare și ștergere sunt din nou conforme cu OpenAPI

  • Fix: După PATCH /v1/codes/{id}, updated_at a fost returnat în formatul SQLite fără fus orar (2026-08-17 09:00:00), deși specificația OpenAPI promite format: date-time iar crearea (POST) furnizează marcajul temporal ISO (2026-08-17T09:00:00.000Z). Același lucru a fost valabil pentru deleted_at/updated_at la soft-delete, precum și pentru căile de editare/ștergere ale cheilor API, organizațiilor, workspace-urilor, membrilor, comentariilor și webhook-urilor, precum și pentru last_used_at al cheilor API și last_triggered_at al webhook-urilor. Toate căile de scriere marchează acum timpul în formatul ISO 8601 (UTC, T și Z).
  • Impact: Clienții care parsează updated_at cu new Date(...) (SDK-uri, CLI, Dashboard) citeau formatul cu spațiu ca fiind ora locală — la codurile care fuseseră editate, CLI afișa ora deplasată cu decalajul de fus orar local (Viena: −2 h). Acest lucru a fost remediat. În plus, o migrare de date normalizează valorile deja salvate în vechiul format la ISO, astfel încât sortarea și comparările în seturile de date mixte să fie corecte. Nicio modificare a numelor de câmpuri sau a structurii de răspuns.
  • Context: Aceeași clasă de erori ca și cele două remedieri de mai jos (expirarea cheii API, limita de re-scanare): datetime('now') din SQLite scrie YYYY-MM-DD HH:MM:SS, în timp ce toți ceilalți scriitori folosesc ISO 8601. Un test de protecție a codului sursă va preveni aparițiile noi pe viitor.

2026-08 · Paginarea listelor nu mai pierde coduri batch

  • Fix: GET /v1/codes și GET /v1/dpp paginau exclusiv pe baza created_at. Cu toate acestea, codurile din POST /v1/codes/batch, POST /v1/dpp/batch și din importul CSV/XLSX partajează același marcaj temporal — de îndată ce un batch era mai mare decât limit (implicit 20), a doua pagină nu mai returna rândurile rămase cu același marcaj temporal. Codurile existau și puteau fi accesate prin GET /v1/codes/:id, dar nu apăreau niciodată în listă (Dashboard, CLI qr3 list, SDK-uri, MCP). Cursorul este acum un keyset pe baza (created_at, id).
  • Modificare API: meta.pagination.next_cursor este de acum o valoare opacă (base64url) în loc de un simplu marcaj temporal. Cei care returnează cursorul neschimbat ca ?cursor= — așa cum fac Dashboard, CLI, toate SDK-urile și serverul MCP — nu trebuie să modifice nimic. Cursorii vechi de tip marcaj temporal vor fi acceptați în continuare temporar; cursorii ilizibili returnează acum 400 în loc să returneze silențios prima pagină.
  • Impact: Dacă ai văzut mai puține coduri în listă după un import batch decât cele create: codurile nu au dispărut niciodată — lista le afișează acum în întregime. Nu este necesară nicio migrare.

2026-08 · Re-scanările de securitate rulează din nou la fiecare 24 de ore

  • Fix: Re-scanarea periodică a URL-urilor de destinație și a linkurilor de landing page (Google Web Risk) omitea codurile a căror ultimă scanare a avut loc în aceeași zi calendaristică cu limita de 24 de ore — în funcție de oră, re-scanarea era amânată cu până la încă o zi. Limita este acum calculată în același format ISO în care sunt stocate marcajele temporale de scanare.
  • Impact: Un URL de destinație clasificat ca nesigur după ultima scanare determină din nou suspendarea automată a codului în fereastra documentată de 24 de ore. Nicio modificare a API-ului sau a formatului de răspuns.

2026-08 · Cheile API expiră la momentul expirării

  • Fix: O cheie API a cărei valoare expires_at era în aceeași zi a continuat să fie acceptată până la miezul nopții UTC. Expirarea este acum comparată ca marcaj temporal în loc de șir de caractere — o cheie expirată returnează imediat 401.
  • Context: expires_at este salvat ca marcaj temporal ISO (2026-08-14T09:00:00Z), în timp ce partea de comparare furniza formatul cu spațiu (2026-08-14 09:00:00). Prin urmare, compararea brută a șirurilor de caractere era corectă doar dacă data era deja diferită.
  • Impact: Nu este necesară nicio migrare, formatul de răspuns al GET /v1/api-keys rămâne neschimbat. Valorile de expirare ilizibile sunt acum considerate expirate în loc de valide.

2026-08 · Referință API: Administrarea tenant-ului documentată

  • OpenAPI: Specificația — și, prin urmare, referința interactivă — documentează acum organizațiile (incl. GET /v1/organizations/usage), workspace-urile, membrii & rolurile și jurnalele de audit.
  • Billing: Prezentarea generală a tarifelor (GET /v1/billing/plans) este publică; finalizarea comenzii (POST /v1/billing/checkout) și portalul pentru clienți Stripe (GET /v1/billing/portal) sunt desemnate ca endpoint-uri pentru administratorii organizației.
  • Export scanări: Statisticile de scanare (GET /v1/codes/{id}/scans) și exportul de date brute (…/scans.csv, …/scans.xlsx) sunt complet documentate — inclusiv nota privind GDPR: ip_hash nu este niciodată inclus în export.
  • Comportament la erori: Nou documentat este și răspunsul 400 al validării cererii: corpul (body) este eroarea brută Zod, nu un document de problemă RFC-7807 — cu toate acestea, este livrat sub Content-Type-ul application/problem+json.

2026-08 · Copierea linkurilor publice ale fișierelor

  • Dashboard: Fișierele publice de pe pagina de detalii a unui cod au acum un buton care le copiază linkul public în clipboard – gata de folosit ca URL de destinație al unui cod QR, atunci când o scanare trebuie să deschidă imediat un anumit document în loc de pagina de destinație cu lista de fișiere.
  • API: Endpoint-urile pentru fișiere (/v1/files) returnează în plus public_url. Câmpul este setat doar pentru fișierele cu visibility: public – fișierele private nu primesc o adresă publică.
  • Comportament: Linkul nu necesită autentificare și deschide fișierul direct în browser. Butonul Înlocuiește nu îl modifică, așa că un cod QR tipărit cu acest link rămâne valabil. Detalii: Fișiere și fișe tehnice.

2026-07 · Roluri de echipă: Colaborator fără ștergere & facturare admin

  • Nou: Rolul de membru Colaborator (fără ștergere) — creează și editează coduri QR, fișiere și Digital Product Passports, dar nu poate șterge resurse și nu poate genera chei API. Toate endpoint-urile distructive verifică rolul pe server (403).
  • Facturare: Upgrade-urile de plan și portalul de clienți Stripe (POST /v1/billing/checkout, GET /v1/billing/portal) sunt acum rezervate exclusiv pentru Administratori ai organizației — toate celelalte roluri văd o prezentare generală a planului în mod doar-citire.
  • Dashboard: Acțiunile nepermise de rolul curent sunt ascunse în interfață: un Vizualizator nu vede, de exemplu, butoanele de creare, editare sau ștergere, în timp ce listele, descărcările și statisticile rămân accesibile. Detalii: Echipă & Roluri.

2026-06 · Linkuri externe pe landing page-ul codului

  • Landing page: Landing page-ul unui cod găzduit de qr3 poate acum să listeze linkuri externe auto-găzduite ({ label, url }) pe lângă sau în locul fișierelor încărcate – de exemplu pentru fișe tehnice de pe propriul tău site.
  • API: POST/PATCH /v1/codes acceptă un array links (0–20 intrări, http(s), ≤ 2048 de caractere). Fiecare URL este verificat cu Google Web Risk; un URL nesigur returnează 422. Un array gol șterge toate linkurile.
  • Dashboard: Adaugă, reordonează și elimină linkuri în pagina de detalii a codului.
  • Securitate: Linkurile randate rămân sigure împotriva XSS (cu escape, doar http(s)), iar pagina își păstrează antetul noindex.

2026-04 · Analize în Dashboard per cod QR

  • Dashboard: Butonul de analize din lista de coduri QR deschide acum pagina de statistici a codului QR respectiv la /dashboard/codes/{id}.
  • Routing: Aliasul /dashboard/codes redirecționează în continuare către /dashboard, dar nu mai interceptează rutele detaliate precum /dashboard/codes/{id}.
  • API: Pagina de detalii încarcă codul QR direct prin GET /v1/codes/:id; astfel, aceasta nu mai depinde de limitele de paginare ale listei.
  • Teste: Testele de regresie acoperă redirecționarea aliasului și încărcarea directă a codului.

2026-04 · Dialog de ștergere în Dashboard pentru codurile QR

  • Dashboard: Pictograma de coș de gunoi din lista de coduri QR deschide acum un dialog React dedicat, în locul unui popup nativ de browser.
  • Feedback: După ștergere, apare o notificare de tip toast pentru succes sau eroare.
  • Teste: packages/dashboard/tests/dashboard.test.ts previne regresiile pentru confirm() în fluxul de ștergere a codurilor QR.
  • Dashboard: Codurile scurte (shortcodes) din lista de coduri QR pot fi acum accesate direct ca linkuri externe de redirecționare. Pictograma de link extern de lângă, de exemplu, wu3qaa deschide https://qr3.app/{shortCode} într-o filă nouă.
  • i18n: S-au adăugat textele pentru tooltip în germană și engleză.
  • Teste: packages/dashboard/tests/dashboard.test.ts protejează href-ul linkului, comportamentul de filă nouă, noopener noreferrer și pictograma împotriva regresiilor.

2026-04 · Rută Redirect-Worker pentru coduri QR dinamice

  • Fix: Codurile QR dinamice de la https://qr3.app/{shortCode} sunt procesate din nou de Redirect-Worker. Ruta de producție folosește acum qr3.app/*, deoarece rutele Cloudflare Workers nu acceptă parametri de cale de tip :code.
  • Securizare: Căile care nu se potrivesc sunt redirecționate către originea paginii de destinație (landing origin), astfel încât paginile normale precum /de/pricing să nu fie blocate de Redirect-Worker.
  • Teste: packages/redirect/tests/unit/redirect.test.ts verifică ruta wildcard, procesarea codurilor scurte și trecerea directă către origine (origin pass-through).

2026-04 · Prezentare generală scanări DPP în Workspace (Q3.4.2)

  • Nou: GET /v1/workspace/stats/dpp?days=30 — agreghează toate scanările dpp_scans din spațiul de lucru (workspace) al cheii API (active_dpps, scans_by_day, top_dpps cu nume produs/categorie).
  • Dashboard: Card pe pagina principală (/dashboard) cu diagramă cu bare pe 30 de zile + liste de top — în paralel cu cardurile de coduri QR.
  • Public: Link scurt de marketing GET /dpp/dpp_<id> (un singur segment) pentru demo-uri live, în paralel cu /dpp/{gtin}/{serial}.

2026-04 · Analize scanări DPP (Q3.4.1)

  • Nou: GET /v1/dpp/:id/stats?days=30 — scanări agregate ale resolverului public GS1 per DPP. Câmpuri: total_scans, period_scans, scans_by_day, top_countries, top_devices, top_representations.
  • Nou: Tabelul dpp_scans (migrarea 0011) — separat de scans (Redirect-Worker). Adresele IP sunt criptate (hashed) cu un salt rotativ zilnic, adresele IP brute neajungând niciodată în D1.
  • Dashboard: Card cu mini-grafic (SVG, fără bibliotecă de grafice) pe /dashboard/dpp/:dppId cu bare pe 30 de zile + defalcări Top 3. Stare goală (empty state) de îndată ce un DPP este live, dar nu are încă scanări.

2026-04 · Simulator live de conformitate UE (Q3.3.7)

  • Nou: POST /v1/dpp/:id/validate-update — simulează actualizări parțiale stateless (status, listă de piețe, …) fără persistență. Răspunsul conține eu_compliance + preview.changed_fields.
  • Dashboard: Card simulator în detaliile DPP (/dashboard/dpp/:dppId) — cipuri pentru DE/AT/FR/IT/ES/NL + personalizat, meniu derulant pentru status, Preview EU impact / Save changes / Reset. Non-blocking prin Remix useFetcher.
  • Securizare: Funcții ajutătoare (helpers) de simulator externalizate (readUpdatePatchFromForm, marketCountriesKey) + 18 teste unitare noi; remediere bug: introducerea unei singure valori non-ISO nu mai șterge lista de piețe.

2026-04 · Previzualizare live a conformității UE în formularul de creare (Q3.3.6)

  • Modificat: POST /v1/dpp/validate returnează în plus eu_compliance — același validator ca GET /v1/dpp/:id/eu-compliance, stateless înainte de salvare.
  • Dashboard: Previzualizare sub panoul de validare existent + un nou banner de protecție la salvare (Save-Guard) înainte de butoanele de trimitere, dacă există erori/avertismente nerezolvate (pluralizare i18n DE/EN).

2026-04 · Validator UE + UI pentru textile (Q3.3.4 + Q3.3.5)

  • Nou: Validator de conformitate UE cu 5 reguli pentru textile (TEXTILE_AGEC_REQUIRED, TEXTILE_MICROPLASTICS_CONSISTENCY, TEXTILE_SVHC_THRESHOLD, TEXTILE_GREENWASHING, TEXTILE_ESPR_READY).
  • Nou: GET /v1/dpp/:id/eu-compliance cu compliant / espr_ready / issues[] / summary.
  • Dashboard: Secțiune de conformitate UE în detaliile DPP (dale de rezumat, carduri de probleme grupate, insignă ESPR-Ready în antet).

2026-04 · Schemă DPP pentru textile (Q3.3.1–Q3.3.3)

  • Nou: Categoria textile cu lanț obligatoriu AGEC (țesut/tricotat → vopsit/imprimat → confecționare), per fibră origin_country + recycled_pct, svhc_substances[], ESPR-Opt-in (PEF, durată de viață, Recyclability).
  • Nou: Câmp de bază market_countries: string[] (ISO 3166-1 alpha-2) pentru toate categoriile DPP — controlează regulile AGEC specifice Franței și nota obligatorie pentru consumatorii francezi.
  • Nou: Șablon HTML pentru consumatori cu casetă de avertizare AGEC pentru microplastic, lanț de origine în 3 etape (pastile cu steaguri), listă SVHC, secțiune de Durability și Recyclability.
  • Migrare: 0010_dpp_market_countries (D1).

2026-04 · Import în masă (Bulk) DPP (Q3.2.1–Q3.2.5)

  • Nou: POST /v1/dpp/import acceptă CSV și XLSX (compatibil cu Workers prin SheetJS xlsx, pachet gzip de ~283 KB).
  • Scalat: limită bazată pe plan (Free 100 → Enterprise 10k) + db.batch() fragmentat (chunked) de câte 100 + limită de 5 MB pentru body.
  • Nou: Raport de erori în format CSV în câmpul errors_csv al răspunsului 201; GET /v1/dpp/import/templates/:category?format=csv|xlsx oferă șabloane gata de utilizare pentru baterii și textile.
  • Dashboard: Încărcare prin glisare și plasare (drag-and-drop) la /dashboard/dpp/import cu proxy de șabloane și descărcare CSV inline.

Modificări non-breaking — Extinderi LTS

Toate modificările menționate mai sus sunt aditive:

  • Clienții existenți ai POST /v1/dpp/validate ignoră noul câmp eu_compliance fără modificări.
  • Fluxurile battery existente rămân neschimbate.
  • market_countries este opțional și are valoarea implicită [].

Consultă Versiunea API pentru politica privind modificările incompatibile (breaking changes).