@generazioneai/events 0.0.1 → 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.
Files changed (58) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +106 -1
  3. package/dist/connection.d.ts +23 -0
  4. package/dist/connection.d.ts.map +1 -0
  5. package/dist/connection.js +113 -0
  6. package/dist/connection.js.map +1 -0
  7. package/dist/consumers.service.d.ts +54 -0
  8. package/dist/consumers.service.d.ts.map +1 -0
  9. package/dist/consumers.service.js +346 -0
  10. package/dist/consumers.service.js.map +1 -0
  11. package/dist/decorators.d.ts +12 -0
  12. package/dist/decorators.d.ts.map +1 -0
  13. package/dist/decorators.js +14 -0
  14. package/dist/decorators.js.map +1 -0
  15. package/dist/errors.d.ts +13 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +24 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/events.module.d.ts +20 -0
  20. package/dist/events.module.d.ts.map +1 -0
  21. package/dist/events.module.js +98 -0
  22. package/dist/events.module.js.map +1 -0
  23. package/dist/index.d.ts +11 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +29 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/metrics.d.ts +18 -0
  28. package/dist/metrics.d.ts.map +1 -0
  29. package/dist/metrics.js +64 -0
  30. package/dist/metrics.js.map +1 -0
  31. package/dist/outbox.service.d.ts +24 -0
  32. package/dist/outbox.service.d.ts.map +1 -0
  33. package/dist/outbox.service.js +88 -0
  34. package/dist/outbox.service.js.map +1 -0
  35. package/dist/prisma-resolver.d.ts +11 -0
  36. package/dist/prisma-resolver.d.ts.map +1 -0
  37. package/dist/prisma-resolver.js +46 -0
  38. package/dist/prisma-resolver.js.map +1 -0
  39. package/dist/relay.service.d.ts +32 -0
  40. package/dist/relay.service.d.ts.map +1 -0
  41. package/dist/relay.service.js +153 -0
  42. package/dist/relay.service.js.map +1 -0
  43. package/dist/tokens.d.ts +4 -0
  44. package/dist/tokens.d.ts.map +1 -0
  45. package/dist/tokens.js +7 -0
  46. package/dist/tokens.js.map +1 -0
  47. package/dist/types.d.ts +116 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +34 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/uuid.d.ts +7 -0
  52. package/dist/uuid.d.ts.map +1 -0
  53. package/dist/uuid.js +32 -0
  54. package/dist/uuid.js.map +1 -0
  55. package/package.json +65 -4
  56. package/prisma/events.prisma +30 -0
  57. package/sql/001-events-outbox-inbox-rientro.sql +3 -0
  58. package/sql/001-events-outbox-inbox.sql +28 -0
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
@@ -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,69 @@
1
1
  {
2
2
  "name": "@generazioneai/events",
3
- "version": "0.0.1",
4
- "description": "Segnaposto: il pacchetto vero arriva con la 0.1.0 dal repo GenerazioneAI-SRL/authz.",
3
+ "version": "0.1.0",
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
+ "@nats-io/jetstream": "^3.4.0",
35
+ "@nats-io/transport-node": "^3.4.0"
36
+ },
37
+ "peerDependencies": {
38
+ "@generazioneai/authz": "^0.7.0",
39
+ "@nestjs/common": "^11.0.0 || ^12.0.0",
40
+ "@nestjs/core": "^11.0.0 || ^12.0.0",
41
+ "reflect-metadata": "^0.2.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "@generazioneai/authz": {
45
+ "optional": true
46
+ }
47
+ },
48
+ "devDependencies": {
49
+ "@nestjs/common": "^11.2.7",
50
+ "@nestjs/core": "^11.2.7",
51
+ "@nestjs/testing": "^11.2.7",
52
+ "@prisma/adapter-pg": "^7.8.0",
53
+ "@prisma/client": "^7.8.0",
54
+ "@types/node": "^22.0.0",
55
+ "@types/pg": "^8.15.0",
56
+ "pg": "^8.16.0",
57
+ "prisma": "^7.8.0",
58
+ "rxjs": "^7.8.1",
59
+ "typescript": "^5.1.3"
60
+ },
61
+ "keywords": [
62
+ "jetstream",
63
+ "nats",
64
+ "outbox",
65
+ "events"
66
+ ],
5
67
  "license": "UNLICENSED",
6
- "repository": { "type": "git", "url": "git+https://github.com/GenerazioneAI-SRL/authz.git", "directory": "packages/events" },
7
- "files": ["README.md"]
68
+ "homepage": "https://github.com/GenerazioneAI-SRL/authz/tree/main/packages/events#readme"
8
69
  }
@@ -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,3 @@
1
+ -- Rientro di 001-events-outbox-inbox.sql: perde gli eventi non ancora pubblicati e l'inbox. Solo con EVENTS_MODE=off.
2
+ DROP TABLE IF EXISTS "inbox_processed";
3
+ DROP TABLE IF EXISTS "outbox_events";
@@ -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");