@verdeect/kit 0.0.0-stage → 1.0.0-rc.2
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 +138 -0
- package/README.md +804 -2
- package/alerts/verdeect-kit.rules.yml +133 -0
- package/dist/cli/commands/crypto.d.ts +9 -0
- package/dist/cli/commands/crypto.js +87 -0
- package/dist/cli/commands/db.d.ts +11 -0
- package/dist/cli/commands/db.js +151 -0
- package/dist/cli/commands/openapi.d.ts +9 -0
- package/dist/cli/commands/openapi.js +144 -0
- package/dist/cli/commands/rabbitmq.d.ts +11 -0
- package/dist/cli/commands/rabbitmq.js +124 -0
- package/dist/cli/commands/redis.d.ts +11 -0
- package/dist/cli/commands/redis.js +119 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +31 -0
- package/dist/config/cli-schemas.d.ts +29 -0
- package/dist/config/cli-schemas.js +15 -0
- package/dist/config/define-env.d.ts +21 -0
- package/dist/config/define-env.js +43 -0
- package/dist/config/fragments.d.ts +105 -0
- package/dist/config/fragments.js +86 -0
- package/dist/config/helpers.d.ts +26 -0
- package/dist/config/helpers.js +62 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.js +7 -0
- package/dist/config/kit-config.module.d.ts +20 -0
- package/dist/config/kit-config.module.js +23 -0
- package/dist/config/parse-env.d.ts +14 -0
- package/dist/config/parse-env.js +28 -0
- package/dist/crypto/cipher.d.ts +61 -0
- package/dist/crypto/cipher.js +136 -0
- package/dist/crypto/index.d.ts +2 -0
- package/dist/crypto/index.js +2 -0
- package/dist/database/batches.d.ts +42 -0
- package/dist/database/batches.js +49 -0
- package/dist/database/db.d.ts +93 -0
- package/dist/database/db.js +234 -0
- package/dist/database/if-newer.d.ts +9 -0
- package/dist/database/if-newer.js +15 -0
- package/dist/database/index.d.ts +17 -0
- package/dist/database/index.js +13 -0
- package/dist/database/kit-database.module.d.ts +47 -0
- package/dist/database/kit-database.module.js +181 -0
- package/dist/database/migrations.d.ts +42 -0
- package/dist/database/migrations.js +142 -0
- package/dist/database/pg.d.ts +20 -0
- package/dist/database/pg.js +19 -0
- package/dist/database/prepare-database.d.ts +39 -0
- package/dist/database/prepare-database.js +75 -0
- package/dist/database/run-exclusive.d.ts +19 -0
- package/dist/database/run-exclusive.js +62 -0
- package/dist/database/tenant.d.ts +34 -0
- package/dist/database/tenant.js +28 -0
- package/dist/http/api/api.decorator.d.ts +18 -0
- package/dist/http/api/api.decorator.js +9 -0
- package/dist/http/api/openapi-document.d.ts +40 -0
- package/dist/http/api/openapi-document.js +452 -0
- package/dist/http/bootstrap-service.d.ts +33 -0
- package/dist/http/bootstrap-service.js +107 -0
- package/dist/http/create-http-app.d.ts +38 -0
- package/dist/http/create-http-app.js +137 -0
- package/dist/http/errors.d.ts +118 -0
- package/dist/http/errors.js +94 -0
- package/dist/http/index.d.ts +12 -0
- package/dist/http/index.js +8 -0
- package/dist/http/kit-exception.filter.d.ts +18 -0
- package/dist/http/kit-exception.filter.js +151 -0
- package/dist/http/kit-http-core.module.d.ts +19 -0
- package/dist/http/kit-http-core.module.js +122 -0
- package/dist/http/pagination.d.ts +30 -0
- package/dist/http/pagination.js +73 -0
- package/dist/http/zod-validation.pipe.d.ts +15 -0
- package/dist/http/zod-validation.pipe.js +47 -0
- package/dist/idempotency/idempotency.interceptor.d.ts +55 -0
- package/dist/idempotency/idempotency.interceptor.js +317 -0
- package/dist/idempotency/idempotent.decorator.d.ts +7 -0
- package/dist/idempotency/idempotent.decorator.js +16 -0
- package/dist/idempotency/index.d.ts +7 -0
- package/dist/idempotency/index.js +6 -0
- package/dist/idempotency/kit-idempotency.module.d.ts +47 -0
- package/dist/idempotency/kit-idempotency.module.js +173 -0
- package/dist/idempotency/peer.d.ts +1 -0
- package/dist/idempotency/peer.js +4 -0
- package/dist/idempotency/table.d.ts +203 -0
- package/dist/idempotency/table.js +86 -0
- package/dist/internal/cleanup-result.d.ts +7 -0
- package/dist/internal/cleanup-result.js +12 -0
- package/dist/internal/ids.d.ts +13 -0
- package/dist/internal/ids.js +41 -0
- package/dist/internal/metadata.d.ts +12 -0
- package/dist/internal/metadata.js +3 -0
- package/dist/internal/outbox-policy.d.ts +8 -0
- package/dist/internal/outbox-policy.js +10 -0
- package/dist/internal/peer.d.ts +16 -0
- package/dist/internal/peer.js +56 -0
- package/dist/internal/pg-errors.d.ts +21 -0
- package/dist/internal/pg-errors.js +115 -0
- package/dist/internal/preparsed-env.d.ts +4 -0
- package/dist/internal/preparsed-env.js +14 -0
- package/dist/internal/rabbitmq-definitions.d.ts +85 -0
- package/dist/internal/rabbitmq-definitions.js +73 -0
- package/dist/internal/redis-acl.d.ts +21 -0
- package/dist/internal/redis-acl.js +30 -0
- package/dist/internal/service-name.d.ts +4 -0
- package/dist/internal/service-name.js +10 -0
- package/dist/internal/sql-identifier.d.ts +5 -0
- package/dist/internal/sql-identifier.js +12 -0
- package/dist/internal/tokens.d.ts +10 -0
- package/dist/internal/tokens.js +12 -0
- package/dist/internal/tx-context.d.ts +5 -0
- package/dist/internal/tx-context.js +9 -0
- package/dist/messaging/cleanup.d.ts +49 -0
- package/dist/messaging/cleanup.js +129 -0
- package/dist/messaging/config.d.ts +43 -0
- package/dist/messaging/config.js +8 -0
- package/dist/messaging/define-event.d.ts +29 -0
- package/dist/messaging/define-event.js +38 -0
- package/dist/messaging/envelope.d.ts +66 -0
- package/dist/messaging/envelope.js +49 -0
- package/dist/messaging/errors.d.ts +45 -0
- package/dist/messaging/errors.js +63 -0
- package/dist/messaging/inbox.d.ts +4 -0
- package/dist/messaging/inbox.js +9 -0
- package/dist/messaging/index.d.ts +13 -0
- package/dist/messaging/index.js +10 -0
- package/dist/messaging/kit-messaging.module.d.ts +39 -0
- package/dist/messaging/kit-messaging.module.js +309 -0
- package/dist/messaging/outbox-relay.d.ts +67 -0
- package/dist/messaging/outbox-relay.js +260 -0
- package/dist/messaging/outbox-store.d.ts +75 -0
- package/dist/messaging/outbox-store.js +126 -0
- package/dist/messaging/peer.d.ts +1 -0
- package/dist/messaging/peer.js +5 -0
- package/dist/messaging/rabbit-connection.d.ts +144 -0
- package/dist/messaging/rabbit-connection.js +472 -0
- package/dist/messaging/redelivery.d.ts +6 -0
- package/dist/messaging/redelivery.js +17 -0
- package/dist/messaging/subscriber.d.ts +102 -0
- package/dist/messaging/subscriber.js +412 -0
- package/dist/messaging/tables.d.ts +315 -0
- package/dist/messaging/tables.js +72 -0
- package/dist/messaging/topology.d.ts +56 -0
- package/dist/messaging/topology.js +56 -0
- package/dist/observability/fail-startup.d.ts +14 -0
- package/dist/observability/fail-startup.js +51 -0
- package/dist/observability/health-registry.d.ts +34 -0
- package/dist/observability/health-registry.js +81 -0
- package/dist/observability/index.d.ts +16 -0
- package/dist/observability/index.js +10 -0
- package/dist/observability/lifecycle.d.ts +52 -0
- package/dist/observability/lifecycle.js +151 -0
- package/dist/observability/logger-options.d.ts +54 -0
- package/dist/observability/logger-options.js +91 -0
- package/dist/observability/metrics-server.d.ts +33 -0
- package/dist/observability/metrics-server.js +108 -0
- package/dist/observability/metrics.d.ts +30 -0
- package/dist/observability/metrics.js +53 -0
- package/dist/observability/redaction.d.ts +23 -0
- package/dist/observability/redaction.js +65 -0
- package/dist/observability/sanitize-error.d.ts +47 -0
- package/dist/observability/sanitize-error.js +194 -0
- package/dist/observability/sentry.d.ts +64 -0
- package/dist/observability/sentry.js +233 -0
- package/dist/redis/channels.d.ts +34 -0
- package/dist/redis/channels.js +128 -0
- package/dist/redis/client.d.ts +42 -0
- package/dist/redis/client.js +85 -0
- package/dist/redis/index.d.ts +7 -0
- package/dist/redis/index.js +7 -0
- package/dist/redis/kit-redis.module.d.ts +18 -0
- package/dist/redis/kit-redis.module.js +131 -0
- package/dist/redis/peer.d.ts +1 -0
- package/dist/redis/peer.js +4 -0
- package/dist/redis/rate-limiter.d.ts +49 -0
- package/dist/redis/rate-limiter.js +139 -0
- package/dist/testing/count-queries.d.ts +30 -0
- package/dist/testing/count-queries.js +58 -0
- package/dist/testing/create-test-app.d.ts +26 -0
- package/dist/testing/create-test-app.js +32 -0
- package/dist/testing/expect-matches-api.d.ts +8 -0
- package/dist/testing/expect-matches-api.js +49 -0
- package/dist/testing/expect-problem.d.ts +24 -0
- package/dist/testing/expect-problem.js +23 -0
- package/dist/testing/images.d.ts +10 -0
- package/dist/testing/images.js +14 -0
- package/dist/testing/index.d.ts +23 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/launch-command.d.ts +52 -0
- package/dist/testing/launch-command.js +510 -0
- package/dist/testing/outbox-coverage.d.ts +26 -0
- package/dist/testing/outbox-coverage.js +98 -0
- package/dist/testing/rabbitmq-admin.d.ts +81 -0
- package/dist/testing/rabbitmq-admin.js +146 -0
- package/dist/testing/redis-worker.d.ts +10 -0
- package/dist/testing/redis-worker.js +15 -0
- package/dist/testing/repo-conventions.d.ts +10 -0
- package/dist/testing/repo-conventions.js +524 -0
- package/dist/testing/repo-files.d.ts +113 -0
- package/dist/testing/repo-files.js +401 -0
- package/dist/testing/scratch-database.d.ts +19 -0
- package/dist/testing/scratch-database.js +75 -0
- package/dist/testing/start-test-stack.d.ts +85 -0
- package/dist/testing/start-test-stack.js +342 -0
- package/dist/testing/tcp-proxy.d.ts +40 -0
- package/dist/testing/tcp-proxy.js +164 -0
- package/dist/testing/tenant-isolation.d.ts +31 -0
- package/dist/testing/tenant-isolation.js +205 -0
- package/dist/testing/test-events.d.ts +37 -0
- package/dist/testing/test-events.js +150 -0
- package/dist/testing/timeout-scale.d.ts +4 -0
- package/dist/testing/timeout-scale.js +17 -0
- package/dist/testing/timing-guard.d.ts +22 -0
- package/dist/testing/timing-guard.js +54 -0
- package/dist/testing/worker-scope.d.ts +31 -0
- package/dist/testing/worker-scope.js +68 -0
- package/package.json +177 -4
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { type PgSchema } from 'drizzle-orm/pg-core';
|
|
2
|
+
/** Опции `createOutboxTable`. */
|
|
3
|
+
export interface KitOutboxTableOptions {
|
|
4
|
+
/** `true` — колонка `tenant` допускает NULL (события `tenant: 'none'`, роль Identity). По умолчанию `false`. */
|
|
5
|
+
readonly tenantNullable?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Таблица `<schema>.outbox` (§6): `id` (`evt_` + ULID), `seq` (identity, порядок публикации), `tenant`, `type`,
|
|
9
|
+
* `version`, `subject_id`, `actor`, `trace_id`, `data`, `occurred_at`, `published_at`, `attempts`, `last_error`;
|
|
10
|
+
* индексы неопубликованных по `seq` и опубликованных по `published_at`; политика `outbox_tenant_isolation` —
|
|
11
|
+
* исключение строгой модели (пустой тенант видит все строки для relay и очистки). Сервис добавляет миграцией
|
|
12
|
+
* `ALTER TABLE <schema>.outbox FORCE ROW LEVEL SECURITY` и `GRANT SELECT, INSERT, UPDATE, DELETE` роли `<svc>_app`.
|
|
13
|
+
* @throws {TypeError} имя схемы вне `^[a-z][a-z0-9-]{1,31}$`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createOutboxTable(schema: PgSchema, options?: KitOutboxTableOptions): import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
16
|
+
name: "outbox";
|
|
17
|
+
schema: string;
|
|
18
|
+
columns: {
|
|
19
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
20
|
+
name: "id";
|
|
21
|
+
tableName: "outbox";
|
|
22
|
+
dataType: "string";
|
|
23
|
+
columnType: "PgText";
|
|
24
|
+
data: string;
|
|
25
|
+
driverParam: string;
|
|
26
|
+
notNull: true;
|
|
27
|
+
hasDefault: false;
|
|
28
|
+
isPrimaryKey: true;
|
|
29
|
+
isAutoincrement: false;
|
|
30
|
+
hasRuntimeDefault: false;
|
|
31
|
+
enumValues: [string, ...string[]];
|
|
32
|
+
baseColumn: never;
|
|
33
|
+
identity: undefined;
|
|
34
|
+
generated: undefined;
|
|
35
|
+
}, {}, {}>;
|
|
36
|
+
seq: import("drizzle-orm/pg-core").PgColumn<{
|
|
37
|
+
name: "seq";
|
|
38
|
+
tableName: "outbox";
|
|
39
|
+
dataType: "number";
|
|
40
|
+
columnType: "PgBigInt53";
|
|
41
|
+
data: number;
|
|
42
|
+
driverParam: string | number;
|
|
43
|
+
notNull: true;
|
|
44
|
+
hasDefault: true;
|
|
45
|
+
isPrimaryKey: false;
|
|
46
|
+
isAutoincrement: false;
|
|
47
|
+
hasRuntimeDefault: false;
|
|
48
|
+
enumValues: undefined;
|
|
49
|
+
baseColumn: never;
|
|
50
|
+
identity: "always";
|
|
51
|
+
generated: undefined;
|
|
52
|
+
}, {}, {}>;
|
|
53
|
+
tenant: import("drizzle-orm/pg-core").PgColumn<{
|
|
54
|
+
name: string;
|
|
55
|
+
tableName: "outbox";
|
|
56
|
+
dataType: "string";
|
|
57
|
+
columnType: "PgText";
|
|
58
|
+
data: string;
|
|
59
|
+
driverParam: string;
|
|
60
|
+
notNull: false;
|
|
61
|
+
hasDefault: false;
|
|
62
|
+
isPrimaryKey: false;
|
|
63
|
+
isAutoincrement: false;
|
|
64
|
+
hasRuntimeDefault: false;
|
|
65
|
+
enumValues: [string, ...string[]];
|
|
66
|
+
baseColumn: never;
|
|
67
|
+
identity: undefined;
|
|
68
|
+
generated: undefined;
|
|
69
|
+
}, {}, {}>;
|
|
70
|
+
type: import("drizzle-orm/pg-core").PgColumn<{
|
|
71
|
+
name: "type";
|
|
72
|
+
tableName: "outbox";
|
|
73
|
+
dataType: "string";
|
|
74
|
+
columnType: "PgText";
|
|
75
|
+
data: string;
|
|
76
|
+
driverParam: string;
|
|
77
|
+
notNull: true;
|
|
78
|
+
hasDefault: false;
|
|
79
|
+
isPrimaryKey: false;
|
|
80
|
+
isAutoincrement: false;
|
|
81
|
+
hasRuntimeDefault: false;
|
|
82
|
+
enumValues: [string, ...string[]];
|
|
83
|
+
baseColumn: never;
|
|
84
|
+
identity: undefined;
|
|
85
|
+
generated: undefined;
|
|
86
|
+
}, {}, {}>;
|
|
87
|
+
version: import("drizzle-orm/pg-core").PgColumn<{
|
|
88
|
+
name: "version";
|
|
89
|
+
tableName: "outbox";
|
|
90
|
+
dataType: "number";
|
|
91
|
+
columnType: "PgInteger";
|
|
92
|
+
data: number;
|
|
93
|
+
driverParam: string | number;
|
|
94
|
+
notNull: true;
|
|
95
|
+
hasDefault: false;
|
|
96
|
+
isPrimaryKey: false;
|
|
97
|
+
isAutoincrement: false;
|
|
98
|
+
hasRuntimeDefault: false;
|
|
99
|
+
enumValues: undefined;
|
|
100
|
+
baseColumn: never;
|
|
101
|
+
identity: undefined;
|
|
102
|
+
generated: undefined;
|
|
103
|
+
}, {}, {}>;
|
|
104
|
+
subjectId: import("drizzle-orm/pg-core").PgColumn<{
|
|
105
|
+
name: "subject_id";
|
|
106
|
+
tableName: "outbox";
|
|
107
|
+
dataType: "string";
|
|
108
|
+
columnType: "PgText";
|
|
109
|
+
data: string;
|
|
110
|
+
driverParam: string;
|
|
111
|
+
notNull: true;
|
|
112
|
+
hasDefault: false;
|
|
113
|
+
isPrimaryKey: false;
|
|
114
|
+
isAutoincrement: false;
|
|
115
|
+
hasRuntimeDefault: false;
|
|
116
|
+
enumValues: [string, ...string[]];
|
|
117
|
+
baseColumn: never;
|
|
118
|
+
identity: undefined;
|
|
119
|
+
generated: undefined;
|
|
120
|
+
}, {}, {}>;
|
|
121
|
+
actor: import("drizzle-orm/pg-core").PgColumn<{
|
|
122
|
+
name: "actor";
|
|
123
|
+
tableName: "outbox";
|
|
124
|
+
dataType: "json";
|
|
125
|
+
columnType: "PgJsonb";
|
|
126
|
+
data: Record<string, unknown>;
|
|
127
|
+
driverParam: unknown;
|
|
128
|
+
notNull: true;
|
|
129
|
+
hasDefault: false;
|
|
130
|
+
isPrimaryKey: false;
|
|
131
|
+
isAutoincrement: false;
|
|
132
|
+
hasRuntimeDefault: false;
|
|
133
|
+
enumValues: undefined;
|
|
134
|
+
baseColumn: never;
|
|
135
|
+
identity: undefined;
|
|
136
|
+
generated: undefined;
|
|
137
|
+
}, {}, {
|
|
138
|
+
$type: Record<string, unknown>;
|
|
139
|
+
}>;
|
|
140
|
+
traceId: import("drizzle-orm/pg-core").PgColumn<{
|
|
141
|
+
name: "trace_id";
|
|
142
|
+
tableName: "outbox";
|
|
143
|
+
dataType: "string";
|
|
144
|
+
columnType: "PgText";
|
|
145
|
+
data: string;
|
|
146
|
+
driverParam: string;
|
|
147
|
+
notNull: true;
|
|
148
|
+
hasDefault: false;
|
|
149
|
+
isPrimaryKey: false;
|
|
150
|
+
isAutoincrement: false;
|
|
151
|
+
hasRuntimeDefault: false;
|
|
152
|
+
enumValues: [string, ...string[]];
|
|
153
|
+
baseColumn: never;
|
|
154
|
+
identity: undefined;
|
|
155
|
+
generated: undefined;
|
|
156
|
+
}, {}, {}>;
|
|
157
|
+
data: import("drizzle-orm/pg-core").PgColumn<{
|
|
158
|
+
name: "data";
|
|
159
|
+
tableName: "outbox";
|
|
160
|
+
dataType: "json";
|
|
161
|
+
columnType: "PgJsonb";
|
|
162
|
+
data: Record<string, unknown>;
|
|
163
|
+
driverParam: unknown;
|
|
164
|
+
notNull: true;
|
|
165
|
+
hasDefault: false;
|
|
166
|
+
isPrimaryKey: false;
|
|
167
|
+
isAutoincrement: false;
|
|
168
|
+
hasRuntimeDefault: false;
|
|
169
|
+
enumValues: undefined;
|
|
170
|
+
baseColumn: never;
|
|
171
|
+
identity: undefined;
|
|
172
|
+
generated: undefined;
|
|
173
|
+
}, {}, {
|
|
174
|
+
$type: Record<string, unknown>;
|
|
175
|
+
}>;
|
|
176
|
+
occurredAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
177
|
+
name: "occurred_at";
|
|
178
|
+
tableName: "outbox";
|
|
179
|
+
dataType: "date";
|
|
180
|
+
columnType: "PgTimestamp";
|
|
181
|
+
data: Date;
|
|
182
|
+
driverParam: string;
|
|
183
|
+
notNull: true;
|
|
184
|
+
hasDefault: true;
|
|
185
|
+
isPrimaryKey: false;
|
|
186
|
+
isAutoincrement: false;
|
|
187
|
+
hasRuntimeDefault: false;
|
|
188
|
+
enumValues: undefined;
|
|
189
|
+
baseColumn: never;
|
|
190
|
+
identity: undefined;
|
|
191
|
+
generated: undefined;
|
|
192
|
+
}, {}, {}>;
|
|
193
|
+
publishedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
194
|
+
name: "published_at";
|
|
195
|
+
tableName: "outbox";
|
|
196
|
+
dataType: "date";
|
|
197
|
+
columnType: "PgTimestamp";
|
|
198
|
+
data: Date;
|
|
199
|
+
driverParam: string;
|
|
200
|
+
notNull: false;
|
|
201
|
+
hasDefault: false;
|
|
202
|
+
isPrimaryKey: false;
|
|
203
|
+
isAutoincrement: false;
|
|
204
|
+
hasRuntimeDefault: false;
|
|
205
|
+
enumValues: undefined;
|
|
206
|
+
baseColumn: never;
|
|
207
|
+
identity: undefined;
|
|
208
|
+
generated: undefined;
|
|
209
|
+
}, {}, {}>;
|
|
210
|
+
attempts: import("drizzle-orm/pg-core").PgColumn<{
|
|
211
|
+
name: "attempts";
|
|
212
|
+
tableName: "outbox";
|
|
213
|
+
dataType: "number";
|
|
214
|
+
columnType: "PgInteger";
|
|
215
|
+
data: number;
|
|
216
|
+
driverParam: string | number;
|
|
217
|
+
notNull: true;
|
|
218
|
+
hasDefault: true;
|
|
219
|
+
isPrimaryKey: false;
|
|
220
|
+
isAutoincrement: false;
|
|
221
|
+
hasRuntimeDefault: false;
|
|
222
|
+
enumValues: undefined;
|
|
223
|
+
baseColumn: never;
|
|
224
|
+
identity: undefined;
|
|
225
|
+
generated: undefined;
|
|
226
|
+
}, {}, {}>;
|
|
227
|
+
lastError: import("drizzle-orm/pg-core").PgColumn<{
|
|
228
|
+
name: "last_error";
|
|
229
|
+
tableName: "outbox";
|
|
230
|
+
dataType: "string";
|
|
231
|
+
columnType: "PgText";
|
|
232
|
+
data: string;
|
|
233
|
+
driverParam: string;
|
|
234
|
+
notNull: false;
|
|
235
|
+
hasDefault: false;
|
|
236
|
+
isPrimaryKey: false;
|
|
237
|
+
isAutoincrement: false;
|
|
238
|
+
hasRuntimeDefault: false;
|
|
239
|
+
enumValues: [string, ...string[]];
|
|
240
|
+
baseColumn: never;
|
|
241
|
+
identity: undefined;
|
|
242
|
+
generated: undefined;
|
|
243
|
+
}, {}, {}>;
|
|
244
|
+
};
|
|
245
|
+
dialect: "pg";
|
|
246
|
+
}>;
|
|
247
|
+
/**
|
|
248
|
+
* Таблица `<schema>.inbox` (§6): `id` (PK, id события), `type`, `received_at`; индекс по `received_at` для очистки.
|
|
249
|
+
* Без RLS (служебная). Сервис выдаёт миграцией `GRANT SELECT, INSERT, DELETE` роли `<svc>_app`.
|
|
250
|
+
* @throws {TypeError} имя схемы вне `^[a-z][a-z0-9-]{1,31}$`.
|
|
251
|
+
*/
|
|
252
|
+
export declare function createInboxTable(schema: PgSchema): import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
253
|
+
name: "inbox";
|
|
254
|
+
schema: string;
|
|
255
|
+
columns: {
|
|
256
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
257
|
+
name: "id";
|
|
258
|
+
tableName: "inbox";
|
|
259
|
+
dataType: "string";
|
|
260
|
+
columnType: "PgText";
|
|
261
|
+
data: string;
|
|
262
|
+
driverParam: string;
|
|
263
|
+
notNull: true;
|
|
264
|
+
hasDefault: false;
|
|
265
|
+
isPrimaryKey: true;
|
|
266
|
+
isAutoincrement: false;
|
|
267
|
+
hasRuntimeDefault: false;
|
|
268
|
+
enumValues: [string, ...string[]];
|
|
269
|
+
baseColumn: never;
|
|
270
|
+
identity: undefined;
|
|
271
|
+
generated: undefined;
|
|
272
|
+
}, {}, {}>;
|
|
273
|
+
type: import("drizzle-orm/pg-core").PgColumn<{
|
|
274
|
+
name: "type";
|
|
275
|
+
tableName: "inbox";
|
|
276
|
+
dataType: "string";
|
|
277
|
+
columnType: "PgText";
|
|
278
|
+
data: string;
|
|
279
|
+
driverParam: string;
|
|
280
|
+
notNull: true;
|
|
281
|
+
hasDefault: false;
|
|
282
|
+
isPrimaryKey: false;
|
|
283
|
+
isAutoincrement: false;
|
|
284
|
+
hasRuntimeDefault: false;
|
|
285
|
+
enumValues: [string, ...string[]];
|
|
286
|
+
baseColumn: never;
|
|
287
|
+
identity: undefined;
|
|
288
|
+
generated: undefined;
|
|
289
|
+
}, {}, {}>;
|
|
290
|
+
receivedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
291
|
+
name: "received_at";
|
|
292
|
+
tableName: "inbox";
|
|
293
|
+
dataType: "date";
|
|
294
|
+
columnType: "PgTimestamp";
|
|
295
|
+
data: Date;
|
|
296
|
+
driverParam: string;
|
|
297
|
+
notNull: true;
|
|
298
|
+
hasDefault: true;
|
|
299
|
+
isPrimaryKey: false;
|
|
300
|
+
isAutoincrement: false;
|
|
301
|
+
hasRuntimeDefault: false;
|
|
302
|
+
enumValues: undefined;
|
|
303
|
+
baseColumn: never;
|
|
304
|
+
identity: undefined;
|
|
305
|
+
generated: undefined;
|
|
306
|
+
}, {}, {}>;
|
|
307
|
+
};
|
|
308
|
+
dialect: "pg";
|
|
309
|
+
}>;
|
|
310
|
+
/** Таблица outbox (`createOutboxTable`). */
|
|
311
|
+
export type KitOutboxTable = ReturnType<typeof createOutboxTable>;
|
|
312
|
+
/** Таблица inbox (`createInboxTable`). */
|
|
313
|
+
export type KitInboxTable = ReturnType<typeof createInboxTable>;
|
|
314
|
+
/** Колонка `tenant` таблицы outbox допускает NULL (`tenantNullable: true`). */
|
|
315
|
+
export declare function outboxTenantNullable(table: KitOutboxTable): boolean;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// createOutboxTable, createInboxTable (prd.md §3.4, §6, AC-5.15). Эталон — O outbox.schema.ts, inbox.schema.ts; из I —
|
|
2
|
+
// nullable tenant (tenantNullable); по PRD §6 — subject_id вместо subject jsonb, id inbox — id события. Файл без pg и
|
|
3
|
+
// без ./peer.js: схему сервиса грузит drizzle-kit через require; проверку peer делает index.ts.
|
|
4
|
+
import { sql } from 'drizzle-orm';
|
|
5
|
+
import { bigint, getTableConfig, index, integer, jsonb, pgPolicy, text, timestamp, } from 'drizzle-orm/pg-core';
|
|
6
|
+
import { INBOX_TABLE, OUTBOX_POLICY, OUTBOX_POLICY_SQL, OUTBOX_TABLE, } from '../internal/outbox-policy.js';
|
|
7
|
+
import { assertServiceName } from '../internal/service-name.js';
|
|
8
|
+
/**
|
|
9
|
+
* Таблица `<schema>.outbox` (§6): `id` (`evt_` + ULID), `seq` (identity, порядок публикации), `tenant`, `type`,
|
|
10
|
+
* `version`, `subject_id`, `actor`, `trace_id`, `data`, `occurred_at`, `published_at`, `attempts`, `last_error`;
|
|
11
|
+
* индексы неопубликованных по `seq` и опубликованных по `published_at`; политика `outbox_tenant_isolation` —
|
|
12
|
+
* исключение строгой модели (пустой тенант видит все строки для relay и очистки). Сервис добавляет миграцией
|
|
13
|
+
* `ALTER TABLE <schema>.outbox FORCE ROW LEVEL SECURITY` и `GRANT SELECT, INSERT, UPDATE, DELETE` роли `<svc>_app`.
|
|
14
|
+
* @throws {TypeError} имя схемы вне `^[a-z][a-z0-9-]{1,31}$`.
|
|
15
|
+
*/
|
|
16
|
+
export function createOutboxTable(schema, options = {}) {
|
|
17
|
+
assertServiceName(schema.schemaName, 'createOutboxTable');
|
|
18
|
+
// Тип колонки один (string | null); NOT NULL — в конфигурации колонки, его читает outboxTenantNullable.
|
|
19
|
+
const tenant = options.tenantNullable === true
|
|
20
|
+
? text('tenant')
|
|
21
|
+
: text('tenant').notNull();
|
|
22
|
+
return schema.table(OUTBOX_TABLE, {
|
|
23
|
+
id: text('id').primaryKey(),
|
|
24
|
+
seq: bigint('seq', { mode: 'number' }).generatedAlwaysAsIdentity(),
|
|
25
|
+
tenant,
|
|
26
|
+
type: text('type').notNull(),
|
|
27
|
+
version: integer('version').notNull(),
|
|
28
|
+
subjectId: text('subject_id').notNull(),
|
|
29
|
+
actor: jsonb('actor').$type().notNull(),
|
|
30
|
+
traceId: text('trace_id').notNull(),
|
|
31
|
+
data: jsonb('data').$type().notNull(),
|
|
32
|
+
occurredAt: timestamp('occurred_at', { withTimezone: true, mode: 'date' })
|
|
33
|
+
.notNull()
|
|
34
|
+
.defaultNow(),
|
|
35
|
+
publishedAt: timestamp('published_at', { withTimezone: true, mode: 'date' }),
|
|
36
|
+
attempts: integer('attempts').notNull().default(0),
|
|
37
|
+
lastError: text('last_error'),
|
|
38
|
+
}, (t) => [
|
|
39
|
+
index('outbox_unpublished_seq_idx')
|
|
40
|
+
.on(t.seq)
|
|
41
|
+
.where(sql `${t.publishedAt} is null`),
|
|
42
|
+
index('outbox_published_at_idx')
|
|
43
|
+
.on(t.publishedAt)
|
|
44
|
+
.where(sql `${t.publishedAt} is not null`),
|
|
45
|
+
pgPolicy(OUTBOX_POLICY, {
|
|
46
|
+
as: 'permissive',
|
|
47
|
+
for: 'all',
|
|
48
|
+
to: 'public',
|
|
49
|
+
using: sql.raw(OUTBOX_POLICY_SQL),
|
|
50
|
+
withCheck: sql.raw(OUTBOX_POLICY_SQL),
|
|
51
|
+
}),
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Таблица `<schema>.inbox` (§6): `id` (PK, id события), `type`, `received_at`; индекс по `received_at` для очистки.
|
|
56
|
+
* Без RLS (служебная). Сервис выдаёт миграцией `GRANT SELECT, INSERT, DELETE` роли `<svc>_app`.
|
|
57
|
+
* @throws {TypeError} имя схемы вне `^[a-z][a-z0-9-]{1,31}$`.
|
|
58
|
+
*/
|
|
59
|
+
export function createInboxTable(schema) {
|
|
60
|
+
assertServiceName(schema.schemaName, 'createInboxTable');
|
|
61
|
+
return schema.table(INBOX_TABLE, {
|
|
62
|
+
id: text('id').primaryKey(),
|
|
63
|
+
type: text('type').notNull(),
|
|
64
|
+
receivedAt: timestamp('received_at', { withTimezone: true, mode: 'date' })
|
|
65
|
+
.notNull()
|
|
66
|
+
.defaultNow(),
|
|
67
|
+
}, (t) => [index('inbox_received_at_idx').on(t.receivedAt)]);
|
|
68
|
+
}
|
|
69
|
+
/** Колонка `tenant` таблицы outbox допускает NULL (`tenantNullable: true`). */
|
|
70
|
+
export function outboxTenantNullable(table) {
|
|
71
|
+
return (getTableConfig(table).columns.find((column) => column.name === 'tenant')?.notNull !== true);
|
|
72
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export { EVENTS_EXCHANGE } from '../internal/rabbitmq-definitions.js';
|
|
2
|
+
/** Заголовок причины у копии в DLQ (§3.4). */
|
|
3
|
+
export declare const DLQ_REASON_HEADER = "x-dlq-reason";
|
|
4
|
+
/** Неподтверждённых сообщений на потребителя (§3.4, AC-5.17). */
|
|
5
|
+
export declare const CONSUMER_PREFETCH = 10;
|
|
6
|
+
/** Повторов по `x-death`, затем DLQ (§3.4). */
|
|
7
|
+
export declare const MAX_RETRIES = 5;
|
|
8
|
+
/** Heartbeat, с; пауза переподключения — RABBITMQ_RETRY_SECONDS. */
|
|
9
|
+
export declare const HEARTBEAT_SECONDS = 10;
|
|
10
|
+
/** Ожидание ответа брокера на закрытие канала, мс. */
|
|
11
|
+
export declare const CHANNEL_CLOSE_TIMEOUT_MS = 2000;
|
|
12
|
+
/** Ожидание ответа брокера на закрытие соединения, мс. */
|
|
13
|
+
export declare const CONNECTION_CLOSE_TIMEOUT_MS = 1000;
|
|
14
|
+
/** Пачка relay (§3.4). */
|
|
15
|
+
export declare const RELAY_BATCH_SIZE = 100;
|
|
16
|
+
/** Общий срок подтверждений одной пачки, мс (§3.4). */
|
|
17
|
+
export declare const RELAY_BATCH_DEADLINE_MS = 5000;
|
|
18
|
+
/** Опрос outbox, мс (§3.4, AC-5.3). */
|
|
19
|
+
export declare const RELAY_POLL_MS = 1000;
|
|
20
|
+
/** Пауза строки после неудачи: 1 с → 5 мин, удвоение (§3.4, AC-5.16). */
|
|
21
|
+
export declare const RELAY_BACKOFF_MIN_MS = 1000;
|
|
22
|
+
/** Предел паузы строки, мс. */
|
|
23
|
+
export declare const RELAY_BACKOFF_MAX_MS = 300000;
|
|
24
|
+
/** Неудач строки до error-лога и GlitchTip (AC-5.16). */
|
|
25
|
+
export declare const RELAY_ALERT_ATTEMPTS = 5;
|
|
26
|
+
/** Таймаут подтверждения публикации, мс. */
|
|
27
|
+
export declare const PUBLISH_CONFIRM_TIMEOUT_MS = 10000;
|
|
28
|
+
/** Хранение опубликованного outbox и inbox, дни (§3.4, AC-5.12). */
|
|
29
|
+
export declare const RETENTION_DAYS = 7;
|
|
30
|
+
/** Интервал очистки, мс. */
|
|
31
|
+
export declare const CLEANUP_INTERVAL_MS = 3600000;
|
|
32
|
+
/** Пачка очистки. */
|
|
33
|
+
export declare const CLEANUP_BATCH_SIZE = 1000;
|
|
34
|
+
/** Пачек за проход. */
|
|
35
|
+
export declare const CLEANUP_MAX_BATCHES = 100;
|
|
36
|
+
/** Очереди сервиса. */
|
|
37
|
+
export interface KitQueues {
|
|
38
|
+
/** Основная очередь. */
|
|
39
|
+
readonly main: string;
|
|
40
|
+
/** Очередь отложенного повтора (TTL → основная). */
|
|
41
|
+
readonly retry: string;
|
|
42
|
+
/** Неразбираемые и исчерпавшие повторы. */
|
|
43
|
+
readonly dlq: string;
|
|
44
|
+
}
|
|
45
|
+
/** Очереди экземпляра: `<svc>`, `<svc>.retry`, `<svc>.dlq`. */
|
|
46
|
+
export declare function queuesOf(service: string): KitQueues;
|
|
47
|
+
/** Очередь реплики `<svc>.replica.<REPLICA_ID>` (AC-5.9). */
|
|
48
|
+
export declare function replicaQueueOf(service: string, replica: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Общий срок обработчиков остановки (relay, подписчик), мс: остаток SHUTDOWN_TIMEOUT_MS после паузы SHUTDOWN_DRAIN_MS и
|
|
51
|
+
* закрытия канала и соединения, не меньше 1 с (П9).
|
|
52
|
+
*/
|
|
53
|
+
export declare function handlersDeadlineMs(env: {
|
|
54
|
+
readonly SHUTDOWN_TIMEOUT_MS: number;
|
|
55
|
+
readonly SHUTDOWN_DRAIN_MS: number;
|
|
56
|
+
}): number;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Топология RabbitMQ и числа relay, подписчика и очистки (prd.md §3.4, AC-5.3, AC-5.7, AC-5.11, AC-5.12, AC-5.16, AC-5.17).
|
|
2
|
+
// Эталон — O topology.ts; имена очередей — от имени экземпляра, заголовок x-dlq-reason (PRD) вместо x-<svc>-dlq-reason,
|
|
3
|
+
// общий срок обработчиков остановки от SHUTDOWN_TIMEOUT_MS (П9) вместо константы O 8000.
|
|
4
|
+
export { EVENTS_EXCHANGE } from '../internal/rabbitmq-definitions.js';
|
|
5
|
+
/** Заголовок причины у копии в DLQ (§3.4). */
|
|
6
|
+
export const DLQ_REASON_HEADER = 'x-dlq-reason';
|
|
7
|
+
/** Неподтверждённых сообщений на потребителя (§3.4, AC-5.17). */
|
|
8
|
+
export const CONSUMER_PREFETCH = 10;
|
|
9
|
+
/** Повторов по `x-death`, затем DLQ (§3.4). */
|
|
10
|
+
export const MAX_RETRIES = 5;
|
|
11
|
+
/** Heartbeat, с; пауза переподключения — RABBITMQ_RETRY_SECONDS. */
|
|
12
|
+
export const HEARTBEAT_SECONDS = 10;
|
|
13
|
+
/** Ожидание ответа брокера на закрытие канала, мс. */
|
|
14
|
+
export const CHANNEL_CLOSE_TIMEOUT_MS = 2_000;
|
|
15
|
+
/** Ожидание ответа брокера на закрытие соединения, мс. */
|
|
16
|
+
export const CONNECTION_CLOSE_TIMEOUT_MS = 1_000;
|
|
17
|
+
/** Пачка relay (§3.4). */
|
|
18
|
+
export const RELAY_BATCH_SIZE = 100;
|
|
19
|
+
/** Общий срок подтверждений одной пачки, мс (§3.4). */
|
|
20
|
+
export const RELAY_BATCH_DEADLINE_MS = 5_000;
|
|
21
|
+
/** Опрос outbox, мс (§3.4, AC-5.3). */
|
|
22
|
+
export const RELAY_POLL_MS = 1_000;
|
|
23
|
+
/** Пауза строки после неудачи: 1 с → 5 мин, удвоение (§3.4, AC-5.16). */
|
|
24
|
+
export const RELAY_BACKOFF_MIN_MS = 1_000;
|
|
25
|
+
/** Предел паузы строки, мс. */
|
|
26
|
+
export const RELAY_BACKOFF_MAX_MS = 300_000;
|
|
27
|
+
/** Неудач строки до error-лога и GlitchTip (AC-5.16). */
|
|
28
|
+
export const RELAY_ALERT_ATTEMPTS = 5;
|
|
29
|
+
/** Таймаут подтверждения публикации, мс. */
|
|
30
|
+
export const PUBLISH_CONFIRM_TIMEOUT_MS = 10_000;
|
|
31
|
+
/** Хранение опубликованного outbox и inbox, дни (§3.4, AC-5.12). */
|
|
32
|
+
export const RETENTION_DAYS = 7;
|
|
33
|
+
/** Интервал очистки, мс. */
|
|
34
|
+
export const CLEANUP_INTERVAL_MS = 3_600_000;
|
|
35
|
+
/** Пачка очистки. */
|
|
36
|
+
export const CLEANUP_BATCH_SIZE = 1_000;
|
|
37
|
+
/** Пачек за проход. */
|
|
38
|
+
export const CLEANUP_MAX_BATCHES = 100;
|
|
39
|
+
/** Очереди экземпляра: `<svc>`, `<svc>.retry`, `<svc>.dlq`. */
|
|
40
|
+
export function queuesOf(service) {
|
|
41
|
+
return { main: service, retry: `${service}.retry`, dlq: `${service}.dlq` };
|
|
42
|
+
}
|
|
43
|
+
/** Очередь реплики `<svc>.replica.<REPLICA_ID>` (AC-5.9). */
|
|
44
|
+
export function replicaQueueOf(service, replica) {
|
|
45
|
+
return `${service}.replica.${replica}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Общий срок обработчиков остановки (relay, подписчик), мс: остаток SHUTDOWN_TIMEOUT_MS после паузы SHUTDOWN_DRAIN_MS и
|
|
49
|
+
* закрытия канала и соединения, не меньше 1 с (П9).
|
|
50
|
+
*/
|
|
51
|
+
export function handlersDeadlineMs(env) {
|
|
52
|
+
return Math.max(1_000, env.SHUTDOWN_TIMEOUT_MS -
|
|
53
|
+
env.SHUTDOWN_DRAIN_MS -
|
|
54
|
+
CHANNEL_CLOSE_TIMEOUT_MS -
|
|
55
|
+
CONNECTION_CLOSE_TIMEOUT_MS);
|
|
56
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Logger } from 'pino';
|
|
2
|
+
/** Ошибка запуска сервиса (§3.2): код и подробности без секретов попадают в fatal-лог и тег события. */
|
|
3
|
+
export interface StartupFailure {
|
|
4
|
+
/** Код отказа (SCREAMING_SNAKE). */
|
|
5
|
+
readonly startupCode: string;
|
|
6
|
+
/** Подробности для лога — без значений окружения, секретов и ПДн. */
|
|
7
|
+
startupDetails(): Readonly<Record<string, unknown>>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Отказ запуска (§3.2, UC-3): `fatal`-лог, событие GlitchTip с ожиданием отправки до 2 с, `exit(1)`.
|
|
11
|
+
* `InvalidEnvironmentError` — код и имена ключей; `StartupFailure` — `startupCode` и `startupDetails()`;
|
|
12
|
+
* прочее — через `sanitizeError`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function failStartup(raw: unknown, logger: Logger): Promise<never>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// failStartup и StartupFailure (prd.md §3.2, AC-2.9, UC-3, NFR-4, NFR-9). Эталон — O fail-startup.ts: доменная ветка
|
|
2
|
+
// ModelCatalogError заменена интерфейсом StartupFailure (распознаётся по форме), параметр exit не переносится.
|
|
3
|
+
import * as Sentry from '@sentry/nestjs';
|
|
4
|
+
import { InvalidEnvironmentError } from '../config/parse-env.js';
|
|
5
|
+
import { sanitizeError } from './sanitize-error.js';
|
|
6
|
+
import { captureError } from './sentry.js';
|
|
7
|
+
/** Ожидание отправки события GlitchTip перед выходом (§3.2). */
|
|
8
|
+
const SENTRY_FLUSH_MS = 2000;
|
|
9
|
+
/** Код события прочих отказов запуска. */
|
|
10
|
+
const STARTUP_FAILED = 'STARTUP_FAILED';
|
|
11
|
+
function isStartupFailure(raw) {
|
|
12
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const { startupCode, startupDetails } = raw;
|
|
16
|
+
return typeof startupCode === 'string' && typeof startupDetails === 'function';
|
|
17
|
+
}
|
|
18
|
+
/** Подробности отказа; исключение в startupDetails() не отменяет fatal-лог, событие и exit(1). */
|
|
19
|
+
function detailsOf(raw) {
|
|
20
|
+
try {
|
|
21
|
+
return raw.startupDetails();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Пишет fatal-лог отказа и возвращает код события. */
|
|
28
|
+
function logFailure(raw, logger) {
|
|
29
|
+
if (raw instanceof InvalidEnvironmentError) {
|
|
30
|
+
// Только имена ключей: значения окружения не попадают никуда (NFR-4).
|
|
31
|
+
logger.fatal({ code: raw.code, keys: raw.keys }, 'Неверное окружение — запуск остановлен');
|
|
32
|
+
return raw.code;
|
|
33
|
+
}
|
|
34
|
+
if (isStartupFailure(raw)) {
|
|
35
|
+
logger.fatal({ ...detailsOf(raw), code: raw.startupCode }, 'Отказ запуска');
|
|
36
|
+
return raw.startupCode;
|
|
37
|
+
}
|
|
38
|
+
logger.fatal({ code: STARTUP_FAILED, err: sanitizeError(raw) }, 'Отказ запуска');
|
|
39
|
+
return STARTUP_FAILED;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Отказ запуска (§3.2, UC-3): `fatal`-лог, событие GlitchTip с ожиданием отправки до 2 с, `exit(1)`.
|
|
43
|
+
* `InvalidEnvironmentError` — код и имена ключей; `StartupFailure` — `startupCode` и `startupDetails()`;
|
|
44
|
+
* прочее — через `sanitizeError`.
|
|
45
|
+
*/
|
|
46
|
+
export async function failStartup(raw, logger) {
|
|
47
|
+
const code = logFailure(raw, logger);
|
|
48
|
+
captureError(raw, { tags: { code } });
|
|
49
|
+
await Sentry.flush(SENTRY_FLUSH_MS);
|
|
50
|
+
return process.exit(1);
|
|
51
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Logger } from 'pino';
|
|
2
|
+
/** Проба зависимости: `true` — up. Исключение и ответ дольше 2 с — down. */
|
|
3
|
+
export type HealthProbe = () => Promise<boolean>;
|
|
4
|
+
/** Ответ `/health` (§3.2). */
|
|
5
|
+
export interface HealthReport {
|
|
6
|
+
/** `ok` — всё up; `degraded` — down некритичная; `unavailable` — down критичная или идёт остановка. */
|
|
7
|
+
readonly status: 'ok' | 'degraded' | 'unavailable';
|
|
8
|
+
/** Состояние каждой пробы по имени. */
|
|
9
|
+
readonly checks: Readonly<Record<string, 'up' | 'down'>>;
|
|
10
|
+
}
|
|
11
|
+
/** Срок пробы (§3.2). */
|
|
12
|
+
export declare const PROBE_TIMEOUT_MS = 2000;
|
|
13
|
+
/** Реестр проб готовности; один на экземпляр приложения. */
|
|
14
|
+
export declare class HealthRegistry {
|
|
15
|
+
private readonly logger;
|
|
16
|
+
private readonly probes;
|
|
17
|
+
private readonly lastState;
|
|
18
|
+
private stopping;
|
|
19
|
+
constructor(options: {
|
|
20
|
+
readonly logger: Logger;
|
|
21
|
+
});
|
|
22
|
+
/** Регистрирует пробу; повторная регистрация имени заменяет прежнюю. */
|
|
23
|
+
register(name: string, probe: HealthProbe, options: {
|
|
24
|
+
readonly critical: boolean;
|
|
25
|
+
}): void;
|
|
26
|
+
/** Шаг 1 остановки: с этого момента `evaluate` — `unavailable` без запуска проб. */
|
|
27
|
+
markStopping(): void;
|
|
28
|
+
/** Идёт ли остановка. */
|
|
29
|
+
get isStopping(): boolean;
|
|
30
|
+
/** Запускает пробы параллельно и сводит статус; при остановке — `unavailable` без проб. */
|
|
31
|
+
evaluate(): Promise<HealthReport>;
|
|
32
|
+
/** Лог только при смене состояния: up → down (и первый down) — warn, down → up — info. */
|
|
33
|
+
private trackState;
|
|
34
|
+
}
|