@nuggetslife/vc 0.1.0 → 0.3.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.
Files changed (70) hide show
  1. package/Cargo.toml +1 -0
  2. package/W3C_CONFORMANCE.md +9 -6
  3. package/bench/frame_compare.mjs +7 -20
  4. package/bench/v2_internals.mjs +3 -5
  5. package/bench/vc_ops.mjs +4 -10
  6. package/index.d.ts +2 -2
  7. package/interop-allowlist.json +3 -0
  8. package/interop-smoke-allowlist.json +3 -0
  9. package/package.json +8 -7
  10. package/src/bls_signatures/bbs_bls_holder_bound_signature_2022/mod.rs +18 -27
  11. package/src/bls_signatures/bbs_bls_holder_bound_signature_proof_2022/mod.rs +9 -10
  12. package/src/bls_signatures/bbs_bls_signature_2020/mod.rs +21 -25
  13. package/src/bls_signatures/bbs_bls_signature_proof_2020/mod.rs +6 -12
  14. package/src/jsonld.rs +40 -75
  15. package/src/ld_signatures.rs +210 -224
  16. package/src/lib.rs +8 -0
  17. package/test-fixtures/interop/README.md +46 -0
  18. package/test-fixtures/interop/_contexts/README.md +51 -0
  19. package/test-fixtures/interop/_contexts/bbs-bound-v1.jsonld +92 -0
  20. package/test-fixtures/interop/_contexts/citizenship-v1.jsonld +58 -0
  21. package/test-fixtures/interop/_contexts/credentials-v1.jsonld +316 -0
  22. package/test-fixtures/interop/_contexts/elm-edc-ap.jsonld +809 -0
  23. package/test-fixtures/interop/_contexts/essif-schemas-vc-2020-v1.jsonld +47 -0
  24. package/test-fixtures/interop/_contexts/identity-v2.jsonld +195 -0
  25. package/test-fixtures/interop/_contexts/nuggets-identity-v1.jsonld +175 -0
  26. package/test-fixtures/interop/_contexts/nuggets-kyb-v1.jsonld +333 -0
  27. package/test-fixtures/interop/_contexts/openbadges-v3.jsonld +445 -0
  28. package/test-fixtures/interop/_contexts/security-bbs-v1.jsonld +93 -0
  29. package/test-fixtures/interop/ebsi/diploma-elm/README.md +29 -0
  30. package/test-fixtures/interop/ebsi/diploma-elm/expected.nq +70 -0
  31. package/test-fixtures/interop/ebsi/diploma-elm/frame.jsonld +24 -0
  32. package/test-fixtures/interop/ebsi/diploma-elm/input.jsonld +444 -0
  33. package/test-fixtures/interop/ebsi/diploma-simple/README.md +19 -0
  34. package/test-fixtures/interop/ebsi/diploma-simple/expected.nq +9 -0
  35. package/test-fixtures/interop/ebsi/diploma-simple/frame.jsonld +13 -0
  36. package/test-fixtures/interop/ebsi/diploma-simple/input.jsonld +24 -0
  37. package/test-fixtures/interop/idv2/full-disclosure/README.md +9 -0
  38. package/test-fixtures/interop/idv2/full-disclosure/expected.nq +59 -0
  39. package/test-fixtures/interop/idv2/full-disclosure/frame.jsonld +9 -0
  40. package/test-fixtures/interop/idv2/full-disclosure/input.jsonld +82 -0
  41. package/test-fixtures/interop/nuggets/identity-v1/README.md +17 -0
  42. package/test-fixtures/interop/nuggets/identity-v1/expected.nq +21 -0
  43. package/test-fixtures/interop/nuggets/identity-v1/frame.jsonld +17 -0
  44. package/test-fixtures/interop/nuggets/identity-v1/input.jsonld +31 -0
  45. package/test-fixtures/interop/nuggets/kyb-v1/README.md +15 -0
  46. package/test-fixtures/interop/nuggets/kyb-v1/expected.nq +18 -0
  47. package/test-fixtures/interop/nuggets/kyb-v1/frame.jsonld +24 -0
  48. package/test-fixtures/interop/nuggets/kyb-v1/input.jsonld +60 -0
  49. package/test-fixtures/interop/openbadges-v3/basic-achievement/README.md +17 -0
  50. package/test-fixtures/interop/openbadges-v3/basic-achievement/expected.nq +12 -0
  51. package/test-fixtures/interop/openbadges-v3/basic-achievement/frame.jsonld +17 -0
  52. package/test-fixtures/interop/openbadges-v3/basic-achievement/input.jsonld +25 -0
  53. package/test-fixtures/interop/openbadges-v3/with-allowed-values/README.md +11 -0
  54. package/test-fixtures/interop/openbadges-v3/with-allowed-values/expected.nq +25 -0
  55. package/test-fixtures/interop/openbadges-v3/with-allowed-values/frame.jsonld +22 -0
  56. package/test-fixtures/interop/openbadges-v3/with-allowed-values/input.jsonld +40 -0
  57. package/test-fixtures/interop/vp/single-vc-wrap/README.md +6 -0
  58. package/test-fixtures/interop/vp/single-vc-wrap/expected.nq +7 -0
  59. package/test-fixtures/interop/vp/single-vc-wrap/frame.jsonld +17 -0
  60. package/test-fixtures/interop/vp/single-vc-wrap/input.jsonld +27 -0
  61. package/test-fixtures/interop/w3c-vc-v1/permanent-resident-card/README.md +5 -0
  62. package/test-fixtures/interop/w3c-vc-v1/permanent-resident-card/expected.nq +13 -0
  63. package/test-fixtures/interop/w3c-vc-v1/permanent-resident-card/frame.jsonld +14 -0
  64. package/test-fixtures/interop/w3c-vc-v1/permanent-resident-card/input.jsonld +29 -0
  65. package/test_interop.mjs +184 -0
  66. package/test_interop_smoke.mjs +388 -0
  67. package/test_w3c_conformance.mjs +7 -0
  68. package/tools/regen_expected.mjs +108 -0
  69. package/w3c-baseline.json +63 -74
  70. package/w3c-denylist.json +6 -1
package/src/jsonld.rs CHANGED
@@ -2,17 +2,14 @@ use serde_json::{json, Value};
2
2
  use std::collections::HashMap;
3
3
  use std::sync::Arc;
4
4
 
5
- use vc::{
6
- document_loader::nuggets::DocumentLoader,
7
- jsonld::{self, context_resolver::ContextResolver},
8
- };
5
+ use vc::document_loader::nuggets::DocumentLoader;
9
6
 
10
7
  use crate::ld_signatures::create_document_loader;
11
8
 
12
9
  /// JSON-LD processor — drop-in replacement for the jsonld.js API.
13
10
  ///
14
- /// Holds a `DocumentLoader` and `ContextResolver` internally so they are
15
- /// created once and reused across all method calls.
11
+ /// Holds a `DocumentLoader` internally so it is created once and reused
12
+ /// across all method calls.
16
13
  ///
17
14
  /// ```js
18
15
  /// const jsonld = new JsonLd({ contexts: { "https://example.org/ctx": {...} } });
@@ -22,7 +19,6 @@ use crate::ld_signatures::create_document_loader;
22
19
  #[napi]
23
20
  pub struct JsonLd {
24
21
  loader: Arc<DocumentLoader>,
25
- cr: Arc<ContextResolver>,
26
22
  additional_contexts: Arc<HashMap<String, Value>>,
27
23
  }
28
24
 
@@ -47,10 +43,9 @@ impl JsonLd {
47
43
  .and_then(|v| v.as_object())
48
44
  .map(|obj| obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
49
45
  .unwrap_or_default();
50
- let (loader, cr) = create_document_loader(additional_contexts.clone());
46
+ let loader = create_document_loader(additional_contexts.clone());
51
47
  Self {
52
48
  loader,
53
- cr,
54
49
  additional_contexts: Arc::new(additional_contexts),
55
50
  }
56
51
  }
@@ -64,15 +59,9 @@ impl JsonLd {
64
59
  #[napi]
65
60
  pub async fn expand(&self, input: Value, options: Option<Value>) -> napi::Result<Value> {
66
61
  let opts = options.unwrap_or(json!({}));
67
- if vc::jsonld_v2::use_v2() {
68
- let additional = self.additional_contexts();
69
- vc::jsonld_v2::expand_v2_sync(input, &additional, opts)
70
- .map_err(|e| napi::Error::from_reason(format!("expand (v2) failed: {e}")))
71
- } else {
72
- jsonld::expand(input, self.loader.clone(), self.cr.clone(), opts)
73
- .await
74
- .map_err(|e| napi::Error::from_reason(format!("expand failed: {e}")))
75
- }
62
+ let additional = self.additional_contexts();
63
+ vc::jsonld_v2::expand_v2_sync(input, &additional, opts)
64
+ .map_err(|e| napi::Error::from_reason(format!("expand failed: {e}")))
76
65
  }
77
66
 
78
67
  /// Compact a JSON-LD document using a context.
@@ -90,15 +79,9 @@ impl JsonLd {
90
79
  options: Option<Value>,
91
80
  ) -> napi::Result<Value> {
92
81
  let opts = options.unwrap_or(json!({}));
93
- if vc::jsonld_v2::use_v2() {
94
- let additional = self.additional_contexts();
95
- vc::jsonld_v2::compact_v2_sync(input, ctx, &additional, opts)
96
- .map_err(|e| napi::Error::from_reason(format!("compact (v2) failed: {e}")))
97
- } else {
98
- jsonld::compact_api(input, ctx, self.loader.clone(), self.cr.clone(), opts)
99
- .await
100
- .map_err(|e| napi::Error::from_reason(format!("compact failed: {e}")))
101
- }
82
+ let additional = self.additional_contexts();
83
+ vc::jsonld_v2::compact_v2_sync(input, ctx, &additional, opts)
84
+ .map_err(|e| napi::Error::from_reason(format!("compact failed: {e}")))
102
85
  }
103
86
 
104
87
  /// Flatten a JSON-LD document.
@@ -118,15 +101,9 @@ impl JsonLd {
118
101
  let opts = options.unwrap_or(json!({}));
119
102
  // Filter out null ctx (JS passes null, Rust expects None)
120
103
  let ctx = ctx.filter(|v| !v.is_null());
121
- if vc::jsonld_v2::use_v2() {
122
- let additional = self.additional_contexts();
123
- vc::jsonld_v2::flatten_v2_sync(input, ctx, &additional, opts)
124
- .map_err(|e| napi::Error::from_reason(format!("flatten (v2) failed: {e}")))
125
- } else {
126
- jsonld::flatten(input, ctx, self.loader.clone(), self.cr.clone(), opts)
127
- .await
128
- .map_err(|e| napi::Error::from_reason(format!("flatten failed: {e}")))
129
- }
104
+ let additional = self.additional_contexts();
105
+ vc::jsonld_v2::flatten_v2_sync(input, ctx, &additional, opts)
106
+ .map_err(|e| napi::Error::from_reason(format!("flatten failed: {e}")))
130
107
  }
131
108
 
132
109
  /// Frame a JSON-LD document.
@@ -144,25 +121,16 @@ impl JsonLd {
144
121
  options: Option<Value>,
145
122
  ) -> napi::Result<Value> {
146
123
  let opts = options.unwrap_or(json!({}));
147
- if vc::jsonld_v2::use_v2() {
148
- // Sprint 3 Phase D cutover: V2 frame is always the native typed-tree
149
- // walker. CLIENTFFI_FRAME_NATIVE / CLIENTFFI_FRAME_FAST flags are no
150
- // longer consulted frame_v2 / frame_v2_fast public APIs were
151
- // retired in this commit.
152
- let loader = self.loader.clone();
153
- let cr = self.cr.clone();
154
- let join = tokio::task::spawn_blocking(move || {
155
- vc::jsonld_v2::frame_native_sync(input, frame, loader, cr, opts)
156
- .map_err(|e| napi::Error::from_reason(format!("frame (native) failed: {e}")))
157
- });
158
- join
159
- .await
160
- .map_err(|e| napi::Error::from_reason(format!("frame (v2) join: {e}")))?
161
- } else {
162
- jsonld::frame(input, frame, self.loader.clone(), self.cr.clone(), opts)
163
- .await
124
+ // V2 frame is the native typed-tree walker (Sprint 3 Phase D cutover).
125
+ let loader = self.loader.clone();
126
+ let additional = self.additional_contexts();
127
+ let join = tokio::task::spawn_blocking(move || {
128
+ vc::jsonld_v2::frame_native_sync(input, frame, loader, opts, &additional)
164
129
  .map_err(|e| napi::Error::from_reason(format!("frame failed: {e}")))
165
- }
130
+ });
131
+ join
132
+ .await
133
+ .map_err(|e| napi::Error::from_reason(format!("frame join: {e}")))?
166
134
  }
167
135
 
168
136
  /// Convert a JSON-LD document to an RDF dataset.
@@ -174,22 +142,23 @@ impl JsonLd {
174
142
  #[napi(js_name = "toRDF")]
175
143
  pub async fn to_rdf(&self, input: Value, options: Option<Value>) -> napi::Result<Value> {
176
144
  let opts = options.unwrap_or(json!({}));
177
- // v2 only produces N-Quads string output. If caller expects
178
- // a quads-array (default when format is omitted), fall back to v1.
145
+ // The N-Quads output path is the V2 fast path (string output);
146
+ // otherwise return the dataset as a JSON quads array.
179
147
  let wants_nquads = opts
180
148
  .get("format")
181
149
  .and_then(|v| v.as_str())
182
150
  .map(|s| s == "application/n-quads")
183
151
  .unwrap_or(false);
184
- if vc::jsonld_v2::use_v2() && wants_nquads {
185
- let additional = self.additional_contexts();
152
+ let additional = self.additional_contexts();
153
+ if wants_nquads {
186
154
  let nquads = vc::jsonld_v2::to_nquads_v2_sync(input, &additional, opts)
187
- .map_err(|e| napi::Error::from_reason(format!("toRDF (v2) failed: {e}")))?;
155
+ .map_err(|e| napi::Error::from_reason(format!("toRDF failed: {e}")))?;
188
156
  Ok(Value::String(nquads))
189
157
  } else {
190
- jsonld::to_rdf(input, self.loader.clone(), self.cr.clone(), opts)
191
- .await
192
- .map_err(|e| napi::Error::from_reason(format!("toRDF failed: {e}")))
158
+ let quads = vc::jsonld_v2::to_quads_v2_sync(input, &additional, opts)
159
+ .map_err(|e| napi::Error::from_reason(format!("toRDF failed: {e}")))?;
160
+ serde_json::to_value(&quads)
161
+ .map_err(|e| napi::Error::from_reason(format!("toRDF serialize failed: {e}")))
193
162
  }
194
163
  }
195
164
 
@@ -202,7 +171,7 @@ impl JsonLd {
202
171
  #[napi(js_name = "fromRDF")]
203
172
  pub fn from_rdf(&self, dataset: Value, options: Option<Value>) -> napi::Result<Value> {
204
173
  let opts = options.unwrap_or(json!({}));
205
- jsonld::from_rdf_api(dataset, opts)
174
+ vc::jsonld_v2::from_rdf_sync(dataset, opts)
206
175
  .map_err(|e| napi::Error::from_reason(format!("fromRDF failed: {e}")))
207
176
  }
208
177
 
@@ -228,20 +197,16 @@ impl JsonLd {
228
197
  .and_then(|v| v.as_str())
229
198
  .map(|s| s == "application/n-quads")
230
199
  .unwrap_or(false);
231
- let result = if vc::jsonld_v2::use_v2() && !input_is_nquads {
232
- let additional = self.additional_contexts();
233
- vc::jsonld_v2::canonize_v2_sync(
234
- input,
235
- &additional,
236
- self.loader.clone(),
237
- self.cr.clone(),
238
- opts,
239
- )
240
- .map_err(|e| napi::Error::from_reason(format!("canonize (v2) failed: {e}")))?
241
- } else {
242
- jsonld::canonize(input, self.loader.clone(), self.cr.clone(), opts)
200
+ let result = if input_is_nquads {
201
+ // canonize_v2 always runs expand+to_rdf and would mis-treat an
202
+ // N-Quads string as JSON-LD; route directly through RDF canonicalize.
203
+ vc::rdf_canonize::canonize(input, opts)
243
204
  .await
244
205
  .map_err(|e| napi::Error::from_reason(format!("canonize failed: {e}")))?
206
+ } else {
207
+ let additional = self.additional_contexts();
208
+ vc::jsonld_v2::canonize_v2_sync(input, &additional, self.loader.clone(), opts)
209
+ .map_err(|e| napi::Error::from_reason(format!("canonize failed: {e}")))?
245
210
  };
246
211
  Ok(Value::String(result))
247
212
  }