emilsoftware-utilities 2.0.0-dev.77 → 2.0.0-dev.78
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/dist/accessi/resource-server/token.js +2 -0
- package/dist/accessi/resource-server/token.js.map +1 -1
- package/dist/accessi/server/standalone.js +2 -1
- package/dist/accessi/server/standalone.js.map +1 -1
- package/dist/accessi-module/AccessiModule.d.ts +3 -0
- package/dist/accessi-module/AccessiModule.js.map +1 -1
- package/dist/accessi-module/Console/console.js +1 -0
- package/dist/accessi-module/Console/wiki.js +1 -0
- package/dist/accessi-module/Console/wiki.js.map +1 -1
- package/dist/accessi-module/Controllers/AccessiIntrospectionController.js +9 -0
- package/dist/accessi-module/Controllers/AccessiIntrospectionController.js.map +1 -1
- package/dist/accessi-module/Controllers/AuthController.js +67 -8
- package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
- package/dist/accessi-module/Controllers/ConsoleEntryController.js +9 -0
- package/dist/accessi-module/Controllers/ConsoleEntryController.js.map +1 -1
- package/dist/accessi-module/Services/AuthService/AuthService.js +8 -2
- package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
- package/dist/accessi-module/index.d.ts +4 -0
- package/dist/accessi-module/index.js +15 -1
- package/dist/accessi-module/index.js.map +1 -1
- package/dist/accessi-module/observability/metrics.d.ts +79 -0
- package/dist/accessi-module/observability/metrics.js +216 -0
- package/dist/accessi-module/observability/metrics.js.map +1 -0
- package/dist/accessi-module/observability/redisMetrics.d.ts +12 -0
- package/dist/accessi-module/observability/redisMetrics.js +44 -0
- package/dist/accessi-module/observability/redisMetrics.js.map +1 -0
- package/dist/accessi-module/security/accessTokenRevocation.js +5 -3
- package/dist/accessi-module/security/accessTokenRevocation.js.map +1 -1
- package/dist/accessi-module/security/consoleEntry.js +5 -3
- package/dist/accessi-module/security/consoleEntry.js.map +1 -1
- package/dist/accessi-module/security/edgeRateLimit.js +3 -0
- package/dist/accessi-module/security/edgeRateLimit.js.map +1 -1
- package/dist/accessi-module/security/jwks.js +3 -0
- package/dist/accessi-module/security/jwks.js.map +1 -1
- package/dist/accessi-module/security/jwtTokenOptions.js +2 -0
- package/dist/accessi-module/security/jwtTokenOptions.js.map +1 -1
- package/dist/accessi-module/security/publicAuthRateLimit.js +3 -0
- package/dist/accessi-module/security/publicAuthRateLimit.js.map +1 -1
- package/dist/accessi-module/security/rateLimitStore.js +4 -2
- package/dist/accessi-module/security/rateLimitStore.js.map +1 -1
- package/dist/accessi-module/security/refreshTokenRotation.js +7 -5
- package/dist/accessi-module/security/refreshTokenRotation.js.map +1 -1
- package/dist/accessi-module/security/serviceTokenRateLimit.js +3 -0
- package/dist/accessi-module/security/serviceTokenRateLimit.js.map +1 -1
- package/dist/accessi-module/security/sessionRevocation.js +7 -3
- package/dist/accessi-module/security/sessionRevocation.js.map +1 -1
- package/dist/emilsoftware-utilities-2.0.0-dev.78.tgz +0 -0
- package/docs/metrics.md +153 -0
- package/docs/redis-load-testing.md +88 -0
- package/package.json +4 -3
- package/dist/emilsoftware-utilities-2.0.0-dev.77.tgz +0 -0
package/docs/metrics.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Metriche e alert
|
|
2
|
+
|
|
3
|
+
Accessi espone metriche di dominio in formato Prometheus. Il servizio standalone
|
|
4
|
+
le include in `/health/metrics` insieme alle metriche HTTP di base; un host
|
|
5
|
+
Nest/Express può montare `renderAccessiMetrics()` su una propria rotta protetta.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { renderAccessiMetrics } from 'emilsoftware-utilities/accessi/server';
|
|
9
|
+
|
|
10
|
+
app.get('/internal/accessi-metrics', (_req, res) => {
|
|
11
|
+
res.type('text/plain; version=0.0.4').send(renderAccessiMetrics());
|
|
12
|
+
});
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Le metriche sono attive per default. Per disattivarle:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const accessiOptions: AccessiOptions = {
|
|
19
|
+
// ...
|
|
20
|
+
metrics: { enabled: false },
|
|
21
|
+
};
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Per inviarle a OpenTelemetry, StatsD o un altro sistema:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const accessiOptions: AccessiOptions = {
|
|
28
|
+
metrics: {
|
|
29
|
+
prefix: 'accessi',
|
|
30
|
+
sink: {
|
|
31
|
+
increment: (name, value, labels) => meter.createCounter(name).add(value, labels),
|
|
32
|
+
observe: (name, valueMs, labels) => histogram.record(valueMs, labels),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`prefix` (default `accessi`) si applica alle metriche emesse dai controller; gli
|
|
39
|
+
store Redis usano il registry globale, sempre renderizzabile con
|
|
40
|
+
`renderAccessiMetrics()`.
|
|
41
|
+
|
|
42
|
+
## Metriche di dominio
|
|
43
|
+
|
|
44
|
+
| Metrica | Tipo | Label | Significato |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| `accessi_auth_login_total` | counter | `result` = `success`, `failure`, `challenge`, `password_required`, `password_expired`, `sso_only`, `rate_limited`, `error` | esito del login |
|
|
47
|
+
| `accessi_auth_login_duration_ms` | histogram | — | durata del login |
|
|
48
|
+
| `accessi_auth_two_factor_total` | counter | `action` = `verify`/`resend`, `result` | verifica e reinvio 2FA |
|
|
49
|
+
| `accessi_auth_two_factor_duration_ms` | histogram | `action` | durata verifica 2FA |
|
|
50
|
+
| `accessi_auth_refresh_total` | counter | `result` = `success`, `invalid`, `reuse`, `family_revoked`, `store_unavailable`, `rate_limited` | esito del refresh |
|
|
51
|
+
| `accessi_auth_refresh_duration_ms` | histogram | — | durata del refresh |
|
|
52
|
+
| `accessi_refresh_reuse_total` | counter | `user_sessions_revoked` | riuso di refresh token oltre la grace |
|
|
53
|
+
| `accessi_refresh_family_revoked_total` | counter | `reason` | famiglie revocate |
|
|
54
|
+
| `accessi_rate_limit_blocked_total` | counter | `scope`, `layer` = `public`/`edge`/`service_token` | richieste bloccate dal rate limit |
|
|
55
|
+
| `accessi_introspection_total` | counter | `result` = `active`, `inactive`, `bad_request`, `error` | introspection service-to-service |
|
|
56
|
+
| `accessi_introspection_duration_ms` | histogram | — | durata introspection |
|
|
57
|
+
| `accessi_jwks_requests_total` | counter | `result` = `ok`, `unavailable` | richieste JWKS |
|
|
58
|
+
| `accessi_jwt_unknown_key_id_total` | counter | `purpose`, `source`, `algorithm` | token con `kid` non riconosciuto |
|
|
59
|
+
| `accessi_sessions_revoked_total` | counter | `reason` = `manual`, `password_change`, `disabled`, `deleted`, `refresh_token_reuse`, ... | revoche sessioni |
|
|
60
|
+
| `accessi_console_ticket_total` | counter | `action` = `create`/`exchange`, `result` | ticket di ingresso console |
|
|
61
|
+
| `accessi_store_errors_total` | counter | `store` | errori degli store Redis |
|
|
62
|
+
| `accessi_store_operation_duration_ms` | histogram | `store` | latenza degli script Lua Redis |
|
|
63
|
+
|
|
64
|
+
Gli store strumentati sono `rate_limit`, `edge_rate_limit`, `service_token_rate_limit`,
|
|
65
|
+
`console_entry`, `session_revocation`, `access_token_revocation` e
|
|
66
|
+
`refresh_token_rotation`.
|
|
67
|
+
|
|
68
|
+
## Alert suggeriti
|
|
69
|
+
|
|
70
|
+
Esempio di regole Prometheus. Le soglie vanno tarate sul traffico reale.
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
groups:
|
|
74
|
+
- name: accessi
|
|
75
|
+
rules:
|
|
76
|
+
- alert: AccessiLoginErrorRate
|
|
77
|
+
expr: |
|
|
78
|
+
sum(rate(accessi_auth_login_total{result=~"failure|error"}[5m]))
|
|
79
|
+
/
|
|
80
|
+
clamp_min(sum(rate(accessi_auth_login_total[5m])), 0.001)
|
|
81
|
+
> 0.3
|
|
82
|
+
for: 10m
|
|
83
|
+
labels:
|
|
84
|
+
severity: warning
|
|
85
|
+
annotations:
|
|
86
|
+
summary: "Oltre il 30% dei login fallisce da 10 minuti"
|
|
87
|
+
|
|
88
|
+
- alert: AccessiRefreshReuse
|
|
89
|
+
expr: increase(accessi_refresh_reuse_total[5m]) > 0
|
|
90
|
+
for: 1m
|
|
91
|
+
labels:
|
|
92
|
+
severity: critical
|
|
93
|
+
annotations:
|
|
94
|
+
summary: "Riuso di refresh token rilevato: verificare possibili furti"
|
|
95
|
+
|
|
96
|
+
- alert: AccessiRateLimitSpike
|
|
97
|
+
expr: sum(rate(accessi_rate_limit_blocked_total[5m])) > 20
|
|
98
|
+
for: 10m
|
|
99
|
+
labels:
|
|
100
|
+
severity: warning
|
|
101
|
+
annotations:
|
|
102
|
+
summary: "Molte richieste bloccate dal rate limit: possibile brute force"
|
|
103
|
+
|
|
104
|
+
- alert: AccessiUnknownJwtKid
|
|
105
|
+
expr: increase(accessi_jwt_unknown_key_id_total[5m]) > 0
|
|
106
|
+
for: 5m
|
|
107
|
+
labels:
|
|
108
|
+
severity: warning
|
|
109
|
+
annotations:
|
|
110
|
+
summary: "Token con kid sconosciuto: rotazione chiavi incompleta?"
|
|
111
|
+
|
|
112
|
+
- alert: AccessiRedisStoreDown
|
|
113
|
+
expr: increase(accessi_store_errors_total[5m]) > 3
|
|
114
|
+
for: 5m
|
|
115
|
+
labels:
|
|
116
|
+
severity: critical
|
|
117
|
+
annotations:
|
|
118
|
+
summary: "Errori Redis: revoca, rotazione o rate limit non affidabili"
|
|
119
|
+
|
|
120
|
+
- alert: AccessiStoreLatency
|
|
121
|
+
expr: |
|
|
122
|
+
histogram_quantile(
|
|
123
|
+
0.95,
|
|
124
|
+
sum by (le, store) (rate(accessi_store_operation_duration_ms_bucket[5m]))
|
|
125
|
+
) > 50
|
|
126
|
+
for: 10m
|
|
127
|
+
labels:
|
|
128
|
+
severity: warning
|
|
129
|
+
annotations:
|
|
130
|
+
summary: "p95 degli script Redis sopra 50 ms per {{ $labels.store }}"
|
|
131
|
+
|
|
132
|
+
- alert: AccessiIntrospectionErrors
|
|
133
|
+
expr: |
|
|
134
|
+
sum(rate(accessi_introspection_total{result="error"}[5m]))
|
|
135
|
+
/
|
|
136
|
+
clamp_min(sum(rate(accessi_introspection_total[5m])), 0.001)
|
|
137
|
+
> 0.05
|
|
138
|
+
for: 10m
|
|
139
|
+
labels:
|
|
140
|
+
severity: warning
|
|
141
|
+
annotations:
|
|
142
|
+
summary: "Errori di introspection oltre il 5%"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Note
|
|
146
|
+
|
|
147
|
+
- Non usare label ad alta cardinalità (email, `jti`, IP): le metriche Accessi
|
|
148
|
+
non le includono.
|
|
149
|
+
- `accessi_jwt_unknown_key_id_total` collega la rotazione chiavi descritta in
|
|
150
|
+
`docs/jwt-key-rotation.md`: un aumento dopo un deploy segnala che un
|
|
151
|
+
consumatore non ha aggiornato la JWKS o la chiave statica.
|
|
152
|
+
- Le metriche non sostituiscono l'audit: per gli eventi di sicurezza usare
|
|
153
|
+
`auditAccessi` e la sua persistenza.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Test di concorrenza e carico su Redis
|
|
2
|
+
|
|
3
|
+
`npm run test:load:redis` verifica che rate limit, ticket console e refresh
|
|
4
|
+
rotation restino corretti con più istanze Accessi sullo stesso Redis, e produce
|
|
5
|
+
un report di throughput e latenza.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
ACCESSI_TEST_REDIS_URL=redis://127.0.0.1:6379 npm run test:load:redis
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
La suite è opt-in: senza `ACCESSI_TEST_REDIS_URL` i test vengono saltati. In CI
|
|
12
|
+
si imposta `ACCESSI_TEST_REQUIRE_REDIS=1` per far fallire il job se Redis non è
|
|
13
|
+
raggiungibile. Usare un database Redis dedicato (es. `redis://127.0.0.1:6379/5`):
|
|
14
|
+
tutte le chiavi hanno un prefisso per processo e vengono cancellate a fine suite,
|
|
15
|
+
ma un database separato evita sorprese con Redis condivisi.
|
|
16
|
+
|
|
17
|
+
## Scenari
|
|
18
|
+
|
|
19
|
+
| Scenario | Cosa dimostra |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| Burst login/reset/2FA | il limite globale per IP è rispettato anche se le richieste sono distribuite su due store (due istanze) |
|
|
22
|
+
| Ticket console | due istanze consumano lo stesso ticket: una sola ottiene il record, l'altra riceve `null` |
|
|
23
|
+
| Refresh rotation | due istanze consumano lo stesso `jti`: una sola vince; la revoca di famiglia è visibile all'altra istanza |
|
|
24
|
+
| Misure | throughput, p50, p95 e massimo per ogni scenario |
|
|
25
|
+
|
|
26
|
+
Le "due istanze" sono simulate con due connessioni Redis indipendenti e due
|
|
27
|
+
oggetti store distinti. La correttezza dipende dagli script Lua atomici degli
|
|
28
|
+
store: il test non testa il processo Node in sé, ma il comportamento condiviso
|
|
29
|
+
su Redis.
|
|
30
|
+
|
|
31
|
+
## Parametri
|
|
32
|
+
|
|
33
|
+
| Variabile | Default | Note |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `ACCESSI_TEST_REDIS_URL` | assente | abilita la suite |
|
|
36
|
+
| `ACCESSI_TEST_REQUIRE_REDIS` | `0` | `1` trasforma lo skip in errore (CI) |
|
|
37
|
+
| `ACCESSI_TEST_LOAD_ITERATIONS` | `200` | operazioni per refresh rotation |
|
|
38
|
+
| `ACCESSI_TEST_LOAD_CONCURRENCY` | `50` | richieste concorrenti |
|
|
39
|
+
| `ACCESSI_TEST_REPORT_FILE` | assente | path del report Markdown |
|
|
40
|
+
|
|
41
|
+
Esempio con carico maggiore:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
ACCESSI_TEST_REDIS_URL=redis://127.0.0.1:6379/5 \
|
|
45
|
+
ACCESSI_TEST_LOAD_ITERATIONS=1000 \
|
|
46
|
+
ACCESSI_TEST_LOAD_CONCURRENCY=100 \
|
|
47
|
+
ACCESSI_TEST_REPORT_FILE=redis-load-report.md \
|
|
48
|
+
npm run test:load:redis
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Report
|
|
52
|
+
|
|
53
|
+
Il report viene sempre stampato a fine suite:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
| Scenario | Operazioni | Errori | Throughput (ops/s) | p50 (ms) | p95 (ms) | max (ms) |
|
|
57
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
58
|
+
| rate limit login | 60 | 0 | 3200.0 | 12.10 | 24.30 | 41.00 |
|
|
59
|
+
| console ticket (2 istanze) | 100 | 0 | 2800.0 | 14.00 | 30.10 | 55.00 |
|
|
60
|
+
| refresh rotation (2 istanze) | 200 | 0 | 3000.0 | 13.00 | 27.00 | 48.00 |
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Con `ACCESSI_TEST_REPORT_FILE` viene scritto anche su file; la CI lo pubblica
|
|
64
|
+
come artifact `redis-load-report`.
|
|
65
|
+
|
|
66
|
+
Le asserzioni verificano correttezza e un limite di sicurezza sulla latenza
|
|
67
|
+
(p95 sotto 1–2 secondi), non un throughput minimo: il throughput dipende
|
|
68
|
+
dall'hardware del runner e non deve rendere la CI instabile. Per una valutazione
|
|
69
|
+
di capacità usare un ambiente dedicato e confrontare i numeri tra release.
|
|
70
|
+
|
|
71
|
+
## Interpretazione
|
|
72
|
+
|
|
73
|
+
- **Errori > 0**: Redis irraggiungibile o script Lua in errore; il test fallisce.
|
|
74
|
+
- **p95 molto alto con throughput basso**: rete verso Redis o Redis saturo;
|
|
75
|
+
controllare `latency monitor` lato Redis e il pool di connessioni.
|
|
76
|
+
- **Più di un consumo riuscito**: violazione dell'atomicità; non distribuire la
|
|
77
|
+
release.
|
|
78
|
+
- **Limite per IP superato**: due istanze non stanno condividendo lo store o
|
|
79
|
+
stanno usando prefissi diversi.
|
|
80
|
+
|
|
81
|
+
## Limiti
|
|
82
|
+
|
|
83
|
+
- Non misura il costo del database Firebird né il flusso HTTP completo: per
|
|
84
|
+
quello usare i test di integrazione Firebird e un ambiente di staging.
|
|
85
|
+
- I tempi includono il client Redis di test (RESP2 minimale) e il round-trip di
|
|
86
|
+
rete: sono un limite superiore, non un benchmark dell'applicazione.
|
|
87
|
+
- Non usare questo test contro un Redis di produzione: scrive e cancella chiavi
|
|
88
|
+
con prefisso, ma è comunque carico reale.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emilsoftware-utilities",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.78",
|
|
4
4
|
"description": "Utilities for EmilSoftware",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -139,12 +139,13 @@
|
|
|
139
139
|
"typecheck": "tsc --noEmit",
|
|
140
140
|
"lint": "node scripts/lint.cjs",
|
|
141
141
|
"test": "npm run typecheck && npm run lint && npm run test:unit",
|
|
142
|
-
"test:unit": "node --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs test/security-headers.cjs test/auth-cookies.cjs",
|
|
143
|
-
"test:coverage": "node --experimental-test-coverage --test-coverage-lines=50 --test-coverage-functions=60 --test-coverage-branches=60 --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs test/security-headers.cjs test/auth-cookies.cjs",
|
|
142
|
+
"test:unit": "node --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs test/security-headers.cjs test/auth-cookies.cjs test/metrics.cjs",
|
|
143
|
+
"test:coverage": "node --experimental-test-coverage --test-coverage-lines=50 --test-coverage-functions=60 --test-coverage-branches=60 --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs test/security-headers.cjs test/auth-cookies.cjs test/metrics.cjs",
|
|
144
144
|
"test:accessi": "npm run test:unit",
|
|
145
145
|
"test:all": "npm test && npm run test:integration && npm run test:integration:redis",
|
|
146
146
|
"test:integration": "node test/integration/run-firebird.cjs 5",
|
|
147
147
|
"test:integration:redis": "node --test test/integration/redis-stores.integration.cjs",
|
|
148
|
+
"test:load:redis": "node --test --test-concurrency=1 test/integration/redis-load.integration.cjs",
|
|
148
149
|
"test:integration:fb5": "node test/integration/run-firebird.cjs 5",
|
|
149
150
|
"test:integration:fb3": "node test/integration/run-firebird.cjs 3",
|
|
150
151
|
"test:integration:fb25": "node test/integration/run-firebird.cjs 2.5",
|
|
Binary file
|