@generazioneai/events 0.0.1 → 0.1.1
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/CHANGELOG.md +27 -0
- package/README.md +106 -1
- package/dist/connection.d.ts +23 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +113 -0
- package/dist/connection.js.map +1 -0
- package/dist/consumers.service.d.ts +54 -0
- package/dist/consumers.service.d.ts.map +1 -0
- package/dist/consumers.service.js +346 -0
- package/dist/consumers.service.js.map +1 -0
- package/dist/decorators.d.ts +12 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +14 -0
- package/dist/decorators.js.map +1 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.module.d.ts +20 -0
- package/dist/events.module.d.ts.map +1 -0
- package/dist/events.module.js +98 -0
- package/dist/events.module.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics.d.ts +26 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +92 -0
- package/dist/metrics.js.map +1 -0
- package/dist/outbox.service.d.ts +24 -0
- package/dist/outbox.service.d.ts.map +1 -0
- package/dist/outbox.service.js +88 -0
- package/dist/outbox.service.js.map +1 -0
- package/dist/prisma-resolver.d.ts +11 -0
- package/dist/prisma-resolver.d.ts.map +1 -0
- package/dist/prisma-resolver.js +46 -0
- package/dist/prisma-resolver.js.map +1 -0
- package/dist/relay.service.d.ts +32 -0
- package/dist/relay.service.d.ts.map +1 -0
- package/dist/relay.service.js +153 -0
- package/dist/relay.service.js.map +1 -0
- package/dist/tokens.d.ts +4 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +7 -0
- package/dist/tokens.js.map +1 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -0
- package/dist/uuid.d.ts +7 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +32 -0
- package/dist/uuid.js.map +1 -0
- package/package.json +66 -4
- package/prisma/events.prisma +30 -0
- package/sql/001-events-outbox-inbox-rientro.sql +3 -0
- package/sql/001-events-outbox-inbox.sql +28 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** Busta di ogni evento (versione 1). Il corpo del messaggio NATS è questo oggetto in JSON. */
|
|
2
|
+
export interface EventEnvelope<T = unknown> {
|
|
3
|
+
v: 1;
|
|
4
|
+
/** UUID v7, uguale a `Nats-Msg-Id`. */
|
|
5
|
+
id: string;
|
|
6
|
+
subject: string;
|
|
7
|
+
aggregateType: string;
|
|
8
|
+
aggregateId: string;
|
|
9
|
+
aggregateVersion: number;
|
|
10
|
+
/** ISO 8601 UTC del momento in cui l'evento è stato scritto nell'outbox. */
|
|
11
|
+
occurredAt: string;
|
|
12
|
+
/** Azienda di riferimento (anche in `Gen-Tenant`). */
|
|
13
|
+
juridicalId?: string;
|
|
14
|
+
/** Solo id e stati, mai dati personali. */
|
|
15
|
+
data: T;
|
|
16
|
+
}
|
|
17
|
+
/** Servizi che pubblicano o consumano eventi (contratto del treno 3+4). */
|
|
18
|
+
export type EventsService = 'id' | 'hr' | 'train' | 'ops' | 'notify' | 'ai' | 'sign' | 'bill' | 'audit';
|
|
19
|
+
/** `EVENTS_MODE`: off = niente; outbox = si scrive l'outbox ma non si pubblica né si consuma; on = tutto. */
|
|
20
|
+
export type EventsMode = 'off' | 'outbox' | 'on';
|
|
21
|
+
/** `EVENTS_SIGNATURE`: off = nessuna firma; shadow = firma e verifica senza rifiutare; enforce = rifiuta le firme assenti o errate. */
|
|
22
|
+
export type EventsSignatureMode = 'off' | 'shadow' | 'enforce';
|
|
23
|
+
/** Il pezzo di Prisma che serve alla libreria dentro una transazione (il `tx` di `$transaction(async tx => …)`). */
|
|
24
|
+
export interface PrismaTx {
|
|
25
|
+
$executeRawUnsafe(query: string, ...values: any[]): Promise<number>;
|
|
26
|
+
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): Promise<T>;
|
|
27
|
+
}
|
|
28
|
+
/** Il pezzo di `PrismaService` che serve alla libreria. Un client di transazione NON ha `$transaction`: così si riconosce. */
|
|
29
|
+
export interface PrismaLike extends PrismaTx {
|
|
30
|
+
$transaction<R>(fn: (tx: PrismaTx) => Promise<R>, options?: {
|
|
31
|
+
timeout?: number;
|
|
32
|
+
maxWait?: number;
|
|
33
|
+
}): Promise<R>;
|
|
34
|
+
}
|
|
35
|
+
export interface SignParams {
|
|
36
|
+
eventId: string;
|
|
37
|
+
subject: string;
|
|
38
|
+
juridicalId?: string | null;
|
|
39
|
+
}
|
|
40
|
+
/** Produce il JWT di `Gen-Sig` (audience `events`). Con authz 0.7: `(p) => signEventToken(ctx, { eventId: p.eventId, subject: p.subject })`. */
|
|
41
|
+
export type EventSigner = (params: SignParams) => string | Promise<string>;
|
|
42
|
+
export interface VerifyParams {
|
|
43
|
+
eventId: string;
|
|
44
|
+
subject: string;
|
|
45
|
+
mode: Exclude<EventsSignatureMode, 'off'>;
|
|
46
|
+
}
|
|
47
|
+
/** Verifica `Gen-Sig`; lancia se non valida. Con authz 0.7: `(t, p) => verifyEventToken(t, p)`. `token` è `undefined` se manca. */
|
|
48
|
+
export type EventVerifier = (token: string | undefined, params: VerifyParams) => void | Promise<void>;
|
|
49
|
+
export interface OnEventOptions {
|
|
50
|
+
/** Nome del consumer durevole: `<servizio>.<gestore>` (es. `hr.employment-changed`). Unico nel cluster. */
|
|
51
|
+
consumer: string;
|
|
52
|
+
/** Messaggi senza ack tollerati dal consumer (default 100). */
|
|
53
|
+
maxAckPending?: number;
|
|
54
|
+
}
|
|
55
|
+
export type EventHandler<T = any> = (evt: EventEnvelope<T>, tx: PrismaTx) => Promise<void>;
|
|
56
|
+
export interface EnqueueParams<T = unknown> {
|
|
57
|
+
/** `evt.<servizio>.<entità>.<fatto>` al passato. */
|
|
58
|
+
subject: string;
|
|
59
|
+
aggregate: {
|
|
60
|
+
type: string;
|
|
61
|
+
id: string;
|
|
62
|
+
version: number;
|
|
63
|
+
};
|
|
64
|
+
juridicalId?: string | null;
|
|
65
|
+
/** Diventa `data` della busta. Solo id e stati. */
|
|
66
|
+
data: T;
|
|
67
|
+
}
|
|
68
|
+
export interface EventsModuleOptions {
|
|
69
|
+
service: EventsService;
|
|
70
|
+
/** Default `EVT_<SERVICE>`. */
|
|
71
|
+
stream?: string;
|
|
72
|
+
/** La classe (o il token) di `PrismaService` da risolvere dal contenitore di Nest, oppure direttamente l'istanza. */
|
|
73
|
+
prisma: unknown;
|
|
74
|
+
/** Default: `EVENTS_MODE` o `off`. */
|
|
75
|
+
mode?: EventsMode;
|
|
76
|
+
/** Default: `EVENTS_SIGNATURE` o `off`. */
|
|
77
|
+
signatureMode?: EventsSignatureMode;
|
|
78
|
+
signer?: EventSigner;
|
|
79
|
+
verifier?: EventVerifier;
|
|
80
|
+
/** Default: `EVENTS_NATS_URL` o `NATS_URL`. */
|
|
81
|
+
natsUrl?: string;
|
|
82
|
+
/** Opzioni di connessione aggiuntive (`@nats-io/transport-node`); di serie `NATS_NKEY_SEED` → autenticazione nkey. */
|
|
83
|
+
connection?: Record<string, unknown>;
|
|
84
|
+
/** Se creare lo stream del servizio all'avvio (default true). */
|
|
85
|
+
createStream?: boolean;
|
|
86
|
+
/** Repliche dello stream (default 3; 1 in sviluppo). Env `EVENTS_STREAM_REPLICAS`. */
|
|
87
|
+
streamReplicas?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Tetto in byte dello stream (default 5 GiB). JetStream lo **riserva** sul `max_file_store` del server alla creazione: con un NATS
|
|
90
|
+
* piccolo (sviluppo, staging, CI) due stream da 5 GiB possono non starci e l'avvio degli eventi resta a riprovare. Env `EVENTS_STREAM_MAX_BYTES`.
|
|
91
|
+
*/
|
|
92
|
+
streamMaxBytes?: number;
|
|
93
|
+
/** Attese fra le consegne: default 10s, 1m, 5m, 30m, 2h, 6h, 12h (8 consegne). */
|
|
94
|
+
retryScheduleMs?: number[];
|
|
95
|
+
/** Intervallo del relay (default 500 ms). */
|
|
96
|
+
relayIntervalMs?: number;
|
|
97
|
+
/** Durata di ogni attesa del consumer prima di ricontrollare lo stop (default 5000 ms, minimo 1000 imposto da JetStream). */
|
|
98
|
+
pollMs?: number;
|
|
99
|
+
/** Righe dell'outbox per giro (default 100). */
|
|
100
|
+
relayBatchSize?: number;
|
|
101
|
+
/** Giorni di conservazione delle righe pubblicate (default 7) e dell'inbox (default 30). */
|
|
102
|
+
outboxRetentionDays?: number;
|
|
103
|
+
inboxRetentionDays?: number;
|
|
104
|
+
/** Per leggere il `traceparent` corrente al momento di `enqueue` (lo fornisce `@generazioneai/telemetry`). */
|
|
105
|
+
traceparent?: () => string | undefined;
|
|
106
|
+
}
|
|
107
|
+
export declare const DEFAULT_RETRY_SCHEDULE_MS: number[];
|
|
108
|
+
export interface ResolvedEventsOptions extends Required<Pick<EventsModuleOptions, 'service' | 'stream' | 'mode' | 'signatureMode' | 'createStream' | 'streamReplicas' | 'streamMaxBytes' | 'retryScheduleMs' | 'relayIntervalMs' | 'relayBatchSize' | 'pollMs' | 'outboxRetentionDays' | 'inboxRetentionDays'>> {
|
|
109
|
+
natsUrl?: string;
|
|
110
|
+
signer?: EventSigner;
|
|
111
|
+
verifier?: EventVerifier;
|
|
112
|
+
connection?: Record<string, unknown>;
|
|
113
|
+
traceparent?: () => string | undefined;
|
|
114
|
+
}
|
|
115
|
+
export declare function resolveOptions(o: EventsModuleOptions, env?: NodeJS.ProcessEnv): ResolvedEventsOptions;
|
|
116
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_RETRY_SCHEDULE_MS = void 0;
|
|
4
|
+
exports.resolveOptions = resolveOptions;
|
|
5
|
+
exports.DEFAULT_RETRY_SCHEDULE_MS = [10_000, 60_000, 300_000, 1_800_000, 7_200_000, 21_600_000, 43_200_000];
|
|
6
|
+
function resolveOptions(o, env = process.env) {
|
|
7
|
+
const mode = (o.mode ?? env.EVENTS_MODE ?? 'off');
|
|
8
|
+
const sig = (o.signatureMode ?? env.EVENTS_SIGNATURE ?? 'off');
|
|
9
|
+
if (!['off', 'outbox', 'on'].includes(mode))
|
|
10
|
+
throw new Error(`EVENTS_MODE non valido: ${mode}`);
|
|
11
|
+
if (!['off', 'shadow', 'enforce'].includes(sig))
|
|
12
|
+
throw new Error(`EVENTS_SIGNATURE non valido: ${sig}`);
|
|
13
|
+
return {
|
|
14
|
+
service: o.service,
|
|
15
|
+
stream: o.stream ?? `EVT_${o.service.toUpperCase()}`,
|
|
16
|
+
mode,
|
|
17
|
+
signatureMode: sig,
|
|
18
|
+
createStream: o.createStream ?? true,
|
|
19
|
+
streamReplicas: o.streamReplicas ?? (env.EVENTS_STREAM_REPLICAS ? Number(env.EVENTS_STREAM_REPLICAS) : 3),
|
|
20
|
+
streamMaxBytes: o.streamMaxBytes ?? (env.EVENTS_STREAM_MAX_BYTES ? Number(env.EVENTS_STREAM_MAX_BYTES) : 5 * 1024 ** 3),
|
|
21
|
+
retryScheduleMs: o.retryScheduleMs ?? exports.DEFAULT_RETRY_SCHEDULE_MS,
|
|
22
|
+
relayIntervalMs: o.relayIntervalMs ?? 500,
|
|
23
|
+
relayBatchSize: o.relayBatchSize ?? 100,
|
|
24
|
+
pollMs: o.pollMs ?? 5_000,
|
|
25
|
+
outboxRetentionDays: o.outboxRetentionDays ?? 7,
|
|
26
|
+
inboxRetentionDays: o.inboxRetentionDays ?? 30,
|
|
27
|
+
natsUrl: o.natsUrl ?? env.EVENTS_NATS_URL ?? env.NATS_URL,
|
|
28
|
+
signer: o.signer,
|
|
29
|
+
verifier: o.verifier,
|
|
30
|
+
connection: o.connection,
|
|
31
|
+
traceparent: o.traceparent,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AA0IA,wCAyBC;AArDY,QAAA,yBAAyB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AA4BjH,SAAgB,cAAc,CAAC,CAAsB,EAAE,MAAyB,OAAO,CAAC,GAAG;IACzF,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAK,GAAG,CAAC,WAA0B,IAAI,KAAK,CAAe,CAAC;IAChF,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,aAAa,IAAK,GAAG,CAAC,gBAAwC,IAAI,KAAK,CAAwB,CAAC;IAC/G,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;IAChG,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;IACxG,OAAO;QACL,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE;QACpD,IAAI;QACJ,aAAa,EAAE,GAAG;QAClB,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,IAAI;QACpC,cAAc,EAAE,CAAC,CAAC,cAAc,IAAI,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzG,cAAc,EAAE,CAAC,CAAC,cAAc,IAAI,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;QACvH,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,iCAAyB;QAC/D,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,GAAG;QACzC,cAAc,EAAE,CAAC,CAAC,cAAc,IAAI,GAAG;QACvC,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,KAAK;QACzB,mBAAmB,EAAE,CAAC,CAAC,mBAAmB,IAAI,CAAC;QAC/C,kBAAkB,EAAE,CAAC,CAAC,kBAAkB,IAAI,EAAE;QAC9C,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,QAAQ;QACzD,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,WAAW,EAAE,CAAC,CAAC,WAAW;KAC3B,CAAC;AACJ,CAAC"}
|
package/dist/uuid.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UUID v7 (RFC 9562): 48 bit di millisecondi + 12 bit di contatore monotono nello stesso millisecondo + 62 bit casuali.
|
|
3
|
+
* Gli id si ordinano come testo nell'ordine in cui sono stati generati (da questo processo).
|
|
4
|
+
*/
|
|
5
|
+
export declare function uuidv7(now?: number): string;
|
|
6
|
+
export declare const UUID_RE: RegExp;
|
|
7
|
+
//# sourceMappingURL=uuid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,MAAM,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM,CAgBvD;AAED,eAAO,MAAM,OAAO,QAAoE,CAAC"}
|
package/dist/uuid.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UUID_RE = void 0;
|
|
4
|
+
exports.uuidv7 = uuidv7;
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
let lastMs = 0;
|
|
7
|
+
let seq = 0;
|
|
8
|
+
/**
|
|
9
|
+
* UUID v7 (RFC 9562): 48 bit di millisecondi + 12 bit di contatore monotono nello stesso millisecondo + 62 bit casuali.
|
|
10
|
+
* Gli id si ordinano come testo nell'ordine in cui sono stati generati (da questo processo).
|
|
11
|
+
*/
|
|
12
|
+
function uuidv7(now = Date.now()) {
|
|
13
|
+
if (now <= lastMs) {
|
|
14
|
+
now = lastMs;
|
|
15
|
+
seq = (seq + 1) & 0xfff;
|
|
16
|
+
if (seq === 0)
|
|
17
|
+
now = ++lastMs; // contatore esaurito: avanza il millisecondo
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
seq = (0, crypto_1.randomBytes)(2).readUInt16BE(0) & 0x1ff; // parte bassa, lascia spazio al contatore
|
|
21
|
+
}
|
|
22
|
+
lastMs = now;
|
|
23
|
+
const b = (0, crypto_1.randomBytes)(16);
|
|
24
|
+
b.writeUIntBE(now, 0, 6);
|
|
25
|
+
b[6] = 0x70 | ((seq >> 8) & 0x0f);
|
|
26
|
+
b[7] = seq & 0xff;
|
|
27
|
+
b[8] = (b[8] & 0x3f) | 0x80;
|
|
28
|
+
const h = b.toString('hex');
|
|
29
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
30
|
+
}
|
|
31
|
+
exports.UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
32
|
+
//# sourceMappingURL=uuid.js.map
|
package/dist/uuid.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":";;;AASA,wBAgBC;AAzBD,mCAAqC;AAErC,IAAI,MAAM,GAAG,CAAC,CAAC;AACf,IAAI,GAAG,GAAG,CAAC,CAAC;AAEZ;;;GAGG;AACH,SAAgB,MAAM,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;IAC7C,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;QAClB,GAAG,GAAG,MAAM,CAAC;QACb,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QACxB,IAAI,GAAG,KAAK,CAAC;YAAE,GAAG,GAAG,EAAE,MAAM,CAAC,CAAC,6CAA6C;IAC9E,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,IAAA,oBAAW,EAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,0CAA0C;IAC1F,CAAC;IACD,MAAM,GAAG,GAAG,CAAC;IACb,MAAM,CAAC,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC;IAClB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AACnG,CAAC;AAEY,QAAA,OAAO,GAAG,iEAAiE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@generazioneai/events",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Eventi durevoli fra i servizi (ADR-009): outbox transazionale, relay su NATS JetStream, consumer durevoli con inbox idempotente, riprove con attesa crescente e dead letter comune.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"sql",
|
|
10
|
+
"prisma",
|
|
11
|
+
"README.md",
|
|
12
|
+
"CHANGELOG.md"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"registry": "https://registry.npmjs.org",
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/GenerazioneAI-SRL/authz.git",
|
|
21
|
+
"directory": "packages/events"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=22.0.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"test": "jest --maxWorkers=2",
|
|
30
|
+
"typecheck": "tsc -p tsconfig.spec.json",
|
|
31
|
+
"test:prepare": "prisma generate --config test/prisma/prisma.config.ts"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@opentelemetry/api": "~1.9.0",
|
|
35
|
+
"@nats-io/jetstream": "^3.4.0",
|
|
36
|
+
"@nats-io/transport-node": "^3.4.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@generazioneai/authz": "^0.7.0",
|
|
40
|
+
"@nestjs/common": "^11.0.0 || ^12.0.0",
|
|
41
|
+
"@nestjs/core": "^11.0.0 || ^12.0.0",
|
|
42
|
+
"reflect-metadata": "^0.2.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"@generazioneai/authz": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@nestjs/common": "^11.2.7",
|
|
51
|
+
"@nestjs/core": "^11.2.7",
|
|
52
|
+
"@nestjs/testing": "^11.2.7",
|
|
53
|
+
"@prisma/adapter-pg": "^7.8.0",
|
|
54
|
+
"@prisma/client": "^7.8.0",
|
|
55
|
+
"@types/node": "^22.0.0",
|
|
56
|
+
"@types/pg": "^8.15.0",
|
|
57
|
+
"pg": "^8.16.0",
|
|
58
|
+
"prisma": "^7.8.0",
|
|
59
|
+
"rxjs": "^7.8.1",
|
|
60
|
+
"typescript": "^5.1.3"
|
|
61
|
+
},
|
|
62
|
+
"keywords": [
|
|
63
|
+
"jetstream",
|
|
64
|
+
"nats",
|
|
65
|
+
"outbox",
|
|
66
|
+
"events"
|
|
67
|
+
],
|
|
5
68
|
"license": "UNLICENSED",
|
|
6
|
-
"
|
|
7
|
-
"files": ["README.md"]
|
|
69
|
+
"homepage": "https://github.com/GenerazioneAI-SRL/authz/tree/main/packages/events#readme"
|
|
8
70
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// @generazioneai/events 0.1.0 — da copiare nello schema del servizio (con prismaSchemaFolder: prisma/schema/events.prisma).
|
|
2
|
+
// Poi `prisma migrate dev --name events_outbox` (o l'SQL di sql/001-events-outbox-inbox.sql nel registro, per HR).
|
|
3
|
+
// I nomi delle tabelle e delle colonne sono quelli che la libreria usa nelle sue query: non cambiarli.
|
|
4
|
+
model OutboxEvent {
|
|
5
|
+
id String @id @db.Uuid // UUID v7: ordinato nel tempo
|
|
6
|
+
subject String // evt.id.employment.changed
|
|
7
|
+
aggregateType String
|
|
8
|
+
aggregateId String
|
|
9
|
+
aggregateVersion Int
|
|
10
|
+
juridicalId String? // l'azienda, anche in Gen-Tenant
|
|
11
|
+
payload Json // `data` della busta: solo id e stati
|
|
12
|
+
headers Json? // { occurredAt, traceparent? }
|
|
13
|
+
createdAt DateTime @default(now())
|
|
14
|
+
publishedAt DateTime?
|
|
15
|
+
attempts Int @default(0)
|
|
16
|
+
lastError String?
|
|
17
|
+
|
|
18
|
+
@@index([publishedAt, id])
|
|
19
|
+
@@map("outbox_events")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
model InboxProcessed {
|
|
23
|
+
consumer String
|
|
24
|
+
eventId String @db.Uuid
|
|
25
|
+
processedAt DateTime @default(now())
|
|
26
|
+
|
|
27
|
+
@@id([consumer, eventId])
|
|
28
|
+
@@index([processedAt])
|
|
29
|
+
@@map("inbox_processed")
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- @generazioneai/events 0.1.0: outbox e inbox. Solo aggiunte; il rientro è 001-events-outbox-inbox-rientro.sql.
|
|
2
|
+
-- Equivalente SQL di prisma/events.prisma (stesse colonne e tipi di quelli che Prisma 7 genera): copiare nella migrazione
|
|
3
|
+
-- del servizio (Prisma: `prisma migrate`; HR: nel registro SQL con `-- db:` e il suo -rientro).
|
|
4
|
+
CREATE TABLE IF NOT EXISTS "outbox_events" (
|
|
5
|
+
"id" UUID NOT NULL,
|
|
6
|
+
"subject" TEXT NOT NULL,
|
|
7
|
+
"aggregateType" TEXT NOT NULL,
|
|
8
|
+
"aggregateId" TEXT NOT NULL,
|
|
9
|
+
"aggregateVersion" INTEGER NOT NULL,
|
|
10
|
+
"juridicalId" TEXT,
|
|
11
|
+
"payload" JSONB NOT NULL,
|
|
12
|
+
"headers" JSONB,
|
|
13
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
14
|
+
"publishedAt" TIMESTAMP(3),
|
|
15
|
+
"attempts" INTEGER NOT NULL DEFAULT 0,
|
|
16
|
+
"lastError" TEXT,
|
|
17
|
+
CONSTRAINT "outbox_events_pkey" PRIMARY KEY ("id")
|
|
18
|
+
);
|
|
19
|
+
-- il relay legge solo le righe non pubblicate, in ordine di id (UUID v7 = ordine di scrittura)
|
|
20
|
+
CREATE INDEX IF NOT EXISTS "outbox_events_publishedAt_id_idx" ON "outbox_events" ("publishedAt", "id");
|
|
21
|
+
|
|
22
|
+
CREATE TABLE IF NOT EXISTS "inbox_processed" (
|
|
23
|
+
"consumer" TEXT NOT NULL,
|
|
24
|
+
"eventId" UUID NOT NULL,
|
|
25
|
+
"processedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
26
|
+
CONSTRAINT "inbox_processed_pkey" PRIMARY KEY ("consumer", "eventId")
|
|
27
|
+
);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS "inbox_processed_processedAt_idx" ON "inbox_processed" ("processedAt");
|