@effing/serde 0.1.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/LICENSE +11 -0
- package/README.md +142 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
O'Saasy License
|
|
2
|
+
|
|
3
|
+
Copyright © 2026, Trackuity BV.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
2. No licensee or downstream recipient may use the Software (including any modified or derivative versions) to directly compete with the original Licensor by offering it to third parties as a hosted, managed, or Software-as-a-Service (SaaS) product or cloud service where the primary value of the service is the functionality of the Software itself.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @effing/serde
|
|
2
|
+
|
|
3
|
+
**URL-safe serialization with compression and HMAC signing.**
|
|
4
|
+
|
|
5
|
+
> Part of the [**Effing**](../../README.md) family — programmatic video creation with TypeScript.
|
|
6
|
+
|
|
7
|
+
Serialize JSON data into URL-safe strings with automatic compression and cryptographic signing. Compatible with Python's [itsdangerous](https://itsdangerous.palletsprojects.com/).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @effing/serde
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { serialize, deserialize } from "@effing/serde";
|
|
19
|
+
|
|
20
|
+
const secret = process.env.SECRET_KEY!;
|
|
21
|
+
const data = { userId: 123, action: "view" };
|
|
22
|
+
|
|
23
|
+
// Signed (and sometimes compressed) URL segment
|
|
24
|
+
const segment = await serialize(data, secret);
|
|
25
|
+
|
|
26
|
+
// Verify signature (and decompress if needed)
|
|
27
|
+
const restored = await deserialize<typeof data>(segment, secret);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Concepts
|
|
31
|
+
|
|
32
|
+
### URL-Safe Base64
|
|
33
|
+
|
|
34
|
+
Standard Base64 uses `+` and `/` which have special meaning in URLs. This package uses URL-safe Base64:
|
|
35
|
+
|
|
36
|
+
- `+` → `-`
|
|
37
|
+
- `/` → `_`
|
|
38
|
+
- No padding (`=`)
|
|
39
|
+
|
|
40
|
+
### Signed Serialization
|
|
41
|
+
|
|
42
|
+
Serialization is signed with HMAC (default: `sha1`) so the result can safely be used in a URL without being tampered with:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// Server: create signed URL segment
|
|
46
|
+
const segment = await serialize(data, secret);
|
|
47
|
+
|
|
48
|
+
// Client: passes segment in URL
|
|
49
|
+
// Server: verify and deserialize
|
|
50
|
+
const data = await deserialize(segment, secret);
|
|
51
|
+
// Throws if signature is invalid.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Compression
|
|
55
|
+
|
|
56
|
+
Payloads are gzip-compressed when it saves space. Compressed payloads are prefixed with a leading `"."` (matching `itsdangerous`).
|
|
57
|
+
|
|
58
|
+
## API Overview
|
|
59
|
+
|
|
60
|
+
#### `serialize(obj, secretKey, options?)`
|
|
61
|
+
|
|
62
|
+
Serialize a value to a URL-safe string.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
function serialize(
|
|
66
|
+
obj: object,
|
|
67
|
+
secretKey: string,
|
|
68
|
+
options?: {
|
|
69
|
+
/** Salt for key derivation (default: "itsdangerous") */
|
|
70
|
+
salt?: string;
|
|
71
|
+
/** Hash algorithm for HMAC (default: "sha1") */
|
|
72
|
+
algorithm?: string;
|
|
73
|
+
},
|
|
74
|
+
): Promise<string>;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### `deserialize(segment, secretKey, options?)`
|
|
78
|
+
|
|
79
|
+
Deserialize a URL segment back to a value.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
function deserialize<T = Record<string, unknown>>(
|
|
83
|
+
segment: string,
|
|
84
|
+
secretKey: string,
|
|
85
|
+
options?: {
|
|
86
|
+
/** Salt for key derivation (default: "itsdangerous") */
|
|
87
|
+
salt?: string;
|
|
88
|
+
/** Hash algorithm for HMAC (default: "sha1") */
|
|
89
|
+
algorithm?: string;
|
|
90
|
+
/** Convert snake_case keys to camelCase (default: true) */
|
|
91
|
+
convertKeysToCamel?: boolean;
|
|
92
|
+
},
|
|
93
|
+
): Promise<T>;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Throws:**
|
|
97
|
+
|
|
98
|
+
- `Error` — If signature verification fails
|
|
99
|
+
|
|
100
|
+
## Examples
|
|
101
|
+
|
|
102
|
+
### Passing Props in URLs
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { serialize, deserialize } from "@effing/serde";
|
|
106
|
+
|
|
107
|
+
// Create URL with serialized props
|
|
108
|
+
const secret = process.env.SECRET_KEY!;
|
|
109
|
+
const props = { imageUrl: "https://example.com/image.png", duration: 5 };
|
|
110
|
+
const segment = await serialize(props, secret);
|
|
111
|
+
const url = `/render/${segment}`;
|
|
112
|
+
|
|
113
|
+
// In route handler
|
|
114
|
+
async function loader({ params }) {
|
|
115
|
+
const props = await deserialize(params.segment, secret);
|
|
116
|
+
// props = { imageUrl: "https://example.com/image.png", duration: 5 }
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
> **Note:** The `convertKeysToCamel` deserialization option (which is `true` by default) is useful when URLs are built in Python (with itsdangerous) and then consumed by Effing. Python typically uses `snake_case` keys, while TypeScript prefers `camelCase`.
|
|
121
|
+
|
|
122
|
+
### Secure Tokens
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const SECRET = process.env.TOKEN_SECRET!;
|
|
126
|
+
|
|
127
|
+
// Create signed token
|
|
128
|
+
async function createToken(userId: number, expiresAt: number) {
|
|
129
|
+
return serialize({ userId, expiresAt }, SECRET);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Verify token
|
|
133
|
+
async function verifyToken(token: string) {
|
|
134
|
+
try {
|
|
135
|
+
const { userId, expiresAt } = await deserialize(token, SECRET);
|
|
136
|
+
if (Date.now() > expiresAt) throw new Error("Token expired");
|
|
137
|
+
return userId;
|
|
138
|
+
} catch (e) {
|
|
139
|
+
throw new Error("Invalid token");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
interface SerializeOptions {
|
|
2
|
+
/** Salt for key derivation (default: "itsdangerous") */
|
|
3
|
+
salt?: string;
|
|
4
|
+
/** Hash algorithm for HMAC (default: "sha1") */
|
|
5
|
+
algorithm?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Serialize an object to a URL-safe segment with optional compression and HMAC signature.
|
|
9
|
+
*
|
|
10
|
+
* The format is compatible with Python's itsdangerous library.
|
|
11
|
+
* - If compression saves space, the payload is prefixed with "."
|
|
12
|
+
* - The signature is appended after a "." separator
|
|
13
|
+
*/
|
|
14
|
+
declare function serialize(obj: object, secretKey: string, options?: SerializeOptions): Promise<string>;
|
|
15
|
+
interface DeserializeOptions extends SerializeOptions {
|
|
16
|
+
/** Whether to convert snake_case keys to camelCase (default: true) */
|
|
17
|
+
convertKeysToCamel?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Deserialize a URL segment, verify its signature, and decompress if needed.
|
|
21
|
+
*
|
|
22
|
+
* Throws an error if the signature is invalid.
|
|
23
|
+
*/
|
|
24
|
+
declare function deserialize<T = Record<string, unknown>>(segment: string, secretKey: string, options?: DeserializeOptions): Promise<T>;
|
|
25
|
+
|
|
26
|
+
export { type DeserializeOptions, type SerializeOptions, deserialize, serialize };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/itsdangerous.ts
|
|
2
|
+
import zlib from "zlib";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import { createHash, createHmac } from "crypto";
|
|
5
|
+
var zip = promisify(zlib.gzip);
|
|
6
|
+
var unzip = promisify(zlib.unzip);
|
|
7
|
+
function urlsafeBase64Encode(data, unsafeCharsMapping = { "+": "-", "/": "_" }) {
|
|
8
|
+
return Buffer.from(data).toString("base64").replace(/[+/]/g, (m) => unsafeCharsMapping[m]);
|
|
9
|
+
}
|
|
10
|
+
function urlsafeBase64Decode(encoded, unsafeCharsMapping = { "-": "+", _: "/" }) {
|
|
11
|
+
return Buffer.from(
|
|
12
|
+
encoded.replace(/[-_]/g, (m) => unsafeCharsMapping[m]),
|
|
13
|
+
"base64"
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
function deriveSigningKey(secretKey, salt, algorithm) {
|
|
17
|
+
return createHash(algorithm).update(`${salt}signer${secretKey}`).digest();
|
|
18
|
+
}
|
|
19
|
+
function keysToCamel(obj) {
|
|
20
|
+
const newObj = {};
|
|
21
|
+
Object.keys(obj).forEach((key) => {
|
|
22
|
+
const camelKey = key.replace(
|
|
23
|
+
/(_[a-z])/gi,
|
|
24
|
+
(s) => s.toUpperCase().replace("_", "")
|
|
25
|
+
);
|
|
26
|
+
newObj[camelKey] = obj[key];
|
|
27
|
+
});
|
|
28
|
+
return newObj;
|
|
29
|
+
}
|
|
30
|
+
async function serialize(obj, secretKey, options = {}) {
|
|
31
|
+
const { salt = "itsdangerous", algorithm = "sha1" } = options;
|
|
32
|
+
let json = Buffer.from(JSON.stringify(obj));
|
|
33
|
+
const compressed = await zip(json);
|
|
34
|
+
let isCompressed = false;
|
|
35
|
+
if (compressed.length < json.length - 1) {
|
|
36
|
+
json = compressed;
|
|
37
|
+
isCompressed = true;
|
|
38
|
+
}
|
|
39
|
+
let encoded = urlsafeBase64Encode(json);
|
|
40
|
+
if (isCompressed) {
|
|
41
|
+
encoded = `.${encoded}`;
|
|
42
|
+
}
|
|
43
|
+
const derivedKey = deriveSigningKey(secretKey, salt, algorithm);
|
|
44
|
+
const hmac = createHmac(algorithm, derivedKey).update(encoded).digest();
|
|
45
|
+
return `${encoded}.${urlsafeBase64Encode(hmac)}`;
|
|
46
|
+
}
|
|
47
|
+
async function deserialize(segment, secretKey, options = {}) {
|
|
48
|
+
const {
|
|
49
|
+
salt = "itsdangerous",
|
|
50
|
+
algorithm = "sha1",
|
|
51
|
+
convertKeysToCamel = true
|
|
52
|
+
} = options;
|
|
53
|
+
const parts = segment.split(".");
|
|
54
|
+
const signature = parts.at(-1);
|
|
55
|
+
let payload = parts.slice(0, -1).join(".");
|
|
56
|
+
const derivedKey = deriveSigningKey(secretKey, salt, algorithm);
|
|
57
|
+
const hmac = createHmac(algorithm, derivedKey).update(payload).digest();
|
|
58
|
+
if (Buffer.compare(urlsafeBase64Decode(signature), hmac) !== 0) {
|
|
59
|
+
throw new Error("invalid url segment signature");
|
|
60
|
+
}
|
|
61
|
+
let decompress = false;
|
|
62
|
+
if (payload.startsWith(".")) {
|
|
63
|
+
decompress = true;
|
|
64
|
+
payload = payload.slice(1);
|
|
65
|
+
}
|
|
66
|
+
const decoded = urlsafeBase64Decode(payload);
|
|
67
|
+
let parsed;
|
|
68
|
+
if (decompress) {
|
|
69
|
+
const decompressed = await unzip(decoded);
|
|
70
|
+
parsed = JSON.parse(decompressed.toString());
|
|
71
|
+
} else {
|
|
72
|
+
parsed = JSON.parse(decoded.toString());
|
|
73
|
+
}
|
|
74
|
+
if (convertKeysToCamel) {
|
|
75
|
+
return keysToCamel(parsed);
|
|
76
|
+
}
|
|
77
|
+
return parsed;
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
deserialize,
|
|
81
|
+
serialize
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/itsdangerous.ts"],"sourcesContent":["/**\n * TypeScript implementation of itsdangerous serialization logic.\n * @see https://github.com/pallets/itsdangerous/\n */\n\nimport zlib from \"node:zlib\";\nimport { promisify } from \"node:util\";\nimport { createHash, createHmac } from \"node:crypto\";\n\nconst zip = promisify(zlib.gzip);\nconst unzip = promisify(zlib.unzip);\n\n/**\n * Encode data as URL-safe base64.\n * Replaces + with - and / with _ by default.\n */\nexport function urlsafeBase64Encode(\n data: string | Buffer,\n unsafeCharsMapping: Record<string, string> = { \"+\": \"-\", \"/\": \"_\" },\n): string {\n return Buffer.from(data)\n .toString(\"base64\")\n .replace(/[+/]/g, (m) => unsafeCharsMapping[m]);\n}\n\n/**\n * Decode URL-safe base64 data.\n * Replaces - with + and _ with / by default.\n */\nexport function urlsafeBase64Decode(\n encoded: string,\n unsafeCharsMapping: Record<string, string> = { \"-\": \"+\", _: \"/\" },\n): Buffer {\n return Buffer.from(\n encoded.replace(/[-_]/g, (m) => unsafeCharsMapping[m]),\n \"base64\",\n );\n}\n\n/**\n * Derive an HMAC signing key using the itsdangerous-compatible format.\n */\nfunction deriveSigningKey(\n secretKey: string,\n salt: string,\n algorithm: string,\n): Buffer {\n return createHash(algorithm).update(`${salt}signer${secretKey}`).digest();\n}\n\n/**\n * Convert snake_case keys to camelCase.\n */\nfunction keysToCamel<T extends Record<string, unknown>>(\n obj: T,\n): Record<string, unknown> {\n const newObj: Record<string, unknown> = {};\n Object.keys(obj).forEach((key) => {\n const camelKey = key.replace(/(_[a-z])/gi, (s) =>\n s.toUpperCase().replace(\"_\", \"\"),\n );\n newObj[camelKey] = obj[key];\n });\n return newObj;\n}\n\nexport interface SerializeOptions {\n /** Salt for key derivation (default: \"itsdangerous\") */\n salt?: string;\n /** Hash algorithm for HMAC (default: \"sha1\") */\n algorithm?: string;\n}\n\n/**\n * Serialize an object to a URL-safe segment with optional compression and HMAC signature.\n *\n * The format is compatible with Python's itsdangerous library.\n * - If compression saves space, the payload is prefixed with \".\"\n * - The signature is appended after a \".\" separator\n */\nexport async function serialize(\n obj: object,\n secretKey: string,\n options: SerializeOptions = {},\n): Promise<string> {\n const { salt = \"itsdangerous\", algorithm = \"sha1\" } = options;\n\n let json = Buffer.from(JSON.stringify(obj));\n const compressed = await zip(json);\n\n let isCompressed = false;\n if (compressed.length < json.length - 1) {\n json = compressed;\n isCompressed = true;\n }\n\n let encoded = urlsafeBase64Encode(json);\n if (isCompressed) {\n encoded = `.${encoded}`;\n }\n\n const derivedKey = deriveSigningKey(secretKey, salt, algorithm);\n const hmac = createHmac(algorithm, derivedKey).update(encoded).digest();\n return `${encoded}.${urlsafeBase64Encode(hmac)}`;\n}\n\nexport interface DeserializeOptions extends SerializeOptions {\n /** Whether to convert snake_case keys to camelCase (default: true) */\n convertKeysToCamel?: boolean;\n}\n\n/**\n * Deserialize a URL segment, verify its signature, and decompress if needed.\n *\n * Throws an error if the signature is invalid.\n */\nexport async function deserialize<T = Record<string, unknown>>(\n segment: string,\n secretKey: string,\n options: DeserializeOptions = {},\n): Promise<T> {\n const {\n salt = \"itsdangerous\",\n algorithm = \"sha1\",\n convertKeysToCamel = true,\n } = options;\n\n const parts = segment.split(\".\");\n const signature = parts.at(-1);\n let payload = parts.slice(0, -1).join(\".\");\n\n const derivedKey = deriveSigningKey(secretKey, salt, algorithm);\n const hmac = createHmac(algorithm, derivedKey).update(payload).digest();\n if (Buffer.compare(urlsafeBase64Decode(signature!), hmac) !== 0) {\n throw new Error(\"invalid url segment signature\");\n }\n\n let decompress = false;\n if (payload.startsWith(\".\")) {\n decompress = true;\n payload = payload.slice(1);\n }\n\n const decoded = urlsafeBase64Decode(payload);\n let parsed: Record<string, unknown>;\n\n if (decompress) {\n const decompressed = await unzip(decoded);\n parsed = JSON.parse(decompressed.toString()) as Record<string, unknown>;\n } else {\n parsed = JSON.parse(decoded.toString()) as Record<string, unknown>;\n }\n\n if (convertKeysToCamel) {\n return keysToCamel(parsed) as T;\n }\n return parsed as T;\n}\n"],"mappings":";AAKA,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,YAAY,kBAAkB;AAEvC,IAAM,MAAM,UAAU,KAAK,IAAI;AAC/B,IAAM,QAAQ,UAAU,KAAK,KAAK;AAM3B,SAAS,oBACd,MACA,qBAA6C,EAAE,KAAK,KAAK,KAAK,IAAI,GAC1D;AACR,SAAO,OAAO,KAAK,IAAI,EACpB,SAAS,QAAQ,EACjB,QAAQ,SAAS,CAAC,MAAM,mBAAmB,CAAC,CAAC;AAClD;AAMO,SAAS,oBACd,SACA,qBAA6C,EAAE,KAAK,KAAK,GAAG,IAAI,GACxD;AACR,SAAO,OAAO;AAAA,IACZ,QAAQ,QAAQ,SAAS,CAAC,MAAM,mBAAmB,CAAC,CAAC;AAAA,IACrD;AAAA,EACF;AACF;AAKA,SAAS,iBACP,WACA,MACA,WACQ;AACR,SAAO,WAAW,SAAS,EAAE,OAAO,GAAG,IAAI,SAAS,SAAS,EAAE,EAAE,OAAO;AAC1E;AAKA,SAAS,YACP,KACyB;AACzB,QAAM,SAAkC,CAAC;AACzC,SAAO,KAAK,GAAG,EAAE,QAAQ,CAAC,QAAQ;AAChC,UAAM,WAAW,IAAI;AAAA,MAAQ;AAAA,MAAc,CAAC,MAC1C,EAAE,YAAY,EAAE,QAAQ,KAAK,EAAE;AAAA,IACjC;AACA,WAAO,QAAQ,IAAI,IAAI,GAAG;AAAA,EAC5B,CAAC;AACD,SAAO;AACT;AAgBA,eAAsB,UACpB,KACA,WACA,UAA4B,CAAC,GACZ;AACjB,QAAM,EAAE,OAAO,gBAAgB,YAAY,OAAO,IAAI;AAEtD,MAAI,OAAO,OAAO,KAAK,KAAK,UAAU,GAAG,CAAC;AAC1C,QAAM,aAAa,MAAM,IAAI,IAAI;AAEjC,MAAI,eAAe;AACnB,MAAI,WAAW,SAAS,KAAK,SAAS,GAAG;AACvC,WAAO;AACP,mBAAe;AAAA,EACjB;AAEA,MAAI,UAAU,oBAAoB,IAAI;AACtC,MAAI,cAAc;AAChB,cAAU,IAAI,OAAO;AAAA,EACvB;AAEA,QAAM,aAAa,iBAAiB,WAAW,MAAM,SAAS;AAC9D,QAAM,OAAO,WAAW,WAAW,UAAU,EAAE,OAAO,OAAO,EAAE,OAAO;AACtE,SAAO,GAAG,OAAO,IAAI,oBAAoB,IAAI,CAAC;AAChD;AAYA,eAAsB,YACpB,SACA,WACA,UAA8B,CAAC,GACnB;AACZ,QAAM;AAAA,IACJ,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,qBAAqB;AAAA,EACvB,IAAI;AAEJ,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,QAAM,YAAY,MAAM,GAAG,EAAE;AAC7B,MAAI,UAAU,MAAM,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG;AAEzC,QAAM,aAAa,iBAAiB,WAAW,MAAM,SAAS;AAC9D,QAAM,OAAO,WAAW,WAAW,UAAU,EAAE,OAAO,OAAO,EAAE,OAAO;AACtE,MAAI,OAAO,QAAQ,oBAAoB,SAAU,GAAG,IAAI,MAAM,GAAG;AAC/D,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,MAAI,aAAa;AACjB,MAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,iBAAa;AACb,cAAU,QAAQ,MAAM,CAAC;AAAA,EAC3B;AAEA,QAAM,UAAU,oBAAoB,OAAO;AAC3C,MAAI;AAEJ,MAAI,YAAY;AACd,UAAM,eAAe,MAAM,MAAM,OAAO;AACxC,aAAS,KAAK,MAAM,aAAa,SAAS,CAAC;AAAA,EAC7C,OAAO;AACL,aAAS,KAAK,MAAM,QAAQ,SAAS,CAAC;AAAA,EACxC;AAEA,MAAI,oBAAoB;AACtB,WAAO,YAAY,MAAM;AAAA,EAC3B;AACA,SAAO;AACT;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effing/serde",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "URL-safe serialization with compression and HMAC signing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"tsup": "^8.0.0",
|
|
17
|
+
"typescript": "^5.9.3",
|
|
18
|
+
"vitest": "^3.2.4"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"serialization",
|
|
22
|
+
"base64",
|
|
23
|
+
"hmac",
|
|
24
|
+
"url-safe"
|
|
25
|
+
],
|
|
26
|
+
"license": "O'Saasy",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest run"
|
|
34
|
+
}
|
|
35
|
+
}
|