@cratestack/cbor 0.5.2
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/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/node.d.ts +10 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +35 -0
- package/dist/node.js.map +1 -0
- package/dist/web.d.ts +2 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +15 -0
- package/dist/web.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stephane Segning
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# @cratestack/cbor
|
|
2
|
+
|
|
3
|
+
A thin umbrella package that auto-selects the right CBOR backend for
|
|
4
|
+
CrateStack's generated TypeScript RPC clients: `@cratestack/cbor-node`
|
|
5
|
+
(#286, native N-API) in Node, `@cratestack/cbor-web` (#287, WASM) in the
|
|
6
|
+
browser — one `npm install`, one import path, the environment picks the
|
|
7
|
+
implementation. The final piece of [epic #285](https://github.com/cratestack/cratestack/issues/285).
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @cratestack/cbor
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createCborCodec } from "@cratestack/cbor";
|
|
15
|
+
|
|
16
|
+
const codec = await createCborCodec();
|
|
17
|
+
|
|
18
|
+
const bytes = codec.encode({ hello: "world" });
|
|
19
|
+
const value = codec.decode(bytes);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Node projects never download the WASM binary; browser bundles never pull
|
|
23
|
+
in native `.node` files — each platform's `dependencies` (`@cratestack/cbor-node`
|
|
24
|
+
or `@cratestack/cbor-web`) is only actually loaded once the matching
|
|
25
|
+
`exports` condition resolves.
|
|
26
|
+
|
|
27
|
+
## How auto-selection works
|
|
28
|
+
|
|
29
|
+
This package's `package.json` declares conditional
|
|
30
|
+
[`exports`](https://nodejs.org/api/packages.html#conditional-exports) for
|
|
31
|
+
its `"."` entry:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"node": { "types": "./dist/node.d.ts", "import": "./dist/node.js" },
|
|
38
|
+
"browser": { "types": "./dist/web.d.ts", "import": "./dist/web.js" },
|
|
39
|
+
"default": { "types": "./dist/web.d.ts", "import": "./dist/web.js" }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- Node's own module resolver (and any bundler/test runner that sets the
|
|
46
|
+
`"node"` condition — e.g. Vitest's default `environment: "node"`,
|
|
47
|
+
webpack's `target: "node"`) picks `dist/node.js`, which re-exports
|
|
48
|
+
`@cratestack/cbor-node`.
|
|
49
|
+
- A browser-targeting bundler (Vite's client build, webpack's default `web`
|
|
50
|
+
target, Next.js client bundles) sets the `"browser"` condition and picks
|
|
51
|
+
`dist/web.js`, which re-exports `@cratestack/cbor-web`.
|
|
52
|
+
- `"default"` is the fallback for resolvers that set neither condition — it
|
|
53
|
+
points at the WASM build (`dist/web.js`), the more broadly portable of
|
|
54
|
+
the two: it only needs `fetch`/`URL`/`WebAssembly`, all present in every
|
|
55
|
+
modern JS runtime (Node, browsers, edge/worker runtimes), whereas the
|
|
56
|
+
Node build depends on a native `.node` binary that plainly cannot load
|
|
57
|
+
outside Node. The same reasoning is why the package-level `"main"`/`"types"`
|
|
58
|
+
fields (read by tooling that ignores `exports` entirely) also point at
|
|
59
|
+
the WASM build.
|
|
60
|
+
|
|
61
|
+
## Escape hatch: explicit subpaths
|
|
62
|
+
|
|
63
|
+
Conditional `exports` resolution isn't perfectly consistent across every
|
|
64
|
+
bundler/SSR setup — some SSR frameworks resolve the `"node"` condition even
|
|
65
|
+
for a module that will also ship to the client (their SSR build sets
|
|
66
|
+
`"node"` because the code *runs* under Node during the server render, even
|
|
67
|
+
though the same module graph gets bundled for the browser too). When
|
|
68
|
+
automatic resolution doesn't do what you need, import the platform build
|
|
69
|
+
directly:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// Force the native Node build, regardless of which condition the
|
|
73
|
+
// resolver would otherwise pick:
|
|
74
|
+
import { createCborCodec } from "@cratestack/cbor/node";
|
|
75
|
+
|
|
76
|
+
// Force the WASM build — e.g. from an SSR entry point whose bundler
|
|
77
|
+
// resolves "node" but whose output actually needs to run in the browser:
|
|
78
|
+
import { createCborCodec } from "@cratestack/cbor/web";
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Both subpaths export the exact same `createCborCodec()` shape as the root
|
|
82
|
+
entry point — they're what the root entry point's `"node"`/`"browser"`
|
|
83
|
+
conditions point at internally, just addressable directly.
|
|
84
|
+
|
|
85
|
+
## Sync vs. async: one uniform API
|
|
86
|
+
|
|
87
|
+
`@cratestack/cbor-node`'s own export (`cborCodec`) is a plain synchronous
|
|
88
|
+
object — native N-API modules load synchronously via Node's
|
|
89
|
+
`require`/ESM interop, so there's no async step to wait on.
|
|
90
|
+
`@cratestack/cbor-web`'s own export is necessarily async
|
|
91
|
+
(`createCborCodec(): Promise<CratestackRpcCodec>`) — one-time WASM
|
|
92
|
+
instantiation has no synchronous equivalent in a browser.
|
|
93
|
+
|
|
94
|
+
This package normalizes **both** platforms to the same async-factory
|
|
95
|
+
shape: `createCborCodec()` everywhere, always returning a `Promise`. On
|
|
96
|
+
Node that Promise resolves immediately (the underlying codec is already
|
|
97
|
+
loaded and synchronous by the time the module's `import` completes) — it's
|
|
98
|
+
strictly unnecessary async ceremony for that platform alone, but it buys a
|
|
99
|
+
single, genuinely platform-agnostic call site. `await createCborCodec()`
|
|
100
|
+
behaves identically no matter which condition resolved; nothing in
|
|
101
|
+
consuming code needs to branch on environment. Once resolved, every
|
|
102
|
+
`encode`/`decode` call is synchronous on both platforms — the async cost,
|
|
103
|
+
real or nominal, is paid exactly once.
|
|
104
|
+
|
|
105
|
+
The alternative — exposing the platform difference directly (a sync export
|
|
106
|
+
on Node, an async factory in the browser) and documenting it — was
|
|
107
|
+
considered and rejected: it would force every consumer of this umbrella
|
|
108
|
+
package to either branch on environment or always `await` a value that's
|
|
109
|
+
sometimes a real async boundary and sometimes not, which defeats the
|
|
110
|
+
purpose of an umbrella package that's supposed to hide exactly that kind
|
|
111
|
+
of platform difference.
|
|
112
|
+
|
|
113
|
+
## Error handling
|
|
114
|
+
|
|
115
|
+
Malformed CBOR input on `decode`, or a value `encode` can't represent,
|
|
116
|
+
throws a catchable JS `Error` on both platforms — see
|
|
117
|
+
`@cratestack/cbor-node`'s and `@cratestack/cbor-web`'s own READMEs for the
|
|
118
|
+
platform-specific mechanism (native panic-to-exception conversion vs. a
|
|
119
|
+
non-poisoning WASM trap boundary).
|
|
120
|
+
|
|
121
|
+
## Null handling
|
|
122
|
+
|
|
123
|
+
Both backends translate every JSON `null` (top-level or nested) to the
|
|
124
|
+
real CBOR null byte (`0xf6`), matching `cratestack-codec-cbor`'s own
|
|
125
|
+
`Option::None` encoding — not the empty-array quirk (`0x80`) some CBOR
|
|
126
|
+
backends produce for a bare unit type. See either platform package's
|
|
127
|
+
README for detail.
|
|
128
|
+
|
|
129
|
+
## Contributing to this package (monorepo-local dev)
|
|
130
|
+
|
|
131
|
+
This package's own build (`tsc`) is pure TypeScript — it never needs a Rust
|
|
132
|
+
or wasm toolchain. But its `dependencies` are `@cratestack/cbor-node` and
|
|
133
|
+
`@cratestack/cbor-web`, which do. Within this monorepo, this package's
|
|
134
|
+
turbo `build` task deliberately does **not** build those two siblings
|
|
135
|
+
automatically (see `turbo.json`'s `@cratestack/cbor#build` override) — a
|
|
136
|
+
default dependency edge would force every toolchain-free environment
|
|
137
|
+
(including this repo's own `js` CI job) to compile native/wasm code just
|
|
138
|
+
to typecheck a thin re-export wrapper.
|
|
139
|
+
|
|
140
|
+
Practical effect: `pnpm turbo run build test lint --filter='./packages/cratestack-cbor'`
|
|
141
|
+
type-checks cleanly and passes lint anywhere, but its Node round-trip
|
|
142
|
+
tests (`tests/node.test.ts`) only run for real if `@cratestack/cbor-node`
|
|
143
|
+
is *already* built — otherwise they skip (not fail) with a console
|
|
144
|
+
warning. For real coverage while developing, build the sibling first:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
pnpm turbo run build --filter='./packages/cratestack-cbor-node'
|
|
148
|
+
pnpm turbo run build test lint --filter='./packages/cratestack-cbor'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or just run the full unfiltered `pnpm turbo run build test lint` at the
|
|
152
|
+
repo root — that builds every package as its own top-level target
|
|
153
|
+
regardless of this edge, so the siblings are present and the tests run
|
|
154
|
+
for real.
|
|
155
|
+
|
|
156
|
+
## See Also
|
|
157
|
+
|
|
158
|
+
- `@cratestack/cbor-node` — the Node/native implementation (napi-rs).
|
|
159
|
+
- `@cratestack/cbor-web` — the browser/WASM implementation (wasm-bindgen).
|
|
160
|
+
- `crates/cratestack-codec-cbor` — the underlying, unchanged Rust codec
|
|
161
|
+
both implementations wrap.
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CratestackRpcCodec } from "@cratestack/ts-types";
|
|
2
|
+
/**
|
|
3
|
+
* Resolves immediately (no real async work — `@cratestack/cbor-node`'s
|
|
4
|
+
* underlying native codec is already loaded and synchronous by the time
|
|
5
|
+
* this module's `import` completes) to a {@link CratestackRpcCodec} backed
|
|
6
|
+
* by the native N-API CBOR codec. Async purely for call-site parity with
|
|
7
|
+
* the browser build's `createCborCodec()`, not because Node needs it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createCborCodec(): Promise<CratestackRpcCodec>;
|
|
10
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;;GAMG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAEnE"}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Node entry point (resolved by the `"node"` condition of this package's
|
|
2
|
+
// root `exports["."]`, and directly importable as the `@cratestack/cbor/node`
|
|
3
|
+
// escape hatch — see package.json and README.md).
|
|
4
|
+
//
|
|
5
|
+
// @cratestack/cbor-node's own export (`cborCodec`, see its src/index.ts) is
|
|
6
|
+
// a plain synchronous object: native N-API modules load synchronously via
|
|
7
|
+
// Node's `require`/ESM interop, so there's no async initialization step to
|
|
8
|
+
// wait on. @cratestack/cbor-web's export, by contrast, is necessarily async
|
|
9
|
+
// (`createCborCodec()`) — one-time WASM instantiation has no synchronous
|
|
10
|
+
// equivalent in a browser.
|
|
11
|
+
//
|
|
12
|
+
// This package normalizes both platforms to the SAME public shape: an async
|
|
13
|
+
// `createCborCodec()` factory everywhere. On this (Node) side that means
|
|
14
|
+
// wrapping an already-available synchronous value in a resolved Promise —
|
|
15
|
+
// strictly unnecessary work for this platform alone, but it buys a single,
|
|
16
|
+
// genuinely platform-agnostic call site (`await createCborCodec()` behaves
|
|
17
|
+
// identically regardless of which condition resolved). The alternative —
|
|
18
|
+
// exposing the sync/async difference directly and documenting it — would
|
|
19
|
+
// force every consumer of this umbrella package to branch on environment
|
|
20
|
+
// (or always `await` a value that's sometimes already resolved and
|
|
21
|
+
// sometimes a real async boundary), which defeats the point of an umbrella
|
|
22
|
+
// package in the first place. See README.md's "Sync vs. async" section and
|
|
23
|
+
// issue #288 for the full reasoning.
|
|
24
|
+
import { cborCodec } from "@cratestack/cbor-node";
|
|
25
|
+
/**
|
|
26
|
+
* Resolves immediately (no real async work — `@cratestack/cbor-node`'s
|
|
27
|
+
* underlying native codec is already loaded and synchronous by the time
|
|
28
|
+
* this module's `import` completes) to a {@link CratestackRpcCodec} backed
|
|
29
|
+
* by the native N-API CBOR codec. Async purely for call-site parity with
|
|
30
|
+
* the browser build's `createCborCodec()`, not because Node needs it.
|
|
31
|
+
*/
|
|
32
|
+
export async function createCborCodec() {
|
|
33
|
+
return cborCodec;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,8EAA8E;AAC9E,kDAAkD;AAClD,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,2EAA2E;AAC3E,4EAA4E;AAC5E,yEAAyE;AACzE,2BAA2B;AAC3B,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AACzE,0EAA0E;AAC1E,2EAA2E;AAC3E,2EAA2E;AAC3E,yEAAyE;AACzE,yEAAyE;AACzE,yEAAyE;AACzE,mEAAmE;AACnE,2EAA2E;AAC3E,2EAA2E;AAC3E,qCAAqC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/web.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Browser entry point (resolved by the `"browser"` and `"default"`
|
|
2
|
+
// conditions of this package's root `exports["."]`, and directly
|
|
3
|
+
// importable as the `@cratestack/cbor/web` escape hatch — see
|
|
4
|
+
// package.json and README.md).
|
|
5
|
+
//
|
|
6
|
+
// @cratestack/cbor-web's own `createCborCodec()` (see its src/index.ts) is
|
|
7
|
+
// already the uniform async-factory shape this umbrella package
|
|
8
|
+
// standardizes on for both platforms — see src/node.ts's doc comment for
|
|
9
|
+
// why. Nothing to adapt here: this file exists so the escape-hatch subpath
|
|
10
|
+
// (`@cratestack/cbor/web`) and the root `"browser"`/`"default"` conditions
|
|
11
|
+
// have their own dedicated compiled entry point, distinct from
|
|
12
|
+
// src/node.ts, rather than sharing one file whose behavior would need to
|
|
13
|
+
// branch at runtime.
|
|
14
|
+
export { createCborCodec } from "@cratestack/cbor-web";
|
|
15
|
+
//# sourceMappingURL=web.js.map
|
package/dist/web.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,iEAAiE;AACjE,8DAA8D;AAC9D,+BAA+B;AAC/B,EAAE;AACF,2EAA2E;AAC3E,gEAAgE;AAChE,yEAAyE;AACzE,2EAA2E;AAC3E,2EAA2E;AAC3E,+DAA+D;AAC/D,yEAAyE;AACzE,qBAAqB;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cratestack/cbor",
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "Umbrella CBOR codec for CrateStack's generated TypeScript RPC clients: conditional exports auto-select @cratestack/cbor-node in Node and @cratestack/cbor-web in the browser behind one import path, one uniform async createCborCodec() API on both platforms.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/cratestack/cratestack.git",
|
|
9
|
+
"directory": "packages/cratestack-cbor"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://cratestack.dev",
|
|
12
|
+
"bugs": "https://github.com/cratestack/cratestack/issues",
|
|
13
|
+
"keywords": ["cratestack", "cstack", "cbor", "codec", "rpc", "isomorphic"],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"main": "./dist/web.js",
|
|
17
|
+
"types": "./dist/web.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"node": {
|
|
21
|
+
"types": "./dist/node.d.ts",
|
|
22
|
+
"import": "./dist/node.js"
|
|
23
|
+
},
|
|
24
|
+
"browser": {
|
|
25
|
+
"types": "./dist/web.d.ts",
|
|
26
|
+
"import": "./dist/web.js"
|
|
27
|
+
},
|
|
28
|
+
"default": {
|
|
29
|
+
"types": "./dist/web.d.ts",
|
|
30
|
+
"import": "./dist/web.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"./node": {
|
|
34
|
+
"types": "./dist/node.d.ts",
|
|
35
|
+
"import": "./dist/node.js"
|
|
36
|
+
},
|
|
37
|
+
"./web": {
|
|
38
|
+
"types": "./dist/web.d.ts",
|
|
39
|
+
"import": "./dist/web.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc -p tsconfig.json",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"lint": "biome check ."
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@cratestack/cbor-node": "0.5.2",
|
|
50
|
+
"@cratestack/cbor-web": "0.5.2",
|
|
51
|
+
"@cratestack/ts-types": "0.5.2"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"typescript": "^5.7.0",
|
|
55
|
+
"vitest": "^3.0.0"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18"
|
|
59
|
+
}
|
|
60
|
+
}
|