emilsoftware-utilities 2.0.0-dev.70 → 2.0.0-dev.72

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.
Files changed (39) hide show
  1. package/README.MD +30 -0
  2. package/dist/accessi/client/admin/index.d.ts +18 -0
  3. package/dist/accessi/client/admin/index.js +58 -0
  4. package/dist/accessi/client/admin/index.js.map +1 -0
  5. package/dist/accessi/client/createAccessiClient.d.ts +21 -0
  6. package/dist/accessi/client/createAccessiClient.js +31 -0
  7. package/dist/accessi/client/createAccessiClient.js.map +1 -0
  8. package/dist/accessi/client/generated/admin.d.ts +601 -0
  9. package/dist/accessi/client/generated/admin.js +557 -0
  10. package/dist/accessi/client/generated/admin.js.map +1 -0
  11. package/dist/accessi/client/generated/public.d.ts +121 -0
  12. package/dist/accessi/client/generated/public.js +119 -0
  13. package/dist/accessi/client/generated/public.js.map +1 -0
  14. package/dist/accessi/client/generated/service.d.ts +53 -0
  15. package/dist/accessi/client/generated/service.js +63 -0
  16. package/dist/accessi/client/generated/service.js.map +1 -0
  17. package/dist/accessi/client/index.d.ts +29 -11
  18. package/dist/accessi/client/index.js +52 -2
  19. package/dist/accessi/client/index.js.map +1 -1
  20. package/dist/accessi/client/public/index.d.ts +18 -0
  21. package/dist/accessi/client/public/index.js +58 -0
  22. package/dist/accessi/client/public/index.js.map +1 -0
  23. package/dist/accessi/client/service/index.d.ts +17 -0
  24. package/dist/accessi/client/service/index.js +57 -0
  25. package/dist/accessi/client/service/index.js.map +1 -0
  26. package/dist/accessi-module/Console/accessiFetch.d.ts +27 -0
  27. package/dist/accessi-module/Console/accessiFetch.js +41 -5
  28. package/dist/accessi-module/Console/accessiFetch.js.map +1 -1
  29. package/dist/accessi-module/Console/console.js +14 -3
  30. package/dist/accessi-module/Console/wiki.js +10 -1
  31. package/dist/accessi-module/Console/wiki.js.map +1 -1
  32. package/dist/accessi-module/Controllers/AccessiIntrospectionController.js +1 -1
  33. package/dist/accessi-module/Controllers/AccessiIntrospectionController.js.map +1 -1
  34. package/dist/accessi-module/security/accessTokenRevocation.js +5 -2
  35. package/dist/accessi-module/security/accessTokenRevocation.js.map +1 -1
  36. package/dist/emilsoftware-utilities-2.0.0-dev.72.tgz +0 -0
  37. package/integration-prompt.md +6 -2
  38. package/package.json +23 -4
  39. package/dist/emilsoftware-utilities-2.0.0-dev.70.tgz +0 -0
package/README.MD CHANGED
@@ -82,6 +82,7 @@ temi light/dark e i token Bootstrap 5.3.
82
82
  | `npm run test:coverage` | Test unitari con report e **soglie minime** di copertura |
83
83
  | `npm run test:integration` | Integration test su Firebird reale (default: porta 3055) |
84
84
  | `npm run test:integration:fb5` / `:fb3` / `:fb25` | Integration test su Firebird 5 / 3 / 2.5 |
85
+ | `npm run test:integration:redis` | Integration test degli store Redis (opt-in, salta senza `ACCESSI_TEST_REDIS_URL`) |
85
86
  | `npm run test:accessi-console` | Test end-to-end della console (richiede un browser Chromium e la build) |
86
87
 
87
88
  ### Integration test su Firebird reale
@@ -115,6 +116,33 @@ La CI (`.github/workflows/ci.yml`) esegue typecheck, test con coverage e build,
115
116
  integration test in matrice su Firebird 5 e 3. Il workflow di release (`publish.yml`) esegue
116
117
  typecheck e test prima di build e pubblicazione.
117
118
 
119
+ ### Integration test su Redis reale
120
+
121
+ Gli store condivisi (rate limit, ticket console, revoca sessioni, revoca `jti`, rotazione refresh
122
+ token) hanno adapter Redis che nei test unitari girano su fake in memoria. La suite di integrazione
123
+ li verifica contro un Redis reale, inclusi i `consume` atomici in concorrenza e gli `healthCheck()`.
124
+
125
+ E' **opt-in**: senza la variabile `ACCESSI_TEST_REDIS_URL` i test vengono marcati come `skip`.
126
+
127
+ ```bash
128
+ docker run --rm -d --name accessi-test-redis -p 6380:6379 redis:7-alpine
129
+ npm run test:integration:redis
130
+ docker stop accessi-test-redis
131
+ ```
132
+
133
+ Su PowerShell:
134
+
135
+ ```powershell
136
+ $env:ACCESSI_TEST_REDIS_URL = 'redis://127.0.0.1:6380'
137
+ npm run test:integration:redis
138
+ ```
139
+
140
+ Variabili: `ACCESSI_TEST_REDIS_URL` (formato `redis://[:password@]host:port[/db]`) e
141
+ `ACCESSI_TEST_REQUIRE_REDIS=1` per fallire invece di saltare quando Redis non e raggiungibile
142
+ (usato dalla CI). I test usano chiavi con prefisso per processo e cancellano solo quelle, quindi
143
+ possono girare anche su un Redis condiviso. `docker-compose.test.yml` resta dedicato solo a
144
+ Firebird: la CI avvia Redis come service del job `Redis stores`.
145
+
118
146
  ## Firebird: drop-in di `es-node-firebird`
119
147
 
120
148
  La libreria **ri-esporta l'intera API di `node-firebird`** (`export * from "node-firebird"` piu il
@@ -197,6 +225,8 @@ lista utenti apre un pannello in cui inserire l'URL dell'applicazione e generare
197
225
  | Backend Express esistente | `await initializeAccessiModule(app, accessiOptions)` monta le rotte `/api/accessi` su un host Nest isolato |
198
226
  | Applicazione NestJS | importa `AccessiModule.forRoot(accessiOptions)` (e/o `AllegatiModule.forRoot(allegatiOptions)`) |
199
227
  | Accessi + Allegati insieme | `EmilsoftwareModule.forRoot({ accessiOptions, allegatiOptions })` |
228
+ | Client multi-istanza / multi-tenant | `createAccessiClient({ baseUrl, getToken })` isola base URL e token per istanza (concorrenza sicura); `configureAccessiClient` resta il default globale |
229
+ | Client per scope | `accessi/client/public` (frontend), `accessi/client/admin` (tool admin), `accessi/client/service` (backend fratelli): stesse tipi del client completo, nessuna operazione fuori scope |
200
230
 
201
231
  Tutta la configurazione e riservata al backend: segreti JWT/SMTP/DB non devono mai raggiungere il browser.
202
232
 
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Client per le API amministrative di Accessi: utenti, ruoli, menu, scope,
3
+ * filtri, configurazione console, provider federati e schema.
4
+ *
5
+ * Richiede un token con privilegi admin. Per login e reset password usare
6
+ * `accessi/client/public`.
7
+ */
8
+ import * as generated from '../generated/admin';
9
+ import type { AccessiClientConfig } from '../../../accessi-module/Console/accessiFetch';
10
+ import { type AccessiScopedClient } from '../createAccessiClient';
11
+ export * from '../generated/admin';
12
+ export * from '../../../accessi-module/Console/generated/model';
13
+ export { AccessiRequestError } from '../../../accessi-module/Console/accessiFetch';
14
+ export type { AccessiClientConfig } from '../../../accessi-module/Console/accessiFetch';
15
+ /** Client admin con configurazione isolata per istanza. */
16
+ export type AccessiAdminClient = AccessiScopedClient<typeof generated>;
17
+ /** Crea un client delle sole API amministrative, con base URL e token propri. */
18
+ export declare function createAccessiAdminClient(config?: AccessiClientConfig): AccessiAdminClient;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.AccessiRequestError = void 0;
40
+ exports.createAccessiAdminClient = createAccessiAdminClient;
41
+ /**
42
+ * Client per le API amministrative di Accessi: utenti, ruoli, menu, scope,
43
+ * filtri, configurazione console, provider federati e schema.
44
+ *
45
+ * Richiede un token con privilegi admin. Per login e reset password usare
46
+ * `accessi/client/public`.
47
+ */
48
+ const generated = __importStar(require("../generated/admin"));
49
+ const createAccessiClient_1 = require("../createAccessiClient");
50
+ __exportStar(require("../generated/admin"), exports);
51
+ __exportStar(require("../../../accessi-module/Console/generated/model"), exports);
52
+ var accessiFetch_1 = require("../../../accessi-module/Console/accessiFetch");
53
+ Object.defineProperty(exports, "AccessiRequestError", { enumerable: true, get: function () { return accessiFetch_1.AccessiRequestError; } });
54
+ /** Crea un client delle sole API amministrative, con base URL e token propri. */
55
+ function createAccessiAdminClient(config = {}) {
56
+ return (0, createAccessiClient_1.createAccessiClientFromModule)(generated, config);
57
+ }
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/accessi/client/admin/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,4DAIC;AA3BD;;;;;;GAMG;AACH,8DAAgD;AAEhD,gEAGgC;AAEhC,qDAAmC;AACnC,kFAAgE;AAChE,6EAAmF;AAA1E,mHAAA,mBAAmB,OAAA;AAM5B,iFAAiF;AACjF,SAAgB,wBAAwB,CACtC,SAA8B,EAAE;IAEhC,OAAO,IAAA,mDAA6B,EAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["/**\n * Client per le API amministrative di Accessi: utenti, ruoli, menu, scope,\n * filtri, configurazione console, provider federati e schema.\n *\n * Richiede un token con privilegi admin. Per login e reset password usare\n * `accessi/client/public`.\n */\nimport * as generated from '../generated/admin';\nimport type { AccessiClientConfig } from '../../../accessi-module/Console/accessiFetch';\nimport {\n createAccessiClientFromModule,\n type AccessiScopedClient,\n} from '../createAccessiClient';\n\nexport * from '../generated/admin';\nexport * from '../../../accessi-module/Console/generated/model';\nexport { AccessiRequestError } from '../../../accessi-module/Console/accessiFetch';\nexport type { AccessiClientConfig } from '../../../accessi-module/Console/accessiFetch';\n\n/** Client admin con configurazione isolata per istanza. */\nexport type AccessiAdminClient = AccessiScopedClient<typeof generated>;\n\n/** Crea un client delle sole API amministrative, con base URL e token propri. */\nexport function createAccessiAdminClient(\n config: AccessiClientConfig = {},\n): AccessiAdminClient {\n return createAccessiClientFromModule(generated, config);\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { type AccessiClientConfig } from '../../accessi-module/Console/accessiFetch';
2
+ /**
3
+ * Client generato con configurazione isolata per istanza.
4
+ *
5
+ * `TModule` e il modulo generato da Orval per lo scope: il client espone solo
6
+ * le operazioni di quello scope (pubblico, admin o service token).
7
+ */
8
+ export type AccessiScopedClient<TModule extends object> = TModule & {
9
+ /** Sostituisce la configurazione dell'istanza senza toccare quella globale. */
10
+ configure(config: AccessiClientConfig): void;
11
+ /** Copia della configurazione corrente dell'istanza. */
12
+ getConfig(): AccessiClientConfig;
13
+ };
14
+ /**
15
+ * Avvolge un modulo generato da Orval con una configurazione per istanza.
16
+ *
17
+ * La configurazione viene applicata alla singola chiamata tramite
18
+ * `runWithAccessiClient`, quindi istanze diverse possono chiamare in concorrenza
19
+ * senza sovrascriversi. Le funzioni in `TModule` sono le sole esposte.
20
+ */
21
+ export declare function createAccessiClientFromModule<TModule extends object>(api: TModule, config?: AccessiClientConfig): AccessiScopedClient<TModule>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAccessiClientFromModule = createAccessiClientFromModule;
4
+ const accessiFetch_1 = require("../../accessi-module/Console/accessiFetch");
5
+ /**
6
+ * Avvolge un modulo generato da Orval con una configurazione per istanza.
7
+ *
8
+ * La configurazione viene applicata alla singola chiamata tramite
9
+ * `runWithAccessiClient`, quindi istanze diverse possono chiamare in concorrenza
10
+ * senza sovrascriversi. Le funzioni in `TModule` sono le sole esposte.
11
+ */
12
+ function createAccessiClientFromModule(api, config = {}) {
13
+ let current = Object.assign({}, config);
14
+ return new Proxy(api, {
15
+ get(target, property, receiver) {
16
+ if (property === 'configure') {
17
+ return (next) => {
18
+ current = Object.assign({}, next);
19
+ };
20
+ }
21
+ if (property === 'getConfig') {
22
+ return () => (Object.assign({}, current));
23
+ }
24
+ const value = Reflect.get(target, property, receiver);
25
+ if (typeof value !== 'function')
26
+ return value;
27
+ return (...args) => (0, accessiFetch_1.runWithAccessiClient)(current, () => value(...args));
28
+ },
29
+ });
30
+ }
31
+ //# sourceMappingURL=createAccessiClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createAccessiClient.js","sourceRoot":"","sources":["../../../src/accessi/client/createAccessiClient.ts"],"names":[],"mappings":";;AAyBA,sEAwBC;AAjDD,4EAGmD;AAenD;;;;;;GAMG;AACH,SAAgB,6BAA6B,CAC3C,GAAY,EACZ,SAA8B,EAAE;IAEhC,IAAI,OAAO,qBAA6B,MAAM,CAAE,CAAC;IAEjD,OAAO,IAAI,KAAK,CAAC,GAAmC,EAAE;QACpD,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC5B,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAC7B,OAAO,CAAC,IAAyB,EAAE,EAAE;oBACnC,OAAO,qBAAQ,IAAI,CAAE,CAAC;gBACxB,CAAC,CAAC;YACJ,CAAC;YACD,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAC7B,OAAO,GAAG,EAAE,CAAC,mBAAM,OAAO,EAAG,CAAC;YAChC,CAAC;YAED,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAY,CAAC;YACjE,IAAI,OAAO,KAAK,KAAK,UAAU;gBAAE,OAAO,KAAK,CAAC;YAE9C,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE,CAC5B,IAAA,mCAAoB,EAAC,OAAO,EAAE,GAAG,EAAE,CAAE,KAA6C,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACjG,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n runWithAccessiClient,\n type AccessiClientConfig,\n} from '../../accessi-module/Console/accessiFetch';\n\n/**\n * Client generato con configurazione isolata per istanza.\n *\n * `TModule` e il modulo generato da Orval per lo scope: il client espone solo\n * le operazioni di quello scope (pubblico, admin o service token).\n */\nexport type AccessiScopedClient<TModule extends object> = TModule & {\n /** Sostituisce la configurazione dell'istanza senza toccare quella globale. */\n configure(config: AccessiClientConfig): void;\n /** Copia della configurazione corrente dell'istanza. */\n getConfig(): AccessiClientConfig;\n};\n\n/**\n * Avvolge un modulo generato da Orval con una configurazione per istanza.\n *\n * La configurazione viene applicata alla singola chiamata tramite\n * `runWithAccessiClient`, quindi istanze diverse possono chiamare in concorrenza\n * senza sovrascriversi. Le funzioni in `TModule` sono le sole esposte.\n */\nexport function createAccessiClientFromModule<TModule extends object>(\n api: TModule,\n config: AccessiClientConfig = {},\n): AccessiScopedClient<TModule> {\n let current: AccessiClientConfig = { ...config };\n\n return new Proxy(api as AccessiScopedClient<TModule>, {\n get(target, property, receiver) {\n if (property === 'configure') {\n return (next: AccessiClientConfig) => {\n current = { ...next };\n };\n }\n if (property === 'getConfig') {\n return () => ({ ...current });\n }\n\n const value = Reflect.get(target, property, receiver) as unknown;\n if (typeof value !== 'function') return value;\n\n return (...args: unknown[]) =>\n runWithAccessiClient(current, () => (value as (...callArgs: unknown[]) => unknown)(...args));\n },\n });\n}\n"]}