pkg: packages field in meta export
packages field added to meta export: { npm: '@gnufoo/canaad', crates: 'canaad-core' }. declares registry links per tool package format spec.
deterministic AAD for AEAD — build it, canonicalize it, get the same bytes everywhere
packages field added to meta export: { npm: '@gnufoo/canaad', crates: 'canaad-core' }. declares registry links per tool package format spec.
WasmNotInitializedError — typed error class with kind = 'WasmNotInitializedError' discriminant, exported from @gnufoo/canaad/tool. replaces bare Error throw in execute().
TENANT_MAX_BYTES and RESOURCE_MAX_BYTES constants extracted from magic numbers with provenance comments.
TextEncoder hoisted to module level — allocated once instead of per validation call.
redundant main field removed from package.json. canaad_wasm.d.ts file-scope eslint-disable documented as known wasm-pack build artifact.
TSDoc on all exported names: initWasm, isInitialized, toolDefinition, execute, inputSchema, outputSchema, CanaadInput, CanaadOutput, meta.
tenant and resource validation corrected from character count to byte length via TextEncoder.
timestamp and extension integers capped at Number.MAX_SAFE_INTEGER.
added # Errors sections to all Result-returning functions documenting error conditions.
split tests/test_vectors.rs (448 lines) into test_vectors/{section_10,negative,edge_cases}.rs by spec section.
split main.rs into args.rs (cli types), commands.rs (handlers), and io.rs (input routing). public cli surface unchanged.
crate-level //! now documents exit code contract (0/1/2) and stdin-rejection invariant.
canonicalize_context and canonicalize_context_string narrowed from pub to pub(crate); neither was re-exported through lib.rs.
validate() documented as a semantic alias for parse() — same validation, use validate when you only care whether input is valid.
with_extension now returns ReservedKeyAsExtension when passed a reserved key (v, tenant, resource, purpose, ts). previously returned InvalidExtensionKeyFormat because is_reserved() was never reached.
SafeInt::try_from: replaced bare usize as u64 cast with u64::try_from for correctness on 32-bit targets.
enforce MAX_SAFE_INTEGER upper bound at the WASM boundary for integer inputs.
Zod schemas aligned with spec constraints for integer range validation.
split types.rs, context.rs, and parse.rs into submodules. public api unchanged.
duplicate-key detection replaced with a single-pass serde_json visitor; previous implementation allocated a Vec<char> and traversed the input twice.
ParsedAad narrowed to pub(crate); was not part of the public api.
removed unused js-sys dependency.
23 integration tests covering all subcommands, output formats, input modes, and exit code contract.
AadContextBuilder::extension_string() and extension_int() now surface validation errors through build() instead of silently dropping invalid values.
AadBuilder::timestamp() and extensionInt() no longer silently cast f64 to u64. build()/buildString() now reject NaN, Infinity, negative, and non-integer values.
added Section 9 "Integration at Decryption Boundaries" to architecture docs.
canaad-core: aad parsing, validation, and canonicalization per rfc 8785 (jcs). duplicate key detection via serde_json visitor. AadContext with builder pattern. constants: CURRENT_VERSION, MAX_AAD_SIZE, MAX_SAFE_INTEGER, RESERVED_KEYS. functions: canonicalize, canonicalize_string, parse, validate.
subcommands: canonicalize, validate, hash. output formats: utf8, hex, base64, raw. input: argument, file (-f), or stdin. exit codes: 0 success, 1 validation error, 2 i/o error.
functions: canonicalize, canonicalizeString, validate, hash. AadBuilder class with fluent api. constants: SPEC_VERSION, MAX_SAFE_INTEGER, MAX_SERIALIZED_BYTES.