@reallyme/codec 0.1.20 → 0.1.22
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 +16 -2
- package/dist/boundary.d.ts +11 -0
- package/dist/boundary.js +250 -0
- package/dist/cbor.js +119 -29
- package/dist/index.d.ts +7 -4
- package/dist/index.js +4 -0
- package/dist/jcs.js +3 -60
- package/dist/multiformat.js +57 -14
- package/dist/pem.d.ts +4 -4
- package/dist/pem.js +183 -36
- package/dist/proto/generated/reallyme/codec/v1/codec_pb.d.ts +335 -1
- package/dist/proto/generated/reallyme/codec/v1/codec_pb.js +149 -14
- package/dist/proto.d.ts +2 -2
- package/dist/proto.js +1 -1
- package/dist/protoProcess.d.ts +16 -0
- package/dist/protoProcess.js +282 -0
- package/dist/readOutput.d.ts +1 -1
- package/dist/readOutput.js +84 -21
- package/dist/wasm/reallyme_codec_wasm.d.ts +35 -95
- package/dist/wasm/reallyme_codec_wasm.js +153 -290
- package/dist/wasm/reallyme_codec_wasm_bg.wasm +0 -0
- package/dist/wasmProvider.d.ts +4 -18
- package/dist/wasmProvider.js +18 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,8 +47,22 @@ const decoded = ReallyMeCodec.base64urlDecode(encoded);
|
|
|
47
47
|
| Multiformats | `base58btcEncode`, `base58btcDecode`, `multibase*`, `multicodec*`, `multikey*`, binding validation |
|
|
48
48
|
| DAG-CBOR and CID | `dagCborEncode`, `dagCborDecode`, `dagCborComputeCid`, `dagCborVerifyCid`, content hash and multihash helpers |
|
|
49
49
|
| JCS | `canonicalizeJson`, `canonicalizeJsonText` |
|
|
50
|
-
| PEM | `encodePem`, `decodePem
|
|
51
|
-
| Protobuf | `@reallyme/codec/proto`
|
|
50
|
+
| PEM | wipeable `Uint8Array` armor through `encodePem`, `decodePem`, with strict label and size policy |
|
|
51
|
+
| Protobuf | `processProto`, `processProtoJson`, and `@reallyme/codec/proto` generated types |
|
|
52
|
+
|
|
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.
|
|
62
|
+
|
|
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.
|
|
52
66
|
|
|
53
67
|
Errors are typed as `ReallyMeCodecError`; they do not include raw input bytes
|
|
54
68
|
or backend exception text.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const MAX_CODEC_FFI_INPUT_BYTES = 1048576;
|
|
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;
|
|
5
|
+
export declare const MAX_CODEC_BOUNDARY_NODES = 1048576;
|
|
6
|
+
export declare const requireBoundaryAggregate: (lengths: ReadonlyArray<number>, maximum?: number) => void;
|
|
7
|
+
/** Returns UTF-8 length without allocating an encoded copy. */
|
|
8
|
+
export declare const utf8ByteLength: (value: string) => number;
|
|
9
|
+
export declare const requireBoundaryUtf8String: (value: string, allowEmpty?: boolean, maximum?: number) => number;
|
|
10
|
+
/** Validates, snapshots, and serializes untrusted JSON within a fixed budget. */
|
|
11
|
+
export declare const stringifyBoundaryJson: (value: unknown) => string;
|
package/dist/boundary.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { ReallyMeCodecError } from "./errors.js";
|
|
5
|
+
export const MAX_CODEC_FFI_INPUT_BYTES = 1_048_576;
|
|
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;
|
|
9
|
+
// Every JSON node needs at least one serialized byte. Deriving this traversal
|
|
10
|
+
// guard from the wire limit prevents hostile in-memory graphs from causing
|
|
11
|
+
// unbounded work without rejecting a document the byte-bounded Rust lane accepts.
|
|
12
|
+
export const MAX_CODEC_BOUNDARY_NODES = MAX_CODEC_FFI_INPUT_BYTES;
|
|
13
|
+
const MAX_JSON_CONTAINER_DEPTH = 128;
|
|
14
|
+
const invalidInput = () => {
|
|
15
|
+
throw new ReallyMeCodecError("invalid-input");
|
|
16
|
+
};
|
|
17
|
+
const consumeBudget = (budget, field, amount, maximum) => {
|
|
18
|
+
if (!Number.isSafeInteger(amount) || amount < 0 || budget[field] > maximum - amount) {
|
|
19
|
+
invalidInput();
|
|
20
|
+
}
|
|
21
|
+
budget[field] += amount;
|
|
22
|
+
};
|
|
23
|
+
export const requireBoundaryAggregate = (lengths, maximum = MAX_CODEC_FFI_INPUT_BYTES) => {
|
|
24
|
+
let aggregate = 0;
|
|
25
|
+
for (const length of lengths) {
|
|
26
|
+
if (!Number.isSafeInteger(length) ||
|
|
27
|
+
length < 0 ||
|
|
28
|
+
aggregate > maximum - length) {
|
|
29
|
+
invalidInput();
|
|
30
|
+
}
|
|
31
|
+
aggregate += length;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/** Returns UTF-8 length without allocating an encoded copy. */
|
|
35
|
+
export const utf8ByteLength = (value) => {
|
|
36
|
+
if (typeof value !== "string") {
|
|
37
|
+
invalidInput();
|
|
38
|
+
}
|
|
39
|
+
let length = 0;
|
|
40
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
41
|
+
const codeUnit = value.charCodeAt(index);
|
|
42
|
+
let increment;
|
|
43
|
+
if (codeUnit <= 0x7f) {
|
|
44
|
+
increment = 1;
|
|
45
|
+
}
|
|
46
|
+
else if (codeUnit <= 0x7ff) {
|
|
47
|
+
increment = 2;
|
|
48
|
+
}
|
|
49
|
+
else if (codeUnit >= 0xd800 &&
|
|
50
|
+
codeUnit <= 0xdbff &&
|
|
51
|
+
index + 1 < value.length) {
|
|
52
|
+
const next = value.charCodeAt(index + 1);
|
|
53
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
54
|
+
increment = 4;
|
|
55
|
+
index += 1;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
increment = 3;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
increment = 3;
|
|
63
|
+
}
|
|
64
|
+
if (length > Number.MAX_SAFE_INTEGER - increment) {
|
|
65
|
+
invalidInput();
|
|
66
|
+
}
|
|
67
|
+
length += increment;
|
|
68
|
+
}
|
|
69
|
+
return length;
|
|
70
|
+
};
|
|
71
|
+
export const requireBoundaryUtf8String = (value, allowEmpty = true, maximum = MAX_CODEC_FFI_INPUT_BYTES) => {
|
|
72
|
+
if (typeof value !== "string" || (!allowEmpty && value.length === 0)) {
|
|
73
|
+
invalidInput();
|
|
74
|
+
}
|
|
75
|
+
// Every UTF-16 code unit contributes at least one UTF-8 byte. This rejects
|
|
76
|
+
// obviously oversized strings before walking their full contents.
|
|
77
|
+
if (value.length > maximum) {
|
|
78
|
+
invalidInput();
|
|
79
|
+
}
|
|
80
|
+
const length = utf8ByteLength(value);
|
|
81
|
+
requireBoundaryAggregate([length], maximum);
|
|
82
|
+
return length;
|
|
83
|
+
};
|
|
84
|
+
const jsonStringByteLength = (value) => {
|
|
85
|
+
let length = 2;
|
|
86
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
87
|
+
const codeUnit = value.charCodeAt(index);
|
|
88
|
+
let increment;
|
|
89
|
+
if (codeUnit === 0x22 || codeUnit === 0x5c) {
|
|
90
|
+
increment = 2;
|
|
91
|
+
}
|
|
92
|
+
else if (codeUnit <= 0x1f) {
|
|
93
|
+
increment =
|
|
94
|
+
codeUnit === 0x08 ||
|
|
95
|
+
codeUnit === 0x09 ||
|
|
96
|
+
codeUnit === 0x0a ||
|
|
97
|
+
codeUnit === 0x0c ||
|
|
98
|
+
codeUnit === 0x0d
|
|
99
|
+
? 2
|
|
100
|
+
: 6;
|
|
101
|
+
}
|
|
102
|
+
else if (codeUnit <= 0x7f) {
|
|
103
|
+
increment = 1;
|
|
104
|
+
}
|
|
105
|
+
else if (codeUnit <= 0x7ff) {
|
|
106
|
+
increment = 2;
|
|
107
|
+
}
|
|
108
|
+
else if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
|
|
109
|
+
if (index + 1 < value.length) {
|
|
110
|
+
const next = value.charCodeAt(index + 1);
|
|
111
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
112
|
+
increment = 4;
|
|
113
|
+
index += 1;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
increment = 6;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
increment = 6;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) {
|
|
124
|
+
increment = 6;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
increment = 3;
|
|
128
|
+
}
|
|
129
|
+
if (length > Number.MAX_SAFE_INTEGER - increment) {
|
|
130
|
+
invalidInput();
|
|
131
|
+
}
|
|
132
|
+
length += increment;
|
|
133
|
+
}
|
|
134
|
+
return length;
|
|
135
|
+
};
|
|
136
|
+
const readOwnDataProperty = (value, key) => {
|
|
137
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
138
|
+
if (descriptor === undefined || !("value" in descriptor)) {
|
|
139
|
+
return invalidInput();
|
|
140
|
+
}
|
|
141
|
+
return descriptor.value;
|
|
142
|
+
};
|
|
143
|
+
const normalizeJson = (value, depth, seen, budget) => {
|
|
144
|
+
consumeBudget(budget, "nodes", 1, MAX_CODEC_BOUNDARY_NODES);
|
|
145
|
+
if (value === null) {
|
|
146
|
+
consumeBudget(budget, "bytes", 4, MAX_CODEC_FFI_INPUT_BYTES);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
switch (typeof value) {
|
|
150
|
+
case "boolean":
|
|
151
|
+
consumeBudget(budget, "bytes", value ? 4 : 5, MAX_CODEC_FFI_INPUT_BYTES);
|
|
152
|
+
return value;
|
|
153
|
+
case "string":
|
|
154
|
+
consumeBudget(budget, "bytes", jsonStringByteLength(value), MAX_CODEC_FFI_INPUT_BYTES);
|
|
155
|
+
return value;
|
|
156
|
+
case "number": {
|
|
157
|
+
if (!Number.isFinite(value)) {
|
|
158
|
+
invalidInput();
|
|
159
|
+
}
|
|
160
|
+
const encoded = JSON.stringify(value);
|
|
161
|
+
if (typeof encoded !== "string") {
|
|
162
|
+
invalidInput();
|
|
163
|
+
}
|
|
164
|
+
consumeBudget(budget, "bytes", encoded.length, MAX_CODEC_FFI_INPUT_BYTES);
|
|
165
|
+
return value;
|
|
166
|
+
}
|
|
167
|
+
case "bigint":
|
|
168
|
+
case "function":
|
|
169
|
+
case "symbol":
|
|
170
|
+
case "undefined":
|
|
171
|
+
return invalidInput();
|
|
172
|
+
case "object":
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
if (seen.has(value) || depth >= MAX_JSON_CONTAINER_DEPTH) {
|
|
176
|
+
invalidInput();
|
|
177
|
+
}
|
|
178
|
+
const prototype = Object.getPrototypeOf(value);
|
|
179
|
+
if (prototype !== Object.prototype && prototype !== null && !Array.isArray(value)) {
|
|
180
|
+
invalidInput();
|
|
181
|
+
}
|
|
182
|
+
seen.add(value);
|
|
183
|
+
try {
|
|
184
|
+
if (Array.isArray(value)) {
|
|
185
|
+
const rawLength = readOwnDataProperty(value, "length");
|
|
186
|
+
if (typeof rawLength !== "number") {
|
|
187
|
+
return invalidInput();
|
|
188
|
+
}
|
|
189
|
+
const length = rawLength;
|
|
190
|
+
if (!Number.isSafeInteger(length) ||
|
|
191
|
+
length < 0 ||
|
|
192
|
+
length > MAX_CODEC_BOUNDARY_NODES - budget.nodes) {
|
|
193
|
+
invalidInput();
|
|
194
|
+
}
|
|
195
|
+
consumeBudget(budget, "bytes", length === 0 ? 2 : length + 1, MAX_CODEC_FFI_INPUT_BYTES);
|
|
196
|
+
const normalized = [];
|
|
197
|
+
for (let index = 0; index < length; index += 1) {
|
|
198
|
+
// Indexed access and iteration both invoke array accessors. Snapshot
|
|
199
|
+
// only own data properties so validation cannot execute untrusted code.
|
|
200
|
+
const entry = readOwnDataProperty(value, String(index));
|
|
201
|
+
normalized.push(normalizeJson(entry, depth + 1, seen, budget));
|
|
202
|
+
}
|
|
203
|
+
return normalized;
|
|
204
|
+
}
|
|
205
|
+
const keys = Object.keys(value);
|
|
206
|
+
if (keys.length > MAX_CODEC_BOUNDARY_NODES - budget.nodes) {
|
|
207
|
+
invalidInput();
|
|
208
|
+
}
|
|
209
|
+
consumeBudget(budget, "bytes", keys.length === 0 ? 2 : keys.length + 1, MAX_CODEC_FFI_INPUT_BYTES);
|
|
210
|
+
const entries = [];
|
|
211
|
+
for (const key of keys) {
|
|
212
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
213
|
+
if (descriptor === undefined) {
|
|
214
|
+
return invalidInput();
|
|
215
|
+
}
|
|
216
|
+
if (!("value" in descriptor)) {
|
|
217
|
+
return invalidInput();
|
|
218
|
+
}
|
|
219
|
+
consumeBudget(budget, "bytes", jsonStringByteLength(key), MAX_CODEC_FFI_INPUT_BYTES);
|
|
220
|
+
consumeBudget(budget, "bytes", 1, MAX_CODEC_FFI_INPUT_BYTES);
|
|
221
|
+
const propertyValue = descriptor.value;
|
|
222
|
+
entries.push([key, normalizeJson(propertyValue, depth + 1, seen, budget)]);
|
|
223
|
+
}
|
|
224
|
+
return Object.fromEntries(entries);
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
seen.delete(value);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
/** Validates, snapshots, and serializes untrusted JSON within a fixed budget. */
|
|
231
|
+
export const stringifyBoundaryJson = (value) => {
|
|
232
|
+
try {
|
|
233
|
+
const normalized = normalizeJson(value, 0, new WeakSet(), {
|
|
234
|
+
bytes: 0,
|
|
235
|
+
nodes: 0,
|
|
236
|
+
});
|
|
237
|
+
const encoded = JSON.stringify(normalized);
|
|
238
|
+
if (typeof encoded !== "string") {
|
|
239
|
+
invalidInput();
|
|
240
|
+
}
|
|
241
|
+
requireBoundaryAggregate([utf8ByteLength(encoded)]);
|
|
242
|
+
return encoded;
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
if (error instanceof ReallyMeCodecError) {
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
return invalidInput();
|
|
249
|
+
}
|
|
250
|
+
};
|
package/dist/cbor.js
CHANGED
|
@@ -1,10 +1,54 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import { MAX_CODEC_BOUNDARY_NODES, stringifyBoundaryJson, } from "./boundary.js";
|
|
4
6
|
import { ReallyMeCodecError } from "./errors.js";
|
|
5
|
-
import {
|
|
7
|
+
import { CodecDagCborVerifyCidRequestSchema, CodecOperationRequestSchema, } from "./proto/generated/reallyme/codec/v1/codec_pb.js";
|
|
8
|
+
import { processGeneratedProtoRequest, protoPayloadOrThrow, } from "./protoProcess.js";
|
|
9
|
+
import { ensureBytesInput, ensureStringValue, readBooleanProperty, readBytesOutput, readObjectOutput, readStringOutput, readStringProperty, readStringValueProperty, readNumberOutput, } from "./readOutput.js";
|
|
6
10
|
import { requireReallyMeCodecWasmProvider } from "./wasmProvider.js";
|
|
7
|
-
const
|
|
11
|
+
const invalidCborInput = () => {
|
|
12
|
+
throw new ReallyMeCodecError("invalid-input");
|
|
13
|
+
};
|
|
14
|
+
const isRecord = (value) => {
|
|
15
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const prototype = Object.getPrototypeOf(value);
|
|
19
|
+
return prototype === Object.prototype || prototype === null;
|
|
20
|
+
};
|
|
21
|
+
const readDataProperty = (value, name) => {
|
|
22
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
23
|
+
if (descriptor === undefined || !("value" in descriptor)) {
|
|
24
|
+
return invalidCborInput();
|
|
25
|
+
}
|
|
26
|
+
return descriptor.value;
|
|
27
|
+
};
|
|
28
|
+
const snapshotArray = (value) => {
|
|
29
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
|
|
30
|
+
return invalidCborInput();
|
|
31
|
+
}
|
|
32
|
+
const rawLength = readDataProperty(value, "length");
|
|
33
|
+
if (typeof rawLength !== "number") {
|
|
34
|
+
return invalidCborInput();
|
|
35
|
+
}
|
|
36
|
+
const length = rawLength;
|
|
37
|
+
if (!Number.isSafeInteger(length) ||
|
|
38
|
+
length < 0 ||
|
|
39
|
+
length > MAX_CODEC_BOUNDARY_NODES) {
|
|
40
|
+
return invalidCborInput();
|
|
41
|
+
}
|
|
42
|
+
const entries = [];
|
|
43
|
+
for (let index = 0; index < length; index += 1) {
|
|
44
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
45
|
+
if (descriptor === undefined || !("value" in descriptor)) {
|
|
46
|
+
return invalidCborInput();
|
|
47
|
+
}
|
|
48
|
+
entries.push(descriptor.value);
|
|
49
|
+
}
|
|
50
|
+
return entries;
|
|
51
|
+
};
|
|
8
52
|
const i64Min = -(1n << 63n);
|
|
9
53
|
const i64Max = (1n << 63n) - 1n;
|
|
10
54
|
const decimalIntegerPattern = /^-?(0|[1-9][0-9]*)$/u;
|
|
@@ -41,8 +85,16 @@ const validateCborInteger = (value) => {
|
|
|
41
85
|
}
|
|
42
86
|
throw new ReallyMeCodecError("invalid-input");
|
|
43
87
|
};
|
|
44
|
-
const validateCborValue = (value, depth = 0, seen = new WeakSet()) => {
|
|
45
|
-
if (
|
|
88
|
+
const validateCborValue = (value, depth = 0, seen = new WeakSet(), state = { nodes: 0 }) => {
|
|
89
|
+
if (state.nodes >= MAX_CODEC_BOUNDARY_NODES) {
|
|
90
|
+
throw new ReallyMeCodecError("invalid-input");
|
|
91
|
+
}
|
|
92
|
+
state.nodes += 1;
|
|
93
|
+
if (!isRecord(value)) {
|
|
94
|
+
throw new ReallyMeCodecError("invalid-input");
|
|
95
|
+
}
|
|
96
|
+
const type = readDataProperty(value, "type");
|
|
97
|
+
if (typeof type !== "string") {
|
|
46
98
|
throw new ReallyMeCodecError("invalid-input");
|
|
47
99
|
}
|
|
48
100
|
if (seen.has(value)) {
|
|
@@ -50,46 +102,64 @@ const validateCborValue = (value, depth = 0, seen = new WeakSet()) => {
|
|
|
50
102
|
}
|
|
51
103
|
seen.add(value);
|
|
52
104
|
try {
|
|
53
|
-
switch (
|
|
105
|
+
switch (type) {
|
|
54
106
|
case "null":
|
|
55
107
|
return { type: "null" };
|
|
56
|
-
case "bool":
|
|
57
|
-
|
|
108
|
+
case "bool": {
|
|
109
|
+
const child = readDataProperty(value, "value");
|
|
110
|
+
if (typeof child !== "boolean") {
|
|
58
111
|
throw new ReallyMeCodecError("invalid-input");
|
|
59
112
|
}
|
|
60
|
-
return { type: "bool", value:
|
|
113
|
+
return { type: "bool", value: child };
|
|
114
|
+
}
|
|
61
115
|
case "int":
|
|
62
|
-
return {
|
|
116
|
+
return {
|
|
117
|
+
type: "int",
|
|
118
|
+
value: validateCborInteger(readDataProperty(value, "value")),
|
|
119
|
+
};
|
|
63
120
|
case "string":
|
|
64
|
-
case "bytes":
|
|
65
|
-
|
|
121
|
+
case "bytes": {
|
|
122
|
+
const child = readDataProperty(value, "value");
|
|
123
|
+
if (typeof child !== "string") {
|
|
66
124
|
throw new ReallyMeCodecError("invalid-input");
|
|
67
125
|
}
|
|
68
|
-
return { type
|
|
69
|
-
|
|
70
|
-
|
|
126
|
+
return { type, value: child };
|
|
127
|
+
}
|
|
128
|
+
case "array": {
|
|
129
|
+
const children = snapshotArray(readDataProperty(value, "value"));
|
|
130
|
+
if (children.length > MAX_CODEC_BOUNDARY_NODES - state.nodes) {
|
|
71
131
|
throw new ReallyMeCodecError("invalid-input");
|
|
72
132
|
}
|
|
73
133
|
return {
|
|
74
134
|
type: "array",
|
|
75
|
-
value:
|
|
135
|
+
value: children.map((entry) => validateCborValue(entry, childContainerDepth(depth), seen, state)),
|
|
76
136
|
};
|
|
77
|
-
|
|
78
|
-
|
|
137
|
+
}
|
|
138
|
+
case "map": {
|
|
139
|
+
const entries = snapshotArray(readDataProperty(value, "value"));
|
|
140
|
+
if (entries.length > MAX_CODEC_BOUNDARY_NODES - state.nodes) {
|
|
79
141
|
throw new ReallyMeCodecError("invalid-input");
|
|
80
142
|
}
|
|
143
|
+
state.nodes += entries.length;
|
|
144
|
+
const keys = new Set();
|
|
81
145
|
return {
|
|
82
146
|
type: "map",
|
|
83
|
-
value:
|
|
84
|
-
if (!isRecord(entry)
|
|
147
|
+
value: entries.map((entry) => {
|
|
148
|
+
if (!isRecord(entry)) {
|
|
149
|
+
throw new ReallyMeCodecError("invalid-input");
|
|
150
|
+
}
|
|
151
|
+
const key = readDataProperty(entry, "key");
|
|
152
|
+
if (typeof key !== "string" || keys.has(key)) {
|
|
85
153
|
throw new ReallyMeCodecError("invalid-input");
|
|
86
154
|
}
|
|
155
|
+
keys.add(key);
|
|
87
156
|
return {
|
|
88
|
-
key
|
|
89
|
-
value: validateCborValue(entry
|
|
157
|
+
key,
|
|
158
|
+
value: validateCborValue(readDataProperty(entry, "value"), childContainerDepth(depth), seen, state),
|
|
90
159
|
};
|
|
91
160
|
}),
|
|
92
161
|
};
|
|
162
|
+
}
|
|
93
163
|
default:
|
|
94
164
|
throw new ReallyMeCodecError("invalid-input");
|
|
95
165
|
}
|
|
@@ -124,8 +194,13 @@ const cborValueForJson = (value) => {
|
|
|
124
194
|
}
|
|
125
195
|
};
|
|
126
196
|
const readCborValue = (json) => {
|
|
127
|
-
|
|
128
|
-
|
|
197
|
+
try {
|
|
198
|
+
const parsed = JSON.parse(json);
|
|
199
|
+
return validateCborValue(parsed);
|
|
200
|
+
}
|
|
201
|
+
catch (_error) {
|
|
202
|
+
throw new ReallyMeCodecError("provider-failure");
|
|
203
|
+
}
|
|
129
204
|
};
|
|
130
205
|
const readCidVerification = (value) => {
|
|
131
206
|
const object = readObjectOutput(value);
|
|
@@ -136,8 +211,17 @@ const readCidVerification = (value) => {
|
|
|
136
211
|
};
|
|
137
212
|
};
|
|
138
213
|
export const dagCborEncode = (value) => {
|
|
139
|
-
|
|
140
|
-
|
|
214
|
+
let normalized;
|
|
215
|
+
try {
|
|
216
|
+
normalized = validateCborValue(value);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
if (error instanceof ReallyMeCodecError) {
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
return invalidCborInput();
|
|
223
|
+
}
|
|
224
|
+
return readBytesOutput(requireReallyMeCodecWasmProvider().dagCborEncode(stringifyBoundaryJson(cborValueForJson(normalized))));
|
|
141
225
|
};
|
|
142
226
|
export const dagCborDecode = (bytes) => {
|
|
143
227
|
ensureBytesInput(bytes);
|
|
@@ -153,14 +237,20 @@ export const dagCborVerifyCid = (cid, bytes) => {
|
|
|
153
237
|
return readCidVerification(requireReallyMeCodecWasmProvider().dagCborVerifyCid(cid, bytes));
|
|
154
238
|
};
|
|
155
239
|
export const dagCborVerifyCidProto = (cid, bytes) => {
|
|
156
|
-
|
|
157
|
-
ensureBytesInput(bytes);
|
|
158
|
-
return readBytesOutput(requireReallyMeCodecWasmProvider().dagCborVerifyCidProto(cid, bytes));
|
|
240
|
+
return protoPayloadOrThrow(dagCborVerifyCidProtoResult(cid, bytes));
|
|
159
241
|
};
|
|
160
242
|
export const dagCborVerifyCidProtoResult = (cid, bytes) => {
|
|
161
243
|
ensureStringValue(cid);
|
|
162
244
|
ensureBytesInput(bytes);
|
|
163
|
-
return
|
|
245
|
+
return processGeneratedProtoRequest(create(CodecOperationRequestSchema, {
|
|
246
|
+
operation: {
|
|
247
|
+
case: "dagCborVerifyCid",
|
|
248
|
+
value: create(CodecDagCborVerifyCidRequestSchema, {
|
|
249
|
+
cid,
|
|
250
|
+
payload: bytes,
|
|
251
|
+
}),
|
|
252
|
+
},
|
|
253
|
+
}));
|
|
164
254
|
};
|
|
165
255
|
export const dagCborSha256ContentHash = (bytes) => {
|
|
166
256
|
ensureBytesInput(bytes);
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { dagCborCodecCode, dagCborComputeCid, dagCborDecode, dagCborEncode, dagC
|
|
|
7
7
|
export type { ReallyMeCborMapEntry, ReallyMeCborValue, ReallyMeDagCborCidVerification, } from "./cbor.js";
|
|
8
8
|
export { canonicalizeJson, canonicalizeJsonText } from "./jcs.js";
|
|
9
9
|
export { decodePem, decodePemProto, decodePemProtoResult, encodePem } from "./pem.js";
|
|
10
|
+
export { processProto, processProtoJson } from "./protoProcess.js";
|
|
10
11
|
export type { ReallyMeCodecProtoResult, ReallyMeCodecProtoStatus } from "./readOutput.js";
|
|
11
12
|
export type { ReallyMePemDecodePolicy, ReallyMePemDocument, ReallyMePemEncodeOptions, ReallyMePemLabel, ReallyMePemLineEnding, } from "./pem.js";
|
|
12
13
|
export { REALLYME_CODEC_WASM_EXPORTS, installReallyMeCodecWasmProvider, requireReallyMeCodecWasmProvider, } from "./wasmProvider.js";
|
|
@@ -54,8 +55,10 @@ export declare const ReallyMeCodec: {
|
|
|
54
55
|
readonly tryParseCid: (cid: string) => string | undefined;
|
|
55
56
|
readonly canonicalizeJson: (value: unknown) => string;
|
|
56
57
|
readonly canonicalizeJsonText: (json: string) => string;
|
|
57
|
-
readonly decodePem: (input:
|
|
58
|
-
readonly decodePemProto: (input:
|
|
59
|
-
readonly decodePemProtoResult: (input:
|
|
60
|
-
readonly encodePem: (label: import("./pem.js").ReallyMePemLabel, der: Uint8Array, options?: import("./pem.js").ReallyMePemEncodeOptions) =>
|
|
58
|
+
readonly decodePem: (input: Uint8Array, policy?: import("./pem.js").ReallyMePemDecodePolicy) => import("./pem.js").ReallyMePemDocument;
|
|
59
|
+
readonly decodePemProto: (input: Uint8Array, policy?: import("./pem.js").ReallyMePemDecodePolicy) => Uint8Array;
|
|
60
|
+
readonly decodePemProtoResult: (input: Uint8Array, policy?: import("./pem.js").ReallyMePemDecodePolicy) => import("./readOutput.js").ReallyMeCodecProtoResult;
|
|
61
|
+
readonly encodePem: (label: import("./pem.js").ReallyMePemLabel, der: Uint8Array, options?: import("./pem.js").ReallyMePemEncodeOptions) => Uint8Array;
|
|
62
|
+
readonly processProto: (requestBytes: Uint8Array) => Uint8Array;
|
|
63
|
+
readonly processProtoJson: (requestJson: Uint8Array) => Uint8Array;
|
|
61
64
|
};
|
package/dist/index.js
CHANGED
|
@@ -7,12 +7,14 @@ export { base58btcDecode, base58btcEncode, bindingTypeMatchesCodec, multibaseBas
|
|
|
7
7
|
export { dagCborCodecCode, dagCborComputeCid, dagCborDecode, dagCborEncode, dagCborMultihash, dagCborSha256ContentHash, dagCborVerifyCid, dagCborVerifyCidProto, dagCborVerifyCidProtoResult, isValidCidString, tryParseCid, } from "./cbor.js";
|
|
8
8
|
export { canonicalizeJson, canonicalizeJsonText } from "./jcs.js";
|
|
9
9
|
export { decodePem, decodePemProto, decodePemProtoResult, encodePem } from "./pem.js";
|
|
10
|
+
export { processProto, processProtoJson } from "./protoProcess.js";
|
|
10
11
|
export { REALLYME_CODEC_WASM_EXPORTS, installReallyMeCodecWasmProvider, requireReallyMeCodecWasmProvider, } from "./wasmProvider.js";
|
|
11
12
|
import { base64Decode, base64Encode, base64urlDecode, base64urlDecodeBytes, base64urlEncode, bytesToLowerHex, lowerHexToBytes, } from "./baseEncoding.js";
|
|
12
13
|
import { dagCborCodecCode, dagCborComputeCid, dagCborDecode, dagCborEncode, dagCborMultihash, dagCborSha256ContentHash, dagCborVerifyCid, dagCborVerifyCidProto, dagCborVerifyCidProtoResult, isValidCidString, tryParseCid, } from "./cbor.js";
|
|
13
14
|
import { canonicalizeJson, canonicalizeJsonText } from "./jcs.js";
|
|
14
15
|
import { base58btcDecode, base58btcEncode, bindingTypeMatchesCodec, multibaseBase58btcEncode, multibaseBase64urlEncode, multibaseDecode, multicodecLookupPrefixProto, multicodecLookupPrefixProtoResult, multicodecLookupPrefix, multicodecPrefixForNameProto, multicodecPrefixForNameProtoResult, multicodecPrefixForName, multicodecStripPrefix, multicodecTable, multicodecTableProto, multicodecTableProtoResult, multikeyEncode, multikeyParse, multikeyParseProto, multikeyParseProtoResult, requireSupportedMulticodec, validateKeyBinding, } from "./multiformat.js";
|
|
15
16
|
import { decodePem, decodePemProto, decodePemProtoResult, encodePem } from "./pem.js";
|
|
17
|
+
import { processProto, processProtoJson } from "./protoProcess.js";
|
|
16
18
|
export const ReallyMeCodec = {
|
|
17
19
|
base64Decode,
|
|
18
20
|
base64Encode,
|
|
@@ -60,4 +62,6 @@ export const ReallyMeCodec = {
|
|
|
60
62
|
decodePemProto,
|
|
61
63
|
decodePemProtoResult,
|
|
62
64
|
encodePem,
|
|
65
|
+
processProto,
|
|
66
|
+
processProtoJson,
|
|
63
67
|
};
|
package/dist/jcs.js
CHANGED
|
@@ -1,71 +1,14 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import {
|
|
4
|
+
import { requireBoundaryUtf8String, stringifyBoundaryJson } from "./boundary.js";
|
|
5
5
|
import { readStringOutput } from "./readOutput.js";
|
|
6
6
|
import { requireReallyMeCodecWasmProvider } from "./wasmProvider.js";
|
|
7
|
-
const maxJsonContainerDepth = 128;
|
|
8
|
-
const validateJsonCompatible = (value, depth = 0, seen = new WeakSet()) => {
|
|
9
|
-
if (value === null) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
switch (typeof value) {
|
|
13
|
-
case "boolean":
|
|
14
|
-
case "string":
|
|
15
|
-
return;
|
|
16
|
-
case "number":
|
|
17
|
-
if (!Number.isFinite(value)) {
|
|
18
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
19
|
-
}
|
|
20
|
-
return;
|
|
21
|
-
case "bigint":
|
|
22
|
-
case "function":
|
|
23
|
-
case "symbol":
|
|
24
|
-
case "undefined":
|
|
25
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
26
|
-
case "object":
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
if (seen.has(value)) {
|
|
30
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
31
|
-
}
|
|
32
|
-
if (depth >= maxJsonContainerDepth) {
|
|
33
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
34
|
-
}
|
|
35
|
-
if (typeof Reflect.get(value, "toJSON") === "function") {
|
|
36
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
37
|
-
}
|
|
38
|
-
const prototype = Object.getPrototypeOf(value);
|
|
39
|
-
if (prototype !== Object.prototype && prototype !== null && !Array.isArray(value)) {
|
|
40
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
41
|
-
}
|
|
42
|
-
seen.add(value);
|
|
43
|
-
try {
|
|
44
|
-
if (Array.isArray(value)) {
|
|
45
|
-
for (const entry of value) {
|
|
46
|
-
validateJsonCompatible(entry, depth + 1, seen);
|
|
47
|
-
}
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
for (const key of Object.keys(value)) {
|
|
51
|
-
validateJsonCompatible(Reflect.get(value, key), depth + 1, seen);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
finally {
|
|
55
|
-
seen.delete(value);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
7
|
export const canonicalizeJson = (value) => {
|
|
59
|
-
|
|
60
|
-
const encoded = JSON.stringify(value);
|
|
61
|
-
if (encoded === undefined) {
|
|
62
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
63
|
-
}
|
|
8
|
+
const encoded = stringifyBoundaryJson(value);
|
|
64
9
|
return readStringOutput(requireReallyMeCodecWasmProvider().canonicalizeJson(encoded));
|
|
65
10
|
};
|
|
66
11
|
export const canonicalizeJsonText = (json) => {
|
|
67
|
-
|
|
68
|
-
throw new ReallyMeCodecError("invalid-input");
|
|
69
|
-
}
|
|
12
|
+
requireBoundaryUtf8String(json, false);
|
|
70
13
|
return readStringOutput(requireReallyMeCodecWasmProvider().canonicalizeJson(json));
|
|
71
14
|
};
|