@neetru/pgbouncer-arithmetic 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/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # @neetru/coredb-pgbouncer-arithmetic
2
+
3
+ Aritmética pura de **dimensionamento do pool do PgBouncer** para as VMs
4
+ Postgres densas do Neetru Core DB. Função pura, determinística,
5
+ **zero dependências de runtime**.
6
+
7
+ ## O problema — o teto de conexões (P0-4)
8
+
9
+ Uma VM Postgres densa hospeda **N bancos logicos** atrás de um único PgBouncer.
10
+ O `max_connections` do Postgres é um **teto rígido**: se o servidor recebe mais
11
+ conexões que isso, elas começam a **falhar**.
12
+
13
+ Se os tamanhos de pool do PgBouncer são cravados na mão, o problema é silencioso
14
+ até crescer: o 1º, 2º, 3º produto cabem; o **5º–10º produto satura o teto** e a
15
+ partir daí qualquer conexão nova falha — para todos os bancos da VM, não só o
16
+ último.
17
+
18
+ Este pacote remove o número mágico. Ele **deriva** o dimensionamento do
19
+ PgBouncer a partir de duas grandezas reais — `max_connections` da VM e a
20
+ **contagem viva de bancos** — de modo que a soma das conexões reais ao Postgres
21
+ nunca estoure o teto.
22
+
23
+ ## API
24
+
25
+ ```ts
26
+ import {
27
+ computePoolSizing,
28
+ assertConnectionInvariant,
29
+ type PoolSizing,
30
+ } from '@neetru/coredb-pgbouncer-arithmetic';
31
+ ```
32
+
33
+ ### `computePoolSizing(nBancos, maxConnections?): PoolSizing`
34
+
35
+ Dimensiona o pool do PgBouncer para uma VM.
36
+
37
+ ```ts
38
+ computePoolSizing(4); // 4 bancos, max_connections default 100
39
+ // { defaultPoolSize: 23, reservePoolSize: 5, maxClientConn: 500 }
40
+
41
+ computePoolSizing(10, 100); // 10 bancos
42
+ // { defaultPoolSize: 9, reservePoolSize: 2, maxClientConn: 500 }
43
+ ```
44
+
45
+ | Campo | Significado |
46
+ |---|---|
47
+ | `defaultPoolSize` | `default_pool_size` — conexões de **servidor** por banco. |
48
+ | `reservePoolSize` | `reserve_pool_size` — folga de burst (¼ do `defaultPoolSize`). |
49
+ | `maxClientConn` | `max_client_conn` — conexões de **cliente** aceitas. Fixo em `500`. |
50
+
51
+ A derivação:
52
+
53
+ - `RESERVED_CONNECTIONS = 8` — superusuário + monitoring + sondas do agente,
54
+ tiradas do teto antes do rateio.
55
+ - `usable = max(maxConnections - 8, nBancos)` — conexões disponíveis para os
56
+ pools dos produtos; nunca menos de 1 por banco.
57
+ - `defaultPoolSize = max(2, floor(usable / nBancos))` — o `floor` garante a
58
+ **propriedade central de segurança**:
59
+
60
+ ```
61
+ nBancos × defaultPoolSize ≤ usable
62
+ ```
63
+
64
+ ou seja, a soma das conexões reais ao Postgres nunca estoura o teto. O
65
+ `max(2, …)` é um **floor de viabilidade** — um pool de tamanho 1 é inútil.
66
+ - `reservePoolSize = max(1, floor(defaultPoolSize / 4))`.
67
+ - `maxClientConn = 500`.
68
+
69
+ **Sanitização (fail-safe):**
70
+
71
+ - `maxConnections` — não-finito / `< 1` / ausente → cai para `100`.
72
+ - `nBancos` — coagido a inteiro positivo (`floor`); NaN / `Infinity` / `≤ 0`
73
+ caem para `1`. O chamador **deve** passar a contagem viva real — o fallback
74
+ para 1 é a última linha de defesa contra lixo, não algo em que se apoiar.
75
+
76
+ **Edge de over-densidade:** quando há bancos demais numa VM pequena,
77
+ `floor(usable / nBancos)` pode dar 0 ou 1 e o **floor-de-2** entra. Nessa
78
+ região `nBancos × 2` *pode* exceder `usable` — a propriedade de segurança é
79
+ rompida. Isso não é um bug do cálculo: é o **sinal de uma VM genuinamente
80
+ over-densa**. `computePoolSizing` ainda devolve `defaultPoolSize: 2` (o mínimo
81
+ viável — um pool menor não funciona), mas o valor **não finge ser seguro**
82
+ nessa região. A correção é operacional: hospedar menos bancos ou usar uma VM
83
+ maior (mais `max_connections`). O chamador que opera VMs densas deve cruzar
84
+ `nBancos × 2 ≤ usable` e alertar. `computePoolSizing` **não lança** — é
85
+ aritmética pura.
86
+
87
+ ### `assertConnectionInvariant({ sdkPoolMax, maxInstances, defaultPoolSize }): void`
88
+
89
+ Protege a invariante crítica de **autoscaling**:
90
+
91
+ ```
92
+ sdkPoolMax × maxInstances ≤ defaultPoolSize
93
+ ```
94
+
95
+ Cada instância do Cloud Run roda um `pg.Pool` com até `sdkPoolMax` conexões.
96
+ Sob autoscaling até `maxInstances` instâncias sobem ao mesmo tempo. O total que
97
+ o produto pode demandar do PgBouncer é `sdkPoolMax × maxInstances` — e isso não
98
+ pode exceder o `default_pool_size`, ou o produto satura o pool e as conexões
99
+ falham.
100
+
101
+ ```ts
102
+ assertConnectionInvariant({ sdkPoolMax: 2, maxInstances: 4, defaultPoolSize: 8 });
103
+ // ok — 2 × 4 = 8 ≤ 8
104
+
105
+ assertConnectionInvariant({ sdkPoolMax: 2, maxInstances: 5, defaultPoolSize: 8 });
106
+ // throws — 2 × 5 = 10 > 8
107
+ ```
108
+
109
+ **Fail-closed:** qualquer um dos três valores que não seja inteiro positivo
110
+ finito **lança** — uma entrada lixo nunca passa silenciosa. Se a invariante for
111
+ violada, o erro nomeia os três números e diz o que fazer: reduzir `pg.Pool.max`,
112
+ reduzir o `maxInstances` do Cloud Run, ou aumentar o `default_pool_size`
113
+ hospedando menos bancos / usando uma VM maior.
114
+
115
+ ## Estado
116
+
117
+ Implementado (M0 — P0-4). Suíte vitest cobrindo a derivação default, a
118
+ propriedade de segurança no regime normal, a região do floor-de-2, sanitização
119
+ de ambos os argumentos, determinismo e a invariante de autoscaling fail-closed.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @neetru/coredb-pgbouncer-arithmetic
3
+ *
4
+ * Aritmetica pura de dimensionamento do pool do PgBouncer. Uma VM Postgres
5
+ * densa hospeda N bancos logicos atras de um unico PgBouncer; `max_connections`
6
+ * e um teto rigido. Este pacote deriva o dimensionamento do PgBouncer a partir
7
+ * de `max_connections` e da contagem viva de bancos, e protege a invariante
8
+ * critica de autoscaling `sdkPoolMax * maxInstances <= defaultPoolSize`.
9
+ *
10
+ * Funcao pura, deterministica, zero dependencias de runtime. Fecha o teto de
11
+ * conexoes (P0-4).
12
+ */
13
+ export { computePoolSizing, assertConnectionInvariant } from './sizing.js';
14
+ export type { PoolSizing } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @neetru/coredb-pgbouncer-arithmetic
3
+ *
4
+ * Aritmetica pura de dimensionamento do pool do PgBouncer. Uma VM Postgres
5
+ * densa hospeda N bancos logicos atras de um unico PgBouncer; `max_connections`
6
+ * e um teto rigido. Este pacote deriva o dimensionamento do PgBouncer a partir
7
+ * de `max_connections` e da contagem viva de bancos, e protege a invariante
8
+ * critica de autoscaling `sdkPoolMax * maxInstances <= defaultPoolSize`.
9
+ *
10
+ * Funcao pura, deterministica, zero dependencias de runtime. Fecha o teto de
11
+ * conexoes (P0-4).
12
+ */
13
+ export { computePoolSizing, assertConnectionInvariant } from './sizing.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Aritmetica de dimensionamento do pool do PgBouncer.
3
+ *
4
+ * Uma VM Postgres densa hospeda N bancos logicos atras de um unico PgBouncer.
5
+ * `max_connections` do Postgres e um teto rigido. Se os tamanhos de pool sao
6
+ * cravados na mao, o 5o-10o produto satura o teto e conexoes comecam a falhar.
7
+ * Este modulo deriva o dimensionamento do PgBouncer a partir de
8
+ * `max_connections` e da contagem viva de bancos, e protege a invariante
9
+ * critica de autoscaling.
10
+ */
11
+ import type { PoolSizing } from './types.js';
12
+ /**
13
+ * Dimensiona o pool do PgBouncer para uma VM Postgres densa.
14
+ *
15
+ * @param nBancos Contagem viva real de bancos logicos na VM. Precisa
16
+ * ser um inteiro 1..10000; lixo LANCA (nao ha fallback —
17
+ * cair para 1 seria fail-open e poderia estourar o teto).
18
+ * @param maxConnections `max_connections` do Postgres da VM. Default 100 se
19
+ * omitido; lixo (NaN/Infinity/<1) LANCA; fracionario
20
+ * sofre `Math.floor`.
21
+ * @returns Dimensionamento puro e deterministico do pool.
22
+ * @throws {Error} se `nBancos` ou `maxConnections` forem invalidos.
23
+ */
24
+ export declare function computePoolSizing(nBancos: number, maxConnections?: number): PoolSizing;
25
+ /**
26
+ * Protege a invariante critica de autoscaling:
27
+ *
28
+ * sdkPoolMax * maxInstances <= defaultPoolSize
29
+ *
30
+ * Cada instancia do Cloud Run roda um `pg.Pool` com ate `sdkPoolMax` conexoes.
31
+ * Sob autoscaling ate `maxInstances` instancias podem subir ao mesmo tempo. O
32
+ * total de conexoes que o produto pode demandar do PgBouncer e
33
+ * `sdkPoolMax * maxInstances`, e isso NAO pode exceder `default_pool_size`, ou
34
+ * o produto satura o pool e conexoes comecam a falhar.
35
+ *
36
+ * FAIL-CLOSED: qualquer um dos tres valores que nao seja inteiro positivo
37
+ * finito faz LANCAR — uma entrada lixo nunca passa silenciosa.
38
+ *
39
+ * @throws {Error} se algum input for invalido, ou se a invariante for violada.
40
+ */
41
+ export declare function assertConnectionInvariant(input: {
42
+ sdkPoolMax: number;
43
+ maxInstances: number;
44
+ defaultPoolSize: number;
45
+ }): void;
package/dist/sizing.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Aritmetica de dimensionamento do pool do PgBouncer.
3
+ *
4
+ * Uma VM Postgres densa hospeda N bancos logicos atras de um unico PgBouncer.
5
+ * `max_connections` do Postgres e um teto rigido. Se os tamanhos de pool sao
6
+ * cravados na mao, o 5o-10o produto satura o teto e conexoes comecam a falhar.
7
+ * Este modulo deriva o dimensionamento do PgBouncer a partir de
8
+ * `max_connections` e da contagem viva de bancos, e protege a invariante
9
+ * critica de autoscaling.
10
+ */
11
+ /**
12
+ * Conexoes que NUNCA pertencem aos pools dos produtos: superusuario,
13
+ * monitoring, sondas (probes) do agente. Subtraidas do teto antes do rateio.
14
+ */
15
+ const RESERVED_CONNECTIONS = 8;
16
+ /**
17
+ * Teto fixo de conexoes de CLIENTE que o PgBouncer aceita. Conexoes de cliente
18
+ * sao baratas (nao consomem conexao de servidor enquanto ociosas no pooling
19
+ * em modo transaction), por isso o valor e generoso e constante.
20
+ */
21
+ const MAX_CLIENT_CONN = 500;
22
+ /** Default de `max_connections` quando o chamador nao informa um valor. */
23
+ const DEFAULT_MAX_CONNECTIONS = 100;
24
+ /**
25
+ * Teto sano para a contagem de bancos numa VM. Acima disso a entrada e
26
+ * quase certamente lixo (nenhuma VM Postgres hospeda 10 mil bancos logicos
27
+ * atras de um unico PgBouncer) — melhor falhar alto do que dimensionar.
28
+ */
29
+ const MAX_N_BANCOS = 10000;
30
+ /**
31
+ * Valida e saneia `maxConnections`.
32
+ *
33
+ * - `undefined` → default 100.
34
+ * - Numero finito >= 1 → aceito. Fracionario sofre `Math.floor` (um
35
+ * `max_connections` fracionario nao faz sentido; truncar e inofensivo e
36
+ * leniente). Um inteiro pequeno legitimo (ex.: 20) e aceito — e um Postgres
37
+ * pequeno, nao lixo.
38
+ * - Qualquer outra coisa (NaN, Infinity, negativo, zero, nao-numero) →
39
+ * LANCA. Uma config de Postgres lixo nunca passa silenciosa (fail-loud).
40
+ *
41
+ * @throws {Error} se `maxConnections` for fornecido e nao for finito ou < 1.
42
+ */
43
+ function sanitizeMaxConnections(maxConnections) {
44
+ if (maxConnections === undefined) {
45
+ return DEFAULT_MAX_CONNECTIONS;
46
+ }
47
+ if (typeof maxConnections !== 'number' || !Number.isFinite(maxConnections) || maxConnections < 1) {
48
+ throw new Error(`computePoolSizing: maxConnections invalido — precisa ser um numero finito >= 1 ` +
49
+ `(o max_connections real do Postgres), recebeu ${String(maxConnections)}.`);
50
+ }
51
+ return Math.floor(maxConnections);
52
+ }
53
+ /**
54
+ * Valida `nBancos`. NAO ha fallback: dimensionar um pool de conexao sem a
55
+ * contagem real de bancos e fail-OPEN — cair para 1 produz o MAIOR pool por
56
+ * banco e, se a VM hospeda muitos bancos, o total estoura o `max_connections`
57
+ * do Postgres. Por isso uma contagem lixo LANCA.
58
+ *
59
+ * `nBancos` precisa ser um inteiro finito no intervalo `1 <= nBancos <= 10000`.
60
+ *
61
+ * @throws {Error} se `nBancos` nao for um inteiro positivo no intervalo sano.
62
+ */
63
+ function validateNBancos(nBancos) {
64
+ if (typeof nBancos !== 'number' ||
65
+ !Number.isInteger(nBancos) ||
66
+ nBancos < 1 ||
67
+ nBancos > MAX_N_BANCOS) {
68
+ throw new Error(`computePoolSizing: nBancos invalido — precisa ser um inteiro 1..${MAX_N_BANCOS} ` +
69
+ `(a contagem real de bancos na VM); nao da pra dimensionar um pool sem a ` +
70
+ `contagem real, recebeu ${String(nBancos)}.`);
71
+ }
72
+ return nBancos;
73
+ }
74
+ /**
75
+ * Dimensiona o pool do PgBouncer para uma VM Postgres densa.
76
+ *
77
+ * @param nBancos Contagem viva real de bancos logicos na VM. Precisa
78
+ * ser um inteiro 1..10000; lixo LANCA (nao ha fallback —
79
+ * cair para 1 seria fail-open e poderia estourar o teto).
80
+ * @param maxConnections `max_connections` do Postgres da VM. Default 100 se
81
+ * omitido; lixo (NaN/Infinity/<1) LANCA; fracionario
82
+ * sofre `Math.floor`.
83
+ * @returns Dimensionamento puro e deterministico do pool.
84
+ * @throws {Error} se `nBancos` ou `maxConnections` forem invalidos.
85
+ */
86
+ export function computePoolSizing(nBancos, maxConnections) {
87
+ const safeMaxConn = sanitizeMaxConnections(maxConnections);
88
+ const safeNBancos = validateNBancos(nBancos);
89
+ // `usable` = conexoes disponiveis para os pools dos produtos, depois de
90
+ // tirar as reservadas. O `Math.max` com `safeNBancos` garante ao menos 1
91
+ // conexao por banco mesmo numa VM minuscula (floor(usable/nBancos) >= 1).
92
+ const usable = Math.max(safeMaxConn - RESERVED_CONNECTIONS, safeNBancos);
93
+ // `floor(usable / nBancos)` garante a propriedade central de seguranca:
94
+ // nBancos * defaultPoolSize <= usable
95
+ // ou seja, a soma das conexoes reais ao Postgres NUNCA estoura o teto.
96
+ //
97
+ // EDGE DE OVER-DENSIDADE: o `Math.max(2, ...)` impoe um floor de viabilidade
98
+ // — um pool com tamanho 1 e inutil. Quando ha bancos demais numa VM pequena,
99
+ // `floor(usable/nBancos)` pode dar 0 ou 1 e o floor-de-2 entra. Nesse caso
100
+ // `nBancos * 2` PODE exceder `usable` — a propriedade de seguranca e
101
+ // ROMPIDA. Isso NAO e silenciado por capricho: e o sinal de uma VM
102
+ // genuinamente over-densa. A correcao real e operacional — hospedar menos
103
+ // bancos ou subir para uma VM maior (mais `max_connections`). `computePoolSizing`
104
+ // ainda devolve `defaultPoolSize: 2` (o minimo viavel) porque um pool menor
105
+ // que 2 nao funciona; mas o valor NAO finge ser seguro nessa regiao. O
106
+ // chamador que opera VMs densas deve cruzar `nBancos * 2 <= usable` e alertar.
107
+ const defaultPoolSize = Math.max(2, Math.floor(usable / safeNBancos));
108
+ // Folga de burst — um quarto do pool base, no minimo 1.
109
+ const reservePoolSize = Math.max(1, Math.floor(defaultPoolSize / 4));
110
+ return {
111
+ defaultPoolSize,
112
+ reservePoolSize,
113
+ maxClientConn: MAX_CLIENT_CONN,
114
+ };
115
+ }
116
+ /** Verdadeiro sse `n` e um inteiro finito estritamente positivo. */
117
+ function isPositiveInteger(n) {
118
+ return typeof n === 'number' && Number.isInteger(n) && n > 0;
119
+ }
120
+ /**
121
+ * Protege a invariante critica de autoscaling:
122
+ *
123
+ * sdkPoolMax * maxInstances <= defaultPoolSize
124
+ *
125
+ * Cada instancia do Cloud Run roda um `pg.Pool` com ate `sdkPoolMax` conexoes.
126
+ * Sob autoscaling ate `maxInstances` instancias podem subir ao mesmo tempo. O
127
+ * total de conexoes que o produto pode demandar do PgBouncer e
128
+ * `sdkPoolMax * maxInstances`, e isso NAO pode exceder `default_pool_size`, ou
129
+ * o produto satura o pool e conexoes comecam a falhar.
130
+ *
131
+ * FAIL-CLOSED: qualquer um dos tres valores que nao seja inteiro positivo
132
+ * finito faz LANCAR — uma entrada lixo nunca passa silenciosa.
133
+ *
134
+ * @throws {Error} se algum input for invalido, ou se a invariante for violada.
135
+ */
136
+ export function assertConnectionInvariant(input) {
137
+ const { sdkPoolMax, maxInstances, defaultPoolSize } = input;
138
+ if (!isPositiveInteger(sdkPoolMax)) {
139
+ throw new Error(`assertConnectionInvariant: sdkPoolMax deve ser um inteiro positivo, recebeu ${String(sdkPoolMax)}.`);
140
+ }
141
+ if (!isPositiveInteger(maxInstances)) {
142
+ throw new Error(`assertConnectionInvariant: maxInstances deve ser um inteiro positivo, recebeu ${String(maxInstances)}.`);
143
+ }
144
+ if (!isPositiveInteger(defaultPoolSize)) {
145
+ throw new Error(`assertConnectionInvariant: defaultPoolSize deve ser um inteiro positivo, recebeu ${String(defaultPoolSize)}.`);
146
+ }
147
+ const demanded = sdkPoolMax * maxInstances;
148
+ if (demanded > defaultPoolSize) {
149
+ throw new Error(`Invariante de conexao violada: sdkPoolMax (${sdkPoolMax}) x maxInstances ` +
150
+ `(${maxInstances}) = ${demanded}, que excede defaultPoolSize (${defaultPoolSize}). ` +
151
+ `Sob autoscaling esse produto satura o pool do PgBouncer e conexoes vao ` +
152
+ `falhar. Corrija de uma destas formas: reduza pg.Pool.max (sdkPoolMax), ` +
153
+ `reduza o maxInstances do Cloud Run, ou aumente o default_pool_size ` +
154
+ `hospedando menos bancos na VM / usando uma VM maior (mais max_connections).`);
155
+ }
156
+ }
157
+ //# sourceMappingURL=sizing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sizing.js","sourceRoot":"","sources":["../src/sizing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tipos canonicos do `@neetru/coredb-pgbouncer-arithmetic`.
3
+ */
4
+ /**
5
+ * Dimensionamento do pool do PgBouncer derivado do `max_connections` do
6
+ * Postgres e da contagem viva de bancos da VM.
7
+ */
8
+ export interface PoolSizing {
9
+ /**
10
+ * `default_pool_size` do PgBouncer — quantas conexoes de servidor o pool
11
+ * abre por banco.
12
+ *
13
+ * No regime normal vale a propriedade de seguranca
14
+ * `nBancos * defaultPoolSize <= usable` — a soma das conexoes reais ao
15
+ * Postgres nao estoura o teto.
16
+ *
17
+ * EXCECAO — VM over-densa: quando `floor(usable/nBancos) < 2`, entra o floor
18
+ * de viabilidade `defaultPoolSize: 2` (um pool menor que 2 nao funciona).
19
+ * Nessa regiao `nBancos * 2` PODE exceder `usable` — o `2` e um valor de
20
+ * viabilidade MINIMA, NAO um valor seguro. E o sinal de que a VM tem bancos
21
+ * demais: a correcao e operacional (hospedar menos bancos ou subir
22
+ * `max_connections`).
23
+ */
24
+ defaultPoolSize: number;
25
+ /**
26
+ * `reserve_pool_size` do PgBouncer — folga de burst acima do
27
+ * `default_pool_size`. Derivado como um quarto do `defaultPoolSize`.
28
+ */
29
+ reservePoolSize: number;
30
+ /**
31
+ * `max_client_conn` do PgBouncer — quantas conexoes de CLIENTE o PgBouncer
32
+ * aceita. E barato (conexao de cliente nao consome conexao de servidor) e
33
+ * fixo em 500.
34
+ */
35
+ maxClientConn: number;
36
+ }
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tipos canonicos do `@neetru/coredb-pgbouncer-arithmetic`.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@neetru/pgbouncer-arithmetic",
3
+ "version": "0.1.0",
4
+ "description": "Dimensionamento aritmetico do pool do PgBouncer derivado do max_connections do Postgres e da contagem viva de bancos. Garante que a VM Postgres densa nunca estoure o teto de conexoes sob autoscaling. Funcao pura, zero dependencias.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "test": "vitest run"
21
+ },
22
+ "dependencies": {
23
+
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "^5.6.0",
27
+ "vitest": "^2.1.0"
28
+ },
29
+ "engines": {
30
+ "node": "\u003e=20"
31
+ }
32
+ }