jscontact 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2b5fd75452d4b1982b14996be3513fef5543d65f36e026b75ab0fb320a7d5f16
4
+ data.tar.gz: 0f938551f087628168cb7a5fe896acf1f339fb5034355305da5c2175b6deada2
5
+ SHA512:
6
+ metadata.gz: 053177fa21d75e63a1a4434ba7418aef8096d0a3399a315bd80bc393c05f79ed6509a77202be8197f773172079580fc0e75b75738af1c5f6e57285c5702afaa9
7
+ data.tar.gz: bd31c7364dca93a41ceb9dd11585b89e584df900d1e722598b5b43b65eec08d23f0324d43bc9eb1cbac5ecf99e3b963b48acec9b7ff10e0bdf3ef105a05e8440
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-10-12
4
+
5
+ Initial release.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # JSContact
2
+
3
+ [![Ruby](https://github.com/ydah/jscontact/actions/workflows/main.yml/badge.svg)](https://github.com/ydah/jscontact/actions/workflows/main.yml)
4
+
5
+ Parse, validate, build, and convert JSContact contact cards in Ruby. Supports
6
+ [RFC 9553](https://www.rfc-editor.org/rfc/rfc9553),
7
+ [RFC 9555](https://www.rfc-editor.org/rfc/rfc9555), and
8
+ [RFC 9982](https://www.rfc-editor.org/rfc/rfc9982).
9
+
10
+ Unknown properties, vendor extensions, and map identifiers survive JSON round
11
+ trips. New cards use JSContact 2.0; reading a 1.0 card keeps its version until you
12
+ explicitly upgrade it.
13
+
14
+ ## Installation
15
+
16
+ Requires Ruby 3.3 or later. CI also tests JRuby and TruffleRuby on Linux and macOS.
17
+
18
+ ```ruby
19
+ gem "jscontact"
20
+ # Add only when using the optional vCard converter:
21
+ gem "jcard_jcal", "~> 0.1.0"
22
+ ```
23
+
24
+ The JSON API depends on `json` and `tzinfo`. On systems without a zoneinfo database
25
+ (such as Windows), also install `tzinfo-data` for time zone and country validation.
26
+
27
+ ## Read and build cards
28
+
29
+ ```ruby
30
+ require "jscontact"
31
+
32
+ card = JSContact::Card.build do |contact|
33
+ contact.name = { full: "Mina Sample" }
34
+ contact.add_email(address: "mina@example.test", contexts: { work: true })
35
+ contact["example.test:customer"] = { "number" => 42 }
36
+ end
37
+
38
+ card.name.full # => "Mina Sample"
39
+ card.emails["e1"].address # => "mina@example.test"
40
+ card.emails["e1"].label = "Desk" # updates the parent card
41
+ card.to_h # independent, deep copy
42
+ json = card.to_json # validates before serializing
43
+ copy = JSContact.parse(json)
44
+ ```
45
+
46
+ `JSContact` and `Jscontact` refer to the same module. The schema defines typed
47
+ classes such as `Name`, `Address`, and `EmailAddress`; nested accessors return typed
48
+ views. `[]` and `[]=` also accept unknown property names. Camel-case property
49
+ accessors have snake-case aliases, for example `preferred_languages`.
50
+
51
+ `Card.new(hash)` copies the input and permits incomplete data while building a
52
+ card. Use `strict: true` to validate immediately. Constructors accept symbol keys
53
+ and normalize them to strings; duplicate normalized keys are rejected.
54
+
55
+ `to_h` preserves absent default properties; accessors expose their defaults.
56
+ `Card.build(explicit_types: true)` includes optional nested `@type` members when
57
+ exporting. Nested objects serialized on their own include their required `@type`.
58
+ Instances are mutable; give each concurrent worker its own card.
59
+
60
+ ## Validation and versions
61
+
62
+ ```ruby
63
+ card = JSContact.parse(json, strict: false)
64
+ card.valid?
65
+ card.warnings.each { |violation| puts "#{violation.pointer}: #{violation.message}" }
66
+ card.validate! # raises JSContact::ValidationError if invalid
67
+
68
+ upgraded = JSContact.upgrade(card) # validates, copies, and sets version to "2.0"
69
+ ```
70
+
71
+ `parse` is strict by default. Lenient parsing retains schema violations in
72
+ `errors` (also available as `warnings`); malformed JSON, duplicate keys, unsupported
73
+ Ruby values, and resource-limit violations still raise. Serialization is always
74
+ strict. `ValidationError#errors` contains every collected `Violation`, with a JSON
75
+ Pointer and message; the empty pointer identifies the document root.
76
+
77
+ Version 1.0 requires `uid`; version 2.0 permits it to be absent. Upgrading changes
78
+ only the version, without inventing a UID or renumbering map identifiers. Unknown
79
+ properties with valid names are preserved. Unknown enum values are reported as
80
+ violations unless they use the vendor extension syntax.
81
+
82
+ ## Patches and localizations
83
+
84
+ ```ruby
85
+ card.localizations = { "ja" => { "name/full" => "サンプル ミナ" } }
86
+ japanese = card.localized("ja")
87
+
88
+ patched = JSContact::PatchObject.new({ "name/full" => "Mina Updated" }).apply(card.to_h)
89
+ ```
90
+
91
+ Patch keys are JSON Pointers without the leading slash. `nil` deletes an object
92
+ member; array entries can be replaced but cannot be inserted or removed. Escapes,
93
+ parent paths, overlapping paths, resulting types, and required properties are
94
+ checked before returning a result. Neither the patch nor its target is modified.
95
+ Schema validation applies when the target has a recognized `@type`; untyped
96
+ objects receive JSON structure validation.
97
+
98
+ `localized` matches language tags without case sensitivity, applies the selected
99
+ patch to a copy, removes its localization map, and sets its language. If no patch
100
+ matches, it returns an unchanged copy. Patches cannot modify `localizations` itself.
101
+
102
+ ## vCard conversion
103
+
104
+ ```ruby
105
+ require "jscontact/vcard"
106
+
107
+ text = JSContact::VCard.from_jscontact(card)
108
+ restored = JSContact::VCard.to_jscontact(text) # JSContact 2.0
109
+ ```
110
+
111
+ Only the optional entrypoint loads `jcard_jcal`. Conversion accepts exactly one
112
+ vCard 4.0. A missing UID stays absent when converting to JSContact 2.0. Requesting
113
+ `version: "1.0"` emits a deprecation warning and supplies a stable UID if necessary.
114
+
115
+ | RFC 9555 rules | Support |
116
+ | --- | --- |
117
+ | Sections 2.2–2.3: value types and parameters | Both directions, including PROP-ID, ALTID, LANGUAGE, and JSCOMPS |
118
+ | Sections 2.4–2.6: general properties, identification, delivery addresses | Both directions, including phonetic values |
119
+ | Sections 2.7–2.10: communication, geography, organizations, personal information | Both directions |
120
+ | Sections 2.11–2.14: explanatory, security, calendar, and extended properties | Both directions |
121
+ | Section 2.15: unmatched properties and parameters | Retained as `vCardProps`, `vCardParams`, and `vCardName` |
122
+ | Section 3: JSContact to vCard | Native properties plus JSPROP/JSPTR for otherwise unrepresentable data |
123
+
124
+ Conversion preserves data, rather than the original text's property order,
125
+ folding, or choice of equivalent representation. Date forms without a JSContact
126
+ equivalent and unmatched birth/death place properties are retained in `vCardProps`.
127
+
128
+ JSContact date fields use the Gregorian calendar even when `calendarScale` names
129
+ another calendar. The converter preserves non-Gregorian vCard CALSCALE properties
130
+ in `vCardProps`; it does not perform calendar arithmetic. In the other direction,
131
+ it emits a Gregorian native date and retains non-Gregorian metadata through JSPROP.
132
+ The underlying vCard parser checks Gregorian date validity, so dates from an
133
+ unsupported calendar that fail those checks raise `ConversionError`.
134
+
135
+ A top-level unknown property whose value is JSON `null` cannot be represented by
136
+ JSPROP: its PatchObject semantics mean deletion. Conversion raises
137
+ `ConversionError` with the property pointer instead of dropping it. Nested nulls
138
+ inside a retained object or array are supported. Ordinary JSON round trips retain
139
+ all null values.
140
+
141
+ ## Supported scope
142
+
143
+ | Feature | Status |
144
+ | --- | --- |
145
+ | RFC 9553 object models, validation, JSON generation | Supported |
146
+ | RFC 9982 JSContact 2.0 and optional UID | Supported; 1.0 is retained when read |
147
+ | PatchObject and localizations | Supported |
148
+ | RFC 9555 and RFC 9554 vCard 4.0 conversion | Supported as described above |
149
+ | Unknown properties and vendor JSON | Preserved, subject to valid names and I-JSON constraints |
150
+ | JMAP for Contacts | Card data supported; protocol operations are outside scope |
151
+ | vCard 2.1/3.0, CardDAV sync, RDAP profiles, locale-specific display formatting | Outside scope |
152
+
153
+ ## Resource limits
154
+
155
+ These options apply to `parse` and model constructors. Exceeding a limit raises
156
+ `LimitError`, including in lenient mode.
157
+
158
+ | Option | Default | Applies to |
159
+ | --- | --- | --- |
160
+ | `max_input_size` | 16 MiB | JSON input bytes in `parse` |
161
+ | `max_depth` | 32 | Nested JSON objects and arrays, including vendor values |
162
+ | `max_map_size` | 10,000 | Schema-defined maps and arrays |
163
+ | `max_data_uri_size` | 1 MiB | Estimated decoded data URI size |
164
+ | `max_patches` | 1,000 | Total localization patches, or entries in a PatchObject |
165
+
166
+ ```ruby
167
+ JSContact.parse(json, max_input_size: 1_048_576, max_depth: 16, max_map_size: 500)
168
+ ```
169
+
170
+ The gem never fetches resource URIs or resolves linked contacts. vCard text parsing
171
+ also uses `jcard_jcal`'s own parser limits. See [SECURITY.md](SECURITY.md) for reporting
172
+ vulnerabilities.
173
+
174
+ ## Development
175
+
176
+ ```sh
177
+ bundle install
178
+ bundle exec rake # fetch and verify external RFC inputs, then test
179
+ COVERAGE=1 bundle exec rake # minimum 90% line coverage
180
+ bundle exec rubocop
181
+ bundle exec rbs -I sig validate
182
+ bundle exec ruby script/interop.rb # requires stable Rust; pinned calcard comparison
183
+ bundle exec ruby script/benchmark.rb # time, allocation and RSS measurements at limits
184
+ bundle exec ruby script/update_registry.rb # verify pinned IANA registry coverage
185
+ bundle exec ruby script/update_registry.rb --refresh # audit current IANA data and update pins
186
+ ```
187
+
188
+ The suite checks 97 generated RFC examples, both directions of 54 vCard conversion
189
+ pairs, malformed input, patches, limits, and deterministic random round trips.
190
+ See [conformance notes](docs/conformance.md) for extraction corrections, reported
191
+ errata, and the two precisely checked upstream interoperability differences.
192
+ The [boundary measurements](docs/performance.md) explain the default limits.
193
+
194
+ RFC originals, extracted examples, IANA downloads, and external comparison sources
195
+ live only in ignored `tmp/` directories. Downloads retain their applicable notices;
196
+ RFC source hashes and the generated corpus fingerprint are checked before testing.
197
+ The packaged gem contains library code, original schema metadata, RBS, and user
198
+ documentation, excluding test corpora and third-party source code.
199
+
200
+ ## Release
201
+
202
+ Configure RubyGems Trusted Publishing for owner `ydah`, repository `jscontact`,
203
+ workflow `release.yml`, and GitHub environment `release`. After checks pass, update
204
+ the gem version and CHANGELOG, then push the matching `vVERSION` tag. The workflow
205
+ tests, builds, publishes through OIDC, and creates a GitHub release from that
206
+ version's CHANGELOG entry. A version already on RubyGems is not published again.
207
+
208
+ ## License
209
+
210
+ The Ruby library is available under the [MIT License](LICENSE.txt). External test
211
+ inputs and comparison dependencies retain their own licenses and are not included
212
+ in the gem.
data/SECURITY.md ADDED
@@ -0,0 +1,6 @@
1
+ # Security
2
+
3
+ Report vulnerabilities through [GitHub private vulnerability reporting](https://github.com/ydah/jscontact/security/advisories/new).
4
+ Please include a minimal reproducer and the affected gem and Ruby versions.
5
+
6
+ The library treats documents as data. It does not retrieve contact URIs or execute embedded content.
data/data/enums.yml ADDED
@@ -0,0 +1,356 @@
1
+ # Original validation metadata; names are protocol identifiers, not copied examples.
2
+ ---
3
+ cardKind:
4
+ - individual
5
+ - group
6
+ - org
7
+ - location
8
+ - device
9
+ - application
10
+ phoneticSystem:
11
+ - ipa
12
+ - jyut
13
+ - piny
14
+ context:
15
+ - private
16
+ - work
17
+ addressContext:
18
+ - private
19
+ - work
20
+ - billing
21
+ - delivery
22
+ relation:
23
+ - acquaintance
24
+ - agent
25
+ - child
26
+ - co-resident
27
+ - co-worker
28
+ - colleague
29
+ - contact
30
+ - crush
31
+ - date
32
+ - emergency
33
+ - friend
34
+ - kin
35
+ - me
36
+ - met
37
+ - muse
38
+ - neighbor
39
+ - parent
40
+ - sibling
41
+ - spouse
42
+ - sweetheart
43
+ nameKind:
44
+ - title
45
+ - given
46
+ - given2
47
+ - surname
48
+ - surname2
49
+ - credential
50
+ - generation
51
+ - separator
52
+ gender:
53
+ - animate
54
+ - common
55
+ - feminine
56
+ - inanimate
57
+ - masculine
58
+ - neuter
59
+ titleKind:
60
+ - title
61
+ - role
62
+ phoneFeature:
63
+ - mobile
64
+ - voice
65
+ - text
66
+ - video
67
+ - main-number
68
+ - textphone
69
+ - fax
70
+ - pager
71
+ calendarKind:
72
+ - calendar
73
+ - freeBusy
74
+ addressKind:
75
+ - room
76
+ - apartment
77
+ - floor
78
+ - building
79
+ - number
80
+ - name
81
+ - block
82
+ - subdistrict
83
+ - district
84
+ - locality
85
+ - region
86
+ - postcode
87
+ - country
88
+ - direction
89
+ - landmark
90
+ - postOfficeBox
91
+ - separator
92
+ directoryKind:
93
+ - directory
94
+ - entry
95
+ linkKind:
96
+ - contact
97
+ mediaKind:
98
+ - photo
99
+ - sound
100
+ - logo
101
+ anniversaryKind:
102
+ - birth
103
+ - death
104
+ - wedding
105
+ personalKind:
106
+ - expertise
107
+ - hobby
108
+ - interest
109
+ level:
110
+ - high
111
+ - medium
112
+ - low
113
+ calendarScale:
114
+ - gregorian
115
+ - buddhist
116
+ - chinese
117
+ - coptic
118
+ - dangi
119
+ - ethiopic
120
+ - ethiopic-amete-alem
121
+ - hebrew
122
+ - indian
123
+ - islamic
124
+ - islamic-civil
125
+ - islamic-rgsa
126
+ - islamic-tbla
127
+ - islamic-umalqura
128
+ - iso8601
129
+ - japanese
130
+ - persian
131
+ - roc
132
+ script:
133
+ - Adlm
134
+ - Afak
135
+ - Aghb
136
+ - Ahom
137
+ - Arab
138
+ - Aran
139
+ - Armi
140
+ - Armn
141
+ - Avst
142
+ - Bali
143
+ - Bamu
144
+ - Bass
145
+ - Batk
146
+ - Beng
147
+ - Berf
148
+ - Bhks
149
+ - Blis
150
+ - Bopo
151
+ - Brah
152
+ - Brai
153
+ - Bugi
154
+ - Buhd
155
+ - Cakm
156
+ - Cans
157
+ - Cari
158
+ - Cham
159
+ - Cher
160
+ - Chis
161
+ - Chrs
162
+ - Cirt
163
+ - Copt
164
+ - Cpmn
165
+ - Cprt
166
+ - Cyrl
167
+ - Cyrs
168
+ - Deva
169
+ - Diak
170
+ - Dogr
171
+ - Dsrt
172
+ - Dupl
173
+ - Egyd
174
+ - Egyh
175
+ - Egyp
176
+ - Elba
177
+ - Elym
178
+ - Ethi
179
+ - Gara
180
+ - Geok
181
+ - Geor
182
+ - Glag
183
+ - Gong
184
+ - Gonm
185
+ - Goth
186
+ - Gran
187
+ - Grek
188
+ - Gujr
189
+ - Gukh
190
+ - Guru
191
+ - Hanb
192
+ - Hang
193
+ - Hani
194
+ - Hano
195
+ - Hans
196
+ - Hant
197
+ - Hatr
198
+ - Hebr
199
+ - Hira
200
+ - Hluw
201
+ - Hmng
202
+ - Hmnp
203
+ - Hntl
204
+ - Hrkt
205
+ - Hung
206
+ - Inds
207
+ - Ital
208
+ - Jamo
209
+ - Java
210
+ - Jpan
211
+ - Jurc
212
+ - Kali
213
+ - Kana
214
+ - Kawi
215
+ - Khar
216
+ - Khmr
217
+ - Khoj
218
+ - Kitl
219
+ - Kits
220
+ - Knda
221
+ - Kore
222
+ - Kpel
223
+ - Krai
224
+ - Kthi
225
+ - Lana
226
+ - Laoo
227
+ - Latf
228
+ - Latg
229
+ - Latn
230
+ - Leke
231
+ - Lepc
232
+ - Limb
233
+ - Lina
234
+ - Linb
235
+ - Lisu
236
+ - Loma
237
+ - Lyci
238
+ - Lydi
239
+ - Mahj
240
+ - Maka
241
+ - Mand
242
+ - Mani
243
+ - Marc
244
+ - Maya
245
+ - Medf
246
+ - Mend
247
+ - Merc
248
+ - Mero
249
+ - Mlym
250
+ - Modi
251
+ - Mong
252
+ - Moon
253
+ - Mroo
254
+ - Mtei
255
+ - Mult
256
+ - Mymr
257
+ - Nagm
258
+ - Nand
259
+ - Narb
260
+ - Nbat
261
+ - Newa
262
+ - Nkdb
263
+ - Nkgb
264
+ - Nkoo
265
+ - Nshu
266
+ - Ogam
267
+ - Olck
268
+ - Onao
269
+ - Orkh
270
+ - Orya
271
+ - Osge
272
+ - Osma
273
+ - Ougr
274
+ - Palm
275
+ - Pauc
276
+ - Pcun
277
+ - Pelm
278
+ - Perm
279
+ - Phag
280
+ - Phli
281
+ - Phlp
282
+ - Phlv
283
+ - Phnx
284
+ - Piqd
285
+ - Plrd
286
+ - Prti
287
+ - Psin
288
+ - Ranj
289
+ - Rjng
290
+ - Rohg
291
+ - Roro
292
+ - Runr
293
+ - Samr
294
+ - Sara
295
+ - Sarb
296
+ - Saur
297
+ - Seal
298
+ - Sgnw
299
+ - Shaw
300
+ - Shrd
301
+ - Shui
302
+ - Sidd
303
+ - Sidt
304
+ - Sind
305
+ - Sinh
306
+ - Sogd
307
+ - Sogo
308
+ - Sora
309
+ - Soyo
310
+ - Sund
311
+ - Sunu
312
+ - Sylo
313
+ - Syrc
314
+ - Syre
315
+ - Syrj
316
+ - Syrn
317
+ - Tagb
318
+ - Takr
319
+ - Tale
320
+ - Talu
321
+ - Taml
322
+ - Tang
323
+ - Tavt
324
+ - Tayo
325
+ - Telu
326
+ - Teng
327
+ - Tfng
328
+ - Tglg
329
+ - Thaa
330
+ - Thai
331
+ - Tibt
332
+ - Tirh
333
+ - Tnsa
334
+ - Todr
335
+ - Tols
336
+ - Toto
337
+ - Tutg
338
+ - Ugar
339
+ - Vaii
340
+ - Visp
341
+ - Vith
342
+ - Wara
343
+ - Wcho
344
+ - Wole
345
+ - Xpeo
346
+ - Xsux
347
+ - Yezi
348
+ - Yiii
349
+ - Zanb
350
+ - Zinh
351
+ - Zmth
352
+ - Zsye
353
+ - Zsym
354
+ - Zxxx
355
+ - Zyyy
356
+ - Zzzz