Revision history for Perl extension Text::JSContact

0.06  2026-11-07
	- writing is deterministic: the same Card gives the same bytes in
	  every process.  Text::VCardFast's hash2vcard wrote parameters in
	  Perl's hash order, so every save of an unchanged contact had a new
	  CardDAV ETag.  The module now writes content lines itself, with
	  parameters in a fixed order (PROP-ID, VALUE, TYPE, PREF, then by
	  name), folded at 75 octets without splitting a character or an
	  escape; Text::VCardFast is still the parser
	- add t/deterministic.t
	- a conversion keeps its counters in a context made for that call,
	  not in package variables.  An id allocated to a property without
	  PROP-ID skips every PROP-ID the vCard has, so EMAIL;PROP-ID=1 and
	  an unnumbered EMAIL no longer both become emails{1}, one lost.
	  A label group allocated by patch_vcard skips every group the
	  original vCard has, so a new label no longer takes item1 from a
	  property that already has one
	- patch_vcard patches the original line by line instead of
	  re-serialising the whole vCard: a line for an entry the change does
	  not touch keeps its bytes, group, folding and place, as does every
	  line JSContact cannot represent.  A changed entry is written over
	  its own line, keeping its group, the parameters the reader does not
	  interpret and the TYPE values it does not map; a removed entry takes
	  its line, its label and the rest of its group with it; a new entry
	  goes after the lines of the same map.  Labels are patched for every
	  property the reader labels, Apple addresses keep X-ABADR/GEO/TZ in
	  their group, an Apple relation change is written as its X-ABLabel,
	  and X-ABDATE and X-SOCIALPROFILE keep their names
	- patch_vcard keeps a vCard 3 card vCard 3: lines patched into it have
	  no PROP-ID, TYPE=pref for PREF=1, ENCODING=b for an inline data: URI,
	  X-SERVICE-TYPE/X-USER, Apple's X-ADDRESSBOOKSERVER-KIND/-MEMBER,
	  X- forms of the other vCard 4 parameters (which the reader now also
	  reads) and 1604 for a date without a year
	- UTF-8: a vCard is decoded once, before parsing, so every value is
	  characters (the ORG name, EMAIL, CATEGORIES, IMPP, RELATED and
	  several parameters were left as bytes and double-encoded when
	  written), and patch_vcard returns bytes for bytes and characters for
	  characters instead of splicing characters into bytes
	- jscontact_to_vcard($card, original => $vcard) writes the Card as a
	  patch of the vCard it was read from, reading the old Card from
	  $vcard itself, so a caller no longer has to keep (and not touch) the
	  Card it first read; an unparseable original gives a new vCard, as
	  without original.  patch_vcard still works
	- add t/original.t
	- several values of one N or ADR component, and the keywords in
	  CATEGORIES, are written as a list (comma-separated) instead of one
	  value with its commas escaped; a comma inside one value is still
	  escaped
	- a TEL whose number is a URI (tel:...) is written with VALUE=uri, so
	  it is not escaped as TEXT (tel:+1-555\;ext=5); in a vCard 3 patch it
	  is written as a phone number
	- a RELATED that is free text, not a URI, is written with VALUE=text

0.05  2026-11-07
	- JSContact version 2.0 (RFC 9982): vcard_to_jscontact writes
	  version "2.0", and a vCard without UID gives a Card without uid
	  instead of one with a random uuid (which changed on every
	  conversion, so nothing could refer to it); jscontact_to_vcard
	  already wrote no UID for a Card without uid
	- write URI values (PHOTO, MEMBER, URL, ...) unescaped
	- read a vCard 3 inline photo once, including one whose TYPE is a list;
	  skip a Media with no uri instead of writing an empty PHOTO
	- add t/rfc9982.t

0.04  2026-10-22
	- security: strip control characters from vCard output; Text::VCardFast
	  escapes LF but writes a CR raw, so a CR in any card field could end
	  the content line early and forge extra properties.  CR and CRLF now
	  become LF (escaped as \n, or ^n in parameters) and control
	  characters other than HTAB are dropped
	- add t/injection.t

0.03  2026-10-09
	- read IMPP's RFC 9554 SERVICE-TYPE and USERNAME parameters, the ones
	  jscontact_to_vcard writes, as well as the older X-SERVICE-TYPE and
	  X-USER; an OnlineService lost its service and user on a round trip
	- an IMPP whose value is just the USERNAME (a service with no uri)
	  no longer comes back with that name as its uri
	- add t/online-services.t

0.02  2026-08-31
	- clarify licensing: same terms as Perl itself (Artistic License 1.0
	  or GPL version 1 or later)
	- include a LICENSE file in the distribution
	- add POD with NAME/AUTHOR/LICENSE (the module had none)
	- README previously claimed Artistic License 2.0, which contradicted
	  the Perl_5 metadata; the README was wrong

0.01  2026-04-05
	- Initial release
	- Bidirectional vCard <=> JSContact conversion
	- Follows RFCs 9553 (JSContact), 9554 (extensions), 9555 (vCard mapping)
	- Covers: name, emails, phones, addresses, organizations, titles,
	  anniversaries, notes, onlineServices, media, links, calendars,
	  schedulingAddresses, cryptoKeys, directories, keywords, members,
	  relatedTo, speakToAs, preferredLanguages, personalInfo
	- 192 unit tests including RFC 9555 test vectors
	- Cyrus CardDAV integration tests
