@sebastienrousseau/crypto-typeorm 0.0.7
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 +8 -0
- package/README.md +365 -0
- package/dist/codec.d.ts +12 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +59 -0
- package/dist/decorator.d.ts +7 -0
- package/dist/decorator.d.ts.map +1 -0
- package/dist/decorator.js +24 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/subscriber.d.ts +15 -0
- package/dist/subscriber.d.ts.map +1 -0
- package/dist/subscriber.js +58 -0
- package/dist/transformer.d.ts +10 -0
- package/dist/transformer.d.ts.map +1 -0
- package/dist/transformer.js +33 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/package.json +73 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/sebastienrousseau/crypto-service/main/assets/crypto-typeorm-logo.svg" alt="crypto-typeorm logo" width="360" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h1 align="center">@sebastienrousseau/crypto-typeorm</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
TypeORM column-level encryption with a single decorator, powered by crypto-lib.
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://github.com/sebastienrousseau/crypto-service/actions"><img src="https://img.shields.io/github/actions/workflow/status/sebastienrousseau/crypto-service/ci.yml?branch=main&style=for-the-badge&logo=github" alt="Build" /></a>
|
|
15
|
+
<a href="https://coveralls.io/github/sebastienrousseau/crypto-service?branch=main"><img src="https://img.shields.io/coveralls/github/sebastienrousseau/crypto-service?branch=main&style=for-the-badge" alt="Coverage" /></a>
|
|
16
|
+
<a href="https://www.npmjs.com/package/@sebastienrousseau/crypto-typeorm"><img src="https://img.shields.io/npm/v/@sebastienrousseau/crypto-typeorm.svg?style=for-the-badge&color=f14041&logo=npm" alt="Registry" /></a>
|
|
17
|
+
<a href="https://sebastienrousseau.github.io/crypto-service/"><img src="https://img.shields.io/badge/docs-TypeDoc-blue.svg?style=for-the-badge&labelColor=555555&logo=typescript" alt="Docs" /></a>
|
|
18
|
+
<a href="https://scorecard.dev/viewer/?uri=github.com/sebastienrousseau/crypto-service" title="ossf-scorecard"><img src="https://img.shields.io/badge/OpenSSF-Scorecard-blue?style=for-the-badge&logo=openssf" alt="OpenSSF Scorecard" /></a>
|
|
19
|
+
<a href="https://github.com/sebastienrousseau/crypto-service/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0%20OR%20MIT-blue.svg?style=for-the-badge" alt="License: Apache-2.0 OR MIT" /></a>
|
|
20
|
+
<a href="https://github.com/sebastienrousseau/crypto-service/blob/main/docs/POLICIES.md"><img src="https://img.shields.io/badge/Node.js-%3E%3D22-93450a.svg?style=for-the-badge&logo=node.js" alt="Node.js 22 or newer" /></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Contents
|
|
26
|
+
|
|
27
|
+
**Getting started**
|
|
28
|
+
|
|
29
|
+
- [Install](#install) — installation via pnpm, npm, or yarn
|
|
30
|
+
- [Requirements](#requirements) — runtime floor and environment prerequisites
|
|
31
|
+
- [Quick Start](#quick-start) — minimal working usage sample
|
|
32
|
+
|
|
33
|
+
**The Crypto Service ecosystem**
|
|
34
|
+
|
|
35
|
+
- [The Crypto Service ecosystem](#the-crypto-service-ecosystem) — full 14-package suite overview
|
|
36
|
+
|
|
37
|
+
**Package reference**
|
|
38
|
+
|
|
39
|
+
- [Reference & Usage](#overview) — features, configuration, and capabilities
|
|
40
|
+
- [Examples](#examples) — runnable sample code
|
|
41
|
+
|
|
42
|
+
**Operational**
|
|
43
|
+
|
|
44
|
+
- [Development](#development) — build, lint, format, and test targets
|
|
45
|
+
- [Security](#security) — vulnerability disclosure and cryptographic invariants
|
|
46
|
+
- [Documentation](#documentation) — TypeDoc API docs and ecosystem guides
|
|
47
|
+
- [Stability guarantees](#stability-guarantees) — SemVer axis and release policy
|
|
48
|
+
- [License](#license)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm add @sebastienrousseau/crypto-typeorm
|
|
56
|
+
# or
|
|
57
|
+
npm install @sebastienrousseau/crypto-typeorm
|
|
58
|
+
# or
|
|
59
|
+
yarn add @sebastienrousseau/crypto-typeorm
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
- **Node.js**: `^22.0.0` or `>=24.0.0` (active and maintenance LTS releases)
|
|
69
|
+
- **Package Manager**: `pnpm >=9` (recommended) or `npm >=10`
|
|
70
|
+
- **TypeScript**: `>=5.0` (when compiling with TypeScript)
|
|
71
|
+
|
|
72
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { Entity, PrimaryGeneratedColumn } from "typeorm";
|
|
80
|
+
import { EncryptedColumn } from "@sebastienrousseau/crypto-typeorm";
|
|
81
|
+
|
|
82
|
+
@Entity()
|
|
83
|
+
class User {
|
|
84
|
+
@PrimaryGeneratedColumn()
|
|
85
|
+
id!: number;
|
|
86
|
+
|
|
87
|
+
@EncryptedColumn({
|
|
88
|
+
encrypt: { key: process.env.COLUMN_ENCRYPTION_KEY! },
|
|
89
|
+
})
|
|
90
|
+
ssn!: string;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
That is it. The `ssn` column is stored as an XChaCha20-Poly1305
|
|
95
|
+
sealed box (Base64) and decrypted transparently on every read.
|
|
96
|
+
|
|
97
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## The Crypto Service ecosystem
|
|
102
|
+
|
|
103
|
+
Crypto Service provides a complete cryptography stack across 14 specialized packages:
|
|
104
|
+
|
|
105
|
+
| Package | Role | Description |
|
|
106
|
+
| :---------------------------------------------------------------------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
|
|
107
|
+
| [`@sebastienrousseau/crypto-api`](../crypto-api) | API Schemas | Shared TypeScript types and utilities for the Crypto Service Suite, defining the canonical API surface. |
|
|
108
|
+
| [`@sebastienrousseau/crypto-cli`](../crypto-cli) | Terminal CLI | An interactive command-line interface for cryptographic operations, supporting both legacy OpenPGP and modern post-quantum algorithms. |
|
|
109
|
+
| [`@sebastienrousseau/crypto-edge`](../crypto-edge) | Edge Runtime | Edge-runtime cryptographic operations using the Web Crypto API, optimized for Cloudflare Workers, Vercel Edge, and Deno. |
|
|
110
|
+
| [`@sebastienrousseau/crypto-kms`](../crypto-kms) | Cloud KMS | Unified Key Management Service interface for AWS KMS, GCP Cloud KMS, Azure Key Vault, and HashiCorp Vault. |
|
|
111
|
+
| [`@sebastienrousseau/crypto-lib`](../crypto-lib) | Core Library | A modern cryptographic library for TypeScript, with post-quantum support, zero unsafe dependencies, and 100% test coverage. |
|
|
112
|
+
| [`@sebastienrousseau/crypto-middleware`](../crypto-middleware) | Middleware | Framework-agnostic cryptographic middleware for Express, Fastify, and Koa applications. |
|
|
113
|
+
| [`@sebastienrousseau/crypto-prisma`](../crypto-prisma) | ORM Adapter | Transparent field-level encryption extension for Prisma Client, powered by XChaCha20-Poly1305. |
|
|
114
|
+
| [`@sebastienrousseau/crypto-react`](../crypto-react) | React Hooks | React hooks and context provider for client-side cryptographic operations with zero boilerplate. |
|
|
115
|
+
| [`@sebastienrousseau/crypto-sdk`](../crypto-sdk) | Client SDK | A zero-dependency, typed HTTP client for the Crypto Service REST API, with full post-quantum support. |
|
|
116
|
+
| [`@sebastienrousseau/crypto-server`](../crypto-server) | HTTP API | A hardened Fastify REST API for cryptographic operations, with rate limiting, OpenAPI schemas, and post-quantum endpoints. |
|
|
117
|
+
| [`@sebastienrousseau/crypto-testing`](../crypto-testing) | Test Support | Deterministic keys, fast mocks, and test fixtures for crypto-lib |
|
|
118
|
+
| **[`@sebastienrousseau/crypto-typeorm`](../crypto-typeorm)** _(this package)_ | **ORM Adapter** | **TypeORM column-level encryption with a single decorator, powered by crypto-lib.** |
|
|
119
|
+
| [`@sebastienrousseau/crypto-vue`](../crypto-vue) | Vue Composables | Vue 3 composables for client-side cryptography |
|
|
120
|
+
| [`@sebastienrousseau/crypto-wasm`](../crypto-wasm) | Acceleration | WebAssembly performance accelerator for crypto-lib |
|
|
121
|
+
|
|
122
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Overview
|
|
127
|
+
|
|
128
|
+
crypto-typeorm provides column-level encryption for TypeORM
|
|
129
|
+
entities. Three integration styles are available: a `@EncryptedColumn`
|
|
130
|
+
decorator that combines TypeORM's `@Column` with automatic
|
|
131
|
+
encryption/decryption, an `EncryptionSubscriber` for centralised
|
|
132
|
+
field configuration, and an `EncryptionTransformer` for manual
|
|
133
|
+
`ValueTransformer` usage. All styles use XChaCha20-Poly1305 via
|
|
134
|
+
crypto-lib's secretbox, with fresh random nonces on every write.
|
|
135
|
+
|
|
136
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Configuration
|
|
141
|
+
|
|
142
|
+
All APIs accept an `EncryptionConfig` object:
|
|
143
|
+
|
|
144
|
+
| Property | Type | Default | Description |
|
|
145
|
+
| :----------------------- | :---------------------- | :--------------------- | :---------------------------------------------------------------------------------------------- |
|
|
146
|
+
| `key` | `string` | **required** | 256-bit key as a 64-char hex string |
|
|
147
|
+
| `algorithm` | `string` | `"xchacha20-poly1305"` | Algorithm identifier |
|
|
148
|
+
| `fields` | `Map<string, string[]>` | `undefined` | Per-entity field list (used by `EncryptionSubscriber`) |
|
|
149
|
+
| `context` | `string` | `""` | Column context bound into each ciphertext (`Entity.property`); set by `@EncryptedColumn` |
|
|
150
|
+
| `allowPlaintextFallback` | `boolean` | `false` | Return values that are not ciphertexts as-is instead of throwing; for plaintext migrations only |
|
|
151
|
+
| `acceptLegacyCiphertext` | `boolean` | `true` | Read ciphertexts written before the `v2:` format; turn off once every row has been re-written |
|
|
152
|
+
|
|
153
|
+
`@EncryptedColumn` reads `process.env.TYPEORM_ENCRYPTION_KEY` when
|
|
154
|
+
no key is provided in decorator options.
|
|
155
|
+
|
|
156
|
+
### Stored format and compatibility
|
|
157
|
+
|
|
158
|
+
New values are written as `v2:` followed by the Base64 sealed box
|
|
159
|
+
(24-byte nonce, ciphertext, 16-byte Poly1305 tag):
|
|
160
|
+
|
|
161
|
+
- **Encryption key.** Values are sealed under a subkey derived from
|
|
162
|
+
`key` with HKDF-SHA-256 (info `crypto-typeorm/enc/v2`), not under
|
|
163
|
+
`key` itself.
|
|
164
|
+
- **Associated data.** Each value is bound to
|
|
165
|
+
`crypto-typeorm/v2:<context>`. `@EncryptedColumn` uses
|
|
166
|
+
`ClassName.property` and `EncryptionSubscriber` uses
|
|
167
|
+
`EntityName.field`, so a ciphertext copied into another column or
|
|
168
|
+
entity fails to decrypt. The primary key is not bound: a
|
|
169
|
+
`ValueTransformer` never sees the row, and generated keys do not
|
|
170
|
+
exist yet at insert time, so two rows can still have the same
|
|
171
|
+
column's value swapped. A bare `EncryptionTransformer` without
|
|
172
|
+
`context` binds the empty context, shared with every other
|
|
173
|
+
context-less transformer; pass `context` explicitly.
|
|
174
|
+
- **Class names.** The default context comes from the class name. If
|
|
175
|
+
a bundler renames classes, pin `encrypt.context` (or keep class
|
|
176
|
+
names) so values written before and after the build still match.
|
|
177
|
+
- **Fail closed.** A value that does not decrypt throws
|
|
178
|
+
`FieldDecryptionError` (exported; it carries `context` and never
|
|
179
|
+
the stored value), from `EncryptionTransformer.from()` and from
|
|
180
|
+
`EncryptionSubscriber.afterLoad`. `allowPlaintextFallback: true`
|
|
181
|
+
returns a value that is not a ciphertext at all as-is, for the
|
|
182
|
+
duration of a plaintext migration; a `v2:` value that fails
|
|
183
|
+
authentication is rejected even then.
|
|
184
|
+
- **Legacy values.** Ciphertexts written by earlier versions (bare
|
|
185
|
+
Base64, sealed with `key` and no associated data) are still read,
|
|
186
|
+
and never written. They are not bound to a column. Rewrite them in
|
|
187
|
+
the `v2:` format (as `examples/migration.ts` does for plaintext:
|
|
188
|
+
read each value, write it back through the transformer), then set
|
|
189
|
+
`acceptLegacyCiphertext: false`.
|
|
190
|
+
|
|
191
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Decorator API
|
|
196
|
+
|
|
197
|
+
### `@EncryptedColumn(options?)`
|
|
198
|
+
|
|
199
|
+
A property decorator that combines TypeORM's `@Column` with an
|
|
200
|
+
`EncryptionTransformer`.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
@EncryptedColumn() // uses TYPEORM_ENCRYPTION_KEY env var
|
|
204
|
+
@EncryptedColumn({ encrypt: { key: "..." } }) // explicit key
|
|
205
|
+
@EncryptedColumn({ type: "text", nullable: true, encrypt: { key: "..." } })
|
|
206
|
+
@EncryptedColumn({ encrypt: { key: "...", context: "User.ssn" } }) // pinned context
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Each value is bound to `ClassName.property` unless `encrypt.context`
|
|
210
|
+
is given.
|
|
211
|
+
|
|
212
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Subscriber API
|
|
217
|
+
|
|
218
|
+
### `EncryptionSubscriber`
|
|
219
|
+
|
|
220
|
+
An `EntitySubscriberInterface` that encrypts/decrypts fields based
|
|
221
|
+
on a centralised configuration.
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { DataSource } from "typeorm";
|
|
225
|
+
import { EncryptionSubscriber } from "@sebastienrousseau/crypto-typeorm";
|
|
226
|
+
|
|
227
|
+
const ds = new DataSource({
|
|
228
|
+
subscribers: [
|
|
229
|
+
new EncryptionSubscriber({
|
|
230
|
+
key: process.env.COLUMN_ENCRYPTION_KEY!,
|
|
231
|
+
fields: new Map([
|
|
232
|
+
["User", ["ssn", "email"]],
|
|
233
|
+
["Payment", ["cardNumber"]],
|
|
234
|
+
]),
|
|
235
|
+
}),
|
|
236
|
+
],
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
| Hook | Behaviour |
|
|
241
|
+
| :------------- | :---------------------------------- |
|
|
242
|
+
| `beforeInsert` | Encrypts configured fields in-place |
|
|
243
|
+
| `beforeUpdate` | Encrypts configured fields in-place |
|
|
244
|
+
| `afterLoad` | Decrypts configured fields in-place |
|
|
245
|
+
|
|
246
|
+
`afterLoad` throws `FieldDecryptionError` when a stored value is not
|
|
247
|
+
a valid ciphertext for `EntityName.field`. To load rows that still
|
|
248
|
+
hold plaintext during a gradual migration, set
|
|
249
|
+
`allowPlaintextFallback: true` until every row has been rewritten.
|
|
250
|
+
|
|
251
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Transformer API
|
|
256
|
+
|
|
257
|
+
### `EncryptionTransformer`
|
|
258
|
+
|
|
259
|
+
A standard TypeORM `ValueTransformer` for manual use on any
|
|
260
|
+
`@Column`.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
|
264
|
+
import { EncryptionTransformer } from "@sebastienrousseau/crypto-typeorm";
|
|
265
|
+
|
|
266
|
+
const transformer = new EncryptionTransformer({
|
|
267
|
+
key: process.env.COLUMN_ENCRYPTION_KEY!,
|
|
268
|
+
context: "Secret.value",
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
@Entity()
|
|
272
|
+
class Secret {
|
|
273
|
+
@PrimaryGeneratedColumn()
|
|
274
|
+
id!: number;
|
|
275
|
+
|
|
276
|
+
@Column({ type: "text", transformer })
|
|
277
|
+
value!: string;
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Method | Input | Output |
|
|
282
|
+
| :------------ | :-------------------------- | :-------------------------------------------------------- |
|
|
283
|
+
| `to(value)` | plaintext or `null` | `v2:` Base64 sealed box or `null` |
|
|
284
|
+
| `from(value)` | stored ciphertext or `null` | plaintext string or `null`; throws `FieldDecryptionError` |
|
|
285
|
+
|
|
286
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Examples
|
|
291
|
+
|
|
292
|
+
All examples are self-contained TypeScript files in the `examples/`
|
|
293
|
+
directory. Run any example with:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npx ts-node examples/<name>.ts
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
| Category | Example | Purpose |
|
|
300
|
+
| :---------- | :---------------------------------------- | :------------------------------------------------ |
|
|
301
|
+
| Decorator | [decorator.ts](examples/decorator.ts) | Using `@EncryptedColumn` on entity fields |
|
|
302
|
+
| Subscriber | [subscriber.ts](examples/subscriber.ts) | Centralised encryption via `EncryptionSubscriber` |
|
|
303
|
+
| Transformer | [transformer.ts](examples/transformer.ts) | Manual `ValueTransformer` on `@Column` |
|
|
304
|
+
| Migration | [migration.ts](examples/migration.ts) | Encrypting existing plaintext columns |
|
|
305
|
+
|
|
306
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
307
|
+
|
|
308
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Development
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
pnpm --filter @sebastienrousseau/crypto-typeorm run build
|
|
316
|
+
pnpm --filter @sebastienrousseau/crypto-typeorm run test
|
|
317
|
+
pnpm --filter @sebastienrousseau/crypto-typeorm run lint
|
|
318
|
+
pnpm --filter @sebastienrousseau/crypto-typeorm run format
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
All 18 packages in the Crypto Service workspace maintain a **100% coverage floor** across statements, branches, functions, and lines.
|
|
322
|
+
|
|
323
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Security
|
|
328
|
+
|
|
329
|
+
Report vulnerabilities privately via [GitHub Security Advisories](https://github.com/sebastienrousseau/crypto-service/security/advisories) or according to [`SECURITY.md`](../../SECURITY.md). Never report security issues publicly.
|
|
330
|
+
|
|
331
|
+
Cryptographic operations use the `@noble/*` libraries, Node.js `crypto` and OpenPGP.js. `@noble/post-quantum` has not been independently audited and does not guarantee constant-time execution, and no module in this suite is FIPS 140-3 validated. Key zeroization is limited: JavaScript strings and garbage-collected buffers cannot be reliably wiped. See [`SECURITY.md`](../../SECURITY.md).
|
|
332
|
+
|
|
333
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Documentation
|
|
338
|
+
|
|
339
|
+
- [Full Suite Documentation](https://sebastienrousseau.github.io/crypto-service/)
|
|
340
|
+
- [API Reference (TypeDoc)](https://sebastienrousseau.github.io/crypto-service/)
|
|
341
|
+
- [Developer Guide](../../DEVELOPMENT.md)
|
|
342
|
+
- [Security Policy](../../SECURITY.md)
|
|
343
|
+
- [Architecture & Design](../../ARCHITECTURE.md)
|
|
344
|
+
|
|
345
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Stability guarantees
|
|
350
|
+
|
|
351
|
+
Versions advance strictly one step at a time on the `0.0.x` line (`v0.0.1` → `v0.0.2` → `v0.0.3` ... → `v0.0.999` → `v0.1.0`). Work for every release iteration begins on a dedicated `feat/v<version>` branch.
|
|
352
|
+
|
|
353
|
+
All 18 packages in the workspace move in lockstep. Public API signatures, cipher output formats, and serialization schemas are strictly versioned. Breaking changes to serialized formats or algorithm defaults are considered major breaking changes. Minimum toolchain upgrades (e.g. Node.js LTS floor) are governed by [POLICIES.md](../../docs/POLICIES.md).
|
|
354
|
+
|
|
355
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## License
|
|
360
|
+
|
|
361
|
+
Dual-licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) or [MIT](https://opensource.org/licenses/MIT), at your option.
|
|
362
|
+
|
|
363
|
+
Copyright (c) 2022-2026 Sebastien Rousseau and The Crypto Service Suite contributors.
|
|
364
|
+
|
|
365
|
+
<p align="right"><a href="#contents">Back to Top</a></p>
|
package/dist/codec.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EncryptionConfig } from "./types";
|
|
2
|
+
export declare const CIPHERTEXT_PREFIX = "v2:";
|
|
3
|
+
export interface ColumnCodec {
|
|
4
|
+
readonly legacyKey: string;
|
|
5
|
+
readonly encKey: string;
|
|
6
|
+
readonly allowPlaintextFallback: boolean;
|
|
7
|
+
readonly acceptLegacyCiphertext: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function createColumnCodec(config: EncryptionConfig, owner?: string): ColumnCodec;
|
|
10
|
+
export declare function sealColumn(codec: ColumnCodec, context: string, plaintext: string): string;
|
|
11
|
+
export declare function openColumn(codec: ColumnCodec, context: string, stored: string): string;
|
|
12
|
+
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGhD,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAcvC,MAAM,WAAW,WAAW;IAE1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IAEzC,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;CAC1C;AAYD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,KAAK,SAA0B,GAC9B,WAAW,CAiBb;AAeD,wBAAgB,UAAU,CACxB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,MAAM,CAOR;AA0BD,wBAAgB,UAAU,CACxB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,CAaR"}
|
package/dist/codec.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CIPHERTEXT_PREFIX = void 0;
|
|
4
|
+
exports.createColumnCodec = createColumnCodec;
|
|
5
|
+
exports.sealColumn = sealColumn;
|
|
6
|
+
exports.openColumn = openColumn;
|
|
7
|
+
const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
exports.CIPHERTEXT_PREFIX = "v2:";
|
|
10
|
+
const HEX_KEY_RE = /^[0-9a-fA-F]{64}$/;
|
|
11
|
+
function createColumnCodec(config, owner = "EncryptionTransformer") {
|
|
12
|
+
if (!HEX_KEY_RE.test(config.key)) {
|
|
13
|
+
throw new Error(`${owner}: key must be a 64-character hex string (256 bits)`);
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
legacyKey: config.key,
|
|
17
|
+
encKey: (0, crypto_lib_1.kdfDerive)({
|
|
18
|
+
algorithm: "hkdf-sha256",
|
|
19
|
+
password: Buffer.from(config.key, "hex"),
|
|
20
|
+
salt: new Uint8Array(32),
|
|
21
|
+
params: { info: "crypto-typeorm/enc/v2" },
|
|
22
|
+
}).derivedKey,
|
|
23
|
+
allowPlaintextFallback: config.allowPlaintextFallback === true,
|
|
24
|
+
acceptLegacyCiphertext: config.acceptLegacyCiphertext !== false,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function contextAad(context) {
|
|
28
|
+
return Buffer.from(`crypto-typeorm/v2:${context}`, "utf8");
|
|
29
|
+
}
|
|
30
|
+
function sealColumn(codec, context, plaintext) {
|
|
31
|
+
const { sealed } = crypto_lib_1.secretbox.seal(codec.encKey, plaintext, contextAad(context));
|
|
32
|
+
return exports.CIPHERTEXT_PREFIX + sealed;
|
|
33
|
+
}
|
|
34
|
+
function tryOpen(key, sealed, aad) {
|
|
35
|
+
try {
|
|
36
|
+
return Buffer.from(crypto_lib_1.secretbox.open(key, sealed, aad)).toString("utf8");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function openColumn(codec, context, stored) {
|
|
43
|
+
if (stored.startsWith(exports.CIPHERTEXT_PREFIX)) {
|
|
44
|
+
const body = stored.slice(exports.CIPHERTEXT_PREFIX.length);
|
|
45
|
+
const plain = tryOpen(codec.encKey, body, contextAad(context));
|
|
46
|
+
if (plain === undefined)
|
|
47
|
+
throw new errors_1.FieldDecryptionError(context);
|
|
48
|
+
return plain;
|
|
49
|
+
}
|
|
50
|
+
const legacy = codec.acceptLegacyCiphertext
|
|
51
|
+
? tryOpen(codec.legacyKey, stored)
|
|
52
|
+
: undefined;
|
|
53
|
+
if (legacy !== undefined)
|
|
54
|
+
return legacy;
|
|
55
|
+
if (codec.allowPlaintextFallback)
|
|
56
|
+
return stored;
|
|
57
|
+
throw new errors_1.FieldDecryptionError(context);
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ColumnOptions } from "typeorm";
|
|
2
|
+
import type { EncryptionConfig } from "./types";
|
|
3
|
+
export interface EncryptedColumnOptions extends Omit<ColumnOptions, "transformer"> {
|
|
4
|
+
encrypt?: EncryptionConfig;
|
|
5
|
+
}
|
|
6
|
+
export declare function EncryptedColumn(options?: EncryptedColumnOptions): PropertyDecorator;
|
|
7
|
+
//# sourceMappingURL=decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorator.d.ts","sourceRoot":"","sources":["../src/decorator.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAgBhD,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAClD,aAAa,EACb,aAAa,CACd;IAKC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAgCD,wBAAgB,eAAe,CAC7B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,iBAAiB,CA0BnB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EncryptedColumn = EncryptedColumn;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
5
|
+
const codec_1 = require("./codec");
|
|
6
|
+
const transformer_1 = require("./transformer");
|
|
7
|
+
function EncryptedColumn(options) {
|
|
8
|
+
const { encrypt, ...columnOptions } = options ?? {};
|
|
9
|
+
const key = encrypt?.key ?? process.env.TYPEORM_ENCRYPTION_KEY;
|
|
10
|
+
if (!key) {
|
|
11
|
+
throw new Error("EncryptedColumn: encryption key is required. Provide it via " +
|
|
12
|
+
"options.encrypt.key or set the TYPEORM_ENCRYPTION_KEY env var.");
|
|
13
|
+
}
|
|
14
|
+
const config = { ...encrypt, key };
|
|
15
|
+
(0, codec_1.createColumnCodec)(config, "EncryptedColumn");
|
|
16
|
+
return (target, propertyKey) => {
|
|
17
|
+
const transformer = new transformer_1.EncryptionTransformer({
|
|
18
|
+
...config,
|
|
19
|
+
context: encrypt?.context ?? `${target.constructor.name}.${String(propertyKey)}`,
|
|
20
|
+
});
|
|
21
|
+
(0, typeorm_1.Column)({ type: "text", ...columnOptions, transformer })(target, propertyKey);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=decorator.js.map
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAwBA,qBAAa,oBAAqB,SAAQ,KAAK;IAE7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGb,OAAO,EAAE,MAAM;CAK5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FieldDecryptionError = void 0;
|
|
4
|
+
class FieldDecryptionError extends Error {
|
|
5
|
+
context;
|
|
6
|
+
constructor(context) {
|
|
7
|
+
super(`Cannot decrypt ${context || "column"}: not a valid ciphertext`);
|
|
8
|
+
this.name = "FieldDecryptionError";
|
|
9
|
+
this.context = context;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.FieldDecryptionError = FieldDecryptionError;
|
|
13
|
+
//# sourceMappingURL=errors.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { EncryptedColumn } from "./decorator";
|
|
2
|
+
export type { EncryptedColumnOptions } from "./decorator";
|
|
3
|
+
export { EncryptionSubscriber } from "./subscriber";
|
|
4
|
+
export { EncryptionTransformer } from "./transformer";
|
|
5
|
+
export { FieldDecryptionError } from "./errors";
|
|
6
|
+
export type { EncryptionConfig } from "./types";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAGhD,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FieldDecryptionError = exports.EncryptionTransformer = exports.EncryptionSubscriber = exports.EncryptedColumn = void 0;
|
|
4
|
+
var decorator_1 = require("./decorator");
|
|
5
|
+
Object.defineProperty(exports, "EncryptedColumn", { enumerable: true, get: function () { return decorator_1.EncryptedColumn; } });
|
|
6
|
+
var subscriber_1 = require("./subscriber");
|
|
7
|
+
Object.defineProperty(exports, "EncryptionSubscriber", { enumerable: true, get: function () { return subscriber_1.EncryptionSubscriber; } });
|
|
8
|
+
var transformer_1 = require("./transformer");
|
|
9
|
+
Object.defineProperty(exports, "EncryptionTransformer", { enumerable: true, get: function () { return transformer_1.EncryptionTransformer; } });
|
|
10
|
+
var errors_1 = require("./errors");
|
|
11
|
+
Object.defineProperty(exports, "FieldDecryptionError", { enumerable: true, get: function () { return errors_1.FieldDecryptionError; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EntitySubscriberInterface, InsertEvent, UpdateEvent, LoadEvent } from "typeorm";
|
|
2
|
+
import type { EncryptionConfig } from "./types";
|
|
3
|
+
export declare class EncryptionSubscriber implements EntitySubscriberInterface {
|
|
4
|
+
private readonly codec;
|
|
5
|
+
private readonly fields;
|
|
6
|
+
constructor(config: EncryptionConfig);
|
|
7
|
+
beforeInsert(event: InsertEvent<Record<string, unknown>>): void;
|
|
8
|
+
beforeUpdate(event: UpdateEvent<Record<string, unknown>>): void;
|
|
9
|
+
afterLoad(entity: Record<string, unknown>, event?: LoadEvent<Record<string, unknown>>): void;
|
|
10
|
+
private getEntityName;
|
|
11
|
+
private getFieldsFor;
|
|
12
|
+
private encryptFields;
|
|
13
|
+
private decryptFields;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=subscriber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriber.d.ts","sourceRoot":"","sources":["../src/subscriber.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,yBAAyB,EACzB,WAAW,EACX,WAAW,EACX,SAAS,EACV,MAAM,SAAS,CAAC;AAOjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAmChD,qBAAa,oBAAqB,YAAW,yBAAyB;IAEpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IAEpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAOnC,MAAM,EAAE,gBAAgB;IAWpC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAO/D,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI;IAS/D,SAAS,CACP,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACzC,IAAI;IAQP,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,aAAa;CAQtB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EncryptionSubscriber = void 0;
|
|
4
|
+
const codec_1 = require("./codec");
|
|
5
|
+
class EncryptionSubscriber {
|
|
6
|
+
codec;
|
|
7
|
+
fields;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
if (!config.key) {
|
|
10
|
+
throw new Error("EncryptionSubscriber: key is required");
|
|
11
|
+
}
|
|
12
|
+
this.codec = (0, codec_1.createColumnCodec)(config, "EncryptionSubscriber");
|
|
13
|
+
this.fields = config.fields ?? new Map();
|
|
14
|
+
}
|
|
15
|
+
beforeInsert(event) {
|
|
16
|
+
this.encryptFields(event.entity);
|
|
17
|
+
}
|
|
18
|
+
beforeUpdate(event) {
|
|
19
|
+
if (event.entity) {
|
|
20
|
+
this.encryptFields(event.entity);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
afterLoad(entity, event) {
|
|
24
|
+
void event;
|
|
25
|
+
this.decryptFields(entity);
|
|
26
|
+
}
|
|
27
|
+
getEntityName(entity) {
|
|
28
|
+
const ctor = entity?.constructor;
|
|
29
|
+
return ctor?.name && ctor.name !== "Object" ? ctor.name : undefined;
|
|
30
|
+
}
|
|
31
|
+
getFieldsFor(entity) {
|
|
32
|
+
const name = this.getEntityName(entity);
|
|
33
|
+
if (!name)
|
|
34
|
+
return { name: "", fields: [] };
|
|
35
|
+
return { name, fields: this.fields.get(name) ?? [] };
|
|
36
|
+
}
|
|
37
|
+
encryptFields(entity) {
|
|
38
|
+
const { name, fields } = this.getFieldsFor(entity);
|
|
39
|
+
for (const field of fields) {
|
|
40
|
+
const value = entity[field];
|
|
41
|
+
if (value === null || value === undefined)
|
|
42
|
+
continue;
|
|
43
|
+
const plaintext = typeof value === "string" ? value : JSON.stringify(value);
|
|
44
|
+
entity[field] = (0, codec_1.sealColumn)(this.codec, `${name}.${field}`, plaintext);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
decryptFields(entity) {
|
|
48
|
+
const { name, fields } = this.getFieldsFor(entity);
|
|
49
|
+
for (const field of fields) {
|
|
50
|
+
const value = entity[field];
|
|
51
|
+
if (typeof value !== "string")
|
|
52
|
+
continue;
|
|
53
|
+
entity[field] = (0, codec_1.openColumn)(this.codec, `${name}.${field}`, value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.EncryptionSubscriber = EncryptionSubscriber;
|
|
58
|
+
//# sourceMappingURL=subscriber.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ValueTransformer } from "typeorm";
|
|
2
|
+
import type { EncryptionConfig } from "./types";
|
|
3
|
+
export declare class EncryptionTransformer implements ValueTransformer {
|
|
4
|
+
private readonly codec;
|
|
5
|
+
private readonly context;
|
|
6
|
+
constructor(config: EncryptionConfig);
|
|
7
|
+
to(value: unknown): string | null;
|
|
8
|
+
from(value: unknown): string | null;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=transformer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.d.ts","sourceRoot":"","sources":["../src/transformer.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAOhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA0BhD,qBAAa,qBAAsB,YAAW,gBAAgB;IAE5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IAEpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAOrB,MAAM,EAAE,gBAAgB;IAgBpC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAkBjC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;CASpC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EncryptionTransformer = void 0;
|
|
4
|
+
const codec_1 = require("./codec");
|
|
5
|
+
class EncryptionTransformer {
|
|
6
|
+
codec;
|
|
7
|
+
context;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
if (!config.key) {
|
|
10
|
+
throw new Error("EncryptionTransformer: key is required");
|
|
11
|
+
}
|
|
12
|
+
this.codec = (0, codec_1.createColumnCodec)(config);
|
|
13
|
+
this.context = config.context ?? "";
|
|
14
|
+
}
|
|
15
|
+
to(value) {
|
|
16
|
+
if (value === null || value === undefined) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const plaintext = typeof value === "string" ? value : JSON.stringify(value);
|
|
20
|
+
return (0, codec_1.sealColumn)(this.codec, this.context, plaintext);
|
|
21
|
+
}
|
|
22
|
+
from(value) {
|
|
23
|
+
if (value === null || value === undefined) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (typeof value !== "string") {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return (0, codec_1.openColumn)(this.codec, this.context, value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.EncryptionTransformer = EncryptionTransformer;
|
|
33
|
+
//# sourceMappingURL=transformer.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,gBAAgB;IAM/B,GAAG,EAAE,MAAM,CAAC;IAQZ,SAAS,CAAC,EAAE,MAAM,CAAC;IAenB,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAa/B,OAAO,CAAC,EAAE,MAAM,CAAC;IAUjB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IASjC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC"}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sebastienrousseau/crypto-typeorm",
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "TypeORM column-level encryption with a single decorator, powered by crypto-lib.",
|
|
5
|
+
"author": "Sebastien Rousseau <sebastienrousseau@users.noreply.github.com>",
|
|
6
|
+
"license": "MIT OR Apache-2.0",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.0.0"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"require": "./dist/index.js",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/**/*.js",
|
|
22
|
+
"dist/**/*.d.ts",
|
|
23
|
+
"dist/**/*.d.ts.map"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@sebastienrousseau/crypto-lib": "0.0.7"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"typeorm": "^1.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@sebastienrousseau/mocha-config": "^0.0.5",
|
|
33
|
+
"@types/chai": "^4.3.7",
|
|
34
|
+
"@types/chai-as-promised": "^8.0.2",
|
|
35
|
+
"@types/mocha": "^10.0.2",
|
|
36
|
+
"@types/node": "^26.4.1",
|
|
37
|
+
"c8": "^12.0.0",
|
|
38
|
+
"chai": "^4.5.0",
|
|
39
|
+
"chai-as-promised": "^7.1.2",
|
|
40
|
+
"mocha": "^10.8.2",
|
|
41
|
+
"rimraf": "^6.1.3",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typedoc": "^0.28.20",
|
|
44
|
+
"typeorm": "^1.1.0",
|
|
45
|
+
"typescript": "~5.9.3"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"typeorm",
|
|
49
|
+
"encryption",
|
|
50
|
+
"column-encryption",
|
|
51
|
+
"crypto",
|
|
52
|
+
"decorator",
|
|
53
|
+
"transformer",
|
|
54
|
+
"subscriber",
|
|
55
|
+
"xchacha20-poly1305",
|
|
56
|
+
"secretbox"
|
|
57
|
+
],
|
|
58
|
+
"homepage": "https://github.com/sebastienrousseau/crypto-service/tree/main/packages/crypto-typeorm",
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
},
|
|
62
|
+
"private": false,
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsc --build tsconfig.json",
|
|
65
|
+
"clean": "rimraf ./coverage ./dist ./docs",
|
|
66
|
+
"test": "c8 --check-coverage --lines 100 --branches 100 --functions 100 --reporter=lcov --reporter=text mocha --config=.mocharc.cjs",
|
|
67
|
+
"docs": "typedoc",
|
|
68
|
+
"lint": "eslint --ext .ts src __tests__",
|
|
69
|
+
"lint:fix": "eslint --cache --fix --ext .ts src __tests__",
|
|
70
|
+
"format": "prettier --write src/**/*.ts",
|
|
71
|
+
"format:check": "prettier --check \"src/**/*.ts\""
|
|
72
|
+
}
|
|
73
|
+
}
|