@reallyme/codec 0.1.22 → 0.2.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/NOTICE ADDED
@@ -0,0 +1,16 @@
1
+ SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2
+
3
+ SPDX-License-Identifier: Apache-2.0
4
+
5
+ ReallyMe Codec
6
+ Copyright © 2026 ReallyMe LLC. All rights reserved.
7
+
8
+ This product includes software developed by ReallyMe LLC and distributed under
9
+ the Apache License, Version 2.0.
10
+
11
+ ReallyMe Codec wraps third-party codec, serialization, protobuf, WASM, JNI, and
12
+ native binding libraries. Dependency license terms are recorded in the package
13
+ manifests and enforced by the repository dependency policy.
14
+
15
+ This NOTICE file is informational and does not modify the license terms in
16
+ LICENSE or any third-party dependency license.
package/README.md CHANGED
@@ -9,8 +9,8 @@ SPDX-License-Identifier: Apache-2.0
9
9
  `@reallyme/codec` is the TypeScript package for the Rust `reallyme-codec`
10
10
  surface. It exposes the same codec families through a WASM-backed facade:
11
11
  base64, unpadded base64url, lowercase hex, multibase, multicodec, multikey,
12
- DAG-CBOR/CID helpers, JCS, PEM armor, and the `reallyme.codec.v1` protobuf
13
- contract.
12
+ deterministic generic CBOR, DAG-CBOR/CID helpers, JCS, PEM armor, and the
13
+ `reallyme.codec.v1` protobuf contract.
14
14
 
15
15
  ```sh
16
16
  npm install @reallyme/codec
@@ -45,24 +45,61 @@ const decoded = ReallyMeCodec.base64urlDecode(encoded);
45
45
  |---|---|
46
46
  | Base encodings | `base64Encode`, `base64Decode`, `base64urlEncode`, `base64urlDecode`, `base64urlDecodeBytes`, `bytesToLowerHex`, `lowerHexToBytes` |
47
47
  | Multiformats | `base58btcEncode`, `base58btcDecode`, `multibase*`, `multicodec*`, `multikey*`, binding validation |
48
+ | Deterministic CBOR | typed `deterministicCborEncode` and `deterministicCborDecode` for the bounded RFC 8949 profile |
48
49
  | DAG-CBOR and CID | `dagCborEncode`, `dagCborDecode`, `dagCborComputeCid`, `dagCborVerifyCid`, content hash and multihash helpers |
49
50
  | JCS | `canonicalizeJson`, `canonicalizeJsonText` |
50
51
  | PEM | wipeable `Uint8Array` armor through `encodePem`, `decodePem`, with strict label and size policy |
51
- | Protobuf | `processProto`, `processProtoJson`, and `@reallyme/codec/proto` generated types |
52
+ | Protobuf | `processOperation`, `processOperationJson`, and `@reallyme/codec/proto` generated types |
53
+
54
+ `processOperation` accepts binary generated `CodecOperationRequest` bytes.
55
+ `processOperationJson` accepts UTF-8 bytes containing the generated ProtoJSON
56
+ view of that same message. Both return binary `CodecOperationResponse` bytes;
57
+ expected validation failures are represented by its typed error outcome and
58
+ are not thrown as backend exceptions. Operation-specific public methods use
59
+ that same fully discriminated response internally; the package does not keep
60
+ operation-specific `*Proto` helper APIs or hand-written structured JSON result
61
+ paths.
62
+
63
+ The deterministic-CBOR API uses a closed recursive value model with exact
64
+ `bigint` integers, `Uint8Array` byte strings, arrays, and entry-list maps with
65
+ integer or text keys. It does not use JavaScript `number` for integers. Floats,
66
+ tags, indefinite-length items, arbitrary simple values, and
67
+ compound map keys are outside the supported profile. DAG-CBOR remains a
68
+ separate, stricter profile and retains its existing APIs and behavior.
52
69
 
53
- `processProto` accepts binary generated `CodecOperationRequest` bytes.
54
- `processProtoJson` accepts UTF-8 bytes containing the generated ProtoJSON view
55
- of that same message. Both return binary `CodecProtoResultEnvelope` bytes;
56
- expected validation failures are represented in the envelope and are not
57
- thrown as backend exceptions.
58
-
59
- The operation-specific `*Proto` helpers remain ergonomic request builders. They
60
- encode generated messages and enter the same single WASM boundary rather than
61
- calling operation-specific WASM exports.
70
+ ```ts
71
+ const value = ReallyMeDeterministicCbor.mapText([
72
+ ["b", ReallyMeDeterministicCbor.unsigned(2n)],
73
+ ["a", ReallyMeDeterministicCbor.bytes(new Uint8Array([0, 1, 2]))],
74
+ ]);
75
+ const encoded = ReallyMeCodec.deterministicCborEncode(value);
76
+
77
+ const dag = ReallyMeDagCbor.mapText([
78
+ ["payload", ReallyMeDagCbor.bytes(new Uint8Array([0, 1, 2]))],
79
+ ]);
80
+ const dagBytes = ReallyMeCodec.dagCborEncode(dag);
81
+ ```
62
82
 
63
- Swift and Kotlin/Java expose the same generic `processProto` and
64
- `processProtoJson` methods through the Rust C/JNI boundary. The method names
65
- and envelope semantics are intentionally identical across SDKs.
83
+ Encoding canonicalizes map ordering. Decoding rejects duplicate semantic keys,
84
+ non-canonical input, unsupported CBOR types, and values beyond the documented
85
+ resource limits. DAG-CBOR builders expose text-key maps and byte/integer
86
+ helpers; deterministic CBOR additionally supports integer-key maps and the
87
+ complete documented `u64`/`i64` integer ranges. `Uint8Array` is the canonical
88
+ mutable-byte boundary for browser, Node, and WASM callers.
89
+
90
+ Encoded CBOR and decoded byte-string values can contain the complete sensitive
91
+ document. Returned buffers belong to the caller and should be cleared with
92
+ `fill(0)` as soon as they are no longer needed. The package snapshots mutable
93
+ inputs and clears its mutable request, response, and intermediate buffers on
94
+ success and failure. JavaScript strings, garbage-collected object graphs, and
95
+ runtime-internal protobuf storage cannot be deterministically erased; callers
96
+ should therefore keep sensitive values short-lived and out of logs and
97
+ telemetry.
98
+
99
+ Swift and Kotlin/Java expose the same generic `processOperation` and
100
+ `processOperationJson` methods through the Rust C/JNI boundary. The method
101
+ names and generated response semantics are intentionally identical across
102
+ SDKs.
66
103
 
67
104
  Errors are typed as `ReallyMeCodecError`; they do not include raw input bytes
68
105
  or backend exception text.
@@ -1,11 +1,17 @@
1
1
  export declare const MAX_CODEC_FFI_INPUT_BYTES = 1048576;
2
2
  export declare const MAX_CODEC_FFI_OUTPUT_BYTES = 67108864;
3
- export declare const MAX_CODEC_PROTO_MESSAGE_BYTES = 1048576;
4
- export declare const MAX_CODEC_PROTO_JSON_BYTES = 1572864;
3
+ export declare const MAX_CODEC_PROTO_MESSAGE_BYTES: number;
4
+ export declare const MAX_CODEC_PROTO_JSON_BYTES: number;
5
5
  export declare const MAX_CODEC_BOUNDARY_NODES = 1048576;
6
6
  export declare const requireBoundaryAggregate: (lengths: ReadonlyArray<number>, maximum?: number) => void;
7
7
  /** Returns UTF-8 length without allocating an encoded copy. */
8
8
  export declare const utf8ByteLength: (value: string) => number;
9
+ /**
10
+ * Returns UTF-8 length while rejecting strings that cannot be represented as
11
+ * Unicode scalar values. TextEncoder and protobuf runtimes otherwise replace
12
+ * lone UTF-16 surrogates, silently changing deterministic-CBOR semantics.
13
+ */
14
+ export declare const strictUtf8ByteLength: (value: string) => number;
9
15
  export declare const requireBoundaryUtf8String: (value: string, allowEmpty?: boolean, maximum?: number) => number;
10
16
  /** Validates, snapshots, and serializes untrusted JSON within a fixed budget. */
11
17
  export declare const stringifyBoundaryJson: (value: unknown) => string;
package/dist/boundary.js CHANGED
@@ -4,8 +4,19 @@
4
4
  import { ReallyMeCodecError } from "./errors.js";
5
5
  export const MAX_CODEC_FFI_INPUT_BYTES = 1_048_576;
6
6
  export const MAX_CODEC_FFI_OUTPUT_BYTES = 67_108_864;
7
- export const MAX_CODEC_PROTO_MESSAGE_BYTES = 1_048_576;
8
- export const MAX_CODEC_PROTO_JSON_BYTES = 1_572_864;
7
+ const MAX_CODEC_PROTO_SENSITIVE_PAYLOAD_BYTES = 2_097_152;
8
+ const MAX_CODEC_PROTO_SEMANTIC_NODES = 65_536;
9
+ const MAX_CODEC_PROTO_STRUCTURAL_BYTES_PER_NODE = 128;
10
+ const MAX_CODEC_PROTO_FIXED_OPERATION_BYTES = 4_096;
11
+ const MAX_CODEC_PROTO_JSON_TEXT_BYTES = 6_291_456;
12
+ const MAX_CODEC_PROTO_JSON_BYTE_STRING_BYTES = 1_398_104;
13
+ export const MAX_CODEC_PROTO_MESSAGE_BYTES = MAX_CODEC_PROTO_SENSITIVE_PAYLOAD_BYTES +
14
+ MAX_CODEC_PROTO_SEMANTIC_NODES * MAX_CODEC_PROTO_STRUCTURAL_BYTES_PER_NODE +
15
+ MAX_CODEC_PROTO_FIXED_OPERATION_BYTES;
16
+ export const MAX_CODEC_PROTO_JSON_BYTES = MAX_CODEC_PROTO_JSON_TEXT_BYTES +
17
+ MAX_CODEC_PROTO_JSON_BYTE_STRING_BYTES +
18
+ MAX_CODEC_PROTO_SEMANTIC_NODES * MAX_CODEC_PROTO_STRUCTURAL_BYTES_PER_NODE +
19
+ MAX_CODEC_PROTO_FIXED_OPERATION_BYTES;
9
20
  // Every JSON node needs at least one serialized byte. Deriving this traversal
10
21
  // guard from the wire limit prevents hostile in-memory graphs from causing
11
22
  // unbounded work without rejecting a document the byte-bounded Rust lane accepts.
@@ -31,8 +42,7 @@ export const requireBoundaryAggregate = (lengths, maximum = MAX_CODEC_FFI_INPUT_
31
42
  aggregate += length;
32
43
  }
33
44
  };
34
- /** Returns UTF-8 length without allocating an encoded copy. */
35
- export const utf8ByteLength = (value) => {
45
+ const utf8ByteLengthWithPolicy = (value, rejectUnpairedSurrogates) => {
36
46
  if (typeof value !== "string") {
37
47
  invalidInput();
38
48
  }
@@ -55,9 +65,18 @@ export const utf8ByteLength = (value) => {
55
65
  index += 1;
56
66
  }
57
67
  else {
68
+ if (rejectUnpairedSurrogates) {
69
+ invalidInput();
70
+ }
58
71
  increment = 3;
59
72
  }
60
73
  }
74
+ else if (codeUnit >= 0xd800 && codeUnit <= 0xdfff) {
75
+ if (rejectUnpairedSurrogates) {
76
+ invalidInput();
77
+ }
78
+ increment = 3;
79
+ }
61
80
  else {
62
81
  increment = 3;
63
82
  }
@@ -68,6 +87,14 @@ export const utf8ByteLength = (value) => {
68
87
  }
69
88
  return length;
70
89
  };
90
+ /** Returns UTF-8 length without allocating an encoded copy. */
91
+ export const utf8ByteLength = (value) => utf8ByteLengthWithPolicy(value, false);
92
+ /**
93
+ * Returns UTF-8 length while rejecting strings that cannot be represented as
94
+ * Unicode scalar values. TextEncoder and protobuf runtimes otherwise replace
95
+ * lone UTF-16 surrogates, silently changing deterministic-CBOR semantics.
96
+ */
97
+ export const strictUtf8ByteLength = (value) => utf8ByteLengthWithPolicy(value, true);
71
98
  export const requireBoundaryUtf8String = (value, allowEmpty = true, maximum = MAX_CODEC_FFI_INPUT_BYTES) => {
72
99
  if (typeof value !== "string" || (!allowEmpty && value.length === 0)) {
73
100
  invalidInput();
package/dist/cbor.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { ReallyMeCodecProtoResult } from "./readOutput.js";
2
1
  export type ReallyMeCborValue = Readonly<{
3
2
  type: "null";
4
3
  }> | Readonly<{
@@ -12,7 +11,7 @@ export type ReallyMeCborValue = Readonly<{
12
11
  value: string;
13
12
  }> | Readonly<{
14
13
  type: "bytes";
15
- value: string;
14
+ value: Uint8Array;
16
15
  }> | Readonly<{
17
16
  type: "array";
18
17
  value: ReadonlyArray<ReallyMeCborValue>;
@@ -29,12 +28,76 @@ export type ReallyMeDagCborCidVerification = Readonly<{
29
28
  expectedCid: string;
30
29
  actualCid: string;
31
30
  }>;
31
+ export type ReallyMeDeterministicCborInteger = Readonly<{
32
+ type: "unsigned";
33
+ value: bigint;
34
+ }> | Readonly<{
35
+ type: "negative";
36
+ value: bigint;
37
+ }>;
38
+ export type ReallyMeDeterministicCborMapKey = Readonly<{
39
+ type: "integer";
40
+ value: ReallyMeDeterministicCborInteger;
41
+ }> | Readonly<{
42
+ type: "text";
43
+ value: string;
44
+ }>;
45
+ export type ReallyMeDeterministicCborMapEntry = Readonly<{
46
+ key: ReallyMeDeterministicCborMapKey;
47
+ value: ReallyMeDeterministicCborValue;
48
+ }>;
49
+ export type ReallyMeDeterministicCborValue = Readonly<{
50
+ type: "null";
51
+ }> | Readonly<{
52
+ type: "bool";
53
+ value: boolean;
54
+ }> | Readonly<{
55
+ type: "integer";
56
+ value: ReallyMeDeterministicCborInteger;
57
+ }> | Readonly<{
58
+ type: "text";
59
+ value: string;
60
+ }> | Readonly<{
61
+ type: "bytes";
62
+ value: Uint8Array;
63
+ }> | Readonly<{
64
+ type: "array";
65
+ value: ReadonlyArray<ReallyMeDeterministicCborValue>;
66
+ }> | Readonly<{
67
+ type: "map";
68
+ value: ReadonlyArray<ReallyMeDeterministicCborMapEntry>;
69
+ }>;
70
+ export declare const ReallyMeDeterministicCbor: {
71
+ readonly null: () => ReallyMeDeterministicCborValue;
72
+ readonly bool: (value: boolean) => ReallyMeDeterministicCborValue;
73
+ readonly unsigned: (value: bigint) => ReallyMeDeterministicCborValue;
74
+ readonly negative: (value: bigint) => ReallyMeDeterministicCborValue;
75
+ readonly text: (value: string) => ReallyMeDeterministicCborValue;
76
+ readonly bytes: (value: Uint8Array) => ReallyMeDeterministicCborValue;
77
+ readonly array: (value: ReadonlyArray<ReallyMeDeterministicCborValue>) => ReallyMeDeterministicCborValue;
78
+ readonly mapInt: (entries: ReadonlyArray<readonly [bigint, ReallyMeDeterministicCborValue]>) => ReallyMeDeterministicCborValue;
79
+ readonly mapText: (entries: ReadonlyArray<readonly [string, ReallyMeDeterministicCborValue]>) => ReallyMeDeterministicCborValue;
80
+ readonly intKey: (value: bigint) => ReallyMeDeterministicCborMapKey;
81
+ readonly textKey: (value: string) => ReallyMeDeterministicCborMapKey;
82
+ readonly entry: (key: ReallyMeDeterministicCborMapKey, value: ReallyMeDeterministicCborValue) => ReallyMeDeterministicCborMapEntry;
83
+ };
84
+ export declare const ReallyMeDagCbor: {
85
+ readonly null: () => ReallyMeCborValue;
86
+ readonly bool: (value: boolean) => ReallyMeCborValue;
87
+ readonly int: (value: number | bigint) => ReallyMeCborValue;
88
+ readonly unsigned: (value: number | bigint) => ReallyMeCborValue;
89
+ readonly negative: (value: number | bigint) => ReallyMeCborValue;
90
+ readonly text: (value: string) => ReallyMeCborValue;
91
+ readonly bytes: (value: Uint8Array) => ReallyMeCborValue;
92
+ readonly array: (value: ReadonlyArray<ReallyMeCborValue>) => ReallyMeCborValue;
93
+ readonly mapText: (entries: ReadonlyArray<readonly [string, ReallyMeCborValue]>) => ReallyMeCborValue;
94
+ };
32
95
  export declare const dagCborEncode: (value: ReallyMeCborValue) => Uint8Array;
33
96
  export declare const dagCborDecode: (bytes: Uint8Array) => ReallyMeCborValue;
97
+ export declare const deterministicCborEncode: (value: unknown) => Uint8Array;
98
+ export declare const deterministicCborDecode: (bytes: Uint8Array) => ReallyMeDeterministicCborValue;
34
99
  export declare const dagCborComputeCid: (bytes: Uint8Array) => string;
35
100
  export declare const dagCborVerifyCid: (cid: string, bytes: Uint8Array) => ReallyMeDagCborCidVerification;
36
- export declare const dagCborVerifyCidProto: (cid: string, bytes: Uint8Array) => Uint8Array;
37
- export declare const dagCborVerifyCidProtoResult: (cid: string, bytes: Uint8Array) => ReallyMeCodecProtoResult;
38
101
  export declare const dagCborSha256ContentHash: (bytes: Uint8Array) => Uint8Array;
39
102
  export declare const dagCborMultihash: (bytes: Uint8Array) => Uint8Array;
40
103
  export declare const isValidCidString: (cid: string) => boolean;