cesr-ts 0.6.0 → 0.8.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 (59) hide show
  1. package/README.md +17 -6
  2. package/esm/mod.js +1 -0
  3. package/esm/src/{annotate/cli.js → cli/commands/annotate.js} +11 -40
  4. package/esm/src/cli/commands/bench.js +137 -0
  5. package/esm/src/cli/commands/validate.js +154 -0
  6. package/esm/src/cli/io-node.js +45 -0
  7. package/esm/src/cli/main.js +42 -0
  8. package/esm/src/cli/node.js +14 -0
  9. package/esm/src/cli/types.js +1 -0
  10. package/esm/src/core/parser-frame-parser.js +3 -0
  11. package/esm/src/primitives/mapper.js +48 -41
  12. package/esm/src/primitives/matter.js +7 -0
  13. package/esm/src/serder/native.js +62 -29
  14. package/esm/src/serder/serder.js +35 -46
  15. package/esm/src/serder/serdery.js +7 -43
  16. package/esm/src/tables/counter.tables.generated.js +1 -5
  17. package/esm/src/tables/hard-code-tables.js +39 -0
  18. package/esm/src/tables/indexer.tables.generated.js +1 -9
  19. package/esm/src/tables/matter.tables.generated.js +1 -14
  20. package/esm/src/version.js +7 -4
  21. package/package.json +3 -9
  22. package/types/mod.d.ts +1 -0
  23. package/types/mod.d.ts.map +1 -1
  24. package/types/src/cli/commands/annotate.d.ts +9 -0
  25. package/types/src/cli/commands/annotate.d.ts.map +1 -0
  26. package/types/src/cli/commands/bench.d.ts +4 -0
  27. package/types/src/cli/commands/bench.d.ts.map +1 -0
  28. package/types/src/cli/commands/validate.d.ts +4 -0
  29. package/types/src/cli/commands/validate.d.ts.map +1 -0
  30. package/types/src/cli/io-node.d.ts +4 -0
  31. package/types/src/cli/io-node.d.ts.map +1 -0
  32. package/types/src/cli/main.d.ts +10 -0
  33. package/types/src/cli/main.d.ts.map +1 -0
  34. package/types/src/cli/node.d.ts +9 -0
  35. package/types/src/cli/node.d.ts.map +1 -0
  36. package/types/src/cli/types.d.ts +22 -0
  37. package/types/src/cli/types.d.ts.map +1 -0
  38. package/types/src/core/parser-frame-parser.d.ts.map +1 -1
  39. package/types/src/primitives/mapper.d.ts.map +1 -1
  40. package/types/src/primitives/matter.d.ts.map +1 -1
  41. package/types/src/serder/native.d.ts +9 -0
  42. package/types/src/serder/native.d.ts.map +1 -1
  43. package/types/src/serder/serder.d.ts.map +1 -1
  44. package/types/src/serder/serdery.d.ts.map +1 -1
  45. package/types/src/tables/counter.tables.generated.d.ts +1 -1
  46. package/types/src/tables/counter.tables.generated.d.ts.map +1 -1
  47. package/types/src/tables/hard-code-tables.d.ts +15 -0
  48. package/types/src/tables/hard-code-tables.d.ts.map +1 -0
  49. package/types/src/tables/indexer.tables.generated.d.ts +1 -1
  50. package/types/src/tables/indexer.tables.generated.d.ts.map +1 -1
  51. package/types/src/tables/matter.tables.generated.d.ts +1 -1
  52. package/types/src/tables/matter.tables.generated.d.ts.map +1 -1
  53. package/types/src/version.d.ts +7 -4
  54. package/types/src/version.d.ts.map +1 -1
  55. package/esm/src/annotate/cli-node.js +0 -53
  56. package/types/src/annotate/cli-node.d.ts +0 -2
  57. package/types/src/annotate/cli-node.d.ts.map +0 -1
  58. package/types/src/annotate/cli.d.ts +0 -23
  59. package/types/src/annotate/cli.d.ts.map +0 -1
package/README.md CHANGED
@@ -25,16 +25,27 @@ const annotated = annotate(text, { domainHint: "txt", pretty: true });
25
25
  ## CLI usage
26
26
 
27
27
  ```bash
28
- npx cesr-annotate --in mystream.cesr --pretty
28
+ npm exec --package cesr-ts -- tephra annotate --in mystream.cesr --pretty
29
+ npm exec --package cesr-ts -- tephra validate --in mystream.cesr
30
+ npm exec --package cesr-ts -- tephra bench --in mystream.cesr --iterations 20 --warmup 3
29
31
  ```
30
32
 
31
- ## Deno CLI usage (from source)
33
+ After global install:
32
34
 
33
35
  ```bash
34
- deno task cesr:annotate --in mystream.cesr --pretty
36
+ tephra annotate --in mystream.cesr --pretty
37
+ tephra validate --in mystream.cesr
38
+ tephra bench --in mystream.cesr --iterations 20 --warmup 3
35
39
  ```
36
40
 
37
- ## Benchmarking (from source)
41
+ ## Deno CLI usage (from repository root)
42
+
43
+ ```bash
44
+ deno task tephra:annotate --in mystream.cesr --pretty
45
+ deno task tephra:validate --in mystream.cesr
46
+ ```
47
+
48
+ ## Benchmarking (from repository root)
38
49
 
39
50
  Run standard parser benchmark baselines:
40
51
 
@@ -45,8 +56,8 @@ deno task bench:cesr
45
56
  Run a benchmark on an arbitrary stream:
46
57
 
47
58
  ```bash
48
- deno task bench:cesr:parser --in ../../samples/cesr-streams/CESR_1_0-oor-auth-vc.cesr
49
- cat ../../samples/cesr-streams/CESR_1_0-oor-auth-vc.cesr | deno task bench:cesr:parser --iterations 20 --warmup 3
59
+ deno task tephra:bench --in ../../samples/cesr-streams/CESR_1_0-oor-auth-vc.cesr
60
+ cat ../../samples/cesr-streams/CESR_1_0-oor-auth-vc.cesr | deno task tephra:bench --iterations 20 --warmup 3
50
61
  ```
51
62
 
52
63
  ## Using cesr-ts through tufa
package/esm/mod.js CHANGED
@@ -1,3 +1,4 @@
1
+ /** cesr-ts npm package root entrypoint. */
1
2
  import "./_dnt.polyfills.js";
2
3
  export * from "./src/index.js";
3
4
  export * from "./src/version.js";
@@ -1,5 +1,7 @@
1
- import { annotate } from "./annotator.js";
1
+ import { annotate } from "../../annotate/annotator.js";
2
2
  const TEXT_DECODER = new TextDecoder();
3
+ const ANNOTATE_USAGE = "Usage: tephra annotate [--in <path>] [--out <path>] [--qb2] [--pretty]";
4
+ /** Parse `tephra annotate` command-line flags without performing IO. */
3
5
  function parseArgs(args) {
4
6
  const out = { qb2: false, pretty: false };
5
7
  for (let i = 0; i < args.length; i++) {
@@ -28,52 +30,21 @@ function parseArgs(args) {
28
30
  i++;
29
31
  continue;
30
32
  }
31
- if (arg === "--help" || arg === "-h") {
32
- throw new Error("Usage: cesr-annotate [--in <path>] [--out <path>] [--qb2] [--pretty]");
33
- }
34
33
  throw new Error(`Unknown argument: ${arg}`);
35
34
  }
36
35
  return out;
37
36
  }
38
37
  /**
39
- * Read a full `ReadableStream` into one contiguous byte buffer.
38
+ * Execute `tephra annotate`.
40
39
  *
41
- * This is the shared browser/runtime-neutral helper used by annotate CLI
42
- * shims that expose stdin as a web stream instead of synchronous file APIs.
40
+ * The command preserves the old standalone annotation behavior while moving it
41
+ * under the package-level dispatcher.
43
42
  */
44
- export async function readAllReadable(stream) {
45
- const reader = stream.getReader();
46
- const chunks = [];
47
- let total = 0;
48
- try {
49
- while (true) {
50
- const { value, done } = await reader.read();
51
- if (done)
52
- break;
53
- if (value) {
54
- chunks.push(value);
55
- total += value.length;
56
- }
57
- }
58
- }
59
- finally {
60
- reader.releaseLock();
61
- }
62
- const out = new Uint8Array(total);
63
- let offset = 0;
64
- for (const chunk of chunks) {
65
- out.set(chunk, offset);
66
- offset += chunk.length;
43
+ export async function annotateCommand(args, io) {
44
+ if (args.includes("--help") || args.includes("-h")) {
45
+ await io.writeStdout(`${ANNOTATE_USAGE}\n`);
46
+ return 0;
67
47
  }
68
- return out;
69
- }
70
- /**
71
- * Execute the standalone annotate CLI against the provided IO boundary.
72
- *
73
- * Error reporting is intentionally normalized here so runtime-specific entry
74
- * points can stay as thin bootstraps.
75
- */
76
- export async function annotateCli(args, io) {
77
48
  try {
78
49
  const options = parseArgs(args);
79
50
  const inputBytes = options.inPath
@@ -95,7 +66,7 @@ export async function annotateCli(args, io) {
95
66
  }
96
67
  catch (error) {
97
68
  const message = error instanceof Error ? error.message : String(error);
98
- await io.writeStderr(`cesr annotate error: ${message}\n`);
69
+ await io.writeStderr(`tephra annotate error: ${message}\n`);
99
70
  return 1;
100
71
  }
101
72
  }
@@ -0,0 +1,137 @@
1
+ import { benchmarkCesrParser } from "../../bench/parser-benchmark.js";
2
+ const BENCH_USAGE = "Usage: tephra bench [--in <path>] [--iterations <n>] [--warmup <n>] [--chunk-size <bytes>] [--framed] [--compat] [--allow-errors] [--json]";
3
+ /** Parse non-negative integer flags used for sizing and warmup controls. */
4
+ function parseNonNegativeInt(argName, value) {
5
+ const parsed = Number(value);
6
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 0) {
7
+ throw new Error(`${argName} must be a non-negative integer`);
8
+ }
9
+ return parsed;
10
+ }
11
+ /** Parse strictly positive integer flags used for measured iteration count. */
12
+ function parsePositiveInt(argName, value) {
13
+ const parsed = parseNonNegativeInt(argName, value);
14
+ if (parsed <= 0) {
15
+ throw new Error(`${argName} must be greater than 0`);
16
+ }
17
+ return parsed;
18
+ }
19
+ /** Parse `tephra bench` command-line flags without performing IO. */
20
+ function parseArgs(args) {
21
+ const out = {
22
+ iterations: 50,
23
+ warmupIterations: 5,
24
+ chunkSize: 0,
25
+ framed: false,
26
+ attachmentDispatchMode: "strict",
27
+ allowErrors: false,
28
+ json: false,
29
+ };
30
+ for (let i = 0; i < args.length; i++) {
31
+ const arg = args[i];
32
+ if (arg === "--in") {
33
+ const next = args[i + 1];
34
+ if (!next)
35
+ throw new Error("Missing value for --in");
36
+ out.inPath = next;
37
+ i++;
38
+ continue;
39
+ }
40
+ if (arg === "--iterations") {
41
+ const next = args[i + 1];
42
+ if (!next)
43
+ throw new Error("Missing value for --iterations");
44
+ out.iterations = parsePositiveInt("--iterations", next);
45
+ i++;
46
+ continue;
47
+ }
48
+ if (arg === "--warmup") {
49
+ const next = args[i + 1];
50
+ if (!next)
51
+ throw new Error("Missing value for --warmup");
52
+ out.warmupIterations = parseNonNegativeInt("--warmup", next);
53
+ i++;
54
+ continue;
55
+ }
56
+ if (arg === "--chunk-size") {
57
+ const next = args[i + 1];
58
+ if (!next)
59
+ throw new Error("Missing value for --chunk-size");
60
+ out.chunkSize = parseNonNegativeInt("--chunk-size", next);
61
+ i++;
62
+ continue;
63
+ }
64
+ if (arg === "--framed") {
65
+ out.framed = true;
66
+ continue;
67
+ }
68
+ if (arg === "--compat") {
69
+ out.attachmentDispatchMode = "compat";
70
+ continue;
71
+ }
72
+ if (arg === "--allow-errors") {
73
+ out.allowErrors = true;
74
+ continue;
75
+ }
76
+ if (arg === "--json") {
77
+ out.json = true;
78
+ continue;
79
+ }
80
+ throw new Error(`Unknown argument: ${arg}`);
81
+ }
82
+ return out;
83
+ }
84
+ /** Render human-readable benchmark output for terminal workflows. */
85
+ function formatHumanReadable(sourceLabel, result) {
86
+ const chunkLabel = result.chunkSize === result.bytesPerIteration
87
+ ? "full stream"
88
+ : `${result.chunkSize} bytes`;
89
+ return [
90
+ "CESR parser benchmark",
91
+ `source: ${sourceLabel}`,
92
+ `iterations: ${result.iterations} (warmup: ${result.warmupIterations})`,
93
+ `input bytes/iteration: ${result.bytesPerIteration}`,
94
+ `chunk size: ${chunkLabel}`,
95
+ `frames/iteration: ${(result.totalFrames / result.iterations).toFixed(2)}`,
96
+ `errors/iteration: ${(result.totalErrors / result.iterations).toFixed(2)}`,
97
+ `avg iteration: ${result.avgIterationMs.toFixed(3)} ms`,
98
+ `throughput: ${result.throughputMiBPerSec.toFixed(3)} MiB/s`,
99
+ `frame rate: ${result.framesPerSec.toFixed(2)} frames/s`,
100
+ ].join("\n");
101
+ }
102
+ /** Execute `tephra bench` using the existing parser benchmark engine. */
103
+ export async function benchCommand(args, io) {
104
+ if (args.includes("--help") || args.includes("-h")) {
105
+ await io.writeStdout(`${BENCH_USAGE}\n`);
106
+ return 0;
107
+ }
108
+ try {
109
+ const options = parseArgs(args);
110
+ const sourceLabel = options.inPath ? options.inPath : "stdin";
111
+ const inputBytes = options.inPath
112
+ ? await io.readFile(options.inPath)
113
+ : await io.readStdin();
114
+ const result = benchmarkCesrParser(inputBytes, {
115
+ iterations: options.iterations,
116
+ warmupIterations: options.warmupIterations,
117
+ chunkSize: options.chunkSize,
118
+ parserOptions: {
119
+ framed: options.framed,
120
+ attachmentDispatchMode: options.attachmentDispatchMode,
121
+ },
122
+ failOnParseError: !options.allowErrors,
123
+ });
124
+ if (options.json) {
125
+ await io.writeStdout(`${JSON.stringify({ source: sourceLabel, ...result })}\n`);
126
+ return 0;
127
+ }
128
+ const rendered = formatHumanReadable(sourceLabel, result);
129
+ await io.writeStdout(`${rendered}\n`);
130
+ return 0;
131
+ }
132
+ catch (error) {
133
+ const message = error instanceof Error ? error.message : String(error);
134
+ await io.writeStderr(`tephra bench error: ${message}\n`);
135
+ return 1;
136
+ }
137
+ }
@@ -0,0 +1,154 @@
1
+ import { parseBytes } from "../../core/parser-engine.js";
2
+ const VALIDATE_USAGE = "Usage: tephra validate [--in <path>] [--framed] [--compat] [--json]";
3
+ /** Parse `tephra validate` command-line flags without performing IO. */
4
+ function parseArgs(args) {
5
+ const out = {
6
+ framed: false,
7
+ attachmentDispatchMode: "strict",
8
+ json: false,
9
+ };
10
+ for (let i = 0; i < args.length; i++) {
11
+ const arg = args[i];
12
+ if (arg === "--in") {
13
+ const next = args[i + 1];
14
+ if (!next)
15
+ throw new Error("Missing value for --in");
16
+ out.inPath = next;
17
+ i++;
18
+ continue;
19
+ }
20
+ if (arg === "--framed") {
21
+ out.framed = true;
22
+ continue;
23
+ }
24
+ if (arg === "--compat") {
25
+ out.attachmentDispatchMode = "compat";
26
+ continue;
27
+ }
28
+ if (arg === "--json") {
29
+ out.json = true;
30
+ continue;
31
+ }
32
+ throw new Error(`Unknown argument: ${arg}`);
33
+ }
34
+ return out;
35
+ }
36
+ /**
37
+ * Convert parser errors into stable validation report entries.
38
+ *
39
+ * Parser error classes may carry useful location fields, but the CLI should not
40
+ * expose entire internal error objects as JSON. This function keeps the stable
41
+ * public fields and includes offset/context only when the parser supplied them.
42
+ */
43
+ function describeError(error) {
44
+ const detail = {
45
+ name: error.name || "Error",
46
+ message: error.message,
47
+ };
48
+ const maybeParserError = error;
49
+ if (typeof maybeParserError.offset === "number") {
50
+ detail.offset = maybeParserError.offset;
51
+ }
52
+ if (typeof maybeParserError.context === "string") {
53
+ detail.context = maybeParserError.context;
54
+ }
55
+ return detail;
56
+ }
57
+ /**
58
+ * Count frames, attachment groups, and parser errors for one input stream.
59
+ *
60
+ * `tephra validate` is strict by default because a validation command should fail
61
+ * on ambiguous attachment dispatch. `--compat` intentionally relaxes that mode
62
+ * for streams that still rely on legacy mixed-major attachment behavior.
63
+ *
64
+ * Empty input is reported as a validation failure even though the parser has no
65
+ * malformed bytes to diagnose. A zero-frame stream is not useful evidence that a
66
+ * CESR payload is valid.
67
+ */
68
+ function validateBytes(bytes, source, options) {
69
+ const events = parseBytes(bytes, {
70
+ framed: options.framed,
71
+ attachmentDispatchMode: options.attachmentDispatchMode,
72
+ });
73
+ const frames = events.filter((event) => event.type === "frame");
74
+ const errors = events
75
+ .filter((event) => event.type === "error")
76
+ .map((event) => describeError(event.error));
77
+ const attachmentGroupCount = frames.reduce((count, event) => count + event.frame.attachments.length, 0);
78
+ if (frames.length === 0 && errors.length === 0) {
79
+ errors.push({
80
+ name: "NoFramesError",
81
+ message: "No CESR frames parsed",
82
+ });
83
+ }
84
+ return {
85
+ ok: errors.length === 0,
86
+ source,
87
+ bytes: bytes.length,
88
+ frameCount: frames.length,
89
+ attachmentGroupCount,
90
+ errorCount: errors.length,
91
+ errors,
92
+ };
93
+ }
94
+ /** Render successful human-readable validation output. */
95
+ function formatSuccess(report) {
96
+ return [
97
+ "CESR validation passed",
98
+ `source: ${report.source}`,
99
+ `bytes: ${report.bytes}`,
100
+ `frames: ${report.frameCount}`,
101
+ `attachment groups: ${report.attachmentGroupCount}`,
102
+ ].join("\n");
103
+ }
104
+ /** Render human-readable validation failure output with per-error details. */
105
+ function formatFailure(report) {
106
+ const lines = [
107
+ "CESR validation failed",
108
+ `source: ${report.source}`,
109
+ `bytes: ${report.bytes}`,
110
+ `frames: ${report.frameCount}`,
111
+ `attachment groups: ${report.attachmentGroupCount}`,
112
+ `errors: ${report.errorCount}`,
113
+ ];
114
+ for (const error of report.errors) {
115
+ lines.push(`- ${error.name}: ${error.message}`);
116
+ if (error.offset !== undefined) {
117
+ lines.push(` offset: ${error.offset}`);
118
+ }
119
+ if (error.context) {
120
+ lines.push(` context: ${error.context}`);
121
+ }
122
+ }
123
+ return lines.join("\n");
124
+ }
125
+ /** Execute `tephra validate` against file input or stdin. */
126
+ export async function validateCommand(args, io) {
127
+ if (args.includes("--help") || args.includes("-h")) {
128
+ await io.writeStdout(`${VALIDATE_USAGE}\n`);
129
+ return 0;
130
+ }
131
+ try {
132
+ const options = parseArgs(args);
133
+ const source = options.inPath ? options.inPath : "stdin";
134
+ const inputBytes = options.inPath
135
+ ? await io.readFile(options.inPath)
136
+ : await io.readStdin();
137
+ const report = validateBytes(inputBytes, source, options);
138
+ if (options.json) {
139
+ await io.writeStdout(`${JSON.stringify(report)}\n`);
140
+ }
141
+ else if (report.ok) {
142
+ await io.writeStdout(`${formatSuccess(report)}\n`);
143
+ }
144
+ else {
145
+ await io.writeStderr(`${formatFailure(report)}\n`);
146
+ }
147
+ return report.ok ? 0 : 1;
148
+ }
149
+ catch (error) {
150
+ const message = error instanceof Error ? error.message : String(error);
151
+ await io.writeStderr(`tephra validate error: ${message}\n`);
152
+ return 1;
153
+ }
154
+ }
@@ -0,0 +1,45 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { stderr, stdin, stdout } from "node:process";
3
+ /** Read Node stdin into one contiguous byte buffer for command execution. */
4
+ async function readNodeStdin() {
5
+ const chunks = [];
6
+ let total = 0;
7
+ for await (const chunk of stdin) {
8
+ const bytes = chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk);
9
+ chunks.push(bytes);
10
+ total += bytes.length;
11
+ }
12
+ const out = new Uint8Array(total);
13
+ let offset = 0;
14
+ for (const chunk of chunks) {
15
+ out.set(chunk, offset);
16
+ offset += chunk.length;
17
+ }
18
+ return out;
19
+ }
20
+ /** Write text to a Node stream and wait for completion or backpressure error. */
21
+ async function writeNodeStream(stream, text) {
22
+ await new Promise((resolve, reject) => {
23
+ stream.write(text, (error) => {
24
+ if (error)
25
+ reject(error);
26
+ else
27
+ resolve();
28
+ });
29
+ });
30
+ }
31
+ /** Create the Node runtime IO adapter for the package-level `tephra` CLI. */
32
+ export function createNodeCliIo() {
33
+ return {
34
+ readFile: async (path) => {
35
+ const data = await readFile(path);
36
+ return new Uint8Array(data);
37
+ },
38
+ writeTextFile: async (path, text) => {
39
+ await writeFile(path, text, "utf8");
40
+ },
41
+ readStdin: readNodeStdin,
42
+ writeStdout: (text) => writeNodeStream(stdout, text),
43
+ writeStderr: (text) => writeNodeStream(stderr, text),
44
+ };
45
+ }
@@ -0,0 +1,42 @@
1
+ import { annotateCommand } from "./commands/annotate.js";
2
+ import { benchCommand } from "./commands/bench.js";
3
+ import { validateCommand } from "./commands/validate.js";
4
+ const COMMANDS = {
5
+ annotate: annotateCommand,
6
+ validate: validateCommand,
7
+ bench: benchCommand,
8
+ };
9
+ const TEPHRA_USAGE = [
10
+ "Usage: tephra <command> [options]",
11
+ "",
12
+ "Commands:",
13
+ " annotate Annotate a CESR stream",
14
+ " validate Validate a CESR stream",
15
+ " bench Benchmark CESR parser throughput",
16
+ "",
17
+ "Run `tephra <command> --help` for command-specific options.",
18
+ ].join("\n");
19
+ /**
20
+ * Execute the package-level `tephra` CLI dispatcher.
21
+ *
22
+ * This function owns only command selection and top-level usage behavior. Each
23
+ * subcommand receives the already-selected argument tail plus a runtime-neutral
24
+ * IO adapter, so command behavior remains identical under Deno and Node.
25
+ */
26
+ export async function tephraCli(args, io) {
27
+ const [command, ...commandArgs] = args;
28
+ if (command === "--help" || command === "-h") {
29
+ await io.writeStdout(`${TEPHRA_USAGE}\n`);
30
+ return 0;
31
+ }
32
+ if (!command) {
33
+ await io.writeStderr(`${TEPHRA_USAGE}\n`);
34
+ return 1;
35
+ }
36
+ const handler = COMMANDS[command];
37
+ if (!handler) {
38
+ await io.writeStderr(`Unknown tephra command: ${command}\n${TEPHRA_USAGE}\n`);
39
+ return 1;
40
+ }
41
+ return await handler(commandArgs, io);
42
+ }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * npm executable entrypoint for the package-level `tephra` CLI.
4
+ *
5
+ * The CESR npm build discovers this file by the marker text in this comment and
6
+ * prepends a Node shebang after DNT emits JavaScript. Keep this launcher thin so
7
+ * Deno-only APIs do not leak into the Node executable path.
8
+ */
9
+ import "../../_dnt.polyfills.js";
10
+ import { argv, exit } from "node:process";
11
+ import { createNodeCliIo } from "./io-node.js";
12
+ import { tephraCli } from "./main.js";
13
+ const code = await tephraCli(argv.slice(2), createNodeCliIo());
14
+ exit(code);
@@ -0,0 +1 @@
1
+ export {};
@@ -242,6 +242,9 @@ export class FrameParser {
242
242
  };
243
243
  }
244
244
  catch (_error) {
245
+ // Non-native body groups wrap one Matter primitive. If Serder hydration
246
+ // fails, expose the primitive body bytes only; callers that inspect the
247
+ // fallback should not see the surrounding group counter envelope.
245
248
  const raw = matter.raw;
246
249
  return {
247
250
  frame: {