@cardanowall/poe-standard 0.9.0 → 0.11.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.
package/README.md CHANGED
@@ -19,17 +19,7 @@ result, in any implementation, against shared test vectors.
19
19
 
20
20
  ## Install
21
21
 
22
- The Label 309 TypeScript packages are pre-1.0 and not yet published to npm. Build from the workspace:
23
-
24
- ```sh
25
- pnpm install
26
- pnpm --filter @cardanowall/poe-standard typecheck
27
- ```
28
-
29
- Once published, the install will be:
30
-
31
22
  ```sh
32
- # once published
33
23
  npm install @cardanowall/poe-standard
34
24
  ```
35
25
 
@@ -58,7 +48,7 @@ const bytes: Uint8Array = encodePoeRecord(record);
58
48
  import { validatePoeRecord } from '@cardanowall/poe-standard';
59
49
 
60
50
  const result = validatePoeRecord(bytes);
61
- if (result.ok) {
51
+ if (result.valid) {
62
52
  // result.record is the decoded PoeRecord.
63
53
  // result.info / result.warnings carry non-fatal issues (e.g. an
64
54
  // unsupported-but-tolerated signature algorithm id).
@@ -73,7 +63,7 @@ if (result.ok) {
73
63
 
74
64
  `validatePoeRecord` never throws: every failure — malformed/non-canonical CBOR, schema mismatch,
75
65
  registry violation, bad URI, malformed signature entry — is returned as data in a discriminated
76
- `ValidateResult` union. The result is sorted, stable, and identical across the TS/Python/Rust
66
+ `ValidationResult` union. The result is sorted, stable, and identical across the TS/Python/Rust
77
67
  implementations.
78
68
 
79
69
  ### Build the bytes a record-level signature covers
@@ -91,22 +81,36 @@ const body: Uint8Array = encodeRecordBodyForSigning(record);
91
81
 
92
82
  ## Records on chain: chunk → validate
93
83
 
94
- The Cardano ledger caps every metadata byte string and text string at 64 bytes, so a Label 309 record
95
- is stored as an **array of ≤64-byte CBOR-bytes chunks** under label 309, and the metadata map itself
96
- is wrapped in the Conway tag-259 form. A verifier reassembles the chunk array (and unwraps tag-259)
97
- before calling `validatePoeRecord` the validator operates on the single reconstructed record byte
98
- string. The chunking helpers are exported from the root:
84
+ The Cardano ledger caps every metadata byte string at 64 bytes, so the serialised record body crosses
85
+ the ledger as a **whole-body chunk array**: a CBOR array of ≤64-byte byte strings whose in-order
86
+ concatenation is the canonical body. This transport split is the _only_ chunking the format performs —
87
+ fields inside the reassembled body (URIs, COSE_Sign1 and COSE_Key blobs) are ordinary CBOR values with
88
+ no per-field chunk wrappers of their own. A verifier reassembles the array before calling
89
+ `validatePoeRecord`, so the validator only ever sees the single reconstructed body. Both directions of
90
+ the transport are exported from the root:
99
91
 
100
92
  ```ts
101
- import { chunkBytes, bytesChunkArrayConcat } from '@cardanowall/poe-standard';
102
-
103
- const chunks = chunkBytes(bytes); // Uint8Array[] of ≤64-byte chunks, for label 309
104
- const whole = bytesChunkArrayConcat(chunks); // reassemble before validate
93
+ import {
94
+ chunkRecordBody,
95
+ encodeLabel309Value,
96
+ reassembleLabel309Value,
97
+ validatePoeRecord,
98
+ } from '@cardanowall/poe-standard';
99
+
100
+ // Producer: canonical body bytes → the chunk array stored under label 309.
101
+ const chunks = chunkRecordBody(bodyBytes); // Uint8Array[], each ≤ 64 bytes
102
+ const label309Value = encodeLabel309Value(bodyBytes); // the CBOR bytes of that array
103
+
104
+ // Verifier: raw label-309 value bytes → the body, before validation.
105
+ const reassembled = reassembleLabel309Value(label309Value);
106
+ if (reassembled.ok) {
107
+ const result = validatePoeRecord(reassembled.body);
108
+ // …handle result.valid
109
+ } else {
110
+ // reassembled.issue is a MALFORMED_CBOR / CHUNK_TOO_LARGE ValidationIssue.
111
+ }
105
112
  ```
106
113
 
107
- `chunkUri` / `reconstructChunkedUri` do the same for the chunked URI arrays carried in
108
- `items[i].uris` and `merkle[i].uris`, splitting on UTF-8 codepoint boundaries.
109
-
110
114
  ## API overview
111
115
 
112
116
  The package root re-exports every group. Subpath imports are available for `./schema`, `./encoder`,
@@ -120,11 +124,12 @@ The package root re-exports every group. Subpath imports are available for `./sc
120
124
 
121
125
  **Validate** (`@cardanowall/poe-standard/validator`)
122
126
 
123
- - `validatePoeRecord(bytes)` — the structural pipeline: canonical decode → schema parse → cross-field
124
- domain checks. Returns a discriminated `ValidateResult`; never throws.
127
+ - `validatePoeRecord(bytes, options?)` — the structural pipeline: canonical decode → schema parse →
128
+ cross-field domain checks. Returns a discriminated `ValidationResult` (`valid: true` with `record` /
129
+ `warnings?` / `info?`, or `valid: false` with `issues`); never throws.
125
130
  - `validateCidProfile(cid)` — offline IPFS CID-profile parser (CIDv0 and the Label 309 CIDv1 multibase
126
131
  / multicodec / multihash profile).
127
- - `type ValidateResult`, `type ValidationIssue`.
132
+ - `type ValidationResult`, `type ValidationIssue`, `type ValidatorOptions`, `type ValidatorRole`.
128
133
 
129
134
  **Error codes** (`@cardanowall/poe-standard/error-codes`)
130
135
 
@@ -138,22 +143,24 @@ The package root re-exports every group. Subpath imports are available for `./sc
138
143
  **Schema + types** (`@cardanowall/poe-standard/schema`)
139
144
 
140
145
  - Zod schemas for the full v1 wire surface: `PoeRecordSchema`, `ItemEntrySchema`,
141
- `MerkleCommitSchema`, `EncryptionEnvelopeSchema`, `SlotSchema`, `PassphraseBlockSchema`,
142
- `Argon2idParamsSchema`, `HashesMapSchema`, `HashDigestSchema`, `SigEntrySchema`,
143
- `SupersedesSchema`, `VersionLiteralSchema`, and the `ChunkedBytesArraySchema` / `UriChunkArraySchema`
144
- chunk-array schemas.
145
- - Inferred types: `PoeRecord`, `ItemEntry`, `MerkleCommit`, `EncryptionEnvelope`, `Slot`,
146
- `PassphraseBlock`, `Argon2idParams`, `HashesMap`, `UriChunkArray`, `ChunkedBytesArray`, `SigEntry`,
146
+ `MerkleCommitSchema`, `EncryptionEnvelopeSchema`, `EncScheme1Schema`, `EncOpaqueSchema`,
147
+ `SlotSchema`, `PassphraseBlockSchema`, `Argon2idParamsSchema`, `HashesMapSchema`, `HashDigestSchema`,
148
+ `UriSchema`, `SigEntrySchema`, `SupersedesSchema`, `VersionLiteralSchema`.
149
+ - Inferred types: `PoeRecord`, `ItemEntry`, `MerkleCommit`, `EncryptionEnvelope`, `EncScheme1`,
150
+ `EncOpaque`, `Slot`, `PassphraseBlock`, `Argon2idParams`, `HashesMap`, `Uri`, `SigEntry`,
147
151
  `Supersedes`.
148
152
  - Extension-key helpers: `TOP_LEVEL_BASE_KEYS`, `isExtensionKey`, `EXTENSION_KEY_VENDOR_RE`,
149
153
  `EXTENSION_KEY_COMPANION_RE`.
150
154
 
151
- **Chunking** (re-exported from the root)
155
+ **Carriage** (re-exported from the root)
152
156
 
153
- - `chunkBytes` / `bytesChunkArrayConcat` — split/reassemble chunked byte strings (COSE_Sign1,
154
- COSE_Key blobs).
155
- - `chunkUri` / `reconstructChunkedUri` — split/reassemble chunked URI arrays on UTF-8 boundaries.
156
- - `type ReconstructUriResult`.
157
+ - `chunkRecordBody(body)` / `encodeLabel309Value(body)` — the producer side: split the canonical body
158
+ into the whole-body ≤64-byte transport chunk array, or serialise that array to the label-309 value
159
+ bytes.
160
+ - `reassembleLabel309Value(valueBytes)` — the consumer side: reassemble the label-309 value into the
161
+ record body, returning a `Label309ReassemblyResult` (`ok: true` with `body`, or `ok: false` with a
162
+ `MALFORMED_CBOR` / `CHUNK_TOO_LARGE` issue). Run before `validatePoeRecord`.
163
+ - `TRANSPORT_CHUNK_MAX_BYTES` (64), `type Label309ReassemblyResult`.
157
164
 
158
165
  For the exhaustive surface, see `src/index.ts`.
159
166
 
package/dist/index.cjs CHANGED
@@ -3021,46 +3021,37 @@ function checkUris(uris, basePath, issues) {
3021
3021
  uris.forEach((uri, ui) => checkOneUri(uri, [...basePath, ui], issues));
3022
3022
  }
3023
3023
  function checkOneUri(uri, path, issues) {
3024
+ const rejection = fetchSetUriRejection(uri);
3025
+ if (rejection !== null) issues.push(issueOf("INVALID_URI", path, rejection));
3026
+ }
3027
+ function isArweaveTxid(body) {
3028
+ return /^[A-Za-z0-9_-]{43}$/.test(body);
3029
+ }
3030
+ function fetchSetUriRejection(uri) {
3024
3031
  if (uri.includes("#")) {
3025
- issues.push(
3026
- issueOf("INVALID_URI", path, "URI contains a fragment identifier ('#'), which is forbidden")
3027
- );
3028
- return;
3032
+ return "URI contains a fragment identifier ('#'), which is forbidden";
3029
3033
  }
3030
3034
  const sepIdx = uri.indexOf("://");
3031
3035
  if (sepIdx <= 0 || !/^[a-z][a-z0-9+.-]*$/i.test(uri.slice(0, sepIdx))) {
3032
- issues.push(
3033
- issueOf("INVALID_URI", path, "URI is not absolute (missing scheme://hierarchical-part)")
3034
- );
3035
- return;
3036
+ return "URI is not absolute (missing scheme://hierarchical-part)";
3036
3037
  }
3037
3038
  const scheme = uri.slice(0, sepIdx).toLowerCase();
3038
3039
  const rest = uri.slice(sepIdx + "://".length);
3039
3040
  if (scheme === "ar") {
3040
- if (!/^[A-Za-z0-9_-]{43}$/.test(rest)) {
3041
- issues.push(
3042
- issueOf(
3043
- "INVALID_URI",
3044
- path,
3045
- "ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)"
3046
- )
3047
- );
3048
- }
3049
- return;
3041
+ return isArweaveTxid(rest) ? null : "ar:// URI does not match `^ar://[A-Za-z0-9_-]{43}$` (43-char base64url txid, no path/query/fragment)";
3050
3042
  }
3051
3043
  if (scheme === "ipfs") {
3052
3044
  const slashIdx = rest.indexOf("/");
3053
3045
  const cid = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
3054
- if (!validateCidProfile(cid)) {
3055
- issues.push(
3056
- issueOf("INVALID_URI", path, "ipfs:// URI is not a valid CID under the Label 309 profile")
3057
- );
3058
- }
3059
- return;
3046
+ return validateCidProfile(cid) ? null : "ipfs:// URI is not a valid CID under the Label 309 profile";
3060
3047
  }
3061
- issues.push(
3062
- issueOf("INVALID_URI", path, "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}")
3063
- );
3048
+ return "unsupported URI scheme; v1 PoE URI set is {ar://, ipfs://}";
3049
+ }
3050
+ function isFetchSetUri(uri) {
3051
+ return fetchSetUriRejection(uri) === null;
3052
+ }
3053
+ function isArweaveTxUri(uri) {
3054
+ return uri.startsWith("ar://") && isArweaveTxid(uri.slice("ar://".length));
3064
3055
  }
3065
3056
  function checkItemEnc(item, idx, opts, issues) {
3066
3057
  const encPath = ["items", idx, "enc"];
@@ -3870,7 +3861,10 @@ exports.encodeLabel309Value = encodeLabel309Value;
3870
3861
  exports.encodePoeRecord = encodePoeRecord;
3871
3862
  exports.encodeRecordBodyForSigning = encodeRecordBodyForSigning;
3872
3863
  exports.errorCodeRegistryIndex = errorCodeRegistryIndex;
3864
+ exports.fetchSetUriRejection = fetchSetUriRejection;
3865
+ exports.isArweaveTxUri = isArweaveTxUri;
3873
3866
  exports.isExtensionKey = isExtensionKey;
3867
+ exports.isFetchSetUri = isFetchSetUri;
3874
3868
  exports.reassembleLabel309Value = reassembleLabel309Value;
3875
3869
  exports.severityOf = severityOf;
3876
3870
  exports.validateCidProfile = validateCidProfile;